From 1c90a11211abe1e12d1c6b970b7a32f25eb40ecb Mon Sep 17 00:00:00 2001 From: kuaifan Date: Wed, 30 Sep 2026 12:44:18 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E7=BB=9F=E4=B8=80=E7=AE=A1=E7=90=86=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E5=8A=9F=E8=83=BD=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SystemFunctions 模块与 api/apps/functions 接口,读取 system-functions 应用的安装状态和开关字段 - 工作报告、日历、我的收藏、最近打开、签到打卡的入口(应用中心、头像菜单、左侧导航)统一受开关控制,卸载后全部隐藏 - 导出签到数据随签到打卡一起隐藏;日历停用后访问日历页跳回仪表盘 - 头像菜单按分组计算分割线,隐藏菜单项后不留多余分割线 - 新增迁移:无配置时自动安装并全部启用,已有配置不覆盖 - 同步 ai-kb 与 api-map --- app/Http/Controllers/Api/AppsController.php | 23 ++++ app/Module/SystemFunctions.php | 103 ++++++++++++++++ ...30_000001_install_system_functions_app.php | 68 +++++++++++ resources/ai-kb/_meta/feature-map.yaml | 13 ++ .../ai-kb/zh/concept/application/center.md | 5 +- .../zh/concept/system-functions/overview.md | 49 ++++++++ .../zh/faq/system-functions/entry-missing.md | 42 +++++++ resources/ai-kb/zh/howto/checkin/export.md | 6 +- .../ai-kb/zh/howto/data-export/checkin.md | 6 +- .../ai-kb/zh/howto/system-functions/toggle.md | 53 ++++++++ resources/ai-kb/zh/menu-map/calendar/entry.md | 8 +- resources/ai-kb/zh/menu-map/checkin/entry.md | 8 +- resources/ai-kb/zh/menu-map/favorite/entry.md | 8 +- .../zh/menu-map/menu-navigation/checkin.md | 8 +- .../zh/menu-map/menu-navigation/favorite.md | 8 +- .../zh/menu-map/menu-navigation/recent.md | 8 +- resources/ai-kb/zh/menu-map/report/entry.md | 8 +- .../components/AIAssistant/action-executor.js | 3 + resources/assets/js/pages/manage.vue | 115 +++++++++++------- .../assets/js/pages/manage/application.vue | 15 ++- resources/assets/js/store/actions.js | 8 +- resources/assets/js/store/getters.js | 11 ++ .../assets/js/store/modules/appBadges.js | 7 +- resources/assets/js/store/mutations.js | 9 ++ resources/assets/js/store/state.js | 3 + routes/api-map.md | 3 +- 26 files changed, 528 insertions(+), 70 deletions(-) create mode 100644 app/Module/SystemFunctions.php create mode 100644 database/migrations/2026_09_30_000001_install_system_functions_app.php create mode 100644 resources/ai-kb/zh/concept/system-functions/overview.md create mode 100644 resources/ai-kb/zh/faq/system-functions/entry-missing.md create mode 100644 resources/ai-kb/zh/howto/system-functions/toggle.md diff --git a/app/Http/Controllers/Api/AppsController.php b/app/Http/Controllers/Api/AppsController.php index 05e0eea64..556aaea0d 100644 --- a/app/Http/Controllers/Api/AppsController.php +++ b/app/Http/Controllers/Api/AppsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api; use App\Models\User; use App\Module\Badge; use App\Module\Base; +use App\Module\SystemFunctions; use Request; /** @@ -14,6 +15,7 @@ use Request; * api/apps/badge/set -> badge__set() 应用密钥鉴权,绝对设置/清除角标 * api/apps/badge/clear -> badge__clear() 当前用户 token 鉴权,清除自己的角标 * api/apps/badge/list -> badge__list() 当前用户 token 鉴权,拉取自己全部角标(初始同步) + * api/apps/functions -> functions() 当前用户 token 鉴权,获取「系统功能」应用的功能开关 */ class AppsController extends AbstractController { @@ -91,4 +93,25 @@ class AppsController extends AbstractController $user = User::auth(); return Base::retSuccess('success', Badge::userBadges((int)$user->userid)); } + + /** + * @api {get} api/apps/functions 获取系统功能开关 + * + * @apiDescription 需要token身份。返回「系统功能」应用是否已安装,以及工作报告、日历、我的收藏、最近打开、签到打卡的启用状态。 + * 应用未安装时全部为 false;前端据此统一显示或隐藏这些功能的入口。 + * @apiVersion 1.0.0 + * @apiGroup apps + * @apiName functions + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息 + * @apiSuccess {Object} data 返回数据 + * @apiSuccess {Boolean} data.installed 应用是否已安装 + * @apiSuccess {Object} data.functions 功能开关(report/calendar/favorite/recent/checkin => Boolean) + */ + public function functions() + { + User::auth(); + return Base::retSuccess('success', SystemFunctions::status()); + } } diff --git a/app/Module/SystemFunctions.php b/app/Module/SystemFunctions.php new file mode 100644 index 000000000..1e2517926 --- /dev/null +++ b/app/Module/SystemFunctions.php @@ -0,0 +1,103 @@ + 应用字段名 + */ + const FUNCTIONS = [ + 'report' => 'REPORT', + 'calendar' => 'CALENDAR', + 'favorite' => 'FAVORITE', + 'recent' => 'RECENT', + 'checkin' => 'CHECKIN', + ]; + + /** + * 应用安装配置文件路径 + * + * @return string + */ + public static function configFile(): string + { + return base_path('docker/appstore/config/' . self::APP_ID . '/config.yml'); + } + + /** + * 获取应用安装状态与各功能的启用状态。 + * 应用未安装时全部为停用;已安装时缺失的字段视为启用(字段默认值即启用)。 + * + * @return array ['installed' => bool, 'functions' => ['report' => bool, ...]] + */ + public static function status(): array + { + $key = 'system_functions_status'; + if (RequestContext::has($key)) { + return RequestContext::get($key); + } + $params = Apps::isInstalled(self::APP_ID) ? self::readParams() : null; + $functions = []; + foreach (self::FUNCTIONS as $name => $field) { + $functions[$name] = $params !== null && self::isOn($params[$field] ?? true); + } + return RequestContext::save($key, [ + 'installed' => $params !== null, + 'functions' => $functions, + ]); + } + + /** + * 指定功能是否启用 + * + * @param string $name 功能标识(report/calendar/favorite/recent/checkin) + * @return bool + */ + public static function enabled(string $name): bool + { + return (bool)(self::status()['functions'][$name] ?? false); + } + + /** + * 读取应用安装参数 + * + * @return array + */ + private static function readParams(): array + { + try { + $config = Yaml::parseFile(self::configFile()); + } catch (\Throwable $e) { + return []; + } + $params = is_array($config) ? ($config['params'] ?? []) : []; + return is_array($params) ? $params : []; + } + + /** + * 开关值是否为开启(兼容布尔值与字符串) + * + * @param mixed $value + * @return bool + */ + private static function isOn($value): bool + { + if (is_string($value)) { + return !in_array(strtolower(trim($value)), ['', '0', 'false', 'off', 'no'], true); + } + return (bool)$value; + } +} diff --git a/database/migrations/2026_09_30_000001_install_system_functions_app.php b/database/migrations/2026_09_30_000001_install_system_functions_app.php new file mode 100644 index 000000000..49ee85eb0 --- /dev/null +++ b/database/migrations/2026_09_30_000001_install_system_functions_app.php @@ -0,0 +1,68 @@ + date('Y-m-d H:i:s'), + 'install_num' => 1, + 'install_version' => $this->latestVersion($appId), + 'status' => 'installed', + 'params' => [ + 'REPORT' => true, + 'CALENDAR' => true, + 'FAVORITE' => true, + 'RECENT' => true, + 'CHECKIN' => true, + ], + 'resources' => [ + 'cpu_limit' => '0', + 'memory_limit' => '0', + ], + ]; + if (@file_put_contents($configFile, Yaml::dump($config, 4, 4)) === false) { + throw new RuntimeException("无法写入配置文件:{$configFile}"); + } + } + + public function down(): void + { + // 不回滚:无法区分配置是本迁移写入的还是管理员后续调整的 + } + + /** + * 应用包内的最新版本号;应用包尚未同步到本地时使用首个版本。 + */ + private function latestVersion(string $appId): string + { + $latest = '1.0.0'; + foreach (glob(base_path("docker/appstore/apps/{$appId}/*"), GLOB_ONLYDIR) ?: [] as $dir) { + $version = basename($dir); + if (preg_match('/^v?\d+(\.\d+){1,2}$/', $version) && version_compare(ltrim($version, 'v'), ltrim($latest, 'v'), '>')) { + $latest = $version; + } + } + return $latest; + } +}; diff --git a/resources/ai-kb/_meta/feature-map.yaml b/resources/ai-kb/_meta/feature-map.yaml index 358bfe3ea..18238040a 100644 --- a/resources/ai-kb/_meta/feature-map.yaml +++ b/resources/ai-kb/_meta/feature-map.yaml @@ -663,6 +663,19 @@ features: - compliance.howto - compliance.entry.menu-map + - id: system-functions + name: 系统功能(内置功能入口开关:工作报告/日历/我的收藏/最近打开/签到打卡) + scope: admin + batch: B4 + priority: P0 + chunk_count_est: 3 + owner: ~ + status: drafted + chunks: + - system-functions.overview.concept + - system-functions.toggle.howto + - system-functions.entry-missing.faq + - id: appstore name: 应用市场(AppStore 管理) scope: admin diff --git a/resources/ai-kb/zh/concept/application/center.md b/resources/ai-kb/zh/concept/application/center.md index 52773539c..900bb75c7 100644 --- a/resources/ai-kb/zh/concept/application/center.md +++ b/resources/ai-kb/zh/concept/application/center.md @@ -17,7 +17,7 @@ prerequisites: [] negative: - 应用中心不是「应用市场」(AppStore),后者是管理员安装/卸载插件的独立入口 - 普通成员看不到管理员应用区(如 LDAP、邮件通知、数据导出等) -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 应用中心是什么 @@ -34,6 +34,9 @@ last_verified: v1.7.90 - **管理员应用**:仅系统管理员可见(LDAP、邮件通知、APP 推送、举报管理、数据导出、团队管理) - **微应用**:由已安装插件提供(OKR、思维导图、流程图、在线文档、Memos、KPI 等),列表随安装情况变化 +## 受「系统功能」应用控制的卡片 +工作报告、我的收藏、最近打开、签到打卡(以及竖屏下的日历)这几张系统应用卡片,只有在「系统功能」应用已安装且对应开关开启时才显示。该应用自身不会在应用中心生成卡片,同一功能只会出现一张卡片。详见 [[system-functions.overview.concept]]。 + ## 个性化 - 拖拽:长按卡片可调整顺序(仅影响个人视图) - 管理员可在「自定义应用菜单」里调整全员可见的应用项 diff --git a/resources/ai-kb/zh/concept/system-functions/overview.md b/resources/ai-kb/zh/concept/system-functions/overview.md new file mode 100644 index 000000000..1f911655e --- /dev/null +++ b/resources/ai-kb/zh/concept/system-functions/overview.md @@ -0,0 +1,49 @@ +--- +id: system-functions.overview.concept +title: 「系统功能」应用是什么 +type: concept +feature: system-functions +scope: end-user +locale: zh +aliases: + - 系统功能应用 + - 系统功能是什么 + - system-functions + - 内置功能开关 + - 怎么隐藏日历 + - 怎么关闭工作报告入口 +related_tools: [] +related_pages: [application] +prerequisites: [] +negative: + - 「系统功能」只控制入口是否显示,不会删除报告、收藏、签到等已有数据 + - 「系统功能」不启动任何容器,安装时没有 CPU、内存、镜像拉取等高级设置 + - 「系统功能」自身不会在应用中心生成卡片,也不会新增菜单 + - 不支持按成员或部门单独开关,开关对全员生效 +last_verified: v1.9.36 +--- + +# 「系统功能」应用是什么 + +## 定义 +「系统功能」(应用 ID:system-functions)是 DooTask 的内置应用,用来统一管理五个内置功能的入口:工作报告、日历、我的收藏、最近打开、签到打卡。它在应用商店(AppStore)中安装、配置和卸载。 + +## 关键属性 +- 每个功能对应一个开关字段,默认全部开启 +- 新部署和升级上来的实例会自动安装「系统功能」并启用全部功能;已有配置不会被覆盖 +- 开关对该功能的所有入口统一生效:应用中心卡片、头像下拉菜单、左侧导航 +- 卸载「系统功能」后,五个功能的入口全部隐藏;重新安装即可恢复 + +## 各功能受控的入口 +| 功能 | 受控入口 | +|---|---| +| 工作报告 | 应用中心「工作报告」卡片及未读角标、头像菜单「工作报告」 | +| 日历 | 左侧导航「日历」、竖屏应用中心「日历」卡片;停用后访问日历页会回到仪表盘 | +| 我的收藏 | 头像菜单「我的收藏」、应用中心「我的收藏」卡片 | +| 最近打开 | 头像菜单「最近打开的任务」、应用中心「最近打开」卡片 | +| 签到打卡 | 应用中心「签到打卡」卡片、「导出签到数据」入口 | + +## 与其他概念的关系 +- 修改开关的步骤见 [[system-functions.toggle.howto]] +- 入口找不到时的排查见 [[system-functions.entry-missing.faq]] +- 应用中心整体说明见 [[application.center.concept]] diff --git a/resources/ai-kb/zh/faq/system-functions/entry-missing.md b/resources/ai-kb/zh/faq/system-functions/entry-missing.md new file mode 100644 index 000000000..8230472f0 --- /dev/null +++ b/resources/ai-kb/zh/faq/system-functions/entry-missing.md @@ -0,0 +1,42 @@ +--- +id: system-functions.entry-missing.faq +title: 为什么找不到工作报告、日历、我的收藏、最近打开或签到打卡的入口 +type: faq +feature: system-functions +scope: end-user +locale: zh +aliases: + - 日历不见了 + - 工作报告入口没了 + - 找不到我的收藏 + - 最近打开不见了 + - 签到打卡卡片消失 + - 左侧栏没有日历 +related_tools: [] +related_pages: [application] +prerequisites: [] +negative: + - 入口隐藏不代表数据丢失,重新启用后原有数据仍在 + - 普通成员无法自行恢复入口,需要联系系统管理员 +last_verified: v1.9.36 +--- + +# 为什么找不到工作报告、日历、我的收藏、最近打开或签到打卡的入口 + +## 原因 +这五个功能的入口由「系统功能」应用统一控制,出现以下任一情况时入口会隐藏: +1. 管理员在「系统功能」应用里关闭了对应功能的开关 +2. 管理员卸载了「系统功能」应用(此时五个功能的入口全部隐藏) + +## 怎么确认 +- 某一个功能的入口在应用中心、头像菜单、左侧导航同时消失:多半是该功能的开关被关闭 +- 五个功能的入口同时消失:多半是「系统功能」应用被卸载 + +## 解决办法 +- 普通成员:联系系统管理员启用 +- 管理员:在应用商店打开「系统功能」应用,安装或重新安装并打开对应开关,步骤见 [[system-functions.toggle.howto]] + +## 其他可能 +- 「工作报告」在系统管理员的头像菜单里本来就不显示,管理员请从应用中心进入 +- 「日历」卡片只在竖屏(移动端)的应用中心显示,桌面端在左侧导航 +- 「签到打卡」还可能被管理员在「签到设置」里整体关闭 diff --git a/resources/ai-kb/zh/howto/checkin/export.md b/resources/ai-kb/zh/howto/checkin/export.md index ddf6607e9..92c37c123 100644 --- a/resources/ai-kb/zh/howto/checkin/export.md +++ b/resources/ai-kb/zh/howto/checkin/export.md @@ -14,8 +14,9 @@ aliases: related_tools: [] related_pages: [] prerequisites: + - 「系统功能」应用已安装且其中的「签到打卡」开关为开启(默认开启) - 需要系统管理员身份 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 怎么导出签到数据 @@ -50,3 +51,6 @@ last_verified: v1.7.90 - 不支持单次导出超过 100 个成员,更多需要分批 - 不支持按部门一键全选,需手动勾选 - 不支持原始多次打卡明细,只导首签和末签两个时间 + +## 入口不显示时 +「导出签到数据」入口受「系统功能」应用控制:管理员在应用商店停用了「签到打卡」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/howto/data-export/checkin.md b/resources/ai-kb/zh/howto/data-export/checkin.md index e574b4987..cee425549 100644 --- a/resources/ai-kb/zh/howto/data-export/checkin.md +++ b/resources/ai-kb/zh/howto/data-export/checkin.md @@ -14,6 +14,7 @@ aliases: related_tools: [] related_pages: [application] prerequisites: + - 「系统功能」应用已安装且其中的「签到打卡」开关为开启(默认开启) - 需要系统管理员权限 - 系统设置中签到功能已开启(`checkinSetting.open = open`) negative: @@ -21,7 +22,7 @@ negative: - 单次最多 100 个成员 - 日期范围最多 35 天 - 必填成员 / 日期 / 时间三个参数,缺一不可 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 导出签到数据 @@ -52,3 +53,6 @@ last_verified: v1.7.90 - 不支持导出无班次成员 - 不支持跨月超 35 天一次导 - 不支持自定义结果判定规则(用系统全局设置) + +## 入口不显示时 +「导出签到数据」入口受「系统功能」应用控制:管理员在应用商店停用了「签到打卡」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/howto/system-functions/toggle.md b/resources/ai-kb/zh/howto/system-functions/toggle.md new file mode 100644 index 000000000..a58c011a3 --- /dev/null +++ b/resources/ai-kb/zh/howto/system-functions/toggle.md @@ -0,0 +1,53 @@ +--- +id: system-functions.toggle.howto +title: 怎么启用或停用系统功能(工作报告、日历、收藏、最近打开、签到) +type: howto +feature: system-functions +scope: admin +locale: zh +aliases: + - 怎么隐藏日历 + - 关闭签到打卡入口 + - 停用工作报告 + - 恢复我的收藏入口 + - 系统功能怎么设置 + - 卸载系统功能 +related_tools: [] +related_pages: [application] +prerequisites: + - 需要系统管理员权限 + - 应用商店(AppStore)可用 +negative: + - 普通成员不能修改这些开关 + - 停用或卸载不会删除任何数据,只隐藏入口 + - 安装页没有「高级设置」,因为该应用不启动容器 +last_verified: v1.9.36 +--- + +# 怎么启用或停用系统功能(工作报告、日历、收藏、最近打开、签到) + +## 入口 +- 桌面端:左侧栏「应用」→ 管理员区「应用商店」→ 找到「系统功能」应用 + +## 操作步骤 +1. 在应用商店打开「系统功能」应用 +2. 进入该应用的安装页(已安装时按钮为「重新安装」,未安装时为「安装」) +3. 在「应用配置」里按需打开或关闭五个开关:工作报告、日历、我的收藏、最近打开、签到打卡 +4. 点击「重新安装」提交,配置立即保存,无需等待容器启动 +5. 回到主界面,入口随即按新配置显示或隐藏;其他成员刷新页面或重新进入「应用」后生效 + +## 字段默认值 +| 开关 | 默认 | +|---|---| +| 工作报告 | 开启 | +| 日历 | 开启 | +| 我的收藏 | 开启 | +| 最近打开 | 开启 | +| 签到打卡 | 开启 | + +## 一次隐藏全部入口 +在应用商店卸载「系统功能」应用,五个功能的入口全部隐藏。重新安装后恢复。 + +## 相关 +- 应用说明见 [[system-functions.overview.concept]] +- 入口找不到的排查见 [[system-functions.entry-missing.faq]] diff --git a/resources/ai-kb/zh/menu-map/calendar/entry.md b/resources/ai-kb/zh/menu-map/calendar/entry.md index f76b3222e..fd2bf4853 100644 --- a/resources/ai-kb/zh/menu-map/calendar/entry.md +++ b/resources/ai-kb/zh/menu-map/calendar/entry.md @@ -13,12 +13,13 @@ aliases: - calendar 在哪 related_tools: [list_tasks] related_pages: [calendar] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「日历」开关为开启(默认开启) negative: - 日历只显示有 end_at 的任务,不显示会议、签到等其他事件 - 移动端日历只读,不能拖动改时间 - 日历不支持其他人的视图 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 日历入口在哪 @@ -50,3 +51,6 @@ last_verified: v1.7.90 - 不显示会议(B2 模块)、签到、报告 - 不支持订阅他人日历 - 详见 [[calendar.meeting-not-shown.faq]] + +## 入口不显示时 +「日历」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「日历」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/checkin/entry.md b/resources/ai-kb/zh/menu-map/checkin/entry.md index 0d4c27963..b586350d9 100644 --- a/resources/ai-kb/zh/menu-map/checkin/entry.md +++ b/resources/ai-kb/zh/menu-map/checkin/entry.md @@ -13,11 +13,12 @@ aliases: - 签到打卡入口 related_tools: [] related_pages: [application] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「签到打卡」开关为开启(默认开启) negative: - 左侧栏没有「签到」一级入口,必须从「应用」展开 - 应用中心看不到「签到打卡」卡片时,多半是被管理员在「签到设置」里整体关闭了 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 签到打卡入口在哪 @@ -43,3 +44,6 @@ last_verified: v1.7.90 - 任何登录成员都能看到「签到打卡」卡片 - 全局「签到设置」按钮仅管理员可见 - 个别能力(如人脸上传 / 修改 MAC)受「允许修改」开关控制,默认允许 + +## 入口不显示时 +「签到打卡」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「签到打卡」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/favorite/entry.md b/resources/ai-kb/zh/menu-map/favorite/entry.md index f5b2eece4..b28a2b6c1 100644 --- a/resources/ai-kb/zh/menu-map/favorite/entry.md +++ b/resources/ai-kb/zh/menu-map/favorite/entry.md @@ -13,12 +13,13 @@ aliases: - 最近浏览入口 related_tools: [] related_pages: [application] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「我的收藏」开关为开启(默认开启) negative: - 没有顶部导航的独立入口,必须通过「应用」中心进入 - 不在左侧栏一级菜单上,不会默认置顶 - 桌面端和移动端入口结构一致,没有平台差异化路径 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 我的收藏 / 最近打开入口 @@ -55,3 +56,6 @@ last_verified: v1.7.90 - 添加收藏:[[favorite.add.howto]] - 取消收藏:[[favorite.remove.howto]] - 收藏与最近的概念区别:[[favorite.concept]] + +## 入口不显示时 +「我的收藏」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「我的收藏」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/menu-navigation/checkin.md b/resources/ai-kb/zh/menu-map/menu-navigation/checkin.md index 1745e2ac1..b93337e0f 100644 --- a/resources/ai-kb/zh/menu-map/menu-navigation/checkin.md +++ b/resources/ai-kb/zh/menu-map/menu-navigation/checkin.md @@ -13,12 +13,13 @@ aliases: - 打卡机器人在哪 related_tools: [] related_pages: [application, messenger] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「签到打卡」开关为开启(默认开启) negative: - 没有左侧一级菜单,必须从「应用」展开 - 不支持快捷键 - 「签到打卡」卡片被管理员关闭时不会显示 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 签到打卡入口在哪 @@ -39,3 +40,6 @@ last_verified: v1.7.90 - 签到入口总览:[[checkin.entry.menu-map]] - 怎么打卡:[[checkin.regular.howto]] - 管理员配置规则:[[checkin.rule.concept]] + +## 入口不显示时 +「签到打卡」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「签到打卡」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/menu-navigation/favorite.md b/resources/ai-kb/zh/menu-map/menu-navigation/favorite.md index 39c709be5..1e52a00d5 100644 --- a/resources/ai-kb/zh/menu-map/menu-navigation/favorite.md +++ b/resources/ai-kb/zh/menu-map/menu-navigation/favorite.md @@ -12,11 +12,12 @@ aliases: - 我收藏过的怎么看 related_tools: [] related_pages: [application] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「我的收藏」开关为开启(默认开启) negative: - 没有左侧栏一级入口,藏在「应用」中心和头像下拉里 - 没有快捷键 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 我的收藏入口在哪 @@ -39,3 +40,6 @@ last_verified: v1.7.90 - 收藏入口与最近打开:[[favorite.entry.menu-map]] - 添加收藏:[[favorite.add.howto]] - 与最近打开的区别:[[favorite.concept]] + +## 入口不显示时 +「我的收藏」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「我的收藏」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/menu-navigation/recent.md b/resources/ai-kb/zh/menu-map/menu-navigation/recent.md index b096b8650..6cfbd3609 100644 --- a/resources/ai-kb/zh/menu-map/menu-navigation/recent.md +++ b/resources/ai-kb/zh/menu-map/menu-navigation/recent.md @@ -12,11 +12,12 @@ aliases: - 历史浏览记录 related_tools: [] related_pages: [application] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「最近打开」开关为开启(默认开启) negative: - 「最近打开」只记录任务 / 文件 / 任务附件 / 消息附件,不包含项目和会话 - 不支持手动清空全部记录 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 最近打开入口在哪 @@ -39,3 +40,6 @@ last_verified: v1.7.90 ## 相关 - 收藏与最近的总入口:[[favorite.entry.menu-map]] + +## 入口不显示时 +「最近打开」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「最近打开」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/ai-kb/zh/menu-map/report/entry.md b/resources/ai-kb/zh/menu-map/report/entry.md index 0708d4e76..223ea54c8 100644 --- a/resources/ai-kb/zh/menu-map/report/entry.md +++ b/resources/ai-kb/zh/menu-map/report/entry.md @@ -14,11 +14,12 @@ aliases: - 打开汇报 related_tools: [] related_pages: [] -prerequisites: [] +prerequisites: + - 「系统功能」应用已安装且其中的「工作报告」开关为开启(默认开启) negative: - 「工作报告」不在左侧主导航栏,是右上角头像菜单 + 应用中心系统应用 - 普通成员菜单显示「工作报告」;系统管理员菜单中不直接显示该项,可改走应用中心 -last_verified: v1.7.90 +last_verified: v1.9.36 --- # 工作报告入口在哪 @@ -41,3 +42,6 @@ last_verified: v1.7.90 ## 权限要求 - end-user 可见,所有登录用户都能写和收报告 - 不需要插件,是主程序内置功能 + +## 入口不显示时 +「工作报告」的所有入口受「系统功能」应用控制:管理员在应用商店停用了「工作报告」开关,或卸载了「系统功能」应用时,入口会统一隐藏(数据不会删除)。恢复方法见 [[system-functions.toggle.howto]]。 diff --git a/resources/assets/js/components/AIAssistant/action-executor.js b/resources/assets/js/components/AIAssistant/action-executor.js index 7090c720e..bac6ad45d 100644 --- a/resources/assets/js/components/AIAssistant/action-executor.js +++ b/resources/assets/js/components/AIAssistant/action-executor.js @@ -206,6 +206,9 @@ class ActionExecutor { * 导航到日历页面 */ async navigateToCalendar() { + if (!this.store?.getters?.systemFunctionEnabled?.('calendar')) { + throw new Error('日历功能未启用'); + } window.$A.goForward({ name: 'manage-calendar' }); return { navigated: true, page: 'calendar' }; } diff --git a/resources/assets/js/pages/manage.vue b/resources/assets/js/pages/manage.vue index d9334829f..c0138161c 100644 --- a/resources/assets/js/pages/manage.vue +++ b/resources/assets/js/pages/manage.vue @@ -82,7 +82,7 @@ {{$L('团队管理')}} {{$L('导出任务统计')}} {{$L('导出超期任务')}} - {{$L('导出签到数据')}} + {{$L('导出签到数据')}} @@ -141,7 +141,7 @@ -
  • +
  • @@ -655,7 +655,7 @@ export default { 'mcpServerStatus' ]), - ...mapGetters(['aiVisible', 'dashboardTask', "filterMicroAppsMenusMain", 'visibleDialogs']), + ...mapGetters(['aiVisible', 'dashboardTask', "filterMicroAppsMenusMain", 'visibleDialogs', 'systemFunctionEnabled']), // 父『应用』入口聚合角标(规则收敛在 appBadges 模块 getter) ...mapGetters('appBadges', { @@ -663,6 +663,14 @@ export default { applicationBadgeDot: 'applicationDot', }), + /** + * 当前处于日历页且日历已明确停用(开关尚未加载时不判定为停用) + * @returns {boolean} + */ + calendarDisabled() { + return this.routeName === 'manage-calendar' && this.$store.state.systemFunctions.calendar === false; + }, + departmentOwnerViewAvailable() { return this.systemConfig.department_owner_project_view === 'open' && (this.userInfo.managed_departments || []).length > 0; }, @@ -783,51 +791,64 @@ export default { * @returns {Array} */ menu() { - const {userIsAdmin} = this; - const array = [ - {path: 'taskBrowse', name: '最近打开的任务'}, - {path: 'favorite', name: '我的收藏'}, - {path: 'download', name: '下载内容', visible: !!this.$Electron}, - {path: 'mcpServer', name: '启用桌面 MCP 服务器', visible: !!this.$Electron, selected: this.mcpServerStatus.running === 'running'}, + const {userIsAdmin, systemFunctionEnabled} = this; + // 按分组组织:每组(第一组除外)的首个可见项显示分割线。 + // 先过滤不可见的菜单项再计算分割线,隐藏任意项都不会留下多余的分割线。 + const groups = [ + [ + {path: 'taskBrowse', name: '最近打开的任务', visible: systemFunctionEnabled('recent')}, + {path: 'favorite', name: '我的收藏', visible: systemFunctionEnabled('favorite')}, + {path: 'download', name: '下载内容', visible: !!this.$Electron}, + {path: 'mcpServer', name: '启用桌面 MCP 服务器', visible: !!this.$Electron, selected: this.mcpServerStatus.running === 'running'}, + ], ]; + const departmentOwnerView = this.departmentOwnerViewAvailable ? [{ + path: 'departmentOwnerView', + name: '负责人视角', + selected: this.departmentOwnerProjectViewEnabled, + selectedCount: this.departmentOwnerProjectViewEnabled ? this.cacheDepartmentOwnerIds.length : 0, + }] : []; if (userIsAdmin) { - array.push(...[ - {path: 'personal', name: '个人设置', divided: true}, - {path: 'system', name: '系统设置'}, - {path: 'license', name: 'License Key'}, - - {path: 'downloadClient', name: '客户端下载', divided: true, visible: this.showDownloadClient}, - {path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion}, - - {path: 'allProject', name: '所有项目', divided: true}, - {path: 'archivedProject', name: '已归档的项目'}, - - {path: 'team', name: '团队管理', divided: true}, - ]) + groups.push( + [ + {path: 'personal', name: '个人设置'}, + {path: 'system', name: '系统设置'}, + {path: 'license', name: 'License Key'}, + ], + [{path: 'downloadClient', name: '客户端下载', visible: this.showDownloadClient}], + [{path: 'version', name: '更新版本', visible: !!this.clientNewVersion}], + [ + {path: 'allProject', name: '所有项目'}, + {path: 'archivedProject', name: '已归档的项目'}, + ], + [ + {path: 'team', name: '团队管理'}, + ...departmentOwnerView, + ], + ) } else { - array.push(...[ - {path: 'personal', name: '个人设置', divided: true}, - {path: 'downloadClient', name: '客户端下载', divided: true, visible: this.showDownloadClient}, - {path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion}, - - {path: 'workReport', name: '工作报告', divided: true}, - {path: 'archivedProject', name: '已归档的项目'}, - ]) + groups.push( + [{path: 'personal', name: '个人设置'}], + [{path: 'downloadClient', name: '客户端下载', visible: this.showDownloadClient}], + [{path: 'version', name: '更新版本', visible: !!this.clientNewVersion}], + [ + {path: 'workReport', name: '工作报告', visible: systemFunctionEnabled('report')}, + {path: 'archivedProject', name: '已归档的项目'}, + ], + departmentOwnerView, + ) } - if (this.departmentOwnerViewAvailable) { - array.push({ - path: 'departmentOwnerView', - name: '负责人视角', - divided: !userIsAdmin, - visible: true, - selected: this.departmentOwnerProjectViewEnabled, - selectedCount: this.departmentOwnerProjectViewEnabled ? this.cacheDepartmentOwnerIds.length : 0, - }); - } - array.push(...[ - {path: 'clearCache', name: '清除缓存', divided: true}, + groups.push([ + {path: 'clearCache', name: '清除缓存'}, {path: 'logout', name: '退出登录', style: {color: '#f40'}} ]) + const array = []; + groups.forEach(group => { + const items = group.filter(item => item.visible !== false); + items.forEach((item, index) => { + array.push(Object.assign({}, item, {divided: index === 0 && array.length > 0})); + }); + }); return array }, @@ -913,6 +934,16 @@ export default { this.chackPass(); }, + // 日历被停用(或「系统功能」已卸载)时,不允许停留在日历页 + calendarDisabled: { + handler(disabled) { + if (disabled) { + this.goForward({name: 'manage-dashboard'}, true); + } + }, + immediate: true + }, + userInfo() { this.chackPass(); }, diff --git a/resources/assets/js/pages/manage/application.vue b/resources/assets/js/pages/manage/application.vue index 0097ba77f..a3cd9e063 100644 --- a/resources/assets/js/pages/manage/application.vue +++ b/resources/assets/js/pages/manage/application.vue @@ -541,15 +541,16 @@ export default { ...mapGetters([ 'filterMicroAppsMenus', 'filterMicroAppsMenusAdmin', + 'systemFunctionEnabled', ]), applyList() { const list = [ // 常用应用 - {value: "favorite", label: "我的收藏", sort: 45}, - {value: "recent", label: "最近打开", sort: 47}, - {value: "report", label: "工作报告", sort: 50}, + {value: "favorite", label: "我的收藏", sort: 45, show: this.systemFunctionEnabled('favorite')}, + {value: "recent", label: "最近打开", sort: 47, show: this.systemFunctionEnabled('recent')}, + {value: "report", label: "工作报告", sort: 50, show: this.systemFunctionEnabled('report')}, {value: "mybot", label: "我的机器人", sort: 55}, - {value: "signin", label: "签到打卡", sort: 70}, + {value: "signin", label: "签到打卡", sort: 70, show: this.systemFunctionEnabled('checkin')}, {value: "meeting", label: "在线会议", sort: 80}, {value: "createGroup", label: "创建群组", sort: 85}, {value: "word-chain", label: "群接龙", sort: 90}, @@ -569,7 +570,7 @@ export default { // 竖屏模式 if (this.windowPortrait) { list.push(...[ - {value: "calendar", label: "日历", sort: 10}, + {value: "calendar", label: "日历", sort: 10, show: this.systemFunctionEnabled('calendar')}, {value: "file", label: "文件", sort: 20}, {value: "setting", label: "设置", sort: 140}, ]) @@ -992,8 +993,10 @@ export default { const list = [ {label: this.$L('导出任务统计'), value: 'task'}, {label: this.$L('导出超期任务'), value: 'overdue'}, - {label: this.$L('导出签到数据'), value: 'checkin'}, ]; + if (this.systemFunctionEnabled('checkin')) { + list.push({label: this.$L('导出签到数据'), value: 'checkin'}); + } this.$store.commit('menu/operation', { event: menuEvent, list, diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 2edb33df5..e00a46bb4 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -1206,7 +1206,8 @@ export default { 'taskRelatedCache', 'dialogCommonCountCache', 'collaborationFileCache', - 'mcpServerStatus' + 'mcpServerStatus', + 'systemFunctions' ] }; @@ -5500,6 +5501,11 @@ export default { * @param dispatch */ async updateMicroAppsStatus({commit, state, dispatch}) { + // 系统功能开关:不依赖应用商店接口,商店不可用时也能更新 + dispatch('call', {url: 'apps/functions'}).then(({data}) => { + commit("systemFunctions/save", data?.functions) + }).catch(() => {}) + // const {data: {code, data}} = await axios.get($A.mainUrl('appstore/api/v1/internal/installed'), { headers: { Token: state.userToken, diff --git a/resources/assets/js/store/getters.js b/resources/assets/js/store/getters.js index 61dba40e6..d22c2f124 100644 --- a/resources/assets/js/store/getters.js +++ b/resources/assets/js/store/getters.js @@ -308,6 +308,17 @@ export default { return state.cacheDialogs.filter(dialog => !$A.isAiBotDialog(dialog)) }, + /** + * 系统功能是否启用(「系统功能」应用未安装或该功能被停用时为 false) + * 工作报告、日历、我的收藏、最近打开、签到打卡的所有入口统一以此判断 + * + * @param {Object} state + * @returns {function(string): boolean} 参数为功能标识:report/calendar/favorite/recent/checkin + */ + systemFunctionEnabled: (state) => (name) => { + return state.systemFunctions?.[name] === true + }, + /** * 获取应用菜单 * 过滤出location为application的菜单项 diff --git a/resources/assets/js/store/modules/appBadges.js b/resources/assets/js/store/modules/appBadges.js index 9fd920683..7eb5772da 100644 --- a/resources/assets/js/store/modules/appBadges.js +++ b/resources/assets/js/store/modules/appBadges.js @@ -122,10 +122,11 @@ export default { }, /** - * 父『应用』入口聚合数字:工作报告未读 + 所有插件角标数字之和 + * 父『应用』入口聚合数字:工作报告未读(功能启用时)+ 所有插件角标数字之和 */ - applicationCount: (state, getters, rootState) => { - return (Number(rootState.reportUnreadNumber) || 0) + getters.totalCount + applicationCount: (state, getters, rootState, rootGetters) => { + const reportCount = rootGetters.systemFunctionEnabled('report') ? (Number(rootState.reportUnreadNumber) || 0) : 0 + return reportCount + getters.totalCount }, /** diff --git a/resources/assets/js/store/mutations.js b/resources/assets/js/store/mutations.js index 8a4a881b6..45b9308f5 100644 --- a/resources/assets/js/store/mutations.js +++ b/resources/assets/js/store/mutations.js @@ -463,6 +463,10 @@ export default { // 更新菜单 const menus = []; data.forEach((item) => { + // 「系统功能」只控制内置入口的显示,自身不产生应用菜单,避免与内置入口重复 + if (item.id === 'system-functions') { + return; + } if (Array.isArray(item.menu_items) && item.menu_items.length > 0) { menus.push(...item.menu_items.map(menu => Object.assign({}, menu, {id: item.id}))); } @@ -483,6 +487,11 @@ export default { $A.IDBSave("microAppsMenus", state.microAppsMenus = menus); }, + // 系统功能开关 + 'systemFunctions/save': function(state, data) { + $A.IDBSave("systemFunctions", state.systemFunctions = $A.isJson(data) ? data : {}); + }, + // MCP 服务器状态 'mcp/server/status': function(state, data) { $A.IDBSave("mcpServerStatus", state.mcpServerStatus = data); diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index fe514eefe..725177a2c 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -301,6 +301,9 @@ export default { microAppsIds: [], microAppsMenus: [], + // 系统功能开关(「系统功能」应用,功能标识 => 是否启用;未安装时全部为 false) + systemFunctions: {}, + // MCP 服务器状态 mcpServerStatus: {running: 'stopped'}, }; diff --git a/routes/api-map.md b/routes/api-map.md index af308a7bc..14a5b2a94 100644 --- a/routes/api-map.md +++ b/routes/api-map.md @@ -2,7 +2,7 @@ > 此文件由 `php artisan doc:api-map` 生成,勿手改。 -接口总数:329 +接口总数:330 ## 路由规则 @@ -420,6 +420,7 @@ API 使用动态路由(见 `routes/web.php`),URL 段映射为控制器方 | api/apps/badge/set | badge__set() | post | 设置角标(应用密钥鉴权) | | api/apps/badge/clear | badge__clear() | post | 清除角标(当前用户 token 鉴权) | | api/apps/badge/list | badge__list() | get | 拉取自己全部角标 | +| api/apps/functions | functions() | get | 获取系统功能开关 | ## test(TestController)