Compare commits

..

99 Commits

Author SHA1 Message Date
kuaifan
9fd8d44a6e build 2025-02-05 01:37:41 +09:00
kuaifan
64262134c4 perf: 支持自定义仪表盘欢迎词 2025-02-05 01:33:32 +09:00
kuaifan
0019c9ef41 build 2025-02-04 13:14:43 +09:00
kuaifan
2676ebd047 no message 2025-02-04 13:10:46 +09:00
kuaifan
97cdd56110 fix: 跨地区发消息出现消息过期的情况 2025-02-04 13:10:46 +09:00
kuaifan
d973451bdc feat: 添加 DeepSeek AI 2025-02-04 13:10:46 +09:00
kuaifan
80313f613e perf: ChatGPT 支持自定义 Base URL 2025-02-04 13:10:46 +09:00
kuaifan
5c564524a3 Merge pull request #247 from zzzzzhy/patch-2
定时任务判断fix
2025-01-17 20:24:22 +08:00
zzzzzhy
e081fbd92b 定时任务判断fix
修复定时更新https证书任务判断逻辑
2025-01-17 14:15:24 +08:00
kuaifan
0ecc20472a Merge pull request #246 from zzzzzhy/patch-1
安装软件依赖
2025-01-16 22:35:38 +08:00
zzzzzhy
b51052f0c6 安装软件依赖 2025-01-16 20:14:08 +08:00
kuaifan
cb106e42ee Merge pull request #245 from zzzzzhy/renew_cert
feat:添加https证书自动更新
2025-01-16 19:54:17 +08:00
zzzzzhy
52f9495ff8 feat:添加https证书自动更新 2025-01-16 06:44:26 +00:00
kuaifan
440b633bad perf: 优化仪表盘任务更新规则 2025-01-15 15:41:38 +08:00
kuaifan
a07913181a fix: 多线程下载文件损坏的问题 2025-01-15 15:27:39 +08:00
kuaifan
34ffd96c86 no message 2025-01-13 10:56:30 +08:00
kuaifan
46a623b430 perf: 优化仪表盘任务更新规则 2025-01-13 10:56:29 +08:00
kuaifan
c16e37023c fix: 多线程下载文件损坏的问题 2025-01-13 10:56:29 +08:00
kuaifan
1cb0cdf540 Merge pull request #242 from nightcp/fix-report-sign-repeat 2025-01-04 20:48:09 +08:00
nightcp
073d03a882 fix: 修复新建周报或日报唯一标识重复 2025-01-04 19:58:37 +08:00
kuaifan
30b9276ab4 no message 2025-01-03 08:19:55 +08:00
kuaifan
76c8b4a4c6 no message 2025-01-03 04:48:38 +08:00
kuaifan
9ea4781d93 perf: 更新小海豚表情包 2025-01-03 03:56:22 +08:00
kuaifan
07d583f73f perf: 优化任务时间冲突提示 2025-01-02 20:25:47 +08:00
kuaifan
12c74aef7a no message 2025-01-02 14:56:30 +08:00
kuaifan
64b10e3060 perf: 优化消息 2025-01-02 14:54:22 +08:00
kuaifan
ab2b29f267 perf: 群聊总人数排除机器人 2025-01-02 14:23:23 +08:00
kuaifan
be9a968ad9 perf: 群聊总人数排除机器人 2025-01-02 13:41:09 +08:00
kuaifan
5f87067a75 fix: 部分电脑无法复制的问题 2025-01-02 12:48:35 +08:00
weifs
ef273bd9dd fix: 修复任务可见性 - 任务重覆获取, 子任务负责人看不到任务问题 2024-12-24 00:11:23 +08:00
weifs
0737a9fae7 fix: 修复任务可见性 - 任务重覆获取, 子任务负责人看不到任务问题 2024-12-23 23:19:22 +08:00
kuaifan
727d7e1d81 build 2024-12-23 11:53:40 +08:00
kuaifan
87e8589aea no message 2024-12-23 11:47:39 +08:00
kuaifan
b13758d3e9 perf: 优化任务面板 2024-12-22 17:45:58 +08:00
kuaifan
14775e2861 perf: 优化子任务的可见性 2024-12-21 22:20:39 +08:00
kuaifan
94af3822d8 perf: 优化客户端 2024-12-21 15:33:01 +08:00
kuaifan
07254c9f27 perf: 优化会议 2024-12-21 11:57:48 +08:00
kuaifan
a99c2f6944 perf: 优化客户端 2024-12-21 11:47:49 +08:00
kuaifan
f9540b08cd perf: 优化会议 2024-12-20 21:24:17 +08:00
kuaifan
34af77eb6d no message 2024-12-20 20:01:42 +08:00
kuaifan
cf3f22776c perf: 优化会员搜索 2024-12-20 19:59:18 +08:00
kuaifan
5bebc8b5ee perf: 优化打开会话 2024-12-20 19:41:00 +08:00
kuaifan
8a4b0c57f9 perf: 优化会议 2024-12-20 19:25:05 +08:00
kuaifan
1acfd7ee34 no message 2024-12-20 09:22:14 +08:00
kuaifan
a29661c54d perf: 优化会议 2024-12-20 09:01:17 +08:00
kuaifan
90558d5ece no message 2024-12-18 20:37:16 +08:00
kuaifan
e6c7007be5 perf: 优化项目面板任务加载 2024-12-18 20:14:35 +08:00
kuaifan
16d0d1687f perf: 优化客户端加载 2024-12-18 19:56:36 +08:00
kuaifan
95ab44d118 perf: 优化客户端加载 2024-12-18 15:23:25 +08:00
kuaifan
e541757b76 no message 2024-12-18 15:22:13 +08:00
kuaifan
f422aea330 fix: 移交账号后工作流的负责人没有更新 2024-12-18 00:25:04 +08:00
kuaifan
d5eb3716aa no message 2024-12-18 00:25:04 +08:00
yijixx
7fb854fb48 feat: 替换网页的资源为本地资源 2024-12-17 17:01:17 +08:00
kuaifan
60b5ecdcd7 fix: 全屏预览时深色皮肤反色的情况 2024-12-17 09:11:12 +08:00
kuaifan
6cce7d31ff build 2024-12-17 08:49:40 +08:00
kuaifan
46f5dd99a6 perf: 优化对话阅读状况 2024-12-17 08:47:05 +08:00
kuaifan
9753dec996 perf: 优化表情回复 2024-12-17 08:41:46 +08:00
kuaifan
53f2e07178 build 2024-12-17 00:14:13 +08:00
kuaifan
3aa2c604d8 perf: 优化桌面端数据处理 2024-12-17 00:07:50 +08:00
kuaifan
d8fbf36e00 perf: 优化资源 2024-12-16 23:29:05 +08:00
kuaifan
008653e3d9 fix: 桌面端查看表情图片缩略图显示错误 2024-12-16 21:23:18 +08:00
kuaifan
23188777fe fix: 项目面板任务不显示的情况 2024-12-16 21:15:30 +08:00
kuaifan
8eb0a49ee6 perf: 优化数据流 2024-12-16 17:04:45 +08:00
kuaifan
207f09a4af fix: 修复移动任务子任务不跟随的情况 2024-12-16 16:07:39 +08:00
kuaifan
69120c5045 build 2024-12-15 23:39:27 +08:00
kuaifan
b8143d1a9b no message 2024-12-15 23:24:38 +08:00
kuaifan
f7eab5893a perf: AI创建任务确认 2024-12-15 22:53:26 +08:00
kuaifan
5fc598a220 no message 2024-12-15 22:45:27 +08:00
kuaifan
783c21ad18 perf: 优化项目面板 2024-12-15 22:27:05 +08:00
kuaifan
a1ce6e6928 perf: 优化项目面板 2024-12-15 09:39:18 +08:00
kuaifan
8cbae629a5 perf: 优化项目面板 2024-12-14 18:51:46 +08:00
kuaifan
da7e832f21 fix: 复制文件权限判断 2024-12-14 18:41:30 +08:00
kuaifan
a572ba0523 perf: 优化项目面板 2024-12-14 18:38:47 +08:00
kuaifan
85a20168dc build 2024-12-13 23:28:46 +08:00
kuaifan
25be9c0fef perf: 优化子任务上下文 2024-12-13 23:19:20 +08:00
kuaifan
a8c890ba51 perf: 优化子任务时间调整 2024-12-13 19:28:05 +08:00
kuaifan
11628b98ca no message 2024-12-13 16:15:47 +08:00
kuaifan
4ae6ca945b no message 2024-12-13 16:00:21 +08:00
kuaifan
49aa1434aa perf: 优化超长文本信息 2024-12-13 15:49:31 +08:00
kuaifan
9e92c61fbf no message 2024-12-13 15:49:31 +08:00
kuaifan
c84111b6b9 perf: 记录版本信息 2024-12-13 15:49:31 +08:00
kuaifan
3a2fcdd18a no message 2024-12-13 15:49:30 +08:00
kuaifan
84a800f69b fix: @在线状态不正确 2024-12-13 15:49:30 +08:00
kuaifan
77e08aa048 no message 2024-12-13 15:49:30 +08:00
kuaifan
0d6fd903f1 perf: 支持更多办公文件格式 2024-12-13 09:47:52 +08:00
kuaifan
bcc74dd927 perf: 请假或外出时取消打卡提醒 2024-12-13 01:00:31 +08:00
kuaifan
dd0720afa7 no message 2024-12-13 00:27:41 +08:00
kuaifan
a06a4095b6 no message 2024-12-12 23:10:33 +08:00
kuaifan
29bc009c07 perf: 图片容错处理 2024-12-12 22:36:25 +08:00
kuaifan
520d2a0e20 no message 2024-12-12 22:01:01 +08:00
kuaifan
dbeb9dd561 perf: 优化全局监听事件 2024-12-12 14:14:51 +08:00
kuaifan
5b02d8008f perf: 优化数据流消息 2024-12-12 13:45:48 +08:00
kuaifan
a032c6114f no message 2024-12-12 12:55:29 +08:00
kuaifan
69ec4966d5 build 2024-12-12 00:44:59 +08:00
kuaifan
87fab80ea3 no message 2024-12-12 00:43:08 +08:00
kuaifan
bd2dabe851 no message 2024-12-12 00:30:25 +08:00
kuaifan
4a45d69e5b no message 2024-12-11 23:26:22 +08:00
kuaifan
e15bea9342 fix: 修复使用AI创建任务顺序错误的问题 2024-12-11 22:36:57 +08:00
kuaifan
7132413837 no message 2024-12-11 22:17:51 +08:00
317 changed files with 6663 additions and 1464 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@
.idea
.vscode
.vagrant
.windsurfrules
.phpunit.result.cache
Homestead.json
Homestead.yaml

View File

@@ -2,6 +2,117 @@
All notable changes to this project will be documented in this file.
## [0.42.57]
### Bug Fixes
- 跨地区发消息出现消息过期的情况
- 多线程下载文件损坏的问题
- 修复新建周报或日报唯一标识重复
### Features
- 添加 DeepSeek AI
- 添加https证书自动更新
### Performance
- 支持自定义仪表盘欢迎词
- ChatGPT 支持自定义 Base URL
- 优化仪表盘任务更新规则
## [0.42.37]
### Bug Fixes
- 部分电脑无法复制的问题
- 修复任务可见性 - 任务重覆获取, 子任务负责人看不到任务问题
### Performance
- 更新小海豚表情包
- 优化任务时间冲突提示
- 优化消息
- 群聊总人数排除机器人
## [0.42.26]
### Bug Fixes
- 移交账号后工作流的负责人没有更新
- 全屏预览时深色皮肤反色的情况
### Features
- 替换网页的资源为本地资源
### Performance
- 优化任务面板
- 优化子任务的可见性
- 优化客户端
- 优化会议
- 优化会员搜索
- 优化打开会话
- 优化项目面板任务加载
- 优化客户端加载
## [0.42.3]
### Performance
- 优化对话阅读状况
- 优化表情回复
## [0.42.0]
### Bug Fixes
- 桌面端查看表情图片缩略图显示错误
- 项目面板任务不显示的情况
- 修复移动任务子任务不跟随的情况
### Performance
- 优化桌面端数据处理
- 优化资源
- 优化数据流
## [0.41.93]
### Bug Fixes
- 复制文件权限判断
### Performance
- AI创建任务确认
- 优化项目面板
## [0.41.84]
### Bug Fixes
- @在线状态不正确
### Performance
- 优化子任务上下文
- 优化子任务时间调整
- 优化超长文本信息
- 记录版本信息
- 支持更多办公文件格式
- 请假或外出时取消打卡提醒
- 图片容错处理
- 优化全局监听事件
- 优化数据流消息
## [0.41.64]
### Bug Fixes
- 修复使用AI创建任务顺序错误的问题
## [0.41.55]
### Performance

View File

@@ -47,16 +47,6 @@ cd dootask
./cmd port 80
```
### Change App Url
```bash
# This URL only affects the email reply.
./cmd url {Your domain url}
# example:
./cmd url https://domain.com
```
### Stop server
```bash

View File

@@ -47,16 +47,6 @@ cd dootask
./cmd port 80
```
### 更换URL
```bash
# 此地址仅影响邮件回复功能
./cmd url {域名地址}
# 例如:
./cmd url https://domain.com
```
### 停止服务
```bash

View File

@@ -223,6 +223,19 @@ class Handler extends ExceptionHandler
} catch (\ImagickException) { }
}
// 容错处理
$patternFault = '/^(images\/.*\.(png|jpg|jpeg))\/crop\/([^\/]+)$/';
$matchesFault = null;
if (preg_match($patternFault, $path, $matchesFault)) {
$file = public_path($matchesFault[1]);
if (!file_exists($file)) {
$file = public_path('images/other/imgerr.jpg');
}
if (file_exists($file)) {
return response()->file($file);
}
}
return null;
}
}

View File

@@ -16,6 +16,7 @@ use App\Tasks\PushTask;
use App\Module\BillExport;
use App\Models\WebSocketDialog;
use App\Models\ApproveProcMsg;
use App\Models\ApproveProcInstHistory;
use App\Exceptions\ApiException;
use App\Models\UserDepartment;
use App\Models\WebSocketDialogMsg;
@@ -1146,13 +1147,9 @@ class ApproveController extends AbstractController
*/
public function user__status()
{
$data['userid'] = intval(Request::input('userid'));
$ret = Ihttp::ihttp_get($this->flow_url . '/api/v1/workflow/process/getUserApprovalStatus?' . http_build_query($data));
$procdef = json_decode($ret['ret'] == 1 ? $ret['data'] : '{}', true);
if (isset($procdef['status']) && $procdef['status'] == 200) {
return Base::retSuccess('success', $procdef['data']["proc_def_name"] ?? '');
}
return Base::retSuccess('success', '');
$userid = intval(Request::input('userid'));
$status = ApproveProcInstHistory::getUserApprovalStatus($userid);
return Base::retSuccess('success', $status);
}
/**

View File

@@ -14,6 +14,8 @@ use App\Module\Base;
use App\Module\Timer;
use App\Module\Extranet;
use App\Module\TimeRange;
use App\Module\MsgTool;
use App\Module\Table\OnlineData;
use App\Models\FileContent;
use App\Models\AbstractModel;
use App\Models\WebSocketDialog;
@@ -288,6 +290,9 @@ class DialogController extends AbstractController
$array = array_filter($data->toArray(), function ($item) {
return $item['userid'] > 0;
});
foreach ($array as &$item) {
$item['online'] = $item['bot'] || OnlineData::live($item['userid']) > 0;
}
} else {
$data = WebSocketDialogUser::select(['web_socket_dialog_users.*', 'users.bot'])
->join('users', 'web_socket_dialog_users.userid', '=', 'users.userid')
@@ -1056,7 +1061,11 @@ class DialogController extends AbstractController
} elseif ($reply_id > 0) {
$action = "reply-$reply_id";
if ($reply_check === 'yes') {
$exisUserMsg = WebSocketDialogMsg::whereDialogId($dialog_id)->where('id', '>', $reply_id)->whereBot(0)->exists();
$exisUserMsg = WebSocketDialogMsg::whereDialogId($dialog_id)
->where('id', '>', $reply_id)
->whereBot(0)
->whereNotIn('type', ['notice', 'template'])
->exists();
if (!$exisUserMsg) {
$action = "";
}
@@ -1088,21 +1097,29 @@ class DialogController extends AbstractController
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,
$text = MsgTool::truncateText($text, 500, $ext);
$desc = strip_tags($markdown ? Base::markdown2html($text) : $text);
$desc = mb_substr(WebSocketDialogMsg::filterEscape($desc), 0, 200);
$msgData = [
'desc' => $desc, // 描述内容
'text' => $text, // 简要内容
'type' => $ext, // 内容类型
'file' => [
'name' => "LongText-{$strlen}.{$ext}",
'size' => $size,
'file' => $file,
'path' => $path,
'url' => Base::fillUrl($path),
'thumb' => '',
'width' => -1,
'height' => -1,
'ext' => $ext,
],
];
if (empty($key)) {
$key = mb_substr(strip_tags($text), 0, 200);
$key = $desc;
}
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence, $key);
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'longtext', $msgData, $user->userid, false, false, $silence, $key);
} else {
$msgData = ['text' => $text];
if ($markdown) {
@@ -1630,6 +1647,18 @@ class DialogController extends AbstractController
$msg = File::formatFileData($msg);
$data['content'] = $msg['content'];
$data['file_mode'] = $msg['file_mode'];
} elseif ($data['type'] == 'longtext') {
$data['content'] = [
'type' => 'htm',
'content' => Doo::translate("内容不存在")
];
if (isset($data['msg']['file']['path'])) {
$filePath = public_path($data['msg']['file']['path']);
if (file_exists($filePath)) {
$data['content']['type'] = $data['msg']['type'];
$data['content']['content'] = file_get_contents($filePath);
}
}
}
//
return Base::retSuccess('success', $data);
@@ -1673,7 +1702,7 @@ class DialogController extends AbstractController
}
//
$filePath = public_path($array['path']);
return Base::BinaryFileResponse($filePath, $array['name']);
return Base::DownloadFileResponse($filePath, $array['name']);
}
/**

View File

@@ -301,6 +301,7 @@ class FileController extends AbstractController
//
$userid = $user->userid;
if ($row->pid > 0) {
File::permissionFind($row->pid, $user, 1);
$userid = intval(File::whereId($row->pid)->value('userid'));
}
//

View File

@@ -1058,20 +1058,20 @@ class ProjectController extends AbstractController
$query->where('project_users.owner', 1);
$query->where('project_users.userid', $userid);
});
$builder->leftJoin('project_task_users as project_sub_task_users', function ($query) use($userid) {
$query->on('project_sub_task_users.task_pid', '=', 'project_tasks.parent_id');
$query->where('project_sub_task_users.userid', $userid);
});
$builder->leftJoin('project_task_visibility_users', function ($query) use($userid) {
$query->on('project_task_visibility_users.task_id', '=', 'project_tasks.id');
$query->where('project_task_visibility_users.userid', $userid);
});
$builder->leftJoin('project_task_visibility_users as project_sub_task_visibility_users', function ($query) use($userid) {
$query->on('project_sub_task_visibility_users.task_id', '=', 'project_tasks.parent_id');
$query->where('project_sub_task_visibility_users.userid', $userid);
});
$builder->where(function ($query) use ($userid) {
$query->where("project_tasks.visibility", 1);
$query->orWhere("project_users.userid", $userid);
$query->orWhere("project_task_users.userid", $userid);
$query->orWhere("project_task_visibility_users.userid", $userid);
$query->orWhere("project_sub_task_users.userid", $userid);
$query->orWhere("project_sub_task_visibility_users.userid", $userid);
});
// 优化子查询汇总
$builder->leftJoinSub(function ($query) {
@@ -1159,6 +1159,7 @@ class ProjectController extends AbstractController
$list = ProjectTask::with(['taskUser'])
->select([
'projects.name as project_name',
'project_tasks.project_id',
'project_tasks.id',
'project_tasks.name',
'project_tasks.start_at',
@@ -1852,7 +1853,7 @@ class ProjectController extends AbstractController
}
//
$filePath = public_path($file->getRawOriginal('path'));
return Base::BinaryFileResponse($filePath, $file->name);
return Base::DownloadFileResponse($filePath, $file->name);
}
/**
@@ -2399,6 +2400,7 @@ class ProjectController extends AbstractController
*/
public function task__move()
{
Base::checkClientVersion('0.42.0');
User::auth();
//
$task_id = intval(Request::input('task_id'));
@@ -2436,9 +2438,26 @@ class ProjectController extends AbstractController
//
$task->moveTask($project_id, $column_id, $flow_item_id, $owner, $assist, $completeAt);
//
$task = ProjectTask::userTask($task_id);
$data = [];
$mainTask = ProjectTask::userTask($task_id)?->toArray();
if ($mainTask) {
$mainTask['column_name'] = ProjectColumn::whereId($mainTask['column_id'])->value('name');
$mainTask['project_name'] = Project::whereId($mainTask['project_id'])->value('name');
$data[] = $mainTask;
//
$subTasks = ProjectTask::whereParentId($task_id)->get();
foreach ($subTasks as $subTask) {
$data[] = [
'id' => $subTask->id,
'project_id' => $subTask->project_id,
'column_id' => $subTask->column_id,
'column_name' => $mainTask['column_name'],
'project_name' => $mainTask['project_name'],
];
}
}
//
return Base::retSuccess('移动成功', $task);
return Base::retSuccess('移动成功', $data);
}
/**

View File

@@ -41,7 +41,7 @@ class SystemController extends AbstractController
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'voice2text', 'translation', 'e2e_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'system_alias', 'image_compress', 'image_quality', 'image_save_local', 'start_home']
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'voice2text', 'translation', 'e2e_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'system_alias', 'system_welcome', 'image_compress', 'image_quality', 'image_save_local', 'start_home']
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -80,6 +80,7 @@ class SystemController extends AbstractController
'user_private_chat_mute',
'user_group_chat_mute',
'system_alias',
'system_welcome',
'image_compress',
'image_quality',
'image_save_local',
@@ -108,6 +109,9 @@ class SystemController extends AbstractController
if ($all['system_alias'] == env('APP_NAME')) {
$all['system_alias'] = '';
}
if ($all['system_welcome'] == '欢迎您,{username}') {
$all['system_welcome'] = '';
}
$setting = Base::setting('system', Base::newTrim($all));
} else {
$setting = Base::setting('system');

View File

@@ -1151,6 +1151,9 @@ class UsersController extends AbstractController
* - update: 更新(默认)
* - remove: 删除
* @apiParam {String} alias 别名
* @apiParam {String} [userAgent] 浏览器信息
* @apiParam {String} [deviceModel] 设备型号
* @apiParam {String} [isNotified] 是否有通知权限0不通知、1通知
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1182,11 +1185,15 @@ class UsersController extends AbstractController
'alias' => $data['alias'],
'platform' => Base::platform(),
];
$version = $data['appVersion'] ? ($data['appVersionName'] . " ({$data['appVersion']})") : '';
$isNotified = trim($data['isNotified']) === 'true' || $data['isNotified'] === true ? 1 : intval($data['isNotified']);
$row = UmengAlias::where($inArray);
if ($row->exists()) {
$row->update([
'ua' => $data['userAgent'],
'device' => $data['deviceModel'],
'version' => $version,
'is_notified' => $isNotified,
'updated_at' => Carbon::now()
]);
return Base::retSuccess('别名已存在');
@@ -1194,6 +1201,8 @@ class UsersController extends AbstractController
$row = UmengAlias::createInstance(array_merge($inArray, [
'ua' => $data['userAgent'],
'device' => $data['deviceModel'],
'version' => $version,
'is_notified' => $isNotified,
]));
if ($row->save()) {
return Base::retSuccess('添加成功');

View File

@@ -210,8 +210,8 @@ class AbstractModel extends Model
/**
* 数据库更新或插入
* @param $where
* @param array $update 存在时更新的内容
* @param array $insert 不存在时插入的内容,如果没有则插入更新内容
* @param array|\Closure $update 存在时更新的内容
* @param array|\Closure $insert 不存在时插入的内容,如果没有则插入更新内容
* @param bool $isInsert 是否是插入数据
* @return AbstractModel|\Illuminate\Database\Eloquent\Builder|Model|object|static|null
*/
@@ -220,6 +220,12 @@ class AbstractModel extends Model
$row = static::where($where)->first();
if (empty($row)) {
$row = new static;
if ($update instanceof \Closure) {
$update = $update();
}
if ($insert instanceof \Closure) {
$insert = $insert();
}
$array = array_merge($where, $insert ?: $update);
if (isset($array[$row->primaryKey])) {
unset($array[$row->primaryKey]);
@@ -227,6 +233,9 @@ class AbstractModel extends Model
$row->updateInstance($array);
$isInsert = true;
} elseif ($update) {
if ($update instanceof \Closure) {
$update = $update();
}
$row->updateInstance($update);
$isInsert = false;
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Cache;
use Carbon\Carbon;
use DB;
class ApproveProcInstHistory extends AbstractModel
{
protected $table = 'approve_proc_inst_history';
/**
* 获取用户审批状态(请假、外出)
* @param $userid
* @return mixed|null
*/
public static function getUserApprovalStatus($userid)
{
if (empty($userid)) {
return null;
}
return Cache::remember('user_is_leave_' . $userid, Carbon::now()->addMinute(), function () use ($userid) {
return self::where([
['start_user_id', '=', $userid],
[DB::raw("JSON_UNQUOTE(JSON_EXTRACT(var, '$.startTime'))"), '<=', Carbon::now()->toDateTimeString()],
[DB::raw("JSON_UNQUOTE(JSON_EXTRACT(var, '$.endTime'))"), '>=', Carbon::now()->toDateTimeString()],
['state', '=', 2]
])->where(function ($query) {
$query->where('proc_def_name', 'like', '%请假%')
->orWhere('proc_def_name', 'like', '%外出%');
})->orderByDesc('id')->value('proc_def_name');
});
}
/**
* 判断用户是否请假(包含:请假、外出)
* @param $userid
* @return bool
*/
public static function userIsLeave($userid)
{
return (bool)self::getUserApprovalStatus($userid);
}
}

View File

@@ -79,9 +79,28 @@ class File extends AbstractModel
* office文件
*/
const officeExt = [
'doc', 'docx',
'xls', 'xlsx',
'ppt', 'pptx',
// 文本文件
'doc', 'docx', // Microsoft Word 文档
'dot', 'dotx', // Word 模板
'odt', // OpenDocument 文本格式
'ott', // OpenDocument 文本模板
'rtf', // 富文本格式
// 电子表格
'xls', 'xlsx', // Microsoft Excel 电子表格
'xlsm', // Excel 含宏的工作簿
'xlt', 'xltx', // Excel 模板
'ods', // OpenDocument 电子表格格式
'ots', // OpenDocument 电子表格模板
'csv', // 逗号分隔值
'tsv', // 制表符分隔值
// 演示文稿
'ppt', 'pptx', // Microsoft PowerPoint 演示文稿
'pps', 'ppsx', // PowerPoint 幻灯片放映
'pot', 'potx', // PowerPoint 模板
'odp', // OpenDocument 演示文稿格式
'otp', // OpenDocument 演示文稿模板
];
/**
@@ -264,9 +283,9 @@ class File extends AbstractModel
'text', 'md', 'markdown' => 'document',
'drawio' => 'drawio',
'mind' => 'mind',
'doc', 'docx' => "word",
'xls', 'xlsx' => "excel",
'ppt', 'pptx' => "ppt",
'doc', 'docx', 'dot', 'dotx', 'odt', 'ott', 'rtf' => "word",
'xls', 'xlsx', 'xlsm', 'xlt', 'xltx', 'ods', 'ots', 'csv', 'tsv' => "excel",
'ppt', 'pptx', 'pps', 'ppsx', 'pot', 'potx', 'odp', 'otp' => "ppt",
'wps' => "wps",
'jpg', 'jpeg', 'webp', 'png', 'gif', 'bmp', 'ico', 'raw', 'svg' => "picture",
'rar', 'zip', 'jar', '7-zip', 'tar', 'gzip', '7z', 'gz', 'apk', 'dmg' => "archive",
@@ -706,9 +725,9 @@ class File extends AbstractModel
* @param int $permission
* @return File
*/
public static function permissionFind(int $id, $user, int $limit = 0, int &$permission = -1)
public static function permissionFind($id, $user, int $limit = 0, int &$permission = -1)
{
$file = File::find($id);
$file = File::find(intval($id));
if (empty($file)) {
throw new ApiException('文件不存在或已被删除');
}

View File

@@ -6,6 +6,7 @@ namespace App\Models;
use App\Module\Base;
use App\Module\Timer;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* App\Models\FileContent
@@ -104,10 +105,10 @@ class FileContent extends AbstractModel
/**
* 获取格式内容(或下载)
* @param File $file
* @param $file
* @param $content
* @param $download
* @return array|\Symfony\Component\HttpFoundation\BinaryFileResponse
* @return array|StreamedResponse
*/
public static function formatContent($file, $content, $download = false)
{
@@ -119,7 +120,7 @@ class FileContent extends AbstractModel
} else {
$filePath = public_path($content['url']);
}
return Base::BinaryFileResponse($filePath, $name);
return Base::DownloadFileResponse($filePath, $name);
}
if (empty($content)) {
$content = match ($file->type) {
@@ -148,7 +149,7 @@ class FileContent extends AbstractModel
if ($download) {
$filePath = public_path($path);
if (isset($filePath)) {
return Base::BinaryFileResponse($filePath, $name);
return Base::DownloadFileResponse($filePath, $name);
} else {
abort(403, "This file not support download.");
}

View File

@@ -219,7 +219,11 @@ class Project extends AbstractModel
'userid' => $userid,
], [
'important' => 1
]);
], function () use ($userid) {
return [
'bot' => User::isBot($userid) ? 1 : 0,
];
});
}
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->remove();
});

View File

@@ -1180,7 +1180,11 @@ class ProjectTask extends AbstractModel
'userid' => $userid,
], [
'important' => 1
]);
], function () use ($userid) {
return [
'bot' => User::isBot($userid) ? 1 : 0,
];
});
}
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->remove();
});
@@ -1833,6 +1837,11 @@ class ProjectTask extends AbstractModel
$taskUser->save();
}
}
// 子任务
ProjectTask::whereParentId($this->id)->change([
'project_id' => $projectId,
'column_id' => $columnId,
]);
//
if ($flowItemId) {
$flowItem = projectFlowItem::whereProjectId($projectId)->whereId($flowItemId)->first();

View File

@@ -2,6 +2,8 @@
namespace App\Models;
use App\Module\Base;
/**
* App\Models\ProjectUser
*
@@ -50,7 +52,9 @@ class ProjectUser extends AbstractModel
*/
public static function transfer($originalUserid, $newUserid)
{
self::whereUserid($originalUserid)->chunkById(100, function ($list) use ($originalUserid, $newUserid) {
$projectIds = [];
// 移交项目身份
self::whereUserid($originalUserid)->chunkById(100, function ($list) use ($originalUserid, $newUserid, &$projectIds) {
/** @var self $item */
foreach ($list as $item) {
$row = self::whereProjectId($item->project_id)->whereUserid($newUserid)->first();
@@ -72,9 +76,23 @@ class ProjectUser extends AbstractModel
}
$item->project->addLog("移交项目身份", ['userid' => [$originalUserid, ' => ', $newUserid]]);
$item->project->syncDialogUser();
$projectIds[] = $item->project_id;
}
}
});
// 移交工作流状态负责人
if ($projectIds) {
ProjectFlowItem::whereIn('project_id', $projectIds)->chunkById(100, function ($list) use ($originalUserid, $newUserid) {
/** @var ProjectFlowItem $item */
foreach ($list as $item) {
if (in_array($originalUserid, $item->userids)) {
$userids = array_values(array_diff($item->userids, [$originalUserid]));
$item->userids = Base::array2json(array_merge($userids, [$newUserid]));
$item->save();
}
}
});
}
}
/**

View File

@@ -139,12 +139,12 @@ class Report extends AbstractModel
// 如果设置了周期偏移量
empty( $offset ) || $now_dt->subWeeks( abs( $offset ) );
$now_dt->startOfWeek(); // 设置为当周第一天
return $now_dt->year . $now_dt->weekOfYear;
return now()->year . $now_dt->weekOfYear;
},
Report::DAILY => function() use ($now_dt, $offset) {
// 如果设置了周期偏移量
empty( $offset ) || $now_dt->subDays( abs( $offset ) );
return $now_dt->format("Ymd");
return now()->format("Ymd");
},
default => "",
};

View File

@@ -65,14 +65,15 @@ class Setting extends AbstractModel
$value['claude_key'] = $value['claude_token'];
}
$array = [];
$aiList = ['openai', 'claude', 'gemini', 'zhipu', 'qianwen', 'wenxin'];
$fieldList = ['key', 'model', 'agency', 'system', 'secret'];
$aiList = ['openai', 'claude', 'deepseek', 'gemini', 'zhipu', 'qianwen', 'wenxin'];
$fieldList = ['key', 'model', 'base_url', 'agency', 'system', 'secret'];
foreach ($aiList as $aiName) {
foreach ($fieldList as $fieldName) {
$key = $aiName . '_' . $fieldName;
$array[$key] = $value[$key] ?: match ($key) {
'openai_model' => 'gpt-4o-mini',
'claude_model' => 'claude-3-5-sonnet-latest',
'deepseek_model' => 'deepseek-chat',
'gemini_model' => 'gemini-1.5-flash',
'zhipu_model' => 'glm-4',
'qianwen_model' => 'qwen-turbo',

View File

@@ -15,7 +15,9 @@ use Hedeqiang\UMeng\IOS;
* @property string|null $alias 别名
* @property string|null $platform 平台类型
* @property string|null $device 设备类型
* @property string|null $version 应用版本号
* @property string|null $ua userAgent
* @property int|null $is_notified 通知权限
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
@@ -30,7 +32,9 @@ use Hedeqiang\UMeng\IOS;
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereAlias($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereDevice($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereVersion($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereIsNotified($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias wherePlatform($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUa($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUpdatedAt($value)
@@ -189,7 +193,11 @@ class UmengAlias extends AbstractModel
$lists = $rows->take(5)->groupBy('platform'); // 每个会员最多推送5个别名
foreach ($lists as $platform => $list) {
$alias = $list->pluck('alias')->implode(',');
self::pushMsgToAlias($alias, $platform, $array);
try {
self::pushMsgToAlias($alias, $platform, $array);
} catch (\Exception $e) {
info("[PushMsg] fail: " . $e->getMessage());
}
}
}
});

View File

@@ -593,6 +593,8 @@ class User extends AbstractModel
return url("images/avatar/default_openai.png");
case 'ai-claude@bot.system':
return url("images/avatar/default_claude.png");
case 'ai-deepseek@bot.system':
return url("images/avatar/default_deepseek.png");
case 'ai-gemini@bot.system':
return url("images/avatar/default_gemini.png");
case 'ai-zhipu@bot.system':

View File

@@ -83,6 +83,7 @@ class UserBot extends AbstractModel
'approval-alert' => '审批',
'ai-openai' => 'ChatGPT',
'ai-claude' => 'Claude',
'ai-deepseek' => 'DeepSeek',
'ai-wenxin' => '文心一言',
'ai-qianwen' => '通义千问',
'ai-gemini' => 'Gemini',

View File

@@ -265,7 +265,9 @@ class WebSocketDialog extends AbstractModel
// 未读消息
$data = array_merge($data, self::generateUnread($data['id'], $userid));
// 对话人数
$data['people'] = $data['people'] ?? WebSocketDialogUser::whereDialogId($data['id'])->count();
if (!isset($data['people'])) {
$data = array_merge($data, self::generatePeople($data['id']));
}
// 有待办
$data['todo_num'] = $data['todo_num'] ?? WebSocketDialogMsgTodo::whereDialogId($data['id'])->whereUserid($userid)->whereDoneAt(null)->count();
// 最后消息
@@ -330,6 +332,9 @@ class WebSocketDialog extends AbstractModel
}
break;
}
if (empty($data['pinyin'])) {
$data['pinyin'] = Base::cn2pinyin($data['name']);
}
// 已存在的消息类型
if ($hasData === true) {
@@ -398,6 +403,26 @@ class WebSocketDialog extends AbstractModel
return $data;
}
/**
* 获取对话人数
* @param $dialogId
* @return array
*/
public static function generatePeople($dialogId)
{
$counts = WebSocketDialogUser::whereDialogId($dialogId)
->groupBy('bot')
->selectRaw('bot, COUNT(*) as count')
->pluck('count', 'bot');
$userCount = $counts->get(0, 0); // 非机器人数量
$botCount = $counts->get(1, 0); // 机器人数量
return [
'people' => $userCount + $botCount,
'people_user' => $userCount,
'people_bot' => $botCount,
];
}
/**
* 加入聊天室
* @param int|array $userid 加入的会员ID或会员ID组
@@ -420,7 +445,11 @@ class WebSocketDialog extends AbstractModel
WebSocketDialogUser::updateInsert([
'dialog_id' => $this->id,
'userid' => $value,
], $updateData, [], $isInsert);
], $updateData, function() use ($value) {
return [
'bot' => User::isBot($value) ? 1 : 0,
];
}, $isInsert);
if ($isInsert) {
WebSocketDialogMsg::sendMsg(null, $this->id, 'notice', [
'notice' => User::userid2nickname($value) . " 已加入群组"
@@ -429,10 +458,9 @@ class WebSocketDialog extends AbstractModel
}
}
});
$this->pushMsg("groupUpdate", [
'id' => $this->id,
'people' => WebSocketDialogUser::whereDialogId($this->id)->count()
]);
$data = WebSocketDialog::generatePeople($this->id);
$data['id'] = $this->id;
$this->pushMsg("groupUpdate", $data);
return true;
}
@@ -489,10 +517,9 @@ class WebSocketDialog extends AbstractModel
});
});
//
$this->pushMsg("groupUpdate", [
'id' => $this->id,
'people' => WebSocketDialogUser::whereDialogId($this->id)->count()
]);
$data = WebSocketDialog::generatePeople($this->id);
$data['id'] = $this->id;
$this->pushMsg("groupUpdate", $data);
}
/**

View File

@@ -592,6 +592,9 @@ class WebSocketDialogMsg extends AbstractModel
case 'text':
return self::previewTextMsg($data['msg'], $preserveHtml);
case 'longtext':
return $data['msg']['desc'] ? Base::cutStr($data['msg']['desc'], 50) : ("[" . Doo::translate("长文本") . "]");
case 'vote':
$action = Doo::translate("投票");
return "[{$action}] " . self::previewTextMsg($data['msg'], $preserveHtml);
@@ -774,14 +777,24 @@ class WebSocketDialogMsg extends AbstractModel
break;
}
}
$key = str_replace(["&quot;", "&amp;", "&lt;", "&gt;"], "", $key);
$key = str_replace(["\r", "\n", "\t", "&nbsp;"], " ", $key);
$key = preg_replace("/^\/[A-Za-z]+/", " ", $key);
$key = preg_replace("/\s+/", " ", $key);
$this->key = trim($key);
$this->key = self::filterEscape($key);
$this->save();
}
/**
* 过滤转义
* @param $content
* @return string
*/
public static function filterEscape($content)
{
$content = str_replace(["&quot;", "&amp;", "&lt;", "&gt;"], "", $content);
$content = str_replace(["\r", "\n", "\t", "&nbsp;"], " ", $content);
$content = preg_replace("/^\/[A-Za-z]+/", " ", $content);
$content = preg_replace("/\s+/", " ", $content);
return trim($content);
}
/**
* 返回引用消息(如果是文本只取预览)
* @return array|mixed

View File

@@ -14,6 +14,7 @@ use Overtrue\Pinyin\Pinyin;
use Redirect;
use Request;
use Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\File;
use Validator;
@@ -2017,7 +2018,7 @@ class Base
$type = ['mp3', 'wma', 'wav', 'amr'];
break;
case 'excel':
$type = ['xls', 'xlsx'];
$type = ['xls', 'xlsx', 'xlsm', 'xlt', 'xltx', 'ods', 'ots', 'csv', 'tsv'];
break;
case 'app':
$type = ['apk'];
@@ -2758,12 +2759,12 @@ class Base
}
/**
* BinaryFileResponse 下载文件
* DownloadFileResponse 下载文件
* @param File|\SplFileInfo|string $file 文件对象或路径
* @param string|null $name 下载文件名
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
* @return StreamedResponse
*/
public static function BinaryFileResponse($file, $name = null)
public static function DownloadFileResponse($file, $name = null)
{
try {
// 处理文件对象
@@ -2780,6 +2781,12 @@ class Base
throw new FileException('File must be readable and exist.');
}
// 获取文件信息
$size = $file->getSize();
if ($size === false || $size < 0) {
throw new FileException('Unable to determine file size.');
}
// 处理文件名
if (empty($name)) {
$name = basename($file->getPathname());
@@ -2791,34 +2798,98 @@ class Base
$name = Base::cutStr($name, 180);
$name = str_replace(['"', '<', '>', '|', '/', '\\', '?', ':'], '', $name);
// IE 浏览器特殊处理
$encodedName = $name;
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/MSIE|Internet Explorer|Trident/i", $_SERVER['HTTP_USER_AGENT'])) {
$encodedName = rawurlencode($name);
// 获取MIME类型
$mimeType = $file->getMimeType();
if (empty($mimeType)) {
$mimeType = 'application/octet-stream';
}
// 创建响应对象
return new \Symfony\Component\HttpFoundation\BinaryFileResponse($file->getPathname(), 200, [
'Content-Type' => $file->getMimeType() ?: 'application/octet-stream',
// 处理 Range 请求
$start = 0;
$end = $size - 1;
$length = $size;
$isRangeRequest = false;
if (isset($_SERVER['HTTP_RANGE'])) {
$range = str_replace('bytes=', '', $_SERVER['HTTP_RANGE']);
if (preg_match('/^(\d+)-(\d*)$/', $range, $matches)) {
$start = intval($matches[1]);
$end = !empty($matches[2]) ? intval($matches[2]) : $size - 1;
// 验证范围的有效性
if ($start >= 0 && $end < $size && $start <= $end) {
$length = $end - $start + 1;
$isRangeRequest = true;
} else {
$start = 0;
$end = $size - 1;
}
}
}
// 设置基本响应头
$headers = [
'Content-Type' => $mimeType,
'Content-Disposition' => sprintf(
'attachment; filename="%s"; filename*=UTF-8\'\'%s',
$encodedName,
$name,
rawurlencode($name)
),
// 添加缓存控制和安全相关的头
'Cache-Control' => 'private, no-transform, no-store, must-revalidate',
'Pragma' => 'public',
'Expires' => '0',
'Accept-Ranges' => 'bytes', // 支持断点续传
'X-Content-Type-Options' => 'nosniff', // 安全相关
]);
'Accept-Ranges' => 'bytes',
'Cache-Control' => 'private, no-transform, no-store, must-revalidate, max-age=0',
'Content-Length' => $length,
'Last-Modified' => gmdate('D, d M Y H:i:s', $file->getMTime()) . ' GMT',
'ETag' => sprintf('"%s"', md5_file($file->getPathname()))
];
if ($isRangeRequest) {
$headers['Content-Range'] = "bytes {$start}-{$end}/{$size}";
$statusCode = 206;
} else {
$statusCode = 200;
}
// 创建流式响应
return new StreamedResponse(
function () use ($file, $start, $length) {
$handle = fopen($file->getPathname(), 'rb');
if ($handle === false) {
throw new FileException('Cannot open file for reading');
}
if (fseek($handle, $start) === -1) {
fclose($handle);
throw new FileException('Cannot seek to position ' . $start);
}
$remaining = $length;
$bufferSize = 8192; // 8KB chunks
while ($remaining > 0 && !feof($handle)) {
$readSize = min($bufferSize, $remaining);
$buffer = fread($handle, $readSize);
if ($buffer === false) {
break;
}
echo $buffer;
flush();
$remaining -= strlen($buffer);
}
fclose($handle);
},
$statusCode,
$headers
);
} catch (\Exception $e) {
\Log::error('File download failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'file' => $file->getPathname() ?? null,
'name' => $name ?? null,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null, // 添加更多调试信息
'ip' => request()->ip()
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'ip' => request()->ip(),
'range' => $_SERVER['HTTP_RANGE'] ?? null
]);
abort(403, 'File download failed');
}

108
app/Module/MsgTool.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
namespace App\Module;
use DOMDocument;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Exception\CommonMarkException;
use League\HTMLToMarkdown\HtmlConverter;
class MsgTool
{
/**
* 截取文本并保持标签完整性
*
* @param string $text 要截取的文本
* @param int $length 截取长度
* @param string $type 文本类型 (htm 或 md)
* @return string 处理后的文本
*/
public static function truncateText($text, $length, $type = 'htm')
{
if (empty($text) || mb_strlen($text) <= $length) {
return $text;
}
$isMd = strtolower($type) === 'md';
// 如果是Markdown转换为HTML
if ($isMd) {
$converter = new CommonMarkConverter();
try {
$text = $converter->convert($text);
} catch (CommonMarkException) {
return "";
}
}
// 创建DOM文档
$dom = new DOMDocument('1.0', 'UTF-8');
libxml_use_internal_errors(true);
$dom->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
// 获取body元素
$body = $dom->getElementsByTagName('body')->item(0);
$truncatedHtml = '';
$currentLength = 0;
// 递归函数来遍历节点并截取内容
self::traverseNodes($body, $currentLength, $length, $truncatedHtml);
// 如果是Markdown转换回Markdown
if ($isMd) {
$converter = new HtmlConverter();
$truncatedHtml = $converter->convert($truncatedHtml);
}
return $truncatedHtml;
}
/**
* 递归遍历节点
* @param $node
* @param $currentLength
* @param $length
* @param $truncatedHtml
* @return void
*/
private static function traverseNodes($node, &$currentLength, $length, &$truncatedHtml)
{
foreach ($node->childNodes as $child) {
if ($currentLength >= $length) {
break;
}
if ($child->nodeType === XML_TEXT_NODE) {
$textContent = $child->textContent;
$remainingLength = $length - $currentLength;
if (mb_strlen($textContent) > $remainingLength) {
$truncatedHtml .= htmlspecialchars(mb_substr($textContent, 0, $remainingLength) . '...');
$currentLength += $remainingLength;
} else {
$truncatedHtml .= htmlspecialchars($textContent);
$currentLength += mb_strlen($textContent);
}
} elseif ($child->nodeType === XML_ELEMENT_NODE) {
$truncatedHtml .= '<' . $child->nodeName;
// 添加属性
if ($child->hasAttributes()) {
foreach ($child->attributes as $attr) {
$truncatedHtml .= ' ' . $attr->nodeName . '="' . htmlspecialchars($attr->nodeValue) . '"';
}
}
$truncatedHtml .= '>';
self::traverseNodes($child, $currentLength, $length, $truncatedHtml);
if ($currentLength < $length || $child->firstChild) {
$truncatedHtml .= '</' . $child->nodeName . '>';
}
}
}
}
}

View File

@@ -92,7 +92,7 @@ class ProjectTaskObserver
}
$array = [];
if (in_array('task', $dataType)) {
$array = array_merge($array, ProjectTaskUser::whereTaskId($projectTask->id)->pluck('userid')->toArray());
$array = array_merge($array, ProjectTaskUser::whereTaskId($projectTask->id)->orWhere('task_pid' ,$projectTask->id)->pluck('userid')->toArray());
}
if (in_array('visibility', $dataType)) {
$array = array_merge($array, ProjectTaskVisibilityUser::whereTaskId($projectTask->id)->pluck('userid')->toArray());
@@ -121,5 +121,6 @@ class ProjectTaskObserver
Deleted::forget('projectTask', $projectTask->id, $forgetUserids);
break;
}
ProjectTask::whereParentId($projectTask->id)->change(['visibility' => $projectTask->visibility]);
}
}

View File

@@ -17,6 +17,9 @@ class ProjectTaskUserObserver
public function created(ProjectTaskUser $projectTaskUser)
{
Deleted::forget('projectTask', $projectTaskUser->task_id, $projectTaskUser->userid);
if ($projectTaskUser->task_pid) {
Deleted::forget('projectTask', $projectTaskUser->task_pid, $projectTaskUser->userid);
}
}
/**

View File

@@ -103,6 +103,10 @@ class WebSocketService implements WebSocketHandlerInterface
case 'receipt':
return;
// 握手信息
case 'handshake':
break;
// 访问状态
case 'path':
$row = WebSocket::whereFd($frame->fd)->first();

View File

@@ -427,6 +427,7 @@ class BotReceiveMsgTask extends AbstractTask
'model_name' => $setting[$type . '_model'],
'system_message' => $setting[$type . '_system'],
'api_key' => $setting[$type . '_key'],
'base_url' => $setting[$type . '_base_url'],
'agency' => $setting[$type . '_agency'],
'server_url' => $serverUrl,
];
@@ -445,7 +446,7 @@ class BotReceiveMsgTask extends AbstractTask
if ($replyMsg) {
$replyCommand = $this->extractCommand($replyMsg);
if ($replyCommand) {
$replyCommand = Base::cutStr($replyCommand, 200) . "\n\n ------------------ Reference above ------------------ \n\n";
$replyCommand = "<quoted>" . Base::cutStr($replyCommand, 2000) . "</quoted>\n\nThe content within the above <quoted> tags is a citation.\n\n";
}
}
$command = $replyCommand . $command;
@@ -585,14 +586,19 @@ class BotReceiveMsgTask extends AbstractTask
case 'user':
break;
case 'project':
$projectInfo = Project::select(['id', 'name', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first();
$projectInfo = Project::whereDialogId($dialog->id)->first();
if ($projectInfo) {
$before_text[] = "当前我在项目【{$projectInfo->name}】中";
$projectText = "当前我在项目【{$projectInfo->name}】中";
if ($projectInfo->archived_at) {
$projectText .= ",此项目已经归档";
}
$before_text[] = $projectText;
if ($projectInfo->desc) {
$before_text[] = "项目描述:{$projectInfo->desc}";
}
$before_text[] = <<<EOF
如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如
如果你判断我想要添加任务,请按照以下格式回复
::: create-task-list
title: 任务标题1
desc: 任务描述1
@@ -604,9 +610,17 @@ class BotReceiveMsgTask extends AbstractTask
}
break;
case 'task':
$taskInfo = ProjectTask::with(['content'])->select(['id', 'name', 'complete_at', 'archived_at', 'deleted_at'])->whereDialogId($dialog->id)->first();
$taskInfo = ProjectTask::with(['content'])->whereDialogId($dialog->id)->first();
if ($taskInfo) {
$before_text[] = "当前我在任务【{$taskInfo->name}】中";
$taskText = "当前我在任务【{$taskInfo->name}】中";
if ($taskInfo->archived_at) {
$taskText .= ",此任务已经归档";
} elseif ($taskInfo->complete_at) {
$taskText .= ",此任务已经完成";
} elseif ($taskInfo->end_at && Carbon::parse($taskInfo->end_at)->lt(Carbon::now())) {
$taskText .= ",此任务已经过期";
}
$before_text[] = $taskText;
if ($taskInfo->content) {
$taskDesc = $taskInfo->content?->getContentInfo();
if ($taskDesc) {
@@ -620,8 +634,27 @@ class BotReceiveMsgTask extends AbstractTask
EOF;
}
}
$subTask = ProjectTask::select(['id', 'name', 'complete_at', 'end_at'])->whereParentId($taskInfo->id)->get();
if ($subTask->isNotEmpty()) {
$subTaskContent = $subTask->map(function($item) {
$status = "";
if ($item->complete_at) {
$status = " (已完成)";
} elseif ($item->end_at && Carbon::parse($item->end_at)->lt(Carbon::now())) {
$status = " (已过期)";
}
return " - {$item->name} {$status}";
})->join("\n");
if ($subTaskContent) {
$before_text[] = <<<EOF
子任务列表:
{$subTaskContent}
EOF;
}
}
$before_text[] = <<<EOF
如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如
如果你判断我想要添加子任务,请按照以下格式回复
::: create-subtask-list
title: 子任务标题1
title: 子任务标题2

View File

@@ -2,12 +2,12 @@
namespace App\Tasks;
use App\Models\ApproveProcInstHistory;
use App\Models\User;
use App\Models\UserCheckinRecord;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Module\Base;
use App\Module\Doo;
use App\Module\Extranet;
use App\Module\Timer;
use Cache;
@@ -82,6 +82,9 @@ class CheckinRemindTask extends AbstractTask
if (!UserCheckinRecord::whereUserid($user->userid)->where('created_at', '>', Carbon::now()->subDays(3))->exists()) {
continue; // 3天内没有打卡
}
if (ApproveProcInstHistory::userIsLeave($user->userid)) {
continue; // 请假、外出
}
$dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid);
if ($dialog) {
if ($type === 'exceed') {

View File

@@ -142,6 +142,7 @@ install() {
if /root/.acme.sh/acme.sh --installcert -d "${domain}" --fullchainpath "${sslPath}/${domain}.crt" --keypath "${sslPath}/${domain}.key" --ecc --force; then
success "SSL 证书配置成功"
sleep 2
cp -r /root/.acme.sh/${domain}_ecc/*.conf ${sslPath}/
fi
else
error "SSL 证书生成失败"
@@ -165,5 +166,54 @@ error_page 497 https://\$host\$request_uri;
EOF
}
check
install
UPDATE_LOG="$(dirname "$PWD")/docker/nginx/site/ssl/update.log"
SSL_PATH="$(dirname "$PWD")/docker/nginx/site/ssl"
upgrade_cert(){
curl https://get.acme.sh | sh
if [[ 0 -ne $? ]]; then
echo "安装证书更新脚本失败"
echo $(date)": 安装证书更新脚本失败" >> ${UPDATE_LOG}
exit 1
fi
file=$1
domain=$(basename "$file" .key)
old_crt_md5=$(md5sum ${SSL_PATH}/${domain}.crt| awk '{print $1}')
/root/.acme.sh/acme.sh --renew --standalone -d ${domain} --fullchainpath "${SSL_PATH}/${domain}.crt" --keypath "${SSL_PATH}/${domain}.key" --ecc --force
new_crt_md5=$(md5sum ${SSL_PATH}/${domain}.crt| awk '{print $1}')
if [ "${old_key_md5}" == "${new_key_md5}" ]; then
echo "${domain} 证书更新脚本失败"
echo $(date)": ${domain} 证书更新失败" >> ${UPDATE_LOG}
echo $(date)": ${old_crt_md5} == ${new_crt_md5}" >> ${UPDATE_LOG}
else
echo "${domain} 证书更新脚本成功"
echo $(date)": ${domain} 证书更新成功" >> ${UPDATE_LOG}
fi
}
check_expire(){
apk add --no-cache openssl socat
find ${SSL_PATH} -type f -name "*.key" | while read -r file; do
CERT_PATH=$file
expiry_date=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
expiry_timestamp=$(date -d "$expiry_date" +%s)
current_timestamp=$(date +%s)
days_remaining=$(( (expiry_timestamp - current_timestamp) / 86400 ))
echo "剩余时间${days_remaining}天" >> ${UPDATE_LOG}
if [ "$days_remaining" -lt 30 ]; then
upgrade_cert $file
fi
done
}
case "${1}" in
"install")
check
install
;;
"renew")
check_expire
;;
*)
echo "test"
;;
esac

16
cmd
View File

@@ -322,15 +322,26 @@ https_auto() {
if [[ "$restart_nginx" == "y" ]]; then
$COMPOSE up -d
fi
docker run -it --rm -v $(pwd):/work nginx:alpine sh "/work/bin/https"
docker run -it --rm -v $(pwd):/work nginx:alpine sh /work/bin/https install
if [[ 0 -eq $? ]]; then
run_exec nginx "nginx -s reload"
fi
new_job="* 6 * * * docker run -it --rm -v $(pwd):/work nginx:alpine sh /work/bin/https renew"
current_crontab=$(crontab -l 2>/dev/null)
if ! echo "$current_crontab" | grep -v "https renew"; then
echo "任务已存在,无需添加。"
else
crontab -l |{
cat
echo "$new_job"
} | crontab -
echo "任务已添加。"
fi
}
env_get() {
local key=$1
local value=`cat ${cur_path}/.env | grep "^$key=" | awk -F '=' '{print $2}'`
local value=`cat ${cur_path}/.env | grep "^$key=" | awk -F '=' '{print $2}' | tr -d '\r\n'`
echo "$value"
}
@@ -433,6 +444,7 @@ if [ $# -gt 0 ]; then
error "数据库初始化失败!"
exit 1
fi
sleep 3
chmod -R 775 "${cur_path}/docker/mysql/data"
done
# 数据库迁移

View File

@@ -10,6 +10,7 @@
"require": {
"php": "^8.0",
"ext-curl": "*",
"ext-dom": "*",
"ext-gd": "*",
"ext-imagick": "*",
"ext-json": "*",

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsNotifiedToUmengAlias extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('umeng_alias', function (Blueprint $table) {
if (!Schema::hasColumn('umeng_alias', 'is_notified')) {
$table->tinyInteger('is_notified')->nullable()->default(0)->after('ua')->comment('通知权限');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('umeng_alias', function (Blueprint $table) {
if (Schema::hasColumn('umeng_alias', 'is_notified')) {
$table->dropColumn('is_notified');
}
});
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddVersionToUmengAlias extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('umeng_alias', function (Blueprint $table) {
if (!Schema::hasColumn('umeng_alias', 'version')) {
$table->string('version', 50)->nullable()->default('')->after('device')->comment('应用版本号');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('umeng_alias', function (Blueprint $table) {
if (Schema::hasColumn('umeng_alias', 'version')) {
$table->dropColumn('version');
}
});
}
}

View File

@@ -0,0 +1,38 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class UpdateProjectTasksSubtaskProjectIdAndColumnId extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$prefix = DB::getTablePrefix();
$now = Carbon::now();
DB::statement("
UPDATE {$prefix}project_tasks AS subtask
INNER JOIN {$prefix}project_tasks AS parent ON subtask.parent_id = parent.id
SET
subtask.project_id = parent.project_id,
subtask.column_id = parent.column_id,
subtask.updated_at = '{$now}'
WHERE subtask.parent_id > 0
");
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// No need for down operation as this is a data correction
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class UpdateProjectTasksSubtaskVisibility extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$prefix = DB::getTablePrefix();
$now = Carbon::now();
DB::statement("
UPDATE {$prefix}project_tasks AS subtask
INNER JOIN {$prefix}project_tasks AS parent ON subtask.parent_id = parent.id
SET
subtask.visibility = parent.visibility,
subtask.updated_at = '{$now}'
WHERE subtask.parent_id > 0
");
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// No need for down operation as this is a data correction
}
}

View File

@@ -0,0 +1,50 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddBotToWebSocketDialogUsersTable 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', 'bot')) {
$table->tinyInteger('bot')->nullable()->default(0)->after('userid')->comment('是否机器人');
$table->index('bot');
}
});
// 获取表前缀
$prefix = DB::getTablePrefix();
// 使用原生SQL更新数据
/** @noinspection SqlNoDataSourceInspection */
DB::statement("
UPDATE {$prefix}web_socket_dialog_users du
INNER JOIN {$prefix}users u ON u.userid = du.userid
SET du.bot = 1
WHERE u.bot = 1
");
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
if (Schema::hasColumn('web_socket_dialog_users', 'bot')) {
$table->dropIndex('bot');
$table->dropColumn('bot');
}
});
}
}

View File

@@ -85,6 +85,7 @@ services:
- ./docker/office/logs:/var/log/onlyoffice
- ./docker/office/data:/var/www/onlyoffice/Data
- ./docker/office/resources/require.js:/var/www/onlyoffice/documentserver/web-apps/vendor/requirejs/require.js
- ./docker/office/resources/common/main/resources/img/header:/var/www/onlyoffice/documentserver/web-apps/apps/common/main/resources/img/header
- ./docker/office/resources/documenteditor/main/resources/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/main/resources/css/app.css
- ./docker/office/resources/documenteditor/mobile/css/526.caf35c11a8d72ca5ac85.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/mobile/css/526.caf35c11a8d72ca5ac85.css
- ./docker/office/resources/presentationeditor/main/resources/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/presentationeditor/main/resources/css/app.css
@@ -167,7 +168,7 @@ services:
ai:
container_name: "dootask-ai-${APP_ID}"
image: "kuaifan/dootask-ai:0.1.6"
image: "kuaifan/dootask-ai:0.2.8"
environment:
REDIS_HOST: "${REDIS_HOST}"
REDIS_PORT: "${REDIS_PORT}"

View File

@@ -192,6 +192,10 @@ server {
# AI
location /ai/ {
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_set_header Scheme $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

View File

@@ -0,0 +1 @@
*.gz

View File

@@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="20" fill="none">
</svg>

After

Width:  |  Height:  |  Size: 82 B

View File

@@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="20" fill="none">
</svg>

After

Width:  |  Height:  |  Size: 83 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="28" height="60"><symbol id="svg-icon-crypted" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 3C7 5 4 5 4 5v3.5c0 2 .563 7.477 6 8.5 5.436-1.023 6-6.5 6-8.5V5s-3 0-6-2m4.023 4.965-1.046-.93-3.55 3.993-2.479-2.066-.896 1.076 3.52 2.934z" clip-rule="evenodd"/></symbol><symbol id="svg-icon-users" viewBox="0 0 28 20"><path fill-rule="evenodd" d="M27 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0m1 0c0 5.523-4.477 10-10 10a9.96 9.96 0 0 1-6.798-2.666 8 8 0 1 1 0-14.667A9.96 9.96 0 0 1 18 0c5.523 0 10 4.477 10 10M10.451 3.441A7 7 0 0 0 8 3a7 7 0 0 0-5.69 11.079 11 11 0 0 1 1.124-.76C4.544 12.666 6.132 12 8 12q.1 0 .2.003A10 10 0 0 1 8.05 11H8a3 3 0 1 1 1.198-5.751 10 10 0 0 1 1.253-1.808M8.767 6.152A2 2 0 0 0 8 6a2 2 0 1 0 0 4 10 10 0 0 1 .767-3.848m-.304 6.864A7 7 0 0 0 8 13c-1.632 0-3.043.584-4.059 1.181a10 10 0 0 0-.99.667A6.98 6.98 0 0 0 8 17a7 7 0 0 0 2.451-.441 10 10 0 0 1-1.988-3.543" clip-rule="evenodd"/></symbol></svg>

After

Width:  |  Height:  |  Size: 994 B

1
electron/build.js vendored
View File

@@ -512,6 +512,7 @@ async function startBuild(data) {
fse.copySync(path.resolve(__dirname, "../public/language"), path.resolve(electronDir, "language"))
// config.js
fs.writeFileSync(electronDir + "/config.js", "window.systemInfo = " + JSON.stringify(systemInfo), 'utf8');
fs.writeFileSync(electronDir + "/dark", '', 'utf8');
fs.writeFileSync(nativeCachePath, utils.formatUrl(data.url));
fs.writeFileSync(devloadCachePath, "", 'utf8');
// index.html

27
electron/electron.js vendored
View File

@@ -434,7 +434,7 @@ function createChildWindow(args) {
if (/^https?:/i.test(hash)) {
browser.loadURL(hash).then(_ => { }).catch(_ => { })
} else if (isPreload) {
browser.webContents.executeJavaScript(`if(typeof $A.goForward === 'function'){$A.goForward('${hash}')}else{throw new Error('no function')}`, true).catch(() => {
browser.webContents.executeJavaScript(`if(typeof window.__initializeApp === 'function'){window.__initializeApp('${hash}')}else{throw new Error('no function')}`, true).catch(() => {
utils.loadUrlOrFile(browser, devloadUrl, hash)
});
} else {
@@ -556,8 +556,6 @@ function createWebTabWindow(args) {
// 创建父级窗口
if (!webTabWindow) {
let config = Object.assign(args.config || {}, userConf.get('webTabWindow', {}));
let webPreferences = args.webPreferences || {};
const titleBarOverlay = {
height: webTabHeight
}
@@ -578,14 +576,14 @@ function createWebTabWindow(args) {
titleBarStyle: 'hidden',
titleBarOverlay,
backgroundColor: nativeTheme.shouldUseDarkColors ? '#3B3B3D' : '#EFF0F4',
webPreferences: Object.assign({
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
nativeWindowOpen: true
}, webPreferences),
}, config))
},
}, userConf.get('webTabWindow', {})))
webTabWindow.on('resize', () => {
resizeWebTab(0)
@@ -650,15 +648,20 @@ function createWebTabWindow(args) {
webTabWindow.show();
// 创建 tab 子窗口
const browserView = new BrowserView({
const viewOptions = Object.assign({
useHTMLTitleAndIcon: true,
useLoadingView: true,
useErrorView: true,
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
}
})
if (nativeTheme.shouldUseDarkColors) {
}, args.config || {})
viewOptions.webPreferences = Object.assign({
preload: path.join(__dirname, 'electron-preload.js'),
nodeIntegration: true,
contextIsolation: true
}, args.webPreferences || {})
const browserView = new BrowserView(viewOptions)
if (args.backgroundColor) {
browserView.setBackgroundColor(args.backgroundColor)
} else if (nativeTheme.shouldUseDarkColors) {
browserView.setBackgroundColor('#575757')
} else {
browserView.setBackgroundColor('#FFFFFF')

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="renderer" content="webkit">
<meta name="format-detection" content="telephone=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title></title>
<!--style-->

View File

@@ -32,7 +32,7 @@
"@electron-forge/maker-squirrel": "^7.6.0",
"@electron-forge/maker-zip": "^7.6.0",
"dotenv": "^16.4.5",
"electron": "^33.2.1",
"electron": "^34.0.2",
"electron-builder": "^25.1.8",
"electron-notarize": "^1.2.2",
"form-data": "^4.0.1",

View File

@@ -37,13 +37,16 @@
const {ipcRenderer} = require('electron');
const thumbnailUrl = (url) => {
url = `${url}`
.replace(/_thumb\.(png|jpg|jpeg)$/, '')
.replace(/\/crop\/([^\/]+)$/, '')
if (!/^https?:\/\/[^\/]+\/uploads\//.test(url)) {
return url
}
const crops = {
ratio: 3,
percentage: '320x0'
}
url = `${url}`
.replace(/_thumb\.(png|jpg|jpeg)$/, '')
.replace(/\/crop\/([^\/]+)$/, '')
return url + "/crop/" + Object.keys(crops).map(key => {
return `${key}:${crops[key]}`
}).join(",")

View File

@@ -802,3 +802,6 @@ webhook地址最长仅支持255个字符。
更新子任务标签
AI机器人不存在
内容不存在
长文本

View File

@@ -1903,3 +1903,24 @@ WiFi签到延迟时长为±1分钟。
请选择示例标签
全部保存成功
消息详情
长文本
你确定要创建任务吗?
你确定要创建子任务吗?
正在处理,请稍后再试...
开启麦克风失败!
开启摄像头失败!
群机器人
群成员 ((*)人)
此消息已经过期
DeepSeek大语言模型算法是北京深度求索人工智能基础技术研究有限公司推出的深度合成服务算法。
请输入模型名称
API请求的基础URL路径如果没有请留空
访问DeepSeek网站查看
欢迎词
仪表盘欢迎词,(*)代表用户昵称

View File

@@ -4815,13 +4815,13 @@
"key": "超期未完成",
"zh": "",
"zh-CHT": "超期未完成",
"en": "Overdue unfinished",
"ko": "기한 초과 미완료",
"ja": "期限切れ未完了",
"de": "Überfällig unvollendet",
"fr": "En retard non terminé",
"id": "Terlambat belum selesai",
"ru": "Просрочено незавершенное"
"en": "Overdue",
"ko": "기한 초과",
"ja": "期限切れ",
"de": "Überfällig",
"fr": "En retard",
"id": "Terlambat",
"ru": "Просрочено"
},
{
"key": "身份已失效",
@@ -27910,5 +27910,209 @@
"fr": "Le robot IA n'existe pas",
"id": "Robot AI tidak ada",
"ru": "ИИ-робот не существует"
},
{
"key": "消息详情",
"zh": "",
"zh-CHT": "消息詳情",
"en": "Message Details",
"ko": "메시지 세부 사항",
"ja": "メッセージの詳細",
"de": "Nachrichtendetails",
"fr": "Détails du message",
"id": "Detail Pesan",
"ru": "Детали сообщения"
},
{
"key": "长文本",
"zh": "",
"zh-CHT": "長文本",
"en": "Long Text",
"ko": "긴 텍스트",
"ja": "長いテキスト",
"de": "Langer Text",
"fr": "Texte Long",
"id": "Teks Panjang",
"ru": "Длинный текст"
},
{
"key": "内容不存在",
"zh": "",
"zh-CHT": "內容不存在",
"en": "Content does not exist",
"ko": "내용이 존재하지 않음",
"ja": "内容が存在しません",
"de": "Inhalt existiert nicht",
"fr": "Le contenu n'existe pas",
"id": "Konten tidak ada",
"ru": "Содержимое не существует"
},
{
"key": "你确定要创建任务吗?",
"zh": "",
"zh-CHT": "你確定要創建任務嗎?",
"en": "Are you sure you want to create a task?",
"ko": "작업을 생성하시겠습니까?",
"ja": "タスクを作成しますか?",
"de": "Sind Sie sicher, dass Sie eine Aufgabe erstellen möchten?",
"fr": "Êtes-vous sûr de vouloir créer une tâche ?",
"id": "Anda yakin ingin membuat tugas?",
"ru": "Вы уверены, что хотите создать задание?"
},
{
"key": "你确定要创建子任务吗?",
"zh": "",
"zh-CHT": "你確定要創建子任務嗎?",
"en": "Are you sure you want to create a subtask?",
"ko": "하위 작업을 생성하시겠습니까?",
"ja": "サブタスクを作成しますか?",
"de": "Sind Sie sicher, dass Sie eine Teilaufgabe erstellen möchten?",
"fr": "Êtes-vous sûr de vouloir créer une sous-tâche ?",
"id": "Anda yakin ingin membuat sub-tugas?",
"ru": "Вы уверены, что хотите создать подзадачу?"
},
{
"key": "正在处理,请稍后再试...",
"zh": "",
"zh-CHT": "正在處理,請稍後再試...",
"en": "Processing, please try again later...",
"ko": "처리 중이니 잠시 후 다시 시도하세요...",
"ja": "処理中です。しばらくしてからもう一度お試しください...",
"de": "Wird bearbeitet, bitte versuchen Sie es später noch einmal...",
"fr": "Traitement en cours, veuillez réessayer plus tard...",
"id": "Sedang diproses, silakan coba lagi nanti...",
"ru": "Обработка, пожалуйста, повторите попытку позже..."
},
{
"key": "开启麦克风失败!",
"zh": "",
"zh-CHT": "開啟麥克風失敗!",
"en": "Failed to turn on the microphone!",
"ko": "마이크 켜기에 실패했습니다!",
"ja": "マイクの起動に失敗しました!",
"de": "Das Einschalten des Mikrofons ist fehlgeschlagen!",
"fr": "Échec de l'activation du microphone !",
"id": "Gagal menghidupkan mikrofon!",
"ru": "Не удалось включить микрофон!"
},
{
"key": "开启摄像头失败!",
"zh": "",
"zh-CHT": "開啟攝像頭失敗!",
"en": "Failed to turn on the camera!",
"ko": "카메라 켜기에 실패했습니다!",
"ja": "カメラの起動に失敗しました!",
"de": "Das Einschalten der Kamera ist fehlgeschlagen!",
"fr": "Échec de l'activation de la caméra !",
"id": "Gagal menghidupkan kamera!",
"ru": "Не удалось включить камеру!"
},
{
"key": "群机器人",
"zh": "",
"zh-CHT": "群機器人",
"en": "Group Bot",
"ko": "그룹 봇",
"ja": "グループボット",
"de": "Gruppen-Bot",
"fr": "Bot de groupe",
"id": "Bot Grup",
"ru": "Групповой бот"
},
{
"key": "群成员 ((%T1)人)",
"zh": "",
"zh-CHT": "群成員 ((%T1)人)",
"en": "Group members ((%T1) people)",
"ko": "그룹 구성원 ((%T1)명)",
"ja": "グループメンバー ((%T1)人)",
"de": "Gruppenmitglieder ((%T1) Personen)",
"fr": "Membres du groupe ((%T1) personnes)",
"id": "Anggota grup ((%T1) orang)",
"ru": "Участники группы ((%T1) человек)"
},
{
"key": "此消息已经过期",
"zh": "",
"zh-CHT": "此消息已經過期",
"en": "This message has expired",
"ko": "이 메시지는 만료되었습니다",
"ja": "このメッセージは有効期限が切れました",
"de": "Diese Nachricht ist abgelaufen",
"fr": "Ce message a expiré",
"id": "Pesan ini telah kedaluwarsa",
"ru": "Это сообщение истекло"
},
{
"key": "DeepSeek大语言模型算法是北京深度求索人工智能基础技术研究有限公司推出的深度合成服务算法。",
"zh": "",
"zh-CHT": "DeepSeek大語言模型算法是北京深度求索人工智能基礎技術研究有限公司推出的深度合成服務算法。",
"en": "The DeepSeek large language model algorithm is a deep synthesis service algorithm launched by Beijing Deep Exploration Artificial Intelligence Foundation Technology Research Co., Ltd.",
"ko": "DeepSeek 대형 언어 모델 알고리즘은 베이징 딥 익스플로레이션 인공지능 기반 기술 연구 유한회사에서 출시한 심층 합성 서비스 알고리즘입니다.",
"ja": "DeepSeek大言語モデルアルゴリズムは、北京深度探索人工知能基盤技術研究有限公司によって発表された深層合成サービスアルゴリズムです。",
"de": "Der DeepSeek-Großsprachenmodellalgorithmus ist ein von der Beijing Deep Exploration Artificial Intelligence Foundation Technology Research Co., Ltd. entwickelter Tiefensynthese-Dienstalgorithmus.",
"fr": "L'algorithme de modèle de langage grande échelle DeepSeek est un algorithme de service de synthèse profonde lancé par Beijing Deep Exploration Artificial Intelligence Foundation Technology Research Co., Ltd.",
"id": "Algoritma model bahasa besar DeepSeek adalah algoritma layanan sintesis dalam yang diluncurkan oleh Beijing Deep Exploration Artificial Intelligence Foundation Technology Research Co., Ltd.",
"ru": "Алгоритм крупной языковой модели DeepSeek является алгоритмом службы глубокого синтеза, разработанным Beijing Deep Exploration Artificial Intelligence Foundation Technology Research Co., Ltd."
},
{
"key": "请输入模型名称",
"zh": "",
"zh-CHT": "請輸入模型名稱",
"en": "Please enter the model name",
"ko": "모델 이름을 입력하세요",
"ja": "モデル名を入力してください",
"de": "Bitte geben Sie den Modellnamen ein",
"fr": "Veuillez entrer le nom du modèle",
"id": "Silakan masukkan nama model",
"ru": "Пожалуйста, введите название модели"
},
{
"key": "API请求的基础URL路径如果没有请留空",
"zh": "",
"zh-CHT": "API請求的基礎URL路徑如果沒有請留空",
"en": "Base URL path for API request, leave blank if none",
"ko": "API 요청의 기본 URL 경로, 없으면 비워 두세요",
"ja": "APIリクエストの基本URLパス、ない場合は空白のままにしてください",
"de": "Basis-URL-Pfad für API-Anfragen, leer lassen, wenn nicht vorhanden",
"fr": "Chemin d'URL de base pour la demande d'API, laisser vide si aucun",
"id": "Jalur URL dasar untuk permintaan API, biarkan kosong jika tidak ada",
"ru": "Базовый путь URL для запроса API, оставить пустым, если отсутствует"
},
{
"key": "访问DeepSeek网站查看",
"zh": "",
"zh-CHT": "訪問DeepSeek網站查看",
"en": "Visit the DeepSeek website to view",
"ko": "DeepSeek 웹사이트 방문하여 보기",
"ja": "DeepSeekのウェブサイトを訪問して確認してください",
"de": "Besuchen Sie die DeepSeek-Website, um zu sehen",
"fr": "Visitez le site Web de DeepSeek pour voir",
"id": "Kunjungi situs web DeepSeek untuk melihat",
"ru": "Посетите веб-сайт DeepSeek, чтобы просмотреть"
},
{
"key": "欢迎词",
"zh": "",
"zh-CHT": "歡迎詞",
"en": "Welcome Message",
"ko": "환영 메시지",
"ja": "歓迎メッセージ",
"de": "Begrüßungsnachricht",
"fr": "Message de bienvenue",
"id": "Pesan Selamat Datang",
"ru": "Приветственное сообщение"
},
{
"key": "仪表盘欢迎词,(%T1)代表用户昵称",
"zh": "",
"zh-CHT": "儀表盤歡迎詞,(%T1)代表用戶暱稱",
"en": "Dashboard welcome message, where (%T1) represents the user nickname",
"ko": "대시보드 환영 메시지, (%T1)은 사용자 닉네임을 나타냅니다",
"ja": "ダッシュボードの歓迎メッセージ、(%T1) はユーザーのニックネームを表します",
"de": "Willkommensnachricht im Dashboard, wobei (%T1) den Benutzernamen darstellt",
"fr": "Message de bienvenue sur le tableau de bord, où (%T1) représente le surnom de l'utilisateur",
"id": "Pesan selamat datang di dasbor, di mana (%T1) mewakili julukan pengguna",
"ru": "Приветственное сообщение на панели управления, где (%T1) представляет собой никнейм пользователя"
}
]

View File

@@ -1,7 +1,7 @@
{
"name": "DooTask",
"version": "0.41.55",
"codeVerson": 158,
"version": "0.42.57",
"codeVerson": 166,
"description": "DooTask is task management system.",
"scripts": {
"start": "./cmd dev",
@@ -39,13 +39,13 @@
"internal-ip": "^6.2.0",
"jquery": "^3.6.4",
"jspdf": "^2.5.1",
"le5le-store": "^1.0.7",
"less": "^4.1.2",
"less-loader": "^10.2.0",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"markdown-it": "^14.1.0",
"markdown-it-link-attributes": "^4.0.1",
"mitt": "^3.0.1",
"notification-koro1": "^1.1.1",
"openpgp_hi": "^5.7.0-1",
"photoswipe": "^5.2.8",
@@ -60,7 +60,7 @@
"stylus-loader": "^7.1.0",
"tinymce": "^5.10.3",
"tui-calendar-hi": "^1.15.1-5",
"view-design-hi": "^4.7.0-59",
"view-design-hi": "^4.7.0-60",
"vite": "^2.9.15",
"vite-plugin-file-copy": "^1.0.0",
"vite-plugin-require": "^1.1.10",

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 489 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1 +0,0 @@
import{n as e}from"./app.01c506d5.js";import"./@micro-zoe.c2e1472d.js";import"./jquery.7bd86b3f.js";import"./@babel.f9bcab46.js";import"./dayjs.54df6ed8.js";import"./localforage.2b5bddcf.js";import"./markdown-it.bda97caf.js";import"./mdurl.ce6c1dd8.js";import"./uc.micro.8d343c98.js";import"./entities.48a44fec.js";import"./linkify-it.c5e8196e.js";import"./punycode.js.4b3f125a.js";import"./highlight.js.ab8aeea4.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.897ae552.js";import"./vue.fd9b772e.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.6ec123f8.js";import"./le5le-store.b40f9152.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.50be9c5e.js";import"./clipboard.058ef547.js";import"./view-design-hi.dbfb3540.js";import"./vuedraggable.9fd6afed.js";import"./sortablejs.d74243d9.js";import"./vue-resize-observer.c3c9ca4e.js";import"./element-sea.7f208f9b.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.0bdc1850.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.49abba38.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var m=function(){var t=this,r=t.$createElement;return t._self._c,t._m(0)},p=[function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"page-404"},[i("div",{staticClass:"flex-center position-ref full-height"},[i("div",{staticClass:"code"},[t._v("404")]),i("div",{staticClass:"message"},[t._v("Not Found")])])])}];const s={},o={};var _=e(s,m,p,!1,n,"7d7154a8",null,null);function n(t){for(let r in o)this[r]=o[r]}var Q=function(){return _.exports}();export{Q as default};

1
public/js/build/404.dad5ee12.js vendored Normal file
View File

@@ -0,0 +1 @@
import{n as e}from"./app.84e898f6.js";import"./@micro-zoe.c2e1472d.js";import"./jquery.0e77e8ff.js";import"./@babel.f9bcab46.js";import"./dayjs.65914b7a.js";import"./localforage.f2941043.js";import"./markdown-it.bda97caf.js";import"./mdurl.ce6c1dd8.js";import"./uc.micro.8d343c98.js";import"./entities.48a44fec.js";import"./linkify-it.c5e8196e.js";import"./punycode.js.4b3f125a.js";import"./highlight.js.ab8aeea4.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.897ae552.js";import"./vue.fd9b772e.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.6ec123f8.js";import"./mitt.1ea0a2a3.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.50be9c5e.js";import"./clipboard.058ef547.js";import"./view-design-hi.0cb8d723.js";import"./vuedraggable.9fd6afed.js";import"./sortablejs.d74243d9.js";import"./vue-resize-observer.c3c9ca4e.js";import"./element-sea.7f208f9b.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.0bdc1850.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.49abba38.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var m=function(){var t=this,r=t.$createElement;return t._self._c,t._m(0)},p=[function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"page-404"},[i("div",{staticClass:"flex-center position-ref full-height"},[i("div",{staticClass:"code"},[t._v("404")]),i("div",{staticClass:"message"},[t._v("Not Found")])])])}];const s={},o={};var _=e(s,m,p,!1,n,"7d7154a8",null,null);function n(t){for(let r in o)this[r]=o[r]}var Q=function(){return _.exports}();export{Q as default};

File diff suppressed because one or more lines are too long

1
public/js/build/AceEditor.a767f934.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/Drawio.107bddf8.js vendored Normal file
View File

@@ -0,0 +1 @@
import{m as s}from"./vuex.cc7cb26e.js";import{I as m}from"./IFrame.0e692d55.js";import{n as l,l as r}from"./app.84e898f6.js";import"./@micro-zoe.c2e1472d.js";import"./jquery.0e77e8ff.js";import"./@babel.f9bcab46.js";import"./dayjs.65914b7a.js";import"./localforage.f2941043.js";import"./markdown-it.bda97caf.js";import"./mdurl.ce6c1dd8.js";import"./uc.micro.8d343c98.js";import"./entities.48a44fec.js";import"./linkify-it.c5e8196e.js";import"./punycode.js.4b3f125a.js";import"./highlight.js.ab8aeea4.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.897ae552.js";import"./vue.fd9b772e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.6ec123f8.js";import"./mitt.1ea0a2a3.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.50be9c5e.js";import"./clipboard.058ef547.js";import"./view-design-hi.0cb8d723.js";import"./vuedraggable.9fd6afed.js";import"./sortablejs.d74243d9.js";import"./vue-resize-observer.c3c9ca4e.js";import"./element-sea.7f208f9b.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.0bdc1850.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.49abba38.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"drawio-content"},[i("IFrame",{ref:"frame",staticClass:"drawio-iframe",attrs:{src:t.url},on:{"on-message":t.onMessage}}),t.loadIng?i("div",{staticClass:"drawio-loading"},[i("Loading")],1):t._e()],1)},d=[];const u={name:"Drawio",components:{IFrame:m},props:{value:{type:Object,default:function(){return{}}},title:{type:String,default:""},readOnly:{type:Boolean,default:!1}},data(){return{loadIng:!0,url:null,bakData:""}},created(){let t=r;switch(r){case"zh-CHT":t="zh-tw";break}let e=this.readOnly?1:0,i=this.readOnly?0:1,n=this.themeName==="dark"?"dark":"kennedy",a=`?title=${this.title?encodeURIComponent(this.title):""}&chrome=${i}&lightbox=${e}&ui=${n}&lang=${t}&offline=1&pwa=0&embed=1&noLangIcon=1&noExitBtn=1&noSaveBtn=1&saveAndExit=0&spin=1&proto=json`;this.$Electron?this.url=$A.originUrl(`drawio/webapp/index.html${a}`):this.url=$A.mainUrl(`drawio/webapp/${a}`)},mounted(){window.addEventListener("message",this.handleMessage)},beforeDestroy(){window.removeEventListener("message",this.handleMessage)},watch:{value:{handler(t){this.bakData!=$A.jsonStringify(t)&&(this.bakData=$A.jsonStringify(t),this.updateContent())},deep:!0}},computed:{...s(["themeName"])},methods:{formatZoom(t){return t+"%"},updateContent(){this.$refs.frame.postMessage(JSON.stringify({action:"load",autosave:1,xml:this.value.xml}))},onMessage(t){switch(t.event){case"init":this.loadIng=!1,this.updateContent();break;case"load":typeof this.value.xml=="undefined"&&this.$refs.frame.postMessage(JSON.stringify({action:"template"}));break;case"autosave":const e={xml:t.xml};this.bakData=$A.jsonStringify(e),this.$emit("input",e);break;case"save":this.$emit("saveData");break}}}},o={};var c=l(u,p,d,!1,h,"39021859",null,null);function h(t){for(let e in o)this[e]=o[e]}var Y=function(){return c.exports}();export{Y as default};

View File

@@ -1 +0,0 @@
import{m as s}from"./vuex.cc7cb26e.js";import{I as m}from"./IFrame.19bca292.js";import{n as l,l as r}from"./app.01c506d5.js";import"./@micro-zoe.c2e1472d.js";import"./jquery.7bd86b3f.js";import"./@babel.f9bcab46.js";import"./dayjs.54df6ed8.js";import"./localforage.2b5bddcf.js";import"./markdown-it.bda97caf.js";import"./mdurl.ce6c1dd8.js";import"./uc.micro.8d343c98.js";import"./entities.48a44fec.js";import"./linkify-it.c5e8196e.js";import"./punycode.js.4b3f125a.js";import"./highlight.js.ab8aeea4.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.897ae552.js";import"./vue.fd9b772e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.6ec123f8.js";import"./le5le-store.b40f9152.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.50be9c5e.js";import"./clipboard.058ef547.js";import"./view-design-hi.dbfb3540.js";import"./vuedraggable.9fd6afed.js";import"./sortablejs.d74243d9.js";import"./vue-resize-observer.c3c9ca4e.js";import"./element-sea.7f208f9b.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.0bdc1850.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.49abba38.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"drawio-content"},[i("IFrame",{ref:"frame",staticClass:"drawio-iframe",attrs:{src:t.url},on:{"on-message":t.onMessage}}),t.loadIng?i("div",{staticClass:"drawio-loading"},[i("Loading")],1):t._e()],1)},d=[];const u={name:"Drawio",components:{IFrame:m},props:{value:{type:Object,default:function(){return{}}},title:{type:String,default:""},readOnly:{type:Boolean,default:!1}},data(){return{loadIng:!0,url:null,bakData:""}},created(){let t=r;switch(r){case"zh-CHT":t="zh-tw";break}let e=this.readOnly?1:0,i=this.readOnly?0:1,n=this.themeName==="dark"?"dark":"kennedy",a=`?title=${this.title?encodeURIComponent(this.title):""}&chrome=${i}&lightbox=${e}&ui=${n}&lang=${t}&offline=1&pwa=0&embed=1&noLangIcon=1&noExitBtn=1&noSaveBtn=1&saveAndExit=0&spin=1&proto=json`;this.$Electron?this.url=$A.originUrl(`drawio/webapp/index.html${a}`):this.url=$A.mainUrl(`drawio/webapp/${a}`)},mounted(){window.addEventListener("message",this.handleMessage)},beforeDestroy(){window.removeEventListener("message",this.handleMessage)},watch:{value:{handler(t){this.bakData!=$A.jsonStringify(t)&&(this.bakData=$A.jsonStringify(t),this.updateContent())},deep:!0}},computed:{...s(["themeName"])},methods:{formatZoom(t){return t+"%"},updateContent(){this.$refs.frame.postMessage(JSON.stringify({action:"load",autosave:1,xml:this.value.xml}))},onMessage(t){switch(t.event){case"init":this.loadIng=!1,this.updateContent();break;case"load":typeof this.value.xml=="undefined"&&this.$refs.frame.postMessage(JSON.stringify({action:"template"}));break;case"autosave":const e={xml:t.xml};this.bakData=$A.jsonStringify(e),this.$emit("input",e);break;case"save":this.$emit("saveData");break}}}},o={};var c=l(u,p,d,!1,h,"39021859",null,null);function h(t){for(let e in o)this[e]=o[e]}var Y=function(){return c.exports}();export{Y as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{n}from"./app.01c506d5.js";var i=function(){var e=this,s=e.$createElement,r=e._self._c||s;return r("iframe",{directives:[{name:"show",rawName:"v-show",value:e.src,expression:"src"}],ref:"iframe",attrs:{src:e.src}})},a=[];const o={name:"IFrame",props:{src:{type:String,default:""}},mounted(){this.$refs.iframe.addEventListener("load",this.handleLoad),window.addEventListener("message",this.handleMessage)},beforeDestroy(){this.$refs.iframe.removeEventListener("load",this.handleLoad),window.removeEventListener("message",this.handleMessage)},methods:{handleLoad(){this.$emit("on-load")},handleMessage({data:e,source:s}){var r;s===((r=this.$refs.iframe)==null?void 0:r.contentWindow)&&(e=$A.jsonParse(e),e.source==="fileView"&&e.action==="picture"&&this.$store.dispatch("previewImage",{index:e.params.index,list:e.params.array}),this.$emit("on-message",e))},postMessage(e,s="*"){this.$refs.iframe&&this.$refs.iframe.contentWindow.postMessage(e,s)}}},t={};var m=n(o,i,a,!1,c,null,null,null);function c(e){for(let s in t)this[s]=t[s]}var l=function(){return m.exports}();export{l as I};
import{n}from"./app.84e898f6.js";var i=function(){var e=this,s=e.$createElement,r=e._self._c||s;return r("iframe",{directives:[{name:"show",rawName:"v-show",value:e.src,expression:"src"}],ref:"iframe",attrs:{src:e.src}})},a=[];const o={name:"IFrame",props:{src:{type:String,default:""}},mounted(){this.$refs.iframe.addEventListener("load",this.handleLoad),window.addEventListener("message",this.handleMessage)},beforeDestroy(){this.$refs.iframe.removeEventListener("load",this.handleLoad),window.removeEventListener("message",this.handleMessage)},methods:{handleLoad(){this.$emit("on-load")},handleMessage({data:e,source:s}){var r;s===((r=this.$refs.iframe)==null?void 0:r.contentWindow)&&(e=$A.jsonParse(e),e.source==="fileView"&&e.action==="picture"&&this.$store.dispatch("previewImage",{index:e.params.index,list:e.params.array}),this.$emit("on-message",e))},postMessage(e,s="*"){this.$refs.iframe&&this.$refs.iframe.contentWindow.postMessage(e,s)}}},t={};var m=n(o,i,a,!1,c,null,null,null);function c(e){for(let s in t)this[s]=t[s]}var l=function(){return m.exports}();export{l as I};

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{V as p}from"./vue.fd9b772e.js";import{s,U as o,a as r,l as n,n as h}from"./app.01c506d5.js";import{m as l}from"./vuex.cc7cb26e.js";import{u as d,E as u}from"./@micro-zoe.c2e1472d.js";import{D as m}from"./DialogWrapper.3c314f38.js";import{i as c}from"./view-design-hi.dbfb3540.js";var f=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"page-microapp"},[a.showSpin?e("transition",{attrs:{name:"microapp-load"}},[e("div",{staticClass:"microapp-load"},[e("Loading")],1)]):a._e(),a.url&&!a.loading?e("micro-app",{attrs:{name:a.name,url:a.url,inline:"","keep-alive":"",disableSandbox:"",data:a.appData},on:{created:a.handleCreate,beforemount:a.handleBeforeMount,mounted:a.handleMount,unmount:a.handleUnmount,error:a.handleError,datachange:a.handleDataChange}}):a._e()],1)},g=[];const w={name:"MicroApps",props:{name:{type:String,default:"micro-app"},url:{type:String,default:""},path:{type:String,default:""},datas:{type:Object,default:()=>{}}},data(){return{showSpin:!1,loading:!1,appData:{}}},mounted(){this.showSpin=!0,this.appData=this.getAppData},watch:{loading(a){a&&(this.showSpin=!0)},path(a){this.appData={path:a}},datas:{handler(a){this.appData=a},deep:!0},$route:{handler(a){(a.name=="manage-apps"||a.name=="single-apps")&&(this.appData={path:a.hash||a.fullPath})},immediate:!0},userToken(a){this.appData=this.getAppData,a?this.loading=!1:(d({destroy:!0}),this.loading=!0)}},computed:{...l(["userInfo","themeName"]),getAppData(){return{type:"init",url:this.url,vues:{Vue:p,store:s,components:{DialogWrapper:m,UserSelect:o,DatePicker:c.exports.DatePicker}},theme:this.themeName,languages:{languageList:r,languageName:n,languageType:n},userInfo:this.userInfo,path:this.path,electron:this.$Electron,openAppChildPage:a=>{this.$store.dispatch("openAppChildPage",a)},openChildWindow:a=>{this.$store.dispatch("openChildWindow",a)},openWebTabWindow:a=>{this.$store.dispatch("openWebTabWindow",a)}}}},methods:{handleCreate(a){window.eventCenterForAppNameVite=new u(a.detail.name),this.appData=this.getAppData,this.showSpin=!window["eventCenterForAppNameViteLoad-"+a.detail.name]},handleBeforeMount(a){window["eventCenterForAppNameViteLoad-"+a.detail.name]=1},handleMount(a){this.datas&&(this.appData=this.datas),this.path&&(this.appData.path=this.path),this.showSpin=!1},handleUnmount(a){window.dispatchEvent(new Event("apps-unmount"))},handleError(a){},handleDataChange(a){}}},i={};var v=h(w,f,g,!1,D,null,null,null);function D(a){for(let t in i)this[t]=i[t]}var y=function(){return v.exports}();export{y as M};
import{V as p}from"./vue.fd9b772e.js";import{s,U as o,a as r,l as n,n as h}from"./app.84e898f6.js";import{m as l}from"./vuex.cc7cb26e.js";import{u as d,E as u}from"./@micro-zoe.c2e1472d.js";import{D as m}from"./DialogWrapper.cc68d864.js";import{i as c}from"./view-design-hi.0cb8d723.js";var f=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"page-microapp"},[a.showSpin?e("transition",{attrs:{name:"microapp-load"}},[e("div",{staticClass:"microapp-load"},[e("Loading")],1)]):a._e(),a.url&&!a.loading?e("micro-app",{attrs:{name:a.name,url:a.url,inline:"","keep-alive":"",disableSandbox:"",data:a.appData},on:{created:a.handleCreate,beforemount:a.handleBeforeMount,mounted:a.handleMount,unmount:a.handleUnmount,error:a.handleError,datachange:a.handleDataChange}}):a._e()],1)},g=[];const w={name:"MicroApps",props:{name:{type:String,default:"micro-app"},url:{type:String,default:""},path:{type:String,default:""},datas:{type:Object,default:()=>{}}},data(){return{showSpin:!1,loading:!1,appData:{}}},mounted(){this.showSpin=!0,this.appData=this.getAppData},watch:{loading(a){a&&(this.showSpin=!0)},path(a){this.appData={path:a}},datas:{handler(a){this.appData=a},deep:!0},$route:{handler(a){(a.name=="manage-apps"||a.name=="single-apps")&&(this.appData={path:a.hash||a.fullPath})},immediate:!0},userToken(a){this.appData=this.getAppData,a?this.loading=!1:(d({destroy:!0}),this.loading=!0)}},computed:{...l(["userInfo","themeName"]),getAppData(){return{type:"init",url:this.url,vues:{Vue:p,store:s,components:{DialogWrapper:m,UserSelect:o,DatePicker:c.exports.DatePicker}},theme:this.themeName,languages:{languageList:r,languageName:n,languageType:n},userInfo:this.userInfo,path:this.path,electron:this.$Electron,openAppChildPage:a=>{this.$store.dispatch("openAppChildPage",a)},openChildWindow:a=>{this.$store.dispatch("openChildWindow",a)},openWebTabWindow:a=>{this.$store.dispatch("openWebTabWindow",a)}}}},methods:{handleCreate(a){window.eventCenterForAppNameVite=new u(a.detail.name),this.appData=this.getAppData,this.showSpin=!window["eventCenterForAppNameViteLoad-"+a.detail.name]},handleBeforeMount(a){window["eventCenterForAppNameViteLoad-"+a.detail.name]=1},handleMount(a){this.datas&&(this.appData=this.datas),this.path&&(this.appData.path=this.path),this.showSpin=!1},handleUnmount(a){window.dispatchEvent(new Event("apps-unmount"))},handleError(a){},handleDataChange(a){}}},i={};var v=h(w,f,g,!1,D,null,null,null);function D(a){for(let t in i)this[t]=i[t]}var y=function(){return v.exports}();export{y as M};

File diff suppressed because one or more lines are too long

1
public/js/build/Minder.f6908ac0.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
.component-only-office[data-v-394e9394]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.component-only-office .placeholder[data-v-394e9394]{flex:1;width:100%;height:100%}.component-only-office .office-loading[data-v-394e9394]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;z-index:2}.component-only-office .load-error{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;padding:8px;display:flex;align-items:center}.component-only-office .load-error .ivu-alert-icon{position:static;margin-right:8px;margin-left:4px}
.component-only-office[data-v-7946f4cf]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.component-only-office .placeholder[data-v-7946f4cf]{flex:1;width:100%;height:100%}.component-only-office .office-loading[data-v-7946f4cf]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;z-index:2}.component-only-office .load-error{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;padding:8px;display:flex;align-items:center}.component-only-office .load-error .ivu-alert-icon{position:static;margin-right:8px;margin-left:4px}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{m as i}from"./vuex.cc7cb26e.js";import{n as l}from"./app.01c506d5.js";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"report-detail"},[a("div",{staticClass:"report-title"},[t._v(" "+t._s(t.data.title)+" "),t.loadIng>0?a("Icon",{staticClass:"icon-loading",attrs:{type:"ios-loading"}}):t._e()],1),t.formOptions.labelWidth==="auto"?a("div",{staticClass:"report-detail-context"},[a("Form",{staticClass:"report-form auto-form",attrs:{"label-width":"auto",inline:""}},[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u4EBA")}},[a("UserAvatar",{attrs:{userid:t.data.userid,size:28}})],1),a("FormItem",{attrs:{label:t.$L("\u63D0\u4EA4\u65F6\u95F4")}},[t._v(" "+t._s(t.data.created_at)+" ")]),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5BF9\u8C61")}},[t.data.receives_user.length===0?[t._v("-")]:t._l(t.data.receives_user,function(r,s){return a("UserAvatar",{key:s,attrs:{userid:r.userid,size:28}})})],2)],1),a("Form",{staticClass:"report-form auto-form",attrs:{"label-width":"auto"}},[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5185\u5BB9")}},[a("div",{staticClass:"report-content",domProps:{innerHTML:t._s(t.data.content)}})])],1)],1):a("div",{staticClass:"report-detail-context"},[a("Form",t._b({staticClass:"report-form"},"Form",t.formOptions,!1),[t.formOptions.labelWidth!=="auto"?[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u4EBA")}},[a("UserAvatar",{attrs:{userid:t.data.userid,size:28}})],1),a("FormItem",{attrs:{label:t.$L("\u63D0\u4EA4\u65F6\u95F4")}},[t._v(" "+t._s(t.data.created_at)+" ")]),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5BF9\u8C61")}},[t.data.receives_user.length===0?[t._v("-")]:t._l(t.data.receives_user,function(r,s){return a("UserAvatar",{key:s,attrs:{userid:r.userid,size:28}})})],2)]:t._e(),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5185\u5BB9")}},[a("div",{staticClass:"report-content",domProps:{innerHTML:t._s(t.data.content)}})])],2)],1)])},d=[];const m={name:"ReportDetail",props:{data:{default:{}}},data(){return{loadIng:0}},computed:{...i(["formOptions"])},watch:{"data.id":{handler(t){t>0&&this.sendRead()},immediate:!0}},methods:{sendRead(){this.loadIng++,this.$store.dispatch("call",{url:"report/read",data:{ids:[this.data.id]}}).then(()=>{}).catch(()=>{}).finally(t=>{this.loadIng--})}}},o={};var c=l(m,n,d,!1,_,null,null,null);function _(t){for(let e in o)this[e]=o[e]}var v=function(){return c.exports}();export{v as R};
import{m as i}from"./vuex.cc7cb26e.js";import{n as l}from"./app.84e898f6.js";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"report-detail"},[a("div",{staticClass:"report-title"},[t._v(" "+t._s(t.data.title)+" "),t.loadIng>0?a("Icon",{staticClass:"icon-loading",attrs:{type:"ios-loading"}}):t._e()],1),t.formOptions.labelWidth==="auto"?a("div",{staticClass:"report-detail-context"},[a("Form",{staticClass:"report-form auto-form",attrs:{"label-width":"auto",inline:""}},[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u4EBA")}},[a("UserAvatar",{attrs:{userid:t.data.userid,size:28}})],1),a("FormItem",{attrs:{label:t.$L("\u63D0\u4EA4\u65F6\u95F4")}},[t._v(" "+t._s(t.data.created_at)+" ")]),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5BF9\u8C61")}},[t.data.receives_user.length===0?[t._v("-")]:t._l(t.data.receives_user,function(r,s){return a("UserAvatar",{key:s,attrs:{userid:r.userid,size:28}})})],2)],1),a("Form",{staticClass:"report-form auto-form",attrs:{"label-width":"auto"}},[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5185\u5BB9")}},[a("div",{staticClass:"report-content",domProps:{innerHTML:t._s(t.data.content)}})])],1)],1):a("div",{staticClass:"report-detail-context"},[a("Form",t._b({staticClass:"report-form"},"Form",t.formOptions,!1),[t.formOptions.labelWidth!=="auto"?[a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u4EBA")}},[a("UserAvatar",{attrs:{userid:t.data.userid,size:28}})],1),a("FormItem",{attrs:{label:t.$L("\u63D0\u4EA4\u65F6\u95F4")}},[t._v(" "+t._s(t.data.created_at)+" ")]),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5BF9\u8C61")}},[t.data.receives_user.length===0?[t._v("-")]:t._l(t.data.receives_user,function(r,s){return a("UserAvatar",{key:s,attrs:{userid:r.userid,size:28}})})],2)]:t._e(),a("FormItem",{attrs:{label:t.$L("\u6C47\u62A5\u5185\u5BB9")}},[a("div",{staticClass:"report-content",domProps:{innerHTML:t._s(t.data.content)}})])],2)],1)])},d=[];const m={name:"ReportDetail",props:{data:{default:{}}},data(){return{loadIng:0}},computed:{...i(["formOptions"])},watch:{"data.id":{handler(t){t>0&&this.sendRead()},immediate:!0}},methods:{sendRead(){this.loadIng++,this.$store.dispatch("call",{url:"report/read",data:{ids:[this.data.id]}}).then(()=>{}).catch(()=>{}).finally(t=>{this.loadIng--})}}},o={};var c=l(m,n,d,!1,_,null,null,null);function _(t){for(let e in o)this[e]=o[e]}var v=function(){return c.exports}();export{v as R};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/TEditor.8f8d4e8b.js vendored Normal file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More