mirror of
https://github.com/kuaifan/dootask.git
synced 2026-09-10 14:08:57 +00:00
feat: reorganize system settings tabs
This commit is contained in:
parent
58f47be68f
commit
139a7a168b
@ -2729,3 +2729,9 @@ WebDAV 路径冲突
|
||||
请输入设备名称
|
||||
撤销应用密码
|
||||
撤销后,使用此应用密码连接的设备将立即断开。
|
||||
帐号与安全
|
||||
基础设置
|
||||
任务设置
|
||||
消息设置
|
||||
文件与存储
|
||||
上传与媒体处理
|
||||
|
||||
@ -39466,5 +39466,77 @@
|
||||
"fr": "Vous renommez un fichier privé appartenant à (%T1). Cette action modifiera directement le nom du fichier.",
|
||||
"id": "Anda akan mengganti nama file pribadi milik (%T1). Tindakan ini akan langsung mengubah nama file.",
|
||||
"ru": "Вы переименовываете личный файл пользователя (%T1). Это действие напрямую изменит имя файла."
|
||||
},
|
||||
{
|
||||
"key": "帐号与安全",
|
||||
"zh": "",
|
||||
"zh-CHT": "帳號與安全",
|
||||
"en": "Account & Security",
|
||||
"ko": "계정 및 보안",
|
||||
"ja": "アカウントとセキュリティ",
|
||||
"de": "Konto und Sicherheit",
|
||||
"fr": "Compte et securite",
|
||||
"id": "Akun dan Keamanan",
|
||||
"ru": "Аккаунт и безопасность"
|
||||
},
|
||||
{
|
||||
"key": "基础设置",
|
||||
"zh": "",
|
||||
"zh-CHT": "基礎設置",
|
||||
"en": "General Settings",
|
||||
"ko": "기본 설정",
|
||||
"ja": "基本設定",
|
||||
"de": "Grundeinstellungen",
|
||||
"fr": "Parametres generaux",
|
||||
"id": "Pengaturan Dasar",
|
||||
"ru": "Основные настройки"
|
||||
},
|
||||
{
|
||||
"key": "任务设置",
|
||||
"zh": "",
|
||||
"zh-CHT": "任務設置",
|
||||
"en": "Task Settings",
|
||||
"ko": "작업 설정",
|
||||
"ja": "タスク設定",
|
||||
"de": "Aufgabeneinstellungen",
|
||||
"fr": "Parametres des taches",
|
||||
"id": "Pengaturan Tugas",
|
||||
"ru": "Настройки задач"
|
||||
},
|
||||
{
|
||||
"key": "消息设置",
|
||||
"zh": "",
|
||||
"zh-CHT": "消息設置",
|
||||
"en": "Message Settings",
|
||||
"ko": "메시지 설정",
|
||||
"ja": "メッセージ設定",
|
||||
"de": "Nachrichteneinstellungen",
|
||||
"fr": "Parametres des messages",
|
||||
"id": "Pengaturan Pesan",
|
||||
"ru": "Настройки сообщений"
|
||||
},
|
||||
{
|
||||
"key": "文件与存储",
|
||||
"zh": "",
|
||||
"zh-CHT": "文件與存儲",
|
||||
"en": "Files & Storage",
|
||||
"ko": "파일 및 저장소",
|
||||
"ja": "ファイルとストレージ",
|
||||
"de": "Dateien und Speicher",
|
||||
"fr": "Fichiers et stockage",
|
||||
"id": "File dan Penyimpanan",
|
||||
"ru": "Файлы и хранилище"
|
||||
},
|
||||
{
|
||||
"key": "上传与媒体处理",
|
||||
"zh": "",
|
||||
"zh-CHT": "上傳與媒體處理",
|
||||
"en": "Upload & Media Processing",
|
||||
"ko": "업로드 및 미디어 처리",
|
||||
"ja": "アップロードとメディア処理",
|
||||
"de": "Upload und Medienverarbeitung",
|
||||
"fr": "Importation et traitement des medias",
|
||||
"id": "Unggah dan Pemrosesan Media",
|
||||
"ru": "Загрузка и обработка медиа"
|
||||
}
|
||||
]
|
||||
|
||||
2
public/language/web/de.js
vendored
2
public/language/web/de.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/en.js
vendored
2
public/language/web/en.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/fr.js
vendored
2
public/language/web/fr.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/id.js
vendored
2
public/language/web/id.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/ja.js
vendored
2
public/language/web/ja.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/key.js
vendored
2
public/language/web/key.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/ko.js
vendored
2
public/language/web/ko.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/ru.js
vendored
2
public/language/web/ru.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/zh-CHT.js
vendored
2
public/language/web/zh-CHT.js
vendored
File diff suppressed because one or more lines are too long
2
public/language/web/zh.js
vendored
2
public/language/web/zh.js
vendored
File diff suppressed because one or more lines are too long
@ -21,13 +21,13 @@ negative:
|
||||
- 归档天数限制 1-100 天,超出范围保存时报错
|
||||
- 子任务不会被独立归档,只看顶层任务(parent_id = 0)
|
||||
- 项目自身若设了"自定义归档"(archive_method=custom),系统级开关对该项目不生效
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 任务自动归档设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「任务相关」→「自动归档」。
|
||||
桌面端:左上角头像 →「系统设置」→「任务设置」→「自动归档」。
|
||||
|
||||
涉及字段:
|
||||
- `auto_archived` — 开关:`open` / `close`,默认 `close`
|
||||
@ -54,7 +54,7 @@ last_verified: v1.7.90
|
||||
能。归档不是删除:归档任务在项目「已归档」列表里仍可查看、撤销归档(恢复到原列)、或彻底删除。归档动作只是设置 `archived_at` 时间戳与 `archived_userid`。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「任务相关」
|
||||
1. 进入「系统设置」→「任务设置」
|
||||
2. 「自动归档」选「开启」
|
||||
3. 在出现的输入框填天数(默认 7,1-100 之间)
|
||||
4. 「提交」保存,下次定时任务执行时按新规则归档
|
||||
|
||||
@ -23,13 +23,13 @@ negative:
|
||||
- 三个开关相互独立,不会联动;要全静音得分别关
|
||||
- 部门群、项目群等系统群不受 user_group_chat_mute 控制,永远可发言(除非加入全员群禁言)
|
||||
- 系统管理员永远可发言,不受任何开关限制
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 群组与私聊禁言设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「消息相关」。
|
||||
桌面端:左上角头像 →「系统设置」→「消息设置」。
|
||||
|
||||
涉及三个独立字段:
|
||||
|
||||
@ -53,7 +53,7 @@ last_verified: v1.7.90
|
||||
能。任何字段从 `close` 改回 `open` 后立即恢复发言能力,不需要重启或重新登录。对历史已发 / 未发消息没有任何回溯影响。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「消息相关」
|
||||
1. 进入「系统设置」→「消息设置」
|
||||
2. 分别在「全员群组禁言」「私聊禁言」「群聊禁言」选「开放」或「禁言」
|
||||
3. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 不支持按部门 / 项目分类区分模板
|
||||
- 修改模板不会影响已创建项目,仅影响后续新建
|
||||
- 保存时空 list 或 list 中没有合法条目会报「参数错误」/「参数为空」
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 列模板(创建项目预置列)
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「列模板」。
|
||||
桌面端:左上角头像 →「系统设置」→「项目设置」→「项目模板」。
|
||||
对应后端:`POST api/system/column/template`,`type=get` 读取,`type=save` 写入(限 admin)。
|
||||
|
||||
## 是什么
|
||||
@ -61,11 +61,11 @@ last_verified: v1.7.90
|
||||
任何缺 `name` 或 `columns` 的条目会被服务端跳过;最终为空数组直接报「参数为空」。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「列模板」页
|
||||
1. 进入「项目设置」tab 的「项目模板」区块
|
||||
2. 「新增模板」→ 填写模板名
|
||||
3. 在列输入框输入列名(多个用逗号分隔)
|
||||
4. 重复添加多个模板
|
||||
5. 点击「保存」立即生效
|
||||
5. 点击页面底部「提交」,与同页项目设置一起保存并立即生效
|
||||
|
||||
## 与项目创建的关系
|
||||
用户「新建项目」对话框 →「选择模板」下拉就是这里维护的列表。选某个模板后,项目创建时按列名顺序生成空列,无任务卡。
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 关键词搜索不能搜到加密传输中的密文内容(已落库的文本可被搜索)
|
||||
- 不区分私聊 / 群聊:是否走 e2e 取决于接口,而非会话类型
|
||||
- 撤回 / 编辑消息的时长限制由 msg_rev_limit / msg_edit_limit 决定,与 e2e 无关
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 端到端加密设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「消息相关」→「端到端加密」。
|
||||
桌面端:左上角头像 →「系统设置」→「消息设置」→「端到端加密」。
|
||||
|
||||
字段名:`e2e_message`,开关枚举 `open` / `close`,默认 `close`。
|
||||
|
||||
@ -46,7 +46,7 @@ WebSocket 通道则会在握手后用 PGP 公钥协商一次会话密钥再传
|
||||
未在白名单内的 HTTP 接口(任务、项目、文件元数据等)仍走明文 + HTTPS。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「消息相关」
|
||||
1. 进入「系统设置」→「消息设置」
|
||||
2. 「端到端加密」选「开启」或「关闭」
|
||||
3. 页面底部「提交」保存,立即生效(无需重启)
|
||||
|
||||
|
||||
@ -21,13 +21,13 @@ negative:
|
||||
- 该限制只控制单个文件大小,不是磁盘配额或总量上限
|
||||
- 留空 = 默认 1G(前后端兜底一致);填具体值后取该值;仍受 PHP / Nginx 层服务级限制
|
||||
- 不能按用户 / 部门设差异化阈值,全局生效
|
||||
last_verified: v1.8.45
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 单文件上传大小限制
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「其他设置」→「文件上传限制」。
|
||||
桌面端:左上角头像 →「系统设置」→「文件与存储」→「上传与媒体处理」→「文件上传限制」。
|
||||
|
||||
字段名:`file_upload_limit`,整数,单位 **MB**,默认留空(前端 placeholder 显示「默认 1G」)。
|
||||
|
||||
@ -53,7 +53,7 @@ last_verified: v1.8.45
|
||||
| `file_upload_limit` | 空(按 1G 兜底) | MB |
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「其他设置」
|
||||
1. 进入「系统设置」→「文件与存储」→「上传与媒体处理」
|
||||
2. 在「文件上传限制」输入框填正整数(如 `100` = 100MB)或留空
|
||||
3. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -21,13 +21,13 @@ negative:
|
||||
- 这页只控制「文件夹打包下载」的访问名单,不控制单文件下载
|
||||
- 单文件下载没有黑白名单,权限全部由文件本身的可见范围决定
|
||||
- SYSTEM_SETTING=disabled 时不能修改
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 文件设置(打包下载权限)
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「文件」。
|
||||
桌面端:左上角头像 →「系统设置」→「文件与存储」→「权限设置」。
|
||||
对应后端:`POST api/system/setting/file`,参数 `type=save`。
|
||||
|
||||
## 是什么
|
||||
@ -47,10 +47,10 @@ last_verified: v1.7.90
|
||||
服务端只接受这 2 个字段,其他键被自动剔除。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「文件」设置页
|
||||
1. 进入「文件与存储」tab
|
||||
2. 选择策略:默认开放 / 仅管理员 / 指定人员 / 关闭
|
||||
3. 「指定人员」模式下从用户选择器勾选成员
|
||||
4. 点击「保存」即生效
|
||||
4. 点击页面底部「提交」,与媒体处理、WebDAV 设置一起保存
|
||||
|
||||
## 影响范围
|
||||
- 文件中心 / 项目附件 / 群聊文件等所有「文件夹批量下载」入口都遵守此设置
|
||||
|
||||
@ -26,13 +26,13 @@ negative:
|
||||
- SYSTEM_SETTING=disabled 时所有系统设置都不能改(演示环境常用)
|
||||
- 自动归档天数限制为 1-100 天,超出会报错
|
||||
- 邀请码留空会自动生成随机码,不能完全关闭"凭码注册"模式
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 通用设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「通用」。
|
||||
桌面端:左上角头像 →「系统设置」→「基础设置」。其他字段按领域分别位于「帐号与安全」「项目设置」「任务设置」「消息设置」「文件与存储」。
|
||||
对应后端:`POST api/system/setting`,参数 `type=save` 提交。
|
||||
|
||||
## 关键字段(按主题分组)
|
||||
@ -69,10 +69,10 @@ last_verified: v1.7.90
|
||||
- `system_alias` / `system_welcome` — 系统名称别名与欢迎语
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「通用」
|
||||
1. 进入「系统设置」,选择需要修改的领域 tab
|
||||
2. 按表单逐项填写(不在白名单内的字段会被服务端自动剔除)
|
||||
3. 点击「保存」,服务端落库到 `setting` 表(`system` 分组)
|
||||
4. 提示「保存成功」即生效,无需重启
|
||||
3. 点击该 tab 底部的「提交」,同一 tab 涉及多个配置接口时会由页面依次保存
|
||||
4. 提示「修改成功」即生效,无需重启
|
||||
|
||||
## 不支持
|
||||
- 无法在 `SYSTEM_SETTING=disabled` 环境保存(一律拒绝)
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 超过设定时长后所有人都不能撤回 / 编辑该消息,系统管理员也不例外
|
||||
- 留空(视作 0)= 不限制,不是「禁止撤回」
|
||||
- 时长以分钟为单位整数,不支持小时 / 秒级粒度
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 消息撤回与编辑时长上限
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「消息相关」→「撤回消息限制」/「修改消息限制」。
|
||||
桌面端:左上角头像 →「系统设置」→「消息设置」→「撤回消息限制」/「修改消息限制」。
|
||||
|
||||
涉及字段:
|
||||
- `msg_rev_limit` — 撤回时长上限(分钟)
|
||||
@ -50,7 +50,7 @@ last_verified: v1.7.90
|
||||
不能。超出时长后服务端会抛 `已超过 X 分钟,此消息不可撤回 / 修改` 错误,前端按钮也会变灰或弹错。系统管理员同样受限,没有"管理员强撤"的旁路。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「消息相关」
|
||||
1. 进入「系统设置」→「消息设置」
|
||||
2. 在「撤回消息限制」「修改消息限制」输入框填分钟数(如 `5`)或留空
|
||||
3. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -21,13 +21,13 @@ negative:
|
||||
- 修改策略不会触发存量用户重置密码,存量密码继续可用
|
||||
- 不支持自定义正则、最小特殊字符数等细粒度规则,只有 simple / complex 两档
|
||||
- 密码长度上限固定 32 位,无管理后台开关
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 密码策略设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「帐号相关」→「密码策略」。
|
||||
桌面端:左上角头像 →「系统设置」→「帐号与安全」→「密码策略」。
|
||||
|
||||
字段名:`password_policy`,枚举 `simple` / `complex`,默认 `simple`。
|
||||
|
||||
@ -49,7 +49,7 @@ last_verified: v1.7.90
|
||||
如果要强制全员升级密码,需要管理员手动通知用户修改,没有内置「强制重置」按钮。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「帐号相关」
|
||||
1. 进入「系统设置」→「帐号与安全」
|
||||
2. 「密码策略」选 `simple` 或 `complex`
|
||||
3. 页面底部「提交」保存
|
||||
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 不能给单个项目自定义独立优先级,全局共享一套
|
||||
- 不支持按优先级自动指派负责人 / 自动通知
|
||||
- 普通用户调用保存接口会被拒绝(仅 admin)
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 任务优先级配置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「任务优先级」。
|
||||
桌面端:左上角头像 →「系统设置」→「任务设置」→「任务优先级」。
|
||||
对应后端:`POST api/system/priority`,`type=get` 读取,`type=save` 写入(限 admin)。
|
||||
|
||||
## 是什么
|
||||
@ -54,10 +54,10 @@ last_verified: v1.7.90
|
||||
服务端使用 `Setting::normalizeTaskPriorityList()` 校验并归一化(去重 / 截断 / 字段裁剪),空数组会拒绝。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「任务优先级」页
|
||||
1. 进入「任务设置」tab 的「任务优先级」区块
|
||||
2. 「新增」追加一行 → 填写名称 / 颜色 / 天数 / 权重
|
||||
3. 拖拽或修改 `priority` 字段调整顺序
|
||||
4. 点击「保存」即时生效,所有项目共享
|
||||
4. 点击页面底部「提交」,与同页任务设置一起保存并即时生效
|
||||
|
||||
## 默认数据
|
||||
首次访问会返回系统默认的优先级(紧急 / 高 / 普通 / 低)。修改后保存即覆盖默认。
|
||||
|
||||
@ -23,13 +23,13 @@ negative:
|
||||
- 注册策略只有 open / close / invite 三档,不支持按邮箱域名白名单
|
||||
- 邀请码留空保存后系统会自动生成一个随机码,不能完全关闭"凭码可注册"模式
|
||||
- 临时帐号一旦创建无法在线一键转为正常帐号
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 注册策略设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「帐号相关」→「允许注册」。
|
||||
桌面端:左上角头像 →「系统设置」→「帐号与安全」→「允许注册」。
|
||||
|
||||
涉及字段:
|
||||
- `reg` — 注册策略
|
||||
@ -58,7 +58,7 @@ last_verified: v1.7.90
|
||||
选 `temp` 时下方可填 `temp_account_alias`(临时帐号别名),用于在用户列表展示区别。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「帐号相关」
|
||||
1. 进入「系统设置」→「帐号与安全」
|
||||
2. 选「允许注册」的三选一(同时配套填邀请码或注册身份)
|
||||
3. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 未安装 ai 插件时,分析逻辑直接跳过,不会有任何产出
|
||||
- 系统开关 = open 但项目级开关 = close 时,该项目的任务也不会被分析
|
||||
- 不会回溯分析历史任务,只对新建后进入待分析队列的任务生效
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# AI 任务自动分析设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「任务相关」→「AI任务分析」。
|
||||
桌面端:左上角头像 →「系统设置」→「任务设置」→「AI任务分析」。
|
||||
|
||||
字段名:`task_ai_auto_analyze`,开关枚举 `open` / `close`,默认 `open`。
|
||||
|
||||
@ -54,7 +54,7 @@ last_verified: v1.7.90
|
||||
|
||||
## 操作步骤
|
||||
1. 确认应用市场已安装 ai 插件
|
||||
2. 进入「系统设置」→「系统设置」→「任务相关」
|
||||
2. 进入「系统设置」→「任务设置」
|
||||
3. 「AI任务分析」选「开启」或「关闭」
|
||||
4. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -20,13 +20,13 @@ negative:
|
||||
- 该字段只控制任务详情 UI 上"可见性"控件是否常驻显示,不直接改任务的可见范围
|
||||
- 不是「公开 / 项目内 / 仅负责人」三选一,只有 open / close 两档
|
||||
- 已有任务的实际可见性数据不受本开关影响
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 任务可见性选项显示策略
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「任务相关」→「可见性选项」。
|
||||
桌面端:左上角头像 →「系统设置」→「任务设置」→「可见性选项」。
|
||||
|
||||
字段名:`task_visible`,枚举 `open` / `close`,默认 `close`。
|
||||
|
||||
@ -48,7 +48,7 @@ last_verified: v1.7.90
|
||||
- 历史任务的可见性范围维持不变
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「任务相关」
|
||||
1. 进入「系统设置」→「任务设置」
|
||||
2. 「可见性选项」选「保持」(open) 或「自动」(close)
|
||||
3. 「提交」保存,下次打开任务详情即按新策略显示
|
||||
|
||||
|
||||
@ -21,13 +21,13 @@ negative:
|
||||
- 这是消息→待办(聊天里把某条消息变成 todo)权限,与项目任务无关
|
||||
- 自己给自己设 / 取消 todo 不受开关限制
|
||||
- 关闭后任何人都不能"批量给一群人设 todo",包括非自己的成员
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 待办设置权限
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「消息相关」→「待办设置权限」。
|
||||
桌面端:左上角头像 →「系统设置」→「消息设置」→「待办设置权限」。
|
||||
|
||||
字段名:`todo_set_permission`,枚举 `open` / `close`,默认 `open`。
|
||||
|
||||
@ -48,7 +48,7 @@ last_verified: v1.7.90
|
||||
后端校验点:`WebSocketDialogMsg::setTodoRemind` 与 `DialogController::msg__todo_indicate` 两处都会读 `todo_set_permission`,关闭时校验 `checkTodoOwnerPermission`。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「消息相关」
|
||||
1. 进入「系统设置」→「消息设置」
|
||||
2. 「待办设置权限」选「允许」或「禁止」
|
||||
3. 「提交」保存,立即生效
|
||||
|
||||
|
||||
@ -22,13 +22,13 @@ negative:
|
||||
- 不会回溯处理历史上传的视频,只对新上传文件生效
|
||||
- 转码 / 压缩失败时回退保留原文件,不会丢消息
|
||||
- 不能选目标分辨率 / 码率,参数固定
|
||||
last_verified: v1.7.90
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 视频转码与压缩设置
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「系统设置」标签 →「消息相关」→「视频转换」/「视频压缩」。
|
||||
桌面端:左上角头像 →「系统设置」→「文件与存储」→「上传与媒体处理」→「视频转换」/「视频压缩」。
|
||||
|
||||
涉及字段:
|
||||
- `convert_video` — 视频转码开关,默认 `close`
|
||||
@ -55,7 +55,7 @@ last_verified: v1.7.90
|
||||
原文件被处理后产物(转码后 MP4 / 压缩后 MP4)替代原文件入库,**原始 MOV / WEBM 不保留**。开关推送给前端后会在聊天预览处显示「视频处理中」气泡。
|
||||
|
||||
## 操作步骤
|
||||
1. 进入「系统设置」→「系统设置」→「消息相关」
|
||||
1. 进入「系统设置」→「文件与存储」→「上传与媒体处理」
|
||||
2. 「视频转换」「视频压缩」分别选「开启」或「关闭」
|
||||
3. 「提交」保存,立即对后续上传生效
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ last_verified: v1.8.89
|
||||
# 管理员启用和配置 WebDAV
|
||||
|
||||
## 入口
|
||||
桌面端:左上角头像 →「系统设置」→「文件」→「WebDAV」。
|
||||
桌面端:左上角头像 →「系统设置」→「文件与存储」→「WebDAV」。
|
||||
|
||||
## 配置项
|
||||
- **启用 WebDAV**:全局协议开关。关闭后 `/dav/` 立即停止提供服务,已有应用密码不会被展示为登录密码。
|
||||
@ -40,7 +40,7 @@ last_verified: v1.8.89
|
||||
|
||||
## 启用流程
|
||||
1. 确认 DooTask 的外部地址使用 HTTPS;生产环境的 DAV 协议入口拒绝明文 HTTP。
|
||||
2. 打开「文件」设置页并检查路径冲突提示。
|
||||
2. 打开「文件与存储」tab 并检查路径冲突提示。
|
||||
3. 如存在冲突,点击「查看冲突文件」,可复制完整冲突路径,并根据拥有者和文件 ID 找到记录。管理员可直接重命名任意冲突文件;重命名其他成员的私人文件前会二次确认并记录审计,但不会开放文件内容、移动或删除权限。管理员本人拥有目标文件,或目标文件已通过现有共享权限向管理员开放时,列表才显示「打开位置」,并自动定位到「我的文件」或「共享文件」;没有查看权限时不会显示该入口。保留其中一条并重命名或删除同组内其余文件后,点击「刷新检测」确认冲突清零。服务端会硬性拒绝在冲突未处理时开启。
|
||||
4. 选择允许范围与凭据限制,开启 WebDAV 后点击「提交」。
|
||||
5. 通知获准用户从「文件」页右上角「···」创建自己的应用密码,管理员不代替用户生成或查看密码。
|
||||
|
||||
@ -19,30 +19,32 @@ related_pages: []
|
||||
negative:
|
||||
- 移动端通常不展示「系统设置」入口,需用桌面端 / 网页后台
|
||||
- 普通成员看不到该页,无入口
|
||||
- 这 4 个 tab 只是「系统设置」一级菜单内的内容;邮件、AI、签到等是同级别的其他左侧菜单项,不在这个页内
|
||||
last_verified: v1.7.90
|
||||
- 这 6 个 tab 只是「系统设置」一级菜单内的内容;邮件、AI、签到等是同级别的其他左侧菜单项,不在这个页内
|
||||
last_verified: v1.8.89
|
||||
---
|
||||
|
||||
# 系统设置页面总览
|
||||
|
||||
## 路径
|
||||
桌面端:左上角头像 → 下拉菜单 →「系统设置」(仅管理员可见)→ 左侧子菜单选「系统设置」一级项。
|
||||
打开后顶部是 4 个并列的 tab,对应同一 URL 下的 4 个子表单。
|
||||
打开后顶部是 6 个并列的 tab,对应同一 URL 下按领域整理的设置表单。
|
||||
|
||||
移动端:不展示,需用桌面端或浏览器后台。
|
||||
|
||||
## 4 个 tab 一句话目录
|
||||
## 6 个 tab 一句话目录
|
||||
|
||||
| Tab | name | 内容 |
|
||||
|---|---|---|
|
||||
| **系统设置** | `setting` | 全站通用开关:注册、密码、消息、视频、上传、欢迎语等几十项;详见 [[system-setting.general.howto]] |
|
||||
| **任务优先级** | `taskPriority` | 自定义任务优先级的颜色、名称、提前提醒天数;详见 [[system-setting.priority.howto]] |
|
||||
| **项目模板** | `columnTemplate` | 新建项目时可选的预置「列模板」清单(如 看板列、状态列);详见 [[system-setting.column-template.howto]] |
|
||||
| **文件设置** | `fileSetting` | 文件相关策略(如打包下载权限、缩略图);详见 [[system-setting.file.howto]] |
|
||||
| **基础设置** | `general` | 系统别名和仪表盘欢迎语;详见 [[system-setting.general.howto]] |
|
||||
| **帐号与安全** | `account` | 注册方式、临时帐号、登录验证码和密码策略 |
|
||||
| **项目设置** | `project` | 项目创建与邀请权限、部门负责人视角、项目模板;详见 [[system-setting.column-template.howto]] |
|
||||
| **任务设置** | `task` | 任务默认规则、提醒、AI 分析和任务优先级;详见 [[system-setting.priority.howto]] |
|
||||
| **消息设置** | `message` | 群聊、私聊、匿名消息、加密、撤回和待办权限 |
|
||||
| **文件与存储** | `file` | 上传与媒体处理、打包下载权限和 WebDAV;详见 [[system-setting.file.howto]] |
|
||||
|
||||
默认进入「系统设置」tab。切换 tab 不会重置已填未保存的表单值,但每个 tab 单独保存。
|
||||
默认进入「基础设置」tab。每个 tab 底部只有一组「提交 / 重置」;一个 tab 涉及多类配置时,提交会依次调用对应接口并统一反馈结果。
|
||||
|
||||
## 同级菜单(不在这 4 tab 内)
|
||||
## 同级菜单(不在这 6 个 tab 内)
|
||||
左侧子菜单的其他独立项见 [[system-setting.entry.menu-map]],包括邮件、会议、AI 设置、AI 机器人、签到、APP 推送、第三方接入、License 等。
|
||||
|
||||
## 权限要求
|
||||
|
||||
@ -30,15 +30,15 @@ const LINKS = {
|
||||
setting_license: { route: { name: 'manage-setting-license' } },
|
||||
setting_password: { route: { name: 'manage-setting-password' } },
|
||||
setting_email: { route: { name: 'manage-setting-email' } },
|
||||
setting_system: { route: { name: 'manage-setting-system', query: { tab: 'setting' } } },
|
||||
setting_system: { route: { name: 'manage-setting-system', query: { tab: 'general' } } },
|
||||
setting_device: { route: { name: 'manage-setting-device' } },
|
||||
setting_version: { route: { name: 'manage-setting-version' } },
|
||||
setting_delete: { route: { name: 'manage-setting-delete' } },
|
||||
|
||||
// 系统设置内的二级 Tab(system.vue 据 query.tab 切换)
|
||||
setting_system_task_priority: { route: { name: 'manage-setting-system', query: { tab: 'taskPriority' } } },
|
||||
setting_system_column_template: { route: { name: 'manage-setting-system', query: { tab: 'columnTemplate' } } },
|
||||
setting_system_file: { route: { name: 'manage-setting-system', query: { tab: 'fileSetting' } } },
|
||||
setting_system_task_priority: { route: { name: 'manage-setting-system', query: { tab: 'task' } } },
|
||||
setting_system_column_template: { route: { name: 'manage-setting-system', query: { tab: 'project' } } },
|
||||
setting_system_file: { route: { name: 'manage-setting-system', query: { tab: 'file' } } },
|
||||
};
|
||||
|
||||
export const CATALOG_IDS = Object.keys(LINKS);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="setting-component-item">
|
||||
<Form ref="formDatum" label-width="auto" @submit.native.prevent>
|
||||
<div :class="{'setting-component-item': !embedded}">
|
||||
<div>
|
||||
<Row class="setting-template">
|
||||
<Col span="8">{{$L('名称')}}</Col>
|
||||
<Col span="16">{{$L('项目模板')}}</Col>
|
||||
@ -18,9 +18,9 @@
|
||||
<TagInput v-model="item.columns"/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Button type="default" icon="md-add" @click="addDatum">{{$L('添加模板')}}</Button>
|
||||
</Form>
|
||||
<div class="setting-footer">
|
||||
<Button class="setting-add-action" type="default" icon="md-add" @click="addDatum">{{$L('添加模板')}}</Button>
|
||||
</div>
|
||||
<div v-if="!embedded" class="setting-footer">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{$L('提交')}}</Button>
|
||||
<Button :loading="loadIng > 0" @click="resetForm">{{$L('重置')}}</Button>
|
||||
</div>
|
||||
@ -32,6 +32,9 @@ import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'SystemColumnTemplate',
|
||||
props: {
|
||||
embedded: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
@ -67,11 +70,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
submitForm() {
|
||||
this.$refs.formDatum.validate((valid) => {
|
||||
if (valid) {
|
||||
this.systemSetting(true);
|
||||
}
|
||||
})
|
||||
this.systemSetting(true);
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
@ -89,16 +88,16 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
systemSetting(save) {
|
||||
systemSetting(save, silent = false) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
return this.$store.dispatch("call", {
|
||||
url: 'system/column/template?type=' + (save ? 'save' : 'get'),
|
||||
method: 'post',
|
||||
data: {
|
||||
list: this.formDatum
|
||||
},
|
||||
}).then(({data}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.messageSuccess('修改成功');
|
||||
}
|
||||
this.$store.state.columnTemplate = $A.cloneJSON(data).map(item => {
|
||||
@ -108,9 +107,12 @@ export default {
|
||||
return item;
|
||||
});
|
||||
}).catch(({msg}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.modalError(msg);
|
||||
}
|
||||
if (silent) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
});
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div class="setting-component-item">
|
||||
<Form
|
||||
ref="formData"
|
||||
:model="formData"
|
||||
:rules="ruleData"
|
||||
v-bind="formOptions"
|
||||
@submit.native.prevent>
|
||||
<div :class="{'setting-component-item': !embedded}">
|
||||
<div>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{$L('权限设置')}}</h3>
|
||||
<div class="form-box">
|
||||
@ -71,8 +66,8 @@
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
<div class="setting-footer">
|
||||
</div>
|
||||
<div v-if="!embedded" class="setting-footer">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button>
|
||||
<Button :loading="loadIng > 0" @click="resetForm">{{ $L('重置') }}</Button>
|
||||
</div>
|
||||
@ -103,6 +98,9 @@ import UserSelect from "../../../../components/UserSelect.vue";
|
||||
export default {
|
||||
name: "SystemFileSetting",
|
||||
components: {UserSelect},
|
||||
props: {
|
||||
embedded: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
@ -170,7 +168,6 @@ export default {
|
||||
formData: {
|
||||
|
||||
},
|
||||
ruleData: {},
|
||||
}
|
||||
},
|
||||
|
||||
@ -179,16 +176,12 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['formOptions', 'userId']),
|
||||
...mapState(['userId']),
|
||||
},
|
||||
|
||||
methods: {
|
||||
submitForm() {
|
||||
this.$refs.formData.validate((valid) => {
|
||||
if (valid) {
|
||||
this.systemSetting(true);
|
||||
}
|
||||
})
|
||||
this.systemSetting(true);
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
@ -271,7 +264,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
systemSetting(save) {
|
||||
systemSetting(save, silent = false) {
|
||||
this.loadIng++;
|
||||
const fileRequest = () => this.$store.dispatch("call", {
|
||||
url: 'system/setting/file?type=' + (save ? 'save' : 'all'),
|
||||
@ -292,17 +285,20 @@ export default {
|
||||
webDavStatusRequest(),
|
||||
])))
|
||||
: Promise.all([fileRequest(), webDavRequest(), webDavStatusRequest()]);
|
||||
request.then(([fileSetting, webDavSetting, webDavStatus]) => {
|
||||
if (save) {
|
||||
return request.then(([fileSetting, webDavSetting, webDavStatus]) => {
|
||||
if (save && !silent) {
|
||||
$A.messageSuccess('修改成功');
|
||||
}
|
||||
this.formData = Object.assign({}, fileSetting.data, webDavSetting.data);
|
||||
this.webDavStatus = webDavStatus.data || {};
|
||||
this.formDatum_bak = $A.cloneJSON(this.formData);
|
||||
}).catch(({msg}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.modalError(msg);
|
||||
}
|
||||
if (silent) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
});
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
:model="formDatum"
|
||||
v-bind="formOptions"
|
||||
@submit.native.prevent>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{ $L('帐号相关') }}</h3>
|
||||
<div v-if="scope === 'account'" class="block-setting-box">
|
||||
<h3>{{ $L('帐号与安全') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('允许注册')" prop="reg">
|
||||
<RadioGroup v-model="formDatum.reg">
|
||||
@ -65,8 +65,8 @@
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{ $L('项目相关') }}</h3>
|
||||
<div v-if="scope === 'project'" class="block-setting-box">
|
||||
<h3>{{ $L('项目设置') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('邀请项目')" prop="projectInvite">
|
||||
<RadioGroup v-model="formDatum.project_invite">
|
||||
@ -104,8 +104,14 @@
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{ $L('任务相关') }}</h3>
|
||||
<div v-if="scope === 'project'" class="block-setting-box">
|
||||
<h3>{{ $L('项目模板') }}</h3>
|
||||
<div class="form-box">
|
||||
<SystemColumnTemplate ref="columnTemplate" embedded/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="scope === 'task'" class="block-setting-box">
|
||||
<h3>{{ $L('任务设置') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('自动归档')" prop="autoArchived">
|
||||
<RadioGroup :value="formDatum.auto_archived" @on-change="formArchived">
|
||||
@ -171,8 +177,14 @@
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{ $L('消息相关') }}</h3>
|
||||
<div v-if="scope === 'task'" class="block-setting-box">
|
||||
<h3>{{ $L('任务优先级') }}</h3>
|
||||
<div class="form-box">
|
||||
<SystemTaskPriority ref="taskPriority" embedded/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="scope === 'message'" class="block-setting-box">
|
||||
<h3>{{ $L('消息设置') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('自动进入全员群')" prop="allGroupAutoin">
|
||||
<RadioGroup v-model="formDatum.all_group_autoin">
|
||||
@ -235,22 +247,6 @@
|
||||
<div v-if="formDatum.todo_set_permission == 'open'" class="form-tip">{{$L('允许:所有成员可设置/取消他人待办。')}}</div>
|
||||
<div v-else class="form-tip">{{$L('禁止:仅本人、系统管理员、群主(含群管理员)、项目负责人(含项目管理员)、任务负责人可设置/取消待办。')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('视频转换')" prop="convertVideo">
|
||||
<RadioGroup v-model="formDatum.convert_video">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
<Radio label="close">{{$L('关闭')}}</Radio>
|
||||
</RadioGroup>
|
||||
<div v-if="formDatum.convert_video == 'open'" class="form-tip">{{$L('将MOV、WEBM格式的视频转换为MP4格式。')}}</div>
|
||||
<div v-else class="form-tip">{{$L('关闭视频格式转换功能。')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('视频压缩')" prop="compressVideo">
|
||||
<RadioGroup v-model="formDatum.compress_video">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
<Radio label="close">{{$L('关闭')}}</Radio>
|
||||
</RadioGroup>
|
||||
<div v-if="formDatum.compress_video == 'open'" class="form-tip">{{$L('对MP4格式的视频进行压缩处理。')}}</div>
|
||||
<div v-else class="form-tip">{{$L('关闭视频压缩功能。')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('端到端加密')" prop="e2eMessage">
|
||||
<RadioGroup v-model="formDatum.e2e_message">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
@ -281,8 +277,8 @@
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-setting-box">
|
||||
<h3>{{ $L('其他设置') }}</h3>
|
||||
<div v-if="scope === 'general'" class="block-setting-box">
|
||||
<h3>{{ $L('基础设置') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('系统别名')" prop="system_alias">
|
||||
<div style="width: 220px;">
|
||||
@ -296,6 +292,11 @@
|
||||
</div>
|
||||
<div class="form-tip">{{$L('仪表盘欢迎词,(*)代表用户昵称', '{username}')}}</div>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="scope === 'file'" class="block-setting-box">
|
||||
<h3>{{ $L('上传与媒体处理') }}</h3>
|
||||
<div class="form-box">
|
||||
<FormItem :label="$L('图片优化')" prop="image_compress">
|
||||
<RadioGroup v-model="formDatum.image_compress">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
@ -324,8 +325,25 @@
|
||||
</div>
|
||||
<div class="form-tip">{{$L('包含消息发送的文件')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('视频转换')" prop="convertVideo">
|
||||
<RadioGroup v-model="formDatum.convert_video">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
<Radio label="close">{{$L('关闭')}}</Radio>
|
||||
</RadioGroup>
|
||||
<div v-if="formDatum.convert_video == 'open'" class="form-tip">{{$L('将MOV、WEBM格式的视频转换为MP4格式。')}}</div>
|
||||
<div v-else class="form-tip">{{$L('关闭视频格式转换功能。')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('视频压缩')" prop="compressVideo">
|
||||
<RadioGroup v-model="formDatum.compress_video">
|
||||
<Radio label="open">{{$L('开启')}}</Radio>
|
||||
<Radio label="close">{{$L('关闭')}}</Radio>
|
||||
</RadioGroup>
|
||||
<div v-if="formDatum.compress_video == 'open'" class="form-tip">{{$L('对MP4格式的视频进行压缩处理。')}}</div>
|
||||
<div v-else class="form-tip">{{$L('关闭视频压缩功能。')}}</div>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
<SystemFileSetting v-if="scope === 'file'" ref="fileSetting" embedded/>
|
||||
</Form>
|
||||
<div class="setting-footer">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{$L('提交')}}</Button>
|
||||
@ -337,11 +355,30 @@
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
import UserSelect from "../../../../components/UserSelect.vue";
|
||||
import SystemColumnTemplate from "./SystemColumnTemplate.vue";
|
||||
import SystemFileSetting from "./SystemFileSetting.vue";
|
||||
import SystemTaskPriority from "./SystemTaskPriority.vue";
|
||||
|
||||
const SCOPE_FIELDS = {
|
||||
general: ['system_alias', 'system_welcome'],
|
||||
account: ['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy'],
|
||||
project: ['project_invite', 'project_add_permission', 'project_add_userids', 'department_owner_project_view'],
|
||||
task: ['auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'task_user_limit', 'unclaimed_task_reminder', 'unclaimed_task_reminder_time', 'task_ai_auto_analyze'],
|
||||
message: ['chat_information', 'anon_message', 'e2e_message', 'msg_rev_limit', 'msg_edit_limit', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'todo_set_permission'],
|
||||
file: ['convert_video', 'compress_video', 'image_compress', 'image_quality', 'image_save_local', 'file_upload_limit'],
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'SystemSetting',
|
||||
|
||||
components: {UserSelect},
|
||||
components: {SystemTaskPriority, SystemFileSetting, SystemColumnTemplate, UserSelect},
|
||||
|
||||
props: {
|
||||
scope: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -380,13 +417,56 @@ export default {
|
||||
submitForm() {
|
||||
this.$refs.formDatum.validate((valid) => {
|
||||
if (valid) {
|
||||
this.systemSetting(true);
|
||||
this.saveAll();
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.formDatum = $A.cloneJSON(this.formDatum_bak);
|
||||
this.$refs.columnTemplate?.resetForm();
|
||||
this.$refs.taskPriority?.resetForm();
|
||||
this.$refs.fileSetting?.resetForm();
|
||||
},
|
||||
|
||||
saveAll() {
|
||||
if (this.loadIng > 0) {
|
||||
return;
|
||||
}
|
||||
const extra = {
|
||||
project: () => this.$refs.columnTemplate.systemSetting(true, true),
|
||||
task: () => this.$refs.taskPriority.systemSetting(true, true),
|
||||
file: () => this.$refs.fileSetting.systemSetting(true, true),
|
||||
}[this.scope];
|
||||
this.loadIng++;
|
||||
this.saveSystemScope()
|
||||
.then(() => extra?.())
|
||||
.then(() => {
|
||||
$A.messageSuccess('修改成功');
|
||||
this.$store.dispatch("getUserInfo").catch(() => {});
|
||||
})
|
||||
.catch(msg => $A.modalError(msg))
|
||||
.finally(() => {
|
||||
this.loadIng--;
|
||||
});
|
||||
},
|
||||
|
||||
saveSystemScope() {
|
||||
const fields = SCOPE_FIELDS[this.scope];
|
||||
const currentValues = fields.reduce((data, field) => {
|
||||
data[field] = $A.cloneJSON(this.formDatum[field]);
|
||||
return data;
|
||||
}, {});
|
||||
return this.$store.dispatch("call", {
|
||||
url: 'system/setting?type=all',
|
||||
method: 'post',
|
||||
}).then(({data}) => this.$store.dispatch("call", {
|
||||
url: 'system/setting?type=save',
|
||||
method: 'post',
|
||||
data: Object.assign({}, data, currentValues),
|
||||
})).then(({data}) => {
|
||||
this.setSystemDatum(data);
|
||||
}).catch(({msg}) => Promise.reject(msg));
|
||||
},
|
||||
|
||||
formArchived(value) {
|
||||
@ -397,29 +477,36 @@ export default {
|
||||
this.formDatum = { ...this.formDatum, unclaimed_task_reminder: value };
|
||||
},
|
||||
|
||||
systemSetting(save) {
|
||||
systemSetting(save, silent = false) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
return this.$store.dispatch("call", {
|
||||
url: 'system/setting?type=' + (save ? 'save' : 'all'),
|
||||
method: 'post',
|
||||
data: this.formDatum,
|
||||
}).then(({data}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.messageSuccess('修改成功');
|
||||
this.$store.dispatch("getUserInfo").catch(() => {});
|
||||
}
|
||||
this.formDatum = data;
|
||||
this.formDatum_bak = $A.cloneJSON(this.formDatum);
|
||||
this.$store.state.systemConfig = Object.assign(this.formDatum_bak, {
|
||||
__state: "success",
|
||||
})
|
||||
this.setSystemDatum(data);
|
||||
}).catch(({msg}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.modalError(msg);
|
||||
}
|
||||
if (silent) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
});
|
||||
},
|
||||
|
||||
setSystemDatum(data) {
|
||||
this.formDatum = data;
|
||||
this.formDatum_bak = $A.cloneJSON(data);
|
||||
this.$store.state.systemConfig = Object.assign({}, this.formDatum_bak, {
|
||||
__state: "success",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="setting-component-item system-task-priority">
|
||||
<Form ref="formDatum" label-width="auto" @submit.native.prevent>
|
||||
<div :class="{'setting-component-item': !embedded, 'system-task-priority': true}">
|
||||
<div>
|
||||
<Row class="setting-color color-label-box">
|
||||
<Col span="2">{{$L('默认')}}</Col>
|
||||
<Col span="10">{{$L('名称')}}</Col>
|
||||
@ -40,11 +40,11 @@
|
||||
</Col>
|
||||
</Row>
|
||||
</RadioGroup>
|
||||
<div class="priority-add-action">
|
||||
<div class="setting-add-action">
|
||||
<Button type="default" icon="md-add" @click="addDatum">{{$L('添加优先级')}}</Button>
|
||||
</div>
|
||||
</Form>
|
||||
<div class="setting-footer">
|
||||
</div>
|
||||
<div v-if="!embedded" class="setting-footer">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{$L('提交')}}</Button>
|
||||
<Button :loading="loadIng > 0" @click="resetForm">{{$L('重置')}}</Button>
|
||||
</div>
|
||||
@ -56,6 +56,9 @@ import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'SystemTaskPriority',
|
||||
props: {
|
||||
embedded: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
@ -101,11 +104,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
submitForm() {
|
||||
this.$refs.formDatum.validate((valid) => {
|
||||
if (valid) {
|
||||
this.systemSetting(true);
|
||||
}
|
||||
})
|
||||
this.systemSetting(true);
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
@ -141,24 +140,27 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
systemSetting(save) {
|
||||
systemSetting(save, silent = false) {
|
||||
this.loadIng++;
|
||||
this.applyDefaultIndex();
|
||||
this.$store.dispatch("call", {
|
||||
return this.$store.dispatch("call", {
|
||||
url: 'system/priority?type=' + (save ? 'save' : 'get'),
|
||||
method: 'post',
|
||||
data: {
|
||||
list: this.formDatum
|
||||
},
|
||||
}).then(({data}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.messageSuccess('修改成功');
|
||||
}
|
||||
this.$store.state.taskPriority = $A.cloneJSON(data);
|
||||
}).catch(({msg}) => {
|
||||
if (save) {
|
||||
if (save && !silent) {
|
||||
$A.modalError(msg);
|
||||
}
|
||||
if (silent) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}).finally(_ => {
|
||||
this.loadIng--;
|
||||
});
|
||||
|
||||
@ -1,17 +1,23 @@
|
||||
<template>
|
||||
<div class="setting-item submit">
|
||||
<Tabs v-model="tabAction">
|
||||
<TabPane :label="$L('系统设置')" name="setting">
|
||||
<SystemSetting/>
|
||||
<TabPane :label="$L('基础设置')" name="general">
|
||||
<SystemSetting scope="general"/>
|
||||
</TabPane>
|
||||
<TabPane :label="$L('任务优先级')" name="taskPriority">
|
||||
<SystemTaskPriority/>
|
||||
<TabPane :label="$L('帐号与安全')" name="account">
|
||||
<SystemSetting scope="account"/>
|
||||
</TabPane>
|
||||
<TabPane :label="$L('项目模板')" name="columnTemplate">
|
||||
<SystemColumnTemplate/>
|
||||
<TabPane :label="$L('项目设置')" name="project">
|
||||
<SystemSetting scope="project"/>
|
||||
</TabPane>
|
||||
<TabPane :label="$L('文件设置')" name="fileSetting">
|
||||
<SystemFileSetting/>
|
||||
<TabPane :label="$L('任务设置')" name="task">
|
||||
<SystemSetting scope="task"/>
|
||||
</TabPane>
|
||||
<TabPane :label="$L('消息设置')" name="message">
|
||||
<SystemSetting scope="message"/>
|
||||
</TabPane>
|
||||
<TabPane :label="$L('文件与存储')" name="file">
|
||||
<SystemSetting scope="file"/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
@ -19,14 +25,17 @@
|
||||
|
||||
<script>
|
||||
import SystemSetting from "./components/SystemSetting";
|
||||
import SystemTaskPriority from "./components/SystemTaskPriority";
|
||||
import SystemColumnTemplate from "./components/SystemColumnTemplate";
|
||||
import SystemFileSetting from "./components/SystemFileSetting";
|
||||
|
||||
const VALID_TABS = ['setting', 'taskPriority', 'columnTemplate', 'fileSetting'];
|
||||
const VALID_TABS = ['general', 'account', 'project', 'task', 'message', 'file'];
|
||||
const LEGACY_TABS = {
|
||||
setting: 'general',
|
||||
taskPriority: 'task',
|
||||
columnTemplate: 'project',
|
||||
fileSetting: 'file',
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {SystemColumnTemplate, SystemTaskPriority, SystemSetting, SystemFileSetting},
|
||||
components: {SystemSetting},
|
||||
data() {
|
||||
return {
|
||||
tabAction: this.tabFromRoute(),
|
||||
@ -41,7 +50,10 @@ export default {
|
||||
methods: {
|
||||
tabFromRoute() {
|
||||
const tab = this.$route?.query?.tab;
|
||||
return VALID_TABS.includes(tab) ? tab : 'setting';
|
||||
if (VALID_TABS.includes(tab)) {
|
||||
return tab;
|
||||
}
|
||||
return LEGACY_TABS[tab] || 'general';
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -233,6 +233,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.setting-add-action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.setting-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user