Compare commits

...

24 Commits
dev ... v1.7.81

Author SHA1 Message Date
kuaifan
36da18af79 release: v1.7.81 2026-06-03 02:20:11 +00:00
kuaifan
363badbc97 chore(appstore): 升级 appstore 镜像至 0.4.3
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 01:54:46 +00:00
kuaifan
9be6265220 fix(download): 大文件下载改用 BinaryFileResponse 走 sendfile
StreamedResponse 在 LaravelS/Swoole 下被 DynamicResponse 用 ob_start/
ob_get_clean 整体缓冲进 PHP 内存,约 700MB 文件会撞 memory_limit 导致
下载失败;且每次请求对整文件 md5_file 生成 ETag 开销巨大。

改为返回 BinaryFileResponse,由 LaravelS StaticResponse 走 Swoole 原生
sendfile(),OS 级零拷贝、不占 PHP 内存,可支持任意大小文件。去掉 ETag
全文件哈希改用 mtime。Swoole 环境下关闭 Range 分段(sendfile 只能整文件
发送,避免 206 头与整文件 body 错位),非 Swoole 环境保留原生 Range。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 10:37:52 +00:00
kuaifan
be53e6c6ac feat(skill): 新增 dootask-backup 数据备份技能
- 备份数据库(必须) + public/uploads(排除 tmp,可选) + docker/appstore/config(可选)
- 汇总到 tmp/ 临时目录并附 README 说明,打包到 backup/ 按日期命名
- 只读取源数据、绝不删改,失败即停
- .gitignore 忽略 /backup,避免归档进入版本库

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:04:29 +00:00
kuaifan
4eab130313 feat(electron-mcp): 对齐 dootask-mcp,新增 send_task_ai_message 等
- 新增 send_task_ai_message 工具(dialog/msg/send_ai_assistant),支持
  自定义发送者昵称 nickname(≤20)与 silence
- complete_task 增加 flow_item_id 参数及多结束状态(-4005)重选处理
- update_task 增加 flow_item_id 参数及多结束/开始状态(-4005/-4006)处理
- request() 捕获 ret/data 并对 -4005/-4006 放行交工具处理(向后兼容)
- 同步头部工具清单注释(27→29)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 07:10:36 +00:00
kuaifan
c706c515ee fix(dialog): AI 助手消息推送补全发送者身份
AI 助手为虚拟用户(userid=-1)无会员记录,userid2nickname 返回空串,
导致群聊推送拼成 ": 内容"。友盟 App 推送与前端桌面/移动通知改为取
msg.nickname 或默认"AI 助手"作为发送者名,显示为 "名称: 内容"。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 06:10:02 +00:00
kuaifan
8a576595ce feat(dialog): send_ai_assistant 支持自定义发送者昵称
接口新增可选 nickname 参数(最长20字),写入消息体 msg.nickname;
前端群聊昵称与回复预览对 AI 助手消息(userid=-1)显示自定义昵称,
留空回退默认"AI 助手"。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 06:09:50 +00:00
kuaifan
8c809bbff1 feat(team): 团队管理支持标记成员邮箱认证状态
- 成员行操作菜单新增「标记邮箱为已认证/未认证」,复用 users.email_verity
  字段与 api/users/operation 接口,新增 setverity/clearverity 操作类型
- 创建用户:邮箱下方新增「标记邮箱为已认证」复选框(默认勾选),
  「首次登录需改密」复选框移到初始密码下方
- 批量导入:预览列表邮箱右侧显示主题色已认证图标(错误行不显示),
  支持勾选行后批量标记已/未认证;部门与认证批量行加标签对齐、
  三个批量按钮样式随选中状态统一
- createByAdmin 新增 emailVerity 选项,createuser/import 透传逐行认证状态
- 新增导入预览默认认证状态单测

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 05:49:33 +00:00
kuaifan
08ed396444 feat(skill): 新增安装/更新/修复权限技能,四技能描述精简为命令触发
- dootask-install:前置检查 + sudo ./cmd install(建库 + migrate --seed)
- dootask-update:前置检查 + sudo ./cmd update,本地改动停下交用户决定
- dootask-fix-permission:对齐 install 赋权逻辑,chown + chmod 775,赋权不删数据
- 四个技能均为命令显式触发,description 去掉关键词堆砌,精简为一句功能说明

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 02:14:36 +00:00
kuaifan
f5eb84589f docs(skill): release 改名 dootask-release,补 CI 确认/iOS/uploads 赋权
- 新增 push 后确认 Publish 工作流、可选 iOS 发布(gh workflow run)
- 构建 EACCES:改为 chown 赋权整个 public/uploads(不删数据),删除仅限 tmp
- 同步 Red Flags;目录与 name 统一为 dootask-release

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 23:19:29 +00:00
kuaifan
daca384822 feat(todo): 待办设置权限放开系统管理员(任意群可设/取消他人待办)
- checkTodoOwnerPermission 最高优先级放行系统管理员,覆盖无群主的全员群等场景
- 同步设/取消待办、到点提醒接口报错文案与系统设置描述
- 补充管理员放行测试(user/project/全员群)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 15:02:20 +00:00
kuaifan
0a6e944a9a Merge pull request #301 from robertsilen/pro
docs: mention MariaDB in README
2026-06-01 22:20:35 +08:00
kuaifan
e0d1b08e89 release: v1.7.67 2026-06-01 13:04:00 +00:00
kuaifan
6b54b7b1c5 feat(todo): 聊天待办支持提醒时间(到点引用原消息+@提及)
给消息待办增加可选「提醒时间」,到点由 todo-alert 机器人对原消息发起
reply、正文 @ 仍在群内的被指派成员,完全复用原生回复/提及链路(定向未读、
红点、绕过会话免打扰、App 推送);被指派人全部退群则跳过发送并标记已提醒。
设/改/取消提醒的权限沿用 todo_set_permission 开关与 checkTodoOwnerPermission。

后端:
- 迁移:web_socket_dialog_msg_todos 增加 remind_at/reminded_at 及索引,
  注册为日期字段
- WebSocketDialogMsgTodo::dueReminders() 选取到点(未提醒/未完成)待办(limit 500)
- WebSocketDialogMsg::setTodoRemind() 纯数据写入(改时间重置 reminded_at),
  接入 toggleTodoMsg($remindAt) 与 msg__todo 透传
- 接口 msg__todoremind 设置/修改/取消提醒(权限闸门、消息类型校验、
  pushMsg 同步 todo_done)
- TodoRemindTask 到点按消息发提醒(reminded_at 防重复、迟发补发、原消息/
  会话删除兜底),buildRemindText 生成 <span class="mention user"> 文本,
  接入 crontab;登记 todo-alert 机器人
- msgJoinGroup 从提醒文本中提取被 @ 成员

前端:
- 设待办弹窗新增「提醒时间」(预设 + 自定义 DatePicker)
- 待办详情浮层每条待办可查看/修改/取消提醒:DatePicker on-clear「清空」
  二次确认后取消,无时间时仅关闭面板不发请求
- 待办浮层窄屏(≤500px)改为 待办/完成 tab 切换,宽屏维持双列;列表为空
  展示空状态占位;提醒时间用 Icon 替换 emoji
- 时间读写对齐项目任务时间的时区约定

测试:tests/Feature/TodoRemindTest(数据/选取/写入/权限决策/buildRemindText/
text mention 提取),TodoSetPermissionTest 无回归。

任务 #124 后续增强。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 12:08:34 +00:00
kuaifan
adc7fb0d07 docs(claude): 补充非 REST 路由最多两段的限制说明
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:52:27 +00:00
kuaifan
f969c8145c fix(cmd): env_set 值未变化时跳过写入,避免无谓重写 .env
env_set 原先对已存在的键无条件 sed -i 重写 .env,即使新值与当前值相同也会
改变文件 mtime。开发模式下 vite 监听 .env,任何文件事件都会触发整服务重启,
导致前端反复刷新。写入前复用 env_get 比较,值未变化则直接返回。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 01:27:42 +00:00
kuaifan
20b5daba50 feat(manage): 团队管理支持管理员创建/批量导入员工账号(含部门、职位)
单个创建:邮箱/昵称/初始密码 + 可选首登改密、职位、部门(多选,选子部门自动补选上级,并加入对应部门群)。

批量导入:上传 Excel/CSV → 预览逐行校验 → 确认后导入。职位为模板第4列(选填,逐行解析校验),部门在预览表按行勾选后由底部设置部门到选中写入;导入按行返回结果(全成功关弹窗+成功提示;含失败留弹窗显示失败明细;仅 success>0 才刷新列表)。

后端:User::createByAdmin 选项数组化 + 校验助手 assertValidProfession/assertValidDepartments;importUsers 逐行 department/profession;UsersController createuser/import;UserImport/UserImportTemplate(含职位列)。

测试:tests/Feature/AdminCreateUserTest、tests/Unit/UserImportParseTest。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 01:26:34 +00:00
kuaifan
aa2e0acaba feat(dialog): 系统设置支持禁止其他人员设置/取消聊天待办
新增系统级开关 todo_set_permission(open=允许默认 / close=禁止)。
开关为禁止时,仅本人、群主/群管理员、项目负责人/项目管理员、任务负责人
可设置或取消聊天消息待办,其他人由后端拦截;默认允许,保持现有行为。

- SystemController::setting 接入开关读写(白名单 + 默认 open)
- WebSocketDialog::checkTodoOwnerPermission 角色判断(复用 isOwner 等)
- WebSocketDialogMsg::toggleTodoMsg 内权限闸门:close 且影响到他人且
  非放行角色时 retError;仅影响自己始终放行;open 时行为零变化
- SystemSetting.vue「消息相关」新增「待办设置权限」开关 UI
- 国际化文案(original-api.txt / original-web.txt)
- TodoSetPermissionTest 覆盖角色判断、闸门决策及真实拦截路径(8 用例)

任务 #124。系统后台 admin 不特殊放行;「完成待办」不在本次范围。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:12:46 +00:00
kuaifan
e57736bcc1 docs: 统一语言偏好为整段回复使用简体中文
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 23:40:53 +00:00
kuaifan
a8db8dde7b i18n(task): 任务只读提示文案改为"负责人视角"
将部门只读提示从"当前为负责人,并参与讨论,但不能编辑任务。"
调整为"当前为负责人视角,并参与讨论,但不能编辑任务。",
并同步更新 original-web.txt、translate.json 及各语言编译产物。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 23:32:24 +00:00
Robert Silén
635f6e5d5a Merge branch 'kuaifan:pro' into pro 2026-05-25 15:04:30 +03:00
Robert Silén
4875574c6e add MariaDB to README 2026-05-25 15:04:16 +03:00
kuaifan
b1d5652bc7 refactor(electron): 发布存储从自建服务迁移到 Cloudflare R2
替换 UPLOAD_TOKEN/UPLOAD_URL 为 R2(S3 兼容)对象存储:
- 新增 r2.js 封装上传/复制/删除/列举等操作
- 新增 release-index.js 从文件名解析平台/架构生成下载索引
- CI 环境变量同步切换为 R2_* 系列

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:31:46 +00:00
kuaifan
025f45df0a feat(upload): 添加上传进度显示和错误处理,记录上传耗时 2026-05-22 14:55:34 +08:00
150 changed files with 4538 additions and 777 deletions

View File

@@ -0,0 +1,119 @@
---
description: 备份 DooTask 数据:数据库(必须)+ public/uploads排除 tmp可选+ docker/appstore/config可选。汇总到临时目录并附 README 说明,打包到 backup/ 按日期命名。只读取源数据、绝不删改,失败即停。
---
# DooTask 数据备份
**刚性技能**——前置检查 → 选可选项 → 确认 → 执行 → 报告。只读取源数据生成归档,**绝不删除或修改任何源数据/既有备份**。任何一步失败立即停止。
## 备份范围
| 项 | 来源 | 是否必须 | 说明 |
|----|------|---------|------|
| 数据库 | `./cmd mysql backup` 产出的 `.sql.gz` | **必须** | 脚本内部用 mysqldump 导出当前库 |
| 上传文件 | `public/uploads`**排除 `public/uploads/tmp`** | 可选 | 头像/聊天/任务/文件等真实上传数据;`tmp` 是临时目录,可重建,不备份 |
| 应用配置 | `docker/appstore/config` | 可选 | 应用市场各应用的配置;含 **root 属主子目录**,收集时可能需 sudo |
> `docker/appstore/apps` **不在备份范围**——可从应用市场重新安装,无需备份。
## 前置检查(全部通过才能继续)
1. **工作目录**:在项目根(存在 `cmd``docker-compose.yml`
2. **数据库容器**`mariadb` 容器在跑DB 备份依赖它;不在则提示用户先 `./cmd up` 起服务)
3. **磁盘空间**:确认 `backup/` 所在盘空间足够(数据库 dump 可能较大)
4. **选可选项**:询问用户本次是否包含 `public/uploads``docker/appstore/config`**默认两个都含**
检查通过、可选项确定后,汇报本次将备份哪些项,**向用户确认一次**再执行。
## 执行
用一个统一时间戳贯穿全程:`TS=$(date +%Y%m%d_%H%M%S)`,临时目录 `WORK="tmp/dootask-backup-${TS}"`
### 1) 建临时工作目录
```shell
mkdir -p "$WORK"
```
`tmp/` 已被 gitignore安全
### 2) 数据库(必须)
```shell
./cmd mysql backup
```
脚本会把 dump 写到 `docker/mysql/backup/<库名>_<时间戳>.sql.gz` 并打印「备份文件:...」。**取该次产出的最新 dump** 复制进工作目录(不用关心它原始落在哪):
```shell
DB_FILE=$(ls -t docker/mysql/backup/*.sql.gz | head -1)
cp "$DB_FILE" "$WORK/"
```
### 3) public/uploads可选排除 tmp
```shell
rsync -a --exclude='tmp' public/uploads/ "$WORK/uploads/"
```
> 无 rsync 时用 tar 管道:`mkdir -p "$WORK/uploads" && tar cf - --exclude='./tmp' -C public/uploads . | tar xf - -C "$WORK/uploads"`
### 4) docker/appstore/config可选
```shell
cp -a docker/appstore/config "$WORK/appstore-config"
```
> 含 root 属主子目录,若报 `permission denied`:改用 `sudo cp -a ...`,随后把整个工作目录属主归还当前用户,保证后续打包/清理不受阻:
> ```shell
> sudo chown -R "$(id -u):$(id -g)" "$WORK"
> ```
### 5) 写 README.md备份说明
`$WORK/README.md` 写明本次备份信息,便于日后识别与还原。模板:
```markdown
# DooTask 备份 — <TS>
- 备份时间:<人类可读时间>
- DooTask 版本:<取自 package.json 的 version>
- 包含内容:
- 数据库:<DB dump 文件名>(来源 mysqldump 当前库)
- 上传文件uploads/(来源 public/uploads已排除 tmp ← 未选则写「未包含」
- 应用配置appstore-config/(来源 docker/appstore/config ← 未选则写「未包含」
- 各项大小:<du -sh 列出工作目录内各项>
## 还原提示
- 数据库:`gunzip < <db>.sql.gz | mysql -u<user> -p<pass> <库名>`,或用 `./cmd mysql recovery` 选对应文件还原。
- 上传文件:将 uploads/ 内容覆盖回项目 public/uploads/。
- 应用配置:将 appstore-config/ 覆盖回 docker/appstore/config/。
```
### 6) 打包到 backup/,清理临时目录
```shell
mkdir -p backup
tar czf "backup/dootask_backup_${TS}.tar.gz" -C tmp "dootask-backup-${TS}"
rm -rf "$WORK"
```
## 报告
向用户报告:
- 最终归档路径:`backup/dootask_backup_<TS>.tar.gz`
- 归档大小(`ls -lh`
- 实际包含了哪些项(数据库 + 视选择含/不含 uploads、appstore-config
## 失败处理
- 任何步骤失败立即停止,原样报告错误
- **不要**自动重试、不要静默跳过某一项(可选项是否包含由前置确认决定,不在执行中临时变更)
- DB 备份失败(如 mariadb 未运行)→ 停止,提示用户起服务后重试
- 打包前若工作目录有 root 属主残留导致 tar/rm 失败 → `sudo chown` 归还属主后继续,不要删源数据
## 禁止项
| 错误做法 | 正确做法 |
|---------|---------|
| 为"省空间"删除源数据或既有备份 | 只读取源数据生成归档,源数据一律不动 |
| 备份 `public/uploads/tmp` | 排除 tmp临时、可重建 |
| 把 `docker/appstore/apps` 也打进去 | 不在范围,可从应用市场重装 |
| 遇 config 的 root 子目录就跳过该项 | `sudo` 收集后 chown 归还,完整备份 |
| 不写 README 直接打包 | 每个归档自带 README便于日后识别还原 |
| 把归档写进 git | 归档放 `backup/`(已 gitignore不提交 |
## Red Flags —— 出现这些念头立即停下
- "源数据太大,删点旧的再备份" → 不,备份只读不删
- "config 有 root 目录,跳过算了" → 不sudo 收集后归还属主
- "apps 也一起备了更全" → 不apps 不在范围
- "tmp 里临时文件顺手也备了" → 不,明确排除 `public/uploads/tmp`

View File

@@ -0,0 +1,76 @@
---
name: dootask-fix-permission
description: 修复 DooTask 可写目录bootstrap/cache、docker、public、storage的属主/权限chown 回当前用户 + 目录 chmod 775对齐 install 的赋权逻辑,赋权不删数据。
---
# DooTask 目录权限修复
容器内进程常以 **root** 写入挂载目录(`storage``public/uploads``bootstrap/cache` 等),导致宿主机当前用户对这些文件**没有写权限**,进而触发:
- `./cmd install` 报「目录【xxx】权限不足」/ 目录权限检测失败
- `./cmd build`vite`EACCES: permission denied, copyfile`(复制 `public/uploads/...` 时)
- Laravel 运行时写 `storage`/`bootstrap/cache` 失败
本技能**对齐 `./cmd install` 的目录赋权逻辑**:对四个可写目录做 `chmod 775`(目录)+ `chown` 回当前用户。
## 适用目录
与 install 一致的四个:
```
bootstrap/cache
docker
public # 含 public/uploads真实上传数据
storage
```
## 核心原则:赋权,不删数据
`public/uploads` 含真实上传文件(头像、附件等)。**永远优先 `chown` 改属主,不要删数据。** 即便用户说"清理一下",也只允许清临时目录 `public/uploads/tmp`**切勿**删 uploads 下其他内容。
## 前置检查
1. **工作目录**:在项目根(存在 `cmd` 且这四个目录在)
2. **sudo**:改属主需 root当前文件多为 root 属主)。本机一般可免密 sudo不行则经 docker 以 root 改权限
3. 确认要修的范围:默认四个目录全修;若用户只想解 build 报错,也可只针对 `public`(含 `public/uploads`
检查通过后汇报将执行的命令,**向用户确认一次**再执行。
## 执行
确认后执行(属主修回当前用户,目录权限 775
```shell
# 1) 属主修回当前用户(递归)
sudo chown -R "$(id -u):$(id -g)" bootstrap/cache docker public storage
# 2) 目录权限 775仅目录对齐 install 的 `find -type d -exec chmod 775`
find bootstrap/cache docker public storage -type d -exec chmod 775 {} \;
```
> 只想解 build 的 uploads 报错时,可只对 `public`
> ```shell
> sudo chown -R "$(id -u):$(id -g)" public/uploads
> ```
执行后报告:改了哪些目录、属主/权限现状(可 `ls -ld` 抽查),并提示用户可重试之前失败的 install/build/update。
## 失败处理
- `chown` 报权限不足 → 当前用户无 sudo 权限,提示用户用有 root 权限的账户,或经 docker 以 root 执行;不要静默跳过
- 任何步骤失败立即停止报告,不自动重试
## 禁止项
| 错误做法 | 正确做法 |
|---------|---------|
| build 报 uploads EACCES 就 `rm` 删文件 | `chown` 修属主,保留数据 |
| 删整个 `public/uploads` 清场 | 最多清 `public/uploads/tmp`,别碰真实上传数据 |
| 对文件无差别 `chmod 777` | 目录 `chmod 775` + `chown` 回当前用户即可 |
| 不加 sudo 直接 chown root 文件 | 改属主需 root |
## Red Flags —— 出现这些念头立即停下
- "uploads 复制失败,删掉再 build" → 不,`chown` 赋权,不丢数据
- "777 一把梭最省事" → 不,按 install 的 775目录+ chown
- "权限不够就跳过这个目录" → 不,报告交用户处理 sudo

View File

@@ -0,0 +1,74 @@
---
name: dootask-install
description: 首次部署 DooTask前置检查后执行 `sudo ./cmd install`(建库 + migrate --seed 的重操作),刚性流程、单次确认、失败即停。
---
# DooTask 安装流程
**刚性技能**——前置检查 → 向用户确认一次 → 执行 → 报告结果。任何一步失败立即停止。
## 核心原则
**违反字面规则 = 违反流程精神。** 不要擅自增加、省略、合并步骤,不要为"省事"绕过 sudo 或确认。
`./cmd install` 已把整套安装封装为单条命令(赋权→起容器→`composer install``key:generate``migrate --seed``up -d`)。本技能的职责是**安装前把关、选对参数、执行前确认、已知失败处理**,而不是把脚本逻辑拆开重做。
## 前置检查(全部通过才能继续)
执行前依次确认:
1. **工作目录**:必须在项目根(存在 `cmd``docker-compose.yml``.env.docker`
2. **Docker**`docker``docker-compose`/`docker compose`(v2+) 可用且 daemon 在跑(脚本 `check_docker` 也会查,但提前确认能更早报错)
3. **Node.js ≥ 20**(脚本 `check_node` 会查)
4. **APP_ID 不冲突**:若 `.env` 已有 `APP_ID` 且被其他实例占用,脚本 `check_instance` 会报错——此时**停止**,提示用户先清空 `.env` 里的 `APP_ID``APP_IPPR` 再装
5. **sudo**`./cmd install` 需 root`check_sudo`),用 `sudo ./cmd install` 执行
⚠️ **这是重操作**:会创建数据库并执行 `migrate --seed`(灌入种子数据)。在已有数据的环境上重装前务必和用户确认,避免覆盖。
检查通过后汇报结果,**向用户确认一次**再执行。
## 参数选择
| 参数 | 作用 | 何时用 |
|------|------|--------|
| `--port <端口>` | 指定 HTTP 端口(脚本会做端口占用检测) | 用户要自定义端口,或默认端口被占 |
| `--relock` | 删除 `node_modules`/`package-lock.json`/`vendor`/`composer.lock` 后重装 | **谨慎**:仅在依赖锁损坏、用户明确要求重建锁时用,会拖慢安装 |
不确定时不要自作主张加参数,按需询问用户。
## 执行
确认后执行(按用户选择带上参数):
```shell
sudo ./cmd install
# 或: sudo ./cmd install --port 8080
```
成功后脚本会输出访问地址并调用 `repassword.sh`。执行完向用户报告:访问地址(`http://127.0.0.1:<APP_PORT>`)、以及数据库密码提示。
## 失败处理
- 任何步骤失败立即停止,原样报告错误信息
- **不要**自动重试,**不要**自动跳过
- 常见失败与对应处理:
- `APP_IDxxx已被其他实例使用` → 停止,让用户清空 `.env``APP_ID`/`APP_IPPR` 再装
- `端口 xxx 已被占用` → 停止,让用户换 `--port`
- `目录【xxx】权限不足` / 目录权限检测失败 → 这是目录属主/权限问题,引导用户用 **dootask-fix-permission** 技能修复后重装
- `安装依赖失败`composer→ 报告,交用户决定(常因网络/镜像源)
## 禁止项
| 错误做法 | 正确做法 |
|---------|---------|
| 不加 sudo 直接 `./cmd install` | 用 `sudo ./cmd install`(脚本强制 root |
| 失败后"我再试一次"或自动跳过 | 立即停止,交还用户 |
| 在已有数据环境上不问就重装 | 先确认会 `migrate --seed`,可能影响现有数据 |
| 遇权限报错自己乱 `chmod`/`chown` | 走 dootask-fix-permission 技能统一处理 |
| 不问就加 `--relock` | 默认不加;仅用户明确要求或锁损坏时用 |
## Red Flags —— 出现这些念头立即停下
- "端口/权限报错了我顺手帮 TA 改一下别的" → 停下,只处理本次报的问题,按指引走对应技能
- "种子数据应该没事,直接重装" → 不,先确认是否会覆盖现有数据
- "sudo 麻烦,先试试不加" → 不install 必须 root

View File

@@ -1,6 +1,6 @@
---
name: release
description: Use when releasing a new DooTask frontend version from the `pro` branch. Rigid sequential workflow (translate → version → build → commit → push) with strict pre-checks (branch, clean worktree, Node 20+) and per-step user confirmation. Use when user says "发布新版本", "release", "出新版本", "打版本". Stop on any failure; do NOT auto-fix dirty worktree, do NOT add tag step, do NOT use `git add -A`.
name: dootask-release
description: `pro` 分支发布 DooTask 前端新版本:刚性顺序流程 translate → version → build → commit → push前置检查 + 每步确认、失败即停。
---
# DooTask 发布流程
@@ -43,6 +43,12 @@ npm run build
```
构建前端生产版本。
> **已知失败**:若 build 报 `public/uploads/...``EACCES: permission denied, copyfile`,是 vite 复制 `public/` 目录时碰到运行时残留的上传文件——这些文件常为 **root 属主**(容器内 root 进程写入),复制需覆盖写入,构建用户没有写权限。报错路径可能落在 `public/uploads` 下任意子目录(`tmp``avatar` 等),不限于 `tmp`。这不是代码问题。**补救(赋权,不删数据)**:把整个 uploads 的属主改回当前用户后重试 build
> ```shell
> sudo chown -R "$(id -u):$(id -g)" public/uploads
> ```
> 需 root本机可免密 sudo或经 docker 以 root 改权限)。**优先赋权,不要删**——`public/uploads` 含真实上传数据。即便用户要求清理,也只清临时目录 `public/uploads/tmp`,切勿删 uploads 下其他内容。
## 最终:提交并推送
所有步骤完成后:
@@ -56,6 +62,26 @@ npm run build
- 提交信息使用 `release: v<新版本号>`(与历史提交风格一致,参见 `git log --oneline | grep '^release:'`
- **只 add 本次发布相关改动**,按文件名显式添加(例如 `git add package.json public/js/...`**不要用 `git add -A``git add .`**,以免卷入未跟踪的本地实验文件
## push 之后确认发布工作流CI 才是真正出包)
push 到 `pro` 只是触发器,真正的构建/出包由 GitHub Actions 完成——**push 成功 ≠ 发布完成**
- **Publish**`.github/workflows/publish.yml`push→pro 触发)跑完才算出包;成功后会自动触发 **Sync to Gitee**(镜像同步)。
- push 完成后**主动确认** Publish 工作流 `conclusion=success`。优先用 `gh`(未装可临时装;公开仓库也可用 GitHub REST API 免鉴权读取 runs
```shell
gh run list --workflow=publish.yml -R kuaifan/dootask -L 1
gh run view <run-id> -R kuaifan/dootask --json status,conclusion,url
```
- 工作流仍在跑时,挂后台轮询、结束即通知用户,**不要在前台死等**。
### 可选iOS 发布
`ios-publish.yml` 是**独立的手动工作流**`workflow_dispatch`),不随 push 触发。**仅当用户明确要求**发 iOS 时执行:
```shell
gh workflow run ios-publish.yml --ref pro -R kuaifan/dootask
```
`gh` 已登录且 token 含 `workflow` 权限。触发后同样可挂后台轮询其结果。
## 失败处理
- 任何步骤失败立即停止,报告错误信息
@@ -81,3 +107,5 @@ npm run build
- "`git add -A` 省事" → 不,显式 add
- "翻译这步没改动可以跳" → 不,按顺序执行、执行后报告结果即可
- "一起 commit + push 一气呵成" → 必须先让用户确认
- "push 上去了,发布就完成了" → 不push 只是触发器,要确认 GitHub Actions 的 Publish 工作流 success
- "build 报 uploads 权限错,我直接删掉" → 优先 `chown` 赋权整个 `public/uploads`(不丢数据);真要删也只删 `tmp`,别碰 uploads 下真实上传数据

View File

@@ -0,0 +1,83 @@
---
name: dootask-update
description: 更新已部署的 DooTask前置检查后执行 `sudo ./cmd update`(拉代码 + composer + 迁移 + 重启),本地有改动时停下交用户决定,不自动强制、失败即停。
---
# DooTask 更新流程
**刚性技能**——前置检查 → 向用户确认一次 → 执行 → 报告结果。任何一步失败立即停止。
## 核心原则
**违反字面规则 = 违反流程精神。** 不要擅自加步骤、绕过 sudo/确认,**尤其不要替用户决定强制更新**(会丢本地改动)。
`./cmd update` 已封装整套更新(检测本地改动→`git fetch`→必要时备份库→`git pull/reset``composer install``migrate`→重启 php+nginx→写 `UPDATE_TIME`)。本技能职责是**更新前把关、选对参数、处理本地改动这一关键岔路、执行前确认**。
## 前置检查(全部通过才能继续)
1. **已安装**:必须存在 `vendor/autoload.php`(脚本会查,没装则报"请先执行安装命令"——此时引导用户走 dootask-install
2. **工作目录**:在项目根
3. **当前分支 / 目标分支**:默认更新当前分支;用户要切分支用 `--branch <分支>`。若用户没说,确认是否就更新当前分支
4. **本地改动**(关键):`git status` 看是否有未提交改动
5. **sudo**`sudo ./cmd update` 需 root
检查通过后汇报结果,**向用户确认一次**再执行。
## 关键岔路:本地有改动
脚本检测到本地改动时会询问是否强制更新。**强制更新 = `git reset --hard origin/<分支>`,会丢弃所有本地改动。**
- 发现本地有改动 → **停下**,把改动清单报告用户,让**用户决定**:先提交/暂存改动,还是确认强制更新
- **不要**替用户选 `--force`
- 只有用户明确说"丢掉改动强制更新"时,才带 `--force`
## 参数选择
| 参数 | 作用 | 何时用 |
|------|------|--------|
| `--branch <分支>` | 切到指定分支再更新 | 用户要换分支(如切 `dev`/`pro` |
| `--force` | 强制更新:`git checkout -f` + `git reset --hard` | **危险**:仅用户明确接受"丢弃本地改动"后 |
| `--local` | 本地更新模式:只备份库 + `migrate` + 重启,不拉远程代码 | 代码已就位(如手动改过/CI 拉过),只需迁移+重启 |
## 数据库
- 远程模式下,脚本检测到 `database/` 目录有迁移变动会**自动备份数据库**再继续——这是脚本内置的,无需手动。
- 但若是大版本升级或用户在意数据,执行前提醒用户:本次可能含库迁移,已有自动备份兜底;如需可先 `./cmd mysql backup` 额外备份。
## 执行
确认(含本地改动决策)后执行:
```shell
sudo ./cmd update
# 切分支: sudo ./cmd update --branch pro
# 强制(丢改动,用户确认后): sudo ./cmd update --force
# 本地模式: sudo ./cmd update --local
```
成功后报告:更新到的分支、是否做了库备份/迁移、服务是否重启完成。
## 失败处理
- 任何步骤失败立即停止,原样报告错误
- **不要**自动重试、不要自动跳过、不要因为 `git pull` 失败就自己改成 `--force`
- 常见失败:
- `请先执行安装命令` → 走 dootask-install
- `代码拉取失败,可能存在冲突` → 报告,让用户决定是否 `--force`(丢改动)或先处理冲突
- 重启服务失败 → 脚本会尝试 `down` 后重起;若仍失败,报告交用户
## 禁止项
| 错误做法 | 正确做法 |
|---------|---------|
| 检测到本地改动就自动 `--force` | 停下,报告改动,交用户决定 |
| `git pull` 失败就自动改用 `--force` | 报告冲突,交用户 |
| 不加 sudo | `sudo ./cmd update` |
| 未装就更新 | 先走 dootask-install |
| 失败后自动重试/跳过 | 立即停止 |
## Red Flags —— 出现这些念头立即停下
- "有点本地改动,强制更新一下就好了" → 不,`--force` 会丢改动,必须用户拍板
- "拉取冲突了,我 reset 一下" → 不,交用户决定
- "已经装过了吧,直接更新" → 先确认 `vendor/autoload.php`

View File

@@ -180,8 +180,11 @@ jobs:
- name: (Android) Upload File
if: matrix.build_type == 'android'
env:
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_PUBLIC_URL: ${{ secrets.R2_PUBLIC_URL }}
run: |
node ./electron/build.js android-upload
@@ -219,8 +222,11 @@ jobs:
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_PUBLIC_URL: ${{ secrets.R2_PUBLIC_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
@@ -230,8 +236,11 @@ jobs:
- name: (Windows) Build Client
if: matrix.build_type == 'windows'
env:
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_PUBLIC_URL: ${{ secrets.R2_PUBLIC_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
shell: bash
@@ -264,8 +273,11 @@ jobs:
- name: Upload Changelog & Publish to Website
env:
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_PUBLIC_URL: ${{ secrets.R2_PUBLIC_URL }}
run: |
pushd electron || exit
npm install

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@
/public/hot
/public/tmp
/tmp
/backup
# Uploads and user-generated content
/public/summary

View File

@@ -2,6 +2,38 @@
All notable changes to this project will be documented in this file.
## [1.7.81]
### Features
- 团队管理中可标记成员邮箱认证状态,成员信息更易管理。
- 系统管理员可在任意群组中设置或取消他人的待办,协作管理更灵活。
### Bug Fixes
- 修复 AI 助手消息推送中发送者身份不完整的问题。
### Performance
- 优化大文件下载方式,下载更稳定、更高效。
## [1.7.67]
### Features
- 聊天待办现在可以设置提醒时间,到点会引用原消息并提醒相关人员,避免遗漏重要事项。
- 团队管理支持管理员创建或批量导入员工账号,并可填写部门、职位等信息,添加成员更方便。
- 系统设置新增聊天待办权限控制,可限制其他人员设置或取消聊天待办。
### Bug Fixes
- 设置内容没有变化时不再重复保存,减少无效操作,让使用更稳定。
### Documentation
- 补充路由使用限制说明,帮助使用者更清楚地了解规则。
- 统一回复语言偏好说明,确保整段回复使用简体中文。
## [1.7.55]
### Features

View File

@@ -28,7 +28,8 @@ Laravel 8 (LaravelS/Swoole) + Vue 2 (Vite) + Electron。开源任务/项目管
### 后端
- **非 REST 路由**所有 API 通过 `Route::any('api/{resource}/{method}')` 路由到 `InvokeController`URL 段映射为控制器方法(如 `api/project/lists``lists()`,带 action 则用双下划线:`api/project/invite/join``invite__join()`
- **非 REST 路由**API 控制器(继承 `InvokeController`)在 `routes/web.php` 按资源注册路由URL 段映射为控制器方法(如 `api/project/lists``lists()`,带 action 则用双下划线:`api/project/invite/join``invite__join()`
- 路由最多两段:方法名最多一个双下划线(`method__action`),不支持 `method__action__xxx`(无对应路由,访问 404
- **响应格式**:统一使用 `Base::retSuccess($msg, $data)` / `Base::retError($msg)`,返回 `{"ret": 1, "msg": "...", "data": {...}}`——不要用 `response()->json()`
- 业务异常通过 `App\Exceptions\ApiException` 抛出,不要用通用 Exception
- 模型继承 `AbstractModel`,使用 `Model::createInstance($params)` 创建——不要用 `new Model()``Model::create()`
@@ -58,4 +59,4 @@ Laravel 8 (LaravelS/Swoole) + Vue 2 (Vite) + Electron。开源任务/项目管
## 语言偏好
- 技术总结和关键结论优先使用简体中文,除非用户明确要求其他语言
- 回复一律使用简体中文,除非用户明确要求其他语言

View File

@@ -22,6 +22,7 @@ English | **[中文文档](./README_CN.md)**
- Required: `Docker v20.10+` and `Docker Compose v2.0+`
- Supported Systems: `CentOS/Debian/Ubuntu/macOS` and other Linux/Unix systems
- Hardware Recommendation: 2+ cores, 4GB+ memory
- Database: MariaDB (provided by the default Docker Compose `mariadb` service)
- Special Note: Windows users can install Linux environment using WSL2 before installing DooTask.
### Deploy Project
@@ -115,13 +116,15 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
After installing the new project, follow these steps to complete migration:
1、Backup original database
1、Backup the MariaDB database
```bash
# Run command in the old project
./cmd mysql backup
```
> `./cmd mysql` is the CLI subcommand name; backups run against the MariaDB container.
2、Copy the following files and directories from old project to the same paths in new project
- `Database backup file`

View File

@@ -22,6 +22,7 @@
- 必须安装:`Docker v20.10+``Docker Compose v2.0+`
- 支持环境:`Centos/Debian/Ubuntu/macOS` 等 linux/unix 系统
- 硬件建议2核4G以上
- 数据库MariaDB默认 Docker Compose 中的 `mariadb` 服务)
- 特别说明Windows 可以使用 WSL2 安装 Linux 环境后再安装 DooTask。
### 部署项目
@@ -115,13 +116,15 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
在新项目安装好之后按照以下步骤完成项目迁移:
1、备份数据库
1、备份 MariaDB 数据库
```bash
# 在旧的项目下执行指令
./cmd mysql backup
```
> `./cmd mysql` 为 CLI 子命令名称,实际操作的是 MariaDB 容器。
2、将旧项目以下文件和目录拷贝至新项目同路径位置
- `数据库备份文件`

View File

@@ -1670,6 +1670,7 @@ class DialogController extends AbstractController
if (!in_array($botType, [
'system-msg',
'task-alert',
'todo-alert',
'check-in',
'approval-alert',
'meeting-alert',
@@ -1715,6 +1716,7 @@ class DialogController extends AbstractController
* @apiParam {String} text 消息内容
* @apiParam {String} [text_type=md] 消息格式md 或 html
* @apiParam {String} [silence=no] 是否静默发送yes/no
* @apiParam {String} [nickname] 自定义发送者昵称最多20字留空则显示"AI 助手"
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@@ -1729,6 +1731,7 @@ class DialogController extends AbstractController
$text = trim(Request::input('text'));
$text_type = strtolower(trim(Request::input('text_type'))) ?: 'md';
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
$nickname = trim(Request::input('nickname'));
$markdown = in_array($text_type, ['md', 'markdown']);
//
if (empty($dialog_id) && empty($task_id)) {
@@ -1740,6 +1743,9 @@ class DialogController extends AbstractController
if (mb_strlen($text) > 200000) {
return Base::retError('消息内容最大不能超过200000字');
}
if (mb_strlen($nickname) > 20) {
return Base::retError('发送者昵称最多不能超过20字');
}
//
if ($dialog_id) {
// Direct dialog mode: verify user is a member
@@ -1786,6 +1792,9 @@ class DialogController extends AbstractController
if ($markdown) {
$msgData['type'] = 'md';
}
if ($nickname !== '') {
$msgData['nickname'] = $nickname;
}
//
$result = WebSocketDialogMsg::sendMsg(
null,
@@ -2571,7 +2580,8 @@ class DialogController extends AbstractController
} else {
$userids = is_array($userids) ? $userids : [];
}
return $msg->toggleTodoMsg($user->userid, $userids);
$remindAt = Request::exists('remind_at') ? (trim(Request::input('remind_at', '')) ?: null) : false;
return $msg->toggleTodoMsg($user->userid, $userids, $remindAt);
}
/**
@@ -2604,6 +2614,64 @@ class DialogController extends AbstractController
return Base::retSuccess('success', $todo ?: []);
}
/**
* @api {post} api/dialog/msg/todoremind 设置/修改/取消待办提醒时间
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__todoremind
*
* @apiParam {Number} msg_id 消息ID
* @apiParam {Array} userids 目标成员ID组
* @apiParam {String} remind_at 提醒时间(空表示取消提醒)
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__todoremind()
{
$user = User::auth();
//
$msg_id = intval(Request::input("msg_id"));
$userids = Request::input('userids');
$userids = is_array($userids) ? array_values(array_filter(array_map('intval', $userids))) : [];
$remindAt = trim(Request::input('remind_at', '')) ?: null;
//
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
if (empty($msg)) {
return Base::retError("消息不存在或已被删除");
}
if (in_array($msg->type, ['tag', 'todo', 'notice'])) {
return Base::retError('此消息不支持设待办');
}
$dialog = WebSocketDialog::checkDialog($msg->dialog_id);
//
if (empty($userids)) {
return Base::retError("请选择成员");
}
// 权限管控(与设/取消待办同一开关与放行规则)
if (Base::settingFind('system', 'todo_set_permission') === 'close') {
$others = array_diff($userids, [$user->userid]);
if ($others && !$dialog->checkTodoOwnerPermission($user->userid)) {
return Base::retError('仅群主、项目/任务负责人或系统管理员可设置或取消他人待办');
}
}
//
$msg->setTodoRemind($userids, $remindAt);
//
$upData = [
'id' => $msg->id,
'todo' => $msg->todo,
'todo_done' => $msg->isTodoDone(true),
'dialog_id' => $msg->dialog_id,
];
$dialog->pushMsg('update', $upData);
//
return Base::retSuccess($remindAt ? '设置成功' : '取消成功', $upData);
}
/**
* @api {get} api/dialog/msg/done 完成待办
*

View File

@@ -95,6 +95,7 @@ class SystemController extends AbstractController
'unclaimed_task_reminder_time',
'task_ai_auto_analyze',
'department_owner_project_view',
'todo_set_permission',
])) {
unset($all[$key]);
}
@@ -142,6 +143,7 @@ class SystemController extends AbstractController
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
$setting['task_visible'] = $setting['task_visible'] ?: 'close';
$setting['all_group_mute'] = $setting['all_group_mute'] ?: 'open';
$setting['todo_set_permission'] = $setting['todo_set_permission'] ?: 'open';
$setting['all_group_autoin'] = $setting['all_group_autoin'] ?: 'yes';
$setting['user_private_chat_mute'] = $setting['user_private_chat_mute'] ?: 'open';
$setting['user_group_chat_mute'] = $setting['user_group_chat_mute'] ?: 'open';

View File

@@ -41,6 +41,9 @@ use Illuminate\Support\Facades\DB;
use App\Models\UserEmailVerification;
use App\Module\AgoraIO\AgoraTokenGenerator;
use Swoole\Coroutine;
use App\Module\UserImport;
use App\Module\UserImportTemplate;
use Maatwebsite\Excel\Facades\Excel;
/**
* @apiDefine users
@@ -1091,6 +1094,8 @@ class UsersController extends AbstractController
* - clearadmin 取消管理员
* - settemp 设为临时帐号
* - cleartemp 取消临时身份(取消临时帐号)
* - setverity 标记邮箱为已认证
* - clearverity 标记邮箱为未认证
* - checkin_macs 修改自动签到mac地址需要参数 checkin_macs
* - checkin_face 修改签到人脸图片(需要参数 checkin_face
* - department 修改部门(需要参数 department
@@ -1154,6 +1159,16 @@ class UsersController extends AbstractController
$upArray['identity'] = array_diff($userInfo->identity, ['temp']);
break;
case 'setverity':
$msg = '设置成功';
$upArray['email_verity'] = 1;
break;
case 'clearverity':
$msg = '取消成功';
$upArray['email_verity'] = 0;
break;
case 'checkin_macs':
$list = is_array($data['checkin_macs']) ? $data['checkin_macs'] : [];
$array = [];
@@ -1263,7 +1278,7 @@ class UsersController extends AbstractController
User::passwordPolicy($password);
$upArray['encrypt'] = Base::generatePassword(6);
$upArray['password'] = Doo::md5s($password, $upArray['encrypt']);
$upArray['changepass'] = 1;
$upArray['changepass'] = intval($data['changepass'] ?? 1) === 1 ? 1 : 0;
$upLdap['userPassword'] = $password;
}
// 昵称
@@ -1340,6 +1355,101 @@ class UsersController extends AbstractController
return Base::retSuccess($msg, $userInfo);
}
/**
* @api {post} api/users/createuser 创建用户(管理员)
*
* @apiDescription 需要token身份管理员
* @apiVersion 1.0.0
* @apiGroup users
* @apiName createuser
*
* @apiParam {String} email 邮箱
* @apiParam {String} password 初始密码
* @apiParam {String} nickname 昵称
* @apiParam {Number} [email_verity] 是否标记邮箱为已认证1是、0否默认1
* @apiParam {String} [profession] 职位/职称可选2-20字
* @apiParam {Array} [department] 部门ID列表可选最多10个
*/
public function createuser()
{
User::auth('admin');
$email = trim(Request::input('email'));
$password = trim(Request::input('password'));
$nickname = trim(Request::input('nickname'));
$changePass = intval(Request::input('changepass', 1)) === 1;
$emailVerity = intval(Request::input('email_verity', 1)) === 1;
$profession = trim((string)Request::input('profession', ''));
$department = Request::input('department', []);
$user = User::createByAdmin($email, $password, $nickname, [
'changePass' => $changePass,
'emailVerity' => $emailVerity,
'profession' => $profession,
'department' => is_array($department) ? $department : [],
]);
return Base::retSuccess('创建成功', $user);
}
/**
* @api {post} api/users/import/preview 批量导入预览(管理员)
*
* @apiDescription 需要token身份管理员。上传 Excel/CSV列顺序邮箱、昵称、初始密码、职位(选填)),仅解析+校验、不创建账号
* @apiVersion 1.0.0
* @apiGroup users
* @apiName import__preview
*/
public function import__preview()
{
User::auth('admin');
$file = Request::file('file');
if (empty($file)) {
return Base::retError('请选择文件');
}
$ext = strtolower($file->getClientOriginalExtension());
if (!in_array($ext, ['xls', 'xlsx', 'csv'])) {
return Base::retError('仅支持 xls/xlsx/csv 文件');
}
$sheets = Excel::toArray(new UserImport, $file);
$sheet = $sheets[0] ?? [];
$rows = User::parseImportRows($sheet);
if (empty($rows)) {
return Base::retError('文件中没有可导入的数据');
}
return Base::retSuccess('解析完成', User::importPreview($rows));
}
/**
* @api {post} api/users/import 批量导入用户(管理员)
*
* @apiDescription 需要token身份管理员。提交预览确认后的行数据 rows每行 {email,nickname,password,profession},可选 department[]、email_verity(1已认证/0未认证默认0))进行创建
* @apiVersion 1.0.0
* @apiGroup users
* @apiName import
*/
public function import()
{
User::auth('admin');
$rows = Request::input('rows');
if (!is_array($rows) || empty($rows)) {
return Base::retError('没有可导入的数据');
}
$changePass = intval(Request::input('changepass', 1)) === 1;
$result = User::importUsers($rows, $changePass);
return Base::retSuccess('导入完成', $result);
}
/**
* @api {get} api/users/import/template 下载批量导入模板(管理员)
*
* @apiVersion 1.0.0
* @apiGroup users
* @apiName import__template
*/
public function import__template()
{
User::auth('admin');
return Excel::download(new UserImportTemplate, 'user_import_template.xlsx');
}
/**
* @api {get} api/users/email/verification 邮箱验证
*

View File

@@ -23,6 +23,7 @@ use App\Tasks\CheckinRemindTask;
use App\Tasks\CloseMeetingRoomTask;
use App\Tasks\ManticoreSyncTask;
use App\Tasks\UnclaimedTaskRemindTask;
use App\Tasks\TodoRemindTask;
use App\Tasks\AiTaskLoopTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Laravolt\Avatar\Avatar;
@@ -270,6 +271,8 @@ class IndexController extends InvokeController
Task::deliver(new JokeSoupTask());
// 未领取任务通知
Task::deliver(new UnclaimedTaskRemindTask());
// 待办提醒
Task::deliver(new TodoRemindTask());
// 关闭会议室
Task::deliver(new CloseMeetingRoomTask());
// Manticore Search 同步

View File

@@ -51,6 +51,8 @@ class AbstractModel extends Model
'read_at',
'done_at',
'remind_at',
'reminded_at',
'created_at',
'updated_at',

View File

@@ -89,6 +89,8 @@ use Carbon\Carbon;
*/
class User extends AbstractModel
{
const IMPORT_MAX = 500;
protected $primaryKey = 'userid';
protected $hidden = [
@@ -425,6 +427,287 @@ class User extends AbstractModel
return $createdUser;
}
/**
* 管理员创建员工账号(复用注册逻辑,强制正式身份,可选首登改密 / 部门 / 职位)
* @param string $email
* @param string $password
* @param string $nickname
* @param array $options changePass(bool,默认true) / emailVerity(bool,默认false,标记邮箱已认证) / department(int[]) / profession(string)
* @return self
* @throws ApiException
*/
public static function createByAdmin(string $email, $password, string $nickname, array $options = []): self
{
$nickname = trim($nickname);
if (mb_strlen($nickname) < 2 || mb_strlen($nickname) > 20) {
throw new ApiException('昵称需为2-20个字');
}
$changePass = ($options['changePass'] ?? true) ? 1 : 0;
$emailVerity = ($options['emailVerity'] ?? false) ? 1 : 0;
$profession = trim((string)($options['profession'] ?? ''));
// 校验前置reg 之前快速失败,且可在无 Swoole 环境单测)
self::assertValidProfession($profession);
$departmentIds = self::assertValidDepartments($options['department'] ?? []);
// 复用 reg邮箱校验/查重、passwordPolicy、Doo::userCreate、az/pinyin、全员群、索引同步、user_onboard hook
$user = self::reg($email, $password, ['nickname' => $nickname]);
// 管理员显式创建的账号视为正式员工,去除系统 reg_identity 可能带上的 temp
if (in_array('temp', $user->identity)) {
$user->identity = Base::arrayImplode(array_diff($user->identity, ['temp']));
}
$user->changepass = $changePass; // 复用现有首登强制改密机制
$user->email_verity = $emailVerity; // 管理员可在创建时直接标记邮箱认证状态
if ($profession !== '') {
$user->profession = $profession;
}
if ($departmentIds) {
$user->department = Base::arrayImplode($departmentIds);
}
$user->save();
// 设置了部门 → 加入对应部门群(复刻 operation 的 type=department 入群逻辑)
if ($departmentIds) {
$departments = UserDepartment::whereIn('id', $departmentIds)->get();
foreach ($departments as $department) {
try {
if ($department->dialog_id > 0 && $dialog = WebSocketDialog::find($department->dialog_id)) {
$dialog->joinGroup([$user->userid], 0, true);
$dialog->pushMsg("groupJoin", null, [$user->userid]);
}
} catch (\Throwable $e) {
// 部门入群为尽力投递:单个部门失败不影响账号创建与其他部门
\Log::warning('createByAdmin: 部门入群失败', [
'userid' => $user->userid,
'department_id' => $department->id,
'error' => $e->getMessage(),
]);
}
}
}
return $user;
}
/**
* 将上传表格Excel::toArray 的二维数组)归一化为导入行
* @param array $sheet
* @return array [{line, email, nickname, password}]
*/
public static function parseImportRows(array $sheet): array
{
$rows = [];
foreach ($sheet as $index => $cells) {
if ($index === 0) {
continue; // 表头
}
$email = trim((string)($cells[0] ?? ''));
$nickname = trim((string)($cells[1] ?? ''));
$password = trim((string)($cells[2] ?? ''));
$profession = trim((string)($cells[3] ?? ''));
if ($email === '' && $nickname === '' && $password === '') {
continue; // 空行(仅职位有值也视为空行跳过)
}
$rows[] = [
'line' => $index + 1, // 电子表格行号(从 1 开始)
'email' => $email,
'nickname' => $nickname,
'password' => $password,
'profession' => $profession,
];
}
return $rows;
}
/**
* 校验单条导入行
* @param array $row ['email'=>,'nickname'=>,'password'=>,'profession'=>(选填)]
* @return string|null 错误文案null 表示通过
*/
public static function validateImportRow(array $row): ?string
{
$email = trim((string)($row['email'] ?? ''));
$nickname = trim((string)($row['nickname'] ?? ''));
$password = trim((string)($row['password'] ?? ''));
if ($email === '' || $nickname === '' || $password === '') {
return '邮箱、昵称、初始密码均为必填';
}
if (!Base::isEmail($email)) {
return '邮箱格式不正确';
}
if (mb_strlen($nickname) < 2 || mb_strlen($nickname) > 20) {
return '昵称需为2-20个字';
}
try {
self::passwordPolicy($password);
} catch (ApiException $e) {
return $e->getMessage();
}
// 职位/职称选填,填写则校验 2-20 字
try {
self::assertValidProfession((string)($row['profession'] ?? ''));
} catch (ApiException $e) {
return $e->getMessage();
}
return null;
}
/**
* 校验职位/职称:非空时必须 2-20 字(复用 operation 的现有文案)
* @param string $profession
* @return void
* @throws ApiException
*/
public static function assertValidProfession(string $profession): void
{
$profession = trim($profession);
if ($profession === '') {
return;
}
if (mb_strlen($profession) < 2) {
throw new ApiException('职位/职称不可以少于2个字');
}
if (mb_strlen($profession) > 20) {
throw new ApiException('职位/职称最多只能设置20个字');
}
}
/**
* 规整并校验部门 ID 列表:转正整数去重、最多 10 个、且每个必须存在
* @param mixed $ids
* @return int[]
* @throws ApiException
*/
public static function assertValidDepartments($ids): array
{
if (!is_array($ids)) {
$ids = [];
}
$ids = array_values(array_unique(array_filter(array_map('intval', $ids), fn($v) => $v > 0)));
if (count($ids) > 10) {
throw new ApiException('最多只可加入10个部门');
}
if ($ids) {
$existing = UserDepartment::whereIn('id', $ids)->pluck('id')->map(fn($v) => (int)$v)->all();
if (count($existing) < count($ids)) {
throw new ApiException('修改部门不存在');
}
}
return $ids;
}
/**
* 批量导入用户(部门/职位逐行department 来自前端逐行设置profession 来自 Excel 行)
* @param array $rows 每行含 email/nickname/password/profession可选 department(int[])
* @param bool $changePass 是否要求首登改密(对本批所有账号生效)
* @return array ['total'=>int, 'success'=>int, 'failed'=>[['line','email','reason']]]
* @throws ApiException 行数超限
*/
public static function importUsers(array $rows, bool $changePass = true): array
{
if (count($rows) > self::IMPORT_MAX) {
throw new ApiException('单次最多导入' . self::IMPORT_MAX . '条');
}
$success = 0;
$failed = [];
$seen = [];
foreach ($rows as $row) {
$error = self::validateImportRow($row);
if ($error === null) {
$emailLower = strtolower(trim((string)$row['email']));
if (isset($seen[$emailLower])) {
$error = '文件内邮箱重复';
} else {
$seen[$emailLower] = true;
}
}
if ($error === null) {
try {
self::createByAdmin($row['email'], $row['password'], $row['nickname'], [
'changePass' => $changePass,
'emailVerity' => !empty($row['email_verity']),
'department' => $row['department'] ?? [],
'profession' => $row['profession'] ?? '',
]);
$success++;
continue;
} catch (ApiException $e) {
$error = $e->getMessage();
}
}
$failed[] = [
'line' => $row['line'] ?? 0,
'email' => $row['email'] ?? '',
'reason' => $error,
];
}
return [
'total' => count($rows),
'success' => $success,
'failed' => $failed,
];
}
/**
* 批量导入预览(只解析+校验,不创建任何账号)
* 逐行判定 ok/error必填/邮箱格式/昵称长度/密码策略、文件内邮箱重复、系统中邮箱已存在
* @param array $rows parseImportRows 的输出
* @return array ['total'=>int,'valid'=>int,'invalid'=>int,'rows'=>[['line','email','nickname','password','status','reason']]]
*/
public static function importPreview(array $rows): array
{
if (count($rows) > self::IMPORT_MAX) {
throw new ApiException('单次最多导入' . self::IMPORT_MAX . '条');
}
// 预查系统中已存在的邮箱(小写比较)
$emails = [];
foreach ($rows as $row) {
$e = strtolower(trim((string)($row['email'] ?? '')));
if ($e !== '') {
$emails[$e] = true;
}
}
$existing = [];
if ($emails) {
foreach (self::whereIn('email', array_keys($emails))->pluck('email') as $em) {
$existing[strtolower($em)] = true;
}
}
$seen = [];
$valid = 0;
$list = [];
foreach ($rows as $row) {
$reason = self::validateImportRow($row);
$emailLower = strtolower(trim((string)($row['email'] ?? '')));
if ($reason === null) {
if (isset($seen[$emailLower])) {
$reason = '文件内邮箱重复';
} else {
$seen[$emailLower] = true;
if (isset($existing[$emailLower])) {
$reason = '邮箱地址已存在';
}
}
}
$ok = $reason === null;
if ($ok) {
$valid++;
}
$list[] = [
'line' => $row['line'] ?? 0,
'email' => $row['email'] ?? '',
'nickname' => $row['nickname'] ?? '',
'password' => $row['password'] ?? '',
'profession' => $row['profession'] ?? '',
'email_verity' => 1, // 默认标记为已认证,前端可在预览中按行调整
'status' => $ok ? 'ok' : 'error',
'reason' => $reason ?? '',
];
}
return [
'total' => count($rows),
'valid' => $valid,
'invalid' => count($rows) - $valid,
'rows' => $list,
];
}
/**
* 获取我的ID
* @return int

View File

@@ -151,6 +151,7 @@ class UserBot extends AbstractModel
$name = match ($name) {
'system-msg' => '系统消息',
'task-alert' => '任务提醒',
'todo-alert' => '待办提醒',
'check-in' => '签到打卡',
'anon-msg' => '匿名消息',
'approval-alert' => '审批',

View File

@@ -710,6 +710,46 @@ class WebSocketDialog extends AbstractModel
return $this->isPrimaryOwner($userid) || $this->isDeputyOwner($userid);
}
/**
* 是否有权限设置/取消本会话内「他人」的待办
* 放行:群主/群管理员、关联项目负责人/项目管理员、关联任务负责人(及任务所属项目负责人/管理员)
*
* @param int $userid
* @return bool
*/
public function checkTodoOwnerPermission($userid): bool
{
$userid = intval($userid);
if ($userid <= 0) {
return false;
}
// 系统管理员:可管理任意会话的他人待办(与管理员全局管理能力一致,覆盖无群主的全员群等)
if (User::find($userid)?->isAdmin()) {
return true;
}
// 群主 / 群管理员
if ($this->isOwner($userid)) {
return true;
}
// 关联项目(项目群)负责人 / 项目管理员
$project = Project::whereDialogId($this->id)->first();
if ($project && $project->isOwner($userid)) {
return true;
}
// 关联任务(任务群)负责人,及任务所属项目负责人 / 管理员
$task = ProjectTask::whereDialogId($this->id)->first();
if ($task) {
if (ProjectTaskUser::whereTaskId($task->id)->whereUserid($userid)->whereOwner(1)->exists()) {
return true;
}
$taskProject = Project::find($task->project_id);
if ($taskProject && $taskProject->isOwner($userid)) {
return true;
}
}
return false;
}
/**
* 群管理员 userid 列表
*

View File

@@ -414,7 +414,7 @@ class WebSocketDialogMsg extends AbstractModel
* @param array $userids 设置给指定会员
* @return mixed
*/
public function toggleTodoMsg($sender, $userids = [])
public function toggleTodoMsg($sender, $userids = [], $remindAt = false)
{
if (in_array($this->type, ['tag', 'todo', 'notice'])) {
return Base::retError('此消息不支持设待办');
@@ -423,6 +423,14 @@ class WebSocketDialogMsg extends AbstractModel
$current = WebSocketDialogMsgTodo::whereMsgId($this->id)->pluck('userid')->toArray();
$cancel = array_diff($current, $userids);
$setup = array_diff($userids, $current);
// 待办操作权限管控(系统开关:禁止其他人员设置/取消待办)
if (Base::settingFind('system', 'todo_set_permission') === 'close') {
$affected = array_unique(array_merge($cancel, $setup)); // 本次真正影响到的用户
$others = array_diff($affected, [$sender]); // 排除"自己"
if ($others && !$dialog->checkTodoOwnerPermission($sender)) {
return Base::retError('仅群主、项目/任务负责人或系统管理员可设置或取消他人待办');
}
}
//
$this->todo = $setup || count($current) > count($cancel) ? $sender : 0;
$this->save();
@@ -477,12 +485,39 @@ class WebSocketDialogMsg extends AbstractModel
];
$dialog->pushMsg('update', $upData);
//
// 提醒时间仅当调用方显式传入时处理false=不传则不动既有提醒)
if ($remindAt !== false) {
$this->setTodoRemind($userids, $remindAt ?: null);
}
//
return Base::retSuccess($this->todo ? '设置成功' : '取消成功', [
'add' => $addData,
'update' => $upData,
]);
}
/**
* 设置/取消本消息指定成员待办的提醒时间(纯数据,无推送)。
* 改动会把 reminded_at 重置为 null使其可再次到点提醒。
*
* @param array $userids 目标成员
* @param string|null $remindAt 提醒时间字符串null/空 表示取消提醒
* @return int 受影响行数
*/
public function setTodoRemind(array $userids, $remindAt = null)
{
$userids = array_values(array_filter(array_map('intval', $userids)));
if (empty($userids)) {
return 0;
}
return WebSocketDialogMsgTodo::whereMsgId($this->id)
->whereIn('userid', $userids)
->update([
'remind_at' => $remindAt ?: null,
'reminded_at' => null,
]);
}
/**
* 转发消息
* @param array|int $dialogids

View File

@@ -2,6 +2,8 @@
namespace App\Models;
use Carbon\Carbon;
/**
* App\Models\WebSocketDialogMsgTodo
*
@@ -50,4 +52,21 @@ class WebSocketDialogMsgTodo extends AbstractModel
}
return $this->appendattrs['msgData'];
}
/**
* 取到点待提醒的待办行:有提醒时间、未提醒、未完成、提醒时间已到。
* 纯查询,无副作用,供 TodoRemindTask 使用。
* @return \Illuminate\Database\Eloquent\Collection
*/
public static function dueReminders()
{
return self::whereNotNull('remind_at')
->whereNull('reminded_at')
->whereNull('done_at')
->where('remind_at', '<=', Carbon::now())
->orderBy('msg_id')
->orderBy('id')
->limit(500)
->get();
}
}

View File

@@ -14,7 +14,7 @@ use Overtrue\Pinyin\Pinyin;
use Redirect;
use Request;
use Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\File;
use Validator;
@@ -2868,9 +2868,15 @@ class Base
/**
* DownloadFileResponse 下载文件
*
* 返回 Symfony BinaryFileResponse在 LaravelS/Swoole 环境下由 StaticResponse 走原生
* sendfile() 发送——OS 级零拷贝、不占用 PHP 内存,可支持任意大小文件(如几百 MB 的大文件)。
* 切勿改回 StreamedResponse它会被 LaravelS 用 ob_start()/ob_get_clean() 把整个响应体
* 缓冲进 PHP 内存,大文件会撞 memory_limit 导致下载失败。
*
* @param File|\SplFileInfo|string $file 文件对象或路径
* @param string|null $name 下载文件名
* @return StreamedResponse
* @return BinaryFileResponse
*/
public static function DownloadFileResponse($file, $name = null)
{
@@ -2889,12 +2895,6 @@ 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());
@@ -2912,83 +2912,27 @@ class Base
$mimeType = 'application/octet-stream';
}
// 处理 Range 请求
$start = 0;
$end = $size - 1;
$length = $size;
$isRangeRequest = false;
// BinaryFileResponseautoEtag=false 避免对大文件做 md5/sha1 全文件哈希autoLastModified=true 取 mtime开销极小
$response = new BinaryFileResponse($file, 200, [], true, null, false, true);
$response->headers->set('Content-Type', $mimeType);
$response->headers->set('Cache-Control', 'private, no-transform, no-store, must-revalidate, max-age=0');
// filename 兜底为纯 ASCIIfilename* 用 UTF-8 编码,兼容含中文/特殊字符的文件名
$asciiName = preg_replace('/[^\x20-\x7e]/', '_', $name);
$response->headers->set('Content-Disposition', sprintf(
'attachment; filename="%s"; filename*=UTF-8\'\'%s',
$asciiName,
rawurlencode($name)
));
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;
}
}
// LaravelS/Swoole 下 StaticResponse 用 sendfile() 整文件发送,不支持分段;
// 若放任 Symfony 处理 Range 会返回 206 头却仍发送完整文件,导致内容错位/损坏。
// 故在 Swoole 环境下移除 Range 请求头,始终以 200 返回完整文件。
if (app()->bound('swoole')) {
Request::instance()->headers->remove('Range');
$response->headers->set('Accept-Ranges', 'none');
}
// 设置基本响应头
$headers = [
'Content-Type' => $mimeType,
'Content-Disposition' => sprintf(
'attachment; filename="%s"; filename*=UTF-8\'\'%s',
$name,
rawurlencode($name)
),
'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
);
return $response;
} catch (\Exception $e) {
\Log::error('File download failed', [
'error' => $e->getMessage(),

13
app/Module/UserImport.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Module;
use Maatwebsite\Excel\Concerns\ToArray;
class UserImport implements ToArray
{
public function array(array $array)
{
return $array;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Module;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
class UserImportTemplate implements FromArray, WithHeadings
{
public function array(): array
{
return [
['employee@example.com', '张三', 'Abc123456', '工程师'],
];
}
public function headings(): array
{
return ['邮箱(必填)', '昵称(必填,2-20字)', '初始密码(必填,6-32位)', '职位(选填,2-20字)'];
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Tasks;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WebSocketDialogMsgTodo;
use App\Module\Doo;
use Carbon\Carbon;
/**
* 待办提醒:到点由 todo-alert 机器人在原会话发一条「引用原消息 + @被指派成员」的普通文本
* (同一消息同批到点的成员合并一条)。
*/
class TodoRemindTask extends AbstractTask
{
public function __construct()
{
parent::__construct();
}
/**
* 构造提醒文本:每个被提醒成员一个 @ span + 提示语。
* 直接拼 <span class="mention user" data-id> 是因为 sendMsg 不会调用 formatMsg
* 文本会原样入库msgJoinGroup 据此 span 正则提取 @。
*/
public static function buildRemindText(array $mentionUserids): string
{
$nicknames = User::whereIn('userid', $mentionUserids)->pluck('nickname', 'userid');
$mentionText = '';
foreach ($mentionUserids as $uid) {
$name = $nicknames[$uid] ?? $uid;
$mentionText .= "<span class=\"mention user\" data-id=\"{$uid}\">@{$name}</span> ";
}
return $mentionText . Doo::translate('你有一条待办到提醒时间啦');
}
public function start()
{
$rows = WebSocketDialogMsgTodo::dueReminders();
if ($rows->isEmpty()) {
return;
}
$botUser = User::botGetOrCreate('todo-alert');
if (empty($botUser)) {
return;
}
foreach ($rows->groupBy('msg_id') as $msgId => $group) {
$rowIds = $group->pluck('id')->toArray();
$userids = $group->pluck('userid')->map('intval')->values()->toArray();
//
$msg = WebSocketDialogMsg::find($msgId);
$dialog = $msg ? WebSocketDialog::find($msg->dialog_id) : null;
if (empty($msg) || empty($dialog)) {
// 原消息/会话已不存在:标记已提醒,避免空转重复扫描
WebSocketDialogMsgTodo::whereIn('id', $rowIds)->update(['reminded_at' => Carbon::now()]);
continue;
}
//
$memberIds = $dialog->dialogUser->pluck('userid')->map('intval')->values()->toArray();
$mentionUserids = array_values(array_intersect($userids, $memberIds));
if (empty($mentionUserids)) {
// 被指派人都已退群:没人可 @,标记已提醒避免空转重复扫描
WebSocketDialogMsgTodo::whereIn('id', $rowIds)->update(['reminded_at' => Carbon::now()]);
continue;
}
$res = WebSocketDialogMsg::sendMsg(
"reply-{$msg->id}", // 引用原消息 → reply_data 自动填充
$dialog->id,
'text', // 普通文本
['text' => self::buildRemindText($mentionUserids)],
$botUser->userid,
false, false, false // push_self / push_retry / push_silence
);
//
if (\App\Module\Base::isSuccess($res)) {
WebSocketDialogMsgTodo::whereIn('id', $rowIds)->update(['reminded_at' => Carbon::now()]);
}
}
}
public function end()
{
}
}

View File

@@ -189,7 +189,12 @@ class WebSocketDialogMsgTask extends AbstractTask
if ($umengUserid) {
$setting = Base::setting('appPushSetting');
if ($setting['push'] === 'open') {
$umengTitle = User::userid2nickname($msg->userid);
if ($msg->userid == -1) {
// AI 助手虚拟用户没有会员记录,取自定义昵称或默认名称
$umengTitle = ($msg->msg['nickname'] ?? '') ?: Doo::translate('AI 助手');
} else {
$umengTitle = User::userid2nickname($msg->userid);
}
$umengBody = WebSocketDialogMsg::previewMsg($msg);
if ($dialog->type == 'group') {
$umengBody = $umengTitle . ': ' . $umengBody;

2
bin/version.js vendored
View File

@@ -11,7 +11,7 @@ const packageFile = path.resolve(process.cwd(), "package.json");
const changeFile = path.resolve(process.cwd(), "CHANGELOG.md");
const verOffset = 6394; // 版本号偏移量
const codeOffset = 34; // 代码版本号偏移量
const codeOffset = 35; // 代码版本号偏移量
const envFilePath = path.resolve(process.cwd(), ".env");
const defaultAiSystemPrompt = "你是一位软件发布日志编辑专家。请产出 Markdown 更新日志,面向普通用户,以通俗友好的简体中文描述更新带来的直接好处,避免技术术语。所有章节标题必须以 `### ` 开头并保持英文 Title Case例如 `### Features`、`### Bug Fixes`、`### Performance`、`### Documentation` 等)。每个章节内的条目按用户价值和影响范围排序,将更重要、影响更广的更新放在前面。";

4
cmd
View File

@@ -394,6 +394,10 @@ env_set() {
echo "" >> $WORK_DIR/.env
echo "$key=$val" >> $WORK_DIR/.env
else
# 值未变化则直接返回,避免无谓重写 .env重写会改 mtime触发 vite 全量重启/前端刷新)
if [[ "$(env_get "$key")" == "$val" ]]; then
return 0
fi
if [[ `uname` == 'Linux' ]]; then
sed -i "/^${key}=/c\\${key}=${val}" ${WORK_DIR}/.env
else

View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddRemindToWebSocketDialogMsgTodos extends Migration
{
public function up()
{
Schema::table('web_socket_dialog_msg_todos', function (Blueprint $table) {
$table->timestamp('remind_at')->nullable()->comment('提醒时间')->after('done_at');
$table->timestamp('reminded_at')->nullable()->comment('已提醒时间')->after('remind_at');
$table->index(['remind_at', 'reminded_at', 'done_at'], 'idx_todo_remind');
});
}
public function down()
{
Schema::table('web_socket_dialog_msg_todos', function (Blueprint $table) {
$table->dropIndex('idx_todo_remind');
$table->dropColumn(['remind_at', 'reminded_at']);
});
}
}

View File

@@ -96,7 +96,7 @@ services:
appstore:
container_name: "dootask-appstore-${APP_ID}"
privileged: true
image: "dootask/appstore:0.4.0"
image: "dootask/appstore:0.4.3"
volumes:
- shared_data:/usr/share/dootask
- ${HOST_DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock

315
electron/build.js vendored
View File

@@ -6,13 +6,14 @@ const child_process = require('child_process');
const ora = require('ora');
const yauzl = require('yauzl');
const axios = require('axios');
const FormData =require('form-data');
const tar = require('tar');
const utils = require('./lib/utils');
const r2 = require('./lib/r2');
const { buildReleaseIndex } = require('./lib/release-index');
const config = require('../package.json')
const env = require('dotenv').config({ path: './.env' })
const argv = process.argv;
const {BUILD_FRONTEND, APPLEID, APPLEIDPASS, GITHUB_TOKEN, GITHUB_REPOSITORY, UPLOAD_TOKEN, UPLOAD_URL} = process.env;
const {BUILD_FRONTEND, APPLEID, APPLEIDPASS, GITHUB_TOKEN, GITHUB_REPOSITORY} = process.env;
const electronDir = path.resolve(__dirname, "public");
const nativeCachePath = path.resolve(__dirname, ".native");
@@ -26,6 +27,7 @@ let buildChecked = false,
updaterChecked = false;
const shellQuote = (value) => `'${String(value).replace(/'/g, `'\\''`)}'`;
const elapsedSeconds = (startTime) => `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
/**
* 检测并下载更新器
@@ -310,193 +312,23 @@ function changeLog() {
}
/**
* 封装 axios 自动重试
* @param data // {axios: object{}, onRetry: function, retryNumber: number}
* @returns {Promise<unknown>}
* 上传单个文件到 R2 的 draft/<version>/ 目录(带进度/spinner
*/
function axiosAutoTry(data) {
return new Promise((resolve, reject) => {
axios(data.axios).then(result => {
resolve(result)
}).catch(error => {
if (typeof data.retryNumber == 'number' && data.retryNumber > 0) {
data.retryNumber--;
if (typeof data.onRetry === "function") {
data.onRetry(error)
}
if (error.code == 'ECONNABORTED' || error.code == 'ECONNRESET') {
// 中止,超时
return resolve(axiosAutoTry(data))
} else {
if (error.response && error.response.status == 407) {
// 代理407
return setTimeout(v => {
resolve(axiosAutoTry(data))
}, 500 + Math.random() * 500)
} else if (error.response && error.response.status == 503) {
// 服务器异常
return setTimeout(v => {
resolve(axiosAutoTry(data))
}, 1000 + Math.random() * 500)
} else if (error.response && error.response.status == 429) {
// 并发超过限制
return setTimeout(v => {
resolve(axiosAutoTry(data))
}, 1000 + Math.random() * 1000)
}
}
}
reject(error)
})
})
}
/**
* 官网发布器
*/
class WebsitePublisher {
constructor({baseUrl, token, version}) {
this.baseUrl = baseUrl
this.token = token
this.version = version
async function uploadDraftFile(client, localFile, version) {
const filename = path.basename(localFile);
const key = `draft/${version}/${filename}`;
const startTime = Date.now();
const spinner = ora(`Upload [0%] ${filename}`).start();
try {
await r2.uploadFile(client, localFile, key, (loaded, total) => {
const pct = Math.min(99, Math.round((loaded / total) * 100)) + '%';
spinner.text = `Upload [${pct}] ${filename}`;
});
} catch (error) {
spinner.fail(`Upload [fail] ${filename} (${elapsedSeconds(startTime)}): ${error.message || error}`);
throw error;
}
/**
* 上传单个文件
* @param localFile 本地文件路径
* @param options { platform, arch } 可选,有则为安装包
*/
async uploadPackage(localFile, options = {}) {
const filename = path.basename(localFile)
let spinner = ora(`Upload [0%] ${filename}`).start()
const formData = new FormData()
formData.append("version", this.version)
if (options.platform) {
formData.append("platform", options.platform)
if (options.arch) {
formData.append("arch", options.arch)
}
}
formData.append("file", fs.createReadStream(localFile))
const {status, data} = await axiosAutoTry({
axios: {
method: 'post',
url: `${this.baseUrl}/api/upload/package`,
data: formData,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'multipart/form-data;boundary=' + formData.getBoundary(),
},
onUploadProgress: progress => {
const complete = Math.min(99, Math.round(progress.loaded / progress.total * 100 | 0)) + '%'
spinner.text = `Upload [${complete}] ${filename}`
},
},
onRetry: (err) => {
const reason = err?.response?.status || err?.code || err?.message || ''
spinner.warn(`Upload [retry] ${filename}${reason ? ': ' + reason : ''}`)
spinner = ora(`Upload [0%] ${filename}`).start()
},
retryNumber: 3
})
if (status !== 200 || !utils.isJson(data) || !data.success) {
const reason = data?.message || `status ${status}`
spinner.fail(`Upload [fail] ${filename}: ${reason}`)
throw new Error(`Upload failed: ${filename}: ${reason}`)
}
spinner.succeed(`Upload [100%] ${filename}`)
}
/**
* 上传 changelog
*/
async uploadChangelog(content) {
const spinner = ora('Uploading changelog...').start()
const {status, data} = await axiosAutoTry({
axios: {
method: 'post',
url: `${this.baseUrl}/api/upload/changelog`,
data: { content },
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
},
},
retryNumber: 3
})
if (status !== 200 || !data.success) {
spinner.fail('Changelog upload failed')
throw new Error('Changelog upload failed')
}
spinner.succeed('Changelog uploaded')
}
/**
* 通知发布完成
*/
async release() {
const spinner = ora('Publishing release...').start()
const {status, data} = await axiosAutoTry({
axios: {
method: 'post',
url: `${this.baseUrl}/api/upload/release`,
data: { version: this.version },
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
},
},
retryNumber: 3
})
if (status !== 200 || !data.success) {
spinner.fail(`Release failed: ${data?.message || status}`)
throw new Error(`Release failed: ${data?.message || status}`)
}
spinner.succeed('Release published')
}
}
// 安装包扩展名
const INSTALLER_EXTS = ['.dmg', '.exe', '.msi', '.appimage', '.deb', '.rpm', '.apk']
/**
* 创建 WebsitePublisher 实例(如果环境变量齐全)
*/
function createPublisher() {
if (!UPLOAD_TOKEN || !UPLOAD_URL) {
return null
}
return new WebsitePublisher({
baseUrl: UPLOAD_URL.replace(/\/+$/, ''),
token: UPLOAD_TOKEN,
version: config.version
})
}
/**
* 从文件名判断是否为安装包
*/
function isInstaller(filename) {
return INSTALLER_EXTS.some(ext => filename.toLowerCase().endsWith(ext))
}
/**
* 从文件名提取 arch
*/
function parseArchFromFilename(filename) {
if (/-arm64[.-]/i.test(filename)) return 'arm64'
if (/-x64[.-]/i.test(filename)) return 'x64'
return null
}
/**
* 将构建平台名映射为 API platform
*/
function mapPlatform(buildPlatform) {
if (buildPlatform.includes('mac')) return 'mac'
if (buildPlatform.includes('win')) return 'win'
if (buildPlatform.includes('linux')) return 'linux'
return null
spinner.succeed(`Upload [100%] ${filename} (${elapsedSeconds(startTime)})`);
}
/**
@@ -668,30 +500,22 @@ async function startBuild(data) {
fs.writeFileSync(packageFile, JSON.stringify(appConfig, null, 4), 'utf8');
child_process.execSync(`npm run ${platform}-publish`, {stdio: "inherit", cwd: "electron"});
}
// generic (build or publish)
appConfig.build.publish = data.publish
// generic (build or publish) —— 有 R2_PUBLIC_URL 时自动更新源指向 R2 release/
appConfig.build.publish = r2.R2_PUBLIC_URL
? { provider: 'generic', url: `${r2.R2_PUBLIC_URL.replace(/\/+$/, '')}/release` }
: data.publish
appConfig.build.directories.output = `${output}-generic`;
fs.writeFileSync(packageFile, JSON.stringify(appConfig, null, 4), 'utf8');
child_process.execSync(`npm run ${platform}`, {stdio: "inherit", cwd: "electron"});
if (publish === true) {
const publisher = createPublisher()
if (publisher) {
const outputDir = path.resolve(__dirname, appConfig.build.directories.output)
if (fs.existsSync(outputDir)) {
const apiPlatform = mapPlatform(platform)
const files = fs.readdirSync(outputDir)
for (const filename of files) {
const localFile = path.join(outputDir, filename)
const fileStat = fs.statSync(localFile)
if (!fileStat.isFile()) continue
if (isInstaller(filename) && apiPlatform) {
const arch = parseArchFromFilename(filename)
await publisher.uploadPackage(localFile, { platform: apiPlatform, arch })
} else {
await publisher.uploadPackage(localFile)
}
}
if (publish === true && r2.r2Configured()) {
const client = r2.createR2Client()
const outputDir = path.resolve(__dirname, appConfig.build.directories.output)
if (fs.existsSync(outputDir)) {
const files = fs.readdirSync(outputDir)
for (const filename of files) {
const localFile = path.join(outputDir, filename)
if (!fs.statSync(localFile).isFile()) continue
await uploadDraftFile(client, localFile, config.version)
}
}
}
@@ -729,13 +553,13 @@ if (["dev"].includes(argv[2])) {
}
})
} else if (["android-upload"].includes(argv[2])) {
// 上传安卓文件GitHub Actions
// 上传安卓文件到 R2 draftGitHub Actions
(async () => {
const publisher = createPublisher()
if (!publisher) {
console.error("缺少 UPLOAD_TOKEN 或 UPLOAD_URL 环境变量")
if (!r2.r2Configured()) {
console.error("缺少 R2_* 环境变量R2_ACCESS_KEY_ID/R2_SECRET_ACCESS_KEY/R2_ENDPOINT/R2_BUCKET")
process.exit(1)
}
const client = r2.createR2Client()
const releaseDir = path.resolve(__dirname, "../resources/mobile/platforms/android/eeuiApp/app/build/outputs/apk/release");
if (!fs.existsSync(releaseDir)) {
console.error("发布文件未找到")
@@ -745,7 +569,7 @@ if (["dev"].includes(argv[2])) {
for (const filename of files) {
const localFile = path.join(releaseDir, filename)
if (/\.apk$/.test(filename) && fs.existsSync(localFile) && fs.statSync(localFile).isFile()) {
await publisher.uploadPackage(localFile, { platform: 'android' })
await uploadDraftFile(client, localFile, config.version)
}
}
})().catch(err => {
@@ -753,24 +577,63 @@ if (["dev"].includes(argv[2])) {
process.exit(1)
})
} else if (["release"].includes(argv[2])) {
// 通知官网发布完成GitHub Actions
// R2 内提升draft/<version> → release/(当前版扁平,旧版归档 release/<prev>/
(async () => {
const publisher = createPublisher()
if (!publisher) {
console.error("缺少 UPLOAD_TOKEN 或 UPLOAD_URL 环境变量")
if (!r2.r2Configured()) {
console.error("缺少 R2_* 环境变量")
process.exit(1)
}
await publisher.release()
const client = r2.createR2Client()
const version = config.version
const draftPrefix = `draft/${version}/`
const draftKeys = await r2.listKeys(client, draftPrefix)
if (!draftKeys.length) {
console.error(`draft/${version}/ 为空,无法发布`)
process.exit(1)
}
const names = draftKeys.map(k => k.slice(draftPrefix.length))
// 读 manifest 取上一发布版
const manifest = JSON.parse(await r2.getText(client, 'manifest.json') || '{"draft":null,"release":null}')
const prev = manifest.release
// 1. 归档上一版扁平文件 → release/<prev>/
if (prev && prev !== version) {
const prevRootKeys = await r2.listKeys(client, 'release/', '/')
for (const key of prevRootKeys) {
const name = key.slice('release/'.length)
await r2.copyObject(client, key, `release/${prev}/${name}`)
}
}
// 2. 清空扁平根层(仅根层对象,版本归档子目录不动)
const rootKeys = await r2.listKeys(client, 'release/', '/')
await r2.deleteKeys(client, rootKeys)
// 3. 铺新扁平:安装包/blockmap/zip 先latest*.yml 最后
const ymls = names.filter(n => /\.ya?ml$/i.test(n))
const others = names.filter(n => !/\.ya?ml$/i.test(n))
for (const name of others) await r2.copyObject(client, `${draftPrefix}${name}`, `release/${name}`)
for (const name of ymls) await r2.copyObject(client, `${draftPrefix}${name}`, `release/${name}`)
// 4. 下载索引
const index = buildReleaseIndex(names)
await r2.putText(client, 'release/index.json', JSON.stringify({ version, files: index }, null, 2))
// 5. 更新 manifest清理 draft
await r2.putText(client, 'manifest.json', JSON.stringify({ draft: null, release: version }, null, 2))
await r2.deleteKeys(client, draftKeys)
console.log(`Release published: v${version}`)
})().catch(err => {
console.error(err.message || err)
process.exit(1)
})
} else if (["upload-changelog"].includes(argv[2])) {
// 上传 changelogGitHub Actions
// 上传 changelog 到 R2GitHub Actions
(async () => {
const publisher = createPublisher()
if (!publisher) {
console.error("缺少 UPLOAD_TOKEN 或 UPLOAD_URL 环境变量")
if (!r2.r2Configured()) {
console.error("缺少 R2_* 环境变量")
process.exit(1)
}
const changelogPath = path.resolve(__dirname, "../CHANGELOG.md")
@@ -778,8 +641,10 @@ if (["dev"].includes(argv[2])) {
console.error("CHANGELOG.md 未找到")
process.exit(1)
}
const client = r2.createR2Client()
const content = fs.readFileSync(changelogPath, 'utf8')
await publisher.uploadChangelog(content)
await r2.putText(client, 'changelog.md', content)
console.log('Changelog uploaded')
})().catch(err => {
console.error(err.message || err)
process.exit(1)
@@ -933,8 +798,8 @@ if (["dev"].includes(argv[2])) {
// 发布判断环境变量
if (answers.publish) {
if (!(UPLOAD_TOKEN && UPLOAD_URL) && !(GITHUB_TOKEN && utils.strExists(GITHUB_REPOSITORY, "/"))) {
console.error("发布需要 UPLOAD_TOKEN + UPLOAD_URL 或 GITHUB_TOKEN + GITHUB_REPOSITORY, 请检查环境变量!");
if (!r2.r2Configured() && !(GITHUB_TOKEN && utils.strExists(GITHUB_REPOSITORY, "/"))) {
console.error("发布需要 R2_* 或 GITHUB_TOKEN + GITHUB_REPOSITORY, 请检查环境变量!");
process.exit()
}
}

125
electron/lib/mcp.js vendored
View File

@@ -4,7 +4,7 @@
* DooTask 的 Electron 客户端集成了 Model Context Protocol (MCP) 服务,
* 允许 AI 助手(如 Claude)直接与 DooTask 任务进行交互。
*
* 提供的工具(共 27 个):
* 提供的工具(共 29 个):
*
* === 用户管理 ===
* - get_users_basic - 批量获取用户基础信息1-50个便于匹配负责人/协助人
@@ -43,6 +43,7 @@
* === 消息通知 ===
* - search_dialogs - 按名称搜索群聊或联系人,返回 dialog_id/userid
* - send_message - 发送消息到对话(支持 dialog_id 或 userid
* - send_task_ai_message - 以AI助手身份发送消息到任务对话支持自定义发送者昵称
* - get_message_list - 获取对话消息记录(支持 dialog_id 或 userid
*
* === 智能搜索 ===
@@ -228,7 +229,7 @@ class DooTaskMCP {
return { error: 'Result contains non-serializable data' };
}
} catch (error) {
return { error: error.msg || error.message || String(error) || 'API request failed' };
return { error: error.msg || error.message || String(error) || 'API request failed', ret: error.ret, data: error.data };
}
})()
`);
@@ -242,6 +243,10 @@ class DooTaskMCP {
const result = await Promise.race([executePromise, timeoutPromise]);
if (result && result.error) {
// 多结束/开始状态(-4005/-4006):保留 ret 与 flow_items 交给工具处理,不直接抛错
if (result.ret === -4005 || result.ret === -4006) {
return result;
}
throw new Error(result.error);
}
@@ -591,14 +596,38 @@ class DooTaskMCP {
task_id: z.number()
.min(1)
.describe('要标记完成的任务ID'),
flow_item_id: z.number()
.optional()
.describe('工作流状态ID'),
}),
execute: async (params) => {
const now = new Date().toISOString().slice(0, 19).replace('T', ' ');
const result = await this.request('POST', 'project/task/update', {
const requestData = {
task_id: params.task_id,
complete_at: now,
});
};
if (params.flow_item_id) {
requestData.flow_item_id = params.flow_item_id;
}
const result = await this.request('POST', 'project/task/update', requestData);
// 处理多结束状态的情况
if (result.ret === -4005) {
const flowItems = result.data?.flow_items || [];
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
message: '存在多个结束状态请选择要使用的状态后重新调用此工具并指定flow_item_id参数',
task_id: params.task_id,
flow_items: flowItems,
}, null, 2)
}]
};
}
if (result.error) {
throw new Error(result.error);
@@ -720,6 +749,9 @@ class DooTaskMCP {
complete_at: z.union([z.string(), z.boolean()])
.optional()
.describe('完成时间。传时间字符串标记完成传false标记未完成'),
flow_item_id: z.number()
.optional()
.describe('工作流状态ID'),
}),
execute: async (params) => {
const requestData = {
@@ -734,9 +766,42 @@ class DooTaskMCP {
if (params.start_at !== undefined) requestData.start_at = params.start_at;
if (params.end_at !== undefined) requestData.end_at = params.end_at;
if (params.complete_at !== undefined) requestData.complete_at = params.complete_at;
if (params.flow_item_id !== undefined) requestData.flow_item_id = params.flow_item_id;
const result = await this.request('POST', 'project/task/update', requestData);
// 处理多结束状态的情况(标记完成时)
if (result.ret === -4005) {
const flowItems = result.data?.flow_items || [];
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
message: '存在多个结束状态请选择要使用的状态后重新调用此工具并指定flow_item_id参数',
task_id: params.task_id,
flow_items: flowItems,
}, null, 2)
}]
};
}
// 处理多开始状态的情况(取消完成时)
if (result.ret === -4006) {
const flowItems = result.data?.flow_items || [];
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
message: '存在多个开始状态请选择要使用的状态后重新调用此工具并指定flow_item_id参数',
task_id: params.task_id,
flow_items: flowItems,
}, null, 2)
}]
};
}
if (result.error) {
throw new Error(result.error);
}
@@ -1291,6 +1356,58 @@ class DooTaskMCP {
}
});
// 以AI助手身份发送消息到任务对话
this.mcp.addTool({
name: 'send_task_ai_message',
description: '以AI助手身份发送消息到任务对话。应在每个重要里程碑、遇到阻塞、以及全部完成时主动调用。',
parameters: z.object({
task_id: z.number()
.describe('目标任务ID'),
text: z.string()
.min(1)
.describe('消息内容,支持 Markdown'),
nickname: z.string()
.max(20)
.optional()
.describe('自定义发送者昵称最多20字不传或留空时默认显示“AI 助手”'),
silence: z.boolean()
.optional()
.describe('静默发送,不触发推送提醒'),
}),
execute: async (params) => {
const payload = {
task_id: params.task_id,
text: params.text,
text_type: 'md',
};
if (params.nickname !== undefined) {
payload.nickname = params.nickname;
}
if (params.silence !== undefined) {
payload.silence = params.silence ? 'yes' : 'no';
}
const result = await this.request('POST', 'dialog/msg/send_ai_assistant', payload);
if (result.error) {
throw new Error(result.error);
}
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
task_id: params.task_id,
message: result.data,
}, null, 2)
}]
};
}
});
// 获取对话消息列表
this.mcp.addTool({
name: 'get_message_list',

133
electron/lib/r2.js vendored Normal file
View File

@@ -0,0 +1,133 @@
const fs = require('fs');
const {
S3Client,
PutObjectCommand,
GetObjectCommand,
CopyObjectCommand,
DeleteObjectsCommand,
ListObjectsV2Command,
} = require('@aws-sdk/client-s3');
const { Upload } = require('@aws-sdk/lib-storage');
const {
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
R2_ENDPOINT,
R2_BUCKET,
R2_PUBLIC_URL,
} = process.env;
function r2Configured() {
return !!(R2_ACCESS_KEY_ID && R2_SECRET_ACCESS_KEY && R2_ENDPOINT && R2_BUCKET);
}
function createR2Client() {
return new S3Client({
region: 'auto',
endpoint: R2_ENDPOINT,
credentials: {
accessKeyId: R2_ACCESS_KEY_ID,
secretAccessKey: R2_SECRET_ACCESS_KEY,
},
});
}
function contentTypeFor(name) {
if (/\.ya?ml$/i.test(name)) return 'text/yaml';
if (/\.json$/i.test(name)) return 'application/json';
if (/\.md$/i.test(name)) return 'text/markdown; charset=utf-8';
return 'application/octet-stream';
}
/** 流式上传本地文件onProgress(loaded, total) */
async function uploadFile(client, localFile, key, onProgress) {
const total = fs.statSync(localFile).size;
const upload = new Upload({
client,
params: {
Bucket: R2_BUCKET,
Key: key,
Body: fs.createReadStream(localFile),
ContentType: contentTypeFor(key),
},
});
if (onProgress) {
upload.on('httpUploadProgress', (p) => onProgress(p.loaded || 0, total));
}
await upload.done();
}
/** 写入文本对象 */
async function putText(client, key, text) {
await client.send(new PutObjectCommand({
Bucket: R2_BUCKET,
Key: key,
Body: text,
ContentType: contentTypeFor(key),
}));
}
/** 读取文本对象,不存在返回 null */
async function getText(client, key) {
try {
const res = await client.send(new GetObjectCommand({ Bucket: R2_BUCKET, Key: key }));
return await res.Body.transformToString();
} catch (err) {
if (err.name === 'NoSuchKey' || err.$metadata?.httpStatusCode === 404) return null;
throw err;
}
}
/** 桶内服务端复制(文件名为安全 ASCII无需额外编码 */
async function copyObject(client, srcKey, destKey) {
await client.send(new CopyObjectCommand({
Bucket: R2_BUCKET,
CopySource: `${R2_BUCKET}/${srcKey}`,
Key: destKey,
ContentType: contentTypeFor(destKey),
MetadataDirective: 'REPLACE',
}));
}
/** 列举 keydelimiter='/' 时仅返回该前缀下的根层对象(子目录归 CommonPrefixes不返回 */
async function listKeys(client, prefix, delimiter) {
const keys = [];
let token;
do {
const res = await client.send(new ListObjectsV2Command({
Bucket: R2_BUCKET,
Prefix: prefix,
Delimiter: delimiter,
ContinuationToken: token,
}));
for (const o of res.Contents || []) keys.push(o.Key);
token = res.IsTruncated ? res.NextContinuationToken : undefined;
} while (token);
return keys;
}
/** 批量删除(每批 1000 */
async function deleteKeys(client, keys) {
for (let i = 0; i < keys.length; i += 1000) {
const batch = keys.slice(i, i + 1000);
if (!batch.length) continue;
await client.send(new DeleteObjectsCommand({
Bucket: R2_BUCKET,
Delete: { Objects: batch.map((Key) => ({ Key })) },
}));
}
}
module.exports = {
r2Configured,
createR2Client,
contentTypeFor,
uploadFile,
putText,
getText,
copyObject,
listKeys,
deleteKeys,
R2_BUCKET,
R2_PUBLIC_URL,
};

46
electron/lib/release-index.js vendored Normal file
View File

@@ -0,0 +1,46 @@
// 仅这些扩展名进入下载索引(排除 .zipmac 自动更新增量包,非下载按钮目标)
const DOWNLOAD_EXTS = ['.dmg', '.exe', '.msi', '.appimage', '.deb', '.rpm', '.apk', '.pkg'];
/**
* 从文件名解析 platform/arch与官网 storage.ts 规则保持一致)
* @returns {{platform: string, arch: string|null}|null}
*/
function parseFilename(filename) {
const lower = filename.toLowerCase();
if (lower.endsWith('.apk')) {
return { platform: 'android', arch: null };
}
if (!DOWNLOAD_EXTS.some((ext) => lower.endsWith(ext))) {
return null;
}
let platform = null;
if (/-mac-/i.test(filename) || lower.endsWith('.dmg') || lower.endsWith('.pkg')) {
platform = 'mac';
} else if (/-win-/i.test(filename) || /-win\./i.test(filename) || lower.endsWith('.msi')) {
platform = 'win';
} else if (/-linux-/i.test(filename) || lower.endsWith('.appimage') || lower.endsWith('.deb') || lower.endsWith('.rpm')) {
platform = 'linux';
}
if (!platform) return null;
let arch = null;
if (/-arm64[.-]/i.test(filename)) arch = 'arm64';
else if (/-x64[.-]/i.test(filename)) arch = 'x64';
return { platform, arch };
}
/**
* 生成下载索引:{ "<platform>": { "<arch|default>": filename } }
*/
function buildReleaseIndex(filenames) {
const index = {};
for (const filename of filenames) {
const parsed = parseFilename(filename);
if (!parsed) continue;
const archKey = parsed.arch || 'default';
index[parsed.platform] = index[parsed.platform] || {};
index[parsed.platform][archKey] = filename;
}
return index;
}
module.exports = { parseFilename, buildReleaseIndex, DOWNLOAD_EXTS };

36
electron/lib/release-index.test.js vendored Normal file
View File

@@ -0,0 +1,36 @@
const test = require('node:test');
const assert = require('node:assert');
const { parseFilename, buildReleaseIndex } = require('./release-index');
test('parseFilename: win exe x64', () => {
assert.deepStrictEqual(parseFilename('DooTask-v1.7.56-win-x64.exe'), { platform: 'win', arch: 'x64' });
});
test('parseFilename: mac dmg arm64', () => {
assert.deepStrictEqual(parseFilename('DooTask-v1.7.56-mac-arm64.dmg'), { platform: 'mac', arch: 'arm64' });
});
test('parseFilename: android apk has null arch', () => {
assert.deepStrictEqual(parseFilename('app-release.apk'), { platform: 'android', arch: null });
});
test('parseFilename: ignores yml/blockmap/zip', () => {
assert.strictEqual(parseFilename('latest.yml'), null);
assert.strictEqual(parseFilename('DooTask-v1.7.56-win-x64.exe.blockmap'), null);
assert.strictEqual(parseFilename('DooTask-v1.7.56-mac-arm64.zip'), null);
});
test('buildReleaseIndex: groups by platform/arch, .zip never overwrites .dmg', () => {
const index = buildReleaseIndex([
'DooTask-v1.7.56-mac-arm64.dmg',
'DooTask-v1.7.56-mac-arm64.zip',
'DooTask-v1.7.56-win-x64.exe',
'latest.yml',
'app-release.apk',
]);
assert.deepStrictEqual(index, {
mac: { arm64: 'DooTask-v1.7.56-mac-arm64.dmg' },
win: { x64: 'DooTask-v1.7.56-win-x64.exe' },
android: { default: 'app-release.apk' },
});
});

View File

@@ -42,6 +42,8 @@
"ora": "^4.1.1"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.1052.0",
"@aws-sdk/lib-storage": "^3.1052.0",
"@dootask/electron-dl": "^4.0.0-rc.2",
"axios": "^1.11.0",
"crc": "^3.8.0",
@@ -60,8 +62,8 @@
"request": "^2.88.2",
"tar": "^7.4.3",
"turndown": "^7.2.2",
"zod": "^3.23.8",
"yauzl": "^3.2.0"
"yauzl": "^3.2.0",
"zod": "^3.23.8"
},
"trayIcon": {
"dev": {

View File

@@ -976,3 +976,21 @@ LDAP 用户缺少邮箱属性,请联系管理员配置
负责人不能任命为项目管理员
普通成员不能移出群主或群管理员
只有群主、群管理员或邀请人可以移出成员
仅群主、项目/任务负责人或系统管理员可设置或取消他人待办
请选择文件
仅支持 xls/xlsx/csv 文件
文件中没有可导入的数据
导入完成
昵称需为2-20个字
邮箱、昵称、初始密码均为必填
邮箱格式不正确
文件内邮箱重复
单次最多导入500条
没有可导入的数据
解析完成
请选择成员
待办提醒
你有一条待办到提醒时间啦
发送者昵称最多不能超过20字
AI 助手

View File

@@ -1668,6 +1668,12 @@ WiFi签到延迟时长为±1分钟。
你确定将【(*)】设为管理员吗?
你确定取消【(*)】管理员身份吗?
你确定将【(*)】的邮箱标记为已认证吗?
你确定将【(*)】的邮箱标记为未认证吗?
标记邮箱为已认证
标记邮箱为未认证
标记选中(*)项为已认证
标记选中(*)项为未认证
你确定要取消任务时间吗?
更新子任务
@@ -2389,7 +2395,48 @@ AI任务分析
当前为负责人视角:你可查看项目和任务,并参与讨论,但不能编辑项目或任务。
选择项目管理员
即将移除
当前为负责人,并参与讨论,但不能编辑任务。
当前为负责人视角,并参与讨论,但不能编辑任务。
部门负责人视角
开启后,部门负责人/部门管理员可只读查看本部门及下级部门成员参与的项目和项目内全部任务。
部门管理员同步失败
待办设置权限
允许:所有成员可设置/取消他人待办。
禁止:仅本人、系统管理员、群主(含群管理员)、项目负责人(含项目管理员)、任务负责人可设置/取消待办。
批量导入用户
请按模板填写后上传,列顺序:邮箱、昵称、初始密码、职位(选填)单次最多导入500条。
下载模板
导入结果:共(*)条,成功(*)条,失败(*)条
行号
失败原因
导入失败
仅支持 xls/xlsx/csv 文件
创建用户
批量导入
初始密码
请输入邮箱
请输入初始密码
员工首次登录需修改密码
邮箱、昵称、初始密码均为必填
员工下次登录需修改密码
重新选择文件
共(*)条 · 可导入(*)条 · 错误(*)条
点击查看明文
原因
可导入
错误
确定导入(*)条
解析失败
设置部门到选中(*)项
提醒时间
不提醒
1 小时后
今晚 20:00
明早 9:00
成功导入(*)条
标记完成
暂无待办
暂无完成
取消提醒
确定取消该成员的提醒时间吗?

View File

@@ -34247,18 +34247,6 @@
"id": "Akan segera dihapus",
"ru": "Будет удалено"
},
{
"key": "当前为负责人,并参与讨论,但不能编辑任务。",
"zh": "",
"zh-CHT": "目前為負責人,並參與討論,但不能編輯任務。",
"en": "Currently a manager and participates in discussions, but cannot edit tasks.",
"ko": "현재 책임자이며 토론에 참여하지만, 작업을 편집할 수는 없습니다.",
"ja": "現在は責任者としてディスカッションに参加できますが、タスクを編集することはできません。",
"de": "Derzeit Verantwortlicher und an Diskussionen beteiligt, kann jedoch keine Aufgaben bearbeiten.",
"fr": "Actuellement responsable et participant aux discussions, mais ne peut pas modifier les tâches.",
"id": "Saat ini sebagai penanggung jawab dan berpartisipasi dalam diskusi, tetapi tidak dapat mengedit tugas.",
"ru": "В настоящее время является ответственным и участвует в обсуждениях, но не может редактировать задачи."
},
{
"key": "部门负责人视角",
"zh": "",
@@ -34558,5 +34546,713 @@
"fr": "Seuls le propriétaire du groupe, les administrateurs du groupe ou linvitant peuvent retirer des membres",
"id": "Hanya pemilik grup, administrator grup, atau pengundang yang dapat mengeluarkan anggota",
"ru": "Только владелец группы, администраторы группы или пригласивший пользователь могут удалять участников"
},
{
"key": "当前为负责人视角,并参与讨论,但不能编辑任务。",
"zh": "",
"zh-CHT": "目前為負責人視角,並參與討論,但不能編輯任務。",
"en": "Currently in the assignee view, participating in discussions, but cannot edit tasks.",
"ko": "현재 담당자 관점이며 토론에 참여하고 있지만 작업을 편집할 수 없습니다.",
"ja": "現在は担当者視点で、ディスカッションに参加していますが、タスクは編集できません。",
"de": "Derzeit in der Verantwortlichenansicht und an Diskussionen beteiligt, kann jedoch Aufgaben nicht bearbeiten.",
"fr": "Actuellement en vue du responsable et participant aux discussions, mais ne peut pas modifier les tâches.",
"id": "Saat ini dalam tampilan penanggung jawab dan ikut berdiskusi, tetapi tidak dapat mengedit tugas.",
"ru": "Сейчас отображается представление ответственного лица; участие в обсуждении возможно, но редактировать задачи нельзя."
},
{
"key": "待办设置权限",
"zh": "",
"zh-CHT": "待辦設定權限",
"en": "To-do Setting Permission",
"ko": "할 일 설정 권한",
"ja": "ToDo設定権限",
"de": "Berechtigung zum Festlegen von Aufgaben",
"fr": "Autorisation de configuration des tâches à faire",
"id": "Izin Pengaturan Tugas",
"ru": "Разрешение на настройку задач"
},
{
"key": "允许:所有成员可设置\/取消他人待办。",
"zh": "",
"zh-CHT": "允許:所有成員可設定\/取消他人待辦。",
"en": "Allow: All members can set\/cancel to-dos for others.",
"ko": "허용: 모든 멤버가 다른 사람의 할 일을 설정\/취소할 수 있습니다.",
"ja": "許可すべてのメンバーが他人のToDoを設定\/取消できます。",
"de": "Zulassen: Alle Mitglieder können Aufgaben für andere festlegen\/abbrechen.",
"fr": "Autoriser : tous les membres peuvent définir\/annuler les tâches à faire des autres.",
"id": "Izinkan: Semua anggota dapat mengatur\/membatalkan tugas orang lain.",
"ru": "Разрешить: все участники могут создавать\/отменять задачи для других."
},
{
"key": "批量导入用户",
"zh": "",
"zh-CHT": "批次匯入使用者",
"en": "Batch Import Users",
"ko": "사용자 일괄 가져오기",
"ja": "ユーザーの一括インポート",
"de": "Benutzer im Batch importieren",
"fr": "Importer des utilisateurs par lot",
"id": "Impor Pengguna Secara Massal",
"ru": "Пакетный импорт пользователей"
},
{
"key": "请按模板填写后上传,列顺序:邮箱、昵称、初始密码、职位(选填)单次最多导入500条。",
"zh": "",
"zh-CHT": "請按範本填寫後上傳欄位順序電子郵件、暱稱、初始密碼、職位選填單次最多匯入500筆。",
"en": "Please fill in the template and upload it. Column order: Email, Nickname, Initial Password, Position (optional); up to 500 records can be imported at a time.",
"ko": "템플릿에 맞게 작성한 후 업로드하세요. 열 순서: 이메일, 닉네임, 초기 비밀번호, 직위(선택); 한 번에 최대 500건까지 가져올 수 있습니다.",
"ja": "テンプレートに従って入力してからアップロードしてください。列の順序メールアドレス、ニックネーム、初期パスワード、役職任意。一度に最大500件までインポートできます。",
"de": "Bitte füllen Sie die Vorlage aus und laden Sie sie hoch. Spaltenreihenfolge: E-Mail, Nickname, Initialpasswort, Position (optional); pro Import können maximal 500 Datensätze importiert werden.",
"fr": "Veuillez remplir le modèle puis le téléverser. Ordre des colonnes : e-mail, surnom, mot de passe initial, poste (facultatif) ; jusquà 500 enregistrements peuvent être importés à la fois.",
"id": "Silakan isi sesuai templat lalu unggah. Urutan kolom: Email, Nama Panggilan, Kata Sandi Awal, Posisi (opsional); maksimal 500 data dapat diimpor setiap kali.",
"ru": "Заполните шаблон и загрузите его. Порядок столбцов: электронная почта, никнейм, начальный пароль, должность (необязательно); за один раз можно импортировать до 500 записей."
},
{
"key": "下载模板",
"zh": "",
"zh-CHT": "下載範本",
"en": "Download Template",
"ko": "템플릿 다운로드",
"ja": "テンプレートをダウンロード",
"de": "Vorlage herunterladen",
"fr": "Télécharger le modèle",
"id": "Unduh Templat",
"ru": "Скачать шаблон"
},
{
"key": "导入结果:共(%T1)条,成功(%T2)条,失败(%T3)条",
"zh": "",
"zh-CHT": "匯入結果:共(%T1)筆,成功(%T2)筆,失敗(%T3)筆",
"en": "Import result: Total (%T1), Successful (%T2), Failed (%T3)",
"ko": "가져오기 결과: 총 (%T1)건, 성공 (%T2)건, 실패 (%T3)건",
"ja": "インポート結果:合計(%T1)件、成功(%T2)件、失敗(%T3)件",
"de": "Importergebnis: Insgesamt (%T1), erfolgreich (%T2), fehlgeschlagen (%T3)",
"fr": "Résultat de limportation : total (%T1), réussis (%T2), échoués (%T3)",
"id": "Hasil impor: Total (%T1), Berhasil (%T2), Gagal (%T3)",
"ru": "Результат импорта: всего (%T1), успешно (%T2), неудачно (%T3)"
},
{
"key": "行号",
"zh": "",
"zh-CHT": "列號",
"en": "Row No.",
"ko": "행 번호",
"ja": "行番号",
"de": "Zeilennummer",
"fr": "N° de ligne",
"id": "No. Baris",
"ru": "Номер строки"
},
{
"key": "失败原因",
"zh": "",
"zh-CHT": "失敗原因",
"en": "Failure Reason",
"ko": "실패 사유",
"ja": "失敗理由",
"de": "Fehlergrund",
"fr": "Raison de léchec",
"id": "Alasan Kegagalan",
"ru": "Причина ошибки"
},
{
"key": "导入失败",
"zh": "",
"zh-CHT": "匯入失敗",
"en": "Import Failed",
"ko": "가져오기 실패",
"ja": "インポートに失敗しました",
"de": "Import fehlgeschlagen",
"fr": "Échec de limportation",
"id": "Impor Gagal",
"ru": "Ошибка импорта"
},
{
"key": "仅支持 xls\/xlsx\/csv 文件",
"zh": "",
"zh-CHT": "僅支援 xls\/xlsx\/csv 檔案",
"en": "Only xls\/xlsx\/csv files are supported",
"ko": "xls\/xlsx\/csv 파일만 지원됩니다",
"ja": "xls\/xlsx\/csv ファイルのみ対応しています",
"de": "Es werden nur xls\/xlsx\/csv-Dateien unterstützt",
"fr": "Seuls les fichiers xls\/xlsx\/csv sont pris en charge",
"id": "Hanya file xls\/xlsx\/csv yang didukung",
"ru": "Поддерживаются только файлы xls\/xlsx\/csv"
},
{
"key": "创建用户",
"zh": "",
"zh-CHT": "建立使用者",
"en": "Create User",
"ko": "사용자 생성",
"ja": "ユーザーを作成",
"de": "Benutzer erstellen",
"fr": "Créer un utilisateur",
"id": "Buat Pengguna",
"ru": "Создать пользователя"
},
{
"key": "批量导入",
"zh": "",
"zh-CHT": "批次匯入",
"en": "Bulk Import",
"ko": "일괄 가져오기",
"ja": "一括インポート",
"de": "Massenimport",
"fr": "Importation en masse",
"id": "Impor Massal",
"ru": "Массовый импорт"
},
{
"key": "初始密码",
"zh": "",
"zh-CHT": "初始密碼",
"en": "Initial Password",
"ko": "초기 비밀번호",
"ja": "初期パスワード",
"de": "Initiales Passwort",
"fr": "Mot de passe initial",
"id": "Kata Sandi Awal",
"ru": "Начальный пароль"
},
{
"key": "请输入邮箱",
"zh": "",
"zh-CHT": "請輸入電子郵件",
"en": "Please enter an email address",
"ko": "이메일을 입력하세요",
"ja": "メールアドレスを入力してください",
"de": "Bitte geben Sie eine E-Mail-Adresse ein",
"fr": "Veuillez saisir une adresse e-mail",
"id": "Silakan masukkan email",
"ru": "Введите адрес электронной почты"
},
{
"key": "请输入初始密码",
"zh": "",
"zh-CHT": "請輸入初始密碼",
"en": "Please enter the initial password",
"ko": "초기 비밀번호를 입력하세요",
"ja": "初期パスワードを入力してください",
"de": "Bitte geben Sie das initiale Passwort ein",
"fr": "Veuillez saisir le mot de passe initial",
"id": "Silakan masukkan kata sandi awal",
"ru": "Введите начальный пароль"
},
{
"key": "员工首次登录需修改密码",
"zh": "",
"zh-CHT": "員工首次登入需修改密碼",
"en": "Employees must change their password upon first login",
"ko": "직원은 최초 로그인 시 비밀번호를 변경해야 합니다",
"ja": "従業員は初回ログイン時にパスワードを変更する必要があります",
"de": "Mitarbeiter müssen ihr Passwort bei der ersten Anmeldung ändern",
"fr": "Les employés doivent modifier leur mot de passe lors de leur première connexion",
"id": "Karyawan harus mengubah kata sandi saat login pertama kali",
"ru": "Сотрудники должны изменить пароль при первом входе"
},
{
"key": "邮箱、昵称、初始密码均为必填",
"zh": "",
"zh-CHT": "電子郵件、暱稱、初始密碼均為必填",
"en": "Email, nickname, and initial password are all required",
"ko": "이메일, 닉네임, 초기 비밀번호는 모두 필수입니다",
"ja": "メールアドレス、ニックネーム、初期パスワードはすべて必須です",
"de": "E-Mail, Spitzname und initiales Passwort sind Pflichtfelder",
"fr": "Ladresse e-mail, le pseudo et le mot de passe initial sont obligatoires",
"id": "Email, nama panggilan, dan kata sandi awal wajib diisi",
"ru": "Электронная почта, никнейм и начальный пароль обязательны для заполнения"
},
{
"key": "员工下次登录需修改密码",
"zh": "",
"zh-CHT": "員工下次登入需修改密碼",
"en": "Employees must change their password at the next login",
"ko": "직원은 다음 로그인 시 비밀번호를 변경해야 합니다",
"ja": "従業員は次回ログイン時にパスワードを変更する必要があります",
"de": "Mitarbeiter müssen ihr Passwort bei der nächsten Anmeldung ändern",
"fr": "Les employés doivent modifier leur mot de passe lors de leur prochaine connexion",
"id": "Karyawan harus mengubah kata sandi saat login berikutnya",
"ru": "Сотрудники должны изменить пароль при следующем входе"
},
{
"key": "重新选择文件",
"zh": "",
"zh-CHT": "重新選擇檔案",
"en": "Reselect File",
"ko": "파일 다시 선택",
"ja": "ファイルを再選択",
"de": "Datei erneut auswählen",
"fr": "Sélectionner à nouveau le fichier",
"id": "Pilih Ulang File",
"ru": "Выбрать файл повторно"
},
{
"key": "共(%T1)条 · 可导入(%T2)条 · 错误(%T3)条",
"zh": "",
"zh-CHT": "共(%T1)條 · 可匯入(%T2)條 · 錯誤(%T3)條",
"en": "Total (%T1) · Importable (%T2) · Errors (%T3)",
"ko": "총 (%T1)개 · 가져오기 가능 (%T2)개 · 오류 (%T3)개",
"ja": "合計(%T1)件 · インポート可能(%T2)件 · エラー(%T3)件",
"de": "Gesamt (%T1) · Importierbar (%T2) · Fehler (%T3)",
"fr": "Total (%T1) · Importables (%T2) · Erreurs (%T3)",
"id": "Total (%T1) · Dapat diimpor (%T2) · Kesalahan (%T3)",
"ru": "Всего (%T1) · Можно импортировать (%T2) · Ошибок (%T3)"
},
{
"key": "点击查看明文",
"zh": "",
"zh-CHT": "點擊查看明文",
"en": "Click to view plain text",
"ko": "클릭하여 일반 텍스트 보기",
"ja": "クリックして平文を表示",
"de": "Klicken, um Klartext anzuzeigen",
"fr": "Cliquez pour afficher le texte en clair",
"id": "Klik untuk melihat teks biasa",
"ru": "Нажмите, чтобы просмотреть открытый текст"
},
{
"key": "原因",
"zh": "",
"zh-CHT": "原因",
"en": "Reason",
"ko": "사유",
"ja": "理由",
"de": "Grund",
"fr": "Raison",
"id": "Alasan",
"ru": "Причина"
},
{
"key": "可导入",
"zh": "",
"zh-CHT": "可匯入",
"en": "Importable",
"ko": "가져오기 가능",
"ja": "インポート可能",
"de": "Importierbar",
"fr": "Importable",
"id": "Dapat diimpor",
"ru": "Можно импортировать"
},
{
"key": "错误",
"zh": "",
"zh-CHT": "錯誤",
"en": "Error",
"ko": "오류",
"ja": "エラー",
"de": "Fehler",
"fr": "Erreur",
"id": "Kesalahan",
"ru": "Ошибка"
},
{
"key": "确定导入(%T1)条",
"zh": "",
"zh-CHT": "確定匯入(%T1)條",
"en": "Confirm import of (%T1) items",
"ko": "(%T1)개 항목을 가져오시겠습니까?",
"ja": "(%T1)件をインポートしますか?",
"de": "Import von (%T1) Elementen bestätigen",
"fr": "Confirmer limportation de (%T1) éléments",
"id": "Konfirmasi impor (%T1) item",
"ru": "Подтвердить импорт (%T1) элементов"
},
{
"key": "解析失败",
"zh": "",
"zh-CHT": "解析失敗",
"en": "Parsing failed",
"ko": "解析 실패",
"ja": "解析に失敗しました",
"de": "Analyse fehlgeschlagen",
"fr": "Échec de lanalyse",
"id": "Penguraian gagal",
"ru": "Не удалось выполнить анализ"
},
{
"key": "设置部门到选中(%T1)项",
"zh": "",
"zh-CHT": "將部門設定到選中的(%T1)項",
"en": "Set department for the selected (%T1) items",
"ko": "선택한 (%T1)개 항목에 부서 설정",
"ja": "選択した(%T1)件に部署を設定",
"de": "Abteilung für die ausgewählten (%T1) Elemente festlegen",
"fr": "Définir le service pour les (%T1) éléments sélectionnés",
"id": "Atur departemen untuk (%T1) item yang dipilih",
"ru": "Установить отдел для выбранных (%T1) элементов"
},
{
"key": "提醒时间",
"zh": "",
"zh-CHT": "提醒時間",
"en": "Reminder time",
"ko": "알림 시간",
"ja": "リマインダー時間",
"de": "Erinnerungszeit",
"fr": "Heure du rappel",
"id": "Waktu pengingat",
"ru": "Время напоминания"
},
{
"key": "不提醒",
"zh": "",
"zh-CHT": "不提醒",
"en": "No reminder",
"ko": "알림 없음",
"ja": "通知しない",
"de": "Keine Erinnerung",
"fr": "Aucun rappel",
"id": "Tidak ada pengingat",
"ru": "Без напоминания"
},
{
"key": "1 小时后",
"zh": "",
"zh-CHT": "1 小時後",
"en": "In 1 hour",
"ko": "1시간 후",
"ja": "1時間後",
"de": "In 1 Stunde",
"fr": "Dans 1 heure",
"id": "Dalam 1 jam",
"ru": "Через 1 час"
},
{
"key": "今晚 20:00",
"zh": "",
"zh-CHT": "今晚 20:00",
"en": "Tonight at 20:00",
"ko": "오늘 밤 20:00",
"ja": "今夜 20:00",
"de": "Heute Abend um 20:00 Uhr",
"fr": "Ce soir à 20:00",
"id": "Malam ini pukul 20:00",
"ru": "Сегодня вечером в 20:00"
},
{
"key": "明早 9:00",
"zh": "",
"zh-CHT": "明早 9:00",
"en": "Tomorrow morning at 9:00",
"ko": "내일 아침 9:00",
"ja": "明朝 9:00",
"de": "Morgen früh um 9:00 Uhr",
"fr": "Demain matin à 9:00",
"id": "Besok pagi pukul 9:00",
"ru": "Завтра утром в 9:00"
},
{
"key": "成功导入(%T1)条",
"zh": "",
"zh-CHT": "成功匯入(%T1)筆",
"en": "Successfully imported (%T1) items",
"ko": "(%T1)개 항목을 성공적으로 가져왔습니다",
"ja": "(%T1)件を正常にインポートしました",
"de": "(%T1) Elemente erfolgreich importiert",
"fr": "(%T1) éléments importés avec succès",
"id": "Berhasil mengimpor (%T1) item",
"ru": "Успешно импортировано элементов: (%T1)"
},
{
"key": "标记完成",
"zh": "",
"zh-CHT": "標記完成",
"en": "Mark as completed",
"ko": "완료로 표시",
"ja": "完了としてマーク",
"de": "Als erledigt markieren",
"fr": "Marquer comme terminé",
"id": "Tandai sebagai selesai",
"ru": "Отметить как выполненное"
},
{
"key": "暂无待办",
"zh": "",
"zh-CHT": "暫無待辦",
"en": "No pending to-dos",
"ko": "대기 중인 할 일이 없습니다",
"ja": "保留中のToDoはありません",
"de": "Keine ausstehenden To-dos",
"fr": "Aucune tâche à faire en attente",
"id": "Tidak ada tugas tertunda",
"ru": "Нет ожидающих задач"
},
{
"key": "暂无完成",
"zh": "",
"zh-CHT": "暫無完成",
"en": "No completed items",
"ko": "완료된 항목이 없습니다",
"ja": "完了済みの項目はありません",
"de": "Keine abgeschlossenen Elemente",
"fr": "Aucun élément terminé",
"id": "Tidak ada item yang selesai",
"ru": "Нет выполненных элементов"
},
{
"key": "取消提醒",
"zh": "",
"zh-CHT": "取消提醒",
"en": "Cancel reminder",
"ko": "알림 취소",
"ja": "リマインダーをキャンセル",
"de": "Erinnerung abbrechen",
"fr": "Annuler le rappel",
"id": "Batalkan pengingat",
"ru": "Отменить напоминание"
},
{
"key": "确定取消该成员的提醒时间吗?",
"zh": "",
"zh-CHT": "確定取消該成員的提醒時間嗎?",
"en": "Are you sure you want to cancel this member's reminder time?",
"ko": "이 멤버의 알림 시간을 취소하시겠습니까?",
"ja": "このメンバーのリマインダー時刻をキャンセルしてもよろしいですか?",
"de": "Möchten Sie die Erinnerungszeit dieses Mitglieds wirklich abbrechen?",
"fr": "Voulez-vous vraiment annuler l'heure de rappel de ce membre ?",
"id": "Anda yakin ingin membatalkan waktu pengingat anggota ini?",
"ru": "Вы уверены, что хотите отменить время напоминания для этого участника?"
},
{
"key": "请选择文件",
"zh": "",
"zh-CHT": "請選擇檔案",
"en": "Please select a file",
"ko": "파일을 선택하세요",
"ja": "ファイルを選択してください",
"de": "Bitte wählen Sie eine Datei aus",
"fr": "Veuillez sélectionner un fichier",
"id": "Silakan pilih file",
"ru": "Пожалуйста, выберите файл"
},
{
"key": "文件中没有可导入的数据",
"zh": "",
"zh-CHT": "檔案中沒有可匯入的資料",
"en": "There is no data to import in the file",
"ko": "파일에 가져올 수 있는 데이터가 없습니다",
"ja": "ファイルにインポート可能なデータがありません",
"de": "Die Datei enthält keine importierbaren Daten",
"fr": "Le fichier ne contient aucune donnée importable",
"id": "Tidak ada data yang dapat diimpor dalam file",
"ru": "В файле нет данных для импорта"
},
{
"key": "导入完成",
"zh": "",
"zh-CHT": "匯入完成",
"en": "Import completed",
"ko": "가져오기가 완료되었습니다",
"ja": "インポートが完了しました",
"de": "Import abgeschlossen",
"fr": "Importation terminée",
"id": "Impor selesai",
"ru": "Импорт завершен"
},
{
"key": "昵称需为2-20个字",
"zh": "",
"zh-CHT": "暱稱需為2-20個字",
"en": "Nickname must be 220 characters",
"ko": "닉네임은 2~20자여야 합니다",
"ja": "ニックネームは220文字で入力してください",
"de": "Der Spitzname muss 220 Zeichen lang sein",
"fr": "Le pseudo doit comporter entre 2 et 20 caractères",
"id": "Nama panggilan harus terdiri dari 220 karakter",
"ru": "Псевдоним должен содержать от 2 до 20 символов"
},
{
"key": "邮箱格式不正确",
"zh": "",
"zh-CHT": "信箱格式不正確",
"en": "Invalid email format",
"ko": "이메일 형식이 올바르지 않습니다",
"ja": "メールアドレスの形式が正しくありません",
"de": "Ungültiges E-Mail-Format",
"fr": "Format de-mail invalide",
"id": "Format email tidak valid",
"ru": "Неверный формат электронной почты"
},
{
"key": "文件内邮箱重复",
"zh": "",
"zh-CHT": "檔案內信箱重複",
"en": "Duplicate email addresses in the file",
"ko": "파일 내 이메일이 중복되었습니다",
"ja": "ファイル内に重複したメールアドレスがあります",
"de": "Doppelte E-Mail-Adressen in der Datei",
"fr": "Adresses e-mail en double dans le fichier",
"id": "Email duplikat dalam file",
"ru": "В файле есть повторяющиеся адреса электронной почты"
},
{
"key": "单次最多导入500条",
"zh": "",
"zh-CHT": "單次最多匯入500筆",
"en": "A maximum of 500 records can be imported at a time",
"ko": "한 번에 최대 500개 항목까지 가져올 수 있습니다",
"ja": "一度にインポートできるのは最大500件です",
"de": "Pro Vorgang können maximal 500 Datensätze importiert werden",
"fr": "Un maximum de 500 enregistrements peut être importé à la fois",
"id": "Maksimal 500 data dapat diimpor sekaligus",
"ru": "За один раз можно импортировать не более 500 записей"
},
{
"key": "没有可导入的数据",
"zh": "",
"zh-CHT": "沒有可匯入的資料",
"en": "No data to import",
"ko": "가져올 수 있는 데이터가 없습니다",
"ja": "インポート可能なデータがありません",
"de": "Keine importierbaren Daten vorhanden",
"fr": "Aucune donnée à importer",
"id": "Tidak ada data yang dapat diimpor",
"ru": "Нет данных для импорта"
},
{
"key": "解析完成",
"zh": "",
"zh-CHT": "解析完成",
"en": "Parsing completed",
"ko": "분석이 완료되었습니다",
"ja": "解析が完了しました",
"de": "Analyse abgeschlossen",
"fr": "Analyse terminée",
"id": "Penguraian selesai",
"ru": "Анализ завершен"
},
{
"key": "待办提醒",
"zh": "",
"zh-CHT": "待辦提醒",
"en": "To-do reminder",
"ko": "할 일 알림",
"ja": "ToDoリマインダー",
"de": "Aufgabenerinnerung",
"fr": "Rappel de tâche",
"id": "Pengingat tugas",
"ru": "Напоминание о задаче"
},
{
"key": "你有一条待办到提醒时间啦",
"zh": "",
"zh-CHT": "你有一條待辦到提醒時間啦",
"en": "You have a to-do item whose reminder time has arrived",
"ko": "알림 시간이 된 할 일이 있습니다",
"ja": "リマインダー時刻になったToDoがあります",
"de": "Eine Aufgabe hat ihre Erinnerungszeit erreicht",
"fr": "Vous avez une tâche dont lheure de rappel est arrivée",
"id": "Anda memiliki satu tugas yang telah mencapai waktu pengingat",
"ru": "У вас есть задача, для которой наступило время напоминания"
},
{
"key": "你确定将【(%T1)】的邮箱标记为已认证吗?",
"zh": "",
"zh-CHT": "你確定將【(%T1)】的郵箱標記為已認證嗎?",
"en": "Are you sure you want to mark the email address of 【(%T1)】 as verified?",
"ko": "【(%T1)】의 이메일을 인증됨으로 표시하시겠습니까?",
"ja": "【(%T1)】のメールアドレスを認証済みにしますか?",
"de": "Möchten Sie die E-Mail-Adresse von 【(%T1)】 wirklich als verifiziert markieren?",
"fr": "Voulez-vous vraiment marquer ladresse e-mail de 【(%T1)】 comme vérifiée ?",
"id": "Apakah Anda yakin ingin menandai email 【(%T1)】 sebagai terverifikasi?",
"ru": "Вы уверены, что хотите отметить адрес электронной почты 【(%T1)】 как подтвержденный?"
},
{
"key": "你确定将【(%T1)】的邮箱标记为未认证吗?",
"zh": "",
"zh-CHT": "你確定將【(%T1)】的郵箱標記為未認證嗎?",
"en": "Are you sure you want to mark the email address of 【(%T1)】 as unverified?",
"ko": "【(%T1)】의 이메일을 미인증으로 표시하시겠습니까?",
"ja": "【(%T1)】のメールアドレスを未認証にしますか?",
"de": "Möchten Sie die E-Mail-Adresse von 【(%T1)】 wirklich als nicht verifiziert markieren?",
"fr": "Voulez-vous vraiment marquer ladresse e-mail de 【(%T1)】 comme non vérifiée ?",
"id": "Apakah Anda yakin ingin menandai email 【(%T1)】 sebagai belum terverifikasi?",
"ru": "Вы уверены, что хотите отметить адрес электронной почты 【(%T1)】 как неподтвержденный?"
},
{
"key": "标记邮箱为已认证",
"zh": "",
"zh-CHT": "標記郵箱為已認證",
"en": "Mark email as verified",
"ko": "이메일을 인증됨으로 표시",
"ja": "メールアドレスを認証済みにする",
"de": "E-Mail als verifiziert markieren",
"fr": "Marquer ladresse e-mail comme vérifiée",
"id": "Tandai email sebagai terverifikasi",
"ru": "Отметить адрес электронной почты как подтвержденный"
},
{
"key": "标记邮箱为未认证",
"zh": "",
"zh-CHT": "標記郵箱為未認證",
"en": "Mark email as unverified",
"ko": "이메일을 미인증으로 표시",
"ja": "メールアドレスを未認証にする",
"de": "E-Mail als nicht verifiziert markieren",
"fr": "Marquer ladresse e-mail comme non vérifiée",
"id": "Tandai email sebagai belum terverifikasi",
"ru": "Отметить адрес электронной почты как неподтвержденный"
},
{
"key": "标记选中(%T1)项为已认证",
"zh": "",
"zh-CHT": "標記選中(%T1)項為已認證",
"en": "Mark the selected (%T1) items as verified",
"ko": "선택한 (%T1)개 항목을 인증됨으로 표시",
"ja": "選択した(%T1)件を認証済みにする",
"de": "Ausgewählte (%T1) Elemente als verifiziert markieren",
"fr": "Marquer les (%T1) éléments sélectionnés comme vérifiés",
"id": "Tandai (%T1) item yang dipilih sebagai terverifikasi",
"ru": "Отметить выбранные элементы (%T1) как подтвержденные"
},
{
"key": "标记选中(%T1)项为未认证",
"zh": "",
"zh-CHT": "標記選中(%T1)項為未認證",
"en": "Mark the selected (%T1) items as unverified",
"ko": "선택한 (%T1)개 항목을 미인증으로 표시",
"ja": "選択した(%T1)件を未認証にする",
"de": "Ausgewählte (%T1) Elemente als nicht verifiziert markieren",
"fr": "Marquer les (%T1) éléments sélectionnés comme non vérifiés",
"id": "Tandai (%T1) item yang dipilih sebagai belum terverifikasi",
"ru": "Отметить выбранные элементы (%T1) как неподтвержденные"
},
{
"key": "禁止:仅本人、系统管理员、群主(含群管理员)、项目负责人(含项目管理员)、任务负责人可设置\/取消待办。",
"zh": "",
"zh-CHT": "禁止:僅本人、系統管理員、群主(含群管理員)、專案負責人(含專案管理員)、任務負責人可設定\/取消待辦。",
"en": "Prohibited: Only the user themself, system administrators, group owners (including group administrators), project owners (including project administrators), and task owners can set\/cancel to-dos.",
"ko": "금지: 본인, 시스템 관리자, 그룹 소유자(그룹 관리자 포함), 프로젝트 책임자(프로젝트 관리자 포함), 작업 책임자만 할 일을 설정\/취소할 수 있습니다.",
"ja": "禁止本人、システム管理者、グループオーナーグループ管理者を含む、プロジェクト責任者プロジェクト管理者を含む、タスク責任者のみがToDoを設定\/取消できます。",
"de": "Unzulässig: Nur die Person selbst, Systemadministratoren, Gruppenbesitzer (einschließlich Gruppenadministratoren), Projektverantwortliche (einschließlich Projektadministratoren) und Aufgabenverantwortliche können To-dos festlegen\/abbrechen.",
"fr": "Interdit : seuls lutilisateur lui-même, les administrateurs système, les propriétaires de groupe (y compris les administrateurs de groupe), les responsables de projet (y compris les administrateurs de projet) et les responsables de tâche peuvent définir\/annuler des tâches à faire.",
"id": "Dilarang: Hanya pengguna itu sendiri, administrator sistem, pemilik grup (termasuk administrator grup), penanggung jawab proyek (termasuk administrator proyek), dan penanggung jawab tugas yang dapat mengatur\/membatalkan to-do.",
"ru": "Запрещено: только сам пользователь, системные администраторы, владельцы групп (включая администраторов групп), ответственные за проект (включая администраторов проекта) и ответственные за задачу могут устанавливать\/отменять задачи."
},
{
"key": "仅群主、项目\/任务负责人或系统管理员可设置或取消他人待办",
"zh": "",
"zh-CHT": "僅群主、專案\/任務負責人或系統管理員可設定或取消他人待辦",
"en": "Only group owners, project\/task owners, or system administrators can set or cancel others' to-dos",
"ko": "그룹 소유자, 프로젝트\/작업 책임자 또는 시스템 관리자만 다른 사람의 할 일을 설정하거나 취소할 수 있습니다.",
"ja": "グループオーナー、プロジェクト\/タスク責任者、またはシステム管理者のみが他人のToDoを設定または取消できます",
"de": "Nur Gruppenbesitzer, Projekt-\/Aufgabenverantwortliche oder Systemadministratoren können To-dos anderer festlegen oder abbrechen",
"fr": "Seuls les propriétaires de groupe, les responsables de projet\/tâche ou les administrateurs système peuvent définir ou annuler les tâches à faire dautres personnes",
"id": "Hanya pemilik grup, penanggung jawab proyek\/tugas, atau administrator sistem yang dapat mengatur atau membatalkan to-do orang lain",
"ru": "Только владельцы групп, ответственные за проект\/задачу или системные администраторы могут устанавливать или отменять задачи других пользователей"
},
{
"key": "发送者昵称最多不能超过20字",
"zh": "",
"zh-CHT": "發送者暱稱最多不能超過20字",
"en": "The sender nickname cannot exceed 20 characters",
"ko": "발신자 닉네임은 최대 20자를 초과할 수 없습니다.",
"ja": "送信者のニックネームは20文字を超えることはできません",
"de": "Der Spitzname des Absenders darf höchstens 20 Zeichen lang sein",
"fr": "Le pseudonyme de lexpéditeur ne peut pas dépasser 20 caractères",
"id": "Nama panggilan pengirim tidak boleh melebihi 20 karakter",
"ru": "Псевдоним отправителя не может превышать 20 символов"
}
]

View File

@@ -1,7 +1,7 @@
{
"name": "DooTask",
"version": "1.7.55",
"codeVerson": 233,
"version": "1.7.81",
"codeVerson": 236,
"description": "DooTask is task management system.",
"scripts": {
"start": "./cmd dev",

View File

@@ -1 +1 @@
import{n as m}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var t=this,r=t.$createElement;return t._self._c,t._m(0)},e=[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 _=m(s,p,e,!1,n,"7d7154a8",null,null);function n(t){for(let r in o)this[r]=o[r]}var it=function(){return _.exports}();export{it as default};
import{n as m}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var t=this,r=t.$createElement;return t._self._c,t._m(0)},e=[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 _=m(s,p,e,!1,n,"7d7154a8",null,null);function n(t){for(let r in o)this[r]=o[r]}var it=function(){return _.exports}();export{it 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

View File

@@ -0,0 +1 @@
.checkin-field .ivu-form-item-label{color:#f90;font-weight:500}.checkin-mac-header[data-v-eb58f07c]{margin-bottom:8px;font-weight:500;color:#606266}.checkin-mac-item[data-v-eb58f07c]{margin-bottom:8px}.checkin-mac-item .ivu-col[data-v-eb58f07c]{padding-right:8px}.checkin-mac-item .ivu-col[data-v-eb58f07c]:last-child{padding-right:0}.checkin-mac-del[data-v-eb58f07c]{display:flex;align-items:center;justify-content:center;cursor:pointer;color:red}.checkin-mac-del[data-v-eb58f07c]:hover{opacity:.8}.form-tip[data-v-eb58f07c]{font-size:12px;color:#999;margin-top:4px}.user-tags-preview[data-v-eb58f07c]{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-height:32px}.user-tags-preview .tag-pill[data-v-eb58f07c]{cursor:pointer;padding:6px 12px;border-radius:12px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:#f5f5f5;color:#606266;line-height:14px;height:26px;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.user-tags-preview .tag-pill.is-recognized[data-v-eb58f07c]{color:#67c23a}.user-tags-preview .tag-pill span[data-v-eb58f07c]{padding-left:8px;position:relative}.user-tags-preview .tag-pill span[data-v-eb58f07c]:before{content:"";position:absolute;left:2px;top:50%;transform:translateY(-50%);width:2px;height:2px;border-radius:50%;background-color:currentColor}.user-tags-preview .tags-empty[data-v-eb58f07c]{color:#909399}.user-tags-preview .tags-total[data-v-eb58f07c]{color:#909399;font-size:12px}.user-tags-preview .manage-button[data-v-eb58f07c]{margin-left:auto;display:inline-flex;align-items:center;gap:4px}.import-user-modal .import-tip[data-v-9d8f7ae8]{color:#808695;margin-bottom:12px}.import-user-modal .import-actions[data-v-9d8f7ae8]{display:flex;gap:12px;align-items:center}.import-user-modal .import-option[data-v-9d8f7ae8]{margin-top:12px}.import-user-modal .import-batch-label[data-v-9d8f7ae8]{flex-shrink:0;min-width:64px;color:#515a6e}.import-user-modal .import-setdept[data-v-9d8f7ae8]{display:flex;align-items:center;gap:8px;margin-top:12px}.import-user-modal .import-setdept .import-setdept-select[data-v-9d8f7ae8]{width:auto}.import-user-modal .import-setverity[data-v-9d8f7ae8]{display:flex;align-items:center;gap:8px;margin-top:12px}.import-user-modal .import-preview[data-v-9d8f7ae8],.import-user-modal .import-result[data-v-9d8f7ae8]{margin-top:16px}.import-user-modal[data-v-9d8f7ae8] .ivu-table-cell{white-space:nowrap}.import-user-modal[data-v-9d8f7ae8] .pwd-cell{cursor:pointer;letter-spacing:1px;-webkit-user-select:none;-moz-user-select:none;user-select:none}.import-user-modal[data-v-9d8f7ae8] .pwd-cell:hover{color:#2d8cf0}.import-user-modal[data-v-9d8f7ae8] .import-row-error td{background-color:#fff2f0}

View File

@@ -1 +0,0 @@
.checkin-field .ivu-form-item-label{color:#f90;font-weight:500}.checkin-mac-header[data-v-39d6b3fb]{margin-bottom:8px;font-weight:500;color:#606266}.checkin-mac-item[data-v-39d6b3fb]{margin-bottom:8px}.checkin-mac-item .ivu-col[data-v-39d6b3fb]{padding-right:8px}.checkin-mac-item .ivu-col[data-v-39d6b3fb]:last-child{padding-right:0}.checkin-mac-del[data-v-39d6b3fb]{display:flex;align-items:center;justify-content:center;cursor:pointer;color:red}.checkin-mac-del[data-v-39d6b3fb]:hover{opacity:.8}.form-tip[data-v-39d6b3fb]{font-size:12px;color:#999;margin-top:4px}.user-tags-preview[data-v-39d6b3fb]{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-height:32px}.user-tags-preview .tag-pill[data-v-39d6b3fb]{cursor:pointer;padding:6px 12px;border-radius:12px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:#f5f5f5;color:#606266;line-height:14px;height:26px;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.user-tags-preview .tag-pill.is-recognized[data-v-39d6b3fb]{color:#67c23a}.user-tags-preview .tag-pill span[data-v-39d6b3fb]{padding-left:8px;position:relative}.user-tags-preview .tag-pill span[data-v-39d6b3fb]:before{content:"";position:absolute;left:2px;top:50%;transform:translateY(-50%);width:2px;height:2px;border-radius:50%;background-color:currentColor}.user-tags-preview .tags-empty[data-v-39d6b3fb]{color:#909399}.user-tags-preview .tags-total[data-v-39d6b3fb]{color:#909399;font-size:12px}.user-tags-preview .manage-button[data-v-39d6b3fb]{margin-left:auto;display:inline-flex;align-items:center;gap:4px}

View File

@@ -1 +1 @@
import{m as i}from"./vuex.cc7cb26e.js";import{n as o}from"./app.155cfddd.js";var d=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("Modal",{attrs:{value:t.value,title:t.$L("\u8D1F\u8D23\u4EBA\u89C6\u89D2"),"mask-closable":!1,width:"520"},on:{input:function(s){return t.$emit("input",s)}}},[e("div",{staticClass:"department-owner-view-modal"},[e("Alert",{attrs:{type:"info","show-icon":""}},[t._v(" "+t._s(t.$L("\u53EF\u67E5\u770B\u6240\u9009\u90E8\u95E8\u53CA\u6240\u6709\u4E0B\u7EA7\u90E8\u95E8\u6210\u5458\u53C2\u4E0E\u7684\u9879\u76EE\u548C\u4EFB\u52A1\uFF0C\u4EC5\u652F\u6301\u53EA\u8BFB\u67E5\u770B\u3002"))+" ")]),t.managedDepartments.length>1?e("div",{staticClass:"department-owner-view-actions"},[e("a",{attrs:{href:"javascript:void(0)"},on:{click:function(s){t.draftIds=[]}}},[t._v(t._s(t.$L("\u6E05\u7A7A")))]),e("a",{attrs:{href:"javascript:void(0)"},on:{click:function(s){t.draftIds=t.managedDepartments.map(function(n){return n.id})}}},[t._v(t._s(t.$L("\u5168\u9009")))]),e("a",{attrs:{href:"javascript:void(0)"},on:{click:t.reverseDraft}},[t._v(t._s(t.$L("\u53CD\u9009")))])]):t._e(),e("CheckboxGroup",{staticClass:"department-owner-view-list",model:{value:t.draftIds,callback:function(s){t.draftIds=s},expression:"draftIds"}},t._l(t.managedDepartments,function(s){return e("div",{key:s.id,class:["department-owner-view-item",t.draftIds.includes(s.id)?"active":""],on:{click:function(n){return t.toggleDraft(s.id)}}},[e("div",{staticClass:"department-owner-view-icon"},[e("i",{staticClass:"taskfont"},[t._v("\uE75C")])]),e("div",{staticClass:"department-owner-view-name"},[t._v(t._s(s.name))]),e("Checkbox",{staticClass:"department-owner-view-checkbox",attrs:{label:s.id},nativeOn:{click:function(n){n.stopPropagation()}}},[e("span")])],1)}),0)],1),e("div",{staticClass:"adaption",attrs:{slot:"footer"},slot:"footer"},[e("Button",{attrs:{type:"default",disabled:t.applyLoading},on:{click:function(s){return t.$emit("input",!1)}}},[t._v(t._s(t.$L("\u53D6\u6D88")))]),e("Button",{attrs:{type:"primary",loading:t.applyLoading},on:{click:t.apply}},[t._v(t._s(t.$L("\u786E\u5B9A")))])],1)])},l=[];const c={name:"DepartmentOwnerView",props:{value:Boolean},data(){return{draftIds:[],applyLoading:!1}},computed:{...i(["userInfo","cacheDepartmentOwnerIds"]),managedDepartments(){return(this.userInfo.managed_departments||[]).map(t=>({...t,id:parseInt(t.id)}))}},watch:{value:{immediate:!0,handler(t){t?this.draftIds=(this.cacheDepartmentOwnerIds||[]).slice():this.applyLoading=!1}}},methods:{toggleDraft(t){t=parseInt(t);const a=this.draftIds.indexOf(t);a>-1?this.draftIds.splice(a,1):this.draftIds.push(t)},reverseDraft(){const t=this.draftIds.map(a=>parseInt(a));this.draftIds=this.managedDepartments.map(a=>a.id).filter(a=>!t.includes(a))},async apply(){if(!this.applyLoading){this.applyLoading=!0;try{await this.$store.dispatch("setDepartmentOwnerIds",this.draftIds),this.$emit("input",!1)}catch(t){$A.modalError((t==null?void 0:t.msg)||this.$L("\u5207\u6362\u5931\u8D25"))}finally{this.applyLoading=!1}}}}},r={};var p=o(c,d,l,!1,f,"624ab3e4",null,null);function f(t){for(let a in r)this[a]=r[a]}var u=function(){return p.exports}();export{u as D};
import{m as i}from"./vuex.cc7cb26e.js";import{n as o}from"./app.003a6843.js";var d=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("Modal",{attrs:{value:t.value,title:t.$L("\u8D1F\u8D23\u4EBA\u89C6\u89D2"),"mask-closable":!1,width:"520"},on:{input:function(s){return t.$emit("input",s)}}},[e("div",{staticClass:"department-owner-view-modal"},[e("Alert",{attrs:{type:"info","show-icon":""}},[t._v(" "+t._s(t.$L("\u53EF\u67E5\u770B\u6240\u9009\u90E8\u95E8\u53CA\u6240\u6709\u4E0B\u7EA7\u90E8\u95E8\u6210\u5458\u53C2\u4E0E\u7684\u9879\u76EE\u548C\u4EFB\u52A1\uFF0C\u4EC5\u652F\u6301\u53EA\u8BFB\u67E5\u770B\u3002"))+" ")]),t.managedDepartments.length>1?e("div",{staticClass:"department-owner-view-actions"},[e("a",{attrs:{href:"javascript:void(0)"},on:{click:function(s){t.draftIds=[]}}},[t._v(t._s(t.$L("\u6E05\u7A7A")))]),e("a",{attrs:{href:"javascript:void(0)"},on:{click:function(s){t.draftIds=t.managedDepartments.map(function(n){return n.id})}}},[t._v(t._s(t.$L("\u5168\u9009")))]),e("a",{attrs:{href:"javascript:void(0)"},on:{click:t.reverseDraft}},[t._v(t._s(t.$L("\u53CD\u9009")))])]):t._e(),e("CheckboxGroup",{staticClass:"department-owner-view-list",model:{value:t.draftIds,callback:function(s){t.draftIds=s},expression:"draftIds"}},t._l(t.managedDepartments,function(s){return e("div",{key:s.id,class:["department-owner-view-item",t.draftIds.includes(s.id)?"active":""],on:{click:function(n){return t.toggleDraft(s.id)}}},[e("div",{staticClass:"department-owner-view-icon"},[e("i",{staticClass:"taskfont"},[t._v("\uE75C")])]),e("div",{staticClass:"department-owner-view-name"},[t._v(t._s(s.name))]),e("Checkbox",{staticClass:"department-owner-view-checkbox",attrs:{label:s.id},nativeOn:{click:function(n){n.stopPropagation()}}},[e("span")])],1)}),0)],1),e("div",{staticClass:"adaption",attrs:{slot:"footer"},slot:"footer"},[e("Button",{attrs:{type:"default",disabled:t.applyLoading},on:{click:function(s){return t.$emit("input",!1)}}},[t._v(t._s(t.$L("\u53D6\u6D88")))]),e("Button",{attrs:{type:"primary",loading:t.applyLoading},on:{click:t.apply}},[t._v(t._s(t.$L("\u786E\u5B9A")))])],1)])},l=[];const c={name:"DepartmentOwnerView",props:{value:Boolean},data(){return{draftIds:[],applyLoading:!1}},computed:{...i(["userInfo","cacheDepartmentOwnerIds"]),managedDepartments(){return(this.userInfo.managed_departments||[]).map(t=>({...t,id:parseInt(t.id)}))}},watch:{value:{immediate:!0,handler(t){t?this.draftIds=(this.cacheDepartmentOwnerIds||[]).slice():this.applyLoading=!1}}},methods:{toggleDraft(t){t=parseInt(t);const a=this.draftIds.indexOf(t);a>-1?this.draftIds.splice(a,1):this.draftIds.push(t)},reverseDraft(){const t=this.draftIds.map(a=>parseInt(a));this.draftIds=this.managedDepartments.map(a=>a.id).filter(a=>!t.includes(a))},async apply(){if(!this.applyLoading){this.applyLoading=!0;try{await this.$store.dispatch("setDepartmentOwnerIds",this.draftIds),this.$emit("input",!1)}catch(t){$A.modalError((t==null?void 0:t.msg)||this.$L("\u5207\u6362\u5931\u8D25"))}finally{this.applyLoading=!1}}}}},r={};var p=o(c,d,l,!1,f,"624ab3e4",null,null);function f(t){for(let a in r)this[a]=r[a]}var u=function(){return p.exports}();export{u as D};

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{m as s}from"./vuex.cc7cb26e.js";import{I as m}from"./IFrame.a92ca567.js";import{n as p,l as o}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var l=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=o;switch(o){case"zh-CHT":t="zh-tw";break}let e=this.readOnly?1:0,i=this.readOnly?0:1,n=this.themeName==="dark"?"dark":"kennedy",r=`?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${r}`):this.url=$A.mainUrl(`drawio/webapp/${r}`)},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}}}},a={};var c=p(u,l,d,!1,h,"39021859",null,null);function h(t){for(let e in a)this[e]=a[e]}var pt=function(){return c.exports}();export{pt as default};
import{m as s}from"./vuex.cc7cb26e.js";import{I as m}from"./IFrame.4b35a35f.js";import{n as p,l as o}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var l=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=o;switch(o){case"zh-CHT":t="zh-tw";break}let e=this.readOnly?1:0,i=this.readOnly?0:1,n=this.themeName==="dark"?"dark":"kennedy",r=`?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${r}`):this.url=$A.mainUrl(`drawio/webapp/${r}`)},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}}}},a={};var c=p(u,l,d,!1,h,"39021859",null,null);function h(t){for(let e in a)this[e]=a[e]}var pt=function(){return c.exports}();export{pt as default};

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.155cfddd.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.003a6843.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

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 as r}from"./app.155cfddd.js";var a=function(){var t=this,n=t.$createElement,e=t._self._c||n;return t.windowTouch?e("div",[e("Button",{attrs:{loading:t.loading,type:"primary",icon:"ios-search"},on:{click:t.onSearch}},[t._v(t._s(t.$L("\u641C\u7D22")))]),t.filtering?e("Button",{attrs:{type:"text"},on:{click:t.onCancelFilter}},[t._v(t._s(t.$L("\u53D6\u6D88\u7B5B\u9009")))]):e("Button",{attrs:{loading:t.loading,type:"text",icon:"md-refresh"},on:{click:t.onRefresh}},[t._v(t._s(t.$L("\u5237\u65B0")))])],1):e("Tooltip",{attrs:{theme:"light",placement:t.placement,"transfer-class-name":"search-button-clear",transfer:""}},[e("Button",{attrs:{loading:t.loading,type:"primary",icon:"ios-search"},on:{click:t.onSearch}},[t._v(t._s(t.$L("\u641C\u7D22")))]),e("div",{attrs:{slot:"content"},slot:"content"},[t.filtering?e("Button",{attrs:{type:"text"},on:{click:t.onCancelFilter}},[t._v(t._s(t.$L("\u53D6\u6D88\u7B5B\u9009")))]):e("Button",{attrs:{loading:t.loading,type:"text"},on:{click:t.onRefresh}},[t._v(t._s(t.$L("\u5237\u65B0")))])],1)],1)},i=[];const l={name:"SearchButton",props:{loading:{type:Boolean,default:!1},filtering:{type:Boolean,default:!1},placement:{type:String,default:"bottom"}},methods:{onSearch(){this.$emit("search")},onRefresh(){this.$emit("refresh")},onCancelFilter(){this.$emit("cancelFilter")}}},o={};var s=r(l,a,i,!1,c,null,null,null);function c(t){for(let n in o)this[n]=o[n]}var h=function(){return s.exports}();export{h as S};
import{n as r}from"./app.003a6843.js";var a=function(){var t=this,n=t.$createElement,e=t._self._c||n;return t.windowTouch?e("div",[e("Button",{attrs:{loading:t.loading,type:"primary",icon:"ios-search"},on:{click:t.onSearch}},[t._v(t._s(t.$L("\u641C\u7D22")))]),t.filtering?e("Button",{attrs:{type:"text"},on:{click:t.onCancelFilter}},[t._v(t._s(t.$L("\u53D6\u6D88\u7B5B\u9009")))]):e("Button",{attrs:{loading:t.loading,type:"text",icon:"md-refresh"},on:{click:t.onRefresh}},[t._v(t._s(t.$L("\u5237\u65B0")))])],1):e("Tooltip",{attrs:{theme:"light",placement:t.placement,"transfer-class-name":"search-button-clear",transfer:""}},[e("Button",{attrs:{loading:t.loading,type:"primary",icon:"ios-search"},on:{click:t.onSearch}},[t._v(t._s(t.$L("\u641C\u7D22")))]),e("div",{attrs:{slot:"content"},slot:"content"},[t.filtering?e("Button",{attrs:{type:"text"},on:{click:t.onCancelFilter}},[t._v(t._s(t.$L("\u53D6\u6D88\u7B5B\u9009")))]):e("Button",{attrs:{loading:t.loading,type:"text"},on:{click:t.onRefresh}},[t._v(t._s(t.$L("\u5237\u65B0")))])],1)],1)},i=[];const l={name:"SearchButton",props:{loading:{type:Boolean,default:!1},filtering:{type:Boolean,default:!1},placement:{type:String,default:"bottom"}},methods:{onSearch(){this.$emit("search")},onRefresh(){this.$emit("refresh")},onCancelFilter(){this.$emit("cancelFilter")}}},o={};var s=r(l,a,i,!1,c,null,null,null);function c(t){for(let n in o)this[n]=o[n]}var h=function(){return s.exports}();export{h as S};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{m}from"./vuex.cc7cb26e.js";import{M as e}from"./index.0c10ac32.js";import{n as a}from"./app.155cfddd.js";import"./vue.adba9046.js";import"./@babel.9410f858.js";import"./view-design-hi.f1128b4d.js";import"./@micro-zoe.39406924.js";import"./DialogWrapper.47fb52c9.js";import"./index.da0cb982.js";import"./vue-virtual-scroll-list-hi.74ad83f0.js";import"./lodash.8fcd6fd4.js";import"./ImgUpload.e8d54143.js";import"./webhook.378987f3.js";import"./jquery.b8b91492.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./html-to-md.f297036e.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var n=function(){var t=this,o=t.$createElement,r=t._self._c||o;return r("MicroApps",{ref:"app",attrs:{"window-type":"popout"}})},s=[];const u={components:{MicroApps:e},computed:{...m(["userIsAdmin"])},async mounted(){const{name:t}=this.$route.params;if(!t){$A.modalError("\u5E94\u7528\u4E0D\u5B58\u5728");return}if(t==="iframe-test"){if(!this.userIsAdmin){$A.modalError("\u4EC5\u7BA1\u7406\u5458\u53EF\u4F7F\u7528\u6B64\u529F\u80FD");return}let{url:r}=this.$route.query;if(!r){if(r=await this.promptIframeUrl(),!r)return;this.$router.replace({path:this.$route.path,query:{...this.$route.query,url:r}}).catch(()=>{})}await this.$refs.app.onOpen({id:"iframe-test",name:"iframe-test",url:r,type:"iframe",transparent:!0,keep_alive:!1});return}const o=(await $A.IDBArray("cacheMicroApps")).reverse().find(r=>r.name===t);if(!o){$A.modalError("\u5E94\u7528\u4E0D\u5B58\u5728");return}await this.$refs.app.onOpen(o)},methods:{promptIframeUrl(){return new Promise((t,o)=>{$A.modalInput({title:this.$L("\u8BF7\u8F93\u5165 URL"),placeholder:"https://example.com",onOk:r=>{const i=(r||"").trim();if(!i)return this.$L("URL\u4E0D\u80FD\u4E3A\u7A7A");t(i)},onCancel:()=>o()})}).catch(()=>null)}}},p={};var c=a(u,n,s,!1,l,null,null,null);function l(t){for(let o in p)this[o]=p[o]}var lr=function(){return c.exports}();export{lr as default};
import{m}from"./vuex.cc7cb26e.js";import{M as e}from"./index.f3ef99e5.js";import{n as a}from"./app.003a6843.js";import"./vue.adba9046.js";import"./@babel.9410f858.js";import"./view-design-hi.f1128b4d.js";import"./@micro-zoe.39406924.js";import"./DialogWrapper.aaa21ecc.js";import"./index.6d890576.js";import"./vue-virtual-scroll-list-hi.74ad83f0.js";import"./lodash.8fcd6fd4.js";import"./ImgUpload.4c7bf661.js";import"./webhook.378987f3.js";import"./jquery.de0a5c6b.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./html-to-md.f297036e.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var n=function(){var t=this,o=t.$createElement,r=t._self._c||o;return r("MicroApps",{ref:"app",attrs:{"window-type":"popout"}})},s=[];const u={components:{MicroApps:e},computed:{...m(["userIsAdmin"])},async mounted(){const{name:t}=this.$route.params;if(!t){$A.modalError("\u5E94\u7528\u4E0D\u5B58\u5728");return}if(t==="iframe-test"){if(!this.userIsAdmin){$A.modalError("\u4EC5\u7BA1\u7406\u5458\u53EF\u4F7F\u7528\u6B64\u529F\u80FD");return}let{url:r}=this.$route.query;if(!r){if(r=await this.promptIframeUrl(),!r)return;this.$router.replace({path:this.$route.path,query:{...this.$route.query,url:r}}).catch(()=>{})}await this.$refs.app.onOpen({id:"iframe-test",name:"iframe-test",url:r,type:"iframe",transparent:!0,keep_alive:!1});return}const o=(await $A.IDBArray("cacheMicroApps")).reverse().find(r=>r.name===t);if(!o){$A.modalError("\u5E94\u7528\u4E0D\u5B58\u5728");return}await this.$refs.app.onOpen(o)},methods:{promptIframeUrl(){return new Promise((t,o)=>{$A.modalInput({title:this.$L("\u8BF7\u8F93\u5165 URL"),placeholder:"https://example.com",onOk:r=>{const i=(r||"").trim();if(!i)return this.$L("URL\u4E0D\u80FD\u4E3A\u7A7A");t(i)},onCancel:()=>o()})}).catch(()=>null)}}},p={};var c=a(u,n,s,!1,l,null,null,null);function l(t){for(let o in p)this[o]=p[o]}var lr=function(){return c.exports}();export{lr 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 as l}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var m=function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"setting-device"},[i("ul",[t.loadIng>0&&t.devices.length===0?i("li",{staticClass:"loading"},[i("Loading")],1):t._l(t.devices,function(e){return i("li",{key:e.id},[i("div",{staticClass:"icon"},[i("span",{class:t.getIcon(e.detail)})]),i("div",{staticClass:"info"},[i("div",{staticClass:"title"},[i("span",{staticClass:"name"},[t._v(t._s(t.getName(e.detail)))]),i("span",{staticClass:"device"},[t._v(t._s(t.getOs(e.detail)))])]),i("div",{staticClass:"time"},[i("EPopover",{attrs:{placement:"bottom-start",trigger:"click"}},[i("div",{staticClass:"setting-device-popover"},[i("p",[t._v(t._s(t.$L("\u767B\u5F55\u65F6\u95F4"))+": "+t._s(e.created_at))]),i("p",[t._v(t._s(t.$L("\u66F4\u65B0\u65F6\u95F4"))+": "+t._s(e.updated_at))]),i("p",[t._v(t._s(t.$L("\u8FC7\u671F\u65F6\u95F4"))+": "+t._s(e.expired_at))])]),i("span",{attrs:{slot:"reference"},slot:"reference"},[t._v(t._s(e.updated_at))])])],1)]),i("div",[e.is_current?i("span",{staticClass:"current"},[t._v(t._s(t.$L("\u5F53\u524D\u8BBE\u5907")))]):i("Button",{on:{click:function(o){return t.onLogout(e)}}},[t._v(t._s(t.$L("\u9000\u51FA\u767B\u5F55")))])],1)])})],2)])},p=[];const c={name:"SettingDevice",data(){return{loadIng:0,devices:[]}},mounted(){this.getDeviceList()},methods:{getDeviceList(){this.loadIng++,this.$store.dispatch("call",{url:"users/device/list"}).then(({data:t})=>{this.devices=t.list,typeof this.$parent.updateDeviceCount=="function"&&this.$parent.updateDeviceCount(this.devices.length)}).catch(({msg:t})=>{$A.modalError(t),this.devices=[]}).finally(()=>{this.loadIng--})},getIcon({app_type:t,app_name:r}){return/ios/i.test(t)?/ipad/i.test(r)?"tablet":/iphone/i.test(r)?"phone":"apple":/android/i.test(t)?/(tablet|phablet)/i.test(r)?"tablet":"android":/mac/i.test(t)?"macos":/win/i.test(t)?"window":"web"},getName({app_brand:t,app_model:r,device_name:i,app_type:e,app_name:o,browser:a}){const s=[];if(/web/i.test(e))s.push(a,this.$L("\u6D4F\u89C8\u5668"));else{if(i)return i;t?s.push(t,r):s.push(o||e,this.$L("\u5BA2\u6237\u7AEF"))}return s.join(" ")},getOs({app_os:t,os:r}){return t||r},onLogout(t){$A.modalConfirm({title:"\u9000\u51FA\u767B\u5F55",content:"\u662F\u5426\u5728\u8BE5\u8BBE\u5907\u4E0A\u9000\u51FA\u767B\u5F55\uFF1F",loading:!0,onOk:()=>new Promise((r,i)=>{this.$store.dispatch("call",{url:"users/device/logout",data:{id:t.id}}).then(({msg:e})=>{r(e),this.getDeviceList()}).catch(({msg:e})=>{i(e)})})})}}},n={};var u=l(c,m,p,!1,d,null,null,null);function d(t){for(let r in n)this[r]=n[r]}var nt=function(){return u.exports}();export{nt as default};
import{n as l}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var m=function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"setting-device"},[i("ul",[t.loadIng>0&&t.devices.length===0?i("li",{staticClass:"loading"},[i("Loading")],1):t._l(t.devices,function(e){return i("li",{key:e.id},[i("div",{staticClass:"icon"},[i("span",{class:t.getIcon(e.detail)})]),i("div",{staticClass:"info"},[i("div",{staticClass:"title"},[i("span",{staticClass:"name"},[t._v(t._s(t.getName(e.detail)))]),i("span",{staticClass:"device"},[t._v(t._s(t.getOs(e.detail)))])]),i("div",{staticClass:"time"},[i("EPopover",{attrs:{placement:"bottom-start",trigger:"click"}},[i("div",{staticClass:"setting-device-popover"},[i("p",[t._v(t._s(t.$L("\u767B\u5F55\u65F6\u95F4"))+": "+t._s(e.created_at))]),i("p",[t._v(t._s(t.$L("\u66F4\u65B0\u65F6\u95F4"))+": "+t._s(e.updated_at))]),i("p",[t._v(t._s(t.$L("\u8FC7\u671F\u65F6\u95F4"))+": "+t._s(e.expired_at))])]),i("span",{attrs:{slot:"reference"},slot:"reference"},[t._v(t._s(e.updated_at))])])],1)]),i("div",[e.is_current?i("span",{staticClass:"current"},[t._v(t._s(t.$L("\u5F53\u524D\u8BBE\u5907")))]):i("Button",{on:{click:function(o){return t.onLogout(e)}}},[t._v(t._s(t.$L("\u9000\u51FA\u767B\u5F55")))])],1)])})],2)])},p=[];const c={name:"SettingDevice",data(){return{loadIng:0,devices:[]}},mounted(){this.getDeviceList()},methods:{getDeviceList(){this.loadIng++,this.$store.dispatch("call",{url:"users/device/list"}).then(({data:t})=>{this.devices=t.list,typeof this.$parent.updateDeviceCount=="function"&&this.$parent.updateDeviceCount(this.devices.length)}).catch(({msg:t})=>{$A.modalError(t),this.devices=[]}).finally(()=>{this.loadIng--})},getIcon({app_type:t,app_name:r}){return/ios/i.test(t)?/ipad/i.test(r)?"tablet":/iphone/i.test(r)?"phone":"apple":/android/i.test(t)?/(tablet|phablet)/i.test(r)?"tablet":"android":/mac/i.test(t)?"macos":/win/i.test(t)?"window":"web"},getName({app_brand:t,app_model:r,device_name:i,app_type:e,app_name:o,browser:a}){const s=[];if(/web/i.test(e))s.push(a,this.$L("\u6D4F\u89C8\u5668"));else{if(i)return i;t?s.push(t,r):s.push(o||e,this.$L("\u5BA2\u6237\u7AEF"))}return s.join(" ")},getOs({app_os:t,os:r}){return t||r},onLogout(t){$A.modalConfirm({title:"\u9000\u51FA\u767B\u5F55",content:"\u662F\u5426\u5728\u8BE5\u8BBE\u5907\u4E0A\u9000\u51FA\u767B\u5F55\uFF1F",loading:!0,onOk:()=>new Promise((r,i)=>{this.$store.dispatch("call",{url:"users/device/logout",data:{id:t.id}}).then(({msg:e})=>{r(e),this.getDeviceList()}).catch(({msg:e})=>{i(e)})})})}}},n={};var u=l(c,m,p,!1,d,null,null,null);function d(t){for(let r in n)this[r]=n[r]}var nt=function(){return u.exports}();export{nt as default};

View File

@@ -1 +1 @@
import{D as p}from"./DialogWrapper.47fb52c9.js";import{m}from"./vuex.cc7cb26e.js";import{n as a}from"./app.155cfddd.js";import"./index.da0cb982.js";import"./vue-virtual-scroll-list-hi.74ad83f0.js";import"./@babel.9410f858.js";import"./vue.adba9046.js";import"./lodash.8fcd6fd4.js";import"./ImgUpload.e8d54143.js";import"./webhook.378987f3.js";import"./jquery.b8b91492.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var t=this,o=t.$createElement,r=t._self._c||o;return r("div",{staticClass:"electron-dialog"},[r("PageTitle",{attrs:{title:t.dialogData.name}}),t.dialogId>0?r("DialogWrapper",{attrs:{dialogId:t.dialogId}}):t._e()],1)},n=[];const s={components:{DialogWrapper:p},computed:{...m(["cacheDialogs"]),dialogId(){const{dialogId:t}=this.$route.params;return parseInt(/^\d+$/.test(t)?t:0)},dialogData(){return this.cacheDialogs.find(({id:t})=>t===this.dialogId)||{}}}},i={};var l=a(s,e,n,!1,d,"4f6d7c8a",null,null);function d(t){for(let o in i)this[o]=i[o]}var st=function(){return l.exports}();export{st as default};
import{D as p}from"./DialogWrapper.aaa21ecc.js";import{m}from"./vuex.cc7cb26e.js";import{n as a}from"./app.003a6843.js";import"./index.6d890576.js";import"./vue-virtual-scroll-list-hi.74ad83f0.js";import"./@babel.9410f858.js";import"./vue.adba9046.js";import"./lodash.8fcd6fd4.js";import"./ImgUpload.4c7bf661.js";import"./webhook.378987f3.js";import"./jquery.de0a5c6b.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var t=this,o=t.$createElement,r=t._self._c||o;return r("div",{staticClass:"electron-dialog"},[r("PageTitle",{attrs:{title:t.dialogData.name}}),t.dialogId>0?r("DialogWrapper",{attrs:{dialogId:t.dialogId}}):t._e()],1)},n=[];const s={components:{DialogWrapper:p},computed:{...m(["cacheDialogs"]),dialogId(){const{dialogId:t}=this.$route.params;return parseInt(/^\d+$/.test(t)?t:0)},dialogData(){return this.cacheDialogs.find(({id:t})=>t===this.dialogId)||{}}}},i={};var l=a(s,e,n,!1,d,"4f6d7c8a",null,null);function d(t){for(let o in i)this[o]=i[o]}var st=function(){return l.exports}();export{st as default};

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"./FileContent.7dd80233.js";import m from"./FilePreview.3a085228.js";import{n as l}from"./app.155cfddd.js";import"./openpgp_hi.15f91b1d.js";import"./IFrame.a92ca567.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var s=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"single-file"},[i("PageTitle",{attrs:{title:t.pageName}}),t.loadIng>0?i("Loading"):t.fileInfo?[t.isPreview?i("FilePreview",{attrs:{code:t.code,file:t.fileInfo,historyId:t.historyId,headerShow:!t.$isEEUIApp}}):i("FileContent",{attrs:{file:t.fileInfo},model:{value:t.fileShow,callback:function(r){t.fileShow=r},expression:"fileShow"}})]:t._e()],2)},p=[];const a={components:{FilePreview:m,FileContent:n},data(){return{loadIng:0,code:null,fileShow:!0,fileInfo:null}},mounted(){},computed:{historyId(){return this.$route.query?$A.runNum(this.$route.query.history_id):0},isPreview(){return this.windowPortrait||this.code||this.historyId>0||this.fileInfo&&this.fileInfo.permission===0},pageName(){return this.$route.query&&this.$route.query.history_title?this.$route.query.history_title:this.fileInfo?`${this.fileInfo.name} [${this.fileInfo.created_at}]`:""}},watch:{$route:{handler(){this.getInfo()},immediate:!0}},methods:{getInfo(){let{codeOrFileId:t}=this.$route.params,e={id:t};if(/^\d+$/.test(t))this.code=null;else if(t)this.code=t;else return;setTimeout(i=>{this.loadIng++},600),this.$store.dispatch("call",{url:"file/one",data:e}).then(({data:i})=>{this.fileInfo=i}).catch(({msg:i})=>{$A.modalError({content:i,onOk:()=>{window.close()}})}).finally(i=>{this.loadIng--})}}},o={};var f=l(a,s,p,!1,u,"662d0b64",null,null);function u(t){for(let e in o)this[e]=o[e]}var st=function(){return f.exports}();export{st as default};
import n from"./FileContent.2299515f.js";import m from"./FilePreview.92fbe21e.js";import{n as l}from"./app.003a6843.js";import"./openpgp_hi.15f91b1d.js";import"./IFrame.4b35a35f.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var s=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"single-file"},[i("PageTitle",{attrs:{title:t.pageName}}),t.loadIng>0?i("Loading"):t.fileInfo?[t.isPreview?i("FilePreview",{attrs:{code:t.code,file:t.fileInfo,historyId:t.historyId,headerShow:!t.$isEEUIApp}}):i("FileContent",{attrs:{file:t.fileInfo},model:{value:t.fileShow,callback:function(r){t.fileShow=r},expression:"fileShow"}})]:t._e()],2)},p=[];const a={components:{FilePreview:m,FileContent:n},data(){return{loadIng:0,code:null,fileShow:!0,fileInfo:null}},mounted(){},computed:{historyId(){return this.$route.query?$A.runNum(this.$route.query.history_id):0},isPreview(){return this.windowPortrait||this.code||this.historyId>0||this.fileInfo&&this.fileInfo.permission===0},pageName(){return this.$route.query&&this.$route.query.history_title?this.$route.query.history_title:this.fileInfo?`${this.fileInfo.name} [${this.fileInfo.created_at}]`:""}},watch:{$route:{handler(){this.getInfo()},immediate:!0}},methods:{getInfo(){let{codeOrFileId:t}=this.$route.params,e={id:t};if(/^\d+$/.test(t))this.code=null;else if(t)this.code=t;else return;setTimeout(i=>{this.loadIng++},600),this.$store.dispatch("call",{url:"file/one",data:e}).then(({data:i})=>{this.fileInfo=i}).catch(({msg:i})=>{$A.modalError({content:i,onOk:()=>{window.close()}})}).finally(i=>{this.loadIng--})}}},o={};var f=l(a,s,p,!1,u,"662d0b64",null,null);function u(t){for(let e in o)this[e]=o[e]}var st=function(){return f.exports}();export{st 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

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{n as m}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var t=this,o=t.$createElement,i=t._self._c||o;return i("div")},n=[];const p={data(){return{}},mounted(){if(/^https?:/i.test(window.location.protocol)){let t=null;if(this.$router.mode==="hash"?$A.stringLength(window.location.pathname)>2&&(t=`${window.location.origin}/#${window.location.pathname}${window.location.search}`):this.$router.mode==="history"&&$A.strExists(window.location.href,"/#/")&&(t=window.location.href.replace("/#/","/")),t)throw this.$store.dispatch("userUrl",t).then(o=>{window.location.href=o}),SyntaxError()}},activated(){this.start()},methods:{start(){this.userId>0?this.goForward({name:"manage-dashboard"},!0):this.goForward({name:"login"},!0)}}},r={};var a=m(p,e,n,!1,s,null,null,null);function s(t){for(let o in r)this[o]=r[o]}var rt=function(){return a.exports}();export{rt as default};
import{n as m}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var t=this,o=t.$createElement,i=t._self._c||o;return i("div")},n=[];const p={data(){return{}},mounted(){if(/^https?:/i.test(window.location.protocol)){let t=null;if(this.$router.mode==="hash"?$A.stringLength(window.location.pathname)>2&&(t=`${window.location.origin}/#${window.location.pathname}${window.location.search}`):this.$router.mode==="history"&&$A.strExists(window.location.href,"/#/")&&(t=window.location.href.replace("/#/","/")),t)throw this.$store.dispatch("userUrl",t).then(o=>{window.location.href=o}),SyntaxError()}},activated(){this.start()},methods:{start(){this.userId>0?this.goForward({name:"manage-dashboard"},!0):this.goForward({name:"login"},!0)}}},r={};var a=m(p,e,n,!1,s,null,null,null);function s(t){for(let o in r)this[o]=r[o]}var rt=function(){return a.exports}();export{rt as default};

View File

@@ -1 +1 @@
import{_ as m}from"./openpgp_hi.15f91b1d.js";import{e as n}from"./index.40a8e116.js";import{n as p}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var a=function(){var t=this,o=t.$createElement,i=t._self._c||o;return t.ready?i("VEditor",{attrs:{leftToolbar:t.leftToolbar,rightToolbar:t.rightToolbar,tocNavPositionRight:t.tocNavPositionRight,includeLevel:t.includeLevel},model:{value:t.content,callback:function(e){t.content=e},expression:"content"}}):i("Loading")},s=[];const l={name:"VMEditor",mixins:[n],components:{VEditor:()=>m(()=>import("./editor.69ce8e47.js"),["js/build/editor.69ce8e47.js","js/build/editor.90492550.css","js/build/@kangc.b5fe0a56.js","js/build/@kangc.d8464d83.css","js/build/@babel.9410f858.js","js/build/vue.adba9046.js","js/build/copy-to-clipboard.a53c061d.js","js/build/toggle-selection.d2487283.js","js/build/prismjs.94ec9288.js","js/build/app.155cfddd.js","js/build/app.d48b83e8.css","js/build/jquery.b8b91492.js","js/build/dayjs.fed09e57.js","js/build/localforage.fcd54ffc.js","js/build/markdown-it.0450edb4.js","js/build/mdurl.ce6c1dd8.js","js/build/uc.micro.8d343c98.js","js/build/entities.48a44fec.js","js/build/linkify-it.c5e8196e.js","js/build/punycode.js.4b3f125a.js","js/build/highlight.js.cbbfb885.js","js/build/markdown-it-link-attributes.e1d5d151.js","js/build/@traptitech.acea8861.js","js/build/vuex.cc7cb26e.js","js/build/openpgp_hi.15f91b1d.js","js/build/axios.37c7f908.js","js/build/mitt.1ea0a2a3.js","js/build/quill-hi.ca2ea0cc.js","js/build/parchment.d5c5924e.js","js/build/quill-delta.385a10bf.js","js/build/fast-diff.f17881f3.js","js/build/lodash.clonedeep.3cc09a31.js","js/build/lodash.isequal.dbdc2157.js","js/build/eventemitter3.78b735ad.js","js/build/lodash-es.76e3a28b.js","js/build/quill-mention-hi.4eeb5a2d.js","js/build/view-design-hi.f1128b4d.js","js/build/html-to-md.f297036e.js","js/build/lodash.8fcd6fd4.js","js/build/vue-router.2d566cd7.js","js/build/vue-clipboard2.fd43a5bc.js","js/build/clipboard.37b37361.js","js/build/vuedraggable.f464b992.js","js/build/sortablejs.3488b922.js","js/build/vue-resize-observer.5af23a43.js","js/build/element-sea.f8a64907.js","js/build/deepmerge.cecf392e.js","js/build/resize-observer-polyfill.5d591c5f.js","js/build/throttle-debounce.7c3948b2.js","js/build/babel-helper-vue-jsx-merge-props.5ed215c3.js","js/build/normalize-wheel.2a034b9f.js","js/build/async-validator.dca2b951.js","js/build/babel-runtime.4773988a.js","js/build/core-js.314b4a1d.js","js/build/codemirror.9d10b9e4.js","js/build/codemirror.9ace6687.css","js/build/index.40a8e116.js","js/build/ImgUpload.e8d54143.js"])},data(){return{ready:!1,content:""}},async mounted(){await $A.loadScriptS(["js/katex/katex.min.js","js/katex/katex.min.css","js/mermaid.min.js"]),this.ready=!0},watch:{value:{handler(t){t==null&&(t=""),this.content=t},immediate:!0},content(t){this.$emit("input",t)}}},r={};var c=p(l,a,s,!1,_,null,null,null);function _(t){for(let o in r)this[o]=r[o]}var nt=function(){return c.exports}();export{nt as default};
import{_ as m}from"./openpgp_hi.15f91b1d.js";import{e as n}from"./index.40a8e116.js";import{n as p}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var a=function(){var t=this,o=t.$createElement,i=t._self._c||o;return t.ready?i("VEditor",{attrs:{leftToolbar:t.leftToolbar,rightToolbar:t.rightToolbar,tocNavPositionRight:t.tocNavPositionRight,includeLevel:t.includeLevel},model:{value:t.content,callback:function(e){t.content=e},expression:"content"}}):i("Loading")},s=[];const l={name:"VMEditor",mixins:[n],components:{VEditor:()=>m(()=>import("./editor.e5f1045c.js"),["js/build/editor.e5f1045c.js","js/build/editor.90492550.css","js/build/@kangc.b5fe0a56.js","js/build/@kangc.d8464d83.css","js/build/@babel.9410f858.js","js/build/vue.adba9046.js","js/build/copy-to-clipboard.a53c061d.js","js/build/toggle-selection.d2487283.js","js/build/prismjs.94ec9288.js","js/build/app.003a6843.js","js/build/app.726e1ec0.css","js/build/jquery.de0a5c6b.js","js/build/dayjs.169453f2.js","js/build/localforage.1d5f26a4.js","js/build/markdown-it.0450edb4.js","js/build/mdurl.ce6c1dd8.js","js/build/uc.micro.8d343c98.js","js/build/entities.48a44fec.js","js/build/linkify-it.c5e8196e.js","js/build/punycode.js.4b3f125a.js","js/build/highlight.js.cbbfb885.js","js/build/markdown-it-link-attributes.e1d5d151.js","js/build/@traptitech.acea8861.js","js/build/vuex.cc7cb26e.js","js/build/openpgp_hi.15f91b1d.js","js/build/axios.37c7f908.js","js/build/mitt.1ea0a2a3.js","js/build/quill-hi.ca2ea0cc.js","js/build/parchment.d5c5924e.js","js/build/quill-delta.385a10bf.js","js/build/fast-diff.f17881f3.js","js/build/lodash.clonedeep.3cc09a31.js","js/build/lodash.isequal.dbdc2157.js","js/build/eventemitter3.78b735ad.js","js/build/lodash-es.76e3a28b.js","js/build/quill-mention-hi.4eeb5a2d.js","js/build/view-design-hi.f1128b4d.js","js/build/html-to-md.f297036e.js","js/build/lodash.8fcd6fd4.js","js/build/vue-router.2d566cd7.js","js/build/vue-clipboard2.fd43a5bc.js","js/build/clipboard.37b37361.js","js/build/vuedraggable.f464b992.js","js/build/sortablejs.3488b922.js","js/build/vue-resize-observer.5af23a43.js","js/build/element-sea.f8a64907.js","js/build/deepmerge.cecf392e.js","js/build/resize-observer-polyfill.5d591c5f.js","js/build/throttle-debounce.7c3948b2.js","js/build/babel-helper-vue-jsx-merge-props.5ed215c3.js","js/build/normalize-wheel.2a034b9f.js","js/build/async-validator.dca2b951.js","js/build/babel-runtime.4773988a.js","js/build/core-js.314b4a1d.js","js/build/codemirror.9d10b9e4.js","js/build/codemirror.9ace6687.css","js/build/index.40a8e116.js","js/build/ImgUpload.4c7bf661.js"])},data(){return{ready:!1,content:""}},async mounted(){await $A.loadScriptS(["js/katex/katex.min.js","js/katex/katex.min.css","js/mermaid.min.js"]),this.ready=!0},watch:{value:{handler(t){t==null&&(t=""),this.content=t},immediate:!0},content(t){this.$emit("input",t)}}},r={};var c=p(l,a,s,!1,_,null,null,null);function _(t){for(let o in r)this[o]=r[o]}var nt=function(){return c.exports}();export{nt as default};

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{h as e,l as n,r as s,n as p}from"./app.155cfddd.js";import{m as l}from"./vuex.cc7cb26e.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var u=function(){var t=this,a=t.$createElement,r=t._self._c||a;return r("div",{staticClass:"setting-item submit"},[r("Form",t._b({ref:"formData",attrs:{model:t.formData,rules:t.ruleData},nativeOn:{submit:function(o){o.preventDefault()}}},"Form",t.formOptions,!1),[r("FormItem",{attrs:{label:t.$L("\u9009\u62E9\u8BED\u8A00"),prop:"language"}},[r("Select",{attrs:{placeholder:t.$L("\u9009\u9879\u8BED\u8A00")},model:{value:t.formData.language,callback:function(o){t.$set(t.formData,"language",o)},expression:"formData.language"}},t._l(t.languageList,function(o,i){return r("Option",{key:i,attrs:{value:i}},[t._v(t._s(o))])}),1)],1)],1),r("div",{staticClass:"setting-footer"},[r("Button",{attrs:{loading:t.loadIng>0,type:"primary"},on:{click:t.submitForm}},[t._v(t._s(t.$L("\u63D0\u4EA4")))]),r("Button",{staticStyle:{"margin-left":"8px"},attrs:{loading:t.loadIng>0},on:{click:t.resetForm}},[t._v(t._s(t.$L("\u91CD\u7F6E")))])],1)],1)},f=[];const g={data(){return{loadIng:0,languageList:e,formData:{language:""},ruleData:{}}},mounted(){this.initData()},computed:{...l(["formOptions"])},methods:{initData(){this.$set(this.formData,"language",n),this.formData_bak=$A.cloneJSON(this.formData)},submitForm(){this.$refs.formData.validate(t=>{t&&s(this.formData.language)})},resetForm(){this.formData=$A.cloneJSON(this.formData_bak)}}},m={};var c=p(g,u,f,!1,_,null,null,null);function _(t){for(let a in m)this[a]=m[a]}var st=function(){return c.exports}();export{st as default};
import{h as e,l as n,r as s,n as p}from"./app.003a6843.js";import{m as l}from"./vuex.cc7cb26e.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var u=function(){var t=this,a=t.$createElement,r=t._self._c||a;return r("div",{staticClass:"setting-item submit"},[r("Form",t._b({ref:"formData",attrs:{model:t.formData,rules:t.ruleData},nativeOn:{submit:function(o){o.preventDefault()}}},"Form",t.formOptions,!1),[r("FormItem",{attrs:{label:t.$L("\u9009\u62E9\u8BED\u8A00"),prop:"language"}},[r("Select",{attrs:{placeholder:t.$L("\u9009\u9879\u8BED\u8A00")},model:{value:t.formData.language,callback:function(o){t.$set(t.formData,"language",o)},expression:"formData.language"}},t._l(t.languageList,function(o,i){return r("Option",{key:i,attrs:{value:i}},[t._v(t._s(o))])}),1)],1)],1),r("div",{staticClass:"setting-footer"},[r("Button",{attrs:{loading:t.loadIng>0,type:"primary"},on:{click:t.submitForm}},[t._v(t._s(t.$L("\u63D0\u4EA4")))]),r("Button",{staticStyle:{"margin-left":"8px"},attrs:{loading:t.loadIng>0},on:{click:t.resetForm}},[t._v(t._s(t.$L("\u91CD\u7F6E")))])],1)],1)},f=[];const g={data(){return{loadIng:0,languageList:e,formData:{language:""},ruleData:{}}},mounted(){this.initData()},computed:{...l(["formOptions"])},methods:{initData(){this.$set(this.formData,"language",n),this.formData_bak=$A.cloneJSON(this.formData)},submitForm(){this.$refs.formData.validate(t=>{t&&s(this.formData.language)})},resetForm(){this.formData=$A.cloneJSON(this.formData_bak)}}},m={};var c=p(g,u,f,!1,_,null,null,null);function _(t){for(let a in m)this[a]=m[a]}var st=function(){return c.exports}();export{st 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

View File

@@ -1 +1 @@
import{n as a}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var s=function(){var i=this,t=i.$createElement,r=i._self._c||t;return r("div")},u=[];const c={mounted(){const{meetingId:i,sharekey:t}=this.$route.params,{nickname:r,avatar:m,audio:p,video:n,type:o}=this.$route.query;this.$store.dispatch("showMeetingWindow",{type:["direct","join"].includes(o)?o:"join",meetingid:i,meetingSharekey:t,meetingNickname:r,meetingAvatar:m,meetingAudio:p,meetingVideo:n,meetingdisabled:!0})},render(){return null}},e={};var d=a(c,s,u,!1,l,null,null,null);function l(i){for(let t in e)this[t]=e[t]}var pt=function(){return d.exports}();export{pt as default};
import{n as a}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var s=function(){var i=this,t=i.$createElement,r=i._self._c||t;return r("div")},u=[];const c={mounted(){const{meetingId:i,sharekey:t}=this.$route.params,{nickname:r,avatar:m,audio:p,video:n,type:o}=this.$route.query;this.$store.dispatch("showMeetingWindow",{type:["direct","join"].includes(o)?o:"join",meetingid:i,meetingSharekey:t,meetingNickname:r,meetingAvatar:m,meetingAudio:p,meetingVideo:n,meetingdisabled:!0})},render(){return null}},e={};var d=a(c,s,u,!1,l,null,null,null);function l(i){for(let t in e)this[t]=e[t]}var pt=function(){return d.exports}();export{pt as default};

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 m}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var a=function(){var t=this,s=t.$createElement,r=t._self._c||s;return r("div",{staticClass:"setting-item submit"},[r("Form",t._b({ref:"formDatum",attrs:{model:t.formDatum,rules:t.ruleDatum},nativeOn:{submit:function(e){e.preventDefault()}}},"Form",t.formOptions,!1),[t.userInfo.changepass?r("Alert",{staticStyle:{"margin-bottom":"32px"},attrs:{type:"warning",showIcon:""}},[t._v(t._s(t.$L("\u8BF7\u5148\u4FEE\u6539\u767B\u5F55\u5BC6\u7801\uFF01")))]):t._e(),r("FormItem",{attrs:{label:t.$L("\u65E7\u5BC6\u7801"),prop:"oldpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.oldpass,callback:function(e){t.$set(t.formDatum,"oldpass",e)},expression:"formDatum.oldpass"}})],1),r("FormItem",{attrs:{label:t.$L("\u65B0\u5BC6\u7801"),prop:"newpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.newpass,callback:function(e){t.$set(t.formDatum,"newpass",e)},expression:"formDatum.newpass"}})],1),r("FormItem",{attrs:{label:t.$L("\u786E\u8BA4\u65B0\u5BC6\u7801"),prop:"checkpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.checkpass,callback:function(e){t.$set(t.formDatum,"checkpass",e)},expression:"formDatum.checkpass"}})],1)],1),r("div",{staticClass:"setting-footer"},[r("Button",{attrs:{loading:t.loadIng>0,type:"primary"},on:{click:t.submitForm}},[t._v(t._s(t.$L("\u63D0\u4EA4")))]),r("Button",{staticStyle:{"margin-left":"8px"},attrs:{loading:t.loadIng>0},on:{click:t.resetForm}},[t._v(t._s(t.$L("\u91CD\u7F6E")))])],1)],1)},p=[];const n={data(){return{loadIng:0,formDatum:{oldpass:"",newpass:"",checkpass:""},ruleDatum:{oldpass:[{required:!0,message:this.$L("\u8BF7\u8F93\u5165\u65E7\u5BC6\u7801\uFF01"),trigger:"change"},{type:"string",min:6,message:this.$L("\u5BC6\u7801\u957F\u5EA6\u81F3\u5C116\u4F4D\uFF01"),trigger:"change"}],newpass:[{validator:(t,s,r)=>{s===""?r(new Error(this.$L("\u8BF7\u8F93\u5165\u65B0\u5BC6\u7801\uFF01"))):(this.formDatum.checkpass!==""&&this.$refs.formDatum.validateField("checkpass"),r())},required:!0,trigger:"change"},{type:"string",min:6,message:this.$L("\u5BC6\u7801\u957F\u5EA6\u81F3\u5C116\u4F4D\uFF01"),trigger:"change"}],checkpass:[{validator:(t,s,r)=>{s===""?r(new Error(this.$L("\u8BF7\u91CD\u65B0\u8F93\u5165\u65B0\u5BC6\u7801\uFF01"))):s!==this.formDatum.newpass?r(new Error(this.$L("\u4E24\u6B21\u5BC6\u7801\u8F93\u5165\u4E0D\u4E00\u81F4\uFF01"))):r()},required:!0,trigger:"change"}]}}},computed:{...i(["userInfo","formOptions"])},methods:{submitForm(){this.$refs.formDatum.validate(t=>{t&&(this.loadIng++,this.$store.dispatch("call",{url:"users/editpass",data:this.formDatum}).then(({data:s})=>{$A.messageSuccess("\u4FEE\u6539\u6210\u529F"),this.$store.dispatch("saveUserInfo",s),this.$refs.formDatum.resetFields()}).catch(({msg:s})=>{$A.modalError(s)}).finally(s=>{this.loadIng--}))})},resetForm(){this.$refs.formDatum.resetFields()}}},o={};var l=m(n,a,p,!1,u,null,null,null);function u(t){for(let s in o)this[s]=o[s]}var ot=function(){return l.exports}();export{ot as default};
import{m as i}from"./vuex.cc7cb26e.js";import{n as m}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var a=function(){var t=this,s=t.$createElement,r=t._self._c||s;return r("div",{staticClass:"setting-item submit"},[r("Form",t._b({ref:"formDatum",attrs:{model:t.formDatum,rules:t.ruleDatum},nativeOn:{submit:function(e){e.preventDefault()}}},"Form",t.formOptions,!1),[t.userInfo.changepass?r("Alert",{staticStyle:{"margin-bottom":"32px"},attrs:{type:"warning",showIcon:""}},[t._v(t._s(t.$L("\u8BF7\u5148\u4FEE\u6539\u767B\u5F55\u5BC6\u7801\uFF01")))]):t._e(),r("FormItem",{attrs:{label:t.$L("\u65E7\u5BC6\u7801"),prop:"oldpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.oldpass,callback:function(e){t.$set(t.formDatum,"oldpass",e)},expression:"formDatum.oldpass"}})],1),r("FormItem",{attrs:{label:t.$L("\u65B0\u5BC6\u7801"),prop:"newpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.newpass,callback:function(e){t.$set(t.formDatum,"newpass",e)},expression:"formDatum.newpass"}})],1),r("FormItem",{attrs:{label:t.$L("\u786E\u8BA4\u65B0\u5BC6\u7801"),prop:"checkpass"}},[r("Input",{attrs:{type:"password"},model:{value:t.formDatum.checkpass,callback:function(e){t.$set(t.formDatum,"checkpass",e)},expression:"formDatum.checkpass"}})],1)],1),r("div",{staticClass:"setting-footer"},[r("Button",{attrs:{loading:t.loadIng>0,type:"primary"},on:{click:t.submitForm}},[t._v(t._s(t.$L("\u63D0\u4EA4")))]),r("Button",{staticStyle:{"margin-left":"8px"},attrs:{loading:t.loadIng>0},on:{click:t.resetForm}},[t._v(t._s(t.$L("\u91CD\u7F6E")))])],1)],1)},p=[];const n={data(){return{loadIng:0,formDatum:{oldpass:"",newpass:"",checkpass:""},ruleDatum:{oldpass:[{required:!0,message:this.$L("\u8BF7\u8F93\u5165\u65E7\u5BC6\u7801\uFF01"),trigger:"change"},{type:"string",min:6,message:this.$L("\u5BC6\u7801\u957F\u5EA6\u81F3\u5C116\u4F4D\uFF01"),trigger:"change"}],newpass:[{validator:(t,s,r)=>{s===""?r(new Error(this.$L("\u8BF7\u8F93\u5165\u65B0\u5BC6\u7801\uFF01"))):(this.formDatum.checkpass!==""&&this.$refs.formDatum.validateField("checkpass"),r())},required:!0,trigger:"change"},{type:"string",min:6,message:this.$L("\u5BC6\u7801\u957F\u5EA6\u81F3\u5C116\u4F4D\uFF01"),trigger:"change"}],checkpass:[{validator:(t,s,r)=>{s===""?r(new Error(this.$L("\u8BF7\u91CD\u65B0\u8F93\u5165\u65B0\u5BC6\u7801\uFF01"))):s!==this.formDatum.newpass?r(new Error(this.$L("\u4E24\u6B21\u5BC6\u7801\u8F93\u5165\u4E0D\u4E00\u81F4\uFF01"))):r()},required:!0,trigger:"change"}]}}},computed:{...i(["userInfo","formOptions"])},methods:{submitForm(){this.$refs.formDatum.validate(t=>{t&&(this.loadIng++,this.$store.dispatch("call",{url:"users/editpass",data:this.formDatum}).then(({data:s})=>{$A.messageSuccess("\u4FEE\u6539\u6210\u529F"),this.$store.dispatch("saveUserInfo",s),this.$refs.formDatum.resetFields()}).catch(({msg:s})=>{$A.modalError(s)}).finally(s=>{this.loadIng--}))})},resetForm(){this.$refs.formDatum.resetFields()}}},o={};var l=m(n,a,p,!1,u,null,null,null);function u(t){for(let s in o)this[s]=o[s]}var ot=function(){return l.exports}();export{ot as default};

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{n as m}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var r=this,t=r.$createElement,i=r._self._c||t;return i("div")},e=[];const n={},o={};var _=m(n,p,e,!1,s,null,null,null);function s(r){for(let t in o)this[t]=o[t]}var ot=function(){return _.exports}();export{ot as default};
import{n as m}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var p=function(){var r=this,t=r.$createElement,i=r._self._c||t;return i("div")},e=[];const n={},o={};var _=m(n,p,e,!1,s,null,null,null);function s(r){for(let t in o)this[t]=o[t]}var ot=function(){return _.exports}();export{ot as default};

View File

@@ -1 +1 @@
import{_ as m}from"./openpgp_hi.15f91b1d.js";import{p}from"./index.40a8e116.js";import{n as e}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var n=function(){var t=this,r=t.$createElement,i=t._self._c||r;return t.ready?i("VPreview",{attrs:{value:t.value}}):i("Loading")},a=[];const s={name:"VMPreview",mixins:[p],components:{VPreview:()=>m(()=>import("./preview.960baef9.js"),["js/build/preview.960baef9.js","js/build/preview.15fbcdd9.css","js/build/@kangc.b5fe0a56.js","js/build/@kangc.d8464d83.css","js/build/@babel.9410f858.js","js/build/vue.adba9046.js","js/build/copy-to-clipboard.a53c061d.js","js/build/toggle-selection.d2487283.js","js/build/prismjs.94ec9288.js","js/build/app.155cfddd.js","js/build/app.d48b83e8.css","js/build/jquery.b8b91492.js","js/build/dayjs.fed09e57.js","js/build/localforage.fcd54ffc.js","js/build/markdown-it.0450edb4.js","js/build/mdurl.ce6c1dd8.js","js/build/uc.micro.8d343c98.js","js/build/entities.48a44fec.js","js/build/linkify-it.c5e8196e.js","js/build/punycode.js.4b3f125a.js","js/build/highlight.js.cbbfb885.js","js/build/markdown-it-link-attributes.e1d5d151.js","js/build/@traptitech.acea8861.js","js/build/vuex.cc7cb26e.js","js/build/openpgp_hi.15f91b1d.js","js/build/axios.37c7f908.js","js/build/mitt.1ea0a2a3.js","js/build/quill-hi.ca2ea0cc.js","js/build/parchment.d5c5924e.js","js/build/quill-delta.385a10bf.js","js/build/fast-diff.f17881f3.js","js/build/lodash.clonedeep.3cc09a31.js","js/build/lodash.isequal.dbdc2157.js","js/build/eventemitter3.78b735ad.js","js/build/lodash-es.76e3a28b.js","js/build/quill-mention-hi.4eeb5a2d.js","js/build/view-design-hi.f1128b4d.js","js/build/html-to-md.f297036e.js","js/build/lodash.8fcd6fd4.js","js/build/vue-router.2d566cd7.js","js/build/vue-clipboard2.fd43a5bc.js","js/build/clipboard.37b37361.js","js/build/vuedraggable.f464b992.js","js/build/sortablejs.3488b922.js","js/build/vue-resize-observer.5af23a43.js","js/build/element-sea.f8a64907.js","js/build/deepmerge.cecf392e.js","js/build/resize-observer-polyfill.5d591c5f.js","js/build/throttle-debounce.7c3948b2.js","js/build/babel-helper-vue-jsx-merge-props.5ed215c3.js","js/build/normalize-wheel.2a034b9f.js","js/build/async-validator.dca2b951.js","js/build/babel-runtime.4773988a.js","js/build/core-js.314b4a1d.js","js/build/index.40a8e116.js"])},data(){return{ready:!1}},async mounted(){await $A.loadScriptS(["js/katex/katex.min.js","js/katex/katex.min.css","js/mermaid.min.js"]),this.ready=!0}},o={};var _=e(s,n,a,!1,l,null,null,null);function l(t){for(let r in o)this[r]=o[r]}var pt=function(){return _.exports}();export{pt as default};
import{_ as m}from"./openpgp_hi.15f91b1d.js";import{p}from"./index.40a8e116.js";import{n as e}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var n=function(){var t=this,r=t.$createElement,i=t._self._c||r;return t.ready?i("VPreview",{attrs:{value:t.value}}):i("Loading")},a=[];const s={name:"VMPreview",mixins:[p],components:{VPreview:()=>m(()=>import("./preview.76b59658.js"),["js/build/preview.76b59658.js","js/build/preview.15fbcdd9.css","js/build/@kangc.b5fe0a56.js","js/build/@kangc.d8464d83.css","js/build/@babel.9410f858.js","js/build/vue.adba9046.js","js/build/copy-to-clipboard.a53c061d.js","js/build/toggle-selection.d2487283.js","js/build/prismjs.94ec9288.js","js/build/app.003a6843.js","js/build/app.726e1ec0.css","js/build/jquery.de0a5c6b.js","js/build/dayjs.169453f2.js","js/build/localforage.1d5f26a4.js","js/build/markdown-it.0450edb4.js","js/build/mdurl.ce6c1dd8.js","js/build/uc.micro.8d343c98.js","js/build/entities.48a44fec.js","js/build/linkify-it.c5e8196e.js","js/build/punycode.js.4b3f125a.js","js/build/highlight.js.cbbfb885.js","js/build/markdown-it-link-attributes.e1d5d151.js","js/build/@traptitech.acea8861.js","js/build/vuex.cc7cb26e.js","js/build/openpgp_hi.15f91b1d.js","js/build/axios.37c7f908.js","js/build/mitt.1ea0a2a3.js","js/build/quill-hi.ca2ea0cc.js","js/build/parchment.d5c5924e.js","js/build/quill-delta.385a10bf.js","js/build/fast-diff.f17881f3.js","js/build/lodash.clonedeep.3cc09a31.js","js/build/lodash.isequal.dbdc2157.js","js/build/eventemitter3.78b735ad.js","js/build/lodash-es.76e3a28b.js","js/build/quill-mention-hi.4eeb5a2d.js","js/build/view-design-hi.f1128b4d.js","js/build/html-to-md.f297036e.js","js/build/lodash.8fcd6fd4.js","js/build/vue-router.2d566cd7.js","js/build/vue-clipboard2.fd43a5bc.js","js/build/clipboard.37b37361.js","js/build/vuedraggable.f464b992.js","js/build/sortablejs.3488b922.js","js/build/vue-resize-observer.5af23a43.js","js/build/element-sea.f8a64907.js","js/build/deepmerge.cecf392e.js","js/build/resize-observer-polyfill.5d591c5f.js","js/build/throttle-debounce.7c3948b2.js","js/build/babel-helper-vue-jsx-merge-props.5ed215c3.js","js/build/normalize-wheel.2a034b9f.js","js/build/async-validator.dca2b951.js","js/build/babel-runtime.4773988a.js","js/build/core-js.314b4a1d.js","js/build/index.40a8e116.js"])},data(){return{ready:!1}},async mounted(){await $A.loadScriptS(["js/katex/katex.min.js","js/katex/katex.min.css","js/mermaid.min.js"]),this.ready=!0}},o={};var _=e(s,n,a,!1,l,null,null,null);function l(t){for(let r in o)this[r]=o[r]}var pt=function(){return _.exports}();export{pt as default};

View File

@@ -1 +1 @@
import{V as e,d as p,a as s,b as n,c as a,_ as l,e as u,v as _}from"./@kangc.b5fe0a56.js";import{P as c}from"./prismjs.94ec9288.js";import{l as v,u as o,n as d}from"./app.155cfddd.js";import{p as f}from"./index.40a8e116.js";import"./@babel.9410f858.js";import"./vue.adba9046.js";import"./copy-to-clipboard.a53c061d.js";import"./toggle-selection.d2487283.js";import"./jquery.b8b91492.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var h=function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"vmpreview-wrapper",on:{click:t.handleClick}},[i("v-md-preview",{attrs:{text:t.previewContent}})],1)},g=[];/^zh/.test(v)?e.lang.use("zh-CN",p):e.lang.use("en-US",s);e.use(n());e.use(a());e.use(l());e.use(u());const w={mixins:[f],components:{[e.name]:e},created(){e.use(_,{Prism:c,extend(t){o.initReasoningPlugin(t)}})},computed:{previewContent({value:t}){return o.clearEmptyReasoning(t)}},methods:{handleClick({target:t}){if(t.nodeName==="IMG"){const r=[...this.$el.querySelectorAll("img").values()].map(i=>i.src);if(r.length===0)return;this.$store.dispatch("previewImage",{index:t.src,list:r})}}}},m={};var x=d(w,h,g,!1,C,"6797ab07",null,null);function C(t){for(let r in m)this[r]=m[r]}var wt=function(){return x.exports}();export{wt as default};
import{V as e,d as p,a as s,b as n,c as a,_ as l,e as u,v as _}from"./@kangc.b5fe0a56.js";import{P as c}from"./prismjs.94ec9288.js";import{l as v,u as o,n as d}from"./app.003a6843.js";import{p as f}from"./index.40a8e116.js";import"./@babel.9410f858.js";import"./vue.adba9046.js";import"./copy-to-clipboard.a53c061d.js";import"./toggle-selection.d2487283.js";import"./jquery.de0a5c6b.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var h=function(){var t=this,r=t.$createElement,i=t._self._c||r;return i("div",{staticClass:"vmpreview-wrapper",on:{click:t.handleClick}},[i("v-md-preview",{attrs:{text:t.previewContent}})],1)},g=[];/^zh/.test(v)?e.lang.use("zh-CN",p):e.lang.use("en-US",s);e.use(n());e.use(a());e.use(l());e.use(u());const w={mixins:[f],components:{[e.name]:e},created(){e.use(_,{Prism:c,extend(t){o.initReasoningPlugin(t)}})},computed:{previewContent({value:t}){return o.clearEmptyReasoning(t)}},methods:{handleClick({target:t}){if(t.nodeName==="IMG"){const r=[...this.$el.querySelectorAll("img").values()].map(i=>i.src);if(r.length===0)return;this.$store.dispatch("previewImage",{index:t.src,list:r})}}}},m={};var x=d(w,h,g,!1,C,"6797ab07",null,null);function C(t){for(let r in m)this[r]=m[r]}var wt=function(){return x.exports}();export{wt as default};

View File

@@ -1 +1 @@
import{n as m,l as p}from"./app.155cfddd.js";import"./jquery.b8b91492.js";import"./@babel.9410f858.js";import"./dayjs.fed09e57.js";import"./localforage.fcd54ffc.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var r=this,t=r.$createElement,i=r._self._c||t;return i("div")},n=[];const l={mounted(){/^zh/.test(p)?window.location.href=$A.mainUrl("site/zh/price.html"):window.location.href=$A.mainUrl("site/en/price.html")}},o={};var a=m(l,e,n,!1,s,null,null,null);function s(r){for(let t in o)this[t]=o[t]}var it=function(){return a.exports}();export{it as default};
import{n as m,l as p}from"./app.003a6843.js";import"./jquery.de0a5c6b.js";import"./@babel.9410f858.js";import"./dayjs.169453f2.js";import"./localforage.1d5f26a4.js";import"./markdown-it.0450edb4.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.cbbfb885.js";import"./markdown-it-link-attributes.e1d5d151.js";import"./@traptitech.acea8861.js";import"./vue.adba9046.js";import"./vuex.cc7cb26e.js";import"./openpgp_hi.15f91b1d.js";import"./axios.37c7f908.js";import"./mitt.1ea0a2a3.js";import"./quill-hi.ca2ea0cc.js";import"./parchment.d5c5924e.js";import"./quill-delta.385a10bf.js";import"./fast-diff.f17881f3.js";import"./lodash.clonedeep.3cc09a31.js";import"./lodash.isequal.dbdc2157.js";import"./eventemitter3.78b735ad.js";import"./lodash-es.76e3a28b.js";import"./quill-mention-hi.4eeb5a2d.js";import"./view-design-hi.f1128b4d.js";import"./html-to-md.f297036e.js";import"./lodash.8fcd6fd4.js";import"./vue-router.2d566cd7.js";import"./vue-clipboard2.fd43a5bc.js";import"./clipboard.37b37361.js";import"./vuedraggable.f464b992.js";import"./sortablejs.3488b922.js";import"./vue-resize-observer.5af23a43.js";import"./element-sea.f8a64907.js";import"./deepmerge.cecf392e.js";import"./resize-observer-polyfill.5d591c5f.js";import"./throttle-debounce.7c3948b2.js";import"./babel-helper-vue-jsx-merge-props.5ed215c3.js";import"./normalize-wheel.2a034b9f.js";import"./async-validator.dca2b951.js";import"./babel-runtime.4773988a.js";import"./core-js.314b4a1d.js";var e=function(){var r=this,t=r.$createElement,i=r._self._c||t;return i("div")},n=[];const l={mounted(){/^zh/.test(p)?window.location.href=$A.mainUrl("site/zh/price.html"):window.location.href=$A.mainUrl("site/en/price.html")}},o={};var a=m(l,e,n,!1,s,null,null,null);function s(r){for(let t in o)this[t]=o[t]}var it=function(){return a.exports}();export{it as default};

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