Merge branch 'pro' into okr
# Conflicts: # app/Http/Controllers/Api/DialogController.php # app/Models/User.php # docker-compose.yml # docker/nginx/default.conf # language/original-web.txt # language/translate.json # public/docs/assets/main.bundle.js # public/docs/index.html # resources/assets/js/pages/manage.vue # resources/assets/js/pages/manage/components/ChatInput/index.vue # resources/assets/sass/pages/_.scss
@ -1,3 +1,5 @@
|
||||
TIMEZONE=PRC
|
||||
|
||||
APP_NAME=DooTask
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
TIMEZONE=PRC
|
||||
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
|
||||
166
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
|
||||
|
||||
- 优化会员选择器
|
||||
- 优化图片压缩
|
||||
- 回复图片显示图片搜略图
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
## 安装程序
|
||||
|
||||
- 必须安装:`Docker` 和 `Docker Compose v2.0+`
|
||||
- 必须安装:`Docker v20.10+` 和 `Docker Compose v2.0+`
|
||||
- 支持环境:`Centos/Debian/Ubuntu/macOS`
|
||||
- 硬件建议:2核4G以上
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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("/<img[^>]*?>/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("/<img[^>]*?>/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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = "<p>{$pre}打卡成功,打卡时间: {$hi}{$remark}</p>";
|
||||
$suff = $getJokeSoup($type);
|
||||
if ($suff) {
|
||||
$text = "{$text}<p>----------</p><p>{$suff}</p>";
|
||||
}
|
||||
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';
|
||||
}
|
||||
|
||||
@ -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')
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -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'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}]'));
|
||||
}
|
||||
});
|
||||
//
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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')) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 = "<p>今日已{$typeDesc}打卡,无需重复打卡。</p>";
|
||||
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 = "<p>{$typeDesc}打卡成功,打卡时间: {$hi}{$remark}</p>";
|
||||
$suff = $getJokeSoup($type);
|
||||
if ($suff) {
|
||||
$text = "{$text}<p>----------</p><p>{$suff}</p>";
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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([
|
||||
|
||||
@ -641,7 +641,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
}
|
||||
// 其他网络图片
|
||||
$imageSaveLocal = Base::settingFind("system", "image_save_local");
|
||||
preg_match_all("/<img[^>]*?src=([\"'])(.*?\.(png|jpg|jpeg|webp|gif))\\1[^>]*?>/is", $text, $matchs);
|
||||
preg_match_all("/<img[^>]*?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("/<span class=\"mention user\" data-id=\"(\d+)\">/", $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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -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, '<');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 class=\"mention user\" data-id=\"(\d+)\">(.*?)<\/span>/", "", $original);
|
||||
}
|
||||
if (preg_match("/<span[^>]*?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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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("/<span class=\"mention user\" data-id=\"(\d+)\">/", $msg->msg['text'], $matchs);
|
||||
if ($matchs) {
|
||||
$mentions = array_values(array_filter(array_unique($matchs[1])));
|
||||
}
|
||||
}
|
||||
|
||||
// 将会话以外的成员加入会话内
|
||||
$diffids = array_values(array_diff($mentions, $userids));
|
||||
if ($diffids) {
|
||||
// 仅(群聊)且(是群主或没有群主)才可以@成员以外的人
|
||||
if ($dialog->type === 'group' && in_array($dialog->owner_id, [0, $msg->userid])) {
|
||||
$dialog->joinGroup($diffids, $msg->userid);
|
||||
$dialog->pushMsg("groupJoin", null, $diffids);
|
||||
$userids = array_values(array_unique(array_merge($mentions, $userids)));
|
||||
}
|
||||
}
|
||||
$msgJoinGroupResult = $msg->msgJoinGroup($dialog);
|
||||
$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) {
|
||||
|
||||
@ -67,7 +67,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'PRC',
|
||||
'timezone' => env('TIMEZONE', 'PRC'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateDefaultRobot extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (WebSocketDialog::count() > 0) {
|
||||
User::botGetOrCreate('ai-openai');
|
||||
User::botGetOrCreate('ai-claude');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use App\Module\Base;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class SettingCheckinModesValue extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$setting = Base::setting('checkinSetting');
|
||||
$setting['modes'] = ['auto'];
|
||||
Base::setting('checkinSetting', $setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectFlowItemsAddColumnid extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_flow_items', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_flow_items', 'columnid')) {
|
||||
$table->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");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddWebSocketDialogUsersColor extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('web_socket_dialog_users', 'color')) {
|
||||
$table->string('color', 20)->nullable()->default('')->after('important')->comment('颜色');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
$table->dropColumn("color");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class repairProjectTasksIsAllVisible extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// 修复子任务可见性字段为null的数据
|
||||
if (Schema::hasTable('project_tasks')) {
|
||||
$prefix = DB::getConfig('prefix');
|
||||
DB::update("
|
||||
UPDATE {$prefix}project_tasks
|
||||
SET is_all_visible = 1
|
||||
WHERE is_all_visible is null AND parent_id = 0;
|
||||
");
|
||||
DB::update("
|
||||
UPDATE {$prefix}project_tasks t1
|
||||
JOIN {$prefix}project_tasks t2 ON t1.parent_id = t2.id
|
||||
SET t1.is_all_visible = t2.is_all_visible
|
||||
WHERE t1.is_all_visible is null AND t1.parent_id > 0;
|
||||
");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -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');
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
168
electron/electron-menu.js
vendored
Normal file
@ -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;
|
||||
93
electron/electron.js
vendored
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
5
electron/utils.js
vendored
@ -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()
|
||||
|
||||
@ -391,7 +391,7 @@ error
|
||||
|
||||
个人项目
|
||||
注册时系统自动创建项目,你可以自由删除。
|
||||
无法发起会话
|
||||
无法发起会话,请联系管理员。
|
||||
无法创建群组
|
||||
无法查看联系电话
|
||||
|
||||
@ -410,6 +410,7 @@ error
|
||||
匿名消息
|
||||
系统管理员
|
||||
我要签到
|
||||
我要打卡
|
||||
|
||||
关键词不能为空
|
||||
|
||||
@ -470,3 +471,4 @@ Api接口文档
|
||||
找不到符合条件的子节点
|
||||
|
||||
文件
|
||||
未完成
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
仅限项目负责人操作
|
||||
任务描述,回车创建
|
||||
你好,扫码确认登录
|
||||
你确定要登出系统?
|
||||
你确定要登出系统吗?
|
||||
你确认领取任务吗?
|
||||
列表名称,回车创建
|
||||
同步修改子任务时间
|
||||
@ -1251,4 +1251,121 @@ Markdown 格式发送
|
||||
会议组件加载失败!
|
||||
|
||||
OKR管理
|
||||
OKR结果分析
|
||||
OKR结果分析
|
||||
计划时间冲突提示
|
||||
忽略并继续
|
||||
你确定要清除缓存吗?
|
||||
|
||||
可见性选项
|
||||
保持
|
||||
保持:任务详情页可见性选项保持显示。
|
||||
自动:默认值情况下显示在合并项目,设置时保持显示。
|
||||
修改子任务时间
|
||||
请输入修改备注
|
||||
|
||||
任务默认时间
|
||||
请选择默认时间
|
||||
添加任务计划时间默认时分。
|
||||
|
||||
导出审批数据
|
||||
审批类型
|
||||
导出类型
|
||||
未完成
|
||||
AI机器人
|
||||
任务相关
|
||||
请填写名称!
|
||||
访问OpenAI网站查看:
|
||||
使用代理
|
||||
支持 http 或 socks 代理
|
||||
例如:http://proxy.com 或 socks5://proxy.com
|
||||
查看 Cookie 中的 sessionKey 便是
|
||||
|
||||
暂不支持拖拽文件夹。
|
||||
暂不支持粘贴文件夹。
|
||||
暂不支持拖拽文件夹,请手动上传文件夹。
|
||||
暂不支持粘贴文件夹,请手动上传文件夹。
|
||||
|
||||
最近
|
||||
暂无结果
|
||||
新项目负责人
|
||||
恢复帐号(已离职)
|
||||
你确定恢复已离职帐号【(*)】吗?(注:此操作仅恢复帐号状态,无法恢复操作离职时移交的数据)
|
||||
流转到【(*)】时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。
|
||||
删除文件夹
|
||||
你确定要删除【(*)】文件夹吗?
|
||||
请输入新的密码
|
||||
请输入新的邮箱((*))
|
||||
|
||||
截图快捷键
|
||||
新项目负责人
|
||||
暂无结果
|
||||
最近
|
||||
加班
|
||||
离职申请
|
||||
录用申请
|
||||
|
||||
修改的内容尚未保存,确定要放弃修改吗?
|
||||
获取方式
|
||||
查看说明
|
||||
正在修改系统机器人:(*)
|
||||
复制选择
|
||||
复制全部
|
||||
返回首页
|
||||
刷新链接
|
||||
确认
|
||||
还原归档
|
||||
签到方式
|
||||
手动签到
|
||||
定位签到
|
||||
详情看下文安装说明
|
||||
通过在签到打卡机器人发送指令签到
|
||||
通过在签到打卡机器人发送位置签到
|
||||
状态设置
|
||||
关联列表
|
||||
选择关联列表
|
||||
取消关联
|
||||
流转到【(*)】时自动将任务移动至关联列表。
|
||||
状态[(*)]设置错误,设置剔除模式时必须填写状态负责人
|
||||
|
||||
键盘设置
|
||||
|
||||
在浏览器中打开
|
||||
图片存储为...
|
||||
复制图片
|
||||
复制电子邮件地址
|
||||
复制链接地址
|
||||
复制图片地址
|
||||
图片保存失败
|
||||
图片无法保存
|
||||
|
||||
事项
|
||||
|
||||
编辑描述
|
||||
没有任何修改!
|
||||
项目类型
|
||||
团队项目
|
||||
个人项目
|
||||
全部项目
|
||||
打开链接
|
||||
|
||||
|
||||
汇报人
|
||||
提交时间
|
||||
|
||||
应用
|
||||
机器人设置
|
||||
去聊天
|
||||
返回
|
||||
会议设置
|
||||
我是一个人工智能助手,为用户提供问题解答和指导。我没有具体的身份,只是一个程序。您有什么问题可以问我哦?
|
||||
我是Claude,一个由Anthropic公司创造出来的AI助手机器人。我的工作是帮助人类,与人对话并给出解答。
|
||||
我是文心一言,英文名是ERNIE Bot。我能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。
|
||||
我是达摩院自主研发的超大规模语言模型,能够回答问题、创作文字,还能表达观点、撰写代码。
|
||||
机器人暂未开启
|
||||
创建一个全新的会议视频会议,与会者可以在实时中进行面对面的视听交流。通过视频会议平台,参与者可以分享屏幕、共享文档,并与其他与会人员进行讨论和协。
|
||||
加入视频会议,参与已经创建的会议,在会议过程中与其他参会人员进行远程实时视听交流和协作。
|
||||
新会议
|
||||
新建会议
|
||||
LDAP设置
|
||||
邮件管理
|
||||
APP推送
|
||||
|
||||
@ -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": "<s:1>または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"
|
||||
}
|
||||
]
|
||||
@ -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"
|
||||
},
|
||||
|
||||
0
public/audio/call.mp3
Executable file → Normal file
0
public/audio/message.mp3
Executable file → Normal file
0
public/css/fonts/ionicons.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 542 KiB After Width: | Height: | Size: 542 KiB |
0
public/css/fonts/ionicons.ttf
Executable file → Normal file
0
public/css/fonts/ionicons.woff
Executable file → Normal file
126
public/docs/assets/main.css
vendored
@ -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
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
13
public/docs/assets/prism-diff-highlight.css
vendored
Normal file
@ -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;
|
||||
}
|
||||
65
public/docs/assets/prism-toolbar.css
vendored
Normal file
@ -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;
|
||||
}
|
||||
22
public/images/apply/addProject.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#87D068;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M35.7,20.1c0-0.8-0.4-1.5-1.1-1.9l-8.9-5.1c-1-0.6-2.2-0.6-3.1,0l-9.1,5.2c-0.6,0.4-1,1-1,1.8s0.4,1.4,1,1.8
|
||||
l9.1,5.2c0.5,0.3,1,0.4,1.6,0.4c0.5,0,1.1-0.1,1.6-0.4l8.9-5.1C35.3,21.6,35.7,20.9,35.7,20.1z"/>
|
||||
<path class="st1" d="M24,29.7c-0.4,0-0.8-0.1-1.2-0.3l-9-5.1c-0.4-0.2-0.9-0.1-1.2,0.3c-0.2,0.4-0.1,0.9,0.3,1.2l9,5.1
|
||||
c0.6,0.4,1.3,0.6,2,0.6c0,0,0,0,0,0c0.5,0,0.9-0.4,0.9-0.9C24.9,30.1,24.5,29.7,24,29.7z"/>
|
||||
<path class="st1" d="M24,33.5c-0.6,0-1.3-0.2-1.8-0.5l-8.4-4.8c-0.4-0.2-0.9-0.1-1.2,0.3c-0.2,0.4-0.1,0.9,0.3,1.2l8.4,4.8
|
||||
c0.8,0.5,1.7,0.7,2.6,0.7c0,0,0,0,0,0c0.5,0,0.9-0.4,0.9-0.9C24.9,33.9,24.5,33.5,24,33.5z"/>
|
||||
<path class="st1" d="M34.8,30h-2.6v-2.6c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9V30h-2.6c-0.5,0-0.9,0.4-0.9,0.9
|
||||
s0.4,0.9,0.9,0.9h2.6v2.6c0,0.5,0.4,0.9,0.9,0.9c0.5,0,0.9-0.4,0.9-0.9v-2.6h2.6c0.5,0,0.9-0.4,0.9-0.9S35.3,30,34.8,30z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
29
public/images/apply/addTask.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#87D068;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<circle class="st1" cx="19.7" cy="26.6" r="0.9"/>
|
||||
<g>
|
||||
<path class="st1" d="M29,26.9c0.5,0,0.9,0.4,0.9,0.9l0,2.6l2.6,0c0.5,0,0.9,0.4,0.9,0.9c0,0.5-0.4,0.9-0.9,0.9l-2.6,0l0,2.6
|
||||
c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9l0-2.6l-2.6,0c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9l2.6,0l0-2.6
|
||||
C28.1,27.3,28.5,26.9,29,26.9"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M26.6,34.8l0-1.2l-1.2,0c0,0,0,0,0,0c-0.6,0-1.2-0.2-1.6-0.7c-0.4-0.4-0.7-1-0.7-1.6c0-0.6,0.2-1.2,0.7-1.6
|
||||
c0.4-0.4,1-0.7,1.6-0.7l1.2,0l0-1.2c0-1.3,1-2.3,2.3-2.3c0,0,0,0,0,0c1.3,0,2.3,1,2.3,2.3l0,1.2l1.2,0c0,0,0,0,0,0
|
||||
c0.3,0,0.6,0.1,0.9,0.2V14.7c0-1.3-1.1-2.3-2.3-2.3H17c-1.3,0-2.3,1.1-2.3,2.3v18.7c0,1.3,1,2.3,2.3,2.3h9.8
|
||||
C26.7,35.4,26.6,35.1,26.6,34.8z M19.7,29.3c-1.4,0-2.6-1.2-2.6-2.6s1.2-2.6,2.6-2.6s2.6,1.2,2.6,2.6S21.1,29.3,19.7,29.3z
|
||||
M22.6,17.9l-2.9,2.9c-0.2,0.2-0.4,0.3-0.6,0.3s-0.4-0.1-0.6-0.3l-1.2-1.2c-0.3-0.3-0.3-0.9,0-1.2s0.9-0.3,1.2,0l0.5,0.5l2.3-2.3
|
||||
c0.3-0.3,0.9-0.3,1.2,0S23,17.6,22.6,17.9z M24.9,17.6h4.7c0.5,0,0.9,0.4,0.9,0.9s-0.4,0.9-0.9,0.9h-4.7c-0.5,0-0.9-0.4-0.9-0.9
|
||||
S24.4,17.6,24.9,17.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
27
public/images/apply/allUser.svg
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F57775;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M21.5,24.5c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6C27.6,21.8,24.9,24.5,21.5,24.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M28.8,35.5H14.3c-1.1,0-2-0.9-2-2v-4.1c0-1.7,1.4-3.1,3.1-3.1h12.2c1.7,0,3.1,1.4,3.1,3.1v4.1
|
||||
C30.7,34.6,29.9,35.5,28.8,35.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M29.6,22.5c-0.3,0-0.5-0.1-0.7-0.3c-0.2-0.3-0.2-0.7,0-1c0.6-0.8,0.9-1.7,0.9-2.7s-0.3-1.9-0.9-2.7
|
||||
c-0.2-0.3-0.2-0.7,0-1s0.6-0.4,0.9-0.3c1.7,0.6,2.9,2.2,2.9,4s-1.2,3.4-2.9,4C29.8,22.5,29.7,22.5,29.6,22.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M34,34.7h-0.7c-0.3,0-0.5-0.1-0.7-0.3s-0.2-0.5-0.1-0.7c0-0.1,0-0.1,0-0.1v-4c0-0.4-0.2-0.7-0.3-0.9
|
||||
c-0.2-0.2-0.2-0.6-0.1-0.9c0.1-0.3,0.4-0.5,0.7-0.5H33c1.4,0,2.6,1.2,2.6,2.6V33C35.7,34,34.9,34.7,34,34.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/images/apply/apppush.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25679"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#9D95E5" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25679)"><g><path d="M35.6226,12.187076C35.9089,12.366451,36.0526,12.70563,35.9824,13.03611L32.5574,33.5856C32.4912,34.000299999999996,32.1282,34.3018,31.7084,34.290800000000004C31.599,34.289100000000005,31.4912,34.2646,31.3918,34.2188L25.333399999999997,31.7437L22.095599999999997,35.6866C21.9377,35.8896,21.69045,36.0025,21.43364,35.9888C21.33001,35.990700000000004,21.22712,35.9711,21.131439999999998,35.9313C20.96605,35.8728,20.82448,35.7615,20.72851,35.6147C20.62727,35.4715,20.57206,35.300799999999995,20.57021,35.1254L20.57021,30.4629L32.125699999999995,16.28835L17.821640000000002,28.6641L12.540347,26.4912C12.225567,26.3831,12.0117595,26.09,12.0050232,25.7573C11.9677587,25.428800000000003,12.140421,25.112299999999998,12.436736,24.9658L34.7016,12.115123C34.833200000000005,12.040216,34.981899999999996,12.000561138,35.1333,12C35.3072,12.0294109,35.4735,12.092995,35.6226,12.187076Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/images/apply/approve.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25613"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#F57775" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25613)"><g><g><path d="M32.753299999999996,26.9593L30.357,26.9593C29.1345,26.957700000000003,28.1438,25.967100000000002,28.142200000000003,24.744500000000002C28.139499999999998,24.1851,28.3448,23.6446,28.7182,23.228099999999998C31.2206,20.597839999999998,31.1498,16.44632,28.5592,13.9029C25.9685,11.359486,21.816380000000002,11.365081,19.23258,13.91547C16.648789999999998,16.46586,16.58916,20.61755,19.09864,23.2411C19.45887,23.6577,19.65585,24.1909,19.65307,24.7416C19.65997,25.8988,18.7744,26.8659,17.62114,26.9608L15.40058,26.9608C14.07758,26.9608,13.00396661,28.0311,13,29.3541L13,31.5142C13,32.024,13.413276,32.4373,13.923077,32.4373L34.227900000000005,32.4373C34.737700000000004,32.4373,35.150999999999996,32.024,35.150999999999996,31.5142L35.150999999999996,29.3541C35.145399999999995,28.0327,34.0747,26.9633,32.753299999999996,26.9593Z" fill="#FFFFFF" fill-opacity="1"/></g><g><path d="M34.2308,35.999994833374025L13.923077,35.999994833374025C13.413276,35.999994833374025,13.000000054161,35.58672483337402,13.000000054161,35.07692183337402C13.000000054161,34.56712083337403,13.413276,34.15384483337402,13.923077,34.15384483337402L34.227900000000005,34.15384483337402C34.737700000000004,34.15384483337402,35.150999999999996,34.56712083337403,35.150999999999996,35.07692183337402C35.150999999999996,35.58672483337402,34.737700000000004,35.999994833374025,34.227900000000005,35.999994833374025L34.2308,35.999994833374025Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
19
public/images/apply/calendar.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFAA64;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<path class="st1" d="M32.5,14.5h-4.3v-1c0-0.4-0.3-0.8-0.8-0.8s-0.8,0.3-0.8,0.8v1h-5.3v-1c0-0.4-0.3-0.8-0.8-0.8s-0.8,0.3-0.8,0.8
|
||||
v1h-4.3c-1.5,0-2.8,1.2-2.8,2.8v15.3c0,1.5,1.2,2.8,2.8,2.8h17c1.5,0,2.8-1.2,2.8-2.8V17.2C35.2,15.7,34,14.5,32.5,14.5z M15.5,16
|
||||
h4.3v1c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8v-1h5.3v1c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8v-1h4.3c0.7,0,1.2,0.6,1.2,1.2v2.9
|
||||
H14.2v-2.9C14.2,16.5,14.8,16,15.5,16z M18.3,28.1L18.3,28.1c0.6,0,1.1,0.5,1.1,1.1c0,0.6-0.5,1-1,1c-0.6,0-1.1-0.5-1.1-1
|
||||
C17.3,28.5,17.8,28.1,18.3,28.1z M17.3,25.1c0-0.6,0.5-1,1-1h0c0.6,0,1,0.5,1,1s-0.5,1-1,1C17.8,26.2,17.3,25.7,17.3,25.1z M24,28.1
|
||||
L24,28.1c0.6,0,1.1,0.5,1.1,1.1c0,0.6-0.5,1-1,1c-0.6,0-1.1-0.5-1.1-1C23,28.5,23.4,28.1,24,28.1z M23,25.1c0-0.6,0.5-1,1-1h0
|
||||
c0.6,0,1,0.5,1,1s-0.5,1-1,1C23.4,26.2,23,25.7,23,25.1z M29.7,28.1L29.7,28.1c0.6,0,1.1,0.5,1.1,1.1c0,0.6-0.5,1-1,1
|
||||
c-0.6,0-1.1-0.5-1.1-1C28.6,28.5,29.1,28.1,29.7,28.1z M28.6,25.1c0-0.6,0.5-1,1-1h0c0.6,0,1,0.5,1,1s-0.5,1-1,1
|
||||
C29.1,26.2,28.6,25.7,28.6,25.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
public/images/apply/createMeeting.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25658"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#72A1F7" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25658)"><g><path d="M33.1767,19.46395L30.5723,21.34133L30.5723,19.01332C30.5723,17.34516,29.217,15.99442094,27.5489,16L15.01332,16C13.34911,15.999999801611,12,17.34911,12,19.01332L12,29.5822C12,31.2464,13.34911,32.595600000000005,15.01332,32.595600000000005L27.5489,32.595600000000005C29.217,32.6011,30.5723,31.2504,30.5723,29.5822L30.5723,27.2542L33.182500000000005,29.1259C33.7237,29.5146,34.436800000000005,29.5683,35.0302,29.2651C35.623599999999996,28.9619,35.9979,28.3525,36,27.6861L36,20.907980000000002C36.0011,20.23879,35.626599999999996,19.62559,35.0308,19.32086C34.4351,19.01614,33.7187,19.07142,33.1767,19.46395Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
17
public/images/apply/file.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFAA64;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M28.7,18.4h3.8c0.5,0,0.8-0.6,0.4-1l-4.7-4.7c-0.4-0.4-1-0.1-1,0.4V17C27.3,17.8,27.9,18.4,28.7,18.4z"/>
|
||||
<path class="st1" d="M28.7,20.2c-1.8,0-3.2-1.4-3.2-3.2v-4c0-0.3-0.3-0.6-0.6-0.6H17c-1.3,0-2.3,1.1-2.3,2.3v18.7
|
||||
c0,1.3,1,2.3,2.3,2.3h14c1.3,0,2.3-1,2.3-2.3V20.7c0-0.3-0.3-0.6-0.6-0.6H28.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 890 B |
20
public/images/apply/joinMeeting.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#53CBAE;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M35.8,18.3l-2.6,2.2c-0.3,0.3-0.5,0.6-0.5,1v4.8c0,0.4,0.2,0.8,0.5,1l2.6,2.2c0.9,0.8,2.2,0.1,2.2-1v-9.3
|
||||
C38,18.2,36.6,17.6,35.8,18.3z"/>
|
||||
<path class="st1" d="M28.8,14.6H12c-1.1,0-2,0.9-2,2v14.8c0,1.1,0.9,2,2,2h16.8c1.1,0,2-0.9,2-2V16.6
|
||||
C30.8,15.5,29.9,14.6,28.8,14.6z M23.4,24.7h-2.2V27c0,0.4-0.3,0.7-0.7,0.7c-0.4,0-0.7-0.3-0.7-0.7v-2.2h-2.2
|
||||
c-0.4,0-0.7-0.3-0.7-0.7s0.3-0.7,0.7-0.7h2.2V21c0-0.4,0.3-0.7,0.7-0.7c0.4,0,0.7,0.3,0.7,0.7v2.2h2.2c0.4,0,0.7,0.3,0.7,0.7
|
||||
S23.8,24.7,23.4,24.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
public/images/apply/ldap.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25667"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#FFC835" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25667)"><g><path d="M33.400099999999995,12C34.0625,12,34.599599999999995,12.537033,34.599599999999995,13.1995L34.599599999999995,34.799099999999996C34.6004,35.4621,34.0631,36,33.400099999999995,36L16.59993,36C14.61174,36,13,34.3883,13,32.400099999999995L13,15.59993C13,13.61174,14.61174,12,16.59993,12L33.400099999999995,12ZM32.1991,31.1991L16.59993,31.1991C15.953240000000001,31.2215,15.440570000000001,31.7523,15.440570000000001,32.3994C15.440570000000001,33.0464,15.953240000000001,33.577200000000005,16.59993,33.599599999999995L32.1991,33.599599999999995L32.1991,31.1991ZM23.799799999999998,22.799799999999998C21.8116,22.799799999999998,20.19986,24.4115,20.19986,26.3997L27.3997,26.3997C27.3997,24.4115,25.788,22.799799999999998,23.799799999999998,22.799799999999998ZM23.799799999999998,16.799419999999998C22.47406,16.799419999999998,21.39935,17.87413,21.39935,19.19986C21.39935,20.525579999999998,22.47406,21.60029,23.799799999999998,21.60029C25.125500000000002,21.60029,26.200200000000002,20.525579999999998,26.200200000000002,19.19986C26.1994,17.87446,25.1252,16.80022,23.799799999999998,16.799419999999998Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
public/images/apply/mail.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25401"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#F57775" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25401)"><g><path d="M36,19.35553L36,31.6471C36,32.8166,35.0519,33.764700000000005,33.882400000000004,33.764700000000005L14.11765,33.764700000000005C12.948108,33.764700000000005,12.00000549316,32.8166,12.00000549316,31.6471L12.00000549316,19.35553L20.804470000000002,26.8202C22.6481,28.3834,25.3519,28.3834,27.1955,26.8202L36,19.35553ZM33.882400000000004,14.000000646254C34.1404,14.000286291,34.3963,14.0471559,34.6376,14.138353C34.984700000000004,14.270521,35.2905,14.492136,35.5242,14.780706C35.6509,14.935896,35.7549,15.10818,35.8334,15.29247C35.925200000000004,15.50988,35.9816,15.74565,35.9958,15.99341L36,16.11765L36,16.823529999999998L25.3744,25.8871C24.6211,26.5295,23.5233,26.5644,22.730800000000002,25.9711L22.625700000000002,25.886400000000002L12.00000517004,16.823529999999998L12.00000517004,16.11765C11.9982451,15.318200000000001,12.448325,14.586376,13.16259,14.227294C13.45869,14.0776132,13.78587,13.999747961,14.11765,14.000000646254L33.882400000000004,14.000000646254Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
public/images/apply/meeting.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25658"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#72A1F7" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25658)"><g><path d="M33.1767,19.46395L30.5723,21.34133L30.5723,19.01332C30.5723,17.34516,29.217,15.99442094,27.5489,16L15.01332,16C13.34911,15.999999801611,12,17.34911,12,19.01332L12,29.5822C12,31.2464,13.34911,32.595600000000005,15.01332,32.595600000000005L27.5489,32.595600000000005C29.217,32.6011,30.5723,31.2504,30.5723,29.5822L30.5723,27.2542L33.182500000000005,29.1259C33.7237,29.5146,34.436800000000005,29.5683,35.0302,29.2651C35.623599999999996,28.9619,35.9979,28.3525,36,27.6861L36,20.907980000000002C36.0011,20.23879,35.626599999999996,19.62559,35.0308,19.32086C34.4351,19.01614,33.7187,19.07142,33.1767,19.46395Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
public/images/apply/report.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25545"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#9D95E5" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25545)"><g><g><path d="M27.3699,31.3292C26.4274,32.2528,24.9169,32.2452,23.983800000000002,31.3121C23.0507,30.379,23.043100000000003,28.8686,23.9667,27.926099999999998L32.3201,19.578400000000002L32.3201,15.27301C32.3201,13.46482,30.8539,11.999210349,29.0457,12L15.27301,12C13.46538,11.999999784513,12,13.46538,12,15.27301L12,32.6318C11.999209911,34.44,13.46482,35.9063,15.27301,35.9063L29.0457,35.9063C30.8541,35.9063,32.3201,34.4403,32.3201,32.6318L32.3201,26.3789L27.3699,31.3292ZM17.74958,20.06886C17.25443,20.06886,16.85304,19.66746,16.85304,19.17232C16.85304,18.67717,17.25443,18.27578,17.74958,18.27578L23.1288,18.27578C23.624000000000002,18.27578,24.025399999999998,18.67717,24.025399999999998,19.17232C24.025399999999998,19.66746,23.624000000000002,20.06886,23.1288,20.06886L17.74958,20.06886Z" fill="#FFFFFF" fill-opacity="1"/></g><g transform="matrix(0.7071067690849304,0.7071067690849304,-0.7071067690849304,0.7071067690849304,23.826460884525744,-18.843463462634503)"><rect x="34.65930366516113" y="19.33935022354126" width="1.8960309028625488" height="14.49763011932373" rx="0.9480154514312744" fill="#FFFFFF" fill-opacity="1"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
public/images/apply/robot.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25405"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#FFC835" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25405)"><g><g><rect x="12" y="26.31494140625" width="1.8119423389434814" height="7.173644542694092" rx="0.9059711694717407" fill="#FFFFFF" fill-opacity="1"/></g><g><rect x="34.18804931640625" y="26.31494140625" width="1.8119423389434814" height="7.173644542694092" rx="0.9059711694717407" fill="#FFFFFF" fill-opacity="1"/></g><g><path d="M29.53465,20.54139L31.35485,17.38422C31.41525,17.38422,31.47695,17.39383,31.53875,17.39383C32.931349999999995,17.39648,33.97485,16.118949999999998,33.69415,14.75489C33.41345,13.390835,31.95045,12.629057,30.67195,13.181321C29.393549999999998,13.733585,28.94525,15.32101,29.74605,16.4604L27.76795,19.89211C25.287950000000002,19.2077,22.669629999999998,19.20201,20.18666,19.87564L18.21274,16.46315C19.0135,15.32376,18.56525,13.73633,17.28679,13.184066C16.00834,12.631803,14.545278,13.393581,14.2646052,14.75764C13.983932,16.1217,15.02738,17.39923,16.42001,17.39657C16.48179,17.39657,16.542180000000002,17.39657,16.60258,17.386960000000002L18.417270000000002,20.529040000000002C16.05625,21.62718,14.483158,23.3979,14.36648,25.4172L14.356871,25.4172L14.356871,33.289500000000004C14.356871,34.7254,15.52087,35.889399999999995,16.95673,35.889399999999995L30.96905,35.889399999999995C32.40495,35.889399999999995,33.56895,34.7254,33.56895,33.289500000000004L33.56895,25.4172L33.559349999999995,25.4172C33.444050000000004,23.4048,31.88195,21.636789999999998,29.53465,20.54139ZM27.063850000000002,29.5105L20.86888,29.5105C19.89243,29.5105,19.10087,28.718899999999998,19.10087,27.7425C19.10087,26.766,19.89243,25.9745,20.86888,25.9745L27.05695,25.9745C28.03335,25.9745,28.82495,26.766,28.82495,27.7425C28.82495,28.718899999999998,28.03335,29.5105,27.05695,29.5105L27.063850000000002,29.5105Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
41
public/images/apply/scan.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#72A1F7;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M20.3,35.8h-4.7c-1.9,0-3.4-1.5-3.4-3.4v-4.7c0-0.4,0.3-0.8,0.8-0.8s0.8,0.3,0.8,0.8v4.7
|
||||
c0,1.1,0.9,1.9,1.9,1.9h4.7c0.4,0,0.8,0.3,0.8,0.8S20.7,35.8,20.3,35.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M13,21.1c-0.4,0-0.8-0.3-0.8-0.8v-4.7c0-1.9,1.5-3.4,3.4-3.4h4.7c0.4,0,0.8,0.3,0.8,0.8s-0.3,0.8-0.8,0.8
|
||||
h-4.7c-1.1,0-1.9,0.9-1.9,1.9v4.7C13.8,20.7,13.4,21.1,13,21.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M35,21.1c-0.4,0-0.8-0.3-0.8-0.8v-4.7c0-1.1-0.9-1.9-1.9-1.9h-4.7c-0.4,0-0.8-0.3-0.8-0.8s0.3-0.8,0.8-0.8
|
||||
h4.7c1.9,0,3.4,1.5,3.4,3.4v4.7C35.8,20.7,35.4,21.1,35,21.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M32.3,35.8h-4.7c-0.4,0-0.8-0.3-0.8-0.8s0.3-0.8,0.8-0.8h4.7c1.1,0,1.9-0.9,1.9-1.9v-4.7
|
||||
c0-0.4,0.3-0.8,0.8-0.8s0.8,0.3,0.8,0.8v4.7C35.8,34.2,34.2,35.8,32.3,35.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M29.5,16.5H18.5c-1.1,0-1.9,0.9-1.9,1.9v11.1c0,1.1,0.9,1.9,1.9,1.9h11.1c1.1,0,1.9-0.9,1.9-1.9V18.5
|
||||
C31.5,17.4,30.6,16.5,29.5,16.5z M27.7,24.8h-7.3c-0.4,0-0.8-0.3-0.8-0.8s0.3-0.8,0.8-0.8h7.3c0.4,0,0.8,0.3,0.8,0.8
|
||||
S28.1,24.8,27.7,24.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
18
public/images/apply/setting.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#72A1F7;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
|
||||
<g>
|
||||
<path class="st1" d="M34.6,26.5c-1.9-1.1-1.9-3.8,0-4.9c0.6-0.3,0.8-1.1,0.5-1.7l-1.9-3.3c-0.3-0.6-1.1-0.8-1.7-0.5
|
||||
c-1.9,1.1-4.3-0.3-4.3-2.5c0-0.7-0.6-1.3-1.3-1.3h-3.8c-0.7,0-1.3,0.6-1.3,1.3c0,2.2-2.4,3.6-4.3,2.5l0,0c-0.6-0.3-1.4-0.1-1.7,0.5
|
||||
L13,19.8c-0.3,0.6-0.1,1.4,0.5,1.7l0,0c1.9,1.1,1.9,3.8,0,4.9c-0.6,0.3-0.8,1.1-0.5,1.7l1.9,3.3c0.3,0.6,1.1,0.8,1.7,0.5
|
||||
c1.9-1.1,4.3,0.3,4.3,2.5v0c0,0.7,0.6,1.3,1.3,1.3h3.8c0.7,0,1.3-0.6,1.3-1.3v0c0-2.2,2.4-3.6,4.3-2.5h0c0.6,0.3,1.4,0.1,1.7-0.5
|
||||
l1.9-3.3C35.4,27.6,35.2,26.8,34.6,26.5z M24,26.6c-1.4,0-2.6-1.2-2.6-2.6c0-1.4,1.2-2.6,2.6-2.6c1.4,0,2.6,1.2,2.6,2.6
|
||||
C26.6,25.4,25.4,26.6,24,26.6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
public/images/apply/signin.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_3054_25526"><rect x="12" y="12" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="48" height="48" rx="12" fill="#72A1F7" fill-opacity="1"/><g clip-path="url(#master_svg0_3054_25526)"><g><g><path d="M34.0464,21.911920000000002C34.0464,16.439700000000002,29.336,12,23.523899999999998,12C17.71187,12,13,16.43682,13,21.911920000000002C13,24.689799999999998,14.21541,27.1998,16.16813,28.9999L23.5211,36L30.7962,29.0676C30.9402,28.9365,31.0842,28.8026,31.2196,28.6643C33.0192,26.8769,34.0359,24.4483,34.0464,21.911920000000002ZM23.397199999999998,26.7015C20.95156,26.6984,18.97124,24.7138,18.97336,22.2681C18.97548,19.82244,20.959229999999998,17.84126,23.404899999999998,17.84232C25.8506,17.84338,27.8326,19.82627,27.8326,22.271900000000002C27.8326,24.720599999999997,25.8459,26.704700000000003,23.397199999999998,26.7015Z" fill="#FFFFFF" fill-opacity="1"/></g><g><ellipse cx="23.397215843200684" cy="22.271930694580078" rx="2.733229160308838" ry="2.733229160308838" fill="#FFFFFF" fill-opacity="1"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/avatar/default_claude.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/images/avatar/default_openai.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
0
public/images/browser/360.png
Executable file → Normal file
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
public/images/browser/chrome.png
Executable file → Normal file
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
0
public/images/browser/firefox.png
Executable file → Normal file
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
0
public/images/browser/ie.png
Executable file → Normal file
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
0
public/images/browser/safari.png
Executable file → Normal file
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
1
public/images/dialog/todo.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691334156944" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20927" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M351.1 644.3m-40.2 0a40.2 40.2 0 1 0 80.4 0 40.2 40.2 0 1 0-80.4 0Z" p-id="20928" fill="#9D95E5"></path><path d="M489 764.8C489 638.1 592.1 535 718.9 535c69 0 130.2 31.2 172.4 79.4V178.7c0-50.6-41.4-91.9-91.9-91.9H224.7c-50.6 0-91.9 41.4-91.9 91.9v666.5c0 50.8 41.2 91.9 91.9 91.9h343.7C520.2 895 489 833.8 489 764.8z m69-471h183.9c15.8 0 28.7 12.9 28.7 28.7s-12.9 28.7-28.7 28.7H558c-15.8 0-28.7-12.9-28.7-28.7s12.8-28.7 28.7-28.7zM351.1 742c-53.9 0-97.7-43.8-97.7-97.7 0-53.9 43.8-97.7 97.7-97.7s97.7 43.8 97.7 97.7c0 53.9-43.8 97.7-97.7 97.7z m112.3-445.1L348.5 411.8c-5.6 5.6-13 8.4-20.3 8.4s-14.7-2.8-20.3-8.4l-46-46c-11.2-11.2-11.2-29.4 0-40.6 11.2-11.2 29.4-11.2 40.7 0l25.6 25.6 94.6-94.6c11.2-11.2 29.4-11.2 40.7 0 11.1 11.3 11.1 29.5-0.1 40.7z" p-id="20929" fill="#9D95E5"></path><path d="M718.9 592.4c-95.2 0-172.4 77.2-172.4 172.4s77.2 172.4 172.4 172.4S891.3 860 891.3 764.8c-0.1-95.2-77.2-172.4-172.4-172.4z m76.2 248.7c-5.6 5.6-12.9 8.4-20.3 8.4s-14.7-2.8-20.3-8.4l-55.9-55.9c-0.4-0.4-0.6-1-1-1.5-2.1-2.4-4-4.9-5.2-7.8-1.4-3.4-2.1-6.9-2.1-10.5 0-0.2-0.1-0.3-0.1-0.5V681c0-15.8 12.9-28.7 28.7-28.7 15.8 0 28.7 12.9 28.7 28.7v72l47.5 47.5c11.2 11.2 11.2 29.3 0 40.6z" p-id="20930" fill="#9D95E5"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
BIN
public/images/emoticon/08/110.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/emoticon/08/666.gif
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/images/emoticon/08/bixin.gif
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/images/emoticon/08/chigua.gif
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/images/emoticon/08/enen.gif
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/images/emoticon/08/guaiqiao.gif
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/images/emoticon/08/gun.gif
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/images/emoticon/08/hahaha.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/emoticon/08/haixiu.gif
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
public/images/emoticon/08/hehe.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/emoticon/08/huahua.gif
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/images/emoticon/08/icon.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
public/images/emoticon/08/keai.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/emoticon/08/kuaichiyao.gif
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/images/emoticon/08/laiba.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/emoticon/08/lengmo.gif
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/images/emoticon/08/ok.gif
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/images/emoticon/08/tuxue.gif
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
public/images/emoticon/08/wanan.gif
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/images/emoticon/08/wenhao.gif
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/images/emoticon/08/wofangle.gif
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/images/emoticon/08/xiaozheliulei.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/emoticon/08/xiexielaoban.gif
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/images/emoticon/08/yongbao.gif
Normal file
|
After Width: | Height: | Size: 14 KiB |