diff --git a/.env.docker b/.env.docker
index af3a54dea..399e91d7d 100644
--- a/.env.docker
+++ b/.env.docker
@@ -1,3 +1,5 @@
+TIMEZONE=PRC
+
APP_NAME=DooTask
APP_ENV=local
APP_KEY=
diff --git a/.env.example b/.env.example
index 3e36d2aeb..49c2212c5 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,5 @@
+TIMEZONE=PRC
+
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f17323cc6..9032303bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,167 @@
All notable changes to this project will be documented in this file.
+## [0.30.13]
+
+### Bug Fixes
+
+- 消息列表过大导致无法查看图片
+- 桌面端drawio版本错误
+
+### Performance
+
+- 优化菜单颜色选择
+
+## [0.30.6]
+
+### Bug Fixes
+
+- 无法在任务新窗口打开引用的任务
+- 在任务新窗口使用显示文件窗口错误的情况
+- 部分iOS系统按录音时页面闪烁的情况
+- 修改cookie协议页面的标题及点击cookie协议链接跳转新页面
+- Claude 机器人返回内容错误的情况
+- 在文件页面编辑文本时选择已传图片缩列图不显示的情况
+- 桌面客户端提示request错误
+- 客户端无法保存网络文件的情况
+- 可以发送空白md消息的情况
+- 桌面客户端缺失文件
+- 打开工作流设置后无法关闭桌面客户端的问题
+- 打不开已归档任务的情况
+
+### Performance
+
+- 升级客户端框架
+- 优化工作汇报提交表单
+- 优化确认框按钮样式
+- 优化时间冲突提示框
+- 文件页面弹出菜单时误操作优化
+- 优化任务描述编辑器
+- 优化表情快捷提示框
+- 优化移动端编辑任务详情
+- 优化桌面端邮件图片菜单
+- 优化表情关键词匹配
+- 工作流支持关联任务列表自动移动
+- 支持手动打卡
+- 优化数据流推送消息页面滚动
+- 优化再次点击消息定位到未读、待办、灰色未读
+- 优化复制链接
+- 优化可见消息列表
+- 优化动画样式
+- 优化菜单显示、选择复制
+- Ai聊天小概率出现重复推流的情况
+- 适配arm64
+
+## [0.29.21]
+
+### Performance
+
+- 会话消息没有接收人时已读进度优化
+- 优化拖拽文件夹上传提示
+- 深色模式硬件加速
+- 优化Android弹出键盘后聊天内容被覆盖的问题
+
+## [0.29.11]
+
+### Bug Fixes
+
+- 修复用户列表默认排序
+- 1. 审批通知模版 - 按钮白色修复 2. 审批详情样式相等 3. 审批评论 - 0分钟换成刚刚 4. 没有加入部门也能发起 审批申请 5. 审批流程设置页 - 样式调整
+
+### Performance
+
+- 管理员可以修改系统机器人基本资料
+- 优化深色模式
+- 添加ChatGPT、Claude智能机器人
+- 机器人群聊消息被@到时发送到webhook
+
+## [0.28.91]
+
+### Bug Fixes
+
+- 修复无法从任务消息对话中打开任务详情的情况
+- 按照dootask启动原始尺寸截取使用说明的图
+- 修改边栏目录滚动效果
+- 修改边栏目录滚动效果
+- 官网使用说明的图重新截取更换
+- 优化官网布局与样式
+- 修复下载英文页面跳转
+
+### Features
+
+- 增加获取更新日志接口,更改前端页面默认请求地址
+- 使用说明提交
+
+### Performance
+
+- 优化审批机器人模板消息样式
+- 优化添加任务样式
+- 优化任务默认时间
+- 优化深色模式
+- 任务详情发送文件时防止按esc关闭发送窗口
+- 深色模式下无法扫描登录二维码的情况
+- 优化iOS深色模式
+- Safari支持暗黑模式
+- 优化任务时间冲突提示
+- IOS部分点击事件存在阻塞的情况
+
+## [0.28.36]
+
+### Bug Fixes
+
+- 修改英文页面
+- 导航按钮英文修改
+- 修复导航按钮
+- 前端取消会议屏幕常亮
+
+### Features
+
+- 修改英文的下载单次手写字母大写
+- 链接调整和价格页面调整
+- 立即体验按钮、价格页面等样式调整
+
+### Performance
+
+- 整理官网页面
+- 任务详情页可见性选项默认不显示
+- 避免删除后不关闭任务窗口
+- 添加任务支持自定义协助人
+
+## [0.28.6]
+
+### Bug Fixes
+
+- 全员群禁言仅管理员可发言无效的问题
+- 发送消息失败再次编辑格式丢失的问题
+- 1.修复审核导出缺少 2.修复审核导出小时计算误差
+- 请假表格导出sheeft里名称显示人名
+- 修正初始化可见性人员异常问题
+- 修复审批通过人员姓名显示不正确
+
+### Features
+
+- 官网页面首版提交
+- 优化子任务可见性
+- 负责人、协助人更改可见性推送收回
+- 新增任务可见性操作模块、任务详情子任务样式优化
+
+### Performance
+
+- 审批中心图片压缩优化
+- 修改可见性推送优化
+- 代码优化
+- 冗余代码去除
+- 调整
+
## [0.27.46]
### Bug Fixes
- 会议窗口恢复不显示的情况
- 修复已知bug
+- 打开会话面板报错
+- 子任务通知无法打开
+- 修复审批的图片无法查看
### Performance
@@ -15,17 +170,6 @@ All notable changes to this project will be documented in this file.
- 聊天输入框iOS输入第一个字母出现抖动的情况
- 优化iOS出现连续加载消息列表的情况
- 移动端键盘发送
-
-## [0.27.35]
-
-### Bug Fixes
-
-- 打开会话面板报错
-- 子任务通知无法打开
-- 修复审批的图片无法查看
-
-### Performance
-
- 优化会员选择器
- 优化图片压缩
- 回复图片显示图片搜略图
diff --git a/README.md b/README.md
index 9ddd96213..02006950f 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Group No.: `546574618`
## Setup
-- `Docker` & `Docker Compose v2.0+` must be installed
+- `Docker v20.10+` & `Docker Compose v2.0+` must be installed
- System: `Centos/Debian/Ubuntu/macOS`
- Hardware suggestion: 2 cores and above 4G memory
diff --git a/README_CN.md b/README_CN.md
index f149aeb16..ebc02d25f 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -11,7 +11,7 @@
## 安装程序
-- 必须安装:`Docker` 和 `Docker Compose v2.0+`
+- 必须安装:`Docker v20.10+` 和 `Docker Compose v2.0+`
- 支持环境:`Centos/Debian/Ubuntu/macOS`
- 硬件建议:2核4G以上
diff --git a/app/Http/Controllers/Api/ApproveController.php b/app/Http/Controllers/Api/ApproveController.php
index d0c408515..12ee28334 100755
--- a/app/Http/Controllers/Api/ApproveController.php
+++ b/app/Http/Controllers/Api/ApproveController.php
@@ -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']));
@@ -190,12 +191,25 @@ class ApproveController extends AbstractController
$data['userid'] = (string)$user->userid;
$data['content'] = Request::input('content'); //内容+图片
+ $processInst = $this->getProcessById($data['proc_inst_id']);
+
$ret = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/process/addGlobalComment', json_encode(Base::arrayKeyToCamel($data)));
$process = json_decode($ret['ret'] == 1 ? $ret['data'] : '{}', true);
if (!$process || $process['status'] != 200) {
return Base::retError($process['message'] ?? '添加失败');
}
- //
+
+ // 推送通知
+ $botUser = User::botGetOrCreate('approval-alert');
+ foreach ( $processInst['userids'] as $id) {
+ if($id != $user->userid){
+ $dialog = WebSocketDialog::checkUserDialog($botUser, $id);
+ $processInst['comment_user_id'] = $user->userid;
+ $processInst['comment_content'] = json_decode($data['content'],true)['content'];
+ $this->approveMsg('approve_comment_notifier', $dialog, $botUser, $processInst, $processInst);
+ }
+ }
+
$res = Base::arrayKeyToUnderline($process['data']);
return Base::retSuccess('success', $res);
}
@@ -206,7 +220,7 @@ class ApproveController extends AbstractController
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup approve
- * @apiName task__complete
+ * @apiName task__complete
*
* @apiQuery {Number} task_id 流程ID
* @apiQuery {String} pass 标题 [true-通过,false-拒绝]
@@ -958,6 +972,8 @@ class ApproveController extends AbstractController
'type' => $process['var']['type'],
'start_time' => $process['var']['start_time'],
'end_time' => $process['var']['end_time'],
+ 'comment_nickname' => $process['comment_user_id'] ? User::userid2nickname($process['comment_user_id']) : '',
+ 'comment_content' => $process['comment_content'] ?? ''
];
$text = view('push.bot', ['type' => $type, 'action' => $action, 'is_finished' => $process['is_finished'], 'data' => (object)$data])->render();
$text = preg_replace("/^\x20+/", "", $text);
@@ -1011,6 +1027,7 @@ class ApproveController extends AbstractController
}
//
$res = Base::arrayKeyToUnderline($process['data']);
+ $res['userids'] = [];
foreach ($res['node_infos'] as &$val) {
if (isset($val['node_user_list'])) {
$node = $val['node_user_list'];
@@ -1020,10 +1037,12 @@ class ApproveController extends AbstractController
continue;
}
$val['node_user_list'][$k]['userimg'] = User::getAvatar($info->userid, $info->userimg, $info->email, $info->nickname);
+ $res['userids'][] = $item['target_id'];
}
}else if($val['aprover_id']){
$info = User::whereUserid($val['aprover_id'])->first();
$val['userimg'] = $info ? User::getAvatar($info->userid, $info->userimg, $info->email, $info->nickname) : '';
+ $res['userids'][] = $val['aprover_id'];
}
}
// 全局评论
@@ -1039,6 +1058,10 @@ class ApproveController extends AbstractController
}
$info = User::whereUserid($res['start_user_id'])->first();
$res['userimg'] = $info ? User::getAvatar($info->userid, $info->userimg, $info->email, $info->nickname) : '';
+ //
+ $res['userids'][] = $info->userid;
+ $res['userids'] = array_unique($res['userids']);
+ //
return $res;
}
diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php
index a40c3fcb4..fa937664a 100755
--- a/app/Http/Controllers/Api/DialogController.php
+++ b/app/Http/Controllers/Api/DialogController.php
@@ -2,7 +2,9 @@
namespace App\Http\Controllers\Api;
+use App\Tasks\PushTask;
use DB;
+use Hhxsv5\LaravelS\Swoole\Task\Task;
use Request;
use Redirect;
use Carbon\Carbon;
@@ -78,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)
@@ -115,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)
@@ -152,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)
@@ -403,6 +405,8 @@ class DialogController extends AbstractController
if ($msg_type) {
if ($msg_type === 'tag') {
$builder->where('tag', '>', 0);
+ } elseif ($msg_type === 'todo') {
+ $builder->where('todo', '>', 0);
} elseif ($msg_type === 'link') {
$builder->whereLink(1);
} elseif (in_array($msg_type, ['text', 'image', 'file', 'record', 'meeting'])) {
@@ -635,7 +639,46 @@ class DialogController extends AbstractController
}
/**
- * @api {post} api/dialog/msg/sendtext 14. 发送消息
+ * @api {post} api/dialog/msg/stream 14. 通知成员监听消息
+ *
+ * @apiDescription 通知指定会员EventSource监听流动消息
+ * @apiVersion 1.0.0
+ * @apiGroup dialog
+ * @apiName msg__stream
+ *
+ * @apiParam {Number} dialog_id 对话ID
+ * @apiParam {Number} userid 通知会员ID
+ * @apiParam {String} stream_url 流动消息地址
+ *
+ * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
+ * @apiSuccess {String} msg 返回信息(错误描述)
+ * @apiSuccess {Object} data 返回数据
+ */
+ public function msg__stream()
+ {
+ // $dialog_id = intval(Request::input('dialog_id'));
+ $userid = intval(Request::input('userid'));
+ $stream_url = trim(Request::input('stream_url'));
+ //
+ if ($userid <= 0) {
+ return Base::retError('参数错误');
+ }
+ //
+ $params = [
+ 'userid' => $userid,
+ 'msg' => [
+ 'type' => 'msgStream',
+ 'stream_url' => $stream_url,
+ ]
+ ];
+ $task = new PushTask($params, false);
+ Task::deliver($task);
+ //
+ return Base::retSuccess('success');
+ }
+
+ /**
+ * @api {post} api/dialog/msg/sendtext 15. 发送消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -648,6 +691,9 @@ class DialogController extends AbstractController
* - html: HTML(默认)
* - md: MARKDOWN
* @apiParam {Number} [update_id] 更新消息ID(优先大于 reply_id)
+ * @apiParam {String} [update_mark] 是否更新标记
+ * - no: 不标记(仅机器人支持)
+ * - yes: 标记(默认)
* @apiParam {Number} [reply_id] 回复ID
* @apiParam {String} [silence] 是否静默发送
* - no: 正常发送(默认)
@@ -674,69 +720,77 @@ class DialogController extends AbstractController
}
//
$dialog_id = intval(Request::input('dialog_id'));
+ $dialog_ids = trim(Request::input('dialog_ids'));
$update_id = intval(Request::input('update_id'));
+ $update_mark = !($user->bot && in_array(strtolower(trim(Request::input('update_mark'))), ['no', 'false', '0']));
$reply_id = intval(Request::input('reply_id'));
$text = trim(Request::input('text'));
$text_type = strtolower(trim(Request::input('text_type')));
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
$markdown = in_array($text_type, ['md', 'markdown']);
- //
- WebSocketDialog::checkDialog($dialog_id);
- //
- if ($update_id > 0) {
- $action = "update-$update_id";
- } elseif ($reply_id > 0) {
- $action = "reply-$reply_id";
- } else {
- $action = "";
- }
- //
- if (!$markdown) {
- $text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
- }
- $strlen = mb_strlen($text);
- $noimglen = mb_strlen(preg_replace("/
]*?>/i", "", $text));
- if ($strlen < 1) {
- return Base::retError('消息内容不能为空');
- }
- if ($noimglen > 200000) {
- return Base::retError('消息内容最大不能超过200000字');
- }
- if ($noimglen > 5000) {
- // 内容过长转成文件发送
- $path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
- Base::makeDir(public_path($path));
- $path = $path . md5($text) . ".htm";
- $file = public_path($path);
- file_put_contents($file, $text);
- $size = filesize(public_path($path));
- if (empty($size)) {
- return Base::retError('消息发送保存失败');
+ //
+ $result = [];
+ $dialogIds = $dialog_ids ? explode(',', $dialog_ids) : [$dialog_id ?: 0];
+ foreach($dialogIds as $dialog_id) {
+ //
+ WebSocketDialog::checkDialog($dialog_id);
+ //
+ if ($update_id > 0) {
+ $action = $update_mark ? "update-$update_id" : "change-$update_id";
+ } elseif ($reply_id > 0) {
+ $action = "reply-$reply_id";
+ } else {
+ $action = "";
}
- $ext = $markdown ? 'md' : 'htm';
- $fileData = [
- 'name' => "LongText-{$strlen}.{$ext}",
- 'size' => $size,
- 'file' => $file,
- 'path' => $path,
- 'url' => Base::fillUrl($path),
- 'thumb' => '',
- 'width' => -1,
- 'height' => -1,
- 'ext' => $ext,
- ];
- return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence);
+ //
+ if (!$markdown) {
+ $text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
+ }
+ $strlen = mb_strlen($text);
+ $noimglen = mb_strlen(preg_replace("/
]*?>/i", "", $text));
+ if ($strlen < 1) {
+ return Base::retError('消息内容不能为空');
+ }
+ if ($noimglen > 200000) {
+ return Base::retError('消息内容最大不能超过200000字');
+ }
+ if ($noimglen > 5000) {
+ // 内容过长转成文件发送
+ $path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
+ Base::makeDir(public_path($path));
+ $path = $path . md5($text) . ".htm";
+ $file = public_path($path);
+ file_put_contents($file, $text);
+ $size = filesize(public_path($path));
+ if (empty($size)) {
+ return Base::retError('消息发送保存失败');
+ }
+ $ext = $markdown ? 'md' : 'htm';
+ $fileData = [
+ 'name' => "LongText-{$strlen}.{$ext}",
+ 'size' => $size,
+ 'file' => $file,
+ 'path' => $path,
+ 'url' => Base::fillUrl($path),
+ 'thumb' => '',
+ 'width' => -1,
+ 'height' => -1,
+ 'ext' => $ext,
+ ];
+ $result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence);
+ }
+ //
+ $msgData = ['text' => $text];
+ if ($markdown) {
+ $msgData['type'] = 'md';
+ }
+ $result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence);
}
- //
- $msgData = ['text' => $text];
- if ($markdown) {
- $msgData['type'] = 'md';
- }
- return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence);
+ return $result;
}
/**
- * @api {post} api/dialog/msg/sendrecord 15. 发送语音
+ * @api {post} api/dialog/msg/sendrecord 16. 发送语音
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -783,7 +837,7 @@ class DialogController extends AbstractController
}
/**
- * @api {post} api/dialog/msg/sendfile 16. 文件上传
+ * @api {post} api/dialog/msg/sendfile 17. 文件上传
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -815,6 +869,7 @@ class DialogController extends AbstractController
/**
* @api {post} api/dialog/msg/sendfiles 17. 群发文件上传
+ * @api {post} api/dialog/msg/sendfiles 18. 群发文件上传
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -869,7 +924,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/sendfileid 18. 通过文件ID发送文件
+ * @api {get} api/dialog/msg/sendfileid 19. 通过文件ID发送文件
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -939,7 +994,7 @@ class DialogController extends AbstractController
}
/**
- * @api {post} api/dialog/msg/sendanon 19. 发送匿名消息
+ * @api {post} api/dialog/msg/sendanon 20. 发送匿名消息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -992,7 +1047,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/readlist 20. 获取消息阅读情况
+ * @api {get} api/dialog/msg/readlist 21. 获取消息阅读情况
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1021,7 +1076,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/detail 21. 消息详情
+ * @api {get} api/dialog/msg/detail 22. 消息详情
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1069,7 +1124,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/download 22. 文件下载
+ * @api {get} api/dialog/msg/download 23. 文件下载
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1112,7 +1167,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/withdraw 23. 聊天消息撤回
+ * @api {get} api/dialog/msg/withdraw 24. 聊天消息撤回
*
* @apiDescription 消息撤回限制24小时内,需要token身份
* @apiVersion 1.0.0
@@ -1138,7 +1193,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/mark 24. 消息标记操作
+ * @api {get} api/dialog/msg/mark 25. 消息标记操作
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1205,7 +1260,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/silence 25. 消息免打扰
+ * @api {get} api/dialog/msg/silence 26. 消息免打扰
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1268,7 +1323,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/forward 26. 转发消息给
+ * @api {get} api/dialog/msg/forward 27. 转发消息给
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1305,7 +1360,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/emoji 27. emoji回复
+ * @api {get} api/dialog/msg/emoji 28. emoji回复
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1340,7 +1395,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/tag 28. 标注/取消标注
+ * @api {get} api/dialog/msg/tag 29. 标注/取消标注
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1369,7 +1424,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/todo 29. 设待办/取消待办
+ * @api {get} api/dialog/msg/todo 30. 设待办/取消待办
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1412,7 +1467,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/todolist 30. 获取消息待办情况
+ * @api {get} api/dialog/msg/todolist 31. 获取消息待办情况
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1442,7 +1497,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/msg/done 31. 完成待办
+ * @api {get} api/dialog/msg/done 32. 完成待办
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1489,7 +1544,48 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/add 32. 新增群组
+ * @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. 新增群组
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1551,7 +1647,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/edit 33. 修改群组
+ * @api {get} api/dialog/group/edit 34. 修改群组
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1612,7 +1708,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/adduser 34. 添加群成员
+ * @api {get} api/dialog/group/adduser 35. 添加群成员
*
* @apiDescription 需要token身份
* - 有群主时:只有群主可以邀请
@@ -1648,7 +1744,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/deluser 35. 移出(退出)群成员
+ * @api {get} api/dialog/group/deluser 36. 移出(退出)群成员
*
* @apiDescription 需要token身份
* - 只有群主、邀请人可以踢人
@@ -1692,7 +1788,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/transfer 36. 转让群组
+ * @api {get} api/dialog/group/transfer 37. 转让群组
*
* @apiDescription 需要token身份
* - 只有群主且是个人类型群可以解散
@@ -1736,7 +1832,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/disband 37. 解散群组
+ * @api {get} api/dialog/group/disband 38. 解散群组
*
* @apiDescription 需要token身份
* - 只有群主且是个人类型群可以解散
@@ -1764,7 +1860,7 @@ class DialogController extends AbstractController
}
/**
- * @api {get} api/dialog/group/searchuser 38. 搜索个人群(仅限管理员)
+ * @api {get} api/dialog/group/searchuser 39. 搜索个人群(仅限管理员)
*
* @apiDescription 需要token身份,用于创建部门搜索个人群组
* @apiVersion 1.0.0
diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php
index 38af3df70..4c507ed6d 100755
--- a/app/Http/Controllers/Api/ProjectController.php
+++ b/app/Http/Controllers/Api/ProjectController.php
@@ -979,10 +979,14 @@ class ProjectController extends AbstractController
$builder->leftJoin('project_users', function ($query) {
$query->on('project_tasks.project_id', '=', 'project_users.project_id')->where('project_users.owner', 1);
});
+ $builder->leftJoin('project_task_users as project_p_task_users', function ($query) {
+ $query->on('project_p_task_users.task_pid', '=', 'project_tasks.parent_id');
+ });
$builder->where(function ($query) use ($userid) {
$query->where("project_tasks.is_all_visible", 1);
$query->orWhere("project_users.userid", $userid);
$query->orWhere("project_task_users.userid", $userid);
+ $query->orWhere("project_p_task_users.userid", $userid);
});
// 优化子查询汇总
$builder->leftJoinSub(function ($query) {
@@ -991,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')
@@ -1003,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("
@@ -1044,7 +1048,63 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/export 19. 导出任务(限管理员)
+ * @api {get} api/project/task/easylists 19. 任务列表-简单的
+ *
+ * @apiDescription 需要token身份
+ * @apiVersion 1.0.0
+ * @apiGroup project
+ * @apiName task__easylists
+
+ * @apiParam {String} [taskid] 排除的任务ID
+ * @apiParam {String} [userid] 用户ID(如:1,2)
+ * @apiParam {String} [timerange] 时间范围(如:2022-03-01 12:12:12,2022-05-01 12:12:12)
+ *
+ * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
+ * @apiSuccess {String} msg 返回信息(错误描述)
+ * @apiSuccess {Object} data 返回数据
+ */
+ public function task__easylists()
+ {
+ User::auth();
+ //
+ $taskid = trim(Request::input('taskid'));
+ $userid = Request::input('userid');
+ $timerange = Request::input('timerange');
+ //
+ $list = ProjectTask::with(['taskUser'])
+ ->select('projects.name as project_name', 'project_tasks.id', 'project_tasks.name', 'project_tasks.start_at', 'project_tasks.end_at')
+ ->join('projects','project_tasks.project_id','=','projects.id')
+ ->leftJoin('project_task_users', function ($query) {
+ $query->on('project_tasks.id', '=', 'project_task_users.task_id')->where('project_task_users.owner', '=', 1);
+ })
+ ->whereIn('project_task_users.userid', is_array($userid) ? $userid : explode(',', $userid) )
+ ->when(!empty($timerange), function ($query) use ($timerange) {
+ if (!is_array($timerange)) {
+ $timerange = explode(',', $timerange);
+ }
+ if (Base::isDateOrTime($timerange[0]) && Base::isDateOrTime($timerange[1])) {
+ $query->where('project_tasks.start_at', '<=', Carbon::parse($timerange[1])->endOfDay());
+ $query->where('project_tasks.end_at', '>=', Carbon::parse($timerange[0])->startOfDay());
+ }
+ })
+ ->when(!empty($taskid), function ($query) use ($taskid) {
+ $query->where('project_tasks.id', "!=", $taskid);
+ })
+ ->whereNull('complete_at')
+ ->distinct()
+ ->orderByDesc('project_tasks.id')
+ ->paginate(Base::getPaginate(200, 100));
+ //
+ $list->transform(function ($customer) {
+ $customer->setAppends([]);
+ return $customer;
+ });
+ //
+ return Base::retSuccess('success', $list);
+ }
+
+ /**
+ * @api {get} api/project/task/export 20. 导出任务(限管理员)
*
* @apiDescription 导出指定范围任务(已完成、未完成、已归档),返回下载地址,需要token身份
* @apiVersion 1.0.0
@@ -1247,7 +1307,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/exportoverdue 20. 导出超期任务(限管理员)
+ * @api {get} api/project/task/exportoverdue 21. 导出超期任务(限管理员)
*
* @apiDescription 导出指定范围任务(已完成、未完成、已归档),返回下载地址,需要token身份
* @apiVersion 1.0.0
@@ -1356,7 +1416,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/down 21. 下载导出的任务
+ * @api {get} api/project/task/down 22. 下载导出的任务
*
* @apiVersion 1.0.0
* @apiGroup project
@@ -1382,7 +1442,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/one 22. 获取单个任务信息
+ * @api {get} api/project/task/one 23. 获取单个任务信息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1426,7 +1486,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/content 23. 获取任务详细描述
+ * @api {get} api/project/task/content 24. 获取任务详细描述
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1454,7 +1514,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/files 24. 获取任务文件列表
+ * @api {get} api/project/task/files 25. 获取任务文件列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1479,7 +1539,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/filedelete 25. 删除任务文件
+ * @api {get} api/project/task/filedelete 26. 删除任务文件
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -1512,7 +1572,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/filedetail 26. 获取任务文件详情
+ * @api {get} api/project/task/filedetail 27. 获取任务文件详情
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1556,7 +1616,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/filedown 27. 下载任务文件
+ * @api {get} api/project/task/filedown 28. 下载任务文件
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1605,7 +1665,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {post} api/project/task/add 28. 添加任务
+ * @api {post} api/project/task/add 29. 添加任务
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1689,7 +1749,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/addsub 29. 添加子任务
+ * @api {get} api/project/task/addsub 30. 添加子任务
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -1722,7 +1782,7 @@ class ProjectController extends AbstractController
'column_id' => $task->column_id,
'times' => [$task->start_at, $task->end_at],
'owner' => [User::userid()],
- 'is_all_visible' => 2,
+ 'is_all_visible' => $task->is_all_visible,
]);
$data = ProjectTask::oneTask($task->id);
$pushUserIds = ProjectTaskUser::whereTaskId($task->id)->pluck('userid')->toArray();
@@ -1734,7 +1794,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {post} api/project/task/update 30. 修改任务、子任务
+ * @api {post} api/project/task/update 31. 修改任务、子任务
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -1833,7 +1893,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/dialog 31. 创建/获取聊天室
+ * @api {get} api/project/task/dialog 32. 创建/获取聊天室
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -1882,7 +1942,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/archived 32. 归档任务
+ * @api {get} api/project/task/archived 33. 归档任务
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -1924,7 +1984,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/remove 33. 删除任务
+ * @api {get} api/project/task/remove 34. 删除任务
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -1958,7 +2018,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/resetfromlog 34. 根据日志重置任务
+ * @api {get} api/project/task/resetfromlog 35. 根据日志重置任务
*
* @apiDescription 需要token身份(限:项目、任务负责人)
* @apiVersion 1.0.0
@@ -2017,7 +2077,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/task/flow 35. 任务工作流信息
+ * @api {get} api/project/task/flow 36. 任务工作流信息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2099,7 +2159,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/flow/list 36. 工作流列表
+ * @api {get} api/project/flow/list 37. 工作流列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2125,7 +2185,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {post} api/project/flow/save 37. 保存工作流
+ * @api {post} api/project/flow/save 38. 保存工作流
*
* @apiDescription 需要token身份(限:项目负责人)
* @apiVersion 1.0.0
@@ -2159,7 +2219,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/flow/delete 38. 删除工作流
+ * @api {get} api/project/flow/delete 39. 删除工作流
*
* @apiDescription 需要token身份(限:项目负责人)
* @apiVersion 1.0.0
@@ -2191,7 +2251,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/log/lists 39. 获取项目、任务日志
+ * @api {get} api/project/log/lists 40. 获取项目、任务日志
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -2244,7 +2304,7 @@ class ProjectController extends AbstractController
}
/**
- * @api {get} api/project/top 40. 项目置顶
+ * @api {get} api/project/top 41. 项目置顶
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
diff --git a/app/Http/Controllers/Api/PublicController.php b/app/Http/Controllers/Api/PublicController.php
index 4e8eaf097..05980b8db 100755
--- a/app/Http/Controllers/Api/PublicController.php
+++ b/app/Http/Controllers/Api/PublicController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Api;
use App\Models\User;
+use App\Models\UserBot;
use App\Models\UserCheckinMac;
use App\Models\UserCheckinRecord;
use App\Models\WebSocketDialog;
@@ -89,90 +90,14 @@ class PublicController extends AbstractController
if ($setting['open'] !== 'open') {
return 'function off';
}
+ if (!in_array('auto', $setting['modes'])) {
+ return 'mode off';
+ }
if ($key != $setting['key']) {
return 'key error';
}
- $times = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
- $advance = (intval($setting['advance']) ?: 120) * 60;
- $delay = (intval($setting['delay']) ?: 120) * 60;
- //
- $nowDate = date("Y-m-d");
- $nowTime = date("H:i:s");
- //
- $timeStart = strtotime("{$nowDate} {$times[0]}");
- $timeEnd = strtotime("{$nowDate} {$times[1]}");
- $timeAdvance = max($timeStart - $advance, strtotime($nowDate));
- $timeDelay = min($timeEnd + $delay, strtotime("{$nowDate} 23:59:59"));
- if (Base::time() < $timeAdvance || $timeDelay < Base::time()) {
- return "not in valid time, valid time is " . date("H:i", $timeAdvance) . "-" . date("H:i", $timeDelay);
- }
- //
- $macs = explode(",", $mac);
- $checkins = [];
- foreach ($macs as $mac) {
- $mac = strtoupper($mac);
- if (Base::isMac($mac) && $UserCheckinMac = UserCheckinMac::whereMac($mac)->first()) {
- $checkins[] = $UserCheckinMac;
- $array = [
- 'userid' => $UserCheckinMac->userid,
- 'mac' => $UserCheckinMac->mac,
- 'date' => $nowDate,
- ];
- $record = UserCheckinRecord::where($array)->first();
- if (empty($record)) {
- $record = UserCheckinRecord::createInstance($array);
- }
- $record->times = Base::array2json(array_merge($record->times, [$nowTime]));
- $record->report_time = $time;
- $record->save();
- }
- }
- //
- if ($checkins && $botUser = User::botGetOrCreate('check-in')) {
- $getJokeSoup = function($type) {
- $pre = $type == "up" ? "每日开心:" : "心灵鸡汤:";
- $key = $type == "up" ? "JokeSoupTask:jokes" : "JokeSoupTask:soups";
- $array = Base::json2array(Cache::get($key));
- if ($array) {
- $item = $array[array_rand($array)];
- if ($item) {
- return $pre . $item;
- }
- }
- return null;
- };
- $sendMsg = function($type, UserCheckinMac $checkin) use ($getJokeSoup, $botUser, $nowDate) {
- $cacheKey = "Checkin::sendMsg-{$nowDate}-{$type}:" . $checkin->userid;
- if (Cache::get($cacheKey) === "yes") {
- return;
- }
- Cache::put($cacheKey, "yes", Carbon::now()->addDay());
- //
- $dialog = WebSocketDialog::checkUserDialog($botUser, $checkin->userid);
- if ($dialog) {
- $hi = date("H:i");
- $pre = $type == "up" ? "上班" : "下班";
- $remark = $checkin->remark ? " ({$checkin->remark})": "";
- $text = "
{$pre}打卡成功,打卡时间: {$hi}{$remark}
";
- $suff = $getJokeSoup($type);
- if ($suff) {
- $text = "{$text}----------
{$suff}
";
- }
- WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, $type != "up");
- }
- };
- if ($timeAdvance <= Base::time() && Base::time() < $timeEnd) {
- // 上班打卡通知(从最早打卡时间 到 下班打卡时间)
- foreach ($checkins as $checkin) {
- $sendMsg('up', $checkin);
- }
- }
- if ($timeEnd <= Base::time() && Base::time() <= $timeDelay) {
- // 下班打卡通知(下班打卡时间 到 最晚打卡时间)
- foreach ($checkins as $checkin) {
- $sendMsg('down', $checkin);
- }
- }
+ if ($error = UserBot::checkinBotCheckin($mac, $time)) {
+ return $error;
}
return 'success';
}
diff --git a/app/Http/Controllers/Api/SystemController.php b/app/Http/Controllers/Api/SystemController.php
index e8f098f9f..0a33b51ed 100755
--- a/app/Http/Controllers/Api/SystemController.php
+++ b/app/Http/Controllers/Api/SystemController.php
@@ -2,6 +2,8 @@
namespace App\Http\Controllers\Api;
+use App\Models\WebSocketDialog;
+use App\Models\WebSocketDialogMsg;
use Request;
use Session;
use Response;
@@ -38,7 +40,7 @@ class SystemController extends AbstractController
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
- * - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'auto_archived', 'archived_day', 'all_group_mute', 'all_group_autoin', 'image_compress', 'image_save_local', 'start_home', 'home_footer'])
+ * - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'image_compress', 'image_save_local', 'start_home'])
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -65,12 +67,13 @@ class SystemController extends AbstractController
'anon_message',
'auto_archived',
'archived_day',
+ 'task_visible',
+ 'task_default_time',
'all_group_mute',
'all_group_autoin',
'image_compress',
'image_save_local',
'start_home',
- 'home_footer'
])) {
unset($all[$key]);
}
@@ -104,6 +107,8 @@ class SystemController extends AbstractController
$setting['anon_message'] = $setting['anon_message'] ?: 'open';
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
+ $setting['task_visible'] = $setting['task_visible'] ?: 'close';
+ $setting['task_default_time'] = $setting['task_default_time'] ? Base::json2array($setting['task_default_time']) : ['09:00', '18:00'];
$setting['all_group_mute'] = $setting['all_group_mute'] ?: 'open';
$setting['all_group_autoin'] = $setting['all_group_autoin'] ?: 'yes';
$setting['image_compress'] = $setting['image_compress'] ?: 'open';
@@ -226,7 +231,92 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/setting/checkin 04. 获取签到设置、保存签到设置(限管理员)
+ * @api {get} api/system/setting/aibot 04. 获取会议设置、保存AI机器人设置(限管理员)
+ *
+ * @apiVersion 1.0.0
+ * @apiGroup system
+ * @apiName setting__aibot
+ *
+ * @apiParam {String} type
+ * - get: 获取(默认)
+ * - save: 保存设置(参数:['openai_key', 'openai_agency', 'claude_token', 'claude_agency'])
+ * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
+ * @apiSuccess {String} msg 返回信息(错误描述)
+ * @apiSuccess {Object} data 返回数据
+ */
+ public function setting__aibot()
+ {
+ $user = User::auth('admin');
+ //
+ $type = trim(Request::input('type'));
+ $setting = Base::setting('aibotSetting');
+
+ $keys = [
+ 'openai_key',
+ 'openai_agency',
+ 'claude_token',
+ 'claude_agency',
+ 'wenxin_key',
+ 'wenxin_secret',
+ 'wenxin_model',
+ 'qianwen_key',
+ 'qianwen_model'
+ ];
+
+ if ($type == 'save') {
+ if (env("SYSTEM_SETTING") == 'disabled') {
+ return Base::retError('当前环境禁止修改');
+ }
+ $all = Request::input();
+ foreach ($all as $key => $value) {
+ if (!in_array($key, $keys)) {
+ unset($all[$key]);
+ }
+ }
+ $backup = $setting;
+ $setting = Base::setting('aibotSetting', Base::newTrim($all));
+ //
+ if ($backup['openai_key'] != $setting['openai_key']) {
+ $botUser = User::botGetOrCreate('ai-openai');
+ if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
+ }
+ }
+ if ($backup['claude_token'] != $setting['claude_token']) {
+ $botUser = User::botGetOrCreate('ai-claude');
+ if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
+ }
+ }
+ if ($backup['wenxin_key'] != $setting['wenxin_key']) {
+ $botUser = User::botGetOrCreate('ai-wenxin');
+ if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
+ }
+ }
+ if ($backup['qianwen_key'] != $setting['qianwen_key']) {
+ $botUser = User::botGetOrCreate('ai-qianwen');
+ if ($botUser && $dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid)) {
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "设置成功"], $botUser->userid, true, false, true);
+ }
+ }
+ }
+ //
+ $setting['wenxin_model'] = $setting['wenxin_model'] ?: 'ERNIE-Bot-turbo';
+ $setting['qianwen_model'] = $setting['qianwen_model'] ?: 'qwen-v1';
+ if (env("SYSTEM_SETTING") == 'disabled') {
+ foreach ($keys as $item) {
+ if (strlen($setting[$item]) > 12) {
+ $setting[$item] = substr($setting[$item], 0, 4) . str_repeat('*', strlen($setting[$item]) - 8) . substr($setting[$item], -4);
+ }
+ }
+ }
+ //
+ return Base::retSuccess('success', $setting ?: json_decode('{}'));
+ }
+
+ /**
+ * @api {get} api/system/setting/checkin 05. 获取签到设置、保存签到设置(限管理员)
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -234,7 +324,7 @@ class SystemController extends AbstractController
*
* @apiParam {String} type
* - get: 获取(默认)
- * - save: 保存设置(参数:['open', 'time', 'advance', 'delay', 'remindin', 'remindexceed', 'edit', 'key'])
+ * - save: 保存设置(参数:['open', 'time', 'advance', 'delay', 'remindin', 'remindexceed', 'edit', 'modes', 'key'])
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
@@ -258,6 +348,7 @@ class SystemController extends AbstractController
'remindin',
'remindexceed',
'edit',
+ 'modes',
'key',
])) {
unset($all[$key]);
@@ -266,6 +357,7 @@ class SystemController extends AbstractController
if ($all['open'] === 'close') {
$all['key'] = md5(Base::generatePassword(32));
}
+ $all['modes'] = array_intersect($all['modes'], ['auto', 'manual', 'location']);
$setting = Base::setting('checkinSetting', Base::newTrim($all));
} else {
$setting = Base::setting('checkinSetting');
@@ -283,13 +375,14 @@ class SystemController extends AbstractController
$setting['remindin'] = intval($setting['remindin']) ?: 5;
$setting['remindexceed'] = intval($setting['remindexceed']) ?: 10;
$setting['edit'] = $setting['edit'] ?: 'close';
+ $setting['modes'] = is_array($setting['modes']) ? $setting['modes'] : [];
$setting['cmd'] = "curl -sSL '" . Base::fillUrl("api/public/checkin/install?key={$setting['key']}") . "' | sh";
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
/**
- * @api {get} api/system/setting/apppush 05. 获取APP推送设置、保存APP推送设置(限管理员)
+ * @api {get} api/system/setting/apppush 06. 获取APP推送设置、保存APP推送设置(限管理员)
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -334,7 +427,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/setting/thirdaccess 06. 第三方帐号(限管理员)
+ * @api {get} api/system/setting/thirdaccess 07. 第三方帐号(限管理员)
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -404,7 +497,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/demo 07. 获取演示帐号
+ * @api {get} api/system/demo 08. 获取演示帐号
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -428,7 +521,7 @@ class SystemController extends AbstractController
}
/**
- * @api {post} api/system/priority 08. 任务优先级
+ * @api {post} api/system/priority 09. 任务优先级
*
* @apiDescription 获取任务优先级、保存任务优先级
* @apiVersion 1.0.0
@@ -477,7 +570,7 @@ class SystemController extends AbstractController
}
/**
- * @api {post} api/system/column/template 09. 创建项目模板
+ * @api {post} api/system/column/template 10. 创建项目模板
*
* @apiDescription 获取创建项目模板、保存创建项目模板
* @apiVersion 1.0.0
@@ -524,7 +617,7 @@ class SystemController extends AbstractController
}
/**
- * @api {post} api/system/license 10. License
+ * @api {post} api/system/license 11. License
*
* @apiDescription 获取License信息、保存License(限管理员)
* @apiVersion 1.0.0
@@ -587,7 +680,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/info 11. 获取终端详细信息
+ * @api {get} api/system/get/info 12. 获取终端详细信息
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -616,7 +709,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/ip 12. 获取IP地址
+ * @api {get} api/system/get/ip 13. 获取IP地址
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -631,7 +724,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/cnip 13. 是否中国IP地址
+ * @api {get} api/system/get/cnip 14. 是否中国IP地址
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -648,7 +741,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/ipgcj02 14. 获取IP地址经纬度
+ * @api {get} api/system/get/ipgcj02 15. 获取IP地址经纬度
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -665,7 +758,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/ipinfo 15. 获取IP地址详细信息
+ * @api {get} api/system/get/ipinfo 16. 获取IP地址详细信息
*
* @apiVersion 1.0.0
* @apiGroup system
@@ -682,7 +775,7 @@ class SystemController extends AbstractController
}
/**
- * @api {post} api/system/imgupload 16. 上传图片
+ * @api {post} api/system/imgupload 17. 上传图片
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -742,7 +835,7 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/imgview 17. 浏览图片空间
+ * @api {get} api/system/get/imgview 18. 浏览图片空间
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -839,7 +932,7 @@ class SystemController extends AbstractController
}
/**
- * @api {post} api/system/fileupload 18. 上传文件
+ * @api {post} api/system/fileupload 19. 上传文件
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
@@ -881,18 +974,18 @@ class SystemController extends AbstractController
}
/**
- * @api {get} api/system/get/showitem 19. 首页显示ITEM
+ * @api {get} api/system/get/updatelog 20. 获取更新日志
*
- * @apiDescription 用于判断首页是否显示:pro、github、更新日志...
+ * @apiDescription 获取更新日志
* @apiVersion 1.0.0
* @apiGroup system
- * @apiName get__showitem
+ * @apiName get__updatelog
*
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
- public function get__showitem()
+ public function get__updatelog()
{
$logPath = base_path('CHANGELOG.md');
$logContent = "";
@@ -905,30 +998,7 @@ class SystemController extends AbstractController
}
}
return Base::retSuccess('success', [
- 'pro' => str_contains(Request::getHost(), "dootask.com") || str_contains(Request::getHost(), "127.0.0.1"),
- 'github' => env('GITHUB_URL') ?: false,
'updateLog' => $logContent ?: false,
- 'updateVer' => $logVersion,
- ]);
- }
-
- /**
- * @api {get} api/system/get/starthome 20. 启动首页设置信息
- *
- * @apiDescription 用于判断注册是否需要启动首页
- * @apiVersion 1.0.0
- * @apiGroup system
- * @apiName get__starthome
- *
- * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
- * @apiSuccess {String} msg 返回信息(错误描述)
- * @apiSuccess {Object} data 返回数据
- */
- public function get__starthome()
- {
- return Base::retSuccess('success', [
- 'need_start' => Base::settingFind('system', 'start_home') == 'open',
- 'home_footer' => Base::settingFind('system', 'home_footer')
]);
}
diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php
index 875fb2482..d6a4d7e96 100755
--- a/app/Http/Controllers/Api/UsersController.php
+++ b/app/Http/Controllers/Api/UsersController.php
@@ -759,8 +759,7 @@ class UsersController extends AbstractController
$builder->whereNull('disable_at');
$builder->where('bot', 0);
}
- $builder = $keys['department'] == '0' ? $builder->orderByDesc('userid') : $builder;
- $list = $builder->paginate(Base::getPaginate(50, 20));
+ $list = $builder->orderByDesc('userid')->paginate(Base::getPaginate(50, 20));
//
if ($getCheckinMac) {
$list->transform(function (User $user) use ($getCheckinMac) {
@@ -1733,21 +1732,36 @@ class UsersController extends AbstractController
}
$userBot = UserBot::whereBotId($botUser->userid)->whereUserid($user->userid)->first();
if (empty($userBot)) {
- return Base::retError('不是你的机器人');
+ if (UserBot::systemBotName($botUser->email)) {
+ // 系统机器人(仅限管理员)
+ if (!$user->isAdmin()) {
+ return Base::retError('权限不足');
+ }
+ } else {
+ // 其他用户的机器人(仅限主人)
+ return Base::retError('不是你的机器人');
+ }
}
- return Base::retSuccess('success', [
+ //
+ $data = [
'id' => $botUser->userid,
'name' => $botUser->nickname,
'avatar' => $botUser->userimg,
- 'clear_day' => $userBot->clear_day,
- 'webhook_url' => $userBot->webhook_url,
- ]);
+ 'clear_day' => 0,
+ 'webhook_url' => '',
+ 'system_name' => UserBot::systemBotName($botUser->email),
+ ];
+ if ($userBot) {
+ $data['clear_day'] = $userBot->clear_day;
+ $data['webhook_url'] = $userBot->webhook_url;
+ }
+ return Base::retSuccess('success', $data);
}
/**
* @api {post} api/users/bot/edit 30. 编辑机器人
*
- * @apiDescription 需要token身份,编辑我的机器人信息
+ * @apiDescription 需要token身份,编辑 我的机器人 或 管理员修改系统机器人 信息
* @apiVersion 1.0.0
* @apiGroup users
* @apiName bot__edit
@@ -1755,8 +1769,8 @@ class UsersController extends AbstractController
* @apiParam {Number} id 机器人ID
* @apiParam {String} [name] 机器人名称
* @apiParam {String} [avatar] 机器人头像
- * @apiParam {Number} [clear_day] 清理天数
- * @apiParam {String} [webhook_url] Webhook地址
+ * @apiParam {Number} [clear_day] 清理天数(仅 我的机器人)
+ * @apiParam {String} [webhook_url] Webhook地址(仅 我的机器人)
*
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1773,7 +1787,15 @@ class UsersController extends AbstractController
}
$userBot = UserBot::whereBotId($botUser->userid)->whereUserid($user->userid)->first();
if (empty($userBot)) {
- return Base::retError('不是你的机器人');
+ if (UserBot::systemBotName($botUser->email)) {
+ // 系统机器人(仅限管理员)
+ if (!$user->isAdmin()) {
+ return Base::retError('权限不足');
+ }
+ } else {
+ // 其他用户的机器人(仅限主人)
+ return Base::retError('不是你的机器人');
+ }
}
//
$data = Request::input();
@@ -1802,18 +1824,24 @@ class UsersController extends AbstractController
$botUser->updateInstance($upUser);
$botUser->save();
}
- if ($upBot) {
+ if ($upBot && $userBot) {
$userBot->updateInstance($upBot);
$userBot->save();
}
//
- return Base::retSuccess('修改成功', [
+ $data = [
'id' => $botUser->userid,
'name' => $botUser->nickname,
'avatar' => $botUser->userimg,
- 'clear_day' => $userBot->clear_day,
- 'webhook_url' => $userBot->webhook_url,
- ]);
+ 'clear_day' => 0,
+ 'webhook_url' => '',
+ 'system_name' => UserBot::systemBotName($botUser->email),
+ ];
+ if ($userBot) {
+ $data['clear_day'] = $userBot->clear_day;
+ $data['webhook_url'] = $userBot->webhook_url;
+ }
+ return Base::retSuccess('修改成功', $data);
}
/**
@@ -1823,6 +1851,10 @@ class UsersController extends AbstractController
* @apiGroup users
* @apiName share__list
*
+ * @apiParam {String} [type] 分享类型:file-文件,text-列表 默认file
+ * @apiParam {Number} [pid] 父级文件id,用于获取子目录和上传到指定目录的id
+ * @apiParam {Number} [upload_file_id] 上传文件id
+ *
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
@@ -1830,6 +1862,7 @@ class UsersController extends AbstractController
public function share__list()
{
$user = User::auth();
+ $type = Request::input('type', 'file');
$pid = intval(Request::input('pid', -1));
$uploadFileId = intval(Request::input('upload_file_id', -1));
// 上传文件
@@ -1841,7 +1874,7 @@ class UsersController extends AbstractController
}
// 获取数据
$lists = [];
- if ($pid !== -1) {
+ if ($type == 'file' && $pid !== -1) {
$fileList = (new File)->getFileList($user, $pid, 'dir', false);
foreach ($fileList as $file) {
if ($file['id'] != $pid) {
@@ -1854,15 +1887,16 @@ class UsersController extends AbstractController
];
}
}
-
} else {
- $lists[] = [
- 'type' => 'children',
- 'url' => Base::fillUrl("api/users/share/list") . "?pid=0",
- 'icon' => url("images/file/light/folder.png"),
- 'extend' => ['upload_file_id' => 0],
- 'name' => Doo::translate('文件'),
- ];
+ if($type == 'file'){
+ $lists[] = [
+ 'type' => 'children',
+ 'url' => Base::fillUrl("api/users/share/list") . "?pid=0",
+ 'icon' => url("images/file/light/folder.png"),
+ 'extend' => ['upload_file_id' => 0],
+ 'name' => Doo::translate('文件'),
+ ];
+ }
$dialogList = (new WebSocketDialog)->getDialogList($user->userid);
foreach ($dialogList['data'] as $dialog) {
if ($dialog['avatar']) {
@@ -1881,8 +1915,13 @@ class UsersController extends AbstractController
'type' => 'item',
'name' => $dialog['name'],
'icon' => $avatar,
- 'url' => Base::fillUrl("api/dialog/msg/sendfiles"),
- 'extend' => ['dialog_ids' => $dialog['id']]
+ 'url' => $type == "file" ? Base::fillUrl("api/dialog/msg/sendfiles") : Base::fillUrl("api/dialog/msg/sendtext"),
+ 'extend' => [
+ 'dialog_ids' => $dialog['id'],
+ 'text_type' => 'text',
+ 'reply_id' => 0,
+ 'silence' => 'no'
+ ]
];
}
}
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 3525f34cc..7eb5c1b9a 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -390,6 +390,7 @@ class Project extends AbstractModel
$userids = Base::arrayRetainInt($item['userids'] ?: [], true);
$usertype = trim($item['usertype']);
$userlimit = intval($item['userlimit']);
+ $columnid = intval($item['columnid']);
if ($usertype == 'replace' && empty($userids)) {
throw new ApiException("状态[{$item['name']}]设置错误,设置流转模式时必须填写状态负责人");
}
@@ -411,6 +412,7 @@ class Project extends AbstractModel
'userids' => $userids,
'usertype' => trim($item['usertype']),
'userlimit' => $userlimit,
+ 'columnid' => $columnid,
], [], $isInsert);
if ($flow) {
$ids[] = $flow->id;
@@ -545,7 +547,7 @@ class Project extends AbstractModel
$project->save();
//
if ($flow == 'open') {
- $project->addFlow(Base::json2array('[{"id":-10,"name":"待处理","status":"start","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-11,"name":"进行中","status":"progress","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-12,"name":"待测试","status":"test","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-13,"name":"已完成","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-14,"name":"已取消","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0}]'));
+ $project->addFlow(Base::json2array('[{"id":-10,"name":"待处理","status":"start","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-11,"name":"进行中","status":"progress","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-12,"name":"待测试","status":"test","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-13,"name":"已完成","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-14,"name":"已取消","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0}]'));
}
});
//
diff --git a/app/Models/ProjectFlowItem.php b/app/Models/ProjectFlowItem.php
index e03c78ff7..b1992d53b 100644
--- a/app/Models/ProjectFlowItem.php
+++ b/app/Models/ProjectFlowItem.php
@@ -16,6 +16,7 @@ use App\Module\Base;
* @property array $userids 状态负责人ID
* @property string|null $usertype 流转模式
* @property int|null $userlimit 限制负责人
+ * @property int|null $columnid 对应的项目列表
* @property int|null $sort 排序
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
@@ -23,6 +24,7 @@ use App\Module\Base;
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem query()
+ * @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereColumnid($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereFlowId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereId($value)
diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php
index 41e28dc74..a4fe40b35 100644
--- a/app/Models/ProjectTask.php
+++ b/app/Models/ProjectTask.php
@@ -363,15 +363,16 @@ class ProjectTask extends AbstractModel
$content = $data['content'];
$times = $data['times'];
$owner = $data['owner'];
- $add_assist = intval($data['add_assist']);
+ $add_assist = intval($data['add_assist']); // 将自己添加到参与者
+ $assist = $data['assist']; // 参与者,此项设置时 add_assist 无效
$subtasks = $data['subtasks'];
$p_level = intval($data['p_level']);
$p_name = $data['p_name'];
$p_color = $data['p_color'];
$top = intval($data['top']);
$userid = User::userid();
- $is_all_visible = $data['visibility_appoint'];
- $visibility_userids = $data['visibility_appointor'];
+ $is_all_visible = isset($data['visibility_appoint']) ? $data['visibility_appoint'] : $data['is_all_visible'];
+ $visibility_userids = $data['visibility_appointor'] ?: [];
//
if (ProjectTask::whereProjectId($project_id)
->whereNull('project_tasks.complete_at')
@@ -400,7 +401,7 @@ class ProjectTask extends AbstractModel
'p_level' => $p_level,
'p_name' => $p_name,
'p_color' => $p_color,
- 'is_all_visible' => $is_all_visible
+ 'is_all_visible' => $is_all_visible ?: 1
]);
if ($content) {
$task->desc = Base::getHtml($content, 100);
@@ -438,9 +439,12 @@ class ProjectTask extends AbstractModel
}
$owner = $tmpArray;
// 协助人员
- $assist = [];
- if (!in_array($userid, $owner) && $add_assist) {
- $assist = [$userid];
+ $assist = is_array($assist) ? $assist : [];
+ if (empty($assist)) {
+ // 添加自己
+ if (!in_array($userid, $owner) && $add_assist) {
+ $assist = [$userid];
+ }
}
// 创建人
$task->userid = $userid;
@@ -614,6 +618,9 @@ class ProjectTask extends AbstractModel
$data['assist'] = array_values(array_unique(array_diff($data['assist'], $data['owner'])));
}
}
+ if ($newFlowItem->columnid && ProjectColumn::whereProjectId($this->project_id)->whereId($newFlowItem->columnid)->exists()) {
+ $data['column_id'] = $newFlowItem->columnid;
+ }
$this->flow_item_id = $newFlowItem->id;
$this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name;
$this->addLog("修改{任务}状态", [
@@ -718,6 +725,7 @@ class ProjectTask extends AbstractModel
if (Arr::exists($data, 'is_all_visible') || Arr::exists($data, 'visibility_appointor')) {
if (Arr::exists($data, 'is_all_visible')) {
ProjectTask::whereId($data['task_id'])->update(['is_all_visible' => $data["is_all_visible"]]);
+ ProjectTask::whereParentId($data['task_id'])->update(['is_all_visible' => $data["is_all_visible"]]);
}
ProjectTaskUser::whereTaskId($data['task_id'])->whereOwner(2)->delete();
if (Arr::exists($data, 'visibility_appointor')) {
diff --git a/app/Models/User.php b/app/Models/User.php
index 257e075ed..0ecd87e81 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -167,7 +167,7 @@ class User extends AbstractModel
if (!$this->bot) {
return 0;
}
- $key = "getBotOwner::" . $this->userid;
+ $key = "userBotOwner::" . $this->userid;
return Cache::remember($key, now()->addMonth(), function() {
return intval(UserBot::whereBotId($this->userid)->value('userid')) ?: $this->userid;
});
@@ -535,6 +535,10 @@ class User extends AbstractModel
return url("images/avatar/default_approval.png");
case 'okr-alert@bot.system':
return url("images/avatar/default_task.png");
+ case 'ai-openai@bot.system':
+ return url("images/avatar/default_openai.png");
+ case 'ai-claude@bot.system':
+ return url("images/avatar/default_claude.png");
case 'bot-manager@bot.system':
return url("images/avatar/default_bot.png");
}
@@ -628,6 +632,7 @@ class User extends AbstractModel
$update['nickname'] = '机器人管理';
break;
}
+ $update['nickname'] = UserBot::systemBotName($email);
}
if ($update) {
$botUser->updateInstance($update);
diff --git a/app/Models/UserBot.php b/app/Models/UserBot.php
index 9444711fc..5c4deaf1b 100644
--- a/app/Models/UserBot.php
+++ b/app/Models/UserBot.php
@@ -2,6 +2,7 @@
namespace App\Models;
+use App\Module\Base;
use App\Module\Doo;
use App\Module\Extranet;
use Cache;
@@ -36,6 +37,31 @@ use Carbon\Carbon;
class UserBot extends AbstractModel
{
+ /**
+ * 系统机器人名称
+ * @param $name string 邮箱 或 邮箱前缀
+ * @return string
+ */
+ public static function systemBotName($name)
+ {
+ if (str_contains($name, "@")) {
+ $name = explode("@", $name)[0];
+ }
+ return match ($name) {
+ 'system-msg' => '系统消息',
+ 'task-alert' => '任务提醒',
+ 'check-in' => '签到打卡',
+ 'anon-msg' => '匿名消息',
+ 'approval-alert' => '审批',
+ 'ai-openai' => 'ChatGPT',
+ 'ai-claude' => 'Claude',
+ 'ai-wenxin' => '文心一言',
+ 'ai-qianwen' => '通义千问',
+ 'bot-manager' => '机器人管理',
+ default => '', // 不是系统机器人时返回空(也可以拿来判断是否是系统机器人)
+ };
+ }
+
/**
* 机器人菜单
* @param $email
@@ -47,7 +73,7 @@ class UserBot extends AbstractModel
'check-in@bot.system' => [
[
'key' => 'checkin',
- 'label' => Doo::translate('我要签到')
+ 'label' => Doo::translate('我要打卡')
], [
'key' => 'it',
'label' => Doo::translate('IT资讯')
@@ -104,11 +130,139 @@ class UserBot extends AbstractModel
}
Cache::put("UserBot::checkinBotQuickMsg:{$userid}", "yes", Carbon::now()->addSecond());
//
- $text = match ($command) {
- "checkin" => "暂未开放手动签到。",
- default => Extranet::checkinBotQuickMsg($command),
- };
- return $text ?: '维护中...';
+ if ($command === 'checkin') {
+ $setting = Base::setting('checkinSetting');
+ if ($setting['open'] !== 'open') {
+ return '暂未开启签到功能。';
+ }
+ if (!in_array('manual', $setting['modes'])) {
+ return '暂未开放手动签到。';
+ }
+ if ($error = UserBot::checkinBotCheckin($userid, Base::time(), true)) {
+ return $error;
+ }
+ return null;
+ } else {
+ return Extranet::checkinBotQuickMsg($command);
+ }
+ }
+
+ /**
+ * 签到机器人签到
+ * @param $mac
+ * @param $time
+ * @param bool $alreadyTip 签到过是否提示
+ * @return string|null 返回string表示错误信息,返回null表示签到成功
+ */
+ public static function checkinBotCheckin($mac, $time, $alreadyTip = false)
+ {
+ $setting = Base::setting('checkinSetting');
+ $times = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
+ $advance = (intval($setting['advance']) ?: 120) * 60;
+ $delay = (intval($setting['delay']) ?: 120) * 60;
+ //
+ $nowDate = date("Y-m-d");
+ $nowTime = date("H:i:s");
+ //
+ $timeStart = strtotime("{$nowDate} {$times[0]}");
+ $timeEnd = strtotime("{$nowDate} {$times[1]}");
+ $timeAdvance = max($timeStart - $advance, strtotime($nowDate));
+ $timeDelay = min($timeEnd + $delay, strtotime("{$nowDate} 23:59:59"));
+ if (Base::time() < $timeAdvance || $timeDelay < Base::time()) {
+ return "不在有效时间内,有效时间为:" . date("H:i", $timeAdvance) . "-" . date("H:i", $timeDelay);
+ }
+ //
+ $macs = explode(",", $mac);
+ $checkins = [];
+ foreach ($macs as $mac) {
+ $mac = strtoupper($mac);
+ $array = [];
+ if (Base::isMac($mac)) {
+ if ($UserCheckinMac = UserCheckinMac::whereMac($mac)->first()) {
+ $array = [
+ 'userid' => $UserCheckinMac->userid,
+ 'mac' => $UserCheckinMac->mac,
+ 'date' => $nowDate,
+ ];
+ $checkins[] = [
+ 'userid' => $UserCheckinMac->userid,
+ 'remark' => $UserCheckinMac->remark,
+ ];
+ }
+ } elseif (Base::isNumber($mac)) {
+ if ($UserInfo = User::whereUserid($mac)->whereBot(0)->first()) {
+ $array = [
+ 'userid' => $UserInfo->userid,
+ 'mac' => '00:00:00:00:00:00',
+ 'date' => $nowDate,
+ ];
+ $checkins[] = [
+ 'userid' => $UserInfo->userid,
+ 'remark' => '手动签到',
+ ];
+ }
+ }
+ if ($array) {
+ $record = UserCheckinRecord::where($array)->first();
+ if (empty($record)) {
+ $record = UserCheckinRecord::createInstance($array);
+ }
+ $record->times = Base::array2json(array_merge($record->times, [$nowTime]));
+ $record->report_time = $time;
+ $record->save();
+ }
+ }
+ //
+ if ($checkins && $botUser = User::botGetOrCreate('check-in')) {
+ $getJokeSoup = function($type) {
+ $pre = $type == "up" ? "每日开心:" : "心灵鸡汤:";
+ $key = $type == "up" ? "JokeSoupTask:jokes" : "JokeSoupTask:soups";
+ $array = Base::json2array(Cache::get($key));
+ if ($array) {
+ $item = $array[array_rand($array)];
+ if ($item) {
+ return $pre . $item;
+ }
+ }
+ return null;
+ };
+ $sendMsg = function($type, $checkin) use ($alreadyTip, $getJokeSoup, $botUser, $nowDate) {
+ $cacheKey = "Checkin::sendMsg-{$nowDate}-{$type}:" . $checkin['userid'];
+ $typeDesc = $type == "up" ? "上班" : "下班";
+ if (Cache::get($cacheKey) === "yes") {
+ if ($alreadyTip && $dialog = WebSocketDialog::checkUserDialog($botUser, $checkin['userid'])) {
+ $text = "今日已{$typeDesc}打卡,无需重复打卡。
";
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, $type != "up");
+ }
+ return;
+ }
+ Cache::put($cacheKey, "yes", Carbon::now()->addDay());
+ //
+ if ($dialog = WebSocketDialog::checkUserDialog($botUser, $checkin['userid'])) {
+ $hi = date("H:i");
+ $remark = $checkin['remark'] ? " ({$checkin['remark']})": "";
+ $text = "{$typeDesc}打卡成功,打卡时间: {$hi}{$remark}
";
+ $suff = $getJokeSoup($type);
+ if ($suff) {
+ $text = "{$text}----------
{$suff}
";
+ }
+ WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, $type != "up");
+ }
+ };
+ if ($timeAdvance <= Base::time() && Base::time() < $timeEnd) {
+ // 上班打卡通知(从最早打卡时间 到 下班打卡时间)
+ foreach ($checkins as $checkin) {
+ $sendMsg('up', $checkin);
+ }
+ }
+ if ($timeEnd <= Base::time() && Base::time() <= $timeDelay) {
+ // 下班打卡通知(下班打卡时间 到 最晚打卡时间)
+ foreach ($checkins as $checkin) {
+ $sendMsg('down', $checkin);
+ }
+ }
+ }
+ return null;
}
/**
diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php
index 0850c101a..035f4d92a 100644
--- a/app/Models/WebSocketDialog.php
+++ b/app/Models/WebSocketDialog.php
@@ -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) {
@@ -193,6 +193,7 @@ class WebSocketDialog extends AbstractModel
$this->has_image = $msgBuilder->clone()->whereMtype('image')->exists();
$this->has_file = $msgBuilder->clone()->whereMtype('file')->exists();
$this->has_link = $msgBuilder->clone()->whereLink(1)->exists();
+ $this->has_todo = $msgBuilder->clone()->where('todo', '>', 0)->exists();
}
return $this;
}
@@ -389,7 +390,7 @@ class WebSocketDialog extends AbstractModel
case 'all':
throw new ApiException('当前会话全员禁言');
case 'user':
- if (!User::find($userid)?->checkAdmin()) {
+ if (!User::find($userid)?->isAdmin()) {
throw new ApiException('当前会话禁言');
}
}
@@ -555,7 +556,7 @@ class WebSocketDialog extends AbstractModel
return $dialogUser;
}
if ($receiver > 0 && $user->isTemp()) {
- throw new ApiException('无法发起会话');
+ throw new ApiException('无法发起会话,请联系管理员。');
}
return AbstractModel::transaction(function () use ($receiver, $user) {
$dialog = self::createInstance([
diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php
index 549dbcea5..70f4f099b 100644
--- a/app/Models/WebSocketDialogMsg.php
+++ b/app/Models/WebSocketDialogMsg.php
@@ -641,7 +641,7 @@ class WebSocketDialogMsg extends AbstractModel
}
// 其他网络图片
$imageSaveLocal = Base::settingFind("system", "image_save_local");
- preg_match_all("/
]*?src=([\"'])(.*?\.(png|jpg|jpeg|webp|gif))\\1[^>]*?>/is", $text, $matchs);
+ preg_match_all("/
]*?src=([\"'])(.*?(png|jpg|jpeg|webp|gif).*?)\\1[^>]*?>/is", $text, $matchs);
foreach ($matchs[2] as $key => $str) {
if ($imageSaveLocal === 'close') {
$imageSize = getimagesize($str);
@@ -770,18 +770,19 @@ class WebSocketDialogMsg extends AbstractModel
* 发送消息、修改消息
* @param string $action 动作
* - reply-98:回复消息ID=98
- * - update-99:更新消息ID=99
+ * - update-99:更新消息ID=99(标记修改)
+ * - change-99:更新消息ID=99(不标记修改)
* @param int $dialog_id 会话ID(即 聊天室ID)
* @param string $type 消息类型
* @param array $msg 发送的消息
- * @param int $sender 发送的会员ID(默认自己,0为系统)
+ * @param int|null $sender 发送的会员ID(默认自己,0为系统)
* @param bool $push_self 推送-是否推给自己
* @param bool $push_retry 推送-失败后重试1次(有时候在事务里执行,数据还没生成时会出现找不到消息的情况)
* @param bool|null $push_silence 推送-静默
* - type = [text|file|record|meeting] 默认为:false
* @return array
*/
- public static function sendMsg($action, $dialog_id, $type, $msg, $sender = 0, $push_self = false, $push_retry = false, $push_silence = null)
+ public static function sendMsg($action, $dialog_id, $type, $msg, $sender = null, $push_self = false, $push_retry = false, $push_silence = null)
{
$link = 0;
$mtype = $type;
@@ -809,15 +810,23 @@ class WebSocketDialogMsg extends AbstractModel
}
//
$update_id = preg_match("/^update-(\d+)$/", $action, $match) ? $match[1] : 0;
+ $change_id = preg_match("/^change-(\d+)$/", $action, $match) ? $match[1] : 0;
$reply_id = preg_match("/^reply-(\d+)$/", $action, $match) ? $match[1] : 0;
- $sender = $sender ?: User::userid();
+ $sender = $sender === null ? User::userid() : $sender;
//
$dialog = WebSocketDialog::find($dialog_id);
if (empty($dialog)) {
throw new ApiException('获取会话失败');
}
- $dialog->checkMute($sender);
+ if ($sender > 0) {
+ $dialog->checkMute($sender);
+ }
//
+ $modify = 1;
+ if ($change_id) {
+ $modify = 0;
+ $update_id = $change_id;
+ }
if ($update_id) {
// 修改
$dialogMsg = self::whereId($update_id)->whereDialogId($dialog_id)->first();
@@ -835,12 +844,14 @@ class WebSocketDialogMsg extends AbstractModel
'mtype' => $mtype,
'link' => $link,
'msg' => $msg,
- 'modify' => 1,
+ 'modify' => $modify,
];
$dialogMsg->updateInstance($updateData);
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
//
+ $dialogMsg->msgJoinGroup($dialog);
+ //
$dialog->pushMsg('update', array_merge($updateData, [
'id' => $dialogMsg->id
]));
@@ -887,4 +898,42 @@ class WebSocketDialogMsg extends AbstractModel
return Base::retSuccess('发送成功', $dialogMsg);
}
}
+
+ /**
+ * 将被@的人加入群
+ * @param WebSocketDialog $dialog 对话
+ * @return array
+ */
+ public function msgJoinGroup(WebSocketDialog $dialog)
+ {
+ $updateds = [];
+ $silences = [];
+ foreach ($dialog->dialogUser as $dialogUser) {
+ $updateds[$dialogUser->userid] = $dialogUser->updated_at;
+ $silences[$dialogUser->userid] = $dialogUser->silence;
+ }
+ $userids = array_keys($silences);
+
+ // 提及会员
+ $mentions = [];
+ if ($this->type === 'text') {
+ preg_match_all("//", $this->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, $this->userid])) {
+ $dialog->joinGroup($diffids, $this->userid);
+ $dialog->pushMsg("groupJoin", null, $diffids);
+ $userids = array_values(array_unique(array_merge($mentions, $userids)));
+ }
+ }
+
+ return compact('updateds', 'silences', 'userids', 'mentions');
+ }
}
diff --git a/app/Module/Base.php b/app/Module/Base.php
index 3fa8a4ba1..569c958c6 100755
--- a/app/Module/Base.php
+++ b/app/Module/Base.php
@@ -125,9 +125,9 @@ class Base
* @param $min
* @return bool
*/
- public static function judgeClientVersion($min)
+ public static function judgeClientVersion($min, $clientVersion = null)
{
- return !version_compare(Base::getClientVersion(), $min, '<');
+ return !version_compare($clientVersion ?: Base::getClientVersion(), $min, '<');
}
/**
diff --git a/app/Tasks/AutoArchivedTask.php b/app/Tasks/AutoArchivedTask.php
index 5b817f0ff..8da584f70 100644
--- a/app/Tasks/AutoArchivedTask.php
+++ b/app/Tasks/AutoArchivedTask.php
@@ -36,6 +36,7 @@ class AutoArchivedTask extends AbstractTask
->whereNull('archived_at')
->take(100)
->get();
+ /** @var ProjectTask $task */
foreach ($taskLists AS $task) {
$task->archivedTask(Carbon::now(), true);
}
diff --git a/app/Tasks/BotReceiveMsgTask.php b/app/Tasks/BotReceiveMsgTask.php
index 6feb2f6ac..0c5020185 100644
--- a/app/Tasks/BotReceiveMsgTask.php
+++ b/app/Tasks/BotReceiveMsgTask.php
@@ -23,12 +23,16 @@ class BotReceiveMsgTask extends AbstractTask
{
protected $userid;
protected $msgId;
+ protected $mention;
+ protected $client = [];
- public function __construct($userid, $msgId)
+ public function __construct($userid, $msgId, $mention, $client = [])
{
parent::__construct(...func_get_args());
$this->userid = $userid;
$this->msgId = $msgId;
+ $this->mention = $mention;
+ $this->client = is_array($client) ? $client : [];
}
public function start()
@@ -42,14 +46,6 @@ class BotReceiveMsgTask extends AbstractTask
return;
}
$msg->readSuccess($botUser->userid);
- //
- $dialog = WebSocketDialog::find($msg->dialog_id);
- if (empty($dialog)) {
- return;
- }
- if ($dialog->type !== 'user') {
- return;
- }
$this->botManagerReceive($msg, $botUser);
}
@@ -70,11 +66,28 @@ class BotReceiveMsgTask extends AbstractTask
return;
}
$original = $msg->msg['text'];
+ if ($this->mention) {
+ $original = preg_replace("/(.*?)<\/span>/", "", $original);
+ }
if (preg_match("/]*?data-quick-key=([\"'])(.*?)\\1[^>]*?>(.*?)<\/span>/is", $original, $match)) {
$command = $match[2];
} else {
$command = trim(strip_tags($original));
}
+ //
+ $dialog = WebSocketDialog::find($msg->dialog_id);
+ if (empty($dialog)) {
+ return;
+ }
+ // 推送Webhook
+ if ($command
+ && !str_starts_with($command, '/')
+ && ($dialog->type === 'user' || $this->mention)) {
+ $this->botManagerWebhook($command, $msg, $botUser, $dialog);
+ }
+ if ($dialog->type !== 'user') {
+ return;
+ }
// 签到机器人
if ($botUser->email === 'check-in@bot.system') {
$text = UserBot::checkinBotQuickMsg($command, $msg->userid);
@@ -300,7 +313,7 @@ class BotReceiveMsgTask extends AbstractTask
$nameKey = $isManager ? $array[2] : $array[1];
$data = $this->botManagerOne($botId, $msg->userid);
if ($data) {
- $list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
+ $list = 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', "%{$nameKey}%")
->where('u.userid', $data->userid)
@@ -337,24 +350,127 @@ class BotReceiveMsgTask extends AbstractTask
$text = preg_replace("/^\x20+/", "", $text);
$text = preg_replace("/\n\x20+/", "\n", $text);
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
+ }
+ }
+
+ /**
+ * 机器人处理 Webhook
+ * @param string $command
+ * @param WebSocketDialogMsg $msg
+ * @param User $botUser
+ * @param WebSocketDialog $dialog
+ * @return void
+ */
+ private function botManagerWebhook(string $command, WebSocketDialogMsg $msg, User $botUser, WebSocketDialog $dialog)
+ {
+ $serverUrl = 'http://' . env('APP_IPPR') . '.3';
+ $userBot = null;
+ $extras = [];
+ $error = null;
+ switch ($botUser->email) {
+ // ChatGPT 机器人
+ case 'ai-openai@bot.system':
+ $setting = Base::setting('aibotSetting');
+ $webhookUrl = "{$serverUrl}/ai/openai/send";
+ $extras = [
+ 'openai_key' => $setting['openai_key'],
+ 'openai_agency' => $setting['openai_agency'],
+ 'server_url' => $serverUrl,
+ ];
+ if (empty($extras['openai_key'])) {
+ $error = 'Robot disabled.';
+ } elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
+ && !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
+ $error = 'The client version is low (required version ≥ v0.29.11).';
+ }
+ break;
+ // Claude 机器人
+ case 'ai-claude@bot.system':
+ $setting = Base::setting('aibotSetting');
+ $webhookUrl = "{$serverUrl}/ai/claude/send";
+ $extras = [
+ 'claude_token' => $setting['claude_token'],
+ 'claude_agency' => $setting['claude_agency'],
+ 'server_url' => $serverUrl,
+ ];
+ if (empty($extras['claude_token'])) {
+ $error = 'Robot disabled.';
+ } elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
+ && !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
+ $error = 'The client version is low (required version ≥ v0.29.11).';
+ }
+ break;
+ // Wenxin 机器人
+ case 'ai-wenxin@bot.system':
+ $setting = Base::setting('aibotSetting');
+ $webhookUrl = "{$serverUrl}/ai/wenxin/send";
+ $extras = [
+ 'wenxin_key' => $setting['wenxin_key'],
+ 'wenxin_secret' => $setting['wenxin_secret'],
+ 'wenxin_model' => $setting['wenxin_model'],
+ 'server_url' => $serverUrl,
+ ];
+ if (empty($extras['wenxin_key'])) {
+ $error = 'Robot disabled.';
+ } elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
+ && !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
+ $error = 'The client version is low (required version ≥ v0.29.12).';
+ }
+ break;
+ // QianWen 机器人
+ case 'ai-qianwen@bot.system':
+ $setting = Base::setting('aibotSetting');
+ $webhookUrl = "{$serverUrl}/ai/qianwen/send";
+ $extras = [
+ 'qianwen_key' => $setting['qianwen_key'],
+ 'qianwen_model' => $setting['qianwen_model'],
+ 'server_url' => $serverUrl,
+ ];
+ if (empty($extras['qianwen_key'])) {
+ $error = 'Robot disabled.';
+ } elseif (in_array($this->client['platform'], ['win', 'mac', 'web'])
+ && !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
+ $error = 'The client version is low (required version ≥ v0.29.12).';
+ }
+ break;
+ // 其他机器人
+ default:
+ $userBot = UserBot::whereBotId($botUser->userid)->first();
+ $webhookUrl = $userBot?->webhook_url;
+ break;
+ }
+ if ($error) {
+ WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $error], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
return;
}
- // 推送Webhook
- if ($command) {
- $userBot = UserBot::whereBotId($botUser->userid)->first();
- if ($userBot && preg_match("/^https*:\/\//", $userBot->webhook_url)) {
- Ihttp::ihttp_post($userBot->webhook_url, [
- 'text' => $command,
- 'token' => User::generateToken($botUser),
- 'dialog_id' => $msg->dialog_id,
- 'msg_id' => $msg->id,
- 'msg_uid' => $msg->userid,
- 'bot_uid' => $botUser->userid,
- 'version' => Base::getVersion(),
- ], 10);
+ if (!preg_match("/^https*:\/\//", $webhookUrl)) {
+ return;
+ }
+ //
+ try {
+ $res = Ihttp::ihttp_post($webhookUrl, [
+ 'text' => $command,
+ 'token' => User::generateToken($botUser),
+ 'dialog_id' => $dialog->id,
+ 'dialog_type' => $dialog->type,
+ 'msg_id' => $msg->id,
+ 'msg_uid' => $msg->userid,
+ 'mention' => $this->mention ? 1 : 0,
+ 'bot_uid' => $botUser->userid,
+ 'version' => Base::getVersion(),
+ 'extras' => Base::array2json($extras)
+ ], 10);
+ if ($userBot) {
$userBot->webhook_num++;
$userBot->save();
}
+ if($res['data'] && $data = json_decode($res['data'])){
+ if($data['code'] != 200 && $data['message']){
+ WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $res['data']['message']], $botUser->userid, false, false, true);
+ }
+ }
+ } catch (\Throwable $th) {
+ //throw $th;
}
}
diff --git a/app/Tasks/WebSocketDialogMsgTask.php b/app/Tasks/WebSocketDialogMsgTask.php
index 4433496cd..d8c2dfd0e 100644
--- a/app/Tasks/WebSocketDialogMsgTask.php
+++ b/app/Tasks/WebSocketDialogMsgTask.php
@@ -25,6 +25,7 @@ class WebSocketDialogMsgTask extends AbstractTask
protected $ignoreFd;
protected $msgNotExistRetry = false; // 推送失败后重试
protected $silence = false; // 静默推送(前端不通知、App不推送,如果会话设置了免打扰则强制静默)
+ protected $client = []; // 客户端信息(版本、语言、平台)
protected $endPush = [];
protected $endArray = [];
@@ -38,6 +39,11 @@ class WebSocketDialogMsgTask extends AbstractTask
parent::__construct(...func_get_args());
$this->id = $id;
$this->ignoreFd = $ignoreFd === null ? Request::header('fd') : $ignoreFd;
+ $this->client = [
+ 'version' => Base::headerOrInput('version'),
+ 'language' => Base::headerOrInput('language'),
+ 'platform' => Base::headerOrInput('platform'),
+ ];
}
/**
@@ -85,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("//", $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);
+ $updateds = $msgJoinGroupResult['updateds'];
+ $silences = $msgJoinGroupResult['silences'];
+ $userids = $msgJoinGroupResult['userids'];
+ $mentions = $msgJoinGroupResult['mentions'];
// 推送目标①:会话成员/群成员
$array = [];
@@ -144,13 +130,28 @@ class WebSocketDialogMsgTask extends AbstractTask
// 机器人收到消处理
$botUser = User::whereUserid($userid)->whereBot(1)->first();
if ($botUser) {
- $this->endArray[] = new BotReceiveMsgTask($botUser->userid, $msg->id);
+ $this->endArray[] = new BotReceiveMsgTask($botUser->userid, $msg->id, $mention, $this->client);
}
}
}
// 更新已发送数量
$msg->send = WebSocketDialogMsgRead::whereMsgId($msg->id)->count();
$msg->save();
+ // 没有接收人时通知发送人已读
+ if ($msg->send === 0) {
+ PushTask::push([
+ 'userid' => $msg->userid,
+ 'msg' => [
+ 'type' => 'dialog',
+ 'mode' => 'readed',
+ 'data' => [
+ 'id' => $msg->id,
+ 'read' => $msg->read,
+ 'percentage' => $msg->percentage,
+ ],
+ ]
+ ]);
+ }
// 开始推送消息
$umengUserid = [];
foreach ($array as $item) {
diff --git a/config/app.php b/config/app.php
index 987bbb270..484ade76c 100644
--- a/config/app.php
+++ b/config/app.php
@@ -67,7 +67,7 @@ return [
|
*/
- 'timezone' => 'PRC',
+ 'timezone' => env('TIMEZONE', 'PRC'),
/*
|--------------------------------------------------------------------------
diff --git a/database/migrations/2023_07_27_145646_create_default_robot.php b/database/migrations/2023_07_27_145646_create_default_robot.php
new file mode 100644
index 000000000..563a78d1f
--- /dev/null
+++ b/database/migrations/2023_07_27_145646_create_default_robot.php
@@ -0,0 +1,32 @@
+ 0) {
+ User::botGetOrCreate('ai-openai');
+ User::botGetOrCreate('ai-claude');
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+
+ }
+}
diff --git a/database/migrations/2023_08_05_140046_setting_checkin_modes_value.php b/database/migrations/2023_08_05_140046_setting_checkin_modes_value.php
new file mode 100644
index 000000000..a29473aee
--- /dev/null
+++ b/database/migrations/2023_08_05_140046_setting_checkin_modes_value.php
@@ -0,0 +1,29 @@
+bigInteger('columnid')->nullable()->default(0)->after('userlimit')->comment('对应的项目列表');
+ }
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('project_flow_items', function (Blueprint $table) {
+ $table->dropColumn("columnid");
+ });
+ }
+}
diff --git a/database/migrations/2023_08_10_093619_add_web_socket_dialog_users_color.php b/database/migrations/2023_08_10_093619_add_web_socket_dialog_users_color.php
new file mode 100644
index 000000000..b96e01c0d
--- /dev/null
+++ b/database/migrations/2023_08_10_093619_add_web_socket_dialog_users_color.php
@@ -0,0 +1,36 @@
+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");
+ });
+ }
+}
diff --git a/database/migrations/2023_08_10_233619_repair_project_tasks_is_all_visible.php b/database/migrations/2023_08_10_233619_repair_project_tasks_is_all_visible.php
new file mode 100644
index 000000000..4b7ee6db2
--- /dev/null
+++ b/database/migrations/2023_08_10_233619_repair_project_tasks_is_all_visible.php
@@ -0,0 +1,42 @@
+ 0;
+ ");
+ }
+
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+
+ }
+}
diff --git a/database/seeders/WebSocketDialogsTableSeeder.php b/database/seeders/WebSocketDialogsTableSeeder.php
index e87c9f7c2..8d52cce1b 100644
--- a/database/seeders/WebSocketDialogsTableSeeder.php
+++ b/database/seeders/WebSocketDialogsTableSeeder.php
@@ -232,6 +232,8 @@ class WebSocketDialogsTableSeeder extends Seeder
$dialog->save();
}
}
+ User::botGetOrCreate('ai-openai');
+ User::botGetOrCreate('ai-claude');
$userids = User::whereBot(0)->whereNull('disable_at')->pluck('userid')->toArray();
WebSocketDialog::createGroup("全体成员 All members", $userids, 'all');
diff --git a/docker-compose.yml b/docker-compose.yml
index 8aca0ef2d..af8bab4ef 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,7 +3,7 @@ version: '3'
services:
php:
container_name: "dootask-php-${APP_ID}"
- image: "kuaifan/php:swoole-8.0.rc10"
+ image: "kuaifan/php:swoole-8.0.rc14"
shm_size: "2gb"
ulimits:
core:
@@ -49,6 +49,7 @@ services:
- drawio-webapp
- drawio-export
- minder
+ - ai
restart: unless-stopped
redis:
@@ -101,8 +102,7 @@ services:
fileview:
container_name: "dootask-fileview-${APP_ID}"
- image: "kuaifan/fileview:4.1.0-SNAPSHOT-RC20"
- platform: linux/amd64
+ image: "kuaifan/fileview:4.2.0-SNAPSHOT-RC22"
environment:
KK_CONTEXT_PATH: "/fileview"
KK_OFFICE_PREVIEW_SWITCH_DISABLED: true
@@ -131,8 +131,7 @@ services:
drawio-export:
container_name: "dootask-drawio-export-${APP_ID}"
- image: "kuaifan/export-server:latest"
- platform: linux/amd64
+ image: "kuaifan/export-server:0.0.1"
networks:
extnetwork:
ipv4_address: "${APP_IPPR}.9"
@@ -150,9 +149,9 @@ services:
approve:
container_name: "dootask-approve-${APP_ID}"
- image: "hitosea2020/go-approve:latest"
+ image: "hitosea2020/go-approve:0.1.4"
environment:
- TZ: "Asia/Shanghai"
+ TZ: "${TIMEZONE:-PRC}"
MYSQL_HOST: "${DB_HOST}"
MYSQL_PORT: "${DB_PORT}"
MYSQL_DBNAME: "${DB_DATABASE}"
@@ -185,6 +184,13 @@ services:
- mariadb
restart: unless-stopped
+ ai:
+ container_name: "dootask-ai-${APP_ID}"
+ image: "kuaifan/dooai:0.0.4"
+ networks:
+ extnetwork:
+ ipv4_address: "${APP_IPPR}.12"
+ restart: unless-stopped
networks:
extnetwork:
diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf
index 0ac73532f..c8bd26e71 100644
--- a/docker/nginx/default.conf
+++ b/docker/nginx/default.conf
@@ -189,6 +189,14 @@ server {
proxy_pass http://okr:5566/apps/okr/;
}
+ # AI
+ location /ai/ {
+ proxy_http_version 1.1;
+ proxy_set_header Scheme $scheme;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_pass http://ai:8881/;
+ }
}
include /etc/nginx/conf.d/conf.d/*.conf;
diff --git a/electron/electron-menu.js b/electron/electron-menu.js
new file mode 100644
index 000000000..1cc656588
--- /dev/null
+++ b/electron/electron-menu.js
@@ -0,0 +1,168 @@
+const {
+ clipboard,
+ nativeImage,
+ Menu,
+ MenuItem,
+ dialog,
+ shell,
+} = require('electron')
+const fs = require('fs')
+const url = require('url')
+const request = require("request");
+
+const MAILTO_PREFIX = "mailto:";
+
+const PERMITTED_URL_SCHEMES = ["http:", "https:", MAILTO_PREFIX];
+
+const electronMenu = {
+ language: {
+ openInBrowser: "在浏览器中打开",
+ saveImageAs: "图片存储为...",
+ copyImage: "复制图片",
+ copyEmailAddress: "复制电子邮件地址",
+ copyLinkAddress: "复制链接地址",
+ copyImageAddress: "复制图片地址",
+ failedToSaveImage: "图片保存失败",
+ theImageFailedToSave: "图片无法保存",
+ },
+
+ setLanguage(language) {
+ this.language = Object.assign(this.language, language);
+ },
+
+ safeOpenURL(target) {
+ const parsedUrl = url.parse(target);
+ if (PERMITTED_URL_SCHEMES.includes(parsedUrl.protocol)) {
+ const newTarget = url.format(parsedUrl);
+ shell.openExternal(newTarget).then(r => {
+ });
+ }
+ },
+
+ isBlob(url) {
+ return url.startsWith("blob:");
+ },
+
+ isDataUrl(url) {
+ return url.startsWith("data:");
+ },
+
+ isBlobOrDataUrl(url) {
+ return electronMenu.isBlob(url) || electronMenu.isDataUrl(url);
+ },
+
+ async saveImageAs(url, params) {
+ const targetFileName = params.suggestedFilename || params.altText || "image.png";
+ const {filePath} = await dialog.showSaveDialog({
+ defaultPath: targetFileName,
+ });
+
+ if (!filePath) return; // user cancelled dialog
+
+ try {
+ if (electronMenu.isBlobOrDataUrl(url)) {
+ await electronMenu.writeNativeImage(filePath, nativeImage.createFromDataURL(url));
+ } else {
+ const writeStream = fs.createWriteStream(filePath)
+ const readStream = request(url)
+ readStream.pipe(writeStream);
+ readStream.on('end', function (response) {
+ writeStream.end();
+ });
+ }
+ } catch (err) {
+ await dialog.showMessageBox({
+ type: "error",
+ title: electronMenu.language.failedToSaveImage,
+ message: electronMenu.language.theImageFailedToSave,
+ });
+ }
+ },
+
+ writeNativeImage(filePath, img) {
+ switch (filePath.split(".").pop()?.toLowerCase()) {
+ case "jpg":
+ case "jpeg":
+ return fs.promises.writeFile(filePath, img.toJPEG(100));
+ case "bmp":
+ return fs.promises.writeFile(filePath, img.toBitmap());
+ case "png":
+ default:
+ return fs.promises.writeFile(filePath, img.toPNG());
+ }
+ },
+
+ webContentsMenu(webContents) {
+ webContents.on("context-menu", function (e, params) {
+ if (params.linkURL || params.srcURL) {
+ const url = params.linkURL || params.srcURL;
+ const popupMenu = new Menu();
+
+ if (!electronMenu.isBlobOrDataUrl(url)) {
+ popupMenu.append(
+ new MenuItem({
+ label: electronMenu.language.openInBrowser,
+ accelerator: "o",
+ click() {
+ electronMenu.safeOpenURL(url);
+ },
+ }),
+ );
+ }
+
+ if (params.hasImageContents) {
+ if (!electronMenu.isBlob(url)) {
+ popupMenu.append(
+ new MenuItem({
+ label: electronMenu.language.saveImageAs,
+ accelerator: "s",
+ click: async function () {
+ await electronMenu.saveImageAs(url, params);
+ },
+ }),
+ );
+ }
+ popupMenu.append(
+ new MenuItem({
+ label: electronMenu.language.copyImage,
+ accelerator: "c",
+ click() {
+ webContents.copyImageAt(params.x, params.y);
+ },
+ }),
+ );
+ }
+
+ if (!electronMenu.isBlobOrDataUrl(url)) {
+ if (url.startsWith(MAILTO_PREFIX)) {
+ popupMenu.append(
+ new MenuItem({
+ label: electronMenu.language.copyEmailAddress,
+ accelerator: "a",
+ click() {
+ clipboard.writeText(url.substring(MAILTO_PREFIX.length));
+ },
+ }),
+ );
+ } else {
+ popupMenu.append(
+ new MenuItem({
+ label: params.hasImageContents ? electronMenu.language.copyImageAddress : electronMenu.language.copyLinkAddress,
+ accelerator: "a",
+ click() {
+ clipboard.writeText(url);
+ },
+ }),
+ );
+ }
+ }
+
+ if (popupMenu.items.length > 0) {
+ popupMenu.popup({});
+ e.preventDefault();
+ }
+ }
+ })
+ }
+}
+module.exports = electronMenu;
diff --git a/electron/electron.js b/electron/electron.js
index b79306dd4..7ea6eb7d6 100644
--- a/electron/electron.js
+++ b/electron/electron.js
@@ -11,6 +11,7 @@ const crc = require('crc');
const zlib = require('zlib');
const utils = require('./utils');
const config = require('./package.json');
+const electronMenu = require("./electron-menu");
const spawn = require("child_process").spawn;
const isMac = process.platform === 'darwin'
@@ -58,6 +59,7 @@ function createMainWindow() {
openExternal(url)
return {action: 'deny'}
})
+ electronMenu.webContentsMenu(mainWindow.webContents)
if (devloadUrl) {
mainWindow.loadURL(devloadUrl).then(_ => {
@@ -77,7 +79,7 @@ function createMainWindow() {
mainWindow.on('close', event => {
if (!willQuitApp) {
- utils.onBeforeUnload(event).then(() => {
+ utils.onBeforeUnload(event, mainWindow).then(() => {
if (process.platform === 'win32') {
mainWindow.hide()
} else if (process.platform === 'darwin') {
@@ -137,8 +139,8 @@ function createSubWindow(args) {
browser.on('close', event => {
if (!willQuitApp) {
- utils.onBeforeUnload(event).then(() => {
- event.sender.destroy()
+ utils.onBeforeUnload(event, browser).then(() => {
+ browser.destroy()
})
}
})
@@ -158,20 +160,58 @@ function createSubWindow(args) {
openExternal(url)
return {action: 'deny'}
})
+ electronMenu.webContentsMenu(browser.webContents)
+ const hash = args.hash || args.path;
if (devloadUrl) {
- browser.loadURL(devloadUrl + '#' + (args.hash || args.path)).then(_ => {
+ browser.loadURL(devloadUrl + '#' + hash).then(_ => {
})
} else {
browser.loadFile('./public/index.html', {
- hash: args.hash || args.path
+ hash
}).then(_ => {
})
}
}
+/**
+ * 更新子窗口
+ * @param browser
+ * @param args
+ */
+function updateSubWindow(browser, args) {
+ if (!args) {
+ return;
+ }
+
+ if (!utils.isJson(args)) {
+ args = {path: args, name: null}
+ }
+
+ const hash = args.hash || args.path;
+ if (hash) {
+ if (devloadUrl) {
+ browser.loadURL(devloadUrl + '#' + hash).then(_ => {
+
+ })
+ } else {
+ browser.loadFile('./public/index.html', {
+ hash
+ }).then(_ => {
+
+ })
+ }
+ }
+ if (args.name) {
+ const er = subWindow.find(item => item.browser == browser);
+ if (er) {
+ er.name = args.name;
+ }
+ }
+}
+
const getTheLock = app.requestSingleInstanceLock()
if (!getTheLock) {
app.quit()
@@ -257,6 +297,17 @@ app.on('browser-window-focus', () => {
}
})
+/**
+ * 设置菜单语言包
+ * @param args {path}
+ */
+ipcMain.on('setMenuLanguage', (event, args) => {
+ if (utils.isJson(args)) {
+ electronMenu.setLanguage(args)
+ }
+ event.returnValue = "ok"
+})
+
/**
* 打开文件
* @param args {path}
@@ -283,6 +334,16 @@ ipcMain.on('windowRouter', (event, args) => {
event.returnValue = "ok"
})
+/**
+ * 更新路由窗口
+ * @param args {?name, ?path} // name: 不是要更改的窗口名,是要把窗口名改成什么, path: 地址
+ */
+ipcMain.on('updateRouter', (event, args) => {
+ const browser = BrowserWindow.fromWebContents(event.sender);
+ updateSubWindow(browser, args)
+ event.returnValue = "ok"
+})
+
/**
* 隐藏窗口(mac、win隐藏,其他关闭)
*/
@@ -475,6 +536,28 @@ ipcMain.on('copyBase64Image', (event, args) => {
event.returnValue = "ok"
})
+/**
+ * 复制图片根据坐标
+ * @param args
+ */
+ipcMain.on('copyImageAt', (event, args) => {
+ try {
+ event.sender.copyImageAt(args.x, args.y);
+ } catch (e) {
+ // log.error(e)
+ }
+ event.returnValue = "ok"
+})
+
+/**
+ * 保存图片
+ * @param args
+ */
+ipcMain.on('saveImageAt', async (event, args) => {
+ await electronMenu.saveImageAs(args.url, args.params)
+ event.returnValue = "ok"
+})
+
/**
* 绑定截图快捷键
* @param args
diff --git a/electron/package.json b/electron/package.json
index 0292891fe..00348e3ca 100644
--- a/electron/package.json
+++ b/electron/package.json
@@ -26,14 +26,14 @@
"url": "https://github.com/kuaifan/dootask.git"
},
"devDependencies": {
- "@electron-forge/cli": "^6.1.1",
- "@electron-forge/maker-deb": "^6.1.1",
- "@electron-forge/maker-rpm": "^6.1.1",
- "@electron-forge/maker-squirrel": "^6.1.1",
- "@electron-forge/maker-zip": "^6.1.1",
+ "@electron-forge/cli": "^6.3.0",
+ "@electron-forge/maker-deb": "^6.3.0",
+ "@electron-forge/maker-rpm": "^6.3.0",
+ "@electron-forge/maker-squirrel": "^6.3.0",
+ "@electron-forge/maker-zip": "^6.3.0",
"dotenv": "^16.0.3",
- "electron": "^24.5.0",
- "electron-builder": "^23.6.0",
+ "electron": "^25.5.0",
+ "electron-builder": "^24.6.3",
"electron-notarize": "^1.2.2",
"form-data": "^4.0.0",
"ora": "^4.1.1"
@@ -44,9 +44,10 @@
"electron-log": "^4.4.8",
"electron-screenshots-tool": "^1.0.4",
"electron-squirrel-startup": "^1.0.0",
- "electron-updater": "^5.3.0",
+ "electron-updater": "^6.1.1",
"fs-extra": "^10.1.0",
- "pdf-lib": "^1.17.1"
+ "pdf-lib": "^1.17.1",
+ "request": "^2.88.2"
},
"trayIcon": {
"dev": {
@@ -67,6 +68,7 @@
},
"files": [
"public/**/*",
+ "electron-menu.js",
"electron-preload.js",
"electron.js",
"utils.js"
diff --git a/electron/utils.js b/electron/utils.js
index b12fba35b..56dc48a33 100644
--- a/electron/utils.js
+++ b/electron/utils.js
@@ -291,12 +291,11 @@ module.exports = {
*/
onBeforeUnload(event, app) {
return new Promise(resolve => {
- const sender = event.sender
- const contents = sender.webContents
+ const contents = app.webContents
if (contents != null) {
contents.executeJavaScript('if(typeof window.__onBeforeUnload === \'function\'){window.__onBeforeUnload()}', true).then(options => {
if (this.isJson(options)) {
- let choice = dialog.showMessageBoxSync(sender, options)
+ let choice = dialog.showMessageBoxSync(app, options)
if (choice === 1) {
contents.executeJavaScript('if(typeof window.__removeBeforeUnload === \'function\'){window.__removeBeforeUnload()}', true).catch(() => {});
resolve()
diff --git a/language/original-api.txt b/language/original-api.txt
index 042f4471a..5d4e65da4 100644
--- a/language/original-api.txt
+++ b/language/original-api.txt
@@ -391,7 +391,7 @@ error
个人项目
注册时系统自动创建项目,你可以自由删除。
-无法发起会话
+无法发起会话,请联系管理员。
无法创建群组
无法查看联系电话
@@ -410,6 +410,7 @@ error
匿名消息
系统管理员
我要签到
+我要打卡
关键词不能为空
@@ -470,3 +471,4 @@ Api接口文档
找不到符合条件的子节点
文件
+未完成
diff --git a/language/original-web.txt b/language/original-web.txt
index 0b591f57e..515e8cbfd 100644
--- a/language/original-web.txt
+++ b/language/original-web.txt
@@ -95,7 +95,7 @@
仅限项目负责人操作
任务描述,回车创建
你好,扫码确认登录
-你确定要登出系统?
+你确定要登出系统吗?
你确认领取任务吗?
列表名称,回车创建
同步修改子任务时间
@@ -1251,4 +1251,121 @@ Markdown 格式发送
会议组件加载失败!
OKR管理
-OKR结果分析
\ No newline at end of file
+OKR结果分析
+计划时间冲突提示
+忽略并继续
+你确定要清除缓存吗?
+
+可见性选项
+保持
+保持:任务详情页可见性选项保持显示。
+自动:默认值情况下显示在合并项目,设置时保持显示。
+修改子任务时间
+请输入修改备注
+
+任务默认时间
+请选择默认时间
+添加任务计划时间默认时分。
+
+导出审批数据
+审批类型
+导出类型
+未完成
+AI机器人
+任务相关
+请填写名称!
+访问OpenAI网站查看:
+使用代理
+支持 http 或 socks 代理
+例如:http://proxy.com 或 socks5://proxy.com
+查看 Cookie 中的 sessionKey 便是
+
+暂不支持拖拽文件夹。
+暂不支持粘贴文件夹。
+暂不支持拖拽文件夹,请手动上传文件夹。
+暂不支持粘贴文件夹,请手动上传文件夹。
+
+最近
+暂无结果
+新项目负责人
+恢复帐号(已离职)
+你确定恢复已离职帐号【(*)】吗?(注:此操作仅恢复帐号状态,无法恢复操作离职时移交的数据)
+流转到【(*)】时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。
+删除文件夹
+你确定要删除【(*)】文件夹吗?
+请输入新的密码
+请输入新的邮箱((*))
+
+截图快捷键
+新项目负责人
+暂无结果
+最近
+加班
+离职申请
+录用申请
+
+修改的内容尚未保存,确定要放弃修改吗?
+获取方式
+查看说明
+正在修改系统机器人:(*)
+复制选择
+复制全部
+返回首页
+刷新链接
+确认
+还原归档
+签到方式
+手动签到
+定位签到
+详情看下文安装说明
+通过在签到打卡机器人发送指令签到
+通过在签到打卡机器人发送位置签到
+状态设置
+关联列表
+选择关联列表
+取消关联
+流转到【(*)】时自动将任务移动至关联列表。
+状态[(*)]设置错误,设置剔除模式时必须填写状态负责人
+
+键盘设置
+
+在浏览器中打开
+图片存储为...
+复制图片
+复制电子邮件地址
+复制链接地址
+复制图片地址
+图片保存失败
+图片无法保存
+
+事项
+
+编辑描述
+没有任何修改!
+项目类型
+团队项目
+个人项目
+全部项目
+打开链接
+
+
+汇报人
+提交时间
+
+应用
+机器人设置
+去聊天
+返回
+会议设置
+我是一个人工智能助手,为用户提供问题解答和指导。我没有具体的身份,只是一个程序。您有什么问题可以问我哦?
+我是Claude,一个由Anthropic公司创造出来的AI助手机器人。我的工作是帮助人类,与人对话并给出解答。
+我是文心一言,英文名是ERNIE Bot。我能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。
+我是达摩院自主研发的超大规模语言模型,能够回答问题、创作文字,还能表达观点、撰写代码。
+机器人暂未开启
+创建一个全新的会议视频会议,与会者可以在实时中进行面对面的视听交流。通过视频会议平台,参与者可以分享屏幕、共享文档,并与其他与会人员进行讨论和协。
+加入视频会议,参与已经创建的会议,在会议过程中与其他参会人员进行远程实时视听交流和协作。
+新会议
+新建会议
+LDAP设置
+邮件管理
+APP推送
diff --git a/language/translate.json b/language/translate.json
index f75e71586..e01eeecc3 100644
--- a/language/translate.json
+++ b/language/translate.json
@@ -12004,11 +12004,11 @@
"key": "待办",
"zh": "",
"zh-CHT": "待辦",
- "en": "To be done",
+ "en": "Todo",
"ko": "할 일",
"ja": "待办",
- "de": "Unbesiegbar",
- "fr": "Le pense-bête",
+ "de": "Todo",
+ "fr": "Todo",
"id": "Harus"
},
{
@@ -12018,7 +12018,7 @@
"en": "Required",
"ko": "필수",
"ja": "必ず埋め",
- "de": "Sie müssen sie füllen.",
+ "de": "Required",
"fr": "Qu’il remplit",
"id": "Akan mengisi"
},
@@ -16905,5 +16905,907 @@
"de": "Wenn die ergebnisse umgekehrt sind",
"fr": "Analyse des résultats OkR",
"id": "Analisis hasil OkR"
+ },
+ {
+ "key": "以下人员已存在任务",
+ "zh": "",
+ "zh-CHT": "以下人員已存在任務",
+ "en": "The following personnel already have tasks",
+ "ko": "다음 구성원에 대한 작업이 이미 있습니다",
+ "ja": "次の者には任務があります",
+ "de": "Die folgenden personen haben den auftrag erledigt",
+ "fr": "Les personnes suivantes ont déjà une mission",
+ "id": "Yang berikut ini ada tugas"
+ },
+ {
+ "key": "你确定要登出系统吗?",
+ "zh": "",
+ "zh-CHT": "你確定要登出系統嗎?",
+ "en": "You sure you want to log out of the system?",
+ "ko": "시스템에서 로그아웃하시겠습니까?",
+ "ja": "システムをログアウトするのは確かですか?",
+ "de": "Willst du dich sicher ins system einloggen?",
+ "fr": "Êtes-vous sûr de vouloir vous déconnecter du système?",
+ "id": "Kau yakin ingin keluar dari sistem?"
+ },
+ {
+ "key": "计划时间冲突提示",
+ "zh": "",
+ "zh-CHT": "計劃時間衝突提示",
+ "en": "Schedule time conflict prompt",
+ "ko": "예약된 시간 알림 알림",
+ "ja": "予定時間の衝突のヒントです",
+ "de": "Geplante zeitkollisionen sind unvermeidlich",
+ "fr": "Conseils de conflit de temps de planification",
+ "id": "Merencanakan benturan waktu"
+ },
+ {
+ "key": "忽略并继续",
+ "zh": "",
+ "zh-CHT": "忽略並繼續",
+ "en": "Ignore and continue",
+ "ko": "무시하고 계속 진행",
+ "ja": "無視し続けます",
+ "de": "Ignorieren und weitermachen.",
+ "fr": "Ignorer et continuer",
+ "id": "Abaikan dan lanjutkan"
+ },
+ {
+ "key": "你确定要清除缓存吗?",
+ "zh": "",
+ "zh-CHT": "你確定要清除緩存嗎?",
+ "en": "Are you sure you want to clear the cache?",
+ "ko": "정말로 캐시를 지우시겠습니까?",
+ "ja": "確かにキャッシュを消去しますか?",
+ "de": "Bist du dir sicher, dass du es löschen willst?",
+ "fr": "Êtes-vous sûr de vouloir effacer votre cache?",
+ "id": "Apakah anda yakin ingin menghapus cache?"
+ },
+ {
+ "key": "可见性选项",
+ "zh": "",
+ "zh-CHT": "可見性選項",
+ "en": "Visibility option",
+ "ko": "표시 옵션",
+ "ja": "可視的選択肢です",
+ "de": "Sichtbaren optionen.",
+ "fr": "Options de visibilité",
+ "id": "Pilihan yang terlihat"
+ },
+ {
+ "key": "保持",
+ "zh": "",
+ "zh-CHT": "保持",
+ "en": "Hold",
+ "ko": "유지",
+ "ja": "保持します。",
+ "de": "Halten! Und halten!",
+ "fr": "Maintenir",
+ "id": "Tetap"
+ },
+ {
+ "key": "保持:任务详情页可见性选项保持显示。",
+ "zh": "",
+ "zh-CHT": "保持:任務詳情頁可見性選項保持顯示。",
+ "en": "Hold: The task details page visibility option remains displayed.",
+ "ko": "유지:작업 정보 페이지 표시 옵션은 계속 표시됩니다.",
+ "ja": "維持:タスク詳細ページの可視性オプションを表示したままにします。",
+ "de": "Beibehaltung: die option sollte im detail beschrieben werden. Die folgenden optionen werden angezeigt",
+ "fr": "Garder: l’option de visibilité de la page de détails de la tâche reste affichée.",
+ "id": "Menjaga: detail misi halaman visibilitas pilihan tetap ditampilkan."
+ },
+ {
+ "key": "自动:默认值情况下显示在合并项目,设置时保持显示。",
+ "zh": "",
+ "zh-CHT": "自動:默認值情況下顯示在合併項目,設置時保持顯示。",
+ "en": "Automatic: The default value is displayed when merging items, and the setting remains displayed.",
+ "ko": "자동:합칠 때 기본값으로 표시되며, 설정하는 동안 표시되지 않습니다.",
+ "ja": "自働:デフォルトの場合はマージ項目を表示し、設定時は表示のままにします。",
+ "de": "Automatische anzeige (standard) zeigt die einstellungen im verlauf einer fusion an",
+ "fr": "Automatique: par défaut, les éléments fusionnés sont affichés.",
+ "id": "Otomatis: standar ditampilkan pada item penggabungan ketika anda mengatur."
+ },
+ {
+ "key": "请输入修改备注",
+ "zh": "",
+ "zh-CHT": "請輸入修改備註",
+ "en": "Please enter modification remarks",
+ "ko": "수정 비고를 입력하십시오",
+ "ja": "修正注記をお願いします。",
+ "de": "Bitte geben sie die anmerkung ein",
+ "fr": "Veuillez entrer une remarque modifiée",
+ "id": "Masukkan komentar revisi"
+ },
+ {
+ "key": "任务默认时间",
+ "zh": "",
+ "zh-CHT": "任務默認時間",
+ "en": "Task default time",
+ "ko": "작업 기본 시간",
+ "ja": "タスクデフォルト時間です",
+ "de": "Standard-zeit für mission:",
+ "fr": "Tâche temps par défaut",
+ "id": "Waktu standar tugas"
+ },
+ {
+ "key": "请选择默认时间",
+ "zh": "",
+ "zh-CHT": "請選擇默認時間",
+ "en": "Please select the default time",
+ "ko": "기본 시간을 선택하십시오",
+ "ja": "デフォルト時間をお願いします。",
+ "de": "Bitte wählen sie die standard-zeit",
+ "fr": "Veuillez sélectionner l’heure par défaut",
+ "id": "Silahkan pilih waktu standar"
+ },
+ {
+ "key": "添加任务计划时间默认时分。",
+ "zh": "",
+ "zh-CHT": "添加任務計劃時間默認時分。",
+ "en": "Add task schedule Time The default time.",
+ "ko": "기본 작업 스케줄 시간을 추가합니다.",
+ "ja": "タスクスケジュールのデフォルト時間を追加します。",
+ "de": "Festlegung der zielzeit standard-zeit hinzufügen",
+ "fr": "Ajouter le temps de planification des tâches par défaut.",
+ "id": "Tambahkan waktu perencanaan tugas ke default."
+ },
+ {
+ "key": "无法发起会话,请联系管理员。",
+ "zh": "",
+ "zh-CHT": "無法發起會話,請聯繫管理員。",
+ "en": "Unable to initiate a session. Please contact the administrator.",
+ "ko": "세션을 시작할 수 없습니다. 관리자에게 문의하십시오.",
+ "ja": "セッションが開始できませんので、管理人に連絡してください。",
+ "de": "Die sitzung kann nicht gestartet werden. Bitte sprechen sie mit dem systemadministrator",
+ "fr": "Impossible d’initier la session. Veuillez contacter l’administrateur.",
+ "id": "Tak dapat memulai sesi, hubungi administrator."
+ },
+ {
+ "key": "未完成",
+ "zh": "",
+ "zh-CHT": "未完成",
+ "en": "Uncompleted",
+ "ko": "완료하지 않음",
+ "ja": "未完成です",
+ "de": "Noch nicht ganz perfekt.",
+ "fr": "Non terminé non terminé",
+ "id": "Tidak lengkap"
+ },
+ {
+ "key": "导出审批数据",
+ "zh": "",
+ "zh-CHT": "導出審批數據",
+ "en": "Export approval data",
+ "ko": "승인 데이터 내보내기",
+ "ja": "決裁データを導き出します",
+ "de": "Exportiert die lizenzdaten",
+ "fr": "Exportation des données d’approbation",
+ "id": "Mengirimkan data persetujuan"
+ },
+ {
+ "key": "审批类型",
+ "zh": "",
+ "zh-CHT": "審批類型",
+ "en": "Approval type",
+ "ko": "승인 형식",
+ "ja": "承認タイプです",
+ "de": "Art des LKW?",
+ "fr": "Type d’agrément",
+ "id": "Tipe persetujuan"
+ },
+ {
+ "key": "导出类型",
+ "zh": "",
+ "zh-CHT": "導出類型",
+ "en": "Derived type",
+ "ko": "형식 내보내기",
+ "ja": "導出型です",
+ "de": "Art des exportieren",
+ "fr": "Types d’export",
+ "id": "Tipe ekspor"
+ },
+ {
+ "key": "AI机器人",
+ "zh": "",
+ "zh-CHT": "AI機器人",
+ "en": "AI robot",
+ "ko": "Ai 로봇",
+ "ja": "AIロボットです",
+ "de": "AI roboter",
+ "fr": "Le robot AI",
+ "id": "Robot al"
+ },
+ {
+ "key": "任务相关",
+ "zh": "",
+ "zh-CHT": "任務相關",
+ "en": "Task correlation",
+ "ko": "작업 관련",
+ "ja": "任務関連です",
+ "de": "Es ist eine relevante aufgabe.",
+ "fr": "Lié à la tâche",
+ "id": "Tugas terkait"
+ },
+ {
+ "key": "请填写名称!",
+ "zh": "",
+ "zh-CHT": "請填寫名稱!",
+ "en": "Please fill in the name!",
+ "ko": "이름을 기입해 주세요!",
+ "ja": "名称をお願いします!",
+ "de": "Bitte geben sie einen namen ein!",
+ "fr": "Veuillez remplir le nom!",
+ "id": "Nama, silakan!"
+ },
+ {
+ "key": "访问OpenAI网站查看:",
+ "zh": "",
+ "zh-CHT": "訪問OpenAI網站查看:",
+ "en": "Visit the OpenAI website to see:",
+ "ko": "Openai 사이트를 방문하십시오:",
+ "ja": "OpenAIのウェブサイトをご覧ください。",
+ "de": "Besuch der website OpenAI:",
+ "fr": "Visitez le site OpenAI pour voir:",
+ "id": "Kunjungi tampilan situs OpenAI:"
+ },
+ {
+ "key": "使用代理",
+ "zh": "",
+ "zh-CHT": "使用代理",
+ "en": "Use agent",
+ "ko": "에이전트 사용하기",
+ "ja": "エージェントを使います",
+ "de": "Verwendung eines agenten",
+ "fr": "Utiliser un proxy",
+ "id": "Gunakan agen"
+ },
+ {
+ "key": "支持 http 或 socks 代理",
+ "zh": "",
+ "zh-CHT": "支持 http 或 socks 代理",
+ "en": "Supports http or socks proxy",
+ "ko": "Http 또는 socks 프록시를 지원한다",
+ "ja": "Httpやsocksエージェントをサポートしています",
+ "de": "Kauft die vertreter vom typ HTTP Oder würmchen",
+ "fr": "Support du proxy HTTP ou socks",
+ "id": "Yang mendukung HTTP atau agen socks"
+ },
+ {
+ "key": "例如:http:\/\/proxy.com 或 socks5:\/\/proxy.com",
+ "zh": "",
+ "zh-CHT": "例如:http:\/\/proxy.com 或 socks5:\/\/proxy.com",
+ "en": "For example, http:\/\/proxy.com or socks5:\/\/proxy.com",
+ "ko": "례를 들면 http:\/\/proxy.com 혹은 socks5\/\/proxy.com이다",
+ "ja": "またはsocks5:\/\/proxy.comです",
+ "de": "Ein beispiel: http:\/\/proxyproxy.com, Oder socks5.com proxy.com",
+ "fr": "Par exemple: http:\/\/proxy.com ou socks5:\/\/proxy.com",
+ "id": "Sebagai contoh: http:\/\/proxycom. Atau socks5:\/proxycom"
+ },
+ {
+ "key": "查看 Cookie 中的 sessionKey 便是",
+ "zh": "",
+ "zh-CHT": "查看 Cookie 中的 sessionKey 便是",
+ "en": "Look at the sessionKey in the Cookie",
+ "ko": "쿠키의 sessionkey를 볼 수 있습니다",
+ "ja": "クッキーのsessionKeyを見るのです",
+ "de": "Ja, guck dir ein date in cookies arsch an",
+ "fr": "Voir la sessionKey dans les cookies est",
+ "id": "Periksa kunci sesi pada Cookie"
+ },
+ {
+ "key": "暂不支持拖拽文件夹。",
+ "zh": "",
+ "zh-CHT": "暫不支持拖拽文件夾。",
+ "en": "Drag-and-drop folders are not supported.",
+ "ko": "폴더 끌기는 지원되지 않습니다.",
+ "ja": "フォルダのドラッグ・ドロップには対応していません。",
+ "de": "Es wird niemandem helfen, den ordner zu ziehen",
+ "fr": "Les dossiers de glisser-déposer ne sont pas pris en charge.",
+ "id": "Tak akan menarik folder."
+ },
+ {
+ "key": "暂不支持粘贴文件夹。",
+ "zh": "",
+ "zh-CHT": "暫不支持粘貼文件夾。",
+ "en": "It does not support pasting folders.",
+ "ko": "폴더 붙여넣기는 지원되지 않습니다.",
+ "ja": "フォルダの貼り付けには対応していません。",
+ "de": "Tragt den ordner nicht ein",
+ "fr": "Les dossiers coller ne sont pas supportés pour le moment.",
+ "id": "Tak akan meminta penundaan folder."
+ },
+ {
+ "key": "暂不支持拖拽文件夹,请手动上传文件夹。",
+ "zh": "",
+ "zh-CHT": "暫不支持拖拽文件夾,請手動上傳文件夾。",
+ "en": "Dragging and dropping folders is not supported, please upload folders manually.",
+ "ko": "폴더를 끌어 올리는 것은 지원되지 않습니다. 폴더를 업로드하십시오.",
+ "ja": "フォルダをドラッグ&ドロップに対応していませんので、手動でフォルダをアップロードしてください。",
+ "de": "Ordner abschleppen unterstützt nicht; bitte diesen ordner manuell hochladen",
+ "fr": "Les dossiers de glisser-déposer ne sont pas pris en charge pour le moment. Veuillez télécharger les dossiers manuellement.",
+ "id": "Tak ada dukungan pada folder penarik, upload secara manual."
+ },
+ {
+ "key": "暂不支持粘贴文件夹,请手动上传文件夹。",
+ "zh": "",
+ "zh-CHT": "暫不支持粘貼文件夾,請手動上傳文件夾。",
+ "en": "It does not support pasting folders. Please upload folders manually.",
+ "ko": "폴더 붙여넣기는 지원되지 않습니다. 폴더 업로드하십시오.",
+ "ja": "フォルダの貼り付けには対応していませんので、手動でフォルダをアップロードしてください。",
+ "de": "Den ordner zum einfügen nicht unterstützen; bitte diesen manuell hochladen",
+ "fr": "Le dossier coller n’est pas pris en charge pour le moment. Veuillez télécharger le dossier manuellement.",
+ "id": "Tak ada folder tempel yang didukung, unggah secara manual."
+ },
+ {
+ "key": "最近",
+ "zh": "",
+ "zh-CHT": "最近",
+ "en": "Lately",
+ "ko": "최근",
+ "ja": "最近です",
+ "de": "Erst kürzlich",
+ "fr": "La récente",
+ "id": "Baru-baru ini"
+ },
+ {
+ "key": "暂无结果",
+ "zh": "",
+ "zh-CHT": "暫無結果",
+ "en": "No result yet",
+ "ko": "아직 결과가 없다.",
+ "ja": "まだ結果はありません",
+ "de": "Vom tisch.",
+ "fr": "Pas encore de résultats",
+ "id": "Tidak ada hasil."
+ },
+ {
+ "key": "新项目负责人",
+ "zh": "",
+ "zh-CHT": "新項目負責人",
+ "en": "New project leader",
+ "ko": "새 프로젝트 책임자",
+ "ja": "新しいプロジェクトリーダーです",
+ "de": "Neuer projektleiter.",
+ "fr": "Nouveau chef de projet",
+ "id": "Direktur proyek baru"
+ },
+ {
+ "key": "恢复帐号(已离职)",
+ "zh": "",
+ "zh-CHT": "恢復帳號(已離職)",
+ "en": "Restore Account (Retired)",
+ "ko": "계정 복원 (이미 종료됨)",
+ "ja": "アカウント復旧(退職しました)",
+ "de": "Reaktivieren sie den account wieder",
+ "fr": "Récupération de compte (quittée)",
+ "id": "Kembalikan akun (tidak aktif)"
+ },
+ {
+ "key": "你确定恢复已离职帐号【(*)】吗?(注:此操作仅恢复帐号状态,无法恢复操作离职时移交的数据)",
+ "zh": "",
+ "zh-CHT": "你確定恢復已離職帳號【(*)】嗎?(注:此操作僅恢復帳號狀態,無法恢復操作離職時移交的數據)",
+ "en": "Are you sure to restore the departed account [(*)]? (Note: This operation only restores the account status and cannot restore the data transferred when the operation is terminated.)",
+ "ko": "이직한 계정 [(*)]을 복원하시겠습니까?(주:이 작업은 계정 상태만 복구할 수 있습니다. 작업이 중단되었을 때 전송된 데이터는 복구할 수 없습니다)",
+ "ja": "退職したアカウント【(*)】の復旧は確実ですか?(注:この作業はアカウントの復旧のみであり、作業終了時に移管したデータの復旧はできません)",
+ "de": "Sind sie sicher, dass sie alles wieder eingestellt haben? Dies stellt nur die rufnummer wieder ein; keine wiederherstellung der im gang befindlichen daten",
+ "fr": "Êtes-vous sûr de récupérer un compte sortant [(*)]? (remarque: cette opération ne permet que de restaurer l’état du compte, les données transférées lors du départ de l’opération ne peuvent pas être récupérées)",
+ "id": "Apakah anda yakin telah kembali akun yang tidak aktif ((*)? (catatan: operasi ini hanya mengembalikan status akun dan tidak dapat mengembalikan data yang diserahkan saat operasi meninggalkan)"
+ },
+ {
+ "key": "流转到【(*)】时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。",
+ "zh": "",
+ "zh-CHT": "流轉到【(*)】時改變任務負責人爲狀態負責人(並保留操作狀態的人員),原本的任務負責人移至協助人員。",
+ "en": "When transferring to [(*)], the task leader is changed to the status leader (and the operational status person is retained), and the original task leader is moved to the assistance person.",
+ "ko": "【(*)】로 이전할 때 임무책임자는 상태책임자 (동시에 조작상태를 보류한 인원)로 변경되며 원래의 임무책임자는 협조인원으로 이전된다.",
+ "ja": "【(*)】に流れた時点でタスクの責任者を状態責任者(作業状態を保持している人)に変更し、元のタスクの責任者をサポートスタッフに移します。",
+ "de": "Nach der umleitung zu [*] ändert sich der dienstleiter zum dienstleiter (der auch diesen bereitschaftsstatus behalten hat), so dass der ursprüngliche controller zu einem helfer umgeleitet wird.",
+ "fr": "Lorsque le flux passe à [(*)], le chef de mission devient le chef d’état (et la personne qui conserve le statut opérationnel), le chef de mission d’origine passe à l’assistant.",
+ "id": "Ubah direktur misi menjadi pemimpin negara (dan tetap memegang status operasional) ketika petugas yang bertugas dipindahkan ke asisten."
+ },
+ {
+ "key": "删除文件夹",
+ "zh": "",
+ "zh-CHT": "刪除文件夾",
+ "en": "Delete folder",
+ "ko": "폴더 지우기",
+ "ja": "フォルダを削除します",
+ "de": "Ordner löschen",
+ "fr": "Supprimer un dossier",
+ "id": "Hapus folder"
+ },
+ {
+ "key": "你确定要删除【(*)】文件夹吗?",
+ "zh": "",
+ "zh-CHT": "你確定要刪除【(*)】文件夾嗎?",
+ "en": "Are you sure you want to delete the [(*)] folder?",
+ "ko": "[(*)] 폴더를 정말로 삭제하시겠습니까?",
+ "ja": "【(*)】フォルダを削除しますか?",
+ "de": "Möchten sie wirklich (*) den ordner löschen?",
+ "fr": "Êtes-vous sûr de vouloir supprimer le dossier [(*)]?",
+ "id": "Apakah anda yakin ingin menghapus folder [*]?"
+ },
+ {
+ "key": "请输入新的密码",
+ "zh": "",
+ "zh-CHT": "請輸入新的密碼",
+ "en": "Please enter a new password",
+ "ko": "새 비밀번호를 입력하십시오",
+ "ja": "新しいパスワードをお願いします。",
+ "de": "Bitte geben sie ein neues passwort ein",
+ "fr": "Veuillez entrer un nouveau mot de passe",
+ "id": "Password baru, silakan"
+ },
+ {
+ "key": "请输入新的邮箱((*))",
+ "zh": "",
+ "zh-CHT": "請輸入新的郵箱((*))",
+ "en": "Please enter a new email ((*))",
+ "ko": "새 편지함을 입력하십시오 (*)",
+ "ja": "新しいメールアドレス((*))をお願いします。",
+ "de": "Bitte geben sie einen neuen briefkasten ein",
+ "fr": "Veuillez entrer un nouvel email ((*))",
+ "id": "Silahkan masukkan email baru (*)"
+ },
+ {
+ "key": "截图快捷键",
+ "zh": "",
+ "zh-CHT": "截圖快捷鍵",
+ "en": "Screenshot shortcut",
+ "ko": "스크린샷 단축키",
+ "ja": "スクリーンショットショートカットキーです",
+ "de": "Fahre den kurzbefehl ab",
+ "fr": "Raccourcis pour captures d’écran",
+ "id": "Shortcut screenshot"
+ },
+ {
+ "key": "加班",
+ "zh": "",
+ "zh-CHT": "加班",
+ "en": "Overtime",
+ "ko": "초과 근무",
+ "ja": "残業",
+ "de": "Überstunden.",
+ "fr": "Les heures supplémentaires",
+ "id": "Kerja lembur."
+ },
+ {
+ "key": "离职申请",
+ "zh": "",
+ "zh-CHT": "離職申請",
+ "en": "Resignation application",
+ "ko": "이직 신청",
+ "ja": "退職願いです",
+ "de": "Eine freistellung beantragen.",
+ "fr": "Demande de départ",
+ "id": "Tinggalkan aplikasi"
+ },
+ {
+ "key": "录用申请",
+ "zh": "",
+ "zh-CHT": "錄用申請",
+ "en": "Application for employment",
+ "ko": "채용 지원서",
+ "ja": "採用申し込みです",
+ "de": "Wir nehmen die bewerbung an.",
+ "fr": "Demande d’offre",
+ "id": "Aplikasi untuk diterima"
+ },
+ {
+ "key": "我要打卡",
+ "zh": "",
+ "zh-CHT": "我要打卡",
+ "en": "I need to punch in",
+ "ko": "카드를 찍을래요",
+ "ja": "タイムカードを押します",
+ "de": "Ich will mit karte spielen",
+ "fr": "Je veux pointer",
+ "id": "Aku mau hitung mundur."
+ },
+ {
+ "key": "修改的内容尚未保存,确定要放弃修改吗?",
+ "zh": "",
+ "zh-CHT": "修改的內容尚未保存,確定要放棄修改嗎?",
+ "en": "The modified content has not been saved. Are you sure you want to abandon the modification?",
+ "ko": "수정된 내용이 저장되지 않았습니다. 수정을 취소하시겠습니까?",
+ "ja": "修正内容が保存されていない場合、修正を断念することは確実でしょうか?",
+ "de": "Die änderungen sind noch nicht gespeichert. Sollten sie auf sie verzichten?",
+ "fr": "Le contenu des modifications n’a pas été sauvegardé. Êtes-vous sûr de vouloir abandonner les modifications?",
+ "id": "Isi revisi belum tersimpan. Apakah anda yakin ingin meninggalkan perubahan?"
+ },
+ {
+ "key": "获取方式",
+ "zh": "",
+ "zh-CHT": "獲取方式",
+ "en": "Acquisition mode",
+ "ko": "가져오는 방법",
+ "ja": "入手方法です",
+ "de": "Mach den zugriff.",
+ "fr": "Modalités d’accès",
+ "id": "Mendapatkan mode"
+ },
+ {
+ "key": "查看说明",
+ "zh": "",
+ "zh-CHT": "查看說明",
+ "en": "View instructions",
+ "ko": "설명 보기",
+ "ja": "説明を見ます",
+ "de": "Prüfe die anweisungen.",
+ "fr": "Voir les instructions",
+ "id": "Tampilkan deskripsi"
+ },
+ {
+ "key": "正在修改系统机器人:(*)",
+ "zh": "",
+ "zh-CHT": "正在修改系統機器人:(*)",
+ "en": "System robot being modified: (*)",
+ "ko": "시스템 로봇 수정하는 중:*)",
+ "ja": "システムを修正していますロボット:(*)",
+ "de": "System-roboter komplett umbauen (*)",
+ "fr": "Modification du robot système :(*)",
+ "id": "Sedang memodifikasi robot sistem :(*)"
+ },
+ {
+ "key": "复制选择",
+ "zh": "",
+ "zh-CHT": "複製選擇",
+ "en": "Copy selection",
+ "ko": "복사 선택",
+ "ja": "複製選択です",
+ "de": "Wahl zum kopieren",
+ "fr": "Copier la sélection",
+ "id": "Salin pilihan"
+ },
+ {
+ "key": "复制全部",
+ "zh": "",
+ "zh-CHT": "複製全部",
+ "en": "Copy all",
+ "ko": "모두 복사",
+ "ja": "全てを複製します",
+ "de": "Sie kopierten alles.",
+ "fr": "Copier tout",
+ "id": "Salin semua"
+ },
+ {
+ "key": "返回首页",
+ "zh": "",
+ "zh-CHT": "返回首頁",
+ "en": "Back to home page",
+ "ko": "홈 페이지로 돌아가기",
+ "ja": "トップページに戻ります",
+ "de": "Mach die titelseite auf.",
+ "fr": "Retour à l’accueil",
+ "id": "Kembali ke rumah"
+ },
+ {
+ "key": "刷新链接",
+ "zh": "",
+ "zh-CHT": "刷新鏈接",
+ "en": "Refresh link",
+ "ko": "링크 새로 고침",
+ "ja": "リンクを更新します",
+ "de": "Aktualisieren.",
+ "fr": "Rafraîchir le lien",
+ "id": "Segarkan tautan"
+ },
+ {
+ "key": "确认",
+ "zh": "",
+ "zh-CHT": "確認",
+ "en": "Verify",
+ "ko": "확인",
+ "ja": "確認します",
+ "de": "Abschuss bestätigen.",
+ "fr": "La confirmation",
+ "id": "Konfirmasi"
+ },
+ {
+ "key": "还原归档",
+ "zh": "",
+ "zh-CHT": "還原歸檔",
+ "en": "Restore archive",
+ "ko": "압축 파일 복원",
+ "ja": "復元アーカイブです",
+ "de": "Archive zurückstellen",
+ "fr": "Restaurer les archives",
+ "id": "Pengurangan file"
+ },
+ {
+ "key": "签到方式",
+ "zh": "",
+ "zh-CHT": "簽到方式",
+ "en": "Sign-in method",
+ "ko": "체크인 방법",
+ "ja": "サインイン方式",
+ "de": "Mach die aufzeichnungen.",
+ "fr": "Mode de connexion",
+ "id": "Cara masuk"
+ },
+ {
+ "key": "手动签到",
+ "zh": "",
+ "zh-CHT": "手動簽到",
+ "en": "Manual check-in",
+ "ko": "수동 체크",
+ "ja": "手動でサインインする",
+ "de": "Manuelle eingabe",
+ "fr": "Se connecter manuellement",
+ "id": "Tanda tangan"
+ },
+ {
+ "key": "定位签到",
+ "zh": "",
+ "zh-CHT": "定位簽到",
+ "en": "Location check-in",
+ "ko": "위치 체크",
+ "ja": "ポジショニングサインイン",
+ "de": "Gps-koordinaten eingeben.",
+ "fr": "Localisez votre connexion",
+ "id": "Letakkan tanda tangan"
+ },
+ {
+ "key": "详情看下文安装说明",
+ "zh": "",
+ "zh-CHT": "詳情看下文安裝說明",
+ "en": "See installation instructions below for details",
+ "ko": "자세한 내용은 아래의 설치 지침을 참조하십시오",
+ "ja": "詳細はインストールの説明をご覧いただきます",
+ "de": "Details zur installation finden sie hier",
+ "fr": "Voir les instructions d’installation ci-dessous pour plus de détails",
+ "id": "Untuk informasi lebih lanjut tentang pemasangan di bawah ini"
+ },
+ {
+ "key": "通过在签到打卡机器人发送指令签到",
+ "zh": "",
+ "zh-CHT": "通過在簽到打卡機器人發送指令簽到",
+ "en": "Sign in by sending instructions at the check-in check-in robot",
+ "ko": "체크인 로봇으로 명령을 보냅니다",
+ "ja": "タイムカードを押すロボットに指示を送ります",
+ "de": "Der keildroide erstellt die aufträge",
+ "fr": "Se connecter en envoyant des instructions au robot de pointage à l’enregistrement",
+ "id": "Dengan mengirimkan perintah masuk ke abbot"
+ },
+ {
+ "key": "通过在签到打卡机器人发送位置签到",
+ "zh": "",
+ "zh-CHT": "通過在簽到打卡機器人發送位置簽到",
+ "en": "Send location check-in by punching in the check-in robot",
+ "ko": "체크인 로봇에 체크인 위치를 보냅니다",
+ "ja": "タイムカードを押すロボットでロケーションチェックをします",
+ "de": "Der kupfer-roboter registriert den ort, an dem er sich einloggt",
+ "fr": "Enregistrement en envoyant l’emplacement par le robot de pointage à l’enregistrement",
+ "id": "Dengan mengirimkan tempat masuk ke mesin masukan"
+ },
+ {
+ "key": "状态设置",
+ "zh": "",
+ "zh-CHT": "狀態設置",
+ "en": "Status setting",
+ "ko": "상태 설정",
+ "ja": "ステータス設定です",
+ "de": "Du schlägst zu!",
+ "fr": "Paramètres de statut",
+ "id": "Pengaturan negara"
+ },
+ {
+ "key": "关联列表",
+ "zh": "",
+ "zh-CHT": "關聯列表",
+ "en": "Association list",
+ "ko": "연결 목록",
+ "ja": "関連リストです",
+ "de": "Eine liste Von dingen.",
+ "fr": "Liste des associations",
+ "id": "Daftar yang terkait"
+ },
+ {
+ "key": "选择关联列表",
+ "zh": "",
+ "zh-CHT": "選擇關聯列表",
+ "en": "Select association list",
+ "ko": "연결된 목록 선택",
+ "ja": "関連リストを選択します",
+ "de": "Wählen sie eine liste Von zufälligen beziehungen",
+ "fr": "Sélectionnez la liste associée",
+ "id": "Pilih daftar yang berkaitan"
+ },
+ {
+ "key": "取消关联",
+ "zh": "",
+ "zh-CHT": "取消關聯",
+ "en": "Disassociate",
+ "ko": "연결 해제",
+ "ja": "関連をキャンセルします",
+ "de": "Streichen sie die verbindung.",
+ "fr": "Annuler une association",
+ "id": "Batal diasosiasikan"
+ },
+ {
+ "key": "流转到【(*)】时自动将任务移动至关联列表。",
+ "zh": "",
+ "zh-CHT": "流轉到【(*)】時自動將任務移動至關聯列表。",
+ "en": "Automatically moves the task to the associated list when flow to [(*)].",
+ "ko": "로 이동하면 자동으로 연결된 목록으로 작업이 이동됩니다.",
+ "ja": "【(*)】にフローすると自動的にタスクが関連リストに移動します。",
+ "de": "Bei einem wechsel zu [*] wird die aufgabe automatisch zu einer liste Von zufälligen verbindungen verschoben.",
+ "fr": "Déplacez automatiquement la tâche à la liste associée lorsque le flux va à [(*)].",
+ "id": "Pindahkan tugas ke senarai terkait ketika ((*)."
+ },
+ {
+ "key": "在浏览器中打开",
+ "zh": "",
+ "zh-CHT": "在瀏覽器中打開",
+ "en": "Open in your browser",
+ "ko": "브라우저에서 열기",
+ "ja": "ブラウザで開きます",
+ "de": "Und öffnet sich im browser",
+ "fr": "Ouvrir dans votre navigateur",
+ "id": "Buka di browser anda"
+ },
+ {
+ "key": "图片存储为...",
+ "zh": "",
+ "zh-CHT": "圖片存儲爲...",
+ "en": "Image stored as...",
+ "ko": "그림 저장...",
+ "ja": "画像は…です",
+ "de": "Die bilder sind als",
+ "fr": "Images stockées pour...",
+ "id": "Simpan gambar sebagai …"
+ },
+ {
+ "key": "复制电子邮件地址",
+ "zh": "",
+ "zh-CHT": "複製電子郵件地址",
+ "en": "Copy the email address",
+ "ko": "전자 우편 주소를 복사합니다",
+ "ja": "メールアドレスをコピーします",
+ "de": "Die e-mail-adresse wird kopiert",
+ "fr": "Copiez l’adresse e-mail",
+ "id": "Salin alamat email"
+ },
+ {
+ "key": "复制链接地址",
+ "zh": "",
+ "zh-CHT": "複製鏈接地址",
+ "en": "Copy link address",
+ "ko": "링크 주소 복사",
+ "ja": "リンク先をコピーします",
+ "de": "Die adresse des link kopieren",
+ "fr": "Copier l’adresse du lien",
+ "id": "Salin alamat tautan"
+ },
+ {
+ "key": "复制图片地址",
+ "zh": "",
+ "zh-CHT": "複製圖片地址",
+ "en": "Copy image address",
+ "ko": "그림 주소 복사",
+ "ja": "コピー画像アドレスです",
+ "de": "Kopien der adresse.",
+ "fr": "Copiez l’adresse de l’image",
+ "id": "Salin alamat gambar"
+ },
+ {
+ "key": "图片无法保存",
+ "zh": "",
+ "zh-CHT": "圖片無法保存",
+ "en": "Image cannot be saved",
+ "ko": "그림을 저장할 수 없음",
+ "ja": "画像は保存できません。",
+ "de": "Bild kann nicht mehr gerettet werden",
+ "fr": "L’image ne peut pas être sauvegardée",
+ "id": "Gambar tidak bisa disimpan"
+ },
+ {
+ "key": "键盘设置",
+ "zh": "",
+ "zh-CHT": "鍵盤設置",
+ "en": "Keyboard Settings",
+ "ko": "키보드 설정",
+ "ja": "キーボードの設定です",
+ "de": "Die tastatureinstellung",
+ "fr": "Réglage du clavier",
+ "id": "Pengaturan papan ketik"
+ },
+ {
+ "key": "事项",
+ "zh": "",
+ "zh-CHT": "事項",
+ "en": "Todo",
+ "ko": "사항",
+ "ja": "事項です",
+ "de": "Todo",
+ "fr": "Todo",
+ "id": "Todo"
+ },
+ {
+ "key": "编辑描述",
+ "zh": "",
+ "zh-CHT": "編輯描述",
+ "en": "Edit description",
+ "ko": "설명 편집",
+ "ja": "記述を編集します。",
+ "de": "Bearbeiten sie die beschreibung",
+ "fr": "Editer la description",
+ "id": "Deskripsi pengeditan"
+ },
+ {
+ "key": "没有任何修改!",
+ "zh": "",
+ "zh-CHT": "沒有任何修改!",
+ "en": "No modifications!",
+ "ko": "변경된 것은 없습니다!",
+ "ja": "何の修正もありません!",
+ "de": "Es gibt keine änderungen!",
+ "fr": "Sans aucune modification!",
+ "id": "Tak ada yang perlu diubah!"
+ },
+ {
+ "key": "项目类型",
+ "zh": "",
+ "zh-CHT": "項目類型",
+ "en": "Item type",
+ "ko": "항목 형식",
+ "ja": "プロジェクトタイプです",
+ "de": "Typ eins?",
+ "fr": "Type de projet",
+ "id": "Jenis item"
+ },
+ {
+ "key": "团队项目",
+ "zh": "",
+ "zh-CHT": "團隊項目",
+ "en": "Team project",
+ "ko": "팀 프로젝트",
+ "ja": "チームプロジェクトです",
+ "de": "Ein teamprojekt.",
+ "fr": "Projets par équipe",
+ "id": "Proyek tim"
+ },
+ {
+ "key": "全部项目",
+ "zh": "",
+ "zh-CHT": "全部項目",
+ "en": "All items",
+ "ko": "모든 항목",
+ "ja": "全項目です",
+ "de": "Gesamte projekte.",
+ "fr": "Tous les projets",
+ "id": "Semua item"
+ },
+ {
+ "key": "打开链接",
+ "zh": "",
+ "zh-CHT": "打開鏈接",
+ "en": "Open the link",
+ "ko": "링크 열기",
+ "ja": "リンクを開きます",
+ "de": "Link öffnen.",
+ "fr": "Ouvrir le lien",
+ "id": "Buka tautan"
+ },
+ {
+ "key": "汇报人",
+ "zh": "",
+ "zh-CHT": "彙報人",
+ "en": "Informant",
+ "ko": "접수인",
+ "ja": "発信者です",
+ "de": "Das telegramm.",
+ "fr": "Consignataire",
+ "id": "Pembawa berita"
+ },
+ {
+ "key": "提交时间",
+ "zh": "",
+ "zh-CHT": "提交時間",
+ "en": "Submission time",
+ "ko": "제출 날짜",
+ "ja": "提出時期です",
+ "de": "Zeit bis zur abgabe",
+ "fr": "Heure de soumission",
+ "id": "Waktu pengiriman"
}
]
\ No newline at end of file
diff --git a/package.json b/package.json
index 090d9573c..13a53c6eb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "DooTask",
- "version": "0.27.46",
+ "version": "0.30.13",
"description": "DooTask is task management system.",
"scripts": {
"start": "./cmd dev",
@@ -26,7 +26,7 @@
"css-loader": "^6.7.2",
"dexie": "^3.2.3",
"echarts": "^5.2.2",
- "element-sea": "^2.15.10-3",
+ "element-sea": "^2.15.10-6",
"file-loader": "^6.2.0",
"highlight.js": "^11.7.0",
"inquirer": "^8.2.0",
@@ -55,7 +55,7 @@
"stylus-loader": "^7.1.0",
"tinymce": "^5.10.3",
"tui-calendar-hi": "^1.15.1-5",
- "view-design-hi": "^4.7.0-47",
+ "view-design-hi": "^4.7.0-48",
"vite": "^2.9.15",
"vite-plugin-file-copy": "^1.0.0",
"vite-plugin-require": "^1.1.10",
@@ -68,7 +68,7 @@
"vue-resize-observer": "^2.0.16",
"vue-router": "^3.6.5",
"vue-template-compiler": "~2.6.14",
- "vue-virtual-scroll-list-hi": "^2.3.4-6",
+ "vue-virtual-scroll-list-hi": "^2.3.5-3",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2"
},
diff --git a/public/audio/call.mp3 b/public/audio/call.mp3
old mode 100755
new mode 100644
diff --git a/public/audio/message.mp3 b/public/audio/message.mp3
old mode 100755
new mode 100644
diff --git a/public/css/fonts/ionicons.svg b/public/css/fonts/ionicons.svg
old mode 100755
new mode 100644
diff --git a/public/css/fonts/ionicons.ttf b/public/css/fonts/ionicons.ttf
old mode 100755
new mode 100644
diff --git a/public/css/fonts/ionicons.woff b/public/css/fonts/ionicons.woff
old mode 100755
new mode 100644
diff --git a/public/css/fonts/taskfont/iconfont.ttf b/public/css/fonts/taskfont/iconfont.ttf
index fcb0a679f..cad739184 100644
Binary files a/public/css/fonts/taskfont/iconfont.ttf and b/public/css/fonts/taskfont/iconfont.ttf differ
diff --git a/public/css/fonts/taskfont/iconfont.woff b/public/css/fonts/taskfont/iconfont.woff
index 72e87b1a4..c84908397 100644
Binary files a/public/css/fonts/taskfont/iconfont.woff and b/public/css/fonts/taskfont/iconfont.woff differ
diff --git a/public/css/fonts/taskfont/iconfont.woff2 b/public/css/fonts/taskfont/iconfont.woff2
index 509bba5c3..b8b5ba5a6 100644
Binary files a/public/css/fonts/taskfont/iconfont.woff2 and b/public/css/fonts/taskfont/iconfont.woff2 differ
diff --git a/public/docs/assets/main.css b/public/docs/assets/main.css
index 4a9c72927..4a3c0b425 100644
--- a/public/docs/assets/main.css
+++ b/public/docs/assets/main.css
@@ -53,6 +53,11 @@ input[type="date"] {
url('./glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
}
+/* Hide vertical scrollbar on off canvas animation ("left" positioning) */
+html {
+ overflow-x: hidden;
+}
+
body {
font-family: "Source Sans Pro", sans-serif;
}
@@ -63,7 +68,6 @@ a:focus {
#content {
margin-top: 10px;
- margin-left: 20%;
padding-left: 10px;
}
@@ -193,16 +197,30 @@ td.code {
/* ------------------------------------------------------------------------------------------
* Sidenav
* ------------------------------------------------------------------------------------------ */
-.sidenav {
- color: var(--white);
- width: 20%;
+#scrollingNav {
position: fixed;
- top: 50px;
+ top: 0;
left: 0;
bottom: 0;
- overflow-x: hidden;
- overflow-y: auto;
+ z-index: 10;
background-color: var(--dark-gray);
+ box-shadow: 0 2px 5px 0 rgb(0 0 0 / 16%), 0 2px 10px 0 rgb(0 0 0 / 12%);
+}
+
+.sidenav {
+ color: var(--white);
+ position: absolute;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow-x: hidden;
+ overflow-y: hidden;
+}
+
+.sidenav:hover {
+ overflow-x: auto;
+ overflow-y: auto;
}
.sidenav > li > a {
@@ -250,14 +268,80 @@ td.code {
}
*/
+/*
+ * Off Canvas
+ * --------------------------------------------------
+ */
+@media screen and (max-width: 767px) {
+ #content {
+ margin-top: 58px;
+ }
+
+ .row-offcanvas {
+ position: relative;
+ -webkit-transition: all .25s ease-out;
+ -o-transition: all .25s ease-out;
+ transition: all .25s ease-out;
+ left: 0;
+ }
+
+ .row-offcanvas,
+ .row-offcanvas * {
+ transition: all 0.5s ease-out;
+ }
+
+ .row-offcanvas .sidebar-offcanvas {
+ position: absolute;
+ top: 0;
+ left: -200px !important; /* 6 columns */
+ width: 100%; /* 6 columns */
+ max-width: 200px;
+ }
+
+ .nav-toggle {
+ position: fixed;
+ left: 0;
+ background: var(--dark-gray);
+ width: 100%;
+ }
+
+ .nav-toggle .btn {
+ margin: 10px 14px;
+ }
+ .nav-toggle .icon-bar {
+ display: block;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px;
+ background-color: var(--white);
+ }
+ .nav-toggle .icon-bar + .icon-bar {
+ margin-top: 4px;
+ }
+
+ .row-offcanvas.active .sidebar-offcanvas {
+ left: 0 !important; /* 6 columns */
+ }
+ .row-offcanvas.active, .row-offcanvas.active .nav-toggle {
+ left: 200px;
+ }
+ /* Styling the three lines to make it an X */
+ .row-offcanvas.active .nav-toggle .btn > .icon-bar {
+ transform: rotate(45deg) translate(-4px, -4px);
+ }
+ .row-offcanvas.active .nav-toggle .btn .icon-bar:nth-child(2) {
+ display: none;
+ }
+ .row-offcanvas.active .nav-toggle .btn .icon-bar:nth-child(3) {
+ transform: rotate(-45deg);
+ }
+}
+
/* ------------------------------------------------------------------------------------------
* Side nav search
* ------------------------------------------------------------------------------------------ */
.sidenav-search {
- width: 20%;
- left: 0px;
- position: fixed;
- padding: 16px 20px 10px 20px;
+ padding: 16px 10px 10px;
background-color: var(--dark-gray);
}
@@ -273,10 +357,28 @@ td.code {
height: 20px;
text-align: center;
right: 28px;
- top: 17px;
+ top: 18px;
background-color: #fff;
}
+/* ------------------------------------------------------------------------------------------
+ * Prism - Toolbar
+ * ------------------------------------------------------------------------------------------ */
+div.code-toolbar.code-toolbar > .toolbar {
+ top: .4rem;
+ right: .4rem;
+}
+div.code-toolbar.code-toolbar > .toolbar > .toolbar-item > button:hover,
+div.code-toolbar.code-toolbar > .toolbar > .toolbar-item > button:focus {
+ color: var(--white);
+}
+div.code-toolbar.code-toolbar > .toolbar > .toolbar-item > button {
+ color: var(--light-gray);
+ padding: .5em;
+ background: var(--hover-gray);
+ box-shadow: 0 2px 1px 1px rgba(0,0,0,.5);
+}
+
/* ------------------------------------------------------------------------------------------
* Compare
* ------------------------------------------------------------------------------------------ */
diff --git a/public/docs/assets/prism-diff-highlight.css b/public/docs/assets/prism-diff-highlight.css
new file mode 100644
index 000000000..0d9eb0c52
--- /dev/null
+++ b/public/docs/assets/prism-diff-highlight.css
@@ -0,0 +1,13 @@
+pre.diff-highlight > code .token.deleted:not(.prefix),
+pre > code.diff-highlight .token.deleted:not(.prefix) {
+ background-color: rgba(255, 0, 0, .1);
+ color: inherit;
+ display: block;
+}
+
+pre.diff-highlight > code .token.inserted:not(.prefix),
+pre > code.diff-highlight .token.inserted:not(.prefix) {
+ background-color: rgba(0, 255, 128, .1);
+ color: inherit;
+ display: block;
+}
diff --git a/public/docs/assets/prism-toolbar.css b/public/docs/assets/prism-toolbar.css
new file mode 100644
index 000000000..59676aea4
--- /dev/null
+++ b/public/docs/assets/prism-toolbar.css
@@ -0,0 +1,65 @@
+div.code-toolbar {
+ position: relative;
+}
+
+div.code-toolbar > .toolbar {
+ position: absolute;
+ z-index: 10;
+ top: .3em;
+ right: .2em;
+ transition: opacity 0.3s ease-in-out;
+ opacity: 0;
+}
+
+div.code-toolbar:hover > .toolbar {
+ opacity: 1;
+}
+
+/* Separate line b/c rules are thrown out if selector is invalid.
+ IE11 and old Edge versions don't support :focus-within. */
+div.code-toolbar:focus-within > .toolbar {
+ opacity: 1;
+}
+
+div.code-toolbar > .toolbar > .toolbar-item {
+ display: inline-block;
+}
+
+div.code-toolbar > .toolbar > .toolbar-item > a {
+ cursor: pointer;
+}
+
+div.code-toolbar > .toolbar > .toolbar-item > button {
+ background: none;
+ border: 0;
+ color: inherit;
+ font: inherit;
+ line-height: normal;
+ overflow: visible;
+ padding: 0;
+ -webkit-user-select: none; /* for button */
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+div.code-toolbar > .toolbar > .toolbar-item > a,
+div.code-toolbar > .toolbar > .toolbar-item > button,
+div.code-toolbar > .toolbar > .toolbar-item > span {
+ color: #bbb;
+ font-size: .8em;
+ padding: 0 .5em;
+ background: #f5f2f0;
+ background: rgba(224, 224, 224, 0.2);
+ box-shadow: 0 2px 0 0 rgba(0,0,0,0.2);
+ border-radius: .5em;
+}
+
+div.code-toolbar > .toolbar > .toolbar-item > a:hover,
+div.code-toolbar > .toolbar > .toolbar-item > a:focus,
+div.code-toolbar > .toolbar > .toolbar-item > button:hover,
+div.code-toolbar > .toolbar > .toolbar-item > button:focus,
+div.code-toolbar > .toolbar > .toolbar-item > span:hover,
+div.code-toolbar > .toolbar > .toolbar-item > span:focus {
+ color: inherit;
+ text-decoration: none;
+}
diff --git a/public/images/apply/addProject.svg b/public/images/apply/addProject.svg
new file mode 100644
index 000000000..3cf886bcd
--- /dev/null
+++ b/public/images/apply/addProject.svg
@@ -0,0 +1,22 @@
+
+
+
diff --git a/public/images/apply/addTask.svg b/public/images/apply/addTask.svg
new file mode 100644
index 000000000..090f1aa72
--- /dev/null
+++ b/public/images/apply/addTask.svg
@@ -0,0 +1,29 @@
+
+
+
diff --git a/public/images/apply/allUser.svg b/public/images/apply/allUser.svg
new file mode 100644
index 000000000..3d145a93b
--- /dev/null
+++ b/public/images/apply/allUser.svg
@@ -0,0 +1,27 @@
+
+
+
diff --git a/public/images/apply/apppush.svg b/public/images/apply/apppush.svg
new file mode 100644
index 000000000..23d67cb70
--- /dev/null
+++ b/public/images/apply/apppush.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/approve.svg b/public/images/apply/approve.svg
new file mode 100644
index 000000000..43b11ce10
--- /dev/null
+++ b/public/images/apply/approve.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/calendar.svg b/public/images/apply/calendar.svg
new file mode 100644
index 000000000..5243eea5d
--- /dev/null
+++ b/public/images/apply/calendar.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/public/images/apply/createMeeting.svg b/public/images/apply/createMeeting.svg
new file mode 100644
index 000000000..c1d209e64
--- /dev/null
+++ b/public/images/apply/createMeeting.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/file.svg b/public/images/apply/file.svg
new file mode 100644
index 000000000..800cfa2f8
--- /dev/null
+++ b/public/images/apply/file.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/public/images/apply/joinMeeting.svg b/public/images/apply/joinMeeting.svg
new file mode 100644
index 000000000..9ae39f7ae
--- /dev/null
+++ b/public/images/apply/joinMeeting.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/public/images/apply/ldap.svg b/public/images/apply/ldap.svg
new file mode 100644
index 000000000..d4ac17dc0
--- /dev/null
+++ b/public/images/apply/ldap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/mail.svg b/public/images/apply/mail.svg
new file mode 100644
index 000000000..2ac06af37
--- /dev/null
+++ b/public/images/apply/mail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/meeting.svg b/public/images/apply/meeting.svg
new file mode 100644
index 000000000..c1d209e64
--- /dev/null
+++ b/public/images/apply/meeting.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/report.svg b/public/images/apply/report.svg
new file mode 100644
index 000000000..7ad49933c
--- /dev/null
+++ b/public/images/apply/report.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/robot.svg b/public/images/apply/robot.svg
new file mode 100644
index 000000000..801356ba6
--- /dev/null
+++ b/public/images/apply/robot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/apply/scan.svg b/public/images/apply/scan.svg
new file mode 100644
index 000000000..f0ecc2c8c
--- /dev/null
+++ b/public/images/apply/scan.svg
@@ -0,0 +1,41 @@
+
+
+
diff --git a/public/images/apply/setting.svg b/public/images/apply/setting.svg
new file mode 100644
index 000000000..99a6c4942
--- /dev/null
+++ b/public/images/apply/setting.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/public/images/apply/signin.svg b/public/images/apply/signin.svg
new file mode 100644
index 000000000..78caec255
--- /dev/null
+++ b/public/images/apply/signin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/avatar/default_claude.png b/public/images/avatar/default_claude.png
new file mode 100644
index 000000000..8c9cdc538
Binary files /dev/null and b/public/images/avatar/default_claude.png differ
diff --git a/public/images/avatar/default_openai.png b/public/images/avatar/default_openai.png
new file mode 100644
index 000000000..e726be963
Binary files /dev/null and b/public/images/avatar/default_openai.png differ
diff --git a/public/images/browser/360.png b/public/images/browser/360.png
old mode 100755
new mode 100644
diff --git a/public/images/browser/chrome.png b/public/images/browser/chrome.png
old mode 100755
new mode 100644
diff --git a/public/images/browser/firefox.png b/public/images/browser/firefox.png
old mode 100755
new mode 100644
diff --git a/public/images/browser/ie.png b/public/images/browser/ie.png
old mode 100755
new mode 100644
diff --git a/public/images/browser/safari.png b/public/images/browser/safari.png
old mode 100755
new mode 100644
diff --git a/public/images/dialog/todo.svg b/public/images/dialog/todo.svg
new file mode 100644
index 000000000..fafd8b6cb
--- /dev/null
+++ b/public/images/dialog/todo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/emoticon/06/icon.png b/public/images/emoticon/06/icon.png
index 405f9b93b..83bb59db6 100644
Binary files a/public/images/emoticon/06/icon.png and b/public/images/emoticon/06/icon.png differ
diff --git a/public/images/emoticon/08/110.gif b/public/images/emoticon/08/110.gif
new file mode 100644
index 000000000..58a61cda0
Binary files /dev/null and b/public/images/emoticon/08/110.gif differ
diff --git a/public/images/emoticon/08/666.gif b/public/images/emoticon/08/666.gif
new file mode 100644
index 000000000..7c7031438
Binary files /dev/null and b/public/images/emoticon/08/666.gif differ
diff --git a/public/images/emoticon/08/bixin.gif b/public/images/emoticon/08/bixin.gif
new file mode 100644
index 000000000..350d05230
Binary files /dev/null and b/public/images/emoticon/08/bixin.gif differ
diff --git a/public/images/emoticon/08/chigua.gif b/public/images/emoticon/08/chigua.gif
new file mode 100644
index 000000000..7a8d78edf
Binary files /dev/null and b/public/images/emoticon/08/chigua.gif differ
diff --git a/public/images/emoticon/08/enen.gif b/public/images/emoticon/08/enen.gif
new file mode 100644
index 000000000..16b841a0d
Binary files /dev/null and b/public/images/emoticon/08/enen.gif differ
diff --git a/public/images/emoticon/08/guaiqiao.gif b/public/images/emoticon/08/guaiqiao.gif
new file mode 100644
index 000000000..50b08650e
Binary files /dev/null and b/public/images/emoticon/08/guaiqiao.gif differ
diff --git a/public/images/emoticon/08/gun.gif b/public/images/emoticon/08/gun.gif
new file mode 100644
index 000000000..f4543493a
Binary files /dev/null and b/public/images/emoticon/08/gun.gif differ
diff --git a/public/images/emoticon/08/hahaha.gif b/public/images/emoticon/08/hahaha.gif
new file mode 100644
index 000000000..ef9c7b934
Binary files /dev/null and b/public/images/emoticon/08/hahaha.gif differ
diff --git a/public/images/emoticon/08/haixiu.gif b/public/images/emoticon/08/haixiu.gif
new file mode 100644
index 000000000..98e2200df
Binary files /dev/null and b/public/images/emoticon/08/haixiu.gif differ
diff --git a/public/images/emoticon/08/hehe.gif b/public/images/emoticon/08/hehe.gif
new file mode 100644
index 000000000..01930bc36
Binary files /dev/null and b/public/images/emoticon/08/hehe.gif differ
diff --git a/public/images/emoticon/08/huahua.gif b/public/images/emoticon/08/huahua.gif
new file mode 100644
index 000000000..8f3319ae1
Binary files /dev/null and b/public/images/emoticon/08/huahua.gif differ
diff --git a/public/images/emoticon/08/icon.png b/public/images/emoticon/08/icon.png
new file mode 100644
index 000000000..ea0e54426
Binary files /dev/null and b/public/images/emoticon/08/icon.png differ
diff --git a/public/images/emoticon/08/keai.gif b/public/images/emoticon/08/keai.gif
new file mode 100644
index 000000000..329169290
Binary files /dev/null and b/public/images/emoticon/08/keai.gif differ
diff --git a/public/images/emoticon/08/kuaichiyao.gif b/public/images/emoticon/08/kuaichiyao.gif
new file mode 100644
index 000000000..9224e14af
Binary files /dev/null and b/public/images/emoticon/08/kuaichiyao.gif differ
diff --git a/public/images/emoticon/08/laiba.gif b/public/images/emoticon/08/laiba.gif
new file mode 100644
index 000000000..d74c80bc0
Binary files /dev/null and b/public/images/emoticon/08/laiba.gif differ
diff --git a/public/images/emoticon/08/lengmo.gif b/public/images/emoticon/08/lengmo.gif
new file mode 100644
index 000000000..7fc9bfc19
Binary files /dev/null and b/public/images/emoticon/08/lengmo.gif differ
diff --git a/public/images/emoticon/08/ok.gif b/public/images/emoticon/08/ok.gif
new file mode 100644
index 000000000..b081018f9
Binary files /dev/null and b/public/images/emoticon/08/ok.gif differ
diff --git a/public/images/emoticon/08/tuxue.gif b/public/images/emoticon/08/tuxue.gif
new file mode 100644
index 000000000..fbf84bd94
Binary files /dev/null and b/public/images/emoticon/08/tuxue.gif differ
diff --git a/public/images/emoticon/08/wanan.gif b/public/images/emoticon/08/wanan.gif
new file mode 100644
index 000000000..4bf4cf257
Binary files /dev/null and b/public/images/emoticon/08/wanan.gif differ
diff --git a/public/images/emoticon/08/wenhao.gif b/public/images/emoticon/08/wenhao.gif
new file mode 100644
index 000000000..9588dd438
Binary files /dev/null and b/public/images/emoticon/08/wenhao.gif differ
diff --git a/public/images/emoticon/08/wofangle.gif b/public/images/emoticon/08/wofangle.gif
new file mode 100644
index 000000000..a64617c63
Binary files /dev/null and b/public/images/emoticon/08/wofangle.gif differ
diff --git a/public/images/emoticon/08/xiaozheliulei.gif b/public/images/emoticon/08/xiaozheliulei.gif
new file mode 100644
index 000000000..3f2641c53
Binary files /dev/null and b/public/images/emoticon/08/xiaozheliulei.gif differ
diff --git a/public/images/emoticon/08/xiexielaoban.gif b/public/images/emoticon/08/xiexielaoban.gif
new file mode 100644
index 000000000..b512d0157
Binary files /dev/null and b/public/images/emoticon/08/xiexielaoban.gif differ
diff --git a/public/images/emoticon/08/yongbao.gif b/public/images/emoticon/08/yongbao.gif
new file mode 100644
index 000000000..2258fc011
Binary files /dev/null and b/public/images/emoticon/08/yongbao.gif differ
diff --git a/public/images/emoticon/08/zan.gif b/public/images/emoticon/08/zan.gif
new file mode 100644
index 000000000..0eecfb4bc
Binary files /dev/null and b/public/images/emoticon/08/zan.gif differ
diff --git a/public/images/emoticon/09/aixin.gif b/public/images/emoticon/09/aixin.gif
new file mode 100644
index 000000000..9835c5cc0
Binary files /dev/null and b/public/images/emoticon/09/aixin.gif differ
diff --git a/public/images/emoticon/09/bixin.gif b/public/images/emoticon/09/bixin.gif
new file mode 100644
index 000000000..78ddf3f7e
Binary files /dev/null and b/public/images/emoticon/09/bixin.gif differ
diff --git a/public/images/emoticon/09/chongya.gif b/public/images/emoticon/09/chongya.gif
new file mode 100644
index 000000000..997997d30
Binary files /dev/null and b/public/images/emoticon/09/chongya.gif differ
diff --git a/public/images/emoticon/09/enen.gif b/public/images/emoticon/09/enen.gif
new file mode 100644
index 000000000..e1d20c379
Binary files /dev/null and b/public/images/emoticon/09/enen.gif differ
diff --git a/public/images/emoticon/09/ganma.gif b/public/images/emoticon/09/ganma.gif
new file mode 100644
index 000000000..8ac1c1ae5
Binary files /dev/null and b/public/images/emoticon/09/ganma.gif differ
diff --git a/public/images/emoticon/09/guzhang.gif b/public/images/emoticon/09/guzhang.gif
new file mode 100644
index 000000000..b7cbef234
Binary files /dev/null and b/public/images/emoticon/09/guzhang.gif differ
diff --git a/public/images/emoticon/09/hahaha.gif b/public/images/emoticon/09/hahaha.gif
new file mode 100644
index 000000000..8e8870c4b
Binary files /dev/null and b/public/images/emoticon/09/hahaha.gif differ
diff --git a/public/images/emoticon/09/haomeng.gif b/public/images/emoticon/09/haomeng.gif
new file mode 100644
index 000000000..e651bc093
Binary files /dev/null and b/public/images/emoticon/09/haomeng.gif differ
diff --git a/public/images/emoticon/09/huanhu.gif b/public/images/emoticon/09/huanhu.gif
new file mode 100644
index 000000000..55ed51108
Binary files /dev/null and b/public/images/emoticon/09/huanhu.gif differ
diff --git a/public/images/emoticon/09/icon.png b/public/images/emoticon/09/icon.png
new file mode 100644
index 000000000..6391ea6ba
Binary files /dev/null and b/public/images/emoticon/09/icon.png differ
diff --git a/public/images/emoticon/09/jiayi.gif b/public/images/emoticon/09/jiayi.gif
new file mode 100644
index 000000000..60428f35e
Binary files /dev/null and b/public/images/emoticon/09/jiayi.gif differ
diff --git a/public/images/emoticon/09/kuqi.gif b/public/images/emoticon/09/kuqi.gif
new file mode 100644
index 000000000..a60821612
Binary files /dev/null and b/public/images/emoticon/09/kuqi.gif differ
diff --git a/public/images/emoticon/09/maimaimai.gif b/public/images/emoticon/09/maimaimai.gif
new file mode 100644
index 000000000..93a0b9147
Binary files /dev/null and b/public/images/emoticon/09/maimaimai.gif differ
diff --git a/public/images/emoticon/09/nielian.gif b/public/images/emoticon/09/nielian.gif
new file mode 100644
index 000000000..f9d787df8
Binary files /dev/null and b/public/images/emoticon/09/nielian.gif differ
diff --git a/public/images/emoticon/09/nizou.gif b/public/images/emoticon/09/nizou.gif
new file mode 100644
index 000000000..c98f26e9e
Binary files /dev/null and b/public/images/emoticon/09/nizou.gif differ
diff --git a/public/images/emoticon/09/qidai.gif b/public/images/emoticon/09/qidai.gif
new file mode 100644
index 000000000..d71d70280
Binary files /dev/null and b/public/images/emoticon/09/qidai.gif differ
diff --git a/public/images/emoticon/09/songxiaohua.gif b/public/images/emoticon/09/songxiaohua.gif
new file mode 100644
index 000000000..ea05727fe
Binary files /dev/null and b/public/images/emoticon/09/songxiaohua.gif differ
diff --git a/public/images/emoticon/09/tiaoyue.gif b/public/images/emoticon/09/tiaoyue.gif
new file mode 100644
index 000000000..fca3574e7
Binary files /dev/null and b/public/images/emoticon/09/tiaoyue.gif differ
diff --git a/public/images/emoticon/09/tietie.gif b/public/images/emoticon/09/tietie.gif
new file mode 100644
index 000000000..9775543a4
Binary files /dev/null and b/public/images/emoticon/09/tietie.gif differ
diff --git a/public/images/emoticon/09/wuyu.gif b/public/images/emoticon/09/wuyu.gif
new file mode 100644
index 000000000..5ada17bc0
Binary files /dev/null and b/public/images/emoticon/09/wuyu.gif differ
diff --git a/public/images/emoticon/09/wuzu.gif b/public/images/emoticon/09/wuzu.gif
new file mode 100644
index 000000000..46f899bb3
Binary files /dev/null and b/public/images/emoticon/09/wuzu.gif differ
diff --git a/public/images/emoticon/09/yaobai.gif b/public/images/emoticon/09/yaobai.gif
new file mode 100644
index 000000000..37f28e28e
Binary files /dev/null and b/public/images/emoticon/09/yaobai.gif differ
diff --git a/public/images/emoticon/09/yiwen.gif b/public/images/emoticon/09/yiwen.gif
new file mode 100644
index 000000000..e12d5ef06
Binary files /dev/null and b/public/images/emoticon/09/yiwen.gif differ
diff --git a/public/images/emoticon/10/bengda.gif b/public/images/emoticon/10/bengda.gif
new file mode 100644
index 000000000..f28ca29f0
Binary files /dev/null and b/public/images/emoticon/10/bengda.gif differ
diff --git a/public/images/emoticon/10/bixin.gif b/public/images/emoticon/10/bixin.gif
new file mode 100644
index 000000000..4520b32fb
Binary files /dev/null and b/public/images/emoticon/10/bixin.gif differ
diff --git a/public/images/emoticon/10/chonga.gif b/public/images/emoticon/10/chonga.gif
new file mode 100644
index 000000000..3b3b358db
Binary files /dev/null and b/public/images/emoticon/10/chonga.gif differ
diff --git a/public/images/emoticon/10/dawoya.gif b/public/images/emoticon/10/dawoya.gif
new file mode 100644
index 000000000..529a49c5c
Binary files /dev/null and b/public/images/emoticon/10/dawoya.gif differ
diff --git a/public/images/emoticon/10/enen.gif b/public/images/emoticon/10/enen.gif
new file mode 100644
index 000000000..3d16a6573
Binary files /dev/null and b/public/images/emoticon/10/enen.gif differ
diff --git a/public/images/emoticon/10/halou.gif b/public/images/emoticon/10/halou.gif
new file mode 100644
index 000000000..4896c3a3d
Binary files /dev/null and b/public/images/emoticon/10/halou.gif differ
diff --git a/public/images/emoticon/10/heihei.gif b/public/images/emoticon/10/heihei.gif
new file mode 100644
index 000000000..4adf36556
Binary files /dev/null and b/public/images/emoticon/10/heihei.gif differ
diff --git a/public/images/emoticon/10/huaixiao.gif b/public/images/emoticon/10/huaixiao.gif
new file mode 100644
index 000000000..04da2cb69
Binary files /dev/null and b/public/images/emoticon/10/huaixiao.gif differ
diff --git a/public/images/emoticon/10/huanhu.gif b/public/images/emoticon/10/huanhu.gif
new file mode 100644
index 000000000..7830a9bb8
Binary files /dev/null and b/public/images/emoticon/10/huanhu.gif differ
diff --git a/public/images/emoticon/10/icon.png b/public/images/emoticon/10/icon.png
new file mode 100644
index 000000000..c31d0459f
Binary files /dev/null and b/public/images/emoticon/10/icon.png differ
diff --git a/public/images/emoticon/10/kanrenao.gif b/public/images/emoticon/10/kanrenao.gif
new file mode 100644
index 000000000..2bd7feadd
Binary files /dev/null and b/public/images/emoticon/10/kanrenao.gif differ
diff --git a/public/images/emoticon/10/kun.gif b/public/images/emoticon/10/kun.gif
new file mode 100644
index 000000000..0d869bbea
Binary files /dev/null and b/public/images/emoticon/10/kun.gif differ
diff --git a/public/images/emoticon/10/laiya.gif b/public/images/emoticon/10/laiya.gif
new file mode 100644
index 000000000..306a9e741
Binary files /dev/null and b/public/images/emoticon/10/laiya.gif differ
diff --git a/public/images/emoticon/10/toukan.gif b/public/images/emoticon/10/toukan.gif
new file mode 100644
index 000000000..4efeca36c
Binary files /dev/null and b/public/images/emoticon/10/toukan.gif differ
diff --git a/public/images/emoticon/10/zan.gif b/public/images/emoticon/10/zan.gif
new file mode 100644
index 000000000..9f60d43e9
Binary files /dev/null and b/public/images/emoticon/10/zan.gif differ
diff --git a/public/images/emoticon/10/zhenjing.gif b/public/images/emoticon/10/zhenjing.gif
new file mode 100644
index 000000000..8162029e4
Binary files /dev/null and b/public/images/emoticon/10/zhenjing.gif differ
diff --git a/public/images/emoticon/11/chouju.gif b/public/images/emoticon/11/chouju.gif
new file mode 100644
index 000000000..416e11de5
Binary files /dev/null and b/public/images/emoticon/11/chouju.gif differ
diff --git a/public/images/emoticon/11/daraole.gif b/public/images/emoticon/11/daraole.gif
new file mode 100644
index 000000000..b8b7e98e1
Binary files /dev/null and b/public/images/emoticon/11/daraole.gif differ
diff --git a/public/images/emoticon/11/emo.gif b/public/images/emoticon/11/emo.gif
new file mode 100644
index 000000000..bcbae96d7
Binary files /dev/null and b/public/images/emoticon/11/emo.gif differ
diff --git a/public/images/emoticon/11/fadou.gif b/public/images/emoticon/11/fadou.gif
new file mode 100644
index 000000000..910795924
Binary files /dev/null and b/public/images/emoticon/11/fadou.gif differ
diff --git a/public/images/emoticon/11/gandaoyali.gif b/public/images/emoticon/11/gandaoyali.gif
new file mode 100644
index 000000000..a4f2e4686
Binary files /dev/null and b/public/images/emoticon/11/gandaoyali.gif differ
diff --git a/public/images/emoticon/11/guzhang.gif b/public/images/emoticon/11/guzhang.gif
new file mode 100644
index 000000000..b5e68ac55
Binary files /dev/null and b/public/images/emoticon/11/guzhang.gif differ
diff --git a/public/images/emoticon/11/haode.gif b/public/images/emoticon/11/haode.gif
new file mode 100644
index 000000000..2c3af4d95
Binary files /dev/null and b/public/images/emoticon/11/haode.gif differ
diff --git a/public/images/emoticon/11/icon.png b/public/images/emoticon/11/icon.png
new file mode 100644
index 000000000..3ca23db89
Binary files /dev/null and b/public/images/emoticon/11/icon.png differ
diff --git a/public/images/emoticon/11/kaixxin.gif b/public/images/emoticon/11/kaixxin.gif
new file mode 100644
index 000000000..61634a730
Binary files /dev/null and b/public/images/emoticon/11/kaixxin.gif differ
diff --git a/public/images/emoticon/11/laile.gif b/public/images/emoticon/11/laile.gif
new file mode 100644
index 000000000..06160c226
Binary files /dev/null and b/public/images/emoticon/11/laile.gif differ
diff --git a/public/images/emoticon/11/no.gif b/public/images/emoticon/11/no.gif
new file mode 100644
index 000000000..a68bd5cd6
Binary files /dev/null and b/public/images/emoticon/11/no.gif differ
diff --git a/public/images/emoticon/11/ok.gif b/public/images/emoticon/11/ok.gif
new file mode 100644
index 000000000..8fab1404d
Binary files /dev/null and b/public/images/emoticon/11/ok.gif differ
diff --git a/public/images/emoticon/11/qiong.gif b/public/images/emoticon/11/qiong.gif
new file mode 100644
index 000000000..882c6cf64
Binary files /dev/null and b/public/images/emoticon/11/qiong.gif differ
diff --git a/public/images/emoticon/11/wa.gif b/public/images/emoticon/11/wa.gif
new file mode 100644
index 000000000..45db5992d
Binary files /dev/null and b/public/images/emoticon/11/wa.gif differ
diff --git a/public/images/emoticon/11/wobuting.gif b/public/images/emoticon/11/wobuting.gif
new file mode 100644
index 000000000..811d08c7f
Binary files /dev/null and b/public/images/emoticon/11/wobuting.gif differ
diff --git a/public/images/emoticon/11/xiexie.gif b/public/images/emoticon/11/xiexie.gif
new file mode 100644
index 000000000..56fcf169f
Binary files /dev/null and b/public/images/emoticon/11/xiexie.gif differ
diff --git a/public/images/emoticon/11/xingba.gif b/public/images/emoticon/11/xingba.gif
new file mode 100644
index 000000000..91508aa5a
Binary files /dev/null and b/public/images/emoticon/11/xingba.gif differ
diff --git a/public/images/emoticon/11/xinnigegui.gif b/public/images/emoticon/11/xinnigegui.gif
new file mode 100644
index 000000000..a7853421d
Binary files /dev/null and b/public/images/emoticon/11/xinnigegui.gif differ
diff --git a/public/images/emoticon/11/yiwen.gif b/public/images/emoticon/11/yiwen.gif
new file mode 100644
index 000000000..fcce244b7
Binary files /dev/null and b/public/images/emoticon/11/yiwen.gif differ
diff --git a/public/images/emoticon/12/bendan.gif b/public/images/emoticon/12/bendan.gif
new file mode 100644
index 000000000..098f29a2d
Binary files /dev/null and b/public/images/emoticon/12/bendan.gif differ
diff --git a/public/images/emoticon/12/buhuole.gif b/public/images/emoticon/12/buhuole.gif
new file mode 100644
index 000000000..6efa47433
Binary files /dev/null and b/public/images/emoticon/12/buhuole.gif differ
diff --git a/public/images/emoticon/12/chi.gif b/public/images/emoticon/12/chi.gif
new file mode 100644
index 000000000..a78615e9e
Binary files /dev/null and b/public/images/emoticon/12/chi.gif differ
diff --git a/public/images/emoticon/12/hahaha.gif b/public/images/emoticon/12/hahaha.gif
new file mode 100644
index 000000000..62cd17620
Binary files /dev/null and b/public/images/emoticon/12/hahaha.gif differ
diff --git a/public/images/emoticon/12/haobang.gif b/public/images/emoticon/12/haobang.gif
new file mode 100644
index 000000000..491a00129
Binary files /dev/null and b/public/images/emoticon/12/haobang.gif differ
diff --git a/public/images/emoticon/12/haode.gif b/public/images/emoticon/12/haode.gif
new file mode 100644
index 000000000..ea1ae98d3
Binary files /dev/null and b/public/images/emoticon/12/haode.gif differ
diff --git a/public/images/emoticon/12/icon.png b/public/images/emoticon/12/icon.png
new file mode 100644
index 000000000..8137563e7
Binary files /dev/null and b/public/images/emoticon/12/icon.png differ
diff --git a/public/images/emoticon/12/nanguo.gif b/public/images/emoticon/12/nanguo.gif
new file mode 100644
index 000000000..70c365a16
Binary files /dev/null and b/public/images/emoticon/12/nanguo.gif differ
diff --git a/public/images/emoticon/12/wa.gif b/public/images/emoticon/12/wa.gif
new file mode 100644
index 000000000..ad4087bc7
Binary files /dev/null and b/public/images/emoticon/12/wa.gif differ
diff --git a/public/images/emoticon/12/weiguan.gif b/public/images/emoticon/12/weiguan.gif
new file mode 100644
index 000000000..907906956
Binary files /dev/null and b/public/images/emoticon/12/weiguan.gif differ
diff --git a/public/images/emoticon/12/wocuole.gif b/public/images/emoticon/12/wocuole.gif
new file mode 100644
index 000000000..a69799e29
Binary files /dev/null and b/public/images/emoticon/12/wocuole.gif differ
diff --git a/public/images/emoticon/12/xiaoku.gif b/public/images/emoticon/12/xiaoku.gif
new file mode 100644
index 000000000..1ddaff2cd
Binary files /dev/null and b/public/images/emoticon/12/xiaoku.gif differ
diff --git a/public/images/emoticon/13/666.gif b/public/images/emoticon/13/666.gif
new file mode 100644
index 000000000..f26e2db26
Binary files /dev/null and b/public/images/emoticon/13/666.gif differ
diff --git a/public/images/emoticon/13/chehui.gif b/public/images/emoticon/13/chehui.gif
new file mode 100644
index 000000000..505c5da2f
Binary files /dev/null and b/public/images/emoticon/13/chehui.gif differ
diff --git a/public/images/emoticon/13/deyi.gif b/public/images/emoticon/13/deyi.gif
new file mode 100644
index 000000000..bd547cb75
Binary files /dev/null and b/public/images/emoticon/13/deyi.gif differ
diff --git a/public/images/emoticon/13/enen.gif b/public/images/emoticon/13/enen.gif
new file mode 100644
index 000000000..84e3c8814
Binary files /dev/null and b/public/images/emoticon/13/enen.gif differ
diff --git a/public/images/emoticon/13/hahaha.gif b/public/images/emoticon/13/hahaha.gif
new file mode 100644
index 000000000..7cbb6a896
Binary files /dev/null and b/public/images/emoticon/13/hahaha.gif differ
diff --git a/public/images/emoticon/13/heng.gif b/public/images/emoticon/13/heng.gif
new file mode 100644
index 000000000..a44389ca3
Binary files /dev/null and b/public/images/emoticon/13/heng.gif differ
diff --git a/public/images/emoticon/13/hobang.gif b/public/images/emoticon/13/hobang.gif
new file mode 100644
index 000000000..1d8ab8370
Binary files /dev/null and b/public/images/emoticon/13/hobang.gif differ
diff --git a/public/images/emoticon/13/icon.png b/public/images/emoticon/13/icon.png
new file mode 100644
index 000000000..4c42d1879
Binary files /dev/null and b/public/images/emoticon/13/icon.png differ
diff --git a/public/images/emoticon/13/laile.gif b/public/images/emoticon/13/laile.gif
new file mode 100644
index 000000000..47d9854c5
Binary files /dev/null and b/public/images/emoticon/13/laile.gif differ
diff --git a/public/images/emoticon/13/ok.gif b/public/images/emoticon/13/ok.gif
new file mode 100644
index 000000000..f6b9e4fcb
Binary files /dev/null and b/public/images/emoticon/13/ok.gif differ
diff --git a/public/images/emoticon/13/tanjita.gif b/public/images/emoticon/13/tanjita.gif
new file mode 100644
index 000000000..0ca57e272
Binary files /dev/null and b/public/images/emoticon/13/tanjita.gif differ
diff --git a/public/images/emoticon/13/tietie.gif b/public/images/emoticon/13/tietie.gif
new file mode 100644
index 000000000..a3ed644eb
Binary files /dev/null and b/public/images/emoticon/13/tietie.gif differ
diff --git a/public/images/emoticon/13/wawa.gif b/public/images/emoticon/13/wawa.gif
new file mode 100644
index 000000000..d3cbbb000
Binary files /dev/null and b/public/images/emoticon/13/wawa.gif differ
diff --git a/public/images/emoticon/13/xiexie.gif b/public/images/emoticon/13/xiexie.gif
new file mode 100644
index 000000000..4c5ae677e
Binary files /dev/null and b/public/images/emoticon/13/xiexie.gif differ
diff --git a/public/images/emoticon/13/xinkule.gif b/public/images/emoticon/13/xinkule.gif
new file mode 100644
index 000000000..f3f21ea08
Binary files /dev/null and b/public/images/emoticon/13/xinkule.gif differ
diff --git a/public/images/emoticon/14/aini.gif b/public/images/emoticon/14/aini.gif
new file mode 100644
index 000000000..a3bae308f
Binary files /dev/null and b/public/images/emoticon/14/aini.gif differ
diff --git a/public/images/emoticon/14/biewenwoshuohua.gif b/public/images/emoticon/14/biewenwoshuohua.gif
new file mode 100644
index 000000000..8398138f8
Binary files /dev/null and b/public/images/emoticon/14/biewenwoshuohua.gif differ
diff --git a/public/images/emoticon/14/chifanlema.gif b/public/images/emoticon/14/chifanlema.gif
new file mode 100644
index 000000000..b231179c6
Binary files /dev/null and b/public/images/emoticon/14/chifanlema.gif differ
diff --git a/public/images/emoticon/14/duohekaishui.gif b/public/images/emoticon/14/duohekaishui.gif
new file mode 100644
index 000000000..f9c3e9170
Binary files /dev/null and b/public/images/emoticon/14/duohekaishui.gif differ
diff --git a/public/images/emoticon/14/haobang.gif b/public/images/emoticon/14/haobang.gif
new file mode 100644
index 000000000..790fe4f76
Binary files /dev/null and b/public/images/emoticon/14/haobang.gif differ
diff --git a/public/images/emoticon/14/icon.png b/public/images/emoticon/14/icon.png
new file mode 100644
index 000000000..3f4d1b89d
Binary files /dev/null and b/public/images/emoticon/14/icon.png differ
diff --git a/public/images/emoticon/14/kuaiquchiba.gif b/public/images/emoticon/14/kuaiquchiba.gif
new file mode 100644
index 000000000..212066c94
Binary files /dev/null and b/public/images/emoticon/14/kuaiquchiba.gif differ
diff --git a/public/images/emoticon/14/mashangchufa.gif b/public/images/emoticon/14/mashangchufa.gif
new file mode 100644
index 000000000..8eb15590f
Binary files /dev/null and b/public/images/emoticon/14/mashangchufa.gif differ
diff --git a/public/images/emoticon/14/nizaiganma.gif b/public/images/emoticon/14/nizaiganma.gif
new file mode 100644
index 000000000..74bb1e866
Binary files /dev/null and b/public/images/emoticon/14/nizaiganma.gif differ
diff --git a/public/images/emoticon/14/suibianla.gif b/public/images/emoticon/14/suibianla.gif
new file mode 100644
index 000000000..4f3cecab3
Binary files /dev/null and b/public/images/emoticon/14/suibianla.gif differ
diff --git a/public/images/emoticon/14/wanan.gif b/public/images/emoticon/14/wanan.gif
new file mode 100644
index 000000000..a982d3d92
Binary files /dev/null and b/public/images/emoticon/14/wanan.gif differ
diff --git a/public/images/emoticon/14/wodaole.gif b/public/images/emoticon/14/wodaole.gif
new file mode 100644
index 000000000..2d5357ebe
Binary files /dev/null and b/public/images/emoticon/14/wodaole.gif differ
diff --git a/public/images/emoticon/14/wodeyanlizhiyouni.gif b/public/images/emoticon/14/wodeyanlizhiyouni.gif
new file mode 100644
index 000000000..47b63fe4f
Binary files /dev/null and b/public/images/emoticon/14/wodeyanlizhiyouni.gif differ
diff --git a/public/images/emoticon/14/wozaimang.gif b/public/images/emoticon/14/wozaimang.gif
new file mode 100644
index 000000000..e6ffac6e6
Binary files /dev/null and b/public/images/emoticon/14/wozaimang.gif differ
diff --git a/public/images/emoticon/14/xiangni.gif b/public/images/emoticon/14/xiangni.gif
new file mode 100644
index 000000000..1ba8e6547
Binary files /dev/null and b/public/images/emoticon/14/xiangni.gif differ
diff --git a/public/images/emoticon/14/xihuanjiumai.gif b/public/images/emoticon/14/xihuanjiumai.gif
new file mode 100644
index 000000000..f690d6911
Binary files /dev/null and b/public/images/emoticon/14/xihuanjiumai.gif differ
diff --git a/public/images/emoticon/14/zaoan.gif b/public/images/emoticon/14/zaoan.gif
new file mode 100644
index 000000000..75e21efad
Binary files /dev/null and b/public/images/emoticon/14/zaoan.gif differ
diff --git a/public/images/emoticon/15/baoqian.gif b/public/images/emoticon/15/baoqian.gif
new file mode 100644
index 000000000..9c37bd9b9
Binary files /dev/null and b/public/images/emoticon/15/baoqian.gif differ
diff --git a/public/images/emoticon/15/bixin.gif b/public/images/emoticon/15/bixin.gif
new file mode 100644
index 000000000..a154adbc8
Binary files /dev/null and b/public/images/emoticon/15/bixin.gif differ
diff --git a/public/images/emoticon/15/chuini.gif b/public/images/emoticon/15/chuini.gif
new file mode 100644
index 000000000..2e289cea1
Binary files /dev/null and b/public/images/emoticon/15/chuini.gif differ
diff --git a/public/images/emoticon/15/enen.gif b/public/images/emoticon/15/enen.gif
new file mode 100644
index 000000000..fd7685b34
Binary files /dev/null and b/public/images/emoticon/15/enen.gif differ
diff --git a/public/images/emoticon/15/haixiu.gif b/public/images/emoticon/15/haixiu.gif
new file mode 100644
index 000000000..0816c33ff
Binary files /dev/null and b/public/images/emoticon/15/haixiu.gif differ
diff --git a/public/images/emoticon/15/heng.gif b/public/images/emoticon/15/heng.gif
new file mode 100644
index 000000000..8ac6047dc
Binary files /dev/null and b/public/images/emoticon/15/heng.gif differ
diff --git a/public/images/emoticon/15/huanhu.gif b/public/images/emoticon/15/huanhu.gif
new file mode 100644
index 000000000..38e8757a0
Binary files /dev/null and b/public/images/emoticon/15/huanhu.gif differ
diff --git a/public/images/emoticon/15/huanying.gif b/public/images/emoticon/15/huanying.gif
new file mode 100644
index 000000000..527b0c0b2
Binary files /dev/null and b/public/images/emoticon/15/huanying.gif differ
diff --git a/public/images/emoticon/15/icon.png b/public/images/emoticon/15/icon.png
new file mode 100644
index 000000000..3b8272bc3
Binary files /dev/null and b/public/images/emoticon/15/icon.png differ
diff --git a/public/images/emoticon/15/kaihuile.gif b/public/images/emoticon/15/kaihuile.gif
new file mode 100644
index 000000000..6d6b2f2b9
Binary files /dev/null and b/public/images/emoticon/15/kaihuile.gif differ
diff --git a/public/images/emoticon/15/kunao.gif b/public/images/emoticon/15/kunao.gif
new file mode 100644
index 000000000..6d4e1ee85
Binary files /dev/null and b/public/images/emoticon/15/kunao.gif differ
diff --git a/public/images/emoticon/15/kuqi.gif b/public/images/emoticon/15/kuqi.gif
new file mode 100644
index 000000000..c544e3f9f
Binary files /dev/null and b/public/images/emoticon/15/kuqi.gif differ
diff --git a/public/images/emoticon/15/laile.gif b/public/images/emoticon/15/laile.gif
new file mode 100644
index 000000000..8fc84c8b6
Binary files /dev/null and b/public/images/emoticon/15/laile.gif differ
diff --git a/public/images/emoticon/15/lihaile.gif b/public/images/emoticon/15/lihaile.gif
new file mode 100644
index 000000000..a1884f673
Binary files /dev/null and b/public/images/emoticon/15/lihaile.gif differ
diff --git a/public/images/emoticon/15/nuligongzuo.gif b/public/images/emoticon/15/nuligongzuo.gif
new file mode 100644
index 000000000..03964293e
Binary files /dev/null and b/public/images/emoticon/15/nuligongzuo.gif differ
diff --git a/public/images/emoticon/15/shoudao.gif b/public/images/emoticon/15/shoudao.gif
new file mode 100644
index 000000000..06f76bb9c
Binary files /dev/null and b/public/images/emoticon/15/shoudao.gif differ
diff --git a/public/images/emoticon/15/tietie.gif b/public/images/emoticon/15/tietie.gif
new file mode 100644
index 000000000..65005c556
Binary files /dev/null and b/public/images/emoticon/15/tietie.gif differ
diff --git a/public/images/emoticon/15/touyun.gif b/public/images/emoticon/15/touyun.gif
new file mode 100644
index 000000000..7f9db5cec
Binary files /dev/null and b/public/images/emoticon/15/touyun.gif differ
diff --git a/public/images/emoticon/15/wanan.gif b/public/images/emoticon/15/wanan.gif
new file mode 100644
index 000000000..d418078c0
Binary files /dev/null and b/public/images/emoticon/15/wanan.gif differ
diff --git a/public/images/emoticon/15/xiexie.gif b/public/images/emoticon/15/xiexie.gif
new file mode 100644
index 000000000..0a136b416
Binary files /dev/null and b/public/images/emoticon/15/xiexie.gif differ
diff --git a/public/images/emoticon/15/xiexielaoban.gif b/public/images/emoticon/15/xiexielaoban.gif
new file mode 100644
index 000000000..8d3f8263b
Binary files /dev/null and b/public/images/emoticon/15/xiexielaoban.gif differ
diff --git a/public/images/emoticon/15/yiwen.gif b/public/images/emoticon/15/yiwen.gif
new file mode 100644
index 000000000..a36339ffe
Binary files /dev/null and b/public/images/emoticon/15/yiwen.gif differ
diff --git a/public/images/emoticon/15/yuanliang.gif b/public/images/emoticon/15/yuanliang.gif
new file mode 100644
index 000000000..04edd3a57
Binary files /dev/null and b/public/images/emoticon/15/yuanliang.gif differ
diff --git a/public/images/emoticon/15/zaima.gif b/public/images/emoticon/15/zaima.gif
new file mode 100644
index 000000000..7f64a2b08
Binary files /dev/null and b/public/images/emoticon/15/zaima.gif differ
diff --git a/public/images/emoticon/15/zaoan.gif b/public/images/emoticon/15/zaoan.gif
new file mode 100644
index 000000000..1eef15478
Binary files /dev/null and b/public/images/emoticon/15/zaoan.gif differ
diff --git a/public/images/emoticon/16/baozaiwoshenshang.gif b/public/images/emoticon/16/baozaiwoshenshang.gif
new file mode 100644
index 000000000..13078c450
Binary files /dev/null and b/public/images/emoticon/16/baozaiwoshenshang.gif differ
diff --git a/public/images/emoticon/16/baozhibaoliang.gif b/public/images/emoticon/16/baozhibaoliang.gif
new file mode 100644
index 000000000..f09dd0767
Binary files /dev/null and b/public/images/emoticon/16/baozhibaoliang.gif differ
diff --git a/public/images/emoticon/16/buyijia.gif b/public/images/emoticon/16/buyijia.gif
new file mode 100644
index 000000000..3456c8a30
Binary files /dev/null and b/public/images/emoticon/16/buyijia.gif differ
diff --git a/public/images/emoticon/16/daraole.gif b/public/images/emoticon/16/daraole.gif
new file mode 100644
index 000000000..ce4efc2fd
Binary files /dev/null and b/public/images/emoticon/16/daraole.gif differ
diff --git a/public/images/emoticon/16/facaile.gif b/public/images/emoticon/16/facaile.gif
new file mode 100644
index 000000000..38aa1b7c6
Binary files /dev/null and b/public/images/emoticon/16/facaile.gif differ
diff --git a/public/images/emoticon/16/henweinan.gif b/public/images/emoticon/16/henweinan.gif
new file mode 100644
index 000000000..ebce5e43a
Binary files /dev/null and b/public/images/emoticon/16/henweinan.gif differ
diff --git a/public/images/emoticon/16/hezuoyukuai.gif b/public/images/emoticon/16/hezuoyukuai.gif
new file mode 100644
index 000000000..224a1c316
Binary files /dev/null and b/public/images/emoticon/16/hezuoyukuai.gif differ
diff --git a/public/images/emoticon/16/icon.png b/public/images/emoticon/16/icon.png
new file mode 100644
index 000000000..59502d8c3
Binary files /dev/null and b/public/images/emoticon/16/icon.png differ
diff --git a/public/images/emoticon/16/kanyixia.gif b/public/images/emoticon/16/kanyixia.gif
new file mode 100644
index 000000000..0a2142f27
Binary files /dev/null and b/public/images/emoticon/16/kanyixia.gif differ
diff --git a/public/images/emoticon/16/keqile.gif b/public/images/emoticon/16/keqile.gif
new file mode 100644
index 000000000..5399c3d0e
Binary files /dev/null and b/public/images/emoticon/16/keqile.gif differ
diff --git a/public/images/emoticon/16/meibanfa.gif b/public/images/emoticon/16/meibanfa.gif
new file mode 100644
index 000000000..03ad0c303
Binary files /dev/null and b/public/images/emoticon/16/meibanfa.gif differ
diff --git a/public/images/emoticon/16/meiwenti.gif b/public/images/emoticon/16/meiwenti.gif
new file mode 100644
index 000000000..b91b1f662
Binary files /dev/null and b/public/images/emoticon/16/meiwenti.gif differ
diff --git a/public/images/emoticon/16/nihao.gif b/public/images/emoticon/16/nihao.gif
new file mode 100644
index 000000000..debf1fa66
Binary files /dev/null and b/public/images/emoticon/16/nihao.gif differ
diff --git a/public/images/emoticon/16/nikanxingma.gif b/public/images/emoticon/16/nikanxingma.gif
new file mode 100644
index 000000000..8ba6937c4
Binary files /dev/null and b/public/images/emoticon/16/nikanxingma.gif differ
diff --git a/public/images/emoticon/16/ninxinkule.gif b/public/images/emoticon/16/ninxinkule.gif
new file mode 100644
index 000000000..a61f5cfaf
Binary files /dev/null and b/public/images/emoticon/16/ninxinkule.gif differ
diff --git a/public/images/emoticon/16/pianyidian.gif b/public/images/emoticon/16/pianyidian.gif
new file mode 100644
index 000000000..251db0f89
Binary files /dev/null and b/public/images/emoticon/16/pianyidian.gif differ
diff --git a/public/images/emoticon/16/shaodeng.gif b/public/images/emoticon/16/shaodeng.gif
new file mode 100644
index 000000000..0e2766348
Binary files /dev/null and b/public/images/emoticon/16/shaodeng.gif differ
diff --git a/public/images/emoticon/16/shoudao.gif b/public/images/emoticon/16/shoudao.gif
new file mode 100644
index 000000000..70af4dcd5
Binary files /dev/null and b/public/images/emoticon/16/shoudao.gif differ
diff --git a/public/images/emoticon/16/wolaile.gif b/public/images/emoticon/16/wolaile.gif
new file mode 100644
index 000000000..3d2a52c97
Binary files /dev/null and b/public/images/emoticon/16/wolaile.gif differ
diff --git a/public/images/emoticon/16/xiexie.gif b/public/images/emoticon/16/xiexie.gif
new file mode 100644
index 000000000..a9b2d02b0
Binary files /dev/null and b/public/images/emoticon/16/xiexie.gif differ
diff --git a/public/images/emoticon/16/xiexielaoban.gif b/public/images/emoticon/16/xiexielaoban.gif
new file mode 100644
index 000000000..bf0d10d7d
Binary files /dev/null and b/public/images/emoticon/16/xiexielaoban.gif differ
diff --git a/public/images/emoticon/16/zaima.gif b/public/images/emoticon/16/zaima.gif
new file mode 100644
index 000000000..e7ada138e
Binary files /dev/null and b/public/images/emoticon/16/zaima.gif differ
diff --git a/public/images/emoticon/16/zaoan.gif b/public/images/emoticon/16/zaoan.gif
new file mode 100644
index 000000000..027f6394e
Binary files /dev/null and b/public/images/emoticon/16/zaoan.gif differ
diff --git a/public/images/emoticon/16/zhidaole.gif b/public/images/emoticon/16/zhidaole.gif
new file mode 100644
index 000000000..c01736047
Binary files /dev/null and b/public/images/emoticon/16/zhidaole.gif differ
diff --git a/public/images/emoticon/16/zuidijiale.gif b/public/images/emoticon/16/zuidijiale.gif
new file mode 100644
index 000000000..0a9f82a57
Binary files /dev/null and b/public/images/emoticon/16/zuidijiale.gif differ
diff --git a/public/images/emoticon/17/baituo.gif b/public/images/emoticon/17/baituo.gif
new file mode 100644
index 000000000..3b38b8bce
Binary files /dev/null and b/public/images/emoticon/17/baituo.gif differ
diff --git a/public/images/emoticon/17/buxiangshangban.gif b/public/images/emoticon/17/buxiangshangban.gif
new file mode 100644
index 000000000..fa321c454
Binary files /dev/null and b/public/images/emoticon/17/buxiangshangban.gif differ
diff --git a/public/images/emoticon/17/chisha.gif b/public/images/emoticon/17/chisha.gif
new file mode 100644
index 000000000..559ad992a
Binary files /dev/null and b/public/images/emoticon/17/chisha.gif differ
diff --git a/public/images/emoticon/17/goodjod.gif b/public/images/emoticon/17/goodjod.gif
new file mode 100644
index 000000000..2f7ee0b4c
Binary files /dev/null and b/public/images/emoticon/17/goodjod.gif differ
diff --git a/public/images/emoticon/17/hezuoyukuai.gif b/public/images/emoticon/17/hezuoyukuai.gif
new file mode 100644
index 000000000..17bbce744
Binary files /dev/null and b/public/images/emoticon/17/hezuoyukuai.gif differ
diff --git a/public/images/emoticon/17/icon.png b/public/images/emoticon/17/icon.png
new file mode 100644
index 000000000..095fc2b5c
Binary files /dev/null and b/public/images/emoticon/17/icon.png differ
diff --git a/public/images/emoticon/17/jiaban.gif b/public/images/emoticon/17/jiaban.gif
new file mode 100644
index 000000000..e4baaa23f
Binary files /dev/null and b/public/images/emoticon/17/jiaban.gif differ
diff --git a/public/images/emoticon/17/jiagongzi.gif b/public/images/emoticon/17/jiagongzi.gif
new file mode 100644
index 000000000..06ec7034f
Binary files /dev/null and b/public/images/emoticon/17/jiagongzi.gif differ
diff --git a/public/images/emoticon/17/kaihuile.gif b/public/images/emoticon/17/kaihuile.gif
new file mode 100644
index 000000000..7caa1b312
Binary files /dev/null and b/public/images/emoticon/17/kaihuile.gif differ
diff --git a/public/images/emoticon/17/kougongzi.gif b/public/images/emoticon/17/kougongzi.gif
new file mode 100644
index 000000000..ea958da39
Binary files /dev/null and b/public/images/emoticon/17/kougongzi.gif differ
diff --git a/public/images/emoticon/17/kuaiganhuo.gif b/public/images/emoticon/17/kuaiganhuo.gif
new file mode 100644
index 000000000..cef0ac53d
Binary files /dev/null and b/public/images/emoticon/17/kuaiganhuo.gif differ
diff --git a/public/images/emoticon/17/liaojie.gif b/public/images/emoticon/17/liaojie.gif
new file mode 100644
index 000000000..919d81b75
Binary files /dev/null and b/public/images/emoticon/17/liaojie.gif differ
diff --git a/public/images/emoticon/17/mang.gif b/public/images/emoticon/17/mang.gif
new file mode 100644
index 000000000..10f7e3b6c
Binary files /dev/null and b/public/images/emoticon/17/mang.gif differ
diff --git a/public/images/emoticon/17/moyu.gif b/public/images/emoticon/17/moyu.gif
new file mode 100644
index 000000000..2c7ac467d
Binary files /dev/null and b/public/images/emoticon/17/moyu.gif differ
diff --git a/public/images/emoticon/17/shoudao.gif b/public/images/emoticon/17/shoudao.gif
new file mode 100644
index 000000000..1838ae7d9
Binary files /dev/null and b/public/images/emoticon/17/shoudao.gif differ
diff --git a/public/images/emoticon/17/sorry.gif b/public/images/emoticon/17/sorry.gif
new file mode 100644
index 000000000..eb79fddda
Binary files /dev/null and b/public/images/emoticon/17/sorry.gif differ
diff --git a/public/images/emoticon/17/woaigongzuo.gif b/public/images/emoticon/17/woaigongzuo.gif
new file mode 100644
index 000000000..af020f10a
Binary files /dev/null and b/public/images/emoticon/17/woaigongzuo.gif differ
diff --git a/public/images/emoticon/17/xiabanle.gif b/public/images/emoticon/17/xiabanle.gif
new file mode 100644
index 000000000..49457dd71
Binary files /dev/null and b/public/images/emoticon/17/xiabanle.gif differ
diff --git a/public/images/emoticon/17/xiexie.gif b/public/images/emoticon/17/xiexie.gif
new file mode 100644
index 000000000..9248db6ad
Binary files /dev/null and b/public/images/emoticon/17/xiexie.gif differ
diff --git a/public/images/emoticon/17/xinkule.gif b/public/images/emoticon/17/xinkule.gif
new file mode 100644
index 000000000..ae18a5987
Binary files /dev/null and b/public/images/emoticon/17/xinkule.gif differ
diff --git a/public/images/emoticon/17/youxiu.gif b/public/images/emoticon/17/youxiu.gif
new file mode 100644
index 000000000..c2d9bcf82
Binary files /dev/null and b/public/images/emoticon/17/youxiu.gif differ
diff --git a/public/images/emoticon/17/zaima.gif b/public/images/emoticon/17/zaima.gif
new file mode 100644
index 000000000..ca78a4b51
Binary files /dev/null and b/public/images/emoticon/17/zaima.gif differ
diff --git a/public/images/emoticon/17/zaixiandeng.gif b/public/images/emoticon/17/zaixiandeng.gif
new file mode 100644
index 000000000..018e4ba7e
Binary files /dev/null and b/public/images/emoticon/17/zaixiandeng.gif differ
diff --git a/public/images/emoticon/17/zhouwula.gif b/public/images/emoticon/17/zhouwula.gif
new file mode 100644
index 000000000..8c7c78490
Binary files /dev/null and b/public/images/emoticon/17/zhouwula.gif differ
diff --git a/public/images/emoticon/18/1.gif b/public/images/emoticon/18/1.gif
new file mode 100644
index 000000000..b334b6009
Binary files /dev/null and b/public/images/emoticon/18/1.gif differ
diff --git a/public/images/emoticon/18/10.gif b/public/images/emoticon/18/10.gif
new file mode 100644
index 000000000..ef505fa5e
Binary files /dev/null and b/public/images/emoticon/18/10.gif differ
diff --git a/public/images/emoticon/18/11.gif b/public/images/emoticon/18/11.gif
new file mode 100644
index 000000000..c0111c16b
Binary files /dev/null and b/public/images/emoticon/18/11.gif differ
diff --git a/public/images/emoticon/18/12.gif b/public/images/emoticon/18/12.gif
new file mode 100644
index 000000000..5cde8ada6
Binary files /dev/null and b/public/images/emoticon/18/12.gif differ
diff --git a/public/images/emoticon/18/13.gif b/public/images/emoticon/18/13.gif
new file mode 100644
index 000000000..753ce4f7e
Binary files /dev/null and b/public/images/emoticon/18/13.gif differ
diff --git a/public/images/emoticon/18/14.gif b/public/images/emoticon/18/14.gif
new file mode 100644
index 000000000..31d3970cb
Binary files /dev/null and b/public/images/emoticon/18/14.gif differ
diff --git a/public/images/emoticon/18/15.gif b/public/images/emoticon/18/15.gif
new file mode 100644
index 000000000..c8128bddd
Binary files /dev/null and b/public/images/emoticon/18/15.gif differ
diff --git a/public/images/emoticon/18/16.gif b/public/images/emoticon/18/16.gif
new file mode 100644
index 000000000..90e8ec70b
Binary files /dev/null and b/public/images/emoticon/18/16.gif differ
diff --git a/public/images/emoticon/18/17.gif b/public/images/emoticon/18/17.gif
new file mode 100644
index 000000000..e479895e5
Binary files /dev/null and b/public/images/emoticon/18/17.gif differ
diff --git a/public/images/emoticon/18/18.gif b/public/images/emoticon/18/18.gif
new file mode 100644
index 000000000..59ec47c4e
Binary files /dev/null and b/public/images/emoticon/18/18.gif differ
diff --git a/public/images/emoticon/18/19.gif b/public/images/emoticon/18/19.gif
new file mode 100644
index 000000000..a612b4f81
Binary files /dev/null and b/public/images/emoticon/18/19.gif differ
diff --git a/public/images/emoticon/18/2.gif b/public/images/emoticon/18/2.gif
new file mode 100644
index 000000000..710637a09
Binary files /dev/null and b/public/images/emoticon/18/2.gif differ
diff --git a/public/images/emoticon/18/20.gif b/public/images/emoticon/18/20.gif
new file mode 100644
index 000000000..4861c1287
Binary files /dev/null and b/public/images/emoticon/18/20.gif differ
diff --git a/public/images/emoticon/18/21.gif b/public/images/emoticon/18/21.gif
new file mode 100644
index 000000000..41cc3313f
Binary files /dev/null and b/public/images/emoticon/18/21.gif differ
diff --git a/public/images/emoticon/18/22.gif b/public/images/emoticon/18/22.gif
new file mode 100644
index 000000000..bce8868ec
Binary files /dev/null and b/public/images/emoticon/18/22.gif differ
diff --git a/public/images/emoticon/18/23.gif b/public/images/emoticon/18/23.gif
new file mode 100644
index 000000000..3fcc8d252
Binary files /dev/null and b/public/images/emoticon/18/23.gif differ
diff --git a/public/images/emoticon/18/24.gif b/public/images/emoticon/18/24.gif
new file mode 100644
index 000000000..bf4cd1b77
Binary files /dev/null and b/public/images/emoticon/18/24.gif differ
diff --git a/public/images/emoticon/18/25.gif b/public/images/emoticon/18/25.gif
new file mode 100644
index 000000000..2449b09ec
Binary files /dev/null and b/public/images/emoticon/18/25.gif differ
diff --git a/public/images/emoticon/18/26.gif b/public/images/emoticon/18/26.gif
new file mode 100644
index 000000000..a7eede251
Binary files /dev/null and b/public/images/emoticon/18/26.gif differ
diff --git a/public/images/emoticon/18/27.gif b/public/images/emoticon/18/27.gif
new file mode 100644
index 000000000..34b355179
Binary files /dev/null and b/public/images/emoticon/18/27.gif differ
diff --git a/public/images/emoticon/18/28.gif b/public/images/emoticon/18/28.gif
new file mode 100644
index 000000000..6559cf642
Binary files /dev/null and b/public/images/emoticon/18/28.gif differ
diff --git a/public/images/emoticon/18/29.gif b/public/images/emoticon/18/29.gif
new file mode 100644
index 000000000..e9d87d09c
Binary files /dev/null and b/public/images/emoticon/18/29.gif differ
diff --git a/public/images/emoticon/18/3.gif b/public/images/emoticon/18/3.gif
new file mode 100644
index 000000000..dd0e4b1dc
Binary files /dev/null and b/public/images/emoticon/18/3.gif differ
diff --git a/public/images/emoticon/18/30.gif b/public/images/emoticon/18/30.gif
new file mode 100644
index 000000000..45e21f036
Binary files /dev/null and b/public/images/emoticon/18/30.gif differ
diff --git a/public/images/emoticon/18/31.gif b/public/images/emoticon/18/31.gif
new file mode 100644
index 000000000..d034173c2
Binary files /dev/null and b/public/images/emoticon/18/31.gif differ
diff --git a/public/images/emoticon/18/32.gif b/public/images/emoticon/18/32.gif
new file mode 100644
index 000000000..9bb8ebf06
Binary files /dev/null and b/public/images/emoticon/18/32.gif differ
diff --git a/public/images/emoticon/18/33.gif b/public/images/emoticon/18/33.gif
new file mode 100644
index 000000000..c64636310
Binary files /dev/null and b/public/images/emoticon/18/33.gif differ
diff --git a/public/images/emoticon/18/34.gif b/public/images/emoticon/18/34.gif
new file mode 100644
index 000000000..79e7a736b
Binary files /dev/null and b/public/images/emoticon/18/34.gif differ
diff --git a/public/images/emoticon/18/4.gif b/public/images/emoticon/18/4.gif
new file mode 100644
index 000000000..f24703f08
Binary files /dev/null and b/public/images/emoticon/18/4.gif differ
diff --git a/public/images/emoticon/18/5.gif b/public/images/emoticon/18/5.gif
new file mode 100644
index 000000000..332fc8fc1
Binary files /dev/null and b/public/images/emoticon/18/5.gif differ
diff --git a/public/images/emoticon/18/6.gif b/public/images/emoticon/18/6.gif
new file mode 100644
index 000000000..3632ad277
Binary files /dev/null and b/public/images/emoticon/18/6.gif differ
diff --git a/public/images/emoticon/18/7.gif b/public/images/emoticon/18/7.gif
new file mode 100644
index 000000000..987eef864
Binary files /dev/null and b/public/images/emoticon/18/7.gif differ
diff --git a/public/images/emoticon/18/8.gif b/public/images/emoticon/18/8.gif
new file mode 100644
index 000000000..ea99b914f
Binary files /dev/null and b/public/images/emoticon/18/8.gif differ
diff --git a/public/images/emoticon/18/9.gif b/public/images/emoticon/18/9.gif
new file mode 100644
index 000000000..3c22ed446
Binary files /dev/null and b/public/images/emoticon/18/9.gif differ
diff --git a/public/images/emoticon/18/icon.png b/public/images/emoticon/18/icon.png
new file mode 100644
index 000000000..aaa07f2e5
Binary files /dev/null and b/public/images/emoticon/18/icon.png differ
diff --git a/public/images/emoticon/19/bukeqi.gif b/public/images/emoticon/19/bukeqi.gif
new file mode 100644
index 000000000..25ff84283
Binary files /dev/null and b/public/images/emoticon/19/bukeqi.gif differ
diff --git a/public/images/emoticon/19/byebye.gif b/public/images/emoticon/19/byebye.gif
new file mode 100644
index 000000000..4ef942360
Binary files /dev/null and b/public/images/emoticon/19/byebye.gif differ
diff --git a/public/images/emoticon/19/changge.gif b/public/images/emoticon/19/changge.gif
new file mode 100644
index 000000000..6cada3bf2
Binary files /dev/null and b/public/images/emoticon/19/changge.gif differ
diff --git a/public/images/emoticon/19/chuipaopao.gif b/public/images/emoticon/19/chuipaopao.gif
new file mode 100644
index 000000000..0423f059f
Binary files /dev/null and b/public/images/emoticon/19/chuipaopao.gif differ
diff --git a/public/images/emoticon/19/dagu.gif b/public/images/emoticon/19/dagu.gif
new file mode 100644
index 000000000..b57e1d95b
Binary files /dev/null and b/public/images/emoticon/19/dagu.gif differ
diff --git a/public/images/emoticon/19/enen.gif b/public/images/emoticon/19/enen.gif
new file mode 100644
index 000000000..abf9338f1
Binary files /dev/null and b/public/images/emoticon/19/enen.gif differ
diff --git a/public/images/emoticon/19/guzhang.gif b/public/images/emoticon/19/guzhang.gif
new file mode 100644
index 000000000..f4a63c4d6
Binary files /dev/null and b/public/images/emoticon/19/guzhang.gif differ
diff --git a/public/images/emoticon/19/hahaha.gif b/public/images/emoticon/19/hahaha.gif
new file mode 100644
index 000000000..1725d7381
Binary files /dev/null and b/public/images/emoticon/19/hahaha.gif differ
diff --git a/public/images/emoticon/19/icon.png b/public/images/emoticon/19/icon.png
new file mode 100644
index 000000000..280698a5e
Binary files /dev/null and b/public/images/emoticon/19/icon.png differ
diff --git a/public/images/emoticon/19/kanxi.gif b/public/images/emoticon/19/kanxi.gif
new file mode 100644
index 000000000..710ac2a61
Binary files /dev/null and b/public/images/emoticon/19/kanxi.gif differ
diff --git a/public/images/emoticon/19/kuqi.gif b/public/images/emoticon/19/kuqi.gif
new file mode 100644
index 000000000..353edd607
Binary files /dev/null and b/public/images/emoticon/19/kuqi.gif differ
diff --git a/public/images/emoticon/19/liekai.gif b/public/images/emoticon/19/liekai.gif
new file mode 100644
index 000000000..321430125
Binary files /dev/null and b/public/images/emoticon/19/liekai.gif differ
diff --git a/public/images/emoticon/19/nanshou.gif b/public/images/emoticon/19/nanshou.gif
new file mode 100644
index 000000000..32c0f76d4
Binary files /dev/null and b/public/images/emoticon/19/nanshou.gif differ
diff --git a/public/images/emoticon/19/qiyazi.gif b/public/images/emoticon/19/qiyazi.gif
new file mode 100644
index 000000000..08ddbe5b4
Binary files /dev/null and b/public/images/emoticon/19/qiyazi.gif differ
diff --git a/public/images/emoticon/19/shaodeng.gif b/public/images/emoticon/19/shaodeng.gif
new file mode 100644
index 000000000..fcf70e844
Binary files /dev/null and b/public/images/emoticon/19/shaodeng.gif differ
diff --git a/public/images/emoticon/19/tanjita.gif b/public/images/emoticon/19/tanjita.gif
new file mode 100644
index 000000000..6ce439993
Binary files /dev/null and b/public/images/emoticon/19/tanjita.gif differ
diff --git a/public/images/emoticon/19/yeah.gif b/public/images/emoticon/19/yeah.gif
new file mode 100644
index 000000000..a0355c808
Binary files /dev/null and b/public/images/emoticon/19/yeah.gif differ
diff --git a/public/images/emoticon/19/zhuanquanquan.gif b/public/images/emoticon/19/zhuanquanquan.gif
new file mode 100644
index 000000000..b9e6eea4a
Binary files /dev/null and b/public/images/emoticon/19/zhuanquanquan.gif differ
diff --git a/public/images/other/dir.png b/public/images/other/dir.png
old mode 100755
new mode 100644
diff --git a/public/images/other/imgerr.jpg b/public/images/other/imgerr.jpg
old mode 100755
new mode 100644
diff --git a/public/images/pro/1.svg b/public/images/pro/1.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/2.svg b/public/images/pro/2.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/3.svg b/public/images/pro/3.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/4.svg b/public/images/pro/4.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/5.svg b/public/images/pro/5.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/6.svg b/public/images/pro/6.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/7.svg b/public/images/pro/7.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/8.svg b/public/images/pro/8.svg
old mode 100755
new mode 100644
diff --git a/public/images/pro/9.svg b/public/images/pro/9.svg
old mode 100755
new mode 100644
diff --git a/public/js/AgoraRTC_N-4.14.2.js b/public/js/AgoraRTC_N-4.14.2.js
deleted file mode 100644
index 3d47f2f01..000000000
--- a/public/js/AgoraRTC_N-4.14.2.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * AgoraWebSDK_N-v4.14.2-0-gbfc9ed7c-dirty Copyright AgoraInc.
- */
-
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).AgoraRTC=t()}(this,(function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},t=function(e){return e&&e.Math==Math&&e},r=t("object"==typeof globalThis&&globalThis)||t("object"==typeof window&&window)||t("object"==typeof self&&self)||t("object"==typeof e&&e)||function(){return this}()||Function("return this")(),i={},n=function(e){try{return!!e()}catch(e){return!0}},o=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),s=!n((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")})),a=s,c=Function.prototype.call,d=a?c.bind(c):function(){return c.apply(c,arguments)},u={},A={}.propertyIsEnumerable,h=Object.getOwnPropertyDescriptor,l=h&&!A.call({1:2},1);u.f=l?function(e){var t=h(this,e);return!!t&&t.enumerable}:A;var p,g,E=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}},f=s,I=Function.prototype,m=I.bind,_=I.call,C=f&&m.bind(_,_),S=f?function(e){return e&&C(e)}:function(e){return e&&function(){return _.apply(e,arguments)}},v=S,T=v({}.toString),R=v("".slice),y=function(e){return R(T(e),8,-1)},w=S,b=n,O=y,N=r.Object,D=w("".split),L=b((function(){return!N("z").propertyIsEnumerable(0)}))?function(e){return"String"==O(e)?D(e,""):N(e)}:N,P=r.TypeError,k=function(e){if(null==e)throw P("Can't call method on "+e);return e},B=L,M=k,U=function(e){return B(M(e))},x=function(e){return"function"==typeof e},Q=x,F=function(e){return"object"==typeof e?null!==e:Q(e)},V=r,j=x,G=function(e){return j(e)?e:void 0},K=function(e,t){return arguments.length<2?G(V[e]):V[e]&&V[e][t]},H=S({}.isPrototypeOf),W=K("navigator","userAgent")||"",Y=r,J=W,q=Y.process,X=Y.Deno,z=q&&q.versions||X&&X.version,Z=z&&z.v8;Z&&(g=(p=Z.split("."))[0]>0&&p[0]<4?1:+(p[0]+p[1])),!g&&J&&(!(p=J.match(/Edge\/(\d+)/))||p[1]>=74)&&(p=J.match(/Chrome\/(\d+)/))&&(g=+p[1]);var $=g,ee=$,te=n,re=!!Object.getOwnPropertySymbols&&!te((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&ee&&ee<41})),ie=re&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,ne=K,oe=x,se=H,ae=ie,ce=r.Object,de=ae?function(e){return"symbol"==typeof e}:function(e){var t=ne("Symbol");return oe(t)&&se(t.prototype,ce(e))},ue=r.String,Ae=function(e){try{return ue(e)}catch(e){return"Object"}},he=x,le=Ae,pe=r.TypeError,ge=function(e){if(he(e))return e;throw pe(le(e)+" is not a function")},Ee=ge,fe=function(e,t){var r=e[t];return null==r?void 0:Ee(r)},Ie=d,me=x,_e=F,Ce=r.TypeError,Se={exports:{}},ve=r,Te=Object.defineProperty,Re=function(e,t){try{Te(ve,e,{value:t,configurable:!0,writable:!0})}catch(r){ve[e]=t}return t},ye=Re,we="__core-js_shared__",be=r[we]||ye(we,{}),Oe=be;(Se.exports=function(e,t){return Oe[e]||(Oe[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.20.3",mode:"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.20.3/LICENSE",source:"https://github.com/zloirock/core-js"});var Ne=k,De=r.Object,Le=function(e){return De(Ne(e))},Pe=Le,ke=S({}.hasOwnProperty),Be=Object.hasOwn||function(e,t){return ke(Pe(e),t)},Me=S,Ue=0,xe=Math.random(),Qe=Me(1..toString),Fe=function(e){return"Symbol("+(void 0===e?"":e)+")_"+Qe(++Ue+xe,36)},Ve=r,je=Se.exports,Ge=Be,Ke=Fe,He=re,We=ie,Ye=je("wks"),Je=Ve.Symbol,qe=Je&&Je.for,Xe=We?Je:Je&&Je.withoutSetter||Ke,ze=function(e){if(!Ge(Ye,e)||!He&&"string"!=typeof Ye[e]){var t="Symbol."+e;He&&Ge(Je,e)?Ye[e]=Je[e]:Ye[e]=We&&qe?qe(t):Xe(t)}return Ye[e]},Ze=d,$e=F,et=de,tt=fe,rt=function(e,t){var r,i;if("string"===t&&me(r=e.toString)&&!_e(i=Ie(r,e)))return i;if(me(r=e.valueOf)&&!_e(i=Ie(r,e)))return i;if("string"!==t&&me(r=e.toString)&&!_e(i=Ie(r,e)))return i;throw Ce("Can't convert object to primitive value")},it=ze,nt=r.TypeError,ot=it("toPrimitive"),st=function(e,t){if(!$e(e)||et(e))return e;var r,i=tt(e,ot);if(i){if(void 0===t&&(t="default"),r=Ze(i,e,t),!$e(r)||et(r))return r;throw nt("Can't convert object to primitive value")}return void 0===t&&(t="number"),rt(e,t)},at=de,ct=function(e){var t=st(e,"string");return at(t)?t:t+""},dt=F,ut=r.document,At=dt(ut)&&dt(ut.createElement),ht=function(e){return At?ut.createElement(e):{}},lt=ht,pt=!o&&!n((function(){return 7!=Object.defineProperty(lt("div"),"a",{get:function(){return 7}}).a})),gt=o,Et=d,ft=u,It=E,mt=U,_t=ct,Ct=Be,St=pt,vt=Object.getOwnPropertyDescriptor;i.f=gt?vt:function(e,t){if(e=mt(e),t=_t(t),St)try{return vt(e,t)}catch(e){}if(Ct(e,t))return It(!Et(ft.f,e,t),e[t])};var Tt={},Rt=o&&n((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),yt=r,wt=F,bt=yt.String,Ot=yt.TypeError,Nt=function(e){if(wt(e))return e;throw Ot(bt(e)+" is not an object")},Dt=o,Lt=pt,Pt=Rt,kt=Nt,Bt=ct,Mt=r.TypeError,Ut=Object.defineProperty,xt=Object.getOwnPropertyDescriptor,Qt="enumerable",Ft="configurable",Vt="writable";Tt.f=Dt?Pt?function(e,t,r){if(kt(e),t=Bt(t),kt(r),"function"==typeof e&&"prototype"===t&&"value"in r&&Vt in r&&!r.writable){var i=xt(e,t);i&&i.writable&&(e[t]=r.value,r={configurable:Ft in r?r.configurable:i.configurable,enumerable:Qt in r?r.enumerable:i.enumerable,writable:!1})}return Ut(e,t,r)}:Ut:function(e,t,r){if(kt(e),t=Bt(t),kt(r),Lt)try{return Ut(e,t,r)}catch(e){}if("get"in r||"set"in r)throw Mt("Accessors not supported");return"value"in r&&(e[t]=r.value),e};var jt=Tt,Gt=E,Kt=o?function(e,t,r){return jt.f(e,t,Gt(1,r))}:function(e,t,r){return e[t]=r,e},Ht={exports:{}},Wt=x,Yt=be,Jt=S(Function.toString);Wt(Yt.inspectSource)||(Yt.inspectSource=function(e){return Jt(e)});var qt,Xt,zt,Zt=Yt.inspectSource,$t=x,er=Zt,tr=r.WeakMap,rr=$t(tr)&&/native code/.test(er(tr)),ir=Se.exports,nr=Fe,or=ir("keys"),sr=function(e){return or[e]||(or[e]=nr(e))},ar={},cr=rr,dr=r,ur=S,Ar=F,hr=Kt,lr=Be,pr=be,gr=sr,Er=ar,fr="Object already initialized",Ir=dr.TypeError,mr=dr.WeakMap;if(cr||pr.state){var _r=pr.state||(pr.state=new mr),Cr=ur(_r.get),Sr=ur(_r.has),vr=ur(_r.set);qt=function(e,t){if(Sr(_r,e))throw new Ir(fr);return t.facade=e,vr(_r,e,t),t},Xt=function(e){return Cr(_r,e)||{}},zt=function(e){return Sr(_r,e)}}else{var Tr=gr("state");Er[Tr]=!0,qt=function(e,t){if(lr(e,Tr))throw new Ir(fr);return t.facade=e,hr(e,Tr,t),t},Xt=function(e){return lr(e,Tr)?e[Tr]:{}},zt=function(e){return lr(e,Tr)}}var Rr={set:qt,get:Xt,has:zt,enforce:function(e){return zt(e)?Xt(e):qt(e,{})},getterFor:function(e){return function(t){var r;if(!Ar(t)||(r=Xt(t)).type!==e)throw Ir("Incompatible receiver, "+e+" required");return r}}},yr=o,wr=Be,br=Function.prototype,Or=yr&&Object.getOwnPropertyDescriptor,Nr=wr(br,"name"),Dr={EXISTS:Nr,PROPER:Nr&&"something"===function(){}.name,CONFIGURABLE:Nr&&(!yr||yr&&Or(br,"name").configurable)},Lr=r,Pr=x,kr=Be,Br=Kt,Mr=Re,Ur=Zt,xr=Dr.CONFIGURABLE,Qr=Rr.get,Fr=Rr.enforce,Vr=String(String).split("String");(Ht.exports=function(e,t,r,i){var n,o=!!i&&!!i.unsafe,s=!!i&&!!i.enumerable,a=!!i&&!!i.noTargetGet,c=i&&void 0!==i.name?i.name:t;Pr(r)&&("Symbol("===String(c).slice(0,7)&&(c="["+String(c).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!kr(r,"name")||xr&&r.name!==c)&&Br(r,"name",c),(n=Fr(r)).source||(n.source=Vr.join("string"==typeof c?c:""))),e!==Lr?(o?!a&&e[t]&&(s=!0):delete e[t],s?e[t]=r:Br(e,t,r)):s?e[t]=r:Mr(t,r)})(Function.prototype,"toString",(function(){return Pr(this)&&Qr(this).source||Ur(this)}));var jr={},Gr=Math.ceil,Kr=Math.floor,Hr=function(e){var t=+e;return t!=t||0===t?0:(t>0?Kr:Gr)(t)},Wr=Hr,Yr=Math.max,Jr=Math.min,qr=function(e,t){var r=Wr(e);return r<0?Yr(r+t,0):Jr(r,t)},Xr=Hr,zr=Math.min,Zr=function(e){return e>0?zr(Xr(e),9007199254740991):0},$r=Zr,ei=function(e){return $r(e.length)},ti=U,ri=qr,ii=ei,ni=function(e){return function(t,r,i){var n,o=ti(t),s=ii(o),a=ri(i,s);if(e&&r!=r){for(;s>a;)if((n=o[a++])!=n)return!0}else for(;s>a;a++)if((e||a in o)&&o[a]===r)return e||a||0;return!e&&-1}},oi={includes:ni(!0),indexOf:ni(!1)},si=Be,ai=U,ci=oi.indexOf,di=ar,ui=S([].push),Ai=function(e,t){var r,i=ai(e),n=0,o=[];for(r in i)!si(di,r)&&si(i,r)&&ui(o,r);for(;t.length>n;)si(i,r=t[n++])&&(~ci(o,r)||ui(o,r));return o},hi=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],li=Ai,pi=hi.concat("length","prototype");jr.f=Object.getOwnPropertyNames||function(e){return li(e,pi)};var gi={};gi.f=Object.getOwnPropertySymbols;var Ei=K,fi=jr,Ii=gi,mi=Nt,_i=S([].concat),Ci=Ei("Reflect","ownKeys")||function(e){var t=fi.f(mi(e)),r=Ii.f;return r?_i(t,r(e)):t},Si=Be,vi=Ci,Ti=i,Ri=Tt,yi=function(e,t,r){for(var i=vi(t),n=Ri.f,o=Ti.f,s=0;so;o++)if((a=E(e[o]))&&$n(oo,a))return a;return new no(!1)}i=eo(e,n)}for(c=i.next;!(d=Jn(c,i)).done;){try{a=E(d.value)}catch(e){ro(i,"throw",e)}if("object"==typeof a&&a&&$n(oo,a))return a}return new no(!1)},Sa=Ao,va=Uo,Ta=ys.set,Ra=Ks,ya=zs,wa=function(e,t){var r=Zs.console;r&&r.error&&(1==arguments.length?r.error(e):r.error(e,t))},ba=Hs,Oa=function(e){try{return{error:!1,value:e()}}catch(e){return{error:!0,value:e}}},Na=na,Da=Rr,La=Bi,Pa=oa,ka=Wo,Ba=$,Ma=ze("species"),Ua="Promise",xa=Da.getterFor(Ua),Qa=Da.set,Fa=Da.getterFor(Ua),Va=ua&&ua.prototype,ja=ua,Ga=Va,Ka=aa.TypeError,Ha=aa.document,Wa=aa.process,Ya=ba.f,Ja=Ya,qa=!!(Ha&&Ha.createEvent&&aa.dispatchEvent),Xa=fa(aa.PromiseRejectionEvent),za="unhandledrejection",Za=!1,$a=La(Ua,(function(){var e=_a(ja),t=e!==String(ja);if(!t&&66===Ba)return!0;if(Ba>=51&&/native code/.test(e))return!1;var r=new ja((function(e){e(1)})),i=function(e){e((function(){}),(function(){}))};return(r.constructor={})[Ma]=i,!(Za=r.then((function(){}))instanceof i)||!t&&Pa&&!Xa})),ec=$a||!Sa((function(e){ja.all(e).catch((function(){}))})),tc=function(e){var t;return!(!Ia(e)||!fa(t=e.then))&&t},rc=function(e,t){var r,i,n,o=t.value,s=1==t.state,a=s?e.ok:e.fail,c=e.resolve,d=e.reject,u=e.domain;try{a?(s||(2===t.rejection&&ac(t),t.rejection=1),!0===a?r=o:(u&&u.enter(),r=a(o),u&&(u.exit(),n=!0)),r===e.promise?d(Ka("Promise-chain cycle")):(i=tc(r))?da(i,r,c,d):c(r)):d(o)}catch(e){u&&!n&&u.exit(),d(e)}},ic=function(e,t){e.notified||(e.notified=!0,Ra((function(){for(var r,i=e.reactions;r=i.get();)rc(r,e);e.notified=!1,t&&!e.rejection&&oc(e)})))},nc=function(e,t,r){var i,n;qa?((i=Ha.createEvent("Event")).promise=t,i.reason=r,i.initEvent(e,!1,!0),aa.dispatchEvent(i)):i={promise:t,reason:r},!Xa&&(n=aa["on"+e])?n(i):e===za&&wa("Unhandled promise rejection",r)},oc=function(e){da(Ta,aa,(function(){var t,r=e.facade,i=e.value;if(sc(e)&&(t=Oa((function(){ka?Wa.emit("unhandledRejection",i,r):nc(za,r,i)})),e.rejection=ka||sc(e)?2:1,t.error))throw t.value}))},sc=function(e){return 1!==e.rejection&&!e.parent},ac=function(e){da(Ta,aa,(function(){var t=e.facade;ka?Wa.emit("rejectionHandled",t):nc("rejectionhandled",t,e.value)}))},cc=function(e,t,r){return function(i){e(t,i,r)}},dc=function(e,t,r){e.done||(e.done=!0,r&&(e=r),e.value=t,e.state=2,ic(e,!0))},uc=function(e,t,r){if(!e.done){e.done=!0,r&&(e=r);try{if(e.facade===t)throw Ka("Promise can't be resolved itself");var i=tc(t);i?Ra((function(){var r={done:!1};try{da(i,t,cc(uc,r,e),cc(dc,r,e))}catch(t){dc(r,t,e)}})):(e.value=t,e.state=1,ic(e,!1))}catch(t){dc({done:!1},t,e)}}};if($a&&(Ga=(ja=function(e){ma(this,Ga),Ea(e),da(ea,this);var t=xa(this);try{e(cc(uc,t),cc(dc,t))}catch(e){dc(t,e)}}).prototype,(ea=function(e){Qa(this,{type:Ua,done:!1,notified:!1,parent:!1,reactions:new Na,rejection:!1,state:0,value:void 0})}).prototype=ha(Ga,{then:function(e,t){var r=Fa(this),i=Ya(va(this,ja));return r.parent=!0,i.ok=!fa(e)||e,i.fail=fa(t)&&t,i.domain=ka?Wa.domain:void 0,0==r.state?r.reactions.add(i):Ra((function(){rc(i,r)})),i.promise},catch:function(e){return this.then(void 0,e)}}),ta=function(){var e=new ea,t=xa(e);this.promise=e,this.resolve=cc(uc,t),this.reject=cc(dc,t)},ba.f=Ya=function(e){return e===ja||e===ra?new ta(e):Ja(e)},fa(ua)&&Va!==Object.prototype)){ia=Va.then,Za||(Aa(Va,"then",(function(e,t){var r=this;return new ja((function(e,t){da(ia,r,e,t)})).then(e,t)}),{unsafe:!0}),Aa(Va,"catch",Ga.catch,{unsafe:!0}));try{delete Va.constructor}catch(e){}la&&la(Va,Ga)}sa({global:!0,wrap:!0,forced:$a},{Promise:ja}),pa(ja,Ua,!1),ga(Ua),ra=ca(Ua),sa({target:Ua,stat:!0,forced:$a},{reject:function(e){var t=Ya(this);return da(t.reject,void 0,e),t.promise}}),sa({target:Ua,stat:!0,forced:$a},{resolve:function(e){return ya(this,e)}}),sa({target:Ua,stat:!0,forced:ec},{all:function(e){var t=this,r=Ya(t),i=r.resolve,n=r.reject,o=Oa((function(){var r=Ea(t.resolve),o=[],s=0,a=1;Ca(e,(function(e){var c=s++,d=!1;a++,da(r,t,e).then((function(e){d||(d=!0,o[c]=e,--a||i(o))}),n)})),--a||i(o)}));return o.error&&n(o.value),r.promise},race:function(e){var t=this,r=Ya(t),i=r.reject,n=Oa((function(){var n=Ea(t.resolve);Ca(e,(function(e){da(n,t,e).then(r.resolve,i)}))}));return n.error&&i(n.value),r.promise}});var Ac=Gi,hc=K,lc=jo,pc=S,gc=n,Ec=r.Array,fc=hc("JSON","stringify"),Ic=pc(/./.exec),mc=pc("".charAt),_c=pc("".charCodeAt),Cc=pc("".replace),Sc=pc(1..toString),vc=/[\uD800-\uDFFF]/g,Tc=/^[\uD800-\uDBFF]$/,Rc=/^[\uDC00-\uDFFF]$/,yc=function(e,t,r){var i=mc(r,t-1),n=mc(r,t+1);return Ic(Tc,e)&&!Ic(Rc,n)||Ic(Rc,e)&&!Ic(Tc,i)?"\\u"+Sc(_c(e,0),16):e},wc=gc((function(){return'"\\udf06\\ud834"'!==fc("\udf06\ud834")||'"\\udead"'!==fc("\udead")}));fc&&Ac({target:"JSON",stat:!0,forced:wc},{stringify:function(e,t,r){for(var i=0,n=arguments.length,o=Ec(n);i0&&ed[0]<4?1:+(ed[0]+ed[1])),!td&&bd&&(!(ed=bd.match(/Edge\/(\d+)/))||ed[1]>=74)&&(ed=bd.match(/Chrome\/(\d+)/))&&(td=+ed[1]);var Pd=td,kd=Pd,Bd=bc,Md=!!Object.getOwnPropertySymbols&&!Bd((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&kd&&kd<41})),Ud=Md&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,xd=Rd,Qd=Kc,Fd=Mc,Vd=Ud,jd=xc.Object,Gd=Vd?function(e){return"symbol"==typeof e}:function(e){var t=xd("Symbol");return Qd(t)&&Fd(t.prototype,jd(e))},Kd=xc.String,Hd=function(e){try{return Kd(e)}catch(e){return"Object"}},Wd=Kc,Yd=Hd,Jd=xc.TypeError,qd=function(e){if(Wd(e))return e;throw Jd(Yd(e)+" is not a function")},Xd=qd,zd=function(e,t){var r=e[t];return null==r?void 0:Xd(r)},Zd=qc,$d=Kc,eu=md,tu=xc.TypeError,ru={exports:{}},iu=xc,nu=Object.defineProperty,ou=function(e,t){try{nu(iu,e,{value:t,configurable:!0,writable:!0})}catch(r){iu[e]=t}return t},su="__core-js_shared__",au=xc[su]||ou(su,{}),cu=au;(ru.exports=function(e,t){return cu[e]||(cu[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.20.3",mode:"pure",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.20.3/LICENSE",source:"https://github.com/zloirock/core-js"});var du=pd,uu=xc.Object,Au=function(e){return uu(du(e))},hu=Au,lu=Bc({}.hasOwnProperty),pu=Object.hasOwn||function(e,t){return lu(hu(e),t)},gu=Bc,Eu=0,fu=Math.random(),Iu=gu(1..toString),mu=function(e){return"Symbol("+(void 0===e?"":e)+")_"+Iu(++Eu+fu,36)},_u=xc,Cu=ru.exports,Su=pu,vu=mu,Tu=Md,Ru=Ud,yu=Cu("wks"),wu=_u.Symbol,bu=wu&&wu.for,Ou=Ru?wu:wu&&wu.withoutSetter||vu,Nu=function(e){if(!Su(yu,e)||!Tu&&"string"!=typeof yu[e]){var t="Symbol."+e;Tu&&Su(wu,e)?yu[e]=wu[e]:yu[e]=Ru&&bu?bu(t):Ou(t)}return yu[e]},Du=qc,Lu=md,Pu=Gd,ku=zd,Bu=function(e,t){var r,i;if("string"===t&&$d(r=e.toString)&&!eu(i=Zd(r,e)))return i;if($d(r=e.valueOf)&&!eu(i=Zd(r,e)))return i;if("string"!==t&&$d(r=e.toString)&&!eu(i=Zd(r,e)))return i;throw tu("Can't convert object to primitive value")},Mu=Nu,Uu=xc.TypeError,xu=Mu("toPrimitive"),Qu=function(e,t){if(!Lu(e)||Pu(e))return e;var r,i=ku(e,xu);if(i){if(void 0===t&&(t="default"),r=Du(i,e,t),!Lu(r)||Pu(r))return r;throw Uu("Can't convert object to primitive value")}return void 0===t&&(t="number"),Bu(e,t)},Fu=Gd,Vu=function(e){var t=Qu(e,"string");return Fu(t)?t:t+""},ju=md,Gu=xc.document,Ku=ju(Gu)&&ju(Gu.createElement),Hu=function(e){return Ku?Gu.createElement(e):{}},Wu=Hu,Yu=!Wc&&!bc((function(){return 7!=Object.defineProperty(Wu("div"),"a",{get:function(){return 7}}).a})),Ju=Wc,qu=qc,Xu=Xc,zu=rd,Zu=fd,$u=Vu,eA=pu,tA=Yu,rA=Object.getOwnPropertyDescriptor;Hc.f=Ju?rA:function(e,t){if(e=Zu(e),t=$u(t),tA)try{return rA(e,t)}catch(e){}if(eA(e,t))return zu(!qu(Xu.f,e,t),e[t])};var iA=bc,nA=Kc,oA=/#|\.prototype\./,sA=function(e,t){var r=cA[aA(e)];return r==uA||r!=dA&&(nA(t)?iA(t):!!t)},aA=sA.normalize=function(e){return String(e).replace(oA,".").toLowerCase()},cA=sA.data={},dA=sA.NATIVE="N",uA=sA.POLYFILL="P",AA=sA,hA=qd,lA=Oc,pA=Bc(Bc.bind),gA=function(e,t){return hA(e),void 0===t?e:lA?pA(e,t):function(){return e.apply(t,arguments)}},EA={},fA=Wc&&bc((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),IA=xc,mA=md,_A=IA.String,CA=IA.TypeError,SA=function(e){if(mA(e))return e;throw CA(_A(e)+" is not an object")},vA=Wc,TA=Yu,RA=fA,yA=SA,wA=Vu,bA=xc.TypeError,OA=Object.defineProperty,NA=Object.getOwnPropertyDescriptor,DA="enumerable",LA="configurable",PA="writable";EA.f=vA?RA?function(e,t,r){if(yA(e),t=wA(t),yA(r),"function"==typeof e&&"prototype"===t&&"value"in r&&PA in r&&!r.writable){var i=NA(e,t);i&&i.writable&&(e[t]=r.value,r={configurable:LA in r?r.configurable:i.configurable,enumerable:DA in r?r.enumerable:i.enumerable,writable:!1})}return OA(e,t,r)}:OA:function(e,t,r){if(yA(e),t=wA(t),yA(r),TA)try{return OA(e,t,r)}catch(e){}if("get"in r||"set"in r)throw bA("Accessors not supported");return"value"in r&&(e[t]=r.value),e};var kA=EA,BA=rd,MA=Wc?function(e,t,r){return kA.f(e,t,BA(1,r))}:function(e,t,r){return e[t]=r,e},UA=xc,xA=Gc,QA=Bc,FA=Kc,VA=Hc.f,jA=AA,GA=_d,KA=gA,HA=MA,WA=pu,YA=function(e){var t=function(r,i,n){if(this instanceof t){switch(arguments.length){case 0:return new e;case 1:return new e(r);case 2:return new e(r,i)}return new e(r,i,n)}return xA(e,this,arguments)};return t.prototype=e.prototype,t},JA=function(e,t){var r,i,n,o,s,a,c,d,u=e.target,A=e.global,h=e.stat,l=e.proto,p=A?UA:h?UA[u]:(UA[u]||{}).prototype,g=A?GA:GA[u]||HA(GA,u,{})[u],E=g.prototype;for(n in t)r=!jA(A?n:u+(h?".":"#")+n,e.forced)&&p&&WA(p,n),s=g[n],r&&(a=e.noTargetGet?(d=VA(p,n))&&d.value:p[n]),o=r&&a?a:t[n],r&&typeof s==typeof o||(c=e.bind&&r?KA(o,UA):e.wrap&&r?YA(o):l&&FA(o)?QA(o):o,(e.sham||o&&o.sham||s&&s.sham)&&HA(c,"sham",!0),HA(g,n,c),l&&(WA(GA,i=u+"Prototype")||HA(GA,i,{}),HA(GA[i],n,o),e.real&&E&&!E[n]&&HA(E,n,o)))},qA=Math.ceil,XA=Math.floor,zA=function(e){var t=+e;return t!=t||0===t?0:(t>0?XA:qA)(t)},ZA=zA,$A=Math.min,eh=function(e){return e>0?$A(ZA(e),9007199254740991):0},th=function(e){return eh(e.length)},rh=qd,ih=Au,nh=hd,oh=th,sh=xc.TypeError,ah=function(e){return function(t,r,i,n){rh(r);var o=ih(t),s=nh(o),a=oh(o),c=e?a-1:0,d=e?-1:1;if(i<2)for(;;){if(c in s){n=s[c],c+=d;break}if(c+=d,e?c<0:a<=c)throw sh("Reduce of empty array with no initial value")}for(;e?c>=0:a>c;c+=d)c in s&&(n=r(n,s[c],c,o));return n}},ch={left:ah(!1),right:ah(!0)},dh=bc,uh=function(e,t){var r=[][e];return!!r&&dh((function(){r.call(null,t||function(){throw 1},1)}))},Ah="process"==sd(xc.process),hh=ch.left,lh=Pd,ph=Ah;JA({target:"Array",proto:!0,forced:!uh("reduce")||!ph&&lh>79&&lh<83},{reduce:function(e){var t=arguments.length;return hh(this,e,t,t>1?arguments[1]:void 0)}});var gh=_d,Eh=function(e){return gh[e+"Prototype"]},fh=Eh("Array").reduce,Ih=Mc,mh=fh,_h=Array.prototype,Ch=function(e){var t=e.reduce;return e===_h||Ih(_h,e)&&t===_h.reduce?mh:t},Sh=Ch,vh=Dn,Th=r.String,Rh=function(e){if("Symbol"===vh(e))throw TypeError("Cannot convert a Symbol value to a string");return Th(e)},yh=Nt,wh=function(){var e=yh(this),t="";return e.global&&(t+="g"),e.ignoreCase&&(t+="i"),e.multiline&&(t+="m"),e.dotAll&&(t+="s"),e.unicode&&(t+="u"),e.sticky&&(t+="y"),t},bh=n,Oh=r.RegExp,Nh=bh((function(){var e=Oh("a","y");return e.lastIndex=2,null!=e.exec("abcd")})),Dh=Nh||bh((function(){return!Oh("a","y").sticky})),Lh={BROKEN_CARET:Nh||bh((function(){var e=Oh("^r","gy");return e.lastIndex=2,null!=e.exec("str")})),MISSED_STICKY:Dh,UNSUPPORTED_Y:Nh},Ph={},kh=Ai,Bh=hi,Mh=Object.keys||function(e){return kh(e,Bh)},Uh=o,xh=Rt,Qh=Tt,Fh=Nt,Vh=U,jh=Mh;Ph.f=Uh&&!xh?Object.defineProperties:function(e,t){Fh(e);for(var r,i=Vh(t),n=jh(t),o=n.length,s=0;o>s;)Qh.f(e,r=n[s++],i[r]);return e};var Gh,Kh=Nt,Hh=Ph,Wh=hi,Yh=ar,Jh=Go,qh=ht,Xh=sr("IE_PROTO"),zh=function(){},Zh=function(e){return"