commit ed9536f6ab6a5655904ecdf33f877ea3680c5e37
Author: JenniferW <1627055433@qq.com>
Date: Thu Jan 15 15:50:49 2026 +0800
首次提交
diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 0000000..dc3bc09
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1,4 @@
+> 1%
+last 2 versions
+not dead
+not ie 11
diff --git a/.changeset/README.md b/.changeset/README.md
new file mode 100644
index 0000000..5654e89
--- /dev/null
+++ b/.changeset/README.md
@@ -0,0 +1,5 @@
+# Changesets
+
+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
+
+We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
diff --git a/.changeset/config.json b/.changeset/config.json
new file mode 100644
index 0000000..737e5d8
--- /dev/null
+++ b/.changeset/config.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
+ "changelog": [
+ "@changesets/changelog-github",
+ { "repo": "aiflowy/aiflowy" }
+ ],
+ "commit": false,
+ "fixed": [["@aiflowy-core/*", "@aiflowy/*"]],
+ "snapshot": {
+ "prereleaseTemplate": "{tag}-{datetime}"
+ },
+ "privatePackages": { "version": true, "tag": true },
+ "linked": [],
+ "access": "public",
+ "baseBranch": "main",
+ "updateInternalDependencies": "patch",
+ "ignore": []
+}
diff --git a/.commitlintrc.js b/.commitlintrc.js
new file mode 100644
index 0000000..e45361c
--- /dev/null
+++ b/.commitlintrc.js
@@ -0,0 +1 @@
+export { default } from '@aiflowy/commitlint-config';
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..52b833a
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+node_modules
+.git
+.gitignore
+*.md
+dist
+.turbo
+dist.zip
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..179aec6
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+indent_style=space
+indent_size=2
+max_line_length = 100
+trim_trailing_whitespace = true
+quote_type = single
+
+[*.{yml,yaml,json}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d4e5bd3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
+
+# Automatically normalize line endings (to LF) for all text-based files.
+* text=auto eol=lf
+
+# Declare files that will always have CRLF line endings on checkout.
+*.{cmd,[cC][mM][dD]} text eol=crlf
+*.{bat,[bB][aA][tT]} text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
\ No newline at end of file
diff --git a/.gitconfig b/.gitconfig
new file mode 100644
index 0000000..4b28a69
--- /dev/null
+++ b/.gitconfig
@@ -0,0 +1,2 @@
+[core]
+ ignorecase = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3399f39
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,52 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+dist.zip
+dist.tar
+dist.war
+.nitro
+.output
+*-dist.zip
+*-dist.tar
+*-dist.war
+coverage
+*.local
+**/.vitepress/cache
+.cache
+.turbo
+.temp
+dev-dist
+.stylelintcache
+yarn.lock
+package-lock.json
+.VSCodeCounter
+**/backend-mock/data
+
+# local env files
+.env.local
+.env.*.local
+.eslintcache
+
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+vite.config.mts.*
+vite.config.mjs.*
+vite.config.js.*
+vite.config.ts.*
+
+# Editor directories and files
+.idea
+# .vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.history
+.cursor
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000..5fda2cf
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,6 @@
+ports:
+ - port: 5555
+ onOpen: open-preview
+tasks:
+ - init: npm i -g corepack && pnpm install
+ command: pnpm run dev:play
diff --git a/.node-version b/.node-version
new file mode 100644
index 0000000..ee5c244
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+22.1.0
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..aeac1ae
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,13 @@
+registry=https://registry.npmmirror.com
+public-hoist-pattern[]=lefthook
+public-hoist-pattern[]=eslint
+public-hoist-pattern[]=prettier
+public-hoist-pattern[]=prettier-plugin-tailwindcss
+public-hoist-pattern[]=stylelint
+public-hoist-pattern[]=*postcss*
+public-hoist-pattern[]=@commitlint/*
+public-hoist-pattern[]=czg
+
+strict-peer-dependencies=false
+auto-install-peers=true
+dedupe-peer-dependents=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..d0b0ca1
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,18 @@
+dist
+dev-dist
+.local
+.output.js
+node_modules
+.nvmrc
+coverage
+CODEOWNERS
+.nitro
+.output
+
+
+**/*.svg
+**/*.sh
+
+public
+.npmrc
+*-lock.yaml
diff --git a/.prettierrc.mjs b/.prettierrc.mjs
new file mode 100644
index 0000000..c7740a9
--- /dev/null
+++ b/.prettierrc.mjs
@@ -0,0 +1 @@
+export { default } from '@aiflowy/prettier-config';
diff --git a/.stylelintignore b/.stylelintignore
new file mode 100644
index 0000000..f4b2db2
--- /dev/null
+++ b/.stylelintignore
@@ -0,0 +1,4 @@
+dist
+public
+__tests__
+coverage
diff --git a/aiflowy.code-workspace b/aiflowy.code-workspace
new file mode 100644
index 0000000..53fc07b
--- /dev/null
+++ b/aiflowy.code-workspace
@@ -0,0 +1,152 @@
+{
+ "folders": [
+ {
+ "name": "@aiflowy/app",
+ "path": "app",
+ },
+ {
+ "name": "@aiflowy/commitlint-config",
+ "path": "internal/lint-configs/commitlint-config",
+ },
+ {
+ "name": "@aiflowy/eslint-config",
+ "path": "internal/lint-configs/eslint-config",
+ },
+ {
+ "name": "@aiflowy/prettier-config",
+ "path": "internal/lint-configs/prettier-config",
+ },
+ {
+ "name": "@aiflowy/stylelint-config",
+ "path": "internal/lint-configs/stylelint-config",
+ },
+ {
+ "name": "@aiflowy/node-utils",
+ "path": "internal/node-utils",
+ },
+ {
+ "name": "@aiflowy/tailwind-config",
+ "path": "internal/tailwind-config",
+ },
+ {
+ "name": "@aiflowy/tsconfig",
+ "path": "internal/tsconfig",
+ },
+ {
+ "name": "@aiflowy/vite-config",
+ "path": "internal/vite-config",
+ },
+ {
+ "name": "@aiflowy-core/design",
+ "path": "packages/@core/base/design",
+ },
+ {
+ "name": "@aiflowy-core/icons",
+ "path": "packages/@core/base/icons",
+ },
+ {
+ "name": "@aiflowy-core/shared",
+ "path": "packages/@core/base/shared",
+ },
+ {
+ "name": "@aiflowy-core/typings",
+ "path": "packages/@core/base/typings",
+ },
+ {
+ "name": "@aiflowy-core/composables",
+ "path": "packages/@core/composables",
+ },
+ {
+ "name": "@aiflowy-core/preferences",
+ "path": "packages/@core/preferences",
+ },
+ {
+ "name": "@aiflowy-core/form-ui",
+ "path": "packages/@core/ui-kit/form-ui",
+ },
+ {
+ "name": "@aiflowy-core/layout-ui",
+ "path": "packages/@core/ui-kit/layout-ui",
+ },
+ {
+ "name": "@aiflowy-core/menu-ui",
+ "path": "packages/@core/ui-kit/menu-ui",
+ },
+ {
+ "name": "@aiflowy-core/popup-ui",
+ "path": "packages/@core/ui-kit/popup-ui",
+ },
+ {
+ "name": "@aiflowy-core/shadcn-ui",
+ "path": "packages/@core/ui-kit/shadcn-ui",
+ },
+ {
+ "name": "@aiflowy-core/tabs-ui",
+ "path": "packages/@core/ui-kit/tabs-ui",
+ },
+ {
+ "name": "@aiflowy/constants",
+ "path": "packages/constants",
+ },
+ {
+ "name": "@aiflowy/access",
+ "path": "packages/effects/access",
+ },
+ {
+ "name": "@aiflowy/common-ui",
+ "path": "packages/effects/common-ui",
+ },
+ {
+ "name": "@aiflowy/hooks",
+ "path": "packages/effects/hooks",
+ },
+ {
+ "name": "@aiflowy/layouts",
+ "path": "packages/effects/layouts",
+ },
+ {
+ "name": "@aiflowy/plugins",
+ "path": "packages/effects/plugins",
+ },
+ {
+ "name": "@aiflowy/request",
+ "path": "packages/effects/request",
+ },
+ {
+ "name": "@aiflowy/icons",
+ "path": "packages/icons",
+ },
+ {
+ "name": "@aiflowy/locales",
+ "path": "packages/locales",
+ },
+ {
+ "name": "@aiflowy/preferences",
+ "path": "packages/preferences",
+ },
+ {
+ "name": "@aiflowy/stores",
+ "path": "packages/stores",
+ },
+ {
+ "name": "@aiflowy/styles",
+ "path": "packages/styles",
+ },
+ {
+ "name": "@aiflowy/types",
+ "path": "packages/types",
+ },
+ {
+ "name": "@aiflowy/utils",
+ "path": "packages/utils",
+ },
+ {
+ "name": "@aiflowy/turbo-run",
+ "path": "scripts/turbo-run",
+ },
+ {
+ "name": "@aiflowy/vsh",
+ "path": "scripts/vsh",
+ },
+ ],
+}
diff --git a/app/.env b/app/.env
new file mode 100644
index 0000000..cf6fcbd
--- /dev/null
+++ b/app/.env
@@ -0,0 +1,8 @@
+# 应用标题
+VITE_APP_TITLE=AIFlowy
+
+# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
+VITE_APP_NAMESPACE=aiflowy-web
+
+# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
+VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key
diff --git a/app/.env.analyze b/app/.env.analyze
new file mode 100644
index 0000000..ffafa8d
--- /dev/null
+++ b/app/.env.analyze
@@ -0,0 +1,7 @@
+# public path
+VITE_BASE=/
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/api
+
+VITE_VISUALIZER=true
diff --git a/app/.env.development b/app/.env.development
new file mode 100644
index 0000000..92e0426
--- /dev/null
+++ b/app/.env.development
@@ -0,0 +1,13 @@
+# 端口号
+VITE_PORT=5090
+
+VITE_BASE=/
+
+# 接口地址
+VITE_GLOB_API_URL=http://127.0.0.1:8080
+
+# 是否打开 devtools,true 为打开,false 为关闭
+VITE_DEVTOOLS=false
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
diff --git a/app/.env.production b/app/.env.production
new file mode 100644
index 0000000..2fc1855
--- /dev/null
+++ b/app/.env.production
@@ -0,0 +1,26 @@
+VITE_BASE=/
+
+# 接口地址
+VITE_GLOB_API_URL=/
+
+# 是否开启压缩,可以设置为 none, brotli, gzip
+VITE_COMPRESS=none
+
+# 是否开启 PWA
+VITE_PWA=false
+
+# vue-router 的模式
+VITE_ROUTER_HISTORY=hash
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
+
+# 打包后是否生成dist.zip
+VITE_ARCHIVER=true
+
+
+
+
+
+
+
diff --git a/app/index.html b/app/index.html
new file mode 100644
index 0000000..239dace
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+ <%= VITE_APP_TITLE %>
+
+
+
+
+
+
+
+
+
+
diff --git a/app/package.json b/app/package.json
new file mode 100644
index 0000000..eba0a8d
--- /dev/null
+++ b/app/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "@aiflowy/app",
+ "version": "1.0.0",
+ "homepage": "https://aiflowy.tech",
+ "bugs": "https://github.com/aiflowy/aiflowy/issues",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/aiflowy/aiflowy.git",
+ "directory": "app"
+ },
+ "type": "module",
+ "scripts": {
+ "build": "pnpm vite build --mode production",
+ "build:analyze": "pnpm vite build --mode analyze",
+ "dev": "pnpm vite --mode development",
+ "preview": "vite preview",
+ "typecheck": "vue-tsc --noEmit --skipLibCheck"
+ },
+ "imports": {
+ "#/*": "./src/*"
+ },
+ "dependencies": {
+ "@aiflowy-core/shadcn-ui": "workspace:*",
+ "@aiflowy/access": "workspace:*",
+ "@aiflowy/common-ui": "workspace:*",
+ "@aiflowy/constants": "workspace:*",
+ "@aiflowy/hooks": "workspace:*",
+ "@aiflowy/icons": "workspace:*",
+ "@aiflowy/layouts": "workspace:*",
+ "@aiflowy/locales": "workspace:*",
+ "@aiflowy/plugins": "workspace:*",
+ "@aiflowy/preferences": "workspace:*",
+ "@aiflowy/request": "workspace:*",
+ "@aiflowy/stores": "workspace:*",
+ "@aiflowy/styles": "workspace:*",
+ "@aiflowy/types": "workspace:*",
+ "@aiflowy/utils": "workspace:*",
+ "@element-plus/icons-vue": "^2.3.2",
+ "@tinyflow-ai/vue": "^1.1.10",
+ "@vueuse/core": "catalog:",
+ "dayjs": "catalog:",
+ "dompurify": "^3.3.1",
+ "element-plus": "catalog:",
+ "fetch-event-stream": "^0.1.6",
+ "highlight.js": "^11.11.1",
+ "markdown-it": "^14.1.0",
+ "pinia": "catalog:",
+ "radash": "^12.1.1",
+ "vue": "catalog:",
+ "vue-cropper": "^1.1.4",
+ "vue-element-plus-x": "^1.3.7",
+ "vue-router": "catalog:",
+ "vue3-json-viewer": "^2.4.1"
+ },
+ "devDependencies": {
+ "cssnano": "catalog:",
+ "unplugin-element-plus": "catalog:"
+ }
+}
diff --git a/app/postcss.config.mjs b/app/postcss.config.mjs
new file mode 100644
index 0000000..b5ceb95
--- /dev/null
+++ b/app/postcss.config.mjs
@@ -0,0 +1 @@
+export { default } from '@aiflowy/tailwind-config/postcss';
diff --git a/app/public/empty-dark.png b/app/public/empty-dark.png
new file mode 100644
index 0000000..d1537a4
Binary files /dev/null and b/app/public/empty-dark.png differ
diff --git a/app/public/empty.png b/app/public/empty.png
new file mode 100644
index 0000000..41429c1
Binary files /dev/null and b/app/public/empty.png differ
diff --git a/app/public/favicon.png b/app/public/favicon.png
new file mode 100644
index 0000000..c03e093
Binary files /dev/null and b/app/public/favicon.png differ
diff --git a/app/public/favicon.svg b/app/public/favicon.svg
new file mode 100644
index 0000000..e4e6ccd
--- /dev/null
+++ b/app/public/favicon.svg
@@ -0,0 +1,11 @@
+
+
diff --git a/app/public/load.min.js b/app/public/load.min.js
new file mode 100644
index 0000000..f482e16
--- /dev/null
+++ b/app/public/load.min.js
@@ -0,0 +1 @@
+const Math=window.Math,head=document.getElementsByTagName("head")[0],TIMEOUT=1e4,TAC_LOADING_DIV='请稍等...
';function showLoading(e){var t=document.querySelector(e);t&&(t.innerHTML=TAC_LOADING_DIV)}function hideLoading(e){let t=document.querySelector(e);t&&(t.innerHTML="")}function loadCaptchaScript(e,t,n,r,o){const i=e.scriptUrls,c=e.cssUrls,l=e.timeout||TIMEOUT;let s=i.length+c.length;function d(e,i){if(s--,e&&0===s){if(hideLoading(t.bindEl),!window.TAC)throw new Error("TAC未加载,请检查地址是否正确");r(new TAC(t,n))}else e||(hideLoading(t.bindEl),o(i))}setTimeout(()=>{0!==s&&showLoading(t.bindEl)},10),i.forEach(function(e){loadResource("string"==typeof e?{url:e}:e,d,"script",l)}),c.forEach(function(e){loadResource("string"==typeof e?{url:e}:e,d,"link",l)})}function loadResource(e,t,n="script",r){if(document.querySelector(`${n}[${"script"===n?"src":"href"}="${e.url}"]`))return void t(!0,e);let o=!1;const i=document.createElement(n);"link"===n?i.rel="stylesheet":i.async=!0,i["script"===n?"src":"href"]=e.url;let c;i.onload=i.onreadystatechange=(()=>{o||i.readyState&&"loaded"!==i.readyState&&"complete"!==i.readyState||function t(n){e.checkOnReady?c=setTimeout(()=>{e.checkOnReady()?n():t(n)},10):n()}(()=>{o=!0,setTimeout(()=>t(o,e),0)})}),i.onerror=(()=>{t(o=!1,e)}),head.appendChild(i),setTimeout(()=>{o||(c&&clearTimeout(c),i.onload=i.onerror=null,i.remove&&i.remove(),t(o,e))},r||TIMEOUT)}function loadTAC(e,t,n){return new Promise((r,o)=>{let i={..."string"==typeof e?{url:e}:e};i.url&&(i.url.endsWith("/")||(i.url+="/"),i.scriptUrls||(i.scriptUrls=[i.url+"js/tac.min.js"]),i.cssUrls||(i.cssUrls=[i.url+"css/tac.css"])),i.scriptUrls&&i.cssUrls?loadCaptchaScript(i,t,n,r,o):o("请按照文档配置tac")})}setTimeout(()=>{let e=document.scripts,t=null;for(let n=0;n1||e[n].src.indexOf("load.min.js")>1){t=e[n].src.substring(e[n].src.indexOf("/"),e[n].src.lastIndexOf("/"));break}},100),window.loadCaptchaScript=loadCaptchaScript,window.loadTAC=loadTAC,window.initTAC=loadTAC;
diff --git a/app/public/logo.svg b/app/public/logo.svg
new file mode 100644
index 0000000..d65df8b
--- /dev/null
+++ b/app/public/logo.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/app/public/logoDark.svg b/app/public/logoDark.svg
new file mode 100644
index 0000000..1a97672
--- /dev/null
+++ b/app/public/logoDark.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/app/public/logoMini.svg b/app/public/logoMini.svg
new file mode 100644
index 0000000..dbc45c8
--- /dev/null
+++ b/app/public/logoMini.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/public/slogan.png b/app/public/slogan.png
new file mode 100644
index 0000000..029391e
Binary files /dev/null and b/app/public/slogan.png differ
diff --git a/app/public/tac/css/tac.css b/app/public/tac/css/tac.css
new file mode 100644
index 0000000..9a842f7
--- /dev/null
+++ b/app/public/tac/css/tac.css
@@ -0,0 +1,7 @@
+#tianai-captcha-parent{box-shadow:0 0 11px 0 #999;width:318px;height:318px;overflow:hidden;position:relative;z-index:997;box-sizing:border-box;border-radius:5px;padding:8px}#tianai-captcha-parent #tianai-captcha-box{height:260px;width:100%;position:relative;overflow:hidden}#tianai-captcha-parent #tianai-captcha-box .loading{width:120px;height:20px;-webkit-mask:linear-gradient(90deg, #000 70%, rgba(0, 0, 0, 0) 0) 0/20%;background:linear-gradient(#f7b645 0 0) 0/0% no-repeat rgba(221,221,221,.4196078431);animation:cartoon 1s infinite steps(6);margin:120px auto}@keyframes cartoon{100%{background-size:120%}}#tianai-captcha-parent #tianai-captcha-box #tianai-captcha{transform-style:preserve-3d;will-change:transform;transition-duration:.45s;transform:translateX(-300px)}#tianai-captcha-parent #tianai-captcha-bg-img{background-color:#fff;background-position:top;background-size:cover;z-index:-1;width:100%;height:100%;top:0;left:0;position:absolute;border-radius:6px}#tianai-captcha-parent .slider-bottom{height:19px;width:100%}#tianai-captcha-parent .slider-bottom .close-btn{width:20px;height:20px;background-image:url(../images/icon.png);background-repeat:no-repeat;background-position:0 -14px;float:right;margin-right:2px;cursor:pointer}#tianai-captcha-parent .slider-bottom .refresh-btn{width:20px;height:20px;background-image:url(../images/icon.png);background-position:0 -167px;background-repeat:no-repeat;float:right;margin-right:10px;cursor:pointer}#tianai-captcha-parent .slider-bottom .logo{height:30px;float:left}#tianai-captcha-parent .slider-move-shadow{animation:myanimation 2s infinite;height:100%;width:5px;background-color:#fff;position:absolute;top:0;left:0;filter:opacity(0.5);box-shadow:1px 1px 1px #fff;border-radius:50%}#tianai-captcha-parent #tianai-captcha-slider-move-track-mask{border-width:1px;border-style:solid;border-color:#00f4ab;width:0;height:32px;background-color:#a9ffe5;opacity:.5;position:absolute;top:-1px;left:-1px;border-radius:5px}
+#tianai-captcha{text-align:left;box-sizing:content-box;width:300px;height:260px;z-index:999}#tianai-captcha .slider-bottom .logo{height:30px}#tianai-captcha .slider-bottom{height:19px;width:100%}#tianai-captcha .content .tianai-captcha-tips{height:25px;width:100%;position:absolute;bottom:-25px;left:0;z-index:999;font-size:15px;line-height:25px;color:#fff;text-align:center;transition:bottom .3s ease-in-out}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-error{background-color:#ff5d39}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-success{background-color:#39c522}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-on{bottom:0}#tianai-captcha .content #tianai-captcha-loading{z-index:9999;background-color:#f5f5f5;text-align:center;height:100%;overflow:hidden;position:relative;display:flex;justify-content:center;align-items:center}#tianai-captcha .content #tianai-captcha-loading img{display:block;width:45px;height:45px}#tianai-captcha #tianai-captcha-slider-bg-canvas{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div .tianai-captcha-slider-bg-div-slice{position:absolute}@keyframes myanimation{from{left:0}to{left:289px}}
+#tianai-captcha.tianai-captcha-slider{z-index:999;position:absolute;left:0;top:0;user-select:none}#tianai-captcha.tianai-captcha-slider .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-slider .bg-img-div{width:100%;height:100%;position:absolute;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .bg-img-div img{height:100%;width:100%;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-img-div{height:100%;position:absolute;left:0;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .slider-img-div #tianai-captcha-slider-move-img{height:100%}#tianai-captcha.tianai-captcha-slider .slider-move{height:34px;width:100%;margin:11px 0;position:relative}#tianai-captcha.tianai-captcha-slider .slider-move-track{position:relative;height:32px;line-height:32px;text-align:center;background:#f5f5f5;color:#999;transition:0s;font-size:14px;box-sizing:content-box;border:1px solid #f5f5f5;border-radius:4px}#tianai-captcha.tianai-captcha-slider .refresh-btn,#tianai-captcha.tianai-captcha-slider .close-btn{display:inline-block}#tianai-captcha.tianai-captcha-slider .slider-move{line-height:38px;font-size:14px;text-align:center;white-space:nowrap;color:#88949d;-moz-user-select:none;-webkit-user-select:none;user-select:none;filter:opacity(0.8)}#tianai-captcha.tianai-captcha-slider .slider-move .slider-move-btn{transform:translate(0px, 0px);position:absolute;top:-6px;left:0;width:63px;height:45px;background-color:#fff;background-repeat:no-repeat;background-size:contain;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-tip{margin-bottom:5px;font-weight:bold;font-size:15px;line-height:normal;color:#000}#tianai-captcha.tianai-captcha-slider .slider-move-btn:hover{cursor:pointer}
+#tianai-captcha.tianai-captcha-rotate .rotate-img-div{height:100%;text-align:center}#tianai-captcha.tianai-captcha-rotate .rotate-img-div img{height:100%;transform:rotate(0deg);display:inline-block}
+#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-img-div{background-size:100% 180px;position:absolute;transform:translate(0px, 0px);z-index:1;width:100%}#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-bg-img{width:100%;height:100%;position:absolute;transform:translate(0px, 0px);background-size:100% 180px}
+#tianai-captcha.tianai-captcha-disable{z-index:999;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-disable .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div{background-image:url(../images/dun.jpeg);width:100%;height:100%;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div #content-span{color:#fff;overflow:hidden;margin-top:132px;display:block;text-align:center}
+#tianai-captcha.tianai-captcha-word-click{box-sizing:border-box}#tianai-captcha.tianai-captcha-word-click .click-tip{position:relative;height:40px;width:100%}#tianai-captcha.tianai-captcha-word-click .click-tip .tip-img{height:35px;position:absolute;right:15px}#tianai-captcha.tianai-captcha-word-click .click-tip #tianai-captcha-click-track-font{font-size:18px;display:inline-block;height:40px;line-height:40px;position:absolute}#tianai-captcha.tianai-captcha-word-click .slider-bottom{position:relative;top:6px}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask{width:100%;height:100%;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask .click-span{position:absolute;left:0;top:0;border-radius:50px;background-color:#409eff;width:20px;height:20px;text-align:center;line-height:20px;color:#fff;border:2px solid #fff;box-sizing:content-box}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn{width:100%;height:35px;border-radius:4px;background-image:linear-gradient(173deg, hsl(38.09, 91%, 57.89%) 0%, hsl(38.09, 89.38%, 71.74%) 100%);font-size:15px;text-align:center;box-sizing:border-box;line-height:35px;color:#fff;margin-top:3px}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn:hover{cursor:pointer}
diff --git a/app/public/tac/images/dun.jpeg b/app/public/tac/images/dun.jpeg
new file mode 100644
index 0000000..347a371
Binary files /dev/null and b/app/public/tac/images/dun.jpeg differ
diff --git a/app/public/tac/images/icon.png b/app/public/tac/images/icon.png
new file mode 100644
index 0000000..586a123
Binary files /dev/null and b/app/public/tac/images/icon.png differ
diff --git a/app/public/tac/js/tac.min.js b/app/public/tac/js/tac.min.js
new file mode 100644
index 0000000..6a4e2f4
--- /dev/null
+++ b/app/public/tac/js/tac.min.js
@@ -0,0 +1 @@
+(()=>{"use strict";var t,e,a={783:(t,e,a)=>{var i=a(618),n=Object.create(null),r="undefined"==typeof document,s=Array.prototype.forEach;function c(){}function o(t,e){if(!e){if(!t.href)return;e=t.href.split("?")[0]}if(l(e)&&!1!==t.isLoaded&&e&&e.indexOf(".css")>-1){t.visited=!0;var a=t.cloneNode();a.isLoaded=!1,a.addEventListener("load",(function(){a.isLoaded||(a.isLoaded=!0,t.parentNode.removeChild(t))})),a.addEventListener("error",(function(){a.isLoaded||(a.isLoaded=!0,t.parentNode.removeChild(t))})),a.href="".concat(e,"?").concat(Date.now()),t.nextSibling?t.parentNode.insertBefore(a,t.nextSibling):t.parentNode.appendChild(a)}}function d(t){if(!t)return!1;var e=document.querySelectorAll("link"),a=!1;return s.call(e,(function(e){if(e.href){var n=function(t,e){var a;return t=i(t),e.some((function(i){t.indexOf(e)>-1&&(a=i)})),a}(e.href,t);l(n)&&!0!==e.visited&&n&&(o(e,n),a=!0)}})),a}function h(){var t=document.querySelectorAll("link");s.call(t,(function(t){!0!==t.visited&&o(t)}))}function l(t){return!!/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(t)}t.exports=function(t,e){if(r)return c;var a,s,o,l=function(t){var e=n[t];if(!e){if(document.currentScript)e=document.currentScript.src;else{var a=document.getElementsByTagName("script"),r=a[a.length-1];r&&(e=r.src)}n[t]=e}return function(t){if(!e)return null;var a=e.split(/([^\\/]+)\.js$/),n=a&&a[1];return n&&t?t.split(",").map((function(t){var a=new RegExp("".concat(n,"\\.js$"),"g");return i(e.replace(a,"".concat(t.replace(/{fileName}/g,n),".css")))})):[e.replace(".js",".css")]}}(t);return a=function(){var t=d(l(e.filename));e.locals?h():t||h()},s=50,o=0,function(){var t=this,e=arguments;clearTimeout(o),o=setTimeout((function(){return a.apply(t,e)}),s)}}},618:t=>{t.exports=function(t){if(t=t.trim(),/^data:/i.test(t))return t;var e=-1!==t.indexOf("//")?t.split("//")[0]+"//":"",a=t.replace(new RegExp(e,"i"),"").split("/"),i=a[0].toLowerCase().replace(/\.$/,"");return a[0]="",e+i+a.reduce((function(t,e){switch(e){case"..":t.pop();break;case".":break;default:t.push(e)}return t}),[]).join("/")}},488:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},523:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},991:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},2:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},492:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},305:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},444:(t,e,a)=>{var i=a(783)(t.id,{locals:!1});t.hot.dispose(i),t.hot.accept(void 0,i)},687:(t,e,a)=>{a(488),a(523),a(444);function i(t){t.preventDefault&&t.preventDefault()}function n(t){f(t).each((t=>{t.addEventListener("touchmove",i,{passive:!1}),t.addEventListener("mousemove",i,{passive:!1})}))}function r(t){if(null!==t.pageX&&void 0!==t.pageX)return{x:Math.round(t.pageX),y:Math.round(t.pageY)};let e;return t.changedTouches?e=t.changedTouches:t.targetTouches?e=t.targetTouches:t.originalEvent&&t.originalEvent.targetTouches&&(e=t.originalEvent.targetTouches),null!==e[0].pageX&&void 0!==e[0].pageX?{x:Math.round(e[0].pageX),y:Math.round(e[0].pageY)}:{x:Math.round(e[0].clientX),y:Math.round(e[0].clientY)}}function s(t,e){const a=r(e);let i=a.x,n=a.y;t.currentCaptchaData.startX=i,t.currentCaptchaData.startY=n;const s=t.currentCaptchaData.trackList;t.currentCaptchaData.startTime=new Date;const o=t.currentCaptchaData.startTime;s.push({x:a.x,y:a.y,type:"down",t:(new Date).getTime()-o.getTime()}),t.__m__=c.bind(null,t),t.__u__=d.bind(null,t),window.addEventListener("mousemove",t.__m__),window.addEventListener("mouseup",t.__u__),window.addEventListener("touchmove",t.__m__,!1),window.addEventListener("touchend",t.__u__,!1),t&&t.doDown&&t.doDown(e,t)}function c(t,e){e.touches&&e.touches.length>0&&(e=e.touches[0]);const a=r(e);let i=a.x,n=a.y;const s=t.currentCaptchaData.startX,c=t.currentCaptchaData.startY,o=t.currentCaptchaData.startTime,d=t.currentCaptchaData.end,h=(t.currentCaptchaData.bgImageWidth,t.currentCaptchaData.trackList);let l=i-s,p=n-c;const u={x:a.x,y:a.y,type:"move",t:(new Date).getTime()-o.getTime()};h.push(u),l<0?l=0:l>d&&(l=d),t.currentCaptchaData.moveX=l,t.currentCaptchaData.moveY=p,t.doMove&&t.doMove(e,t)}function o(t){t&&(t.__m__&&(window.removeEventListener("mousemove",t.__m__),window.removeEventListener("touchmove",t.__m__)),t.__u__&&(window.removeEventListener("mouseup",t.__u__),window.removeEventListener("touchend",t.__u__)))}function d(t,e){o(t);const a=r(e);t.currentCaptchaData.stopTime=new Date;const i=t.currentCaptchaData.startTime,n=t.currentCaptchaData.trackList,s={x:a.x,y:a.y,type:"up",t:(new Date).getTime()-i.getTime()};n.push(s),t.doUp&&t.doUp(e,t),t.endCallback(t.currentCaptchaData,t)}function h(t,e,a,i,n){const r={startTime:new Date,trackList:[],movePercent:0,clickCount:0,bgImageWidth:Math.round(t),bgImageHeight:Math.round(e),templateImageWidth:Math.round(a),templateImageHeight:Math.round(i),end:n};return r}function l(t,e){f(t).find("#tianai-captcha-tips").removeClass("tianai-captcha-tips-on"),e&&setTimeout(e,.35)}function p(t,e,a,i){const n=f(t).find("#tianai-captcha-tips");n.text(e),1===a?(n.removeClass("tianai-captcha-tips-error"),n.addClass("tianai-captcha-tips-success")):(n.removeClass("tianai-captcha-tips-success"),n.addClass("tianai-captcha-tips-error")),n.addClass("tianai-captcha-tips-on"),setTimeout(i,1e3)}class u{showTips(t,e,a){p(this.el,t,e,a)}closeTips(t,e){l(this.el,t)}}function f(t,e){return new m(t,e)}class m{constructor(t,e){if(e&&"object"==typeof e&&void 0!==e.nodeType)return this.dom=e,void(this.domStr=t);if(t instanceof m)this.dom=t.dom,this.domStr=t.domStr;else if("string"==typeof t)this.dom=document.querySelector(t),this.domStr=t;else{if("object"!=typeof document||void 0===document.nodeType)throw new Error("不支持的类型");this.dom=t,this.domStr=t.nodeName}}each(t){this.getTarget().querySelectorAll("*").forEach(t)}removeClass(t){let e=this.getTarget();if(e.classList)e.classList.remove(t);else{const a=e.className,i=new RegExp("(?:^|\\s)"+t+"(?!\\S)","g");e.className=a.replace(i,"")}return this}addClass(t){const e=this.getTarget();if(e.classList)e.classList.add(t);else{let a=e.className;-1===a.indexOf(t)&&(e.className=a+" "+t)}return this}find(t){const e=this.getTarget().querySelector(t);return e?new m(t,e):null}children(t){const e=this.getTarget().childNodes;for(let a=0;a\n \n ${t.i18n.slider_title}\n
\n \n
\n
![]()
\n
\n
\n
\n
\n
![]()
\n
\n
\n
\n \n\n\n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}showTips(t,e,a){p(this.el,t,e,a)}closeTips(t){l(this.el,t)}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-img-div").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-slider-move-img");a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage),a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}))}};a(305);const g=class extends u{constructor(t,e){super(),this.boxEl=t,this.styleConfig=e,this.type="ROTATE",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append(function(t){return`\n\n
\n ${t.i18n.rotate_title}\n
\n
\n
\n
![]()
\n
\n
\n
\n
![]()
\n
\n
\n
\n
\n
\n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-move-img").css("transform","rotate("+t/(this.currentCaptchaData.end/360)+"deg)"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-slider-move-img");a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage),a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}))}};a(991);const b=class extends u{constructor(t,e){super(),this.boxEl=f(t),this.styleConfig=e,this.type="CONCAT",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append((this.styleConfig,'\n \n
\n 拖动滑块完成拼图\n
\n
\n
\n
![]()
\n
\n
\n
\n
\n
\n
\n ')),this.el=this.boxEl.find("#tianai-captcha"),this.loadStyle(),this.el.find("#tianai-captcha-slider-move-btn").mousedown(s.bind(null,this)),this.el.find("#tianai-captcha-slider-move-btn").touchstart(s.bind(null,this)),n(this.el),window.currentCaptcha=this,this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){o();const t=this.boxEl.children("#tianai-captcha");t&&t.remove()}doMove(){const t=this.currentCaptchaData.moveX;this.el.find("#tianai-captcha-slider-move-btn").css("transform","translate("+t+"px, 0px)"),this.el.find("#tianai-captcha-slider-concat-img-div").css("background-position-x",t+"px"),this.el.find("#tianai-captcha-slider-move-track-mask").css("width",t+"px")}loadStyle(){let t="",e="#00f4ab",a="#a9ffe5";const i=this.styleConfig;i&&(t=i.btnUrl,a=i.moveTrackMaskBgColor,e=i.moveTrackMaskBorderColor),this.el.find(".slider-move .slider-move-btn").css("background-image","url("+t+")"),this.el.find("#tianai-captcha-slider-move-track-mask").css("border-color",e),this.el.find("#tianai-captcha-slider-move-track-mask").css("background-color",a)}loadCaptchaForData(t,e){const a=t.el.find(".tianai-captcha-slider-concat-bg-img"),i=t.el.find("#tianai-captcha-slider-concat-img-div");a.css("background-image","url("+e.data.backgroundImage+")"),i.css("background-image","url("+e.data.backgroundImage+")"),i.css("background-position","0px 0px");var n=e.data.backgroundImageHeight,r=(n-e.data.data.randomY)/n*180;i.css("height",r+"px"),t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height(),242),t.currentCaptchaData.currentCaptchaId=e.data.id}};a(2);const C=class{constructor(t,e){this.boxEl=t,this.styleConfig=e,this.type="DISABLE",this.currentCaptchaData={}}init(t,e,a){return this.destroy(),this.boxEl.append(function(t){return`\n \n
\n ${t.i18n.disable_title}\n
\n
\n
\n\x3c!--
--\x3e\n
\n
\n
\n
\n `}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadCaptchaForData(this,t),this.endCallback=e,a&&a(this),this}destroy(){const t=this.boxEl.find("#tianai-captcha");t&&t.remove()}loadCaptchaForData(t,e){const a=e.msg||e.message||"接口异常";t.el.find("#content-span").text(a)}};a(492);const y=class extends u{constructor(t,e){super(),this.boxEl=t,this.styleConfig=e,this.type="IMAGE_CLICK",this.currentCaptchaData={}}init(t,e,a){this.destroy(),this.boxEl.append(function(t){return`\n\n
\n
${t.i18n.image_click_title}\n
![]()
\n
\n
\n
\n
![]()
\n
\n
\n
\n
\n
\n
确定
\n
\n`}(this.styleConfig)),this.el=this.boxEl.find("#tianai-captcha"),this.loadCaptchaForData(this,t),this.endCallback=e;const i=c.bind(null,this);return this.el.find("#bg-img-click-mask").click((t=>{if("click-span"===t.target.className)return;this.currentCaptchaData.clickCount++;const e=this.currentCaptchaData.trackList;1===this.currentCaptchaData.clickCount&&(this.currentCaptchaData.startTime=new Date,window.addEventListener("mousemove",i),this.currentCaptchaData.startX=t.offsetX,this.currentCaptchaData.startY=t.offsetY);const a=this.currentCaptchaData.startTime;e.push({x:Math.round(t.offsetX),y:Math.round(t.offsetY),type:"click",t:(new Date).getTime()-a.getTime()});const n=t.offsetX-10,r=t.offsetY-10;this.el.find("#bg-img-click-mask").append(""+this.currentCaptchaData.clickCount+"")})),this.el.find(".click-confirm-btn").click((()=>{this.currentCaptchaData.clickCount>0&&(this.currentCaptchaData.stopTime=new Date,window.removeEventListener("mousemove",i),this.endCallback(this.currentCaptchaData,this))})),a&&a(this),this}destroy(){const t=this.boxEl.children("#tianai-captcha");t&&t.remove(),o()}loadCaptchaForData(t,e){const a=t.el.find("#tianai-captcha-slider-bg-img"),i=t.el.find("#tianai-captcha-tip-img");a.on("load",(()=>{t.currentCaptchaData=h(a.width(),a.height(),i.width(),i.height()),t.currentCaptchaData.currentCaptchaId=e.data.id})),a.attr("src",e.data.backgroundImage),i.attr("src",e.data.templateImage)}};const w=class extends y{constructor(t,e){super(t,e),this.type="WORD_IMAGE_CLICK"}},k={btnUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAABkCAYAAABU19jRAAAJcUlEQVR4nO2d63MT1xmHf9rV6mr5fgNMuSW+ENsY8N0EE2BMhinJNB8y/dD2Qz/0v+gMf0w/JHTKNJAhICwbsA02TpNAHEMgQIwNBSEb8F2rvXTeY1kjYyA+TmVJmfeZ8YiRWa9299E57/mdI63Dtm3E+RjAKTDMaj4F8AU9uyzMCQBn+EQxb+EjAF+RMH8AcJrPFLMGvCSMzWeKWSN/I2GiAFx8xpi1oPBZYiTQWRhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGChaGkYKFYaRgYRgpWBhGCiefrtShGwZiup74+4qqwu12Z/W7lIVJEfN6FDfv3sPXfYOIRRfpm1UQKC7EkQ+PYFtRcdZKw8KkiLsPJ/CfgSFcH7yOxWhU7MSluYQoR44fxdaCoqyUhoVJEfZ8FN99c1N0Sx6PR+zEMAz0XAgBNtB14hi25OXDkWXHxUVvinA4ln6ScTqdsGwbvRd7EPwyiEcvXyDbvpyHhUkRaq4fe/c3wEWSWFZiJySNYZroCYYQPHsBY1OTWSWNevLkyb/TYwa8lt8UAb8ftluDW9UwPj4hDs0Rb3JUVRXd09j9nwELKKgoR4HXlw2Hb3INkyK8mob9NdUwLROq4sCVKwMrdqRpGkzTFN0TaWR2HcKu0rKMr2lYmBTi1jS01dUt7UBx4PKlfvHP5JaGuqseIY0DjmOHsKukNKOPiYVJMU5VRXt9PSwboO+fvHJ5QEiiKEvlIz3S86HuHiiqAhw9iJ0lpRnb0rAwG4CqKHh/Tz0UhwOWaWGg/5oofEkmJLU4wfPdQia765CQJhNHJCzMBkEtSVtdLRw2YNo2hgaGEDMMMWpahrwJBUMUCkM9djgjE2EWZgOhFqW5rlbMKdm2heHBYUT1mCiAEW9pKKfpPh8Sj5mYCLMwG4zLqWJfTZWQgL5S++uhYURjBrR4S0MtUSYnwixMGvBoGvZUV4quh0S4Pjgsaho1XtOIcM8wxJCb+qmu33dljDS/CWEeTb/E/Pw89EUdebkBVBQWrnnbWVjQoMAtsT9asGDQhf8VUbnX5UJ9VaVoZahVuXZ1cMXoiaSJxWIiEab/dPj4UXFczjRrk/VJ70/hp/jhuxF89o9TGP1+FH6fD9OxGHw5Pnicb34/PJ2dweitu7hwLojvb47A9rhQmJeXGLm8iQeP/4uRH27h88/+iZhhYs40UFZQsK7XrqkqigvyYbk18VrHH74+EX74YAzRqI66mupE15UmzKwW5kEkgtFvRxA8ex7hJ2HMzczgzu0f8fjxExRt2YzcgB9udfUJjuo6Tv/7HE6f+pe4GHd//AkwLRhuDeXFRW+U5v7EI4yMjKI3GMLt0Tt4cO8BAoEcWJoTZYXrl6asqBC6U0GOy42HY+MrZi1JmoWFRZQW5sNyuVBeUpxOabJ7aiASjiB4/iKmnj+H5loaacwvLOL2jRF4AjnY8dc/I/DKbTdoSHvr8SO8DD/DzPSMWHrg1JwYvHZdpK2NVZWU26/aF3VDTyLP0N/bh4mJR3C7XZiZnRVdht/nx7u7tsOzzg5qORFWHAocigO9vX2Jronwej24cXMEbq8XrfW169rH/4usnq02o1FEo9FEE47luN22sTAzC0OPrd7ItnHn9h0MDg3D6/WKbZdHJqYRg26ar92XDgvD39zA2Ng4VKdTbEf7mpmeRX/fAPRfeRch+luNNTXICeSu+h3ti7okUzdgp3luO6uFUTUN9lLmnniOCkdKVnML8uB0r76rD72Di4qL4NI0IUnydpZlw/WmGsY00bRvDzZvKhfFKLAU9VOG8v7BdijW+i8kLX649yyMz0+fwVQksur3NILyejzw5efCoaT3kmW1MN68AMq2bBIXXtd18WMZBt6r242DBzvgda3uWhQ4xNzOkeNdohZYXFjA4vwCfD4/Sio2i9bjdeSoGirKylFYXirykehiFHpUR2FJCbZu+x1yXlMrrQWSZWwygv6Ll3DxXBCX+66u6I7o2DRFRWtbM1o62xNdb7rI7lGSqqBs+zZMTj4XLYY/x49t7+zABx8eReWO7ciLL41ctZmqoqRiE/x+P6amp5FbkI9jx7tw+GgncqmbesPuPAEfduzcgenZOTg0FaWby/GXP/0RdZXvrOvlkyzjzyfR81UIoQs9IpRJniqglszt0tDc1oS9bc2o37lTLMhKI2bW35HtRXQRs3MLmH/xUrzzVb8HJQUFyHX/crJCQ+JwOALFqaKspGjNRWtkbg5zc7PQXC5szl/f6Ig6MFqiSavuqHCmumuFLIYBt+ZEY0sTGtua0VBTJQK/NKPzLfzSQEKWL4NiiG5a1gpZzPhMdnNrE/a3N2NPVaUI+jIAnacGNhiShdbx9pzrFgunSA4tqeUQRbuqoLW9BQ0tjSINzhBZBCzMBvPzVAS950KiG6KWJVkWGnXRELrjQBtqG/eioTqzZAELs3FQy3Iv/BR9wUtiUtGOr+tNhoptGt1V7atD4+4aEehlGizMBnH/WRj9wcuiG7LjI7Vllm8d3nnoAKoaakXq+0tzWumChUkxdlyWge4rYt0uzRMpSck01SzUDR3s7MC7e2pFRqSmOZx7GyxMCrESLcsldAd7oCgrEx6xrldRRM1SvbceHfV1K0K7TISFSREx28L41KRIcGmdruOVz82KBFd1oqWjBe/tb0ArLd3McFnAwqSOiclJ9JwP4fLFXtEtJXdDywluU2uTGDpTgZupNcur8GerU8R0eBJDV6+LRVbJLYdIcF2aSHD3tzaL9b20zjdbYGFShB0z4HY6V9QtFNLRXFATxf2U4FZXZkLcLwULkyJoaUXMNMV6HbyS4O6jicQMS3DXCguTInJKC9HU0YoPOg8k1uy0t7eivnmfSHB9WSgLwZOPKcKwLcT0GL69cxe3b46KoK6+ZS92V2zNyAR3jfBsdaox6LPSpiVyf/rEo/rq11JlFzxbnWoomEMW5CtrhWsYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgoWhpGChWGkYGEYKVgYRgr6qGx6b4/BZBXUwnzCl4xZI5844g3MCQBn+Kwxb+EjAGcdST3SxwBO8RljXsOnAL4AgP8BXnVIgIvemwsAAAAASUVORK5CYII=",moveTrackMaskBgColor:"#89d2ff",moveTrackMaskBorderColor:"#0298f8",i18n:{tips_success:"验证成功,耗时%s秒",tips_error:"验证失败,请重新尝试!",slider_title:"拖动滑块完成拼图",concat_title:"拖动滑块完成拼图",image_click_title:"请依次点击:",rotate_title:"拖动滑块完成拼图",slider_title_size:"15px",concat_title_size:"15px",image_click_title_size:"20px",rotate_title_size:"15px"}};class E{constructor(t){if(!t.bindEl)throw new Error("[TAC] 必须配置 [bindEl]用于将验证码绑定到该元素上");if(!t.requestCaptchaDataUrl)throw new Error("[TAC] 必须配置 [requestCaptchaDataUrl]请求验证码接口");if(!t.validCaptchaUrl)throw new Error("[TAC] 必须配置 [validCaptchaUrl]验证验证码接口");this.bindEl=t.bindEl,this.domBindEl=f(t.bindEl),this.requestCaptchaDataUrl=t.requestCaptchaDataUrl,this.validCaptchaUrl=t.validCaptchaUrl,t.validSuccess&&(this.validSuccess=t.validSuccess),t.validFail&&(this.validFail=t.validFail),t.requestHeaders?this.requestHeaders=t.requestHeaders:this.requestHeaders={},t.btnCloseFun&&(this.btnCloseFun=t.btnCloseFun),t.btnRefreshFun&&(this.btnRefreshFun=t.btnRefreshFun),this.requestChain=[],this.timeToTimestamp=t.timeToTimestamp||!0,this.insertRequestChain(0,{preRequest(t,e,a,i){if(this.timeToTimestamp&&e.data)for(let t in e.data)e.data[t]instanceof Date&&(e.data[t]=e.data[t].getTime());return!0}})}addRequestChain(t){this.requestChain.push(t)}insertRequestChain(t,e){this.requestChain.splice(t,0,e)}removeRequestChain(t){this.requestChain.splice(t,1)}requestCaptchaData(){const t={};t.headers=this.requestHeaders||{},t.data={},t.headers["Content-Type"]="application/json;charset=UTF-8",t.method="POST",t.url=this.requestCaptchaDataUrl,this._preRequest("requestCaptchaData",t);return this.doSendRequest(t).then((e=>(this._postRequest("requestCaptchaData",t,e),e)))}doSendRequest(t){if(t.headers)for(const e in t.headers)if(t.headers[e].indexOf("application/json")>-1){"string"!=typeof t.data&&(t.data=JSON.stringify(t.data));break}return(e=t,new Promise((function(t,a){var i=new XMLHttpRequest;if(i.open(e.method||"GET",e.url),e.headers)for(const t in e.headers)e.headers.hasOwnProperty(t)&&i.setRequestHeader(t,e.headers[t]);i.onreadystatechange=function(){if(i.readyState===XMLHttpRequest.DONE)if(i.status>=200&&i.status<=500){const e=i.getResponseHeader("Content-Type");e&&-1!==e.indexOf("application/json")?t(JSON.parse(i.responseText)):t(i.responseText)}else a(new Error("Request failed with status: "+i.status))},i.onerror=function(){a(new Error("Network Error"))},i.send(e.data)}))).then((t=>{try{return JSON.parse(t)}catch(e){return t}}));var e}_preRequest(t,e,a,i){for(let n=0;n(this._postRequest("validCaptcha",r,t,a,i),t))).then((t=>{if(200==t.code){const n=(e.stopTime-e.startTime)/1e3;a.showTips(`验证成功,耗时${n}秒`,1,(()=>this.validSuccess(t,a,i)))}else{let e="验证失败,请重新尝试!";t.code&&4001!=t.code&&(e="验证码被黑洞吸走了!"),a.showTips(e,0,(()=>this.validFail(t,a,i)))}})).catch((t=>{let e=a.styleConfig.i18n.tips_error;t.code&&200!=t.code&&(4001!=res.code&&(e=a.styleConfig.i18n.tips_4001),a.showTips(e,0,(()=>this.validFail(res,a,i))))}))}validSuccess(t,e,a){window.currentCaptchaRes=t,a.destroyWindow()}validFail(t,e,a){a.reloadCaptcha()}}window.TAC=class{constructor(t,e){this.config=function(t){return t instanceof E?t:new E(t)}(t),this.config.btnRefreshFun&&(this.btnRefreshFun=this.config.btnRefreshFun),this.config.btnCloseFun&&(this.btnCloseFun=this.config.btnCloseFun),this.style=function(t){let e={...k,...t};return e.i18n={...k.i18n,...t?.i18n},e}(e)}init(){return this.destroyWindow(),this.config.domBindEl.append('\n \n
\n
\n \x3c!-- 底部 --\x3e\n
\n

\n
\n
\n
\n
\n '),this.domTemplate=this.config.domBindEl.find("#tianai-captcha-parent"),n(this.domTemplate),this.loadStyle(),this.config.domBindEl.find("#tianai-captcha-slider-refresh-btn").click((t=>{this.btnRefreshFun(t,this)})),this.config.domBindEl.find("#tianai-captcha-slider-close-btn").click((t=>{this.btnCloseFun(t,this)})),this.reloadCaptcha(),this}btnRefreshFun(t,e){e.reloadCaptcha()}btnCloseFun(t,e){e.destroyWindow()}reloadCaptcha(){this.showLoading(),this.destroyCaptcha((()=>{this.createCaptcha()}))}showLoading(){this.config.domBindEl.find("#tianai-captcha-loading").css("display","block")}closeLoading(){this.config.domBindEl.find("#tianai-captcha-loading").css("display","none")}loadStyle(){const t=this.style.bgUrl,e=this.style.logoUrl;t&&this.config.domBindEl.find("#tianai-captcha-bg-img").css("background-image","url("+t+")"),e&&""!==e?this.config.domBindEl.find("#tianai-captcha-logo").attr("src",e):null===e&&this.config.domBindEl.find("#tianai-captcha-logo").css("display","none")}destroyWindow(){this.C&&(this.C.destroy(),this.C=void 0),this.domTemplate&&this.domTemplate.remove()}openCaptcha(){setTimeout((()=>{this.C.el.css("transform","translateX(0)")}),10)}createCaptcha(){this.config.requestCaptchaData().then((t=>{if(this.closeLoading(),!t.code)throw new Error("[TAC] 后台验证码接口数据错误!!!");let e=200===t.code?t.data?.type:"DISABLED";const a=function(t,e){const a=e.config.domBindEl.find("#tianai-captcha-box"),i=e.style;switch(t){case"SLIDER":return new v(a,i);case"ROTATE":return new g(a,i);case"CONCAT":return new b(a,i);case"WORD_IMAGE_CLICK":return new w(a,i);case"DISABLED":return new C(a,i);default:return null}}(e,this);if(null==a)throw new Error("[TAC] 未知的验证码类型["+e+"]");a.init(t,((t,e)=>{const a=e.currentCaptchaData,i={bgImageWidth:a.bgImageWidth,bgImageHeight:a.bgImageHeight,templateImageWidth:a.templateImageWidth,templateImageHeight:a.templateImageHeight,startTime:a.startTime.getTime(),stopTime:a.stopTime.getTime(),trackList:a.trackList};"ROTATE_DEGREE"!==e.type&&"ROTATE"!==e.type||(i.bgImageWidth=e.currentCaptchaData.end),a.data&&(i.data=a.data);const n=e.currentCaptchaData.currentCaptchaId;e.currentCaptchaData=void 0,this.config.validCaptcha(n,i,e,this)})),this.C=a,this.openCaptcha()}))}destroyCaptcha(t){this.C?(this.C.el.css("transform","translateX(300px)"),setTimeout((()=>{this.C.destroy(),t&&t()}),500)):t()}},window.CaptchaConfig=E}},i={};function n(t){var e=i[t];if(void 0!==e){if(void 0!==e.error)throw e.error;return e.exports}var r=i[t]={id:t,exports:{}};try{var s={id:t,module:r,factory:a[t],require:n};n.i.forEach((function(t){t(s)})),r=s.module,s.factory.call(r.exports,r,r.exports,s.require)}catch(t){throw r.error=t,t}return r.exports}n.m=a,n.c=i,n.i=[],n.hu=t=>t+"."+n.h()+".hot-update.js",n.miniCssF=t=>{},n.hmrF=()=>"main."+n.h()+".hot-update.json",n.h=()=>"7ca661d8917e051f770d",n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),t={},e="webpack-demo:",n.l=(a,i,r,s)=>{if(t[a])t[a].push(i);else{var c,o;if(void 0!==r)for(var d=document.getElementsByTagName("script"),h=0;h{c.onerror=c.onload=null,clearTimeout(u);var n=t[a];if(delete t[a],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach((t=>t(i))),e)return e(i)},u=setTimeout(p.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=p.bind(null,c.onerror),c.onload=p.bind(null,c.onload),o&&document.head.appendChild(c)}},(()=>{var t,e,a,i={},r=n.c,s=[],c=[],o="idle",d=0,h=[];function l(t){o=t;for(var e=[],a=0;a0)return l("abort").then((function(){throw n[0]}));var r=l("dispose");i.forEach((function(t){t.dispose&&t.dispose()}));var s,c=l("apply"),o=function(t){s||(s=t)},d=[];return i.forEach((function(t){if(t.apply){var e=t.apply(o);if(e)for(var a=0;a=0&&b._disposeHandlers.splice(e,1)},invalidate:function(){switch(this._selfInvalidated=!0,o){case"idle":e=[],Object.keys(n.hmrI).forEach((function(t){n.hmrI[t](m,e)})),l("ready");break;case"ready":Object.keys(n.hmrI).forEach((function(t){n.hmrI[t](m,e)}));break;case"prepare":case"check":case"dispose":case"apply":(a=a||[]).push(m)}},check:u,apply:f,status:function(t){if(!t)return o;c.push(t)},addStatusHandler:function(t){c.push(t)},removeStatusHandler:function(t){var e=c.indexOf(t);e>=0&&c.splice(e,1)},data:i[m]},t=void 0,b),C.parents=s,C.children=[],s=[],h.require=y})),n.hmrC={},n.hmrI={}})(),(()=>{var t;n.g.importScripts&&(t=n.g.location+"");var e=n.g.document;if(!t&&e&&(e.currentScript&&(t=e.currentScript.src),!t)){var a=e.getElementsByTagName("script");if(a.length)for(var i=a.length-1;i>-1&&!t;)t=a[i--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=t+"../../"})(),(()=>{if("undefined"!=typeof document){var t=(t,e,a,i,n)=>{var r=document.createElement("link");r.rel="stylesheet",r.type="text/css";return r.onerror=r.onload=a=>{if(r.onerror=r.onload=null,"load"===a.type)i();else{var s=a&&("load"===a.type?"missing":a.type),c=a&&a.target&&a.target.href||e,o=new Error("Loading CSS chunk "+t+" failed.\n("+c+")");o.code="CSS_CHUNK_LOAD_FAILED",o.type=s,o.request=c,r.parentNode&&r.parentNode.removeChild(r),n(o)}},r.href=e,a?a.parentNode.insertBefore(r,a.nextSibling):document.head.appendChild(r),r},e=(t,e)=>{for(var a=document.getElementsByTagName("link"),i=0;i({dispose:()=>{for(var t=0;t{for(var t=0;t{h.push(r),s.forEach((r=>{var s=n.miniCssF(r),c=n.p+s,o=e(s,c);o&&d.push(new Promise(((e,n)=>{var s=t(r,c,o,(()=>{s.as="style",s.rel="preload",e()}),n);a.push(o),i.push(s)})))}))}}})(),(()=>{var t,e,a,i,r,s=n.hmrS_jsonp=n.hmrS_jsonp||{179:0},c={};function o(e,a){return t=a,new Promise(((t,a)=>{c[e]=t;var i=n.p+n.hu(e),r=new Error;n.l(i,(t=>{if(c[e]){c[e]=void 0;var i=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;r.message="Loading hot update chunk "+e+" failed.\n("+i+": "+n+")",r.name="ChunkLoadError",r.type=i,r.request=n,a(r)}}))}))}function d(t){function c(t){for(var e=[t],a={},i=e.map((function(t){return{chain:[t],id:t}}));i.length>0;){var r=i.pop(),s=r.id,c=r.chain,d=n.c[s];if(d&&(!d.hot._selfAccepted||d.hot._selfInvalidated)){if(d.hot._selfDeclined)return{type:"self-declined",chain:c,moduleId:s};if(d.hot._main)return{type:"unaccepted",chain:c,moduleId:s};for(var h=0;h ")),f.type){case"self-declined":t.onDeclined&&t.onDeclined(f),t.ignoreDeclined||(v=new Error("Aborted because of self decline: "+f.moduleId+C));break;case"declined":t.onDeclined&&t.onDeclined(f),t.ignoreDeclined||(v=new Error("Aborted because of declined dependency: "+f.moduleId+" in "+f.parentId+C));break;case"unaccepted":t.onUnaccepted&&t.onUnaccepted(f),t.ignoreUnaccepted||(v=new Error("Aborted because "+u+" is not accepted"+C));break;case"accepted":t.onAccepted&&t.onAccepted(f),g=!0;break;case"disposed":t.onDisposed&&t.onDisposed(f),b=!0;break;default:throw new Error("Unexception type "+f.type)}if(v)return{error:v};if(g)for(u in l[u]=m,o(h,f.outdatedModules),f.outdatedDependencies)n.o(f.outdatedDependencies,u)&&(d[u]||(d[u]=[]),o(d[u],f.outdatedDependencies[u]));b&&(o(h,[f.moduleId]),l[u]=p)}a=void 0;for(var y,w=[],k=0;k0;){var r=a.pop(),c=n.c[r];if(c){var o={},l=c.hot._disposeHandlers;for(k=0;k=0&&p.parents.splice(t,1))}}}for(var u in d)if(n.o(d,u)&&(c=n.c[u]))for(y=d[u],k=0;k=0&&c.children.splice(t,1)},apply:function(e){for(var a in l)n.o(l,a)&&(n.m[a]=l[a]);for(var i=0;i{for(var o in i)n.o(i,o)&&(a[o]=i[o],t&&t.push(o));s&&r.push(s),c[e]&&(c[e](),c[e]=void 0)},n.hmrI.jsonp=function(t,e){a||(a={},r=[],i=[],e.push(d)),n.o(a,t)||(a[t]=n.m[t])},n.hmrC.jsonp=function(t,c,h,l,p,u){p.push(d),e={},i=c,a=h.reduce((function(t,e){return t[e]=!1,t}),{}),r=[],t.forEach((function(t){n.o(s,t)&&void 0!==s[t]?(l.push(o(t,u)),e[t]=!0):e[t]=!1})),n.f&&(n.f.jsonpHmr=function(t,a){e&&n.o(e,t)&&!e[t]&&(a.push(o(t)),e[t]=!0)})},n.hmrM=()=>{if("undefined"==typeof fetch)throw new Error("No browser support: need fetch API");return fetch(n.p+n.hmrF()).then((t=>{if(404!==t.status){if(!t.ok)throw new Error("Failed to fetch update manifest "+t.statusText);return t.json()}}))}})();n(687)})();
diff --git a/app/src/adapter/component/index.ts b/app/src/adapter/component/index.ts
new file mode 100644
index 0000000..25d2fad
--- /dev/null
+++ b/app/src/adapter/component/index.ts
@@ -0,0 +1,331 @@
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 aiflowy-form、aiflowy-modal、aiflowy-drawer 等组件使用,
+ */
+
+import type { Component } from 'vue';
+
+import type { BaseFormComponentType } from '@aiflowy/common-ui';
+import type { Recordable } from '@aiflowy/types';
+
+import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
+
+import { ApiComponent, globalShareState, IconPicker } from '@aiflowy/common-ui';
+import { $t } from '@aiflowy/locales';
+
+import { ElNotification } from 'element-plus';
+
+const ElButton = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/button/index'),
+ import('element-plus/es/components/button/style/css'),
+ ]).then(([res]) => res.ElButton),
+);
+const ElCheckbox = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/checkbox/index'),
+ import('element-plus/es/components/checkbox/style/css'),
+ ]).then(([res]) => res.ElCheckbox),
+);
+const ElCheckboxButton = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/checkbox/index'),
+ import('element-plus/es/components/checkbox-button/style/css'),
+ ]).then(([res]) => res.ElCheckboxButton),
+);
+const ElCheckboxGroup = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/checkbox/index'),
+ import('element-plus/es/components/checkbox-group/style/css'),
+ ]).then(([res]) => res.ElCheckboxGroup),
+);
+const ElDatePicker = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/date-picker/index'),
+ import('element-plus/es/components/date-picker/style/css'),
+ ]).then(([res]) => res.ElDatePicker),
+);
+const ElDivider = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/divider/index'),
+ import('element-plus/es/components/divider/style/css'),
+ ]).then(([res]) => res.ElDivider),
+);
+const ElInput = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/input/index'),
+ import('element-plus/es/components/input/style/css'),
+ ]).then(([res]) => res.ElInput),
+);
+const ElInputNumber = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/input-number/index'),
+ import('element-plus/es/components/input-number/style/css'),
+ ]).then(([res]) => res.ElInputNumber),
+);
+const ElRadio = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/radio/index'),
+ import('element-plus/es/components/radio/style/css'),
+ ]).then(([res]) => res.ElRadio),
+);
+const ElRadioButton = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/radio/index'),
+ import('element-plus/es/components/radio-button/style/css'),
+ ]).then(([res]) => res.ElRadioButton),
+);
+const ElRadioGroup = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/radio/index'),
+ import('element-plus/es/components/radio-group/style/css'),
+ ]).then(([res]) => res.ElRadioGroup),
+);
+const ElSelectV2 = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/select-v2/index'),
+ import('element-plus/es/components/select-v2/style/css'),
+ ]).then(([res]) => res.ElSelectV2),
+);
+const ElSpace = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/space/index'),
+ import('element-plus/es/components/space/style/css'),
+ ]).then(([res]) => res.ElSpace),
+);
+const ElSwitch = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/switch/index'),
+ import('element-plus/es/components/switch/style/css'),
+ ]).then(([res]) => res.ElSwitch),
+);
+const ElTimePicker = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/time-picker/index'),
+ import('element-plus/es/components/time-picker/style/css'),
+ ]).then(([res]) => res.ElTimePicker),
+);
+const ElTreeSelect = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/tree-select/index'),
+ import('element-plus/es/components/tree-select/style/css'),
+ ]).then(([res]) => res.ElTreeSelect),
+);
+const ElUpload = defineAsyncComponent(() =>
+ Promise.all([
+ import('element-plus/es/components/upload/index'),
+ import('element-plus/es/components/upload/style/css'),
+ ]).then(([res]) => res.ElUpload),
+);
+
+const withDefaultPlaceholder = (
+ component: T,
+ type: 'input' | 'select',
+ componentProps: Recordable = {},
+) => {
+ return defineComponent({
+ name: component.name,
+ inheritAttrs: false,
+ setup: (props: any, { attrs, expose, slots }) => {
+ const placeholder =
+ props?.placeholder ||
+ attrs?.placeholder ||
+ $t(`ui.placeholder.${type}`);
+ // 透传组件暴露的方法
+ const innerRef = ref();
+ expose(
+ new Proxy(
+ {},
+ {
+ get: (_target, key) => innerRef.value?.[key],
+ has: (_target, key) => key in (innerRef.value || {}),
+ },
+ ),
+ );
+ return () =>
+ h(
+ component,
+ { ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
+ slots,
+ );
+ },
+ });
+};
+
+// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
+export type ComponentType =
+ | 'ApiSelect'
+ | 'ApiTreeSelect'
+ | 'Checkbox'
+ | 'CheckboxGroup'
+ | 'DatePicker'
+ | 'Divider'
+ | 'IconPicker'
+ | 'Input'
+ | 'InputNumber'
+ | 'RadioGroup'
+ | 'Select'
+ | 'Space'
+ | 'Switch'
+ | 'TimePicker'
+ | 'TreeSelect'
+ | 'Upload'
+ | BaseFormComponentType;
+
+async function initComponentAdapter() {
+ const components: Partial> = {
+ // 如果你的组件体积比较大,可以使用异步加载
+ // Button: () =>
+ // import('xxx').then((res) => res.Button),
+ ApiSelect: withDefaultPlaceholder(
+ {
+ ...ApiComponent,
+ name: 'ApiSelect',
+ },
+ 'select',
+ {
+ component: ElSelectV2,
+ loadingSlot: 'loading',
+ visibleEvent: 'onVisibleChange',
+ },
+ ),
+ ApiTreeSelect: withDefaultPlaceholder(
+ {
+ ...ApiComponent,
+ name: 'ApiTreeSelect',
+ },
+ 'select',
+ {
+ component: ElTreeSelect,
+ props: { label: 'label', children: 'children' },
+ nodeKey: 'value',
+ loadingSlot: 'loading',
+ optionsPropName: 'data',
+ visibleEvent: 'onVisibleChange',
+ },
+ ),
+ Checkbox: ElCheckbox,
+ CheckboxGroup: (props, { attrs, slots }) => {
+ let defaultSlot;
+ if (Reflect.has(slots, 'default')) {
+ defaultSlot = slots.default;
+ } else {
+ const { options, isButton } = attrs;
+ if (Array.isArray(options)) {
+ defaultSlot = () =>
+ options.map((option) =>
+ h(isButton ? ElCheckboxButton : ElCheckbox, option),
+ );
+ }
+ }
+ return h(
+ ElCheckboxGroup,
+ { ...props, ...attrs },
+ { ...slots, default: defaultSlot },
+ );
+ },
+ // 自定义默认按钮
+ DefaultButton: (props, { attrs, slots }) => {
+ return h(ElButton, { ...props, attrs, type: 'info' }, slots);
+ },
+ // 自定义主要按钮
+ PrimaryButton: (props, { attrs, slots }) => {
+ return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
+ },
+ Divider: ElDivider,
+ IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
+ iconSlot: 'append',
+ modelValueProp: 'model-value',
+ inputComponent: ElInput,
+ }),
+ Input: withDefaultPlaceholder(ElInput, 'input'),
+ InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
+ RadioGroup: (props, { attrs, slots }) => {
+ let defaultSlot;
+ if (Reflect.has(slots, 'default')) {
+ defaultSlot = slots.default;
+ } else {
+ const { options } = attrs;
+ if (Array.isArray(options)) {
+ defaultSlot = () =>
+ options.map((option) =>
+ h(attrs.isButton ? ElRadioButton : ElRadio, option),
+ );
+ }
+ }
+ return h(
+ ElRadioGroup,
+ { ...props, ...attrs },
+ { ...slots, default: defaultSlot },
+ );
+ },
+ Select: (props, { attrs, slots }) => {
+ return h(ElSelectV2, { ...props, attrs }, slots);
+ },
+ Space: ElSpace,
+ Switch: ElSwitch,
+ TimePicker: (props, { attrs, slots }) => {
+ const { name, id, isRange } = props;
+ const extraProps: Recordable = {};
+ if (isRange) {
+ if (name && !Array.isArray(name)) {
+ extraProps.name = [name, `${name}_end`];
+ }
+ if (id && !Array.isArray(id)) {
+ extraProps.id = [id, `${id}_end`];
+ }
+ }
+ return h(
+ ElTimePicker,
+ {
+ ...props,
+ ...attrs,
+ ...extraProps,
+ },
+ slots,
+ );
+ },
+ DatePicker: (props, { attrs, slots }) => {
+ const { name, id, type } = props;
+ const extraProps: Recordable = {};
+ if (type && type.includes('range')) {
+ if (name && !Array.isArray(name)) {
+ extraProps.name = [name, `${name}_end`];
+ }
+ if (id && !Array.isArray(id)) {
+ extraProps.id = [id, `${id}_end`];
+ }
+ }
+ return h(
+ ElDatePicker,
+ {
+ ...props,
+ ...attrs,
+ ...extraProps,
+ },
+ slots,
+ );
+ },
+ TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
+ Upload: ElUpload,
+ };
+
+ // 将组件注册到全局共享状态中
+ globalShareState.setComponents(components);
+
+ // 定义全局共享状态中的消息提示
+ globalShareState.defineMessage({
+ // 复制成功消息提示
+ copyPreferencesSuccess: (title, content) => {
+ ElNotification({
+ title,
+ message: content,
+ position: 'bottom-right',
+ duration: 0,
+ type: 'success',
+ });
+ },
+ });
+}
+
+export { initComponentAdapter };
diff --git a/app/src/adapter/form.ts b/app/src/adapter/form.ts
new file mode 100644
index 0000000..9542f52
--- /dev/null
+++ b/app/src/adapter/form.ts
@@ -0,0 +1,41 @@
+import type {
+ AIFlowyFormSchema as FormSchema,
+ AIFlowyFormProps,
+} from '@aiflowy/common-ui';
+
+import type { ComponentType } from './component';
+
+import { setupAIFlowyForm, useAIFlowyForm as useForm, z } from '@aiflowy/common-ui';
+import { $t } from '@aiflowy/locales';
+
+async function initSetupAIFlowyForm() {
+ setupAIFlowyForm({
+ config: {
+ modelPropNameMap: {
+ Upload: 'fileList',
+ CheckboxGroup: 'model-value',
+ },
+ },
+ defineRules: {
+ required: (value, _params, ctx) => {
+ if (value === undefined || value === null || value.length === 0) {
+ return $t('ui.formRules.required', [ctx.label]);
+ }
+ return true;
+ },
+ selectRequired: (value, _params, ctx) => {
+ if (value === undefined || value === null) {
+ return $t('ui.formRules.selectRequired', [ctx.label]);
+ }
+ return true;
+ },
+ },
+ });
+}
+
+const useAIFlowyForm = useForm;
+
+export { initSetupAIFlowyForm, useAIFlowyForm, z };
+
+export type AIFlowyFormSchema = FormSchema;
+export type { AIFlowyFormProps };
diff --git a/app/src/adapter/vxe-table.ts b/app/src/adapter/vxe-table.ts
new file mode 100644
index 0000000..9a96581
--- /dev/null
+++ b/app/src/adapter/vxe-table.ts
@@ -0,0 +1,70 @@
+import type { VxeTableGridOptions } from '@aiflowy/plugins/vxe-table';
+
+import { h } from 'vue';
+
+import { setupAIFlowyVxeTable, useAIFlowyVxeGrid } from '@aiflowy/plugins/vxe-table';
+
+import { ElButton, ElImage } from 'element-plus';
+
+import { useAIFlowyForm } from './form';
+
+setupAIFlowyVxeTable({
+ configVxeTable: (vxeUI) => {
+ vxeUI.setConfig({
+ grid: {
+ align: 'center',
+ border: false,
+ columnConfig: {
+ resizable: true,
+ },
+ minHeight: 180,
+ formConfig: {
+ // 全局禁用vxe-table的表单配置,使用formOptions
+ enabled: false,
+ },
+ proxyConfig: {
+ autoLoad: true,
+ response: {
+ result: 'items',
+ total: 'total',
+ list: 'items',
+ },
+ showActiveMsg: true,
+ showResponseMsg: false,
+ },
+ round: true,
+ showOverflow: true,
+ size: 'small',
+ } as VxeTableGridOptions,
+ });
+
+ // 表格配置项可以用 cellRender: { name: 'CellImage' },
+ vxeUI.renderer.add('CellImage', {
+ renderTableDefault(_renderOpts, params) {
+ const { column, row } = params;
+ const src = row[column.field];
+ return h(ElImage, { src, previewSrcList: [src] });
+ },
+ });
+
+ // 表格配置项可以用 cellRender: { name: 'CellLink' },
+ vxeUI.renderer.add('CellLink', {
+ renderTableDefault(renderOpts) {
+ const { props } = renderOpts;
+ return h(
+ ElButton,
+ { size: 'small', link: true },
+ { default: () => props?.text },
+ );
+ },
+ });
+
+ // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
+ // vxeUI.formats.add
+ },
+ useAIFlowyForm,
+});
+
+export { useAIFlowyVxeGrid };
+
+export type * from '@aiflowy/plugins/vxe-table';
diff --git a/app/src/api/ai/bot.ts b/app/src/api/ai/bot.ts
new file mode 100644
index 0000000..1f4e249
--- /dev/null
+++ b/app/src/api/ai/bot.ts
@@ -0,0 +1,138 @@
+import type {
+ AiLlm,
+ BotInfo,
+ ChatMessage,
+ RequestResult,
+ Session,
+} from '@aiflowy/types';
+
+import { api } from '#/api/request.js';
+
+/** 获取bot详情 */
+export const getBotDetails = (id: string) => {
+ return api.get>('/api/v1/bot/getDetail', {
+ params: { id },
+ });
+};
+
+export interface GetSessionListParams {
+ botId: string;
+ tempUserId: string;
+}
+/** 获取bot对话列表 */
+export const getSessionList = (params: GetSessionListParams) => {
+ return api.get>(
+ '/api/v1/conversation/externalList',
+ { params },
+ );
+};
+
+export interface SaveBotParams {
+ icon: string;
+ title: string;
+ alias: string;
+ description: string;
+ categoryId: any;
+ status: number;
+}
+/** 创建Bot */
+export const saveBot = (params: SaveBotParams) => {
+ return api.post('/api/v1/bot/save', { ...params });
+};
+
+export interface UpdateBotParams extends SaveBotParams {
+ id: string;
+}
+/** 修改Bot */
+export const updateBotApi = (params: UpdateBotParams) => {
+ return api.post('/api/v1/bot/update', { ...params });
+};
+
+/** 删除Bot */
+export const removeBotFromId = (id: string) => {
+ return api.post('/api/v1/bot/update', { id });
+};
+
+export interface GetMessageListParams {
+ conversationId: string;
+ botId: string;
+ tempUserId: string;
+}
+/** 获取单个对话的信息列表 */
+export const getMessageList = (params: GetMessageListParams) => {
+ return api.get>(
+ '/api/v1/botMessage/messageList',
+ {
+ params,
+ },
+ );
+};
+
+/** 更新Bot的LLM配置 */
+export interface UpdateLlmOptionsParams {
+ id: string;
+ llmOptions: {
+ [key: string]: any;
+ };
+}
+export interface UpdateBotOptionsParams {
+ id: string;
+ options: {
+ [key: string]: any;
+ };
+}
+
+export const updateLlmOptions = (params: UpdateLlmOptionsParams) => {
+ return api.post('/api/v1/bot/updateLlmOptions', {
+ ...params,
+ });
+};
+
+export const updateBotOptions = (params: UpdateBotOptionsParams) => {
+ return api.post('/api/v1/bot/updateOptions', {
+ ...params,
+ });
+};
+
+/** 更新Bot的LLM配置 */
+export interface GetAiLlmListParams {
+ [key: string]: any;
+}
+export const getAiLlmList = (params: GetAiLlmListParams) => {
+ return api.get>('/api/v1/model/list', {
+ params,
+ });
+};
+
+/** 更新modelId */
+export interface UpdateLlmIdParams {
+ id: string;
+ modelId: string;
+}
+export const updateLlmId = (params: UpdateLlmIdParams) => {
+ return api.post('/api/v1/bot/updateLlmId', {
+ ...params,
+ });
+};
+
+export const doPostBotPluginTools = (botId: string) => {
+ return api.post>('/api/v1/pluginItem/tool/list', {
+ id: botId,
+ });
+};
+
+export const getPerQuestions = (presetQuestions: any[]) => {
+ if (!presetQuestions) {
+ return [];
+ }
+ return presetQuestions
+ .filter((item: any) => {
+ return (
+ typeof item.description === 'string' && item.description.trim() !== ''
+ );
+ })
+ .map((item: any) => ({
+ key: item.key,
+ description: item.description,
+ }));
+};
diff --git a/app/src/api/ai/index.ts b/app/src/api/ai/index.ts
new file mode 100644
index 0000000..9db119a
--- /dev/null
+++ b/app/src/api/ai/index.ts
@@ -0,0 +1,2 @@
+export * from './bot';
+export * from './llm';
diff --git a/app/src/api/ai/knowledge.ts b/app/src/api/ai/knowledge.ts
new file mode 100644
index 0000000..e69de29
diff --git a/app/src/api/ai/llm.ts b/app/src/api/ai/llm.ts
new file mode 100644
index 0000000..76cb2b3
--- /dev/null
+++ b/app/src/api/ai/llm.ts
@@ -0,0 +1,44 @@
+import { api } from '#/api/request.js';
+
+// 获取LLM供应商
+export async function getLlmProviderList() {
+ return api.get('/api/v1/modelProvider/list');
+}
+
+// 保存LLM
+export async function saveLlm(data: string) {
+ return api.post('/api/v1/model/save', data);
+}
+
+// 删除LLM
+export async function deleteLlm(data: any) {
+ return api.post(`/api/v1/model/remove`, data);
+}
+
+// 修改LLM
+export async function updateLlm(data: any) {
+ return api.post(`/api/v1/model/update`, data);
+}
+
+// 一键添加LLM
+export async function quickAddLlm(data: any) {
+ return api.post(`/api/v1/model/quickAdd`, data);
+}
+
+export interface llmType {
+ id: string;
+ title: string;
+ modelProvider: {
+ icon: string;
+ providerName: string;
+ providerType: string;
+ };
+ withUsed: boolean;
+ llmModel: string;
+ icon: string;
+ description: string;
+ modelType: string;
+ groupName: string;
+ added: boolean;
+ aiLlmProvider: any;
+}
diff --git a/app/src/api/common/file.ts b/app/src/api/common/file.ts
new file mode 100644
index 0000000..525beb2
--- /dev/null
+++ b/app/src/api/common/file.ts
@@ -0,0 +1,27 @@
+/**
+ * 格式化文件大小(字节转 B/KB/MB/GB/TB)
+ * @param bytes - 文件大小(单位:字节 Byte)
+ * @param decimalPlaces - 保留小数位数(默认 2 位)
+ * @returns 格式化后的大小字符串(如:1.23 MB、456 B、7.8 GB)
+ */
+export function formatFileSize(
+ bytes: number,
+ decimalPlaces: number = 2,
+): string {
+ // 处理特殊情况:bytes 为 0 或非数字
+ if (Number.isNaN(bytes) || bytes < 0) return '0 B';
+ if (bytes === 0) return '0 B';
+
+ // 单位数组(从 Byte 到 TB)
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
+ // 计算合适的单位索引(1 KB = 1024 B,每次除以 1024 切换单位)
+ const unitIndex = Math.floor(Math.log(bytes) / Math.log(1024));
+ // 计算对应单位的大小(保留指定小数位)
+ const formattedSize = (bytes / 1024 ** unitIndex).toFixed(decimalPlaces);
+
+ // 移除末尾多余的 .00(如 2.00 MB → 2 MB,1.50 KB → 1.5 KB)
+ const sizeWithoutTrailingZeros = Number.parseFloat(formattedSize).toString();
+
+ // 返回格式化结果(单位与大小拼接)
+ return `${sizeWithoutTrailingZeros} ${units[unitIndex]}`;
+}
diff --git a/app/src/api/common/hasPermission.ts b/app/src/api/common/hasPermission.ts
new file mode 100644
index 0000000..e1aa91b
--- /dev/null
+++ b/app/src/api/common/hasPermission.ts
@@ -0,0 +1,9 @@
+import { useAccessStore } from '@aiflowy/stores';
+
+export function hasPermission(codes: string[]) {
+ const accessStore = useAccessStore();
+ const userCodesSet = new Set(accessStore.accessCodes);
+
+ const intersection = codes.filter((item) => userCodesSet.has(item));
+ return intersection.length > 0;
+}
diff --git a/app/src/api/core/auth.ts b/app/src/api/core/auth.ts
new file mode 100644
index 0000000..d0e8700
--- /dev/null
+++ b/app/src/api/core/auth.ts
@@ -0,0 +1,52 @@
+import { baseRequestClient, requestClient } from '#/api/request';
+
+export namespace AuthApi {
+ /** 登录接口参数 */
+ export interface LoginParams {
+ password?: string;
+ username?: string;
+ }
+
+ /** 登录接口返回值 */
+ export interface LoginResult {
+ accessToken: string;
+ token: string;
+ }
+
+ export interface RefreshTokenResult {
+ data: string;
+ status: number;
+ }
+}
+
+/**
+ * 登录
+ */
+export async function loginApi(data: AuthApi.LoginParams) {
+ return requestClient.post('/api/v1/auth/login', data);
+}
+
+/**
+ * 刷新accessToken
+ */
+export async function refreshTokenApi() {
+ return baseRequestClient.post('/auth/refresh', {
+ withCredentials: true,
+ });
+}
+
+/**
+ * 退出登录
+ */
+export async function logoutApi() {
+ return requestClient.post('/api/v1/auth/logout', {
+ withCredentials: true,
+ });
+}
+
+/**
+ * 获取用户权限码
+ */
+export async function getAccessCodesApi() {
+ return requestClient.get('/api/v1/auth/getPermissions');
+}
diff --git a/app/src/api/core/index.ts b/app/src/api/core/index.ts
new file mode 100644
index 0000000..28a5aef
--- /dev/null
+++ b/app/src/api/core/index.ts
@@ -0,0 +1,3 @@
+export * from './auth';
+export * from './menu';
+export * from './user';
diff --git a/app/src/api/core/menu.ts b/app/src/api/core/menu.ts
new file mode 100644
index 0000000..42af3ac
--- /dev/null
+++ b/app/src/api/core/menu.ts
@@ -0,0 +1,12 @@
+import type { RouteRecordStringComponent } from '@aiflowy/types';
+
+import { requestClient } from '#/api/request';
+
+/**
+ * 获取用户所有菜单
+ */
+export async function getAllMenusApi() {
+ return requestClient.get(
+ '/api/v1/sysMenu/treeV2',
+ );
+}
diff --git a/app/src/api/core/user.ts b/app/src/api/core/user.ts
new file mode 100644
index 0000000..5fa2b14
--- /dev/null
+++ b/app/src/api/core/user.ts
@@ -0,0 +1,10 @@
+import type { UserInfo } from '@aiflowy/types';
+
+import { requestClient } from '#/api/request';
+
+/**
+ * 获取用户信息
+ */
+export async function getUserInfoApi() {
+ return requestClient.get('/api/v1/sysAccount/myProfile');
+}
diff --git a/app/src/api/index.ts b/app/src/api/index.ts
new file mode 100644
index 0000000..3b785a2
--- /dev/null
+++ b/app/src/api/index.ts
@@ -0,0 +1,2 @@
+export * from './ai';
+export * from './core';
diff --git a/app/src/api/request.ts b/app/src/api/request.ts
new file mode 100644
index 0000000..f62a542
--- /dev/null
+++ b/app/src/api/request.ts
@@ -0,0 +1,221 @@
+import type { ServerSentEventMessage } from 'fetch-event-stream';
+
+/**
+ * 该文件可自行根据业务逻辑进行调整
+ */
+import type { RequestClientOptions } from '@aiflowy/request';
+
+import { useAppConfig } from '@aiflowy/hooks';
+import { preferences } from '@aiflowy/preferences';
+import {
+ authenticateResponseInterceptor,
+ defaultResponseInterceptor,
+ errorMessageResponseInterceptor,
+ RequestClient,
+} from '@aiflowy/request';
+import { useAccessStore } from '@aiflowy/stores';
+
+import { ElMessage } from 'element-plus';
+import { events } from 'fetch-event-stream';
+
+import { useAuthStore } from '#/store';
+
+import { refreshTokenApi } from './core';
+
+const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
+
+function createRequestClient(baseURL: string, options?: RequestClientOptions) {
+ const client = new RequestClient({
+ ...options,
+ baseURL,
+ });
+
+ /**
+ * 重新认证逻辑
+ */
+ async function doReAuthenticate() {
+ console.warn('Access token or refresh token is invalid or expired. ');
+ const accessStore = useAccessStore();
+ const authStore = useAuthStore();
+ accessStore.setAccessToken(null);
+ if (
+ preferences.app.loginExpiredMode === 'modal' &&
+ accessStore.isAccessChecked
+ ) {
+ accessStore.setLoginExpired(true);
+ } else {
+ await authStore.logout();
+ }
+ }
+
+ /**
+ * 刷新token逻辑
+ */
+ async function doRefreshToken() {
+ const accessStore = useAccessStore();
+ const resp = await refreshTokenApi();
+ const newToken = resp.data;
+ accessStore.setAccessToken(newToken);
+ return newToken;
+ }
+
+ function formatToken(token: null | string) {
+ return token ? `${token}` : null;
+ }
+
+ // 请求头处理
+ client.addRequestInterceptor({
+ fulfilled: async (config) => {
+ const accessStore = useAccessStore();
+
+ config.headers['aiflowy-token'] = formatToken(accessStore.accessToken);
+ config.headers['Accept-Language'] = preferences.app.locale;
+ return config;
+ },
+ });
+
+ // 处理返回的响应数据格式
+ client.addResponseInterceptor(
+ defaultResponseInterceptor({
+ codeField: 'errorCode',
+ dataField: 'data',
+ showErrorMessage: (message) => {
+ ElMessage.error(message);
+ },
+ successCode: 0,
+ }),
+ );
+
+ // token过期的处理
+ client.addResponseInterceptor(
+ authenticateResponseInterceptor({
+ client,
+ doReAuthenticate,
+ doRefreshToken,
+ enableRefreshToken: preferences.app.enableRefreshToken,
+ formatToken,
+ }),
+ );
+
+ // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
+ client.addResponseInterceptor(
+ errorMessageResponseInterceptor((msg: string, error) => {
+ // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
+ // 当前mock接口返回的错误字段是 error 或者 message
+ const responseData = error?.response?.data ?? {};
+ const errorMessage = responseData?.error ?? responseData?.message ?? '';
+ // 如果没有错误信息,则会根据状态码进行提示
+ ElMessage.error(errorMessage || msg);
+ }),
+ );
+
+ return client;
+}
+
+export const requestClient = createRequestClient(apiURL, {
+ responseReturn: 'data',
+});
+
+export const api = createRequestClient(apiURL, {
+ responseReturn: 'body',
+});
+
+export const baseRequestClient = new RequestClient({ baseURL: apiURL });
+
+export interface SseOptions {
+ onMessage?: (message: ServerSentEventMessage) => void;
+ onError?: (err: any) => void;
+ onFinished?: () => void;
+}
+export class SseClient {
+ private controller: AbortController | null = null;
+ private currentRequestId = 0;
+
+ abort(): void {
+ if (this.controller) {
+ this.controller.abort();
+ this.controller = null;
+ }
+ }
+
+ isActive(): boolean {
+ return this.controller !== null;
+ }
+
+ async post(url: string, data?: any, options?: SseOptions): Promise {
+ // 生成唯一的请求ID
+ const requestId = ++this.currentRequestId;
+ const currentRequestId = requestId;
+
+ // 如果已有请求,先取消
+ this.abort();
+
+ // 创建新的控制器
+ const controller = new AbortController();
+ this.controller = controller;
+
+ // 保存信号的引用到局部变量
+ const signal = controller.signal;
+
+ try {
+ const res = await fetch(apiURL + url, {
+ method: 'POST',
+ signal, // 使用局部变量 signal
+ headers: this.getHeaders(),
+ body: JSON.stringify(data),
+ });
+
+ if (!res.ok) {
+ const error = new Error(`HTTP ${res.status}: ${res.statusText}`);
+ options?.onError?.(error);
+ return;
+ }
+
+ // 在开始事件流之前检查是否还是同一个请求
+ if (this.currentRequestId !== currentRequestId) {
+ return;
+ }
+
+ const msgEvents = events(res, signal);
+
+ try {
+ for await (const event of msgEvents) {
+ // 每次迭代都检查是否还是同一个请求
+ if (this.currentRequestId !== currentRequestId) {
+ break;
+ }
+ options?.onMessage?.(event);
+ }
+ } catch (innerError) {
+ options?.onError?.(innerError);
+ }
+
+ // 只有在还是同一个请求的情况下才调用 onFinished
+ if (this.currentRequestId === currentRequestId) {
+ options?.onFinished?.();
+ }
+ } catch (error) {
+ if (this.currentRequestId !== currentRequestId) {
+ return;
+ }
+ console.error('SSE错误:', error);
+ options?.onError?.(error);
+ } finally {
+ // 只有当还是当前请求时才清除 controller
+ if (this.currentRequestId === currentRequestId) {
+ this.controller = null;
+ }
+ }
+ }
+
+ private getHeaders() {
+ const accessStore = useAccessStore();
+ return {
+ Accept: 'text/event-stream',
+ 'Content-Type': 'application/json',
+ 'aiflowy-token': accessStore.accessToken || '',
+ };
+ }
+}
+
+export const sseClient = new SseClient();
diff --git a/app/src/app.vue b/app/src/app.vue
new file mode 100644
index 0000000..db9459c
--- /dev/null
+++ b/app/src/app.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/app/src/assets/ai/bot/defaultBotAvatar.png b/app/src/assets/ai/bot/defaultBotAvatar.png
new file mode 100644
index 0000000..817cc26
Binary files /dev/null and b/app/src/assets/ai/bot/defaultBotAvatar.png differ
diff --git a/app/src/assets/ai/knowledge/book.png b/app/src/assets/ai/knowledge/book.png
new file mode 100644
index 0000000..6a2d8f2
Binary files /dev/null and b/app/src/assets/ai/knowledge/book.png differ
diff --git a/app/src/assets/ai/knowledge/book.svg b/app/src/assets/ai/knowledge/book.svg
new file mode 100644
index 0000000..840d42e
--- /dev/null
+++ b/app/src/assets/ai/knowledge/book.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/app/src/assets/ai/knowledge/document.svg b/app/src/assets/ai/knowledge/document.svg
new file mode 100644
index 0000000..53589b8
--- /dev/null
+++ b/app/src/assets/ai/knowledge/document.svg
@@ -0,0 +1,27 @@
+
+
\ No newline at end of file
diff --git a/app/src/assets/ai/plugin/defaultPluginIcon.png b/app/src/assets/ai/plugin/defaultPluginIcon.png
new file mode 100644
index 0000000..4f1aa84
Binary files /dev/null and b/app/src/assets/ai/plugin/defaultPluginIcon.png differ
diff --git a/app/src/assets/ai/resource/audio-icon.png b/app/src/assets/ai/resource/audio-icon.png
new file mode 100644
index 0000000..97a01b4
Binary files /dev/null and b/app/src/assets/ai/resource/audio-icon.png differ
diff --git a/app/src/assets/ai/resource/doc-icon.png b/app/src/assets/ai/resource/doc-icon.png
new file mode 100644
index 0000000..26853bb
Binary files /dev/null and b/app/src/assets/ai/resource/doc-icon.png differ
diff --git a/app/src/assets/ai/resource/other-icon.png b/app/src/assets/ai/resource/other-icon.png
new file mode 100644
index 0000000..0ae38ae
Binary files /dev/null and b/app/src/assets/ai/resource/other-icon.png differ
diff --git a/app/src/assets/ai/resource/video-icon.png b/app/src/assets/ai/resource/video-icon.png
new file mode 100644
index 0000000..43598b9
Binary files /dev/null and b/app/src/assets/ai/resource/video-icon.png differ
diff --git a/app/src/assets/ai/workflow/confirm-file.png b/app/src/assets/ai/workflow/confirm-file.png
new file mode 100644
index 0000000..fa28fb8
Binary files /dev/null and b/app/src/assets/ai/workflow/confirm-file.png differ
diff --git a/app/src/assets/ai/workflow/confirm-icon.png b/app/src/assets/ai/workflow/confirm-icon.png
new file mode 100644
index 0000000..985da1e
Binary files /dev/null and b/app/src/assets/ai/workflow/confirm-icon.png differ
diff --git a/app/src/assets/ai/workflow/confirm-other.png b/app/src/assets/ai/workflow/confirm-other.png
new file mode 100644
index 0000000..b3e00c5
Binary files /dev/null and b/app/src/assets/ai/workflow/confirm-other.png differ
diff --git a/app/src/assets/ai/workflow/fileIcon.png b/app/src/assets/ai/workflow/fileIcon.png
new file mode 100644
index 0000000..adde504
Binary files /dev/null and b/app/src/assets/ai/workflow/fileIcon.png differ
diff --git a/app/src/assets/ai/workflow/workflowIcon.png b/app/src/assets/ai/workflow/workflowIcon.png
new file mode 100644
index 0000000..69d3c8b
Binary files /dev/null and b/app/src/assets/ai/workflow/workflowIcon.png differ
diff --git a/app/src/assets/datacenter/table2x.png b/app/src/assets/datacenter/table2x.png
new file mode 100644
index 0000000..7e41bbe
Binary files /dev/null and b/app/src/assets/datacenter/table2x.png differ
diff --git a/app/src/assets/datacenter/upload.png b/app/src/assets/datacenter/upload.png
new file mode 100644
index 0000000..2fdfe76
Binary files /dev/null and b/app/src/assets/datacenter/upload.png differ
diff --git a/app/src/assets/defaultUserAvatar.png b/app/src/assets/defaultUserAvatar.png
new file mode 100644
index 0000000..207dc70
Binary files /dev/null and b/app/src/assets/defaultUserAvatar.png differ
diff --git a/app/src/assets/login/dingding-60.png b/app/src/assets/login/dingding-60.png
new file mode 100644
index 0000000..3b86c17
Binary files /dev/null and b/app/src/assets/login/dingding-60.png differ
diff --git a/app/src/assets/login/wx-60.png b/app/src/assets/login/wx-60.png
new file mode 100644
index 0000000..52383ef
Binary files /dev/null and b/app/src/assets/login/wx-60.png differ
diff --git a/app/src/bootstrap.ts b/app/src/bootstrap.ts
new file mode 100644
index 0000000..366e088
--- /dev/null
+++ b/app/src/bootstrap.ts
@@ -0,0 +1,90 @@
+import { createApp, watchEffect } from 'vue';
+import {
+ BubbleList,
+ Conversations,
+ Sender,
+ XMarkdown,
+} from 'vue-element-plus-x';
+
+import { registerAccessDirective } from '@aiflowy/access';
+import { registerLoadingDirective } from '@aiflowy/common-ui';
+import { preferences } from '@aiflowy/preferences';
+import { initStores } from '@aiflowy/stores';
+import '@aiflowy/styles';
+import '@aiflowy/styles/ele';
+
+import { useTitle } from '@vueuse/core';
+import { ElLoading } from 'element-plus';
+
+import { $t, setupI18n } from '#/locales';
+
+import { initComponentAdapter } from './adapter/component';
+import { initSetupAIFlowyForm } from './adapter/form';
+import App from './app.vue';
+import { router } from './router';
+
+async function bootstrap(namespace: string) {
+ // 初始化组件适配器
+ await initComponentAdapter();
+
+ // 初始化表单组件
+ await initSetupAIFlowyForm();
+
+ // // 设置弹窗的默认配置
+ // setDefaultModalProps({
+ // fullscreenButton: false,
+ // });
+ // // 设置抽屉的默认配置
+ // setDefaultDrawerProps({
+ // zIndex: 2000,
+ // });
+ const app = createApp(App);
+
+ // 注册Element Plus提供的v-loading指令
+ app.directive('loading', ElLoading.directive);
+
+ app.component('ElBubbleList', BubbleList);
+ app.component('ElConversations', Conversations);
+ app.component('ElSender', Sender);
+ app.component('XMarkdown', XMarkdown);
+
+ // 注册AIFlowy提供的v-loading和v-spinning指令
+ registerLoadingDirective(app, {
+ loading: false, // AIFlowy提供的v-loading指令和Element Plus提供的v-loading指令二选一即可,此处false表示不注册AIFlowy提供的v-loading指令
+ spinning: 'spinning',
+ });
+
+ // 国际化 i18n 配置
+ await setupI18n(app);
+
+ // 配置 pinia-tore
+ await initStores(app, { namespace });
+
+ // 安装权限指令
+ registerAccessDirective(app);
+
+ // 初始化 tippy
+ const { initTippy } = await import('@aiflowy/common-ui/es/tippy');
+ initTippy(app);
+
+ // 配置路由及路由守卫
+ app.use(router);
+
+ // 配置Motion插件
+ const { MotionPlugin } = await import('@aiflowy/plugins/motion');
+ app.use(MotionPlugin);
+
+ // 动态更新标题
+ watchEffect(() => {
+ if (preferences.app.dynamicTitle) {
+ const routeTitle = router.currentRoute.value.meta?.title;
+ const pageTitle =
+ (routeTitle ? `${$t(routeTitle)} - ` : '') + preferences.app.name;
+ useTitle(pageTitle);
+ }
+ });
+
+ app.mount('#app');
+}
+
+export { bootstrap };
diff --git a/app/src/components/botAvatar/botAvatar.vue b/app/src/components/botAvatar/botAvatar.vue
new file mode 100644
index 0000000..8b6bc90
--- /dev/null
+++ b/app/src/components/botAvatar/botAvatar.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/app/src/components/cardPage/CardPage.vue b/app/src/components/cardPage/CardPage.vue
new file mode 100644
index 0000000..3621443
--- /dev/null
+++ b/app/src/components/cardPage/CardPage.vue
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ action.label }}
+
+
+
+
+ handleActionClick(command, item)"
+ >
+
+
+
+
+
+
+ {{ action.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/categoryPanel/CategoryCrudPanel.vue b/app/src/components/categoryPanel/CategoryCrudPanel.vue
new file mode 100644
index 0000000..21d0dd1
--- /dev/null
+++ b/app/src/components/categoryPanel/CategoryCrudPanel.vue
@@ -0,0 +1,325 @@
+
+
+
+
+
+
+
+ {{ $t('button.add') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('button.cancel') }}
+
+
+ {{ $t('button.confirm') }}
+
+
+
+
+
+
+
diff --git a/app/src/components/categoryPanel/CategoryPanel.vue b/app/src/components/categoryPanel/CategoryPanel.vue
new file mode 100644
index 0000000..4a32d23
--- /dev/null
+++ b/app/src/components/categoryPanel/CategoryPanel.vue
@@ -0,0 +1,370 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+ {{ category[titleKey] }}
+
+
+
+
+
+
+
+
diff --git a/app/src/components/chat/ProblemPresupposition.vue b/app/src/components/chat/ProblemPresupposition.vue
new file mode 100644
index 0000000..0d99199
--- /dev/null
+++ b/app/src/components/chat/ProblemPresupposition.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/chat/PublishWxOfficalAccount.vue b/app/src/components/chat/PublishWxOfficalAccount.vue
new file mode 100644
index 0000000..6a336df
--- /dev/null
+++ b/app/src/components/chat/PublishWxOfficalAccount.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/chat/SenderHeader.vue b/app/src/components/chat/SenderHeader.vue
new file mode 100644
index 0000000..ac63180
--- /dev/null
+++ b/app/src/components/chat/SenderHeader.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/chat/chat.vue b/app/src/components/chat/chat.vue
new file mode 100644
index 0000000..9204473
--- /dev/null
+++ b/app/src/components/chat/chat.vue
@@ -0,0 +1,407 @@
+
+
+
+
+
+
+
+
+
+
+ {{ new Date(item.created).toLocaleString() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ bot?.title }}
+
+ {{ bot?.description }}
+
+
+
+
+
+ {{ item.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/collapse/CustomCoolapse.vue b/app/src/components/collapse/CustomCoolapse.vue
new file mode 100644
index 0000000..0ff32f2
--- /dev/null
+++ b/app/src/components/collapse/CustomCoolapse.vue
@@ -0,0 +1,309 @@
+
+
+
+
+
Vue3 折叠面板
+
使用 Vue3 Composition API 实现
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/collapseViewItem/CollapseViewItem.vue b/app/src/components/collapseViewItem/CollapseViewItem.vue
new file mode 100644
index 0000000..e38b664
--- /dev/null
+++ b/app/src/components/collapseViewItem/CollapseViewItem.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
{{ item[titleKey] }}
+
{{ item[descriptionKey] }}
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/commonSelectModal/CommonSelectDataModal.vue b/app/src/components/commonSelectModal/CommonSelectDataModal.vue
new file mode 100644
index 0000000..725c1e0
--- /dev/null
+++ b/app/src/components/commonSelectModal/CommonSelectDataModal.vue
@@ -0,0 +1,471 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item[titleKey] }}
+
+
{{ item.description }}
+
+
+
+
+
+
+
+
+
+
+ {{ tool.name }}
+
+
{{ tool.description }}
+
+
+
+ toggleSelection(tool.id, val)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+ {{ tool.name }}
+
+
{{ tool.description }}
+
+
+
+
+ toggleSelectionMcp(
+ item.id,
+ tool.name,
+ tool.description,
+ val,
+ )
+ "
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
{{ item.description }}
+
+
+
+ toggleSelection(item.id, val)"
+ />
+
+
+
+
+
+
+
+
+
+
+ {{ $t('button.cancel') }}
+
+
+ {{ $t('button.confirm') }}
+
+
+
+
+
+
diff --git a/app/src/components/cron/CronGenerator.vue b/app/src/components/cron/CronGenerator.vue
new file mode 100644
index 0000000..a0951b5
--- /dev/null
+++ b/app/src/components/cron/CronGenerator.vue
@@ -0,0 +1,281 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('cron.GenerateResult') }}
+
+
+
+ {{ $t('cron.CronExpression') }}
+
+
+ {{ $t('cron.UseThisValue') }}
+
+
+ {{ $t('cron.CheckLast5ExecutionTimes') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('cron.Last5ExecutionTimes') }}
+
+
+ {{ item }}
+
+
+
+
+
+
+
diff --git a/app/src/components/cron/CronPicker.vue b/app/src/components/cron/CronPicker.vue
new file mode 100644
index 0000000..78775f0
--- /dev/null
+++ b/app/src/components/cron/CronPicker.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+ {{ $t('cron.ClickGenerate') }}
+
+
+
+
+
diff --git a/app/src/components/cron/CronTabPane.vue b/app/src/components/cron/CronTabPane.vue
new file mode 100644
index 0000000..afeb34c
--- /dev/null
+++ b/app/src/components/cron/CronTabPane.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+ {{ $t('cron.Per') }}{{ label }} (*)
+
+
+
+
+ {{ $t('cron.NotSpecified') }} (?)
+
+
+
+
+ {{ $t('cron.Cycle') }}
+ {{ $t('cron.From') }}
+ updateVal('loopStart', v)"
+ :min="min"
+ :max="max"
+ size="small"
+ controls-position="right"
+ />
+ {{ label }}{{ $t('cron.StartPer') }}
+ updateVal('loopStep', v)"
+ :min="1"
+ :max="max"
+ size="small"
+ controls-position="right"
+ />
+ {{ label }}{{ $t('cron.ExecuteOnce') }}
+
+
+
+
+ {{ $t('cron.Rang') }}
+ {{ $t('cron.From') }}
+ updateVal('rangeStart', v)"
+ :min="min"
+ :max="max"
+ size="small"
+ controls-position="right"
+ />
+ {{ $t('cron.To') }}
+ updateVal('rangeEnd', v)"
+ :min="min"
+ :max="max"
+ size="small"
+ controls-position="right"
+ />
+ {{ label }}
+
+
+
+
+ {{ $t('cron.Specify') }}
+ updateVal('specificList', v)"
+ multiple
+ :placeholder="$t('dictSelect.placeholder')"
+ style="width: 100%; min-width: 200px; margin-left: 10px"
+ size="small"
+ >
+
+
+
+
+
+
+
+
diff --git a/app/src/components/dict/DictSelect.vue b/app/src/components/dict/DictSelect.vue
new file mode 100644
index 0000000..4baa82e
--- /dev/null
+++ b/app/src/components/dict/DictSelect.vue
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+ {{ dictCode }}
+
+
+
+
+
diff --git a/app/src/components/headerSearch/HeaderSearch.vue b/app/src/components/headerSearch/HeaderSearch.vue
new file mode 100644
index 0000000..c4e5e56
--- /dev/null
+++ b/app/src/components/headerSearch/HeaderSearch.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/CategorizeIcon.vue b/app/src/components/icons/CategorizeIcon.vue
new file mode 100644
index 0000000..9aeffdd
--- /dev/null
+++ b/app/src/components/icons/CategorizeIcon.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/DesignIcon.vue b/app/src/components/icons/DesignIcon.vue
new file mode 100644
index 0000000..c5052ae
--- /dev/null
+++ b/app/src/components/icons/DesignIcon.vue
@@ -0,0 +1,18 @@
+
+
+
diff --git a/app/src/components/icons/EditIcon.vue b/app/src/components/icons/EditIcon.vue
new file mode 100644
index 0000000..92141a8
--- /dev/null
+++ b/app/src/components/icons/EditIcon.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/MagicStaffIcon.vue b/app/src/components/icons/MagicStaffIcon.vue
new file mode 100644
index 0000000..318bf7b
--- /dev/null
+++ b/app/src/components/icons/MagicStaffIcon.vue
@@ -0,0 +1,55 @@
+
+
+
diff --git a/app/src/components/icons/ManageIcon.vue b/app/src/components/icons/ManageIcon.vue
new file mode 100644
index 0000000..866f6e4
--- /dev/null
+++ b/app/src/components/icons/ManageIcon.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/PluginIcon.vue b/app/src/components/icons/PluginIcon.vue
new file mode 100644
index 0000000..0e8497e
--- /dev/null
+++ b/app/src/components/icons/PluginIcon.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/PluginToolIcon.vue b/app/src/components/icons/PluginToolIcon.vue
new file mode 100644
index 0000000..dcc0e42
--- /dev/null
+++ b/app/src/components/icons/PluginToolIcon.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/app/src/components/icons/RecordingIcon.vue b/app/src/components/icons/RecordingIcon.vue
new file mode 100644
index 0000000..6165ebd
--- /dev/null
+++ b/app/src/components/icons/RecordingIcon.vue
@@ -0,0 +1,129 @@
+
+
+
diff --git a/app/src/components/icons/SendEnableIcon.vue b/app/src/components/icons/SendEnableIcon.vue
new file mode 100644
index 0000000..bfa1d10
--- /dev/null
+++ b/app/src/components/icons/SendEnableIcon.vue
@@ -0,0 +1,37 @@
+
+
+
diff --git a/app/src/components/icons/SendIcon.vue b/app/src/components/icons/SendIcon.vue
new file mode 100644
index 0000000..a400927
--- /dev/null
+++ b/app/src/components/icons/SendIcon.vue
@@ -0,0 +1,38 @@
+
+
+
diff --git a/app/src/components/icons/SendingIcon.vue b/app/src/components/icons/SendingIcon.vue
new file mode 100644
index 0000000..d29473b
--- /dev/null
+++ b/app/src/components/icons/SendingIcon.vue
@@ -0,0 +1,47 @@
+
+
+
diff --git a/app/src/components/json/ShowJson.vue b/app/src/components/json/ShowJson.vue
new file mode 100644
index 0000000..193fcf5
--- /dev/null
+++ b/app/src/components/json/ShowJson.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/page/CardList.vue b/app/src/components/page/CardList.vue
new file mode 100644
index 0000000..4e7cea8
--- /dev/null
+++ b/app/src/components/page/CardList.vue
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+ {{ item[titleField] }}
+
+
+
+ {{ item[titleField] }}
+
+
+
+
+
+
+ {{ action.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ action.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/page/PageData.vue b/app/src/components/page/PageData.vue
new file mode 100644
index 0000000..a1cc74f
--- /dev/null
+++ b/app/src/components/page/PageData.vue
@@ -0,0 +1,129 @@
+
+
+
+
+
diff --git a/app/src/components/page/PageSide.vue b/app/src/components/page/PageSide.vue
new file mode 100644
index 0000000..dc193ef
--- /dev/null
+++ b/app/src/components/page/PageSide.vue
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+ {{ item[labelKey] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ btn.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ footerButton.label }}
+
+
+
+
+
diff --git a/app/src/components/tag/Tag.vue b/app/src/components/tag/Tag.vue
new file mode 100644
index 0000000..85a6cf3
--- /dev/null
+++ b/app/src/components/tag/Tag.vue
@@ -0,0 +1,226 @@
+
+
+
+
+
+ {{ text }}
+
+
+
+ ×
+
+
+
+
+
diff --git a/app/src/components/tree/Tree.vue b/app/src/components/tree/Tree.vue
new file mode 100644
index 0000000..61556c3
--- /dev/null
+++ b/app/src/components/tree/Tree.vue
@@ -0,0 +1,262 @@
+
+
+
+
+
+
+
+
+
+ {{ $t(node.label) }}
+
+ ({{ data[defaultProps.children].length }})
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/upload/Cropper.vue b/app/src/components/upload/Cropper.vue
new file mode 100644
index 0000000..0dae046
--- /dev/null
+++ b/app/src/components/upload/Cropper.vue
@@ -0,0 +1,461 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/upload/CropperMulti.vue b/app/src/components/upload/CropperMulti.vue
new file mode 100644
index 0000000..8afe5af
--- /dev/null
+++ b/app/src/components/upload/CropperMulti.vue
@@ -0,0 +1,603 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ file.uploading
+ ? $t('cropper.Uploading')
+ : $t('cropper.Re-upload')
+ }}
+
+
+
+ {{ $t('button.delete') }}
+
+
+
+ {{ file.name }}
+
+
+
+
+
+
+
+
+ {{ $t('cropper.ClickToUpload') }}
+
+ {{ $t('cropper.message.fileCount', { count: maxCount }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/components/upload/DragFileUpload.vue b/app/src/components/upload/DragFileUpload.vue
new file mode 100644
index 0000000..3c042dc
--- /dev/null
+++ b/app/src/components/upload/DragFileUpload.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+ {{ $t('message.upload.title') }}
+ {{
+ $t('message.upload.description')
+ }}
+
+
+
diff --git a/app/src/components/upload/Upload.vue b/app/src/components/upload/Upload.vue
new file mode 100644
index 0000000..b6d889c
--- /dev/null
+++ b/app/src/components/upload/Upload.vue
@@ -0,0 +1,72 @@
+
+
+
+
+ {{ $t('button.upload') }}
+
+
diff --git a/app/src/components/upload/UploadAvatar.vue b/app/src/components/upload/UploadAvatar.vue
new file mode 100644
index 0000000..22719a1
--- /dev/null
+++ b/app/src/components/upload/UploadAvatar.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/layouts/auth.vue b/app/src/layouts/auth.vue
new file mode 100644
index 0000000..ed889f8
--- /dev/null
+++ b/app/src/layouts/auth.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/layouts/basic.vue b/app/src/layouts/basic.vue
new file mode 100644
index 0000000..35ee1e8
--- /dev/null
+++ b/app/src/layouts/basic.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/layouts/index.ts b/app/src/layouts/index.ts
new file mode 100644
index 0000000..53bd2b7
--- /dev/null
+++ b/app/src/layouts/index.ts
@@ -0,0 +1,6 @@
+const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
+
+const IFrameView = () => import('@aiflowy/layouts').then((m) => m.IFrameView);
+
+export { AuthPageLayout, BasicLayout, IFrameView };
diff --git a/app/src/locales/README.md b/app/src/locales/README.md
new file mode 100644
index 0000000..7b45103
--- /dev/null
+++ b/app/src/locales/README.md
@@ -0,0 +1,3 @@
+# locale
+
+每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。
diff --git a/app/src/locales/index.ts b/app/src/locales/index.ts
new file mode 100644
index 0000000..551dff8
--- /dev/null
+++ b/app/src/locales/index.ts
@@ -0,0 +1,102 @@
+import type { Language } from 'element-plus/es/locale';
+
+import type { App } from 'vue';
+
+import type { LocaleSetupOptions, SupportedLanguagesType } from '@aiflowy/locales';
+
+import { ref } from 'vue';
+
+import {
+ $t,
+ setupI18n as coreSetup,
+ loadLocalesMapFromDir,
+} from '@aiflowy/locales';
+import { preferences } from '@aiflowy/preferences';
+
+import dayjs from 'dayjs';
+import enLocale from 'element-plus/es/locale/lang/en';
+import defaultLocale from 'element-plus/es/locale/lang/zh-cn';
+
+const elementLocale = ref(defaultLocale);
+
+const modules = import.meta.glob('./langs/**/*.json');
+
+const localesMap = loadLocalesMapFromDir(
+ /\.\/langs\/([^/]+)\/(.*)\.json$/,
+ modules,
+);
+/**
+ * 加载应用特有的语言包
+ * 这里也可以改造为从服务端获取翻译数据
+ * @param lang
+ */
+async function loadMessages(lang: SupportedLanguagesType) {
+ const [appLocaleMessages] = await Promise.all([
+ localesMap[lang]?.(),
+ loadThirdPartyMessage(lang),
+ ]);
+ return appLocaleMessages?.default;
+}
+
+/**
+ * 加载第三方组件库的语言包
+ * @param lang
+ */
+async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
+ await Promise.all([loadElementLocale(lang), loadDayjsLocale(lang)]);
+}
+
+/**
+ * 加载dayjs的语言包
+ * @param lang
+ */
+async function loadDayjsLocale(lang: SupportedLanguagesType) {
+ let locale;
+ switch (lang) {
+ case 'en-US': {
+ locale = await import('dayjs/locale/en');
+ break;
+ }
+ case 'zh-CN': {
+ locale = await import('dayjs/locale/zh-cn');
+ break;
+ }
+ // 默认使用英语
+ default: {
+ locale = await import('dayjs/locale/en');
+ }
+ }
+ if (locale) {
+ dayjs.locale(locale);
+ } else {
+ console.error(`Failed to load dayjs locale for ${lang}`);
+ }
+}
+
+/**
+ * 加载element-plus的语言包
+ * @param lang
+ */
+async function loadElementLocale(lang: SupportedLanguagesType) {
+ switch (lang) {
+ case 'en-US': {
+ elementLocale.value = enLocale;
+ break;
+ }
+ case 'zh-CN': {
+ elementLocale.value = defaultLocale;
+ break;
+ }
+ }
+}
+
+async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
+ await coreSetup(app, {
+ defaultLocale: preferences.app.locale,
+ loadMessages,
+ missingWarn: !import.meta.env.PROD,
+ ...options,
+ });
+}
+
+export { $t, elementLocale, setupI18n };
diff --git a/app/src/locales/langs/en-US/aiResource.json b/app/src/locales/langs/en-US/aiResource.json
new file mode 100644
index 0000000..f18527a
--- /dev/null
+++ b/app/src/locales/langs/en-US/aiResource.json
@@ -0,0 +1,20 @@
+{
+ "id": "Id",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "resourceType": "ResourceType",
+ "resourceName": "ResourceName",
+ "suffix": "Suffix",
+ "resourceUrl": "ResourceUrl",
+ "origin": "Origin",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "options": "Options",
+ "isDeleted": "IsDeleted",
+ "fileSize": "FileSize",
+ "categoryId": "Category",
+ "choose": "Choose"
+}
diff --git a/app/src/locales/langs/en-US/aiWorkflow.json b/app/src/locales/langs/en-US/aiWorkflow.json
new file mode 100644
index 0000000..e3f7393
--- /dev/null
+++ b/app/src/locales/langs/en-US/aiWorkflow.json
@@ -0,0 +1,71 @@
+{
+ "id": "Id",
+ "alias": "Alias",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "title": "Title",
+ "description": "Description",
+ "icon": "Icon",
+ "content": "Content",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "englishName": "EnglishName",
+ "status": "ShowInUserCenter",
+ "categoryId": "Category",
+ "params": "Params",
+ "steps": "Steps",
+ "result": "Result",
+ "confirm": "For contents to be confirmed, please confirm first!",
+ "completed": "Chain has been completed, please start a new one.",
+ "fileContentExtraction": "FileContentExtraction",
+ "documentAddress": "DocumentAddress",
+ "parsedText": "ParsedText",
+ "resourceSync": "ResourceSync",
+ "originUrl": "OriginUrl",
+ "savedUrl": "SavedUrl",
+ "saveOptions": "SaveOptions",
+ "image": "Image",
+ "video": "Video",
+ "audio": "Audio",
+ "document": "Document",
+ "other": "Other",
+ "fileGeneration": "FileGeneration",
+ "fileSettings": "FileSettings",
+ "fileDownloadURL": "FileDownloadURL",
+ "pluginSelect": "PluginSelect",
+ "saveData": "SaveData",
+ "dataToBeSaved": "DataToBeSaved",
+ "successInsertedRecords": "SuccessInsertedRecords",
+ "dataTable": "DataTable",
+ "queryData": "QueryData",
+ "queryResult": "QueryResult",
+ "filterConditions": "FilterConditions",
+ "limit": "Limit",
+ "sqlQuery": "SQL Query",
+ "subProcess": "SubProcess",
+ "workflowSelect": "WorkflowSelect",
+ "bochaSearch": "BochaSearch",
+ "descriptions": {
+ "fileContentExtraction": "Extract text content from PDF or Word documents, etc",
+ "documentAddress": "Document URL address",
+ "parsedText": "Parsed text content",
+ "resourceSync": "Download resource files and save to system resource library",
+ "originUrl": "File origin URL",
+ "resourceType": "Please select the type of resource",
+ "fileGeneration": "Generate Word, PDF, HTML, etc. files for users to download",
+ "fileType": "Please select the type of file to generate",
+ "fileDownloadURL": "Generated file URL",
+ "plugin": "Select a predefined plugin",
+ "saveData": "Save data to data hub",
+ "dataToBeSaved": "List of data to be saved",
+ "dataTable": "Please select a data table",
+ "queryData": "Query data from the data hub",
+ "filterConditions": "For example: name='张三' and age=21 or field = {{process variable}}",
+ "sqlQuery": "Query the database via SQL",
+ "enterSQL": "Please enter the SQL statement",
+ "queryResultJson": "Query result (JSON object)",
+ "subProcess": "Select a predefined process"
+ }
+}
diff --git a/app/src/locales/langs/en-US/aiWorkflowCategory.json b/app/src/locales/langs/en-US/aiWorkflowCategory.json
new file mode 100644
index 0000000..ed18a81
--- /dev/null
+++ b/app/src/locales/langs/en-US/aiWorkflowCategory.json
@@ -0,0 +1,10 @@
+{
+ "id": "Id",
+ "categoryName": "CategoryName",
+ "sortNo": "SortNo",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "status": "Status"
+}
\ No newline at end of file
diff --git a/app/src/locales/langs/en-US/aiWorkflowExecRecord.json b/app/src/locales/langs/en-US/aiWorkflowExecRecord.json
new file mode 100644
index 0000000..a568b58
--- /dev/null
+++ b/app/src/locales/langs/en-US/aiWorkflowExecRecord.json
@@ -0,0 +1,25 @@
+{
+ "id": "Id",
+ "execKey": "ExecKey",
+ "workflowId": "WorkflowId",
+ "title": "Title",
+ "description": "Description",
+ "input": "Input",
+ "output": "Output",
+ "workflowJson": "WorkflowJson",
+ "startTime": "StartTime",
+ "endTime": "EndTime",
+ "tokens": "Tokens",
+ "status": "Status",
+ "createdKey": "CreatedKey",
+ "createdBy": "CreatedBy",
+ "errorInfo": "ErrorInfo",
+ "moduleName": "ExecuteRecords",
+ "execTime": "TimeConsuming",
+ "status1": "Running",
+ "status5": "Suspend",
+ "status10": "Error",
+ "status20": "Success",
+ "status21": "Failed",
+ "status22": "Cancel"
+}
diff --git a/app/src/locales/langs/en-US/aiWorkflowRecordStep.json b/app/src/locales/langs/en-US/aiWorkflowRecordStep.json
new file mode 100644
index 0000000..0aa8c11
--- /dev/null
+++ b/app/src/locales/langs/en-US/aiWorkflowRecordStep.json
@@ -0,0 +1,22 @@
+{
+ "id": "Id",
+ "recordId": "RecordId",
+ "execKey": "ExecKey",
+ "nodeId": "NodeId",
+ "nodeName": "NodeName",
+ "input": "Input",
+ "output": "Output",
+ "nodeData": "NodeData",
+ "startTime": "StartTime",
+ "endTime": "EndTime",
+ "tokens": "Tokens",
+ "status": "Status",
+ "errorInfo": "ErrorInfo",
+ "moduleName": "Steps",
+ "execTime": "TimeConsuming",
+ "status1": "Running",
+ "status6": "Suspend",
+ "status10": "Error",
+ "status20": "Success",
+ "status21": "Failed"
+}
diff --git a/app/src/locales/langs/en-US/bot.json b/app/src/locales/langs/en-US/bot.json
new file mode 100644
index 0000000..d1615d9
--- /dev/null
+++ b/app/src/locales/langs/en-US/bot.json
@@ -0,0 +1,26 @@
+{
+ "problemPresupposition": "ProblemPresupposition",
+ "noPermission": "NoPermission",
+ "llm": "LLM",
+ "temperature": "Temperature",
+ "maxReplyLength": "MaxReplyLength",
+ "historyCount": "HistoryCount",
+ "skill": "Skill",
+ "conversationSettings": "ConversationSettings",
+ "welcomeMessage": "WelcomeMessage",
+ "deepThinking": "DeepThinking",
+ "enableDeepThinking": "EnableDeepThinking",
+ "publish": "Publish",
+ "postToWeChatOfficialAccount": "PostToWeChatOfficialAccount",
+ "configured": "Configured",
+ "notConfigured": "NotConfigured",
+ "placeholder": {
+ "welcome": "Please enter welcome message",
+ "prompt": "You are an AI assistant. Please provide clear and accurate answers based on the user's questions.",
+ "permission": "No permission to configure the bot!"
+ },
+ "systemPrompt": "System Prompt",
+ "aiOptimization": "AI Optimization",
+ "weChatOfficialAccountConfiguration": "WeChat Official Account configuration",
+ "aiOptimizedPrompts": "AI Optimized Prompts"
+}
diff --git a/app/src/locales/langs/en-US/button.json b/app/src/locales/langs/en-US/button.json
new file mode 100644
index 0000000..6facfab
--- /dev/null
+++ b/app/src/locales/langs/en-US/button.json
@@ -0,0 +1,46 @@
+{
+ "query": "Query",
+ "reset": "Reset",
+ "add": "Add",
+ "edit": "Edit",
+ "delete": "Delete",
+ "export": "Export",
+ "import": "Import",
+ "save": "Save",
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "addLlm": "New large model added",
+ "oneClickAdd": "One-click add",
+ "newConversation": "New Conversation",
+ "start": "Start",
+ "stop": "Stop",
+ "log": "Log",
+ "back": "Back",
+ "importFile": "ImportFile",
+ "view": "View",
+ "download": "Download",
+ "upload": "Upload",
+ "preview": "Preview",
+ "nextStep": "NextStep",
+ "previousStep": "PreviousStep",
+ "addLine": "AddRecord",
+ "batchImport": "BatchImport",
+ "startImport": "StartImport",
+ "design": "Design",
+ "run": "Run",
+ "runTest": "RunTest",
+ "copy": "Copy",
+ "selectAll": "Select All",
+ "choose": "Select",
+ "setting": "Setting",
+ "create": "Create",
+ "update": "Update",
+ "oneClickOptimization": "One-click optimization",
+ "replace": "Replace",
+ "markAsRead": "MarkAsRead",
+ "markAsResolved": "MarkAsResolved",
+ "optimizing": "Optimizing",
+ "regenerate": "Regenerate",
+ "hide": "Hide",
+ "more": "Mode"
+}
diff --git a/app/src/locales/langs/en-US/common.json b/app/src/locales/langs/en-US/common.json
new file mode 100644
index 0000000..5cbb07c
--- /dev/null
+++ b/app/src/locales/langs/en-US/common.json
@@ -0,0 +1,25 @@
+{
+ "handle": "Operate",
+ "searchPlaceholder": "Please enter search content",
+ "allCategories": "All",
+ "history": "History",
+ "noDataAvailable": "No data available",
+ "isRequired": " is required",
+ "avatar": "Avatar",
+ "otherLoginType": "Other login methods",
+ "Sun": "Sun",
+ "Mon": "Mon",
+ "Tue": "Tue",
+ "Wed": "Wed",
+ "Thu": "Thu",
+ "Fri": "Fri",
+ "Sat": "Sat",
+ "Second": "Second",
+ "Min": "Min",
+ "Hour": "Hour",
+ "Day": "Day",
+ "Month": "Month",
+ "Week": "Week",
+ "yes": "Yes",
+ "no": "No"
+}
diff --git a/app/src/locales/langs/en-US/cron.json b/app/src/locales/langs/en-US/cron.json
new file mode 100644
index 0000000..cb967bb
--- /dev/null
+++ b/app/src/locales/langs/en-US/cron.json
@@ -0,0 +1,18 @@
+{
+ "cronExpressionGenerator": "CronExpressionGenerator",
+ "GenerateResult": "GenerateResult",
+ "CronExpression": "CronExpression",
+ "UseThisValue": "UseThisValue",
+ "CheckLast5ExecutionTimes": "CheckLast5ExecutionTimes",
+ "Last5ExecutionTimes": "Last5ExecutionTimes",
+ "ClickGenerate": "ClickGenerate",
+ "Per": "Per",
+ "NotSpecified": "NotSpecified",
+ "Cycle": "Cycle",
+ "From": "From",
+ "StartPer": "Start, Per",
+ "ExecuteOnce": "ExecuteOnce",
+ "Rang": "Rang",
+ "To": "To",
+ "Specify": "Specify"
+}
diff --git a/app/src/locales/langs/en-US/cropper.json b/app/src/locales/langs/en-US/cropper.json
new file mode 100644
index 0000000..142d076
--- /dev/null
+++ b/app/src/locales/langs/en-US/cropper.json
@@ -0,0 +1,20 @@
+{
+ "ImageCropping": "ImageCropping",
+ "message": {
+ "onlyImage": "Only image files can be uploaded!",
+ "imgSize": "Image size must not exceed {limit}MB!",
+ "uploadFailed": "Upload failed",
+ "notUrl": "Upload successful but no image URL returned",
+ "uploadSuccessful": "Upload successful!",
+ "reuploadSuccessful": "Re-upload successful!",
+ "notInitialized": "Cropper not initialized",
+ "cropFailed": "Crop failed, unable to get cropped image",
+ "fileCount": "Maximum {count} files allowed",
+ "avatarFormat": "Avatar must be {format} format only",
+ "avatarSize": "Avatar limit {limit} MB"
+ },
+ "Uploading": "Uploading...",
+ "ConfirmCrop": "ConfirmCrop",
+ "Re-upload": "Re-upload",
+ "ClickToUpload": "ClickToUpload"
+}
diff --git a/app/src/locales/langs/en-US/datacenterTable.json b/app/src/locales/langs/en-US/datacenterTable.json
new file mode 100644
index 0000000..fa9c56d
--- /dev/null
+++ b/app/src/locales/langs/en-US/datacenterTable.json
@@ -0,0 +1,33 @@
+{
+ "title": "DataCenter",
+ "id": "Id",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "tableName": "TableName",
+ "tableDesc": "TableDesc",
+ "actualTable": "ActualTable",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "options": "Options",
+ "fields": "Fields",
+ "fieldName": "FiledName",
+ "fieldDesc": "Description",
+ "fieldType": "Type",
+ "required": "Required",
+ "noFieldError": "At least one field is required",
+ "fieldInfoError": "Field info is not completed",
+ "nameRegx": "It can only contain lowercase letters, numbers, and underscores, and must start with a lowercase letter",
+ "structure": "TableStructure",
+ "data": "Data",
+ "uploadTitle": "Click to upload",
+ "uploadDesc": "Choose an excel file to upload, file size limit 10MB.",
+ "downloadTemplate": "Download template",
+ "importComplete": "Import is completed",
+ "totalNum": "Total Count",
+ "successNum": "Success Count",
+ "failNum": "Fail Count",
+ "failList": "Fail List"
+}
diff --git a/app/src/locales/langs/en-US/datacenterTableFields.json b/app/src/locales/langs/en-US/datacenterTableFields.json
new file mode 100644
index 0000000..a125757
--- /dev/null
+++ b/app/src/locales/langs/en-US/datacenterTableFields.json
@@ -0,0 +1,13 @@
+{
+ "id": "Id",
+ "tableId": "TableId",
+ "fieldName": "FieldName",
+ "fieldDesc": "FieldDesc",
+ "fieldType": "FieldType",
+ "required": "Required",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "options": "Options"
+}
diff --git a/app/src/locales/langs/en-US/demos.json b/app/src/locales/langs/en-US/demos.json
new file mode 100644
index 0000000..f6f01bd
--- /dev/null
+++ b/app/src/locales/langs/en-US/demos.json
@@ -0,0 +1,14 @@
+{
+ "title": "Demos",
+ "elementPlus": "Element Plus",
+ "form": "Form",
+ "aiflowy": {
+ "title": "Project",
+ "about": "About",
+ "document": "Document",
+ "antdv": "Ant Design Vue Version",
+ "naive-ui": "Naive UI Version",
+ "element-plus": "Element Plus Version",
+ "tdesign": "TDesign Vue Version"
+ }
+}
diff --git a/app/src/locales/langs/en-US/dictSelect.json b/app/src/locales/langs/en-US/dictSelect.json
new file mode 100644
index 0000000..db64bc8
--- /dev/null
+++ b/app/src/locales/langs/en-US/dictSelect.json
@@ -0,0 +1,4 @@
+{
+ "placeholder": "Please select",
+ "getError": "Get Data Error!"
+}
diff --git a/app/src/locales/langs/en-US/documentCollection.json b/app/src/locales/langs/en-US/documentCollection.json
new file mode 100644
index 0000000..bcbc238
--- /dev/null
+++ b/app/src/locales/langs/en-US/documentCollection.json
@@ -0,0 +1,81 @@
+{
+ "id": "Id",
+ "alias": "Alias",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "icon": "Icon",
+ "title": "Title",
+ "description": "Description",
+ "slug": "Slug",
+ "vectorStoreEnable": "VectorStoreEnable",
+ "vectorStoreType": "VectorStoreType",
+ "vectorStoreCollection": "VectorStoreCollection",
+ "vectorStoreConfig": "VectorStoreConfig",
+ "vectorEmbedLlmId": "VectorEmbedLlm",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "options": "Options",
+ "rerankLlmId": "RerankLlm",
+ "searchEngineEnable": "SearchEngineEnable",
+ "englishName": "EnglishName",
+ "documentType": "DocumentType",
+ "fileName": "fileName",
+ "knowledgeCount": "Number of knowledge items",
+ "createdModifyTime": "Creation/update time",
+ "documentList": "documentList",
+ "knowledgeRetrieval": "knowledgeRetrieval",
+ "sorting": "Sorting",
+ "content": "Content",
+ "placeholder": {
+ "title": "Please input title",
+ "description": "Please provide a description so that the large model can better understand the knowledge base and make calls",
+ "englishName": "Please enter an English name",
+ "alias": "Please enter an alias, Chinese is not allowed",
+ "embedLlm": "Please choose a vector model",
+ "rerankLlm": "Please choose to rearrange the model",
+ "vectorStoreCollection": "Can only contain letters, numbers, and underscores with a length between 3-20 characters",
+ "vectorStoreType": "Please select the vector database type"
+ },
+ "importDoc": {
+ "fileUpload": "File upload",
+ "parameterSettings": "ParameterSettings",
+ "segmentedPreview": "SegmentedPreview",
+ "confirmImport": "ConfirmImport",
+ "fileName": "File Name",
+ "progressUpload": "Progress of file upload",
+ "fileSize": "File size"
+ },
+ "splitterDoc": {
+ "fileType": "FileType",
+ "splitterName": "Segmenter",
+ "chunkSize": "SegmentLength",
+ "overlapSize": "SegmentOverlap",
+ "regex": "RegularExpression",
+ "document": "Document",
+ "simpleDocumentSplitter": "SimpleDocumentSplitter",
+ "simpleTokenizeSplitter": "SimpleTokenizeSplitter",
+ "regexDocumentSplitter": "RegexDocumentSplitter",
+ "uploadStatus": "UploadStatus",
+ "pendingUpload": "PendingUpload",
+ "completed": "Completed",
+ "uploading": "Parsing in progress",
+ "importSuccess": "ImportSuccess"
+ },
+ "documentManagement": "Document management",
+ "actions": {
+ "knowledge": "Knowledge",
+ "retrieve": "Retrieve",
+ "addKnowledge": "AddKnowledge",
+ "confirmImport": "ConfirmImport",
+ "cancelImport": "CancelImport"
+ },
+ "searchResults": "SearchResults",
+ "documentPreview": "DocumentPreview",
+ "total": "Total",
+ "segments": "Segments",
+ "similarityScore": "SimilarityScore",
+ "alibabaCloud": "AlibabaCloud",
+ "tencentCloud": "tencentCloud"
+}
diff --git a/app/src/locales/langs/en-US/headerSearch.json b/app/src/locales/langs/en-US/headerSearch.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/app/src/locales/langs/en-US/headerSearch.json
@@ -0,0 +1 @@
+{}
diff --git a/app/src/locales/langs/en-US/llm.json b/app/src/locales/langs/en-US/llm.json
new file mode 100644
index 0000000..41709b1
--- /dev/null
+++ b/app/src/locales/langs/en-US/llm.json
@@ -0,0 +1,79 @@
+{
+ "filed":
+ {
+ "id": "Id",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "title": "Title",
+ "brand": "Brand",
+ "icon": "Icon",
+ "description": "Description",
+ "supportChat": "SupportChat",
+ "supportFunctionCalling": "SupportFunctionCalling",
+ "supportEmbed": "SupportEmbed",
+ "supportReranker": "SupportReranker",
+ "supportTextToImage": "SupportTextToImage",
+ "supportImageToImage": "SupportImageToImage",
+ "supportTextToAudio": "SupportTextToAudio",
+ "supportAudioToAudio": "SupportAudioToAudio",
+ "supportTextToVideo": "SupportTextToVideo",
+ "supportImageToVideo": "SupportImageToVideo",
+ "multimodal": "multimodal",
+ "llmEndpoint": "LlmEndpoint",
+ "chatPath": "ChatPath",
+ "embedPath": "embedPath",
+ "llmModel": "LlmModel",
+ "llmApiKey": "apiKey",
+ "llmExtraConfig": "LlmExtraConfig",
+ "options": "Options",
+ "ability": "Ability"
+ }
+ ,
+ "llmModal": {
+ "TitleRequired": "Please enter the name",
+ "BrandRequired": "Please enter the brand",
+ "ModelRequired": "Please enter the model",
+ "ApiKeyRequired": "Please enter the apiKey",
+ "QuickAddLlm": "One-click addition of large models"
+ },
+ "placeholder": {
+ "title": "Please enter the title",
+ "brand": "Please enter the brand",
+ "llmModel": "Please enter the llmModel",
+ "description": "Please enter the description"
+ },
+ "actions": {
+ "verifyConfiguration": "Verify Configuration"
+ },
+ "message": {
+ "verifySuccess": "Verification successful"
+ },
+ "addProvider": "Provider list",
+ "modelType": "ModelType",
+ "llmModel": "ModelName",
+ "title": "name",
+ "groupName": "GroupName",
+ "provider": "供应商",
+ "ability": "ModelAbility",
+ "button": {
+ "management": "Management",
+ "test": "Test",
+ "addAllLlm": "Add models from the list",
+ "RetrieveAgain": "Retrieve the model list again"
+ },
+ "all": "All",
+ "verifyLlmTitle": "Verify Large Model",
+ "searchTextPlaceholder": "Search for model name or name",
+ "testSuccess": "TestSuccess",
+ "modelAbility": {
+ "supportThinking": "Thinking",
+ "supportTool": "Tool",
+ "SupportAudio": "Audio",
+ "SupportVideo": "Video",
+ "SupportImage": "Image",
+ "supportFree": "Free",
+ "supportImageB64Only": "ImageB64Only",
+ "supportToolMessage": "SupportToolMessage"
+ },
+ "requestPath": "RequestPath"
+}
diff --git a/app/src/locales/langs/en-US/llmProvider.json b/app/src/locales/langs/en-US/llmProvider.json
new file mode 100644
index 0000000..b0e9d6f
--- /dev/null
+++ b/app/src/locales/langs/en-US/llmProvider.json
@@ -0,0 +1,15 @@
+{
+ "providerName": "ProviderName",
+ "provider": "Provider",
+ "icon": "Icon",
+ "apiKey": "ApiKey",
+ "endpoint": "APIAddress",
+ "embedPath": "EmbedPath",
+ "chatPath": "ChatPath",
+ "rerankPath": "RerankPath",
+ "embeddingModel": "EmbeddingModel",
+ "chatModel": "ChatModel",
+ "rerankModel": "RerankModel",
+ "model": "Model",
+ "apiType": "ApiType"
+}
diff --git a/app/src/locales/langs/en-US/mcp.json b/app/src/locales/langs/en-US/mcp.json
new file mode 100644
index 0000000..01c823f
--- /dev/null
+++ b/app/src/locales/langs/en-US/mcp.json
@@ -0,0 +1,20 @@
+{
+ "id": "Id",
+ "title": "Title",
+ "description": "Description",
+ "configJson": "ConfigJson",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "status": "Status",
+ "modal": {
+ "config": "Config",
+ "tool": "Tool",
+ "table": {
+ "availableTools": "Available Tools"
+ }
+ }
+}
diff --git a/app/src/locales/langs/en-US/menus.json b/app/src/locales/langs/en-US/menus.json
new file mode 100644
index 0000000..342b95d
--- /dev/null
+++ b/app/src/locales/langs/en-US/menus.json
@@ -0,0 +1,33 @@
+{
+ "system": {
+ "title": "System",
+ "sysAccount": "Account",
+ "sysRole": "Role",
+ "sysMenu": "Menu",
+ "sysDept": "Department",
+ "sysPosition": "Position",
+ "sysDict": "Dictionary",
+ "sysJob": "Job",
+ "sysLog": "Log",
+ "sysFeedback": "UserFeedback",
+ "sysAppearance": "Appearance",
+ "oauth": "OAuth"
+ },
+ "ai": {
+ "bots": "ChatAssistant",
+ "title": "AI",
+ "resources": "Resources",
+ "datacenter": "Datacenter",
+ "workflow": "Workflow",
+ "plugin": "Plugin",
+ "model": "Model",
+ "documentCollection": "DocumentCollection",
+ "knowledge": "DocumentManagement",
+ "mcp": "MCP"
+ },
+ "settings": {
+ "title": "SettingsConfiguration",
+ "settingsConfig": "SettingsConfig",
+ "apiKey": "API Key"
+ }
+}
diff --git a/app/src/locales/langs/en-US/message.json b/app/src/locales/langs/en-US/message.json
new file mode 100644
index 0000000..0cfb8e9
--- /dev/null
+++ b/app/src/locales/langs/en-US/message.json
@@ -0,0 +1,46 @@
+{
+ "deleteAlert": "Do you want to delete this item?",
+ "noticeTitle": "Notice",
+ "ok": "Yes",
+ "cancel": "No",
+ "required": "Item is required",
+ "saveOkMessage": "Saved successfully",
+ "saveFailMessage": "Save failed",
+ "deleteOkMessage": "Deleted successfully",
+ "deleteFailMessage": "Delete failed",
+ "loading": "Loading...",
+ "getDataError": "Get data error",
+ "updateOkMessage": "Updated successfully",
+ "startAlert": "Are you sure to start?",
+ "stopAlert": "Are you sure to stop?",
+ "preview": "Preview",
+ "notEmpty": "Can not be empty",
+ "success": "Success",
+ "fail": "Fail",
+ "pleaseSelect": "Please Select {name}",
+ "pleaseInputContent": "Please input content",
+ "notSupported": "Not supported yet",
+ "englishNameRule": "Rural subsistence allowance please enter characters consisting of letters, numbers, underscores, and hyphens, with a length not exceeding 64 characters",
+ "downloadSuccess": "Download success",
+ "downloadFail": "Download fail",
+ "copySuccess": "Copy success",
+ "copyFail": "Copy fail",
+ "upload": {
+ "title": "Click or drag and drop files here to upload",
+ "description": "TXT, PDF, DOCX, MD, PPT, PPTX, and XLSX files are supported. Each upload allows one file only, with a maximum size of 20M per file."
+ },
+ "uploadFileFirst": "Please upload the file first",
+ "deleteModelAlert": "This operation will delete the large model. Are you sure to delete it?",
+ "deleteModelGroupAlert": "This operation will delete all large models under this group. Are you sure to delete them?",
+ "cannotBeEmpty": {
+ "name": "Parameter name cannot be empty",
+ "description": "Parameter description cannot be empty",
+ "method": "Input method cannot be empty",
+ "type": "Parameter type cannot be empty",
+ "all": "Please complete all required fields before submitting",
+ "error": "Parameter validation failed. Please complete all required fields"
+ },
+ "completeForm": "Please complete the form information",
+ "notVideo": "Your browser does not support the video element.",
+ "notAudio": "Your browser does not support the audio element."
+}
diff --git a/app/src/locales/langs/en-US/page.json b/app/src/locales/langs/en-US/page.json
new file mode 100644
index 0000000..fada1d6
--- /dev/null
+++ b/app/src/locales/langs/en-US/page.json
@@ -0,0 +1,23 @@
+{
+ "auth": {
+ "login": "Login",
+ "register": "Register",
+ "codeLogin": "Code Login",
+ "qrcodeLogin": "Qr Code Login",
+ "forgetPassword": "Forget Password",
+ "profile": "Profile",
+ "accountPassword": "Account Password",
+ "systemMessage": "System Message",
+ "todoTasks": "Todo Tasks"
+ },
+ "dashboard": {
+ "title": "Dashboard",
+ "analytics": "Analytics",
+ "workspace": "Workspace"
+ },
+ "description": {
+ "accountPassword": "Messages from other users will be notified via site message",
+ "systemMessage": "System messages will be notified via site message",
+ "todoTasks": "Todo tasks will be notified via site message."
+ }
+}
diff --git a/app/src/locales/langs/en-US/plugin.json b/app/src/locales/langs/en-US/plugin.json
new file mode 100644
index 0000000..a2589e4
--- /dev/null
+++ b/app/src/locales/langs/en-US/plugin.json
@@ -0,0 +1,33 @@
+{
+ "pluginCategory": "Plugin category",
+ "id": "Id",
+ "alias": "Alias",
+ "name": "Name",
+ "description": "Description",
+ "type": "Type",
+ "baseUrl": "BaseUrl",
+ "authType": "AuthType",
+ "created": "Created",
+ "icon": "Icon",
+ "position": "Position",
+ "headers": "Headers",
+ "tokenKey": "TokenKey",
+ "tokenValue": "TokenValue",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "createdBy": "CreatedBy",
+ "category": "Category",
+ "placeholder": {
+ "name": "Please enter plugin name",
+ "description": "Please enter plugin description",
+ "categorize": "Please enter categorize"
+ },
+ "button": {
+ "addPlugin": "Add Plugin",
+ "categorize": "categorize",
+ "tools": "tools"
+ },
+ "toolsManagement": "Tools Management",
+ "searchUsers": "Search Users",
+ "parameterValue": "ParameterValue"
+}
diff --git a/app/src/locales/langs/en-US/pluginItem.json b/app/src/locales/langs/en-US/pluginItem.json
new file mode 100644
index 0000000..54dc215
--- /dev/null
+++ b/app/src/locales/langs/en-US/pluginItem.json
@@ -0,0 +1,35 @@
+{
+ "id": "Id",
+ "pluginId": "PluginId",
+ "name": "Tool name",
+ "description": "Description",
+ "basePath": "BasePath",
+ "created": "Created",
+ "status": "Status",
+ "inputData": "InputData",
+ "outputData": "OutputData",
+ "requestMethod": "RequestMethod",
+ "serviceStatus": "ServiceStatus",
+ "debugStatus": "DebugStatus",
+ "englishName": "EnglishName",
+ "createPluginTool": "Create tool",
+ "pluginToolEdit": {
+ "basicInfo": "Basic Info",
+ "configureInputParameters": "Configure input parameters",
+ "configureOutputParameters": "Configure output parameters",
+ "trialRun": "Trial run",
+ "toolPath": "Tool path",
+ "requestMethod": "RequestMethod",
+ "runResult": "Run result",
+ "run": "run"
+ },
+ "parameterName": "Name",
+ "parameterDescription": "Description",
+ "parameterType": "Type",
+ "inputMethod": "InputMethod",
+ "required": "Required",
+ "defaultValue": "DefaultValue",
+ "enabledStatus": "EnabledStatus",
+ "addChildNode": "AddChildNode",
+ "addParameter": "Add Parameter"
+}
diff --git a/app/src/locales/langs/en-US/settingsConfig.json b/app/src/locales/langs/en-US/settingsConfig.json
new file mode 100644
index 0000000..171e521
--- /dev/null
+++ b/app/src/locales/langs/en-US/settingsConfig.json
@@ -0,0 +1,10 @@
+{
+ "title": "Large Model Configuration",
+ "modelOfChat": "Chat Model Provider",
+ "dialogModel": "Chat Model Settings",
+ "modelName": "Model Name",
+ "basic": "BasicInformation",
+ "updatePwd": "UpdatePassword",
+ "systemAIFunctionSettings": "System AI Function Settings",
+ "note": "Note: This config only applies to system AI features, not [Chat Assistant]."
+}
diff --git a/app/src/locales/langs/en-US/sysAccount.json b/app/src/locales/langs/en-US/sysAccount.json
new file mode 100644
index 0000000..e7796dc
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysAccount.json
@@ -0,0 +1,27 @@
+{
+ "id": "Id",
+ "deptId": "Dept",
+ "tenantId": "TenantId",
+ "loginName": "LoginName",
+ "password": "Password",
+ "accountType": "AccountType",
+ "nickname": "Nickname",
+ "mobile": "Mobile",
+ "email": "Email",
+ "avatar": "Avatar",
+ "dataScope": "DataScope",
+ "deptIdList": "DeptIdList",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "remark": "Remark",
+ "isDeleted": "IsDeleted",
+ "roleIds": "Role",
+ "oldPwd": "OldPassword",
+ "newPwd": "NewPassword",
+ "confirmPwd": "ConfirmPassword",
+ "repeatPwd": "Please confirm your password again",
+ "notSamePwd": "The two passwords are inconsistent"
+}
diff --git a/app/src/locales/langs/en-US/sysApiKey.json b/app/src/locales/langs/en-US/sysApiKey.json
new file mode 100644
index 0000000..19cc1ec
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysApiKey.json
@@ -0,0 +1,18 @@
+{
+ "id": "Id",
+ "apiKey": "ApiKey",
+ "created": "Created",
+ "status": "Status",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "expiredAt": "ExpiredAt",
+ "createdBy": "CreatedBy",
+ "addApiKey": "addApiKey",
+ "actions": {
+ "enable": "Enable",
+ "disable": "NotDisable",
+ "failure": "Failure"
+ },
+ "permissions": "AuthInterface",
+ "addApiKeyNotice": "This operation will generate an API key. Please confirm whether to proceed"
+}
diff --git a/app/src/locales/langs/en-US/sysApiKeyResourcePermission.json b/app/src/locales/langs/en-US/sysApiKeyResourcePermission.json
new file mode 100644
index 0000000..484e442
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysApiKeyResourcePermission.json
@@ -0,0 +1,6 @@
+{
+ "id": "Id",
+ "requestInterface": "RequestInterface",
+ "title": "Title",
+ "addPermission": "AddRequestInterface"
+}
diff --git a/app/src/locales/langs/en-US/sysAppearance.json b/app/src/locales/langs/en-US/sysAppearance.json
new file mode 100644
index 0000000..bb0dae1
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysAppearance.json
@@ -0,0 +1,24 @@
+{
+ "Theme and Color Scheme": "Theme and Color Scheme",
+ "Theme Mode": "Theme Mode",
+ "Theme Color": "Theme Color",
+ "Layout & Navigation": "Layout & Navigation",
+ "Layout Mode": "Layout Mode",
+ "Interface Display": "Interface Display",
+ "Page Tabs": "Page Tabs",
+ "Animation": "Animation",
+ "Login Page Appearance": "Login Page Appearance",
+ "Login Page Layout": "Login Page Layout",
+ "Login Page Image": "Login Page Image",
+ "OnlyJPG": "Only .jpg format supported",
+ "Login Page Brand Copy": "Login Page Brand Copy",
+ "Welcome Title": "Welcome Title",
+ "Please enter the welcome title": "Please enter the welcome title",
+ "Welcome Description": "Welcome Description",
+ "Please enter the welcome description": "Please enter the welcome description",
+ "Slogan Title": "Slogan Title",
+ "Please enter the slogan title": "Please enter the slogan title",
+ "Slogan Description": "Slogan Description",
+ "Please enter the slogan description": "Please enter the slogan description",
+ "Thumbnail": "Thumbnail"
+}
diff --git a/app/src/locales/langs/en-US/sysDept.json b/app/src/locales/langs/en-US/sysDept.json
new file mode 100644
index 0000000..9f9cbd4
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysDept.json
@@ -0,0 +1,17 @@
+{
+ "root": "Root",
+ "id": "Id",
+ "tenantId": "TenantId",
+ "parentId": "Parent",
+ "ancestors": "Ancestors",
+ "deptName": "DeptName",
+ "deptCode": "DeptCode",
+ "sortNo": "SortNo",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "remark": "Remark",
+ "isDeleted": "IsDeleted"
+}
diff --git a/app/src/locales/langs/en-US/sysDict.json b/app/src/locales/langs/en-US/sysDict.json
new file mode 100644
index 0000000..4d39378
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysDict.json
@@ -0,0 +1,12 @@
+{
+ "id": "Id",
+ "name": "Name",
+ "code": "Code",
+ "description": "Description",
+ "dictType": "DictType",
+ "sortNo": "SortNo",
+ "status": "Status",
+ "options": "Options",
+ "created": "Created",
+ "modified": "Modified"
+}
diff --git a/app/src/locales/langs/en-US/sysFeedback.json b/app/src/locales/langs/en-US/sysFeedback.json
new file mode 100644
index 0000000..7905c58
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysFeedback.json
@@ -0,0 +1,20 @@
+{
+ "feedbackType": "FeedbackType",
+ "processingStatus": "ProcessingStatus",
+ "category": "Category",
+ "description": "Description",
+ "contactInformation": "ContactInformation",
+ "submittedAt": "SubmittedAt",
+ "functionalFailure": "FunctionalFailure",
+ "optimizationSuggestions": "OptimizationSuggestions",
+ "accountIssue": "AccountIssue",
+ "other": "Other",
+ "notViewed": "NotViewed",
+ "viewed": "Viewed",
+ "processed": "Processed",
+ "closed/Invalid": "Closed/Invalid",
+ "markedSuccessfully": "Marked Successfully!",
+ "basicInformation": "BasicInformation",
+ "feedbackContent": "FeedbackContent",
+ "attachments": "Attachments"
+}
diff --git a/app/src/locales/langs/en-US/sysJob.json b/app/src/locales/langs/en-US/sysJob.json
new file mode 100644
index 0000000..35437af
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysJob.json
@@ -0,0 +1,23 @@
+{
+ "id": "Id",
+ "deptId": "DeptId",
+ "tenantId": "TenantId",
+ "jobName": "JobName",
+ "jobType": "JobType",
+ "jobParams": "JobParams",
+ "cronExpression": "CronExpression",
+ "allowConcurrent": "AllowConcurrent",
+ "misfirePolicy": "MisfirePolicy",
+ "options": "Options",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "remark": "Remark",
+ "isDeleted": "IsDeleted",
+ "workflow": "Workflow",
+ "beanMethod": "BeanMethod",
+ "javaMethod": "JavaMethod",
+ "example": "example"
+}
diff --git a/app/src/locales/langs/en-US/sysJobLog.json b/app/src/locales/langs/en-US/sysJobLog.json
new file mode 100644
index 0000000..521ac9b
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysJobLog.json
@@ -0,0 +1,14 @@
+{
+ "title": "JobLog",
+ "id": "Id",
+ "jobId": "JobId",
+ "jobName": "JobName",
+ "jobParams": "JobParams",
+ "jobResult": "JobResult",
+ "errorInfo": "ErrorInfo",
+ "status": "Status",
+ "startTime": "StartTime",
+ "endTime": "EndTime",
+ "created": "Created",
+ "remark": "Remark"
+}
diff --git a/app/src/locales/langs/en-US/sysLog.json b/app/src/locales/langs/en-US/sysLog.json
new file mode 100644
index 0000000..651d7be
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysLog.json
@@ -0,0 +1,14 @@
+{
+ "id": "Id",
+ "accountId": "AccountId",
+ "actionName": "ActionName",
+ "actionType": "ActionType",
+ "actionClass": "ActionClass",
+ "actionMethod": "ActionMethod",
+ "actionUrl": "ActionUrl",
+ "actionIp": "ActionIp",
+ "actionParams": "ActionParams",
+ "actionBody": "ActionBody",
+ "status": "Status",
+ "created": "Created"
+}
diff --git a/app/src/locales/langs/en-US/sysMenu.json b/app/src/locales/langs/en-US/sysMenu.json
new file mode 100644
index 0000000..acda776
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysMenu.json
@@ -0,0 +1,20 @@
+{
+"root":"Top",
+"id": "Id",
+"parentId": "Parent",
+"menuType": "MenuType",
+"menuTitle": "MenuTitle",
+"menuUrl": "MenuUrl",
+"component": "Component",
+"menuIcon": "MenuIcon",
+"isShow": "IsShow",
+"permissionTag": "PermissionTag",
+"sortNo": "SortNo",
+"status": "Status",
+"created": "Created",
+"createdBy": "CreatedBy",
+"modified": "Modified",
+"modifiedBy": "ModifiedBy",
+"remark": "Remark",
+"isDeleted": "IsDeleted"
+}
diff --git a/app/src/locales/langs/en-US/sysOption.json b/app/src/locales/langs/en-US/sysOption.json
new file mode 100644
index 0000000..614f41d
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysOption.json
@@ -0,0 +1,4 @@
+{
+ "oauthWxWeb": "WechatLogin",
+ "oauthDingTalk": "DingTalkLogin"
+}
diff --git a/app/src/locales/langs/en-US/sysPosition.json b/app/src/locales/langs/en-US/sysPosition.json
new file mode 100644
index 0000000..a3d2a53
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysPosition.json
@@ -0,0 +1,24 @@
+{
+ "id": "Id",
+ "tenantId": "TenantId",
+ "deptId": "Dept",
+ "positionName": "PositionName",
+ "positionCode": "PositionCode",
+ "sortNo": "SortNo",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "remark": "Remark",
+ "isDeleted": "IsDeleted",
+ "enable": "Enable",
+ "disable": "Disable",
+ "message": {
+ "title": "Confirm to {actionText} \"{positionName}\"?"
+ },
+ "placeholder": {
+ "positionName": "Please enter position name",
+ "positionCode": "Please enter position code"
+ }
+}
diff --git a/app/src/locales/langs/en-US/sysRole.json b/app/src/locales/langs/en-US/sysRole.json
new file mode 100644
index 0000000..8759257
--- /dev/null
+++ b/app/src/locales/langs/en-US/sysRole.json
@@ -0,0 +1,17 @@
+{
+ "id": "Id",
+ "tenantId": "TenantId",
+ "roleName": "RoleName",
+ "roleKey": "RoleKey",
+ "status": "Status",
+ "created": "Created",
+ "createdBy": "CreatedBy",
+ "modified": "Modified",
+ "modifiedBy": "ModifiedBy",
+ "remark": "Remark",
+ "isDeleted": "IsDeleted",
+ "menuPermission": "MenuPermission",
+ "dataPermission": "DataPermission",
+ "checkStrictlyTrue": "Linked",
+ "checkStrictlyFalse": "NotLinked"
+}
diff --git a/app/src/locales/langs/zh-CN/aiResource.json b/app/src/locales/langs/zh-CN/aiResource.json
new file mode 100644
index 0000000..999dc3e
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/aiResource.json
@@ -0,0 +1,20 @@
+{
+ "id": "主键",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "resourceType": "素材类型",
+ "resourceName": "素材名称",
+ "suffix": "后缀",
+ "resourceUrl": "素材地址",
+ "origin": "素材来源",
+ "status": "数据状态",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "options": "扩展项",
+ "isDeleted": "删除标识",
+ "fileSize": "文件大小",
+ "categoryId": "分类",
+ "choose": "选择素材"
+}
diff --git a/app/src/locales/langs/zh-CN/aiWorkflow.json b/app/src/locales/langs/zh-CN/aiWorkflow.json
new file mode 100644
index 0000000..5d3ea6b
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/aiWorkflow.json
@@ -0,0 +1,71 @@
+{
+ "id": "ID 主键",
+ "alias": "别名",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "title": "名称",
+ "description": "描述",
+ "icon": "图标",
+ "content": "工作流设计的 JSON 内容",
+ "created": "创建时间",
+ "createdBy": "创建人",
+ "modified": "最后修改时间",
+ "modifiedBy": "最后修改的人",
+ "englishName": "英文名称",
+ "status": "在用户中心显示",
+ "categoryId": "分类",
+ "params": "执行参数",
+ "steps": "执行步骤",
+ "result": "执行结果",
+ "confirm": "有待确认的内容,请先确认!",
+ "completed": "流程已执行完毕,请重新发起。",
+ "fileContentExtraction": "文件内容提取",
+ "documentAddress": "文档地址",
+ "parsedText": "解析后的文本",
+ "resourceSync": "素材同步",
+ "originUrl": "源地址",
+ "savedUrl": "保存后的地址",
+ "saveOptions": "保存选项",
+ "image": "图片",
+ "video": "视频",
+ "audio": "音频",
+ "document": "文档",
+ "other": "其他",
+ "fileGeneration": "文件生成",
+ "fileSettings": "文件设置",
+ "fileDownloadURL": "文件下载地址",
+ "pluginSelect": "插件选择",
+ "saveData": "保存数据",
+ "dataToBeSaved": "待保存的数据",
+ "successInsertedRecords": "成功插入条数",
+ "dataTable": "数据表",
+ "queryData": "查询数据",
+ "queryResult": "查询结果",
+ "filterConditions": "过滤条件",
+ "limit": "限制条数",
+ "sqlQuery": "SQL 查询",
+ "subProcess": "子流程",
+ "workflowSelect": "工作流选择",
+ "bochaSearch": "博查搜索",
+ "descriptions": {
+ "fileContentExtraction": "提取 PDF 或者 Word 等文件中的文字内容",
+ "documentAddress": "文档的url地址",
+ "parsedText": "解析后的文本内容",
+ "resourceSync": "下载素材文件并保存到系统素材库",
+ "originUrl": "文件的源地址",
+ "resourceType": "请选择素材的类型",
+ "fileGeneration": "生成 Word、PDF、HTML 等文件供用户下载",
+ "fileType": "请选择生成的文件类型",
+ "fileDownloadURL": "生成后的文件地址",
+ "plugin": "选择定义好的插件",
+ "saveData": "保存数据到数据中枢",
+ "dataToBeSaved": "待保存的数据列表",
+ "dataTable": "请选择数据表",
+ "queryData": "查询数据中枢的数据",
+ "filterConditions": "如:name='张三' and age=21 or field = {{流程变量}}",
+ "sqlQuery": "通过 SQL 查询数据库",
+ "enterSQL": "请输入SQL语句",
+ "queryResultJson": "查询结果(json对象)",
+ "subProcess": "选择定义好的流程"
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/aiWorkflowCategory.json b/app/src/locales/langs/zh-CN/aiWorkflowCategory.json
new file mode 100644
index 0000000..45ce6ba
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/aiWorkflowCategory.json
@@ -0,0 +1,10 @@
+{
+ "id": "主键",
+ "categoryName": "分类名称",
+ "sortNo": "排序",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "status": "数据状态"
+}
\ No newline at end of file
diff --git a/app/src/locales/langs/zh-CN/aiWorkflowExecRecord.json b/app/src/locales/langs/zh-CN/aiWorkflowExecRecord.json
new file mode 100644
index 0000000..25d4a6b
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/aiWorkflowExecRecord.json
@@ -0,0 +1,25 @@
+{
+ "id": "主键",
+ "execKey": "执行标识",
+ "workflowId": "工作流ID",
+ "title": "标题",
+ "description": "描述",
+ "input": "输入",
+ "output": "输出",
+ "workflowJson": "工作流执行时的配置",
+ "startTime": "开始时间",
+ "endTime": "结束时间",
+ "tokens": "消耗总token",
+ "status": "状态",
+ "createdKey": "执行人标识[有可能是用户|外部|定时任务等情况]",
+ "createdBy": "执行人",
+ "errorInfo": "错误信息",
+ "moduleName": "执行记录",
+ "execTime": "耗时",
+ "status1": "运行中",
+ "status5": "挂起",
+ "status10": "错误",
+ "status20": "成功",
+ "status21": "失败",
+ "status22": "取消"
+}
diff --git a/app/src/locales/langs/zh-CN/aiWorkflowRecordStep.json b/app/src/locales/langs/zh-CN/aiWorkflowRecordStep.json
new file mode 100644
index 0000000..a4d0f12
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/aiWorkflowRecordStep.json
@@ -0,0 +1,22 @@
+{
+ "id": "主键",
+ "recordId": "执行记录ID",
+ "execKey": "执行标识",
+ "nodeId": "节点ID",
+ "nodeName": "节点名称",
+ "input": "输入",
+ "output": "输出",
+ "nodeData": "节点信息",
+ "startTime": "开始时间",
+ "endTime": "结束时间",
+ "tokens": "消耗总token",
+ "status": "状态",
+ "errorInfo": "错误信息",
+ "moduleName": "步骤信息",
+ "execTime": "耗时",
+ "status1": "运行中",
+ "status6": "挂起",
+ "status10": "错误",
+ "status20": "成功",
+ "status21": "失败"
+}
diff --git a/app/src/locales/langs/zh-CN/bot.json b/app/src/locales/langs/zh-CN/bot.json
new file mode 100644
index 0000000..b74429c
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/bot.json
@@ -0,0 +1,26 @@
+{
+ "problemPresupposition": "问题预设",
+ "noPermission": "没有权限",
+ "llm": "大模型",
+ "temperature": "温度",
+ "maxReplyLength": "最大回复长度",
+ "historyCount": "携带历史条数",
+ "skill": "技能",
+ "conversationSettings": "对话设置",
+ "welcomeMessage": "欢迎语",
+ "deepThinking": "深度思考",
+ "enableDeepThinking": "是否启用深度思考",
+ "publish": "发布",
+ "postToWeChatOfficialAccount": "发布到微信公众号",
+ "configured": "已配置",
+ "notConfigured": "未配置",
+ "placeholder": {
+ "welcome": "请输入欢迎语",
+ "prompt": "你是一个AI助手,请根据用户的问题给出清晰、准确的回答。",
+ "permission": "你没有配置bot的权限!"
+ },
+ "systemPrompt": "系统提示词",
+ "aiOptimization": "AI优化",
+ "weChatOfficialAccountConfiguration": "微信公众号配置",
+ "aiOptimizedPrompts": "AI优化提示词"
+}
diff --git a/app/src/locales/langs/zh-CN/button.json b/app/src/locales/langs/zh-CN/button.json
new file mode 100644
index 0000000..ee092d8
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/button.json
@@ -0,0 +1,46 @@
+{
+ "query": "查询",
+ "reset": "重置",
+ "add": "添加",
+ "edit": "编辑",
+ "delete": "删除",
+ "export": "导出",
+ "import": "导入",
+ "save": "保存",
+ "cancel": "取消",
+ "confirm": "确认",
+ "addLlm": "新增大模型",
+ "oneClickAdd": "一键添加",
+ "newConversation": "新建会话",
+ "start": "启动",
+ "stop": "停止",
+ "log": "日志",
+ "back": "返回",
+ "importFile": "导入文件",
+ "view": "查看",
+ "download": "下载",
+ "upload": "上传",
+ "preview": "预览",
+ "nextStep": "下一步",
+ "previousStep": "上一步",
+ "addLine": "增加行",
+ "batchImport": "批量导入",
+ "startImport": "开始导入",
+ "design": "设计",
+ "run": "运行",
+ "runTest": "试运行",
+ "copy": "复制",
+ "selectAll": "全选",
+ "choose": "选择",
+ "setting": "设置",
+ "create": "创建",
+ "update": "更新",
+ "oneClickOptimization": "一键优化",
+ "replace": "替换",
+ "markAsRead": "标记已查看",
+ "markAsResolved": "标记已处理",
+ "optimizing": "正在优化中...",
+ "regenerate": "重新生成",
+ "hide": "隐藏",
+ "more": "更多"
+}
diff --git a/app/src/locales/langs/zh-CN/common.json b/app/src/locales/langs/zh-CN/common.json
new file mode 100644
index 0000000..552d2d9
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/common.json
@@ -0,0 +1,25 @@
+{
+ "handle": "操作",
+ "searchPlaceholder": "请输入搜索内容",
+ "allCategories": "全部",
+ "history": "历史记录",
+ "noDataAvailable": "暂无数据",
+ "isRequired": "不能为空",
+ "avatar": "头像",
+ "otherLoginType": "其他登录方式",
+ "Sun": "周日",
+ "Mon": "周一",
+ "Tue": "周二",
+ "Wed": "周三",
+ "Thu": "周四",
+ "Fri": "周五",
+ "Sat": "周六",
+ "Second": "秒",
+ "Min": "分",
+ "Hour": "时",
+ "Day": "日",
+ "Month": "月",
+ "Week": "周",
+ "yes": "是",
+ "no": "否"
+}
diff --git a/app/src/locales/langs/zh-CN/cron.json b/app/src/locales/langs/zh-CN/cron.json
new file mode 100644
index 0000000..de4c1a1
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/cron.json
@@ -0,0 +1,18 @@
+{
+ "cronExpressionGenerator": "Cron 表达式生成器",
+ "GenerateResult": "生成结果",
+ "CronExpression": "Cron 表达式",
+ "UseThisValue": "使用该值",
+ "CheckLast5ExecutionTimes": "查看最近5次执行时间",
+ "Last5ExecutionTimes": "最近5次执行时间",
+ "ClickGenerate": "点击生成",
+ "Per": "每",
+ "NotSpecified": "不指定",
+ "Cycle": "周期",
+ "From": "从",
+ "StartPer": "开始,每",
+ "ExecuteOnce": "执行一次",
+ "Rang": "区间",
+ "To": "至",
+ "Specify": "指定"
+}
diff --git a/app/src/locales/langs/zh-CN/cropper.json b/app/src/locales/langs/zh-CN/cropper.json
new file mode 100644
index 0000000..af8ccbc
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/cropper.json
@@ -0,0 +1,20 @@
+{
+ "ImageCropping": "图片裁剪",
+ "message": {
+ "onlyImage": "只能上传图片文件!",
+ "imgSize": "图片大小不能超过 {limit}MB!",
+ "uploadFailed": "上传失败",
+ "notUrl": "上传成功但未返回图片URL",
+ "uploadSuccessful": "上传成功!",
+ "reuploadSuccessful": "重新上传成功!",
+ "notInitialized": "裁剪器未初始化",
+ "cropFailed": "裁剪失败,无法获取裁剪后的图片",
+ "fileCount": "最多只能上传 {count} 个文件",
+ "avatarFormat": "头像只能是{format}格式",
+ "avatarSize": "头像限制 {limit} MB"
+ },
+ "Uploading": "上传中...",
+ "ConfirmCrop": "确认裁剪",
+ "Re-upload": "重新上传",
+ "ClickToUpload": "点击上传"
+}
diff --git a/app/src/locales/langs/zh-CN/datacenterTable.json b/app/src/locales/langs/zh-CN/datacenterTable.json
new file mode 100644
index 0000000..4cbc381
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/datacenterTable.json
@@ -0,0 +1,33 @@
+{
+ "title": "数据中枢",
+ "id": "主键",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "tableName": "数据表名",
+ "tableDesc": "数据表描述",
+ "actualTable": "物理表名",
+ "status": "数据状态",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "options": "扩展项",
+ "fields": "字段",
+ "fieldName": "字段名",
+ "fieldDesc": "字段描述",
+ "fieldType": "字段类型",
+ "required": "是否必填",
+ "noFieldError": "至少包含一个字段",
+ "fieldInfoError": "字段信息不完善",
+ "nameRegx": "只能包含小写字母、数字和下划线,且必须以小写字母开头",
+ "structure": "表结构",
+ "data": "数据",
+ "uploadTitle": "点击或将文件拖拽到这里上传",
+ "uploadDesc": "上传一份Excel文档,文件大小限制10MB以内。",
+ "downloadTemplate": "下载模板",
+ "importComplete": "导入完成",
+ "totalNum": "总数",
+ "successNum": "成功数",
+ "failNum": "失败数",
+ "failList": "失败记录"
+}
diff --git a/app/src/locales/langs/zh-CN/datacenterTableFields.json b/app/src/locales/langs/zh-CN/datacenterTableFields.json
new file mode 100644
index 0000000..55abd14
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/datacenterTableFields.json
@@ -0,0 +1,13 @@
+{
+ "id": "主键",
+ "tableId": "数据表ID",
+ "fieldName": "字段名称",
+ "fieldDesc": "字段描述",
+ "fieldType": "字段类型",
+ "required": "是否必填",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "options": "扩展项"
+}
diff --git a/app/src/locales/langs/zh-CN/demos.json b/app/src/locales/langs/zh-CN/demos.json
new file mode 100644
index 0000000..2959121
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/demos.json
@@ -0,0 +1,14 @@
+{
+ "title": "演示",
+ "elementPlus": "Element Plus",
+ "form": "表单演示",
+ "aiflowy": {
+ "title": "项目",
+ "about": "关于",
+ "document": "文档",
+ "antdv": "Ant Design Vue 版本",
+ "naive-ui": "Naive UI 版本",
+ "element-plus": "Element Plus 版本",
+ "tdesign": "TDesign Vue 版本"
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/dictSelect.json b/app/src/locales/langs/zh-CN/dictSelect.json
new file mode 100644
index 0000000..16798be
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/dictSelect.json
@@ -0,0 +1,4 @@
+{
+ "placeholder": "请选择",
+ "getError": "获取字典数据失败"
+}
diff --git a/app/src/locales/langs/zh-CN/documentCollection.json b/app/src/locales/langs/zh-CN/documentCollection.json
new file mode 100644
index 0000000..c10e82b
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/documentCollection.json
@@ -0,0 +1,81 @@
+{
+ "id": "Id",
+ "alias": "别名",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "icon": "ICON",
+ "title": "名称",
+ "description": "描述",
+ "slug": "URL 别名",
+ "vectorStoreEnable": "是否启用向量数据库",
+ "vectorStoreType": "向量数据库类型",
+ "vectorStoreCollection": "向量数据库集合",
+ "vectorStoreConfig": "向量数据库配置",
+ "vectorEmbedLlmId": "向量模型",
+ "created": "创建时间",
+ "createdBy": "创建用户ID",
+ "modified": "最后一次修改时间",
+ "modifiedBy": "最后一次修改用户ID",
+ "options": "其他配置",
+ "rerankLlmId": "重排模型",
+ "searchEngineEnable": "是否启用搜索引擎",
+ "englishName": "英文名称",
+ "documentType": "文件类型",
+ "fileName": "文件名",
+ "knowledgeCount": "知识条数",
+ "createdModifyTime": "创建/更新时间",
+ "documentList": "文档列表",
+ "knowledgeRetrieval": "知识检索",
+ "sorting": "排序",
+ "content": "内容",
+ "placeholder": {
+ "title": "请输入名称",
+ "description": "请输入描述,以便大模型更好的理解该知识库并且调用",
+ "englishName": "请输入英文名称",
+ "alias": "请输入别名,不允许含中文",
+ "embedLlm": "请选择向量模型",
+ "rerankLlm": "请选择重排模型",
+ "vectorStoreCollection": "只能包含字母、数字和下划线且长度在3-20个字符之间",
+ "vectorStoreType": "请选择向量数据库类型"
+ },
+ "importDoc": {
+ "fileUpload": "文件上传",
+ "parameterSettings": "参数设置",
+ "segmentedPreview": "分段预览",
+ "confirmImport": "确认导入",
+ "fileName": "文件名称",
+ "progressUpload": "文件上传进度",
+ "fileSize": "文件大小"
+ },
+ "splitterDoc": {
+ "fileType": "文件类型",
+ "splitterName": "分割器",
+ "chunkSize": "分段长度",
+ "overlapSize": "分段重叠",
+ "regex": "正则表达式",
+ "document": "文档",
+ "simpleDocumentSplitter": "简单文档分割器",
+ "simpleTokenizeSplitter": "简单分词器",
+ "regexDocumentSplitter": "正则文档分割器",
+ "uploadStatus": "上传状态",
+ "pendingUpload": "待上传",
+ "completed": "已完成",
+ "uploading": "解析中",
+ "importSuccess": "导入成功"
+ },
+ "documentManagement": "文档管理",
+ "actions": {
+ "knowledge": "知识",
+ "retrieve": "检索",
+ "addKnowledge": "新增知识库",
+ "confirmImport": "确认导入",
+ "cancelImport": "取消导入"
+ },
+ "searchResults": "检索结果",
+ "documentPreview": "文档预览",
+ "total": "共",
+ "segments": "个分段",
+ "similarityScore": "相似度",
+ "alibabaCloud": "阿里云",
+ "tencentCloud": "腾讯云"
+}
diff --git a/app/src/locales/langs/zh-CN/headerSearch.json b/app/src/locales/langs/zh-CN/headerSearch.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/headerSearch.json
@@ -0,0 +1 @@
+{}
diff --git a/app/src/locales/langs/zh-CN/llm.json b/app/src/locales/langs/zh-CN/llm.json
new file mode 100644
index 0000000..11b6ca3
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/llm.json
@@ -0,0 +1,76 @@
+{
+ "filed":
+ {
+ "title": "标题",
+ "brand": "供应商",
+ "llmModel": "大模型名称",
+ "icon": "ICON",
+ "description": "描述",
+ "supportChat": "对话模型",
+ "supportFunctionCalling": "方法调用",
+ "supportEmbed": "向量化",
+ "supportReranker": "重排",
+ "supportTextToImage": "文生图",
+ "supportImageToImage": "图生图",
+ "supportTextToAudio": "文生音频",
+ "supportAudioToAudio": "音频转音频",
+ "supportTextToVideo": "文生视频",
+ "supportImageToVideo": "图生成视频",
+ "llmEndpoint": "请求地址",
+ "chatPath": "对话路径",
+ "embedPath": "向量化路径",
+ "multimodal": "多模态",
+ "llmApiKey": "apiKey",
+ "llmExtraConfig": "大模型其他属性配置",
+ "options": "其他配置内容",
+ "ability": "能力"
+ }
+,
+ "llmModal": {
+ "TitleRequired": "请输入名称",
+ "BrandRequired": "请选择供应商",
+ "ModelRequired": "请输入大模型",
+ "ApiKeyRequired": "请输入apiKey",
+ "QuickAddLlm": "一键添加大模型"
+ },
+ "placeholder": {
+ "title": "请输入名称",
+ "brand": "请选择品牌",
+ "llmModel": "请输入大模型名称",
+ "description": "请输入描述"
+ },
+ "actions": {
+ "verifyConfiguration": "验证配置"
+ },
+ "message": {
+ "verifySuccess": "验证成功"
+ },
+ "addProvider": "供应商列表",
+ "modelType": "模型类型",
+ "llmModel": "模型名称",
+ "title": "名称",
+ "groupName": "分组名称",
+ "provider": "供应商",
+ "ability": "模型能力",
+ "button": {
+ "management": "管理",
+ "test": "检测",
+ "addAllLlm": "添加列表中的所有模型",
+ "RetrieveAgain": "重新获取模型列表"
+ },
+ "all": "全部",
+ "verifyLlmTitle": "请选择要检测的模型",
+ "testSuccess": "检测成功",
+ "searchTextPlaceholder": "搜索模型名称或名称",
+ "modelAbility": {
+ "supportThinking": "推理",
+ "supportTool": "工具",
+ "supportAudio": "音频",
+ "supportVideo": "视频",
+ "supportImage": "图片",
+ "supportFree": "免费",
+ "supportImageB64Only": "仅支持Base64图片",
+ "supportToolMessage": "支持Tool消息"
+ },
+ "requestPath": "请求路径"
+}
diff --git a/app/src/locales/langs/zh-CN/llmProvider.json b/app/src/locales/langs/zh-CN/llmProvider.json
new file mode 100644
index 0000000..5bdc6f8
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/llmProvider.json
@@ -0,0 +1,15 @@
+{
+ "providerName": "供应商名称",
+ "provider": "供应商",
+ "icon": "Icon",
+ "apiKey": "API 密钥",
+ "endpoint": "API 地址",
+ "embedPath": "向量地址",
+ "chatPath": "对话地址",
+ "rerankPath": "重排地址",
+ "embeddingModel": "向量模型",
+ "chatModel": "对话模型",
+ "rerankModel": "重排模型",
+ "model": "模型",
+ "apiType": "API类型"
+}
diff --git a/app/src/locales/langs/zh-CN/mcp.json b/app/src/locales/langs/zh-CN/mcp.json
new file mode 100644
index 0000000..f3b3c6a
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/mcp.json
@@ -0,0 +1,20 @@
+{
+ "id": "id",
+ "title": "名称",
+ "description": "描述",
+ "configJson": "MCP配置JSON",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "created": "创建时间",
+ "createdBy": "创建者ID",
+ "modified": "修改时间",
+ "modifiedBy": "修改者ID",
+ "status": "是否启用",
+ "modal": {
+ "config": "配置",
+ "tool": "工具",
+ "table": {
+ "availableTools": "可用工具"
+ }
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/menus.json b/app/src/locales/langs/zh-CN/menus.json
new file mode 100644
index 0000000..877cd7b
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/menus.json
@@ -0,0 +1,33 @@
+{
+ "system": {
+ "title": "系统管理",
+ "sysAccount": "用户管理",
+ "sysRole": "角色管理",
+ "sysMenu": "菜单管理",
+ "sysDept": "部门管理",
+ "sysPosition": "岗位管理",
+ "sysDict": "字典管理",
+ "sysJob": "定时任务",
+ "sysLog": "日志管理",
+ "sysFeedback": "用户反馈",
+ "sysAppearance": "外观设置",
+ "oauth": "认证设置"
+ },
+ "ai": {
+ "bots": "聊天助手",
+ "title": "AI能力",
+ "resources": "素材库",
+ "datacenter": "数据中枢",
+ "workflow": "工作流",
+ "plugin": "插件",
+ "model": "模型管理",
+ "documentCollection": "知识库",
+ "knowledge": "知识管理",
+ "mcp": "MCP"
+ },
+ "settings": {
+ "title": "系统配置",
+ "settingsConfig": "系统设置",
+ "apiKey": "访问令牌"
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/message.json b/app/src/locales/langs/zh-CN/message.json
new file mode 100644
index 0000000..b41ccdf
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/message.json
@@ -0,0 +1,46 @@
+{
+ "deleteAlert": "确定删除吗?",
+ "noticeTitle": "提示",
+ "ok": "确定",
+ "cancel": "取消",
+ "required": "该项为必填项",
+ "saveOkMessage": "保存成功!",
+ "saveFailMessage": "保存失败!",
+ "deleteOkMessage": "删除成功!",
+ "deleteFailMessage": "删除失败!",
+ "loading": "加载中...",
+ "getDataError": "获取数据失败",
+ "updateOkMessage": "更新成功!",
+ "startAlert": "确定启动吗?",
+ "stopAlert": "确定停止吗?",
+ "preview": "预览",
+ "notEmpty": "不能为空",
+ "success": "成功",
+ "fail": "失败",
+ "pleaseSelect": "请选择{name}",
+ "pleaseInputContent": "请输入内容",
+ "notSupported": "暂不支持",
+ "englishNameRule": "请输入由字母、数字、下划线、连字符组成的字符,且长度不超过64位",
+ "downloadSuccess": "下载成功",
+ "downloadFail": "下载失败",
+ "copySuccess": "复制成功",
+ "copyFail": "复制失败",
+ "upload": {
+ "title": "点击或将文件拖拽到这里上传",
+ "description": "TXT, PDF, DOCX, MD, PPT, PPTX,XLSX 格式文件,单个大小不超过20M。"
+ },
+ "uploadFileFirst": "请先上传文件",
+ "deleteModelAlert": "该操作会删除大模型,确定删除吗?",
+ "deleteModelGroupAlert": "该操作会删除该分组下所有大模型,确定删除吗?",
+ "cannotBeEmpty": {
+ "name": "参数名称不能为空",
+ "description": "参数描述不能为空",
+ "method": "传入方法不能为空",
+ "type": "参数类型不能为空",
+ "all": "请完善所有必填项后提交",
+ "error": "参数校验失败,请完善必填项"
+ },
+ "completeForm": "请完善表单信息",
+ "notVideo": "您的浏览器不支持 video 元素。",
+ "notAudio": "您的浏览器不支持 audio 元素。"
+}
diff --git a/app/src/locales/langs/zh-CN/page.json b/app/src/locales/langs/zh-CN/page.json
new file mode 100644
index 0000000..7b61a5a
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/page.json
@@ -0,0 +1,23 @@
+{
+ "auth": {
+ "login": "登录",
+ "register": "注册",
+ "codeLogin": "验证码登录",
+ "qrcodeLogin": "二维码登录",
+ "forgetPassword": "忘记密码",
+ "profile": "个人中心",
+ "accountPassword": "账户密码",
+ "systemMessage": "系统消息",
+ "todoTasks": "待办任务"
+ },
+ "dashboard": {
+ "title": "概览",
+ "analytics": "分析页",
+ "workspace": "工作台"
+ },
+ "description": {
+ "accountPassword": "其他用户的消息将以站内信的形式通知",
+ "systemMessage": "系统消息将以站内信的形式通知",
+ "todoTasks": "待办任务将以站内信的形式通知"
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/plugin.json b/app/src/locales/langs/zh-CN/plugin.json
new file mode 100644
index 0000000..a0444b2
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/plugin.json
@@ -0,0 +1,33 @@
+{
+ "pluginCategory": "插件分类",
+ "id": "插件id",
+ "alias": "别名",
+ "name": "名称",
+ "description": "描述",
+ "type": "类型",
+ "baseUrl": "基础URL",
+ "authType": "认证方式",
+ "created": "创建时间",
+ "icon": "图标地址",
+ "position": "认证参数位置",
+ "headers": "请求头",
+ "tokenKey": "token键",
+ "tokenValue": "token值",
+ "deptId": "部门id",
+ "tenantId": "租户id",
+ "createdBy": "创建人",
+ "category": "分类",
+ "placeholder": {
+ "name": "请输入插件名称",
+ "description": "请输入插件描述",
+ "categorize": "请选择分类"
+ },
+ "button": {
+ "addPlugin": "新增插件",
+ "categorize": "归类",
+ "tools": "工具"
+ },
+ "toolsManagement": "工具管理",
+ "searchUsers": "搜索用户",
+ "parameterValue": "参数值"
+}
diff --git a/app/src/locales/langs/zh-CN/pluginItem.json b/app/src/locales/langs/zh-CN/pluginItem.json
new file mode 100644
index 0000000..781a3c9
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/pluginItem.json
@@ -0,0 +1,35 @@
+{
+ "id": "插件工具id",
+ "pluginId": "插件id",
+ "name": "工具名称",
+ "description": "工具描述",
+ "basePath": "基础路径",
+ "created": "创建时间",
+ "status": "是否启用",
+ "inputData": "输入参数",
+ "outputData": "输出参数",
+ "requestMethod": "请求方式【Post, Get, Put, Delete】",
+ "serviceStatus": "服务状态[0 下线 1 上线]",
+ "debugStatus": "调试状态【0失败 1成功】",
+ "englishName": "英文名称",
+ "createPluginTool": "创建工具",
+ "pluginToolEdit": {
+ "basicInfo": "基本信息",
+ "configureInputParameters": "配置输入参数",
+ "configureOutputParameters": "配置输出参数",
+ "trialRun": "试运行",
+ "toolPath": "工具路径",
+ "requestMethod": "请求方法",
+ "runResult": "运行结果",
+ "run": "运行"
+ },
+ "parameterName": "参数名称",
+ "parameterDescription": "参数描述",
+ "parameterType": "参数类型",
+ "inputMethod": "传入方法",
+ "required": "是否必填",
+ "defaultValue": "默认值",
+ "enabledStatus": "启用状态",
+ "addChildNode": "添加子节点",
+ "addParameter": "新增参数"
+}
diff --git a/app/src/locales/langs/zh-CN/settingsConfig.json b/app/src/locales/langs/zh-CN/settingsConfig.json
new file mode 100644
index 0000000..b9ec6c4
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/settingsConfig.json
@@ -0,0 +1,10 @@
+{
+ "title": "大模型配置",
+ "modelOfChat": "对话模型供应商",
+ "dialogModel": "对话模型配置",
+ "modelName": "模型名称",
+ "basic": "基本设置",
+ "updatePwd": "修改密码",
+ "systemAIFunctionSettings": "系统 AI 功能设置",
+ "note": "注意:此项配置,仅用于系统的 AI 功能,而非【聊天助手】。"
+}
diff --git a/app/src/locales/langs/zh-CN/sysAccount.json b/app/src/locales/langs/zh-CN/sysAccount.json
new file mode 100644
index 0000000..a55262a
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysAccount.json
@@ -0,0 +1,28 @@
+{
+ "id": "主键",
+ "deptId": "部门",
+ "tenantId": "租户ID",
+ "loginName": "登录账号",
+ "password": "密码",
+ "accountType": "账户类型",
+ "nickname": "昵称",
+ "mobile": "手机电话",
+ "email": "邮件",
+ "avatar": "账户头像",
+ "dataScope": "数据权限类型",
+ "deptIdList": "自定义部门权限",
+ "status": "是否启用",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "remark": "备注",
+ "isDeleted": "删除标识",
+ "roleIds": "角色",
+ "positionIds": "岗位",
+ "oldPwd": "旧密码",
+ "newPwd": "新密码",
+ "confirmPwd": "确认密码",
+ "repeatPwd": "请再次输入密码",
+ "notSamePwd": "两次输入的密码不一致"
+}
diff --git a/app/src/locales/langs/zh-CN/sysApiKey.json b/app/src/locales/langs/zh-CN/sysApiKey.json
new file mode 100644
index 0000000..dac3fc0
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysApiKey.json
@@ -0,0 +1,18 @@
+{
+ "id": "id",
+ "apiKey": "apiKey",
+ "created": "创建时间",
+ "status": "数据状态",
+ "deptId": "部门id",
+ "tenantId": "租户id",
+ "expiredAt": "失效时间",
+ "createdBy": "创建人",
+ "addApiKey": "新增apiKey",
+ "actions": {
+ "enable": "启用",
+ "disable": "未启用",
+ "failure": "已失效"
+ },
+ "permissions": "授权接口",
+ "addApiKeyNotice": "该操作会生成一个apiKey,请确认是否生成"
+}
diff --git a/app/src/locales/langs/zh-CN/sysApiKeyResourcePermission.json b/app/src/locales/langs/zh-CN/sysApiKeyResourcePermission.json
new file mode 100644
index 0000000..51e9183
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysApiKeyResourcePermission.json
@@ -0,0 +1,6 @@
+{
+ "id": "id",
+ "requestInterface": "请求接口",
+ "title": "标题",
+ "addPermission": "添加请求接口"
+}
diff --git a/app/src/locales/langs/zh-CN/sysAppearance.json b/app/src/locales/langs/zh-CN/sysAppearance.json
new file mode 100644
index 0000000..e6ed991
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysAppearance.json
@@ -0,0 +1,24 @@
+{
+ "Theme and Color Scheme": "主题与配色",
+ "Theme Mode": "主题模式",
+ "Theme Color": "主题色",
+ "Layout & Navigation": "布局与导航",
+ "Layout Mode": "布局模式",
+ "Interface Display": "界面显示",
+ "Page Tabs": "页面标签页",
+ "Animation": "动画",
+ "Login Page Appearance": "登录页外观",
+ "Login Page Layout": "登录页布局",
+ "Login Page Image": "登录页图片",
+ "OnlyJPG": "只支持.jpg 格式",
+ "Login Page Brand Copy": "登录页品牌文案",
+ "Welcome Title": "欢迎语标题",
+ "Please enter the welcome title": "请填写欢迎语标题",
+ "Welcome Description": "欢迎语描述",
+ "Please enter the welcome description": "请填写欢迎语描述",
+ "Slogan Title": "Slogn标题",
+ "Please enter the slogan title": "请填写Slogn标题",
+ "Slogan Description": "Slogn描述",
+ "Please enter the slogan description": "请填写Slogn描述",
+ "Thumbnail": "缩略图"
+}
diff --git a/app/src/locales/langs/zh-CN/sysDept.json b/app/src/locales/langs/zh-CN/sysDept.json
new file mode 100644
index 0000000..e1f5130
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysDept.json
@@ -0,0 +1,17 @@
+{
+ "root": "根部门",
+ "id": "主键",
+ "tenantId": "租户ID",
+ "parentId": "父级",
+ "ancestors": "父级部门ID集合",
+ "deptName": "部门名称",
+ "deptCode": "部门编码",
+ "sortNo": "排序",
+ "status": "数据状态",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "remark": "备注",
+ "isDeleted": "删除标识"
+}
diff --git a/app/src/locales/langs/zh-CN/sysDict.json b/app/src/locales/langs/zh-CN/sysDict.json
new file mode 100644
index 0000000..f9db052
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysDict.json
@@ -0,0 +1,12 @@
+{
+ "id": "主键",
+ "name": "数据字典名称",
+ "code": "字典编码",
+ "description": "字典描述或备注",
+ "dictType": "字典类型 1 自定义字典、2 数据表字典、 3 枚举类字典、 4 系统字典(自定义 DictLoader)",
+ "sortNo": "排序编号",
+ "status": "是否启用",
+ "options": "扩展字典 存放 json",
+ "created": "创建时间",
+ "modified": "修改时间"
+}
diff --git a/app/src/locales/langs/zh-CN/sysFeedback.json b/app/src/locales/langs/zh-CN/sysFeedback.json
new file mode 100644
index 0000000..67a95a7
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysFeedback.json
@@ -0,0 +1,20 @@
+{
+ "feedbackType": "反馈类型",
+ "processingStatus": "处理状态",
+ "category": "问题类型",
+ "description": "问题摘要",
+ "contactInformation": "联系方式",
+ "submittedAt": "提交时间",
+ "functionalFailure": "功能故障",
+ "optimizationSuggestions": "优化建议",
+ "accountIssue": "账号问题",
+ "other": "其它",
+ "notViewed": "未查看",
+ "viewed": "已查看",
+ "processed": "已处理",
+ "closed/Invalid": "已关闭/无效",
+ "markedSuccessfully": "标记成功!",
+ "basicInformation": "基础信息",
+ "feedbackContent": "反馈内容",
+ "attachments": "附件"
+}
diff --git a/app/src/locales/langs/zh-CN/sysJob.json b/app/src/locales/langs/zh-CN/sysJob.json
new file mode 100644
index 0000000..9813018
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysJob.json
@@ -0,0 +1,23 @@
+{
+ "id": "主键",
+ "deptId": "部门ID",
+ "tenantId": "租户ID",
+ "jobName": "任务名称",
+ "jobType": "任务类型",
+ "jobParams": "任务参数",
+ "cronExpression": "cron表达式",
+ "allowConcurrent": "是否并发执行",
+ "misfirePolicy": "错过策略",
+ "options": "其他配置",
+ "status": "任务状态",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "remark": "备注",
+ "isDeleted": "删除标识",
+ "workflow": "工作流",
+ "beanMethod": "bean方法",
+ "javaMethod": "java方法",
+ "example": "示例"
+}
diff --git a/app/src/locales/langs/zh-CN/sysJobLog.json b/app/src/locales/langs/zh-CN/sysJobLog.json
new file mode 100644
index 0000000..32dabc1
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysJobLog.json
@@ -0,0 +1,14 @@
+{
+ "title": "任务日志",
+ "id": "主键",
+ "jobId": "任务ID",
+ "jobName": "任务名称",
+ "jobParams": "任务参数",
+ "jobResult": "执行结果",
+ "errorInfo": "错误信息",
+ "status": "执行状态",
+ "startTime": "开始时间",
+ "endTime": "结束时间",
+ "created": "创建时间",
+ "remark": "备注"
+}
diff --git a/app/src/locales/langs/zh-CN/sysLog.json b/app/src/locales/langs/zh-CN/sysLog.json
new file mode 100644
index 0000000..82fd4fe
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysLog.json
@@ -0,0 +1,14 @@
+{
+ "id": "ID",
+ "accountId": "操作人",
+ "actionName": "操作名称",
+ "actionType": "操作的类型",
+ "actionClass": "操作涉及的类",
+ "actionMethod": "操作涉及的方法",
+ "actionUrl": "操作涉及的 URL 地址",
+ "actionIp": "操作涉及的用户 IP 地址",
+ "actionParams": "操作请求参数",
+ "actionBody": "操作请求body",
+ "status": "操作状态 1 成功 9 失败",
+ "created": "操作时间"
+}
diff --git a/app/src/locales/langs/zh-CN/sysMenu.json b/app/src/locales/langs/zh-CN/sysMenu.json
new file mode 100644
index 0000000..6aee2d5
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysMenu.json
@@ -0,0 +1,20 @@
+{
+"root":"顶级",
+"id": "主键",
+"parentId": "父菜单",
+"menuType": "菜单类型",
+"menuTitle": "菜单标题",
+"menuUrl": "菜单url",
+"component": "组件路径",
+"menuIcon": "图标",
+"isShow": "是否显示",
+"permissionTag": "权限标识",
+"sortNo": "排序",
+"status": "数据状态",
+"created": "创建时间",
+"createdBy": "创建者",
+"modified": "修改时间",
+"modifiedBy": "修改者",
+"remark": "备注",
+"isDeleted": "删除标识"
+}
diff --git a/app/src/locales/langs/zh-CN/sysOption.json b/app/src/locales/langs/zh-CN/sysOption.json
new file mode 100644
index 0000000..0ac65bf
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysOption.json
@@ -0,0 +1,4 @@
+{
+ "oauthWxWeb": "微信登录",
+ "oauthDingTalk": "钉钉登录"
+}
diff --git a/app/src/locales/langs/zh-CN/sysPosition.json b/app/src/locales/langs/zh-CN/sysPosition.json
new file mode 100644
index 0000000..20c6c18
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysPosition.json
@@ -0,0 +1,24 @@
+{
+ "id": "主键",
+ "tenantId": "租户ID",
+ "deptId": "部门",
+ "positionName": "岗位名称",
+ "positionCode": "岗位编码",
+ "sortNo": "排序",
+ "status": "数据状态",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "remark": "备注",
+ "isDeleted": "删除标识",
+ "enable": "启用",
+ "disable": "禁用",
+ "message": {
+ "title": "确认要{actionText}\"{positionName}\"吗?"
+ },
+ "placeholder": {
+ "positionName": "请输入岗位名称",
+ "positionCode": "请输入岗位编码"
+ }
+}
diff --git a/app/src/locales/langs/zh-CN/sysRole.json b/app/src/locales/langs/zh-CN/sysRole.json
new file mode 100644
index 0000000..0b77250
--- /dev/null
+++ b/app/src/locales/langs/zh-CN/sysRole.json
@@ -0,0 +1,17 @@
+{
+ "id": "主键",
+ "tenantId": "租户ID",
+ "roleName": "角色名称",
+ "roleKey": "角色标识",
+ "status": "是否启用",
+ "created": "创建时间",
+ "createdBy": "创建者",
+ "modified": "修改时间",
+ "modifiedBy": "修改者",
+ "remark": "备注",
+ "isDeleted": "删除标识",
+ "menuPermission": "菜单权限",
+ "dataPermission": "数据权限",
+ "checkStrictlyTrue": "联动",
+ "checkStrictlyFalse": "不联动"
+}
diff --git a/app/src/main.ts b/app/src/main.ts
new file mode 100644
index 0000000..68e8b77
--- /dev/null
+++ b/app/src/main.ts
@@ -0,0 +1,31 @@
+import { initPreferences } from '@aiflowy/preferences';
+import { unmountGlobalLoading } from '@aiflowy/utils';
+
+import { overridesPreferences } from './preferences';
+
+/**
+ * 应用初始化完成之后再进行页面加载渲染
+ */
+async function initApplication() {
+ // name用于指定项目唯一标识
+ // 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据
+ const env = import.meta.env.PROD ? 'prod' : 'dev';
+ const appVersion = import.meta.env.VITE_APP_VERSION;
+ const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
+
+ // app偏好设置初始化
+ await initPreferences({
+ namespace,
+ overrides: overridesPreferences,
+ });
+
+ // 启动应用并挂载
+ // vue应用主要逻辑及视图
+ const { bootstrap } = await import('./bootstrap');
+ await bootstrap(namespace);
+
+ // 移除并销毁loading
+ unmountGlobalLoading();
+}
+
+initApplication();
diff --git a/app/src/preferences.ts b/app/src/preferences.ts
new file mode 100644
index 0000000..6342ccd
--- /dev/null
+++ b/app/src/preferences.ts
@@ -0,0 +1,14 @@
+import { defineOverridesPreferences } from '@aiflowy/preferences';
+
+/**
+ * @description 项目配置文件
+ * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
+ * !!! 更改配置后请清空缓存,否则可能不生效
+ */
+export const overridesPreferences = defineOverridesPreferences({
+ // overrides
+ app: {
+ name: import.meta.env.VITE_APP_TITLE,
+ accessMode: 'mixed',
+ },
+});
diff --git a/app/src/router/access.ts b/app/src/router/access.ts
new file mode 100644
index 0000000..27e7482
--- /dev/null
+++ b/app/src/router/access.ts
@@ -0,0 +1,42 @@
+import type {
+ ComponentRecordType,
+ GenerateMenuAndRoutesOptions,
+} from '@aiflowy/types';
+
+import { generateAccessible } from '@aiflowy/access';
+import { preferences } from '@aiflowy/preferences';
+
+import { ElMessage } from 'element-plus';
+
+import { getAllMenusApi } from '#/api';
+import { BasicLayout, IFrameView } from '#/layouts';
+import { $t } from '#/locales';
+
+const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
+
+async function generateAccess(options: GenerateMenuAndRoutesOptions) {
+ const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue');
+
+ const layoutMap: ComponentRecordType = {
+ BasicLayout,
+ IFrameView,
+ };
+
+ return await generateAccessible(preferences.app.accessMode, {
+ ...options,
+ fetchMenuListAsync: async () => {
+ ElMessage({
+ duration: 1500,
+ message: `${$t('common.loadingMenu')}...`,
+ });
+ return await getAllMenusApi();
+ },
+ // 可以指定没有权限跳转403页面
+ forbiddenComponent,
+ // 如果 route.meta.menuVisibleWithForbidden = true
+ layoutMap,
+ pageMap,
+ });
+}
+
+export { generateAccess };
diff --git a/app/src/router/guard.ts b/app/src/router/guard.ts
new file mode 100644
index 0000000..cdd147c
--- /dev/null
+++ b/app/src/router/guard.ts
@@ -0,0 +1,133 @@
+import type { Router } from 'vue-router';
+
+import { LOGIN_PATH } from '@aiflowy/constants';
+import { preferences } from '@aiflowy/preferences';
+import { useAccessStore, useUserStore } from '@aiflowy/stores';
+import { startProgress, stopProgress } from '@aiflowy/utils';
+
+import { accessRoutes, coreRouteNames } from '#/router/routes';
+import { useAuthStore } from '#/store';
+
+import { generateAccess } from './access';
+
+/**
+ * 通用守卫配置
+ * @param router
+ */
+function setupCommonGuard(router: Router) {
+ // 记录已经加载的页面
+ const loadedPaths = new Set();
+
+ router.beforeEach((to) => {
+ to.meta.loaded = loadedPaths.has(to.path);
+
+ // 页面加载进度条
+ if (!to.meta.loaded && preferences.transition.progress) {
+ startProgress();
+ }
+ return true;
+ });
+
+ router.afterEach((to) => {
+ // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
+
+ loadedPaths.add(to.path);
+
+ // 关闭页面加载进度条
+ if (preferences.transition.progress) {
+ stopProgress();
+ }
+ });
+}
+
+/**
+ * 权限访问守卫配置
+ * @param router
+ */
+function setupAccessGuard(router: Router) {
+ router.beforeEach(async (to, from) => {
+ const accessStore = useAccessStore();
+ const userStore = useUserStore();
+ const authStore = useAuthStore();
+
+ // 基本路由,这些路由不需要进入权限拦截
+ if (coreRouteNames.includes(to.name as string)) {
+ if (to.path === LOGIN_PATH && accessStore.accessToken) {
+ return decodeURIComponent(
+ (to.query?.redirect as string) ||
+ userStore.userInfo?.homePath ||
+ preferences.app.defaultHomePath,
+ );
+ }
+ return true;
+ }
+
+ // accessToken 检查
+ if (!accessStore.accessToken) {
+ // 明确声明忽略权限访问权限,则可以访问
+ if (to.meta.ignoreAccess) {
+ return true;
+ }
+
+ // 没有访问权限,跳转登录页面
+ if (to.fullPath !== LOGIN_PATH) {
+ return {
+ path: LOGIN_PATH,
+ // 如不需要,直接删除 query
+ query:
+ to.fullPath === preferences.app.defaultHomePath
+ ? {}
+ : { redirect: encodeURIComponent(to.fullPath) },
+ // 携带当前跳转的页面,登录后重新跳转该页面
+ replace: true,
+ };
+ }
+ return to;
+ }
+
+ // 是否已经生成过动态路由
+ if (accessStore.isAccessChecked) {
+ return true;
+ }
+
+ // 生成路由表
+ // 当前登录用户拥有的角色标识列表
+ const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
+ const userRoles = userInfo.roles ?? [];
+
+ // 生成菜单和路由
+ const { accessibleMenus, accessibleRoutes } = await generateAccess({
+ roles: userRoles,
+ router,
+ // 则会在菜单中显示,但是访问会被重定向到403
+ routes: accessRoutes,
+ });
+
+ // 保存菜单信息和路由信息
+ accessStore.setAccessMenus(accessibleMenus);
+ accessStore.setAccessRoutes(accessibleRoutes);
+ accessStore.setIsAccessChecked(true);
+ const redirectPath = (from.query.redirect ??
+ (to.path === preferences.app.defaultHomePath
+ ? userInfo.homePath || preferences.app.defaultHomePath
+ : to.fullPath)) as string;
+
+ return {
+ ...router.resolve(decodeURIComponent(redirectPath)),
+ replace: true,
+ };
+ });
+}
+
+/**
+ * 项目守卫配置
+ * @param router
+ */
+function createRouterGuard(router: Router) {
+ /** 通用 */
+ setupCommonGuard(router);
+ /** 权限访问 */
+ setupAccessGuard(router);
+}
+
+export { createRouterGuard };
diff --git a/app/src/router/index.ts b/app/src/router/index.ts
new file mode 100644
index 0000000..18f0080
--- /dev/null
+++ b/app/src/router/index.ts
@@ -0,0 +1,37 @@
+import {
+ createRouter,
+ createWebHashHistory,
+ createWebHistory,
+} from 'vue-router';
+
+import { resetStaticRoutes } from '@aiflowy/utils';
+
+import { createRouterGuard } from './guard';
+import { routes } from './routes';
+
+/**
+ * @zh_CN 创建vue-router实例
+ */
+const router = createRouter({
+ history:
+ import.meta.env.VITE_ROUTER_HISTORY === 'hash'
+ ? createWebHashHistory(import.meta.env.VITE_BASE)
+ : createWebHistory(import.meta.env.VITE_BASE),
+ // 应该添加到路由的初始路由列表。
+ routes,
+ scrollBehavior: (to, _from, savedPosition) => {
+ if (savedPosition) {
+ return savedPosition;
+ }
+ return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
+ },
+ // 是否应该禁止尾部斜杠。
+ // strict: true,
+});
+
+const resetRoutes = () => resetStaticRoutes(router, routes);
+
+// 创建路由守卫
+createRouterGuard(router);
+
+export { resetRoutes, router };
diff --git a/app/src/router/routes/core.ts b/app/src/router/routes/core.ts
new file mode 100644
index 0000000..0ff0457
--- /dev/null
+++ b/app/src/router/routes/core.ts
@@ -0,0 +1,108 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { LOGIN_PATH } from '@aiflowy/constants';
+import { preferences } from '@aiflowy/preferences';
+
+import { $t } from '#/locales';
+
+const BasicLayout = () => import('#/layouts/basic.vue');
+const AuthPageLayout = () => import('#/layouts/auth.vue');
+/** 全局404页面 */
+const fallbackNotFoundRoute: RouteRecordRaw = {
+ component: () => import('#/views/_core/fallback/not-found.vue'),
+ meta: {
+ hideInBreadcrumb: true,
+ hideInMenu: true,
+ hideInTab: true,
+ title: '404',
+ },
+ name: 'FallbackNotFound',
+ path: '/:path(.*)*',
+};
+
+/** 基本路由,这些路由是必须存在的 */
+const coreRoutes: RouteRecordRaw[] = [
+ {
+ component: () => import('#/views/_core/authentication/oauth-page.vue'),
+ meta: {
+ hideInBreadcrumb: true,
+ hideInMenu: true,
+ hideInTab: true,
+ title: 'OAuth',
+ },
+ name: 'OAuth',
+ path: '/oauth',
+ },
+ /**
+ * 根路由
+ * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。
+ * 此路由必须存在,且不应修改
+ */
+ {
+ component: BasicLayout,
+ meta: {
+ hideInBreadcrumb: true,
+ title: 'Root',
+ },
+ name: 'Root',
+ path: '/',
+ redirect: preferences.app.defaultHomePath,
+ children: [],
+ },
+ {
+ component: AuthPageLayout,
+ meta: {
+ hideInTab: true,
+ title: 'Authentication',
+ },
+ name: 'Authentication',
+ path: '/auth',
+ redirect: LOGIN_PATH,
+ children: [
+ {
+ name: 'Login',
+ path: 'login',
+ component: () => import('#/views/_core/authentication/login.vue'),
+ meta: {
+ title: $t('page.auth.login'),
+ },
+ },
+ {
+ name: 'CodeLogin',
+ path: 'code-login',
+ component: () => import('#/views/_core/authentication/code-login.vue'),
+ meta: {
+ title: $t('page.auth.codeLogin'),
+ },
+ },
+ {
+ name: 'QrCodeLogin',
+ path: 'qrcode-login',
+ component: () =>
+ import('#/views/_core/authentication/qrcode-login.vue'),
+ meta: {
+ title: $t('page.auth.qrcodeLogin'),
+ },
+ },
+ {
+ name: 'ForgetPassword',
+ path: 'forget-password',
+ component: () =>
+ import('#/views/_core/authentication/forget-password.vue'),
+ meta: {
+ title: $t('page.auth.forgetPassword'),
+ },
+ },
+ {
+ name: 'Register',
+ path: 'register',
+ component: () => import('#/views/_core/authentication/register.vue'),
+ meta: {
+ title: $t('page.auth.register'),
+ },
+ },
+ ],
+ },
+];
+
+export { coreRoutes, fallbackNotFoundRoute };
diff --git a/app/src/router/routes/index.ts b/app/src/router/routes/index.ts
new file mode 100644
index 0000000..939c35e
--- /dev/null
+++ b/app/src/router/routes/index.ts
@@ -0,0 +1,47 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { mergeRouteModules, traverseTreeValues } from '@aiflowy/utils';
+
+import { coreRoutes, fallbackNotFoundRoute } from './core';
+
+const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
+ eager: true,
+});
+
+// 有需要可以自行打开注释,并创建文件夹
+// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true });
+// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true });
+
+/** 动态路由 */
+const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
+
+/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统(不会显示在菜单中) */
+// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
+// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles);
+const staticRoutes: RouteRecordRaw[] = [];
+const externalRoutes: RouteRecordRaw[] = [];
+
+/** 路由列表,由基本路由、外部路由和404兜底路由组成
+ * 无需走权限验证(会一直显示在菜单中) */
+const routes: RouteRecordRaw[] = [
+ ...coreRoutes,
+ ...externalRoutes,
+ fallbackNotFoundRoute,
+];
+
+/** 基本路由列表,这些路由不需要进入权限拦截 */
+const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
+
+/** 有权限校验的路由列表,包含动态路由和静态路由 */
+const accessRoutes = [...dynamicRoutes, ...staticRoutes];
+
+const componentKeys: string[] = Object.keys(
+ import.meta.glob('../../views/**/*.vue'),
+)
+ .filter((item) => !item.includes('/modules/'))
+ .map((v) => {
+ const path = v.replace('../../views/', '/');
+ return path.endsWith('.vue') ? path.slice(0, -4) : path;
+ });
+
+export { accessRoutes, componentKeys, coreRouteNames, routes };
diff --git a/app/src/router/routes/modules/aiflowy.ts b/app/src/router/routes/modules/aiflowy.ts
new file mode 100644
index 0000000..67c9d68
--- /dev/null
+++ b/app/src/router/routes/modules/aiflowy.ts
@@ -0,0 +1,63 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+// import { APP_DOC_URL, APP_GITHUB_URL, APP_LOGO_URL } from '@aiflowy/constants';
+//
+// import { IFrameView } from '#/layouts';
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ // {
+ // meta: {
+ // badgeType: 'dot',
+ // icon: APP_LOGO_URL,
+ // order: 9998,
+ // title: $t('demos.aiflowy.title'),
+ // },
+ // name: 'AIFlowyProject',
+ // path: '/aiflowy-admin',
+ // children: [
+ // {
+ // name: 'AIFlowyDocument',
+ // path: '/aiflowy-admin/document',
+ // component: IFrameView,
+ // meta: {
+ // icon: 'lucide:book-open-text',
+ // link: APP_DOC_URL,
+ // title: $t('demos.aiflowy.document'),
+ // },
+ // },
+ // {
+ // name: 'AIFlowyGithub',
+ // path: '/aiflowy-admin/github',
+ // component: IFrameView,
+ // meta: {
+ // icon: 'mdi:github',
+ // link: APP_GITHUB_URL,
+ // title: 'Github',
+ // },
+ // },
+ // ],
+ // },
+ // {
+ // name: 'AIFlowyAbout',
+ // path: '/aiflowy-admin/about',
+ // component: () => import('#/views/_core/about/index.vue'),
+ // meta: {
+ // icon: 'lucide:copyright',
+ // title: $t('demos.aiflowy.about'),
+ // order: 9999,
+ // },
+ // },
+ {
+ name: 'Profile',
+ path: '/profile',
+ component: () => import('#/views/_core/profile/index.vue'),
+ meta: {
+ icon: 'lucide:user',
+ hideInMenu: true,
+ title: $t('page.auth.profile'),
+ },
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/bot.ts b/app/src/router/routes/modules/bot.ts
new file mode 100644
index 0000000..6975c89
--- /dev/null
+++ b/app/src/router/routes/modules/bot.ts
@@ -0,0 +1,32 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+const routes: RouteRecordRaw[] = [
+ {
+ name: 'BotRun',
+ path: '/ai/bots/run/:botId/:sessionId?',
+ component: () => import('#/views/ai/bots/pages/Run.vue'),
+ meta: {
+ title: 'Bots',
+ noBasicLayout: true,
+ openInNewWindow: true,
+ hideInMenu: true,
+ hideInBreadcrumb: true,
+ hideInTab: true,
+ },
+ },
+ {
+ name: 'BotSetting',
+ path: '/ai/bots/setting/:id',
+ component: () => import('#/views/ai/bots/pages/setting/index.vue'),
+ meta: {
+ title: 'Bots',
+ openInNewWindow: true,
+ hideInMenu: true,
+ hideInBreadcrumb: true,
+ hideInTab: true,
+ activePath: '/ai/bots',
+ },
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/dashboard.ts b/app/src/router/routes/modules/dashboard.ts
new file mode 100644
index 0000000..02c9055
--- /dev/null
+++ b/app/src/router/routes/modules/dashboard.ts
@@ -0,0 +1,38 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+// import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ // {
+ // meta: {
+ // icon: 'lucide:layout-dashboard',
+ // order: -1,
+ // title: $t('page.dashboard.title'),
+ // },
+ // name: 'Dashboard',
+ // path: '/dashboard',
+ // children: [
+ // {
+ // name: 'Analytics',
+ // path: '/analytics',
+ // component: () => import('#/views/dashboard/analytics/index.vue'),
+ // meta: {
+ // affixTab: true,
+ // icon: 'lucide:area-chart',
+ // title: $t('page.dashboard.analytics'),
+ // },
+ // },
+ // {
+ // name: 'Workspace',
+ // path: '/workspace',
+ // component: () => import('#/views/dashboard/workspace/index.vue'),
+ // meta: {
+ // icon: 'carbon:workspace',
+ // title: $t('page.dashboard.workspace'),
+ // },
+ // },
+ // ],
+ // },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/datacenter.ts b/app/src/router/routes/modules/datacenter.ts
new file mode 100644
index 0000000..8de210a
--- /dev/null
+++ b/app/src/router/routes/modules/datacenter.ts
@@ -0,0 +1,20 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ meta: {
+ icon: 'clarity:database',
+ title: $t('datacenterTable.title'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ },
+ name: 'TableDetail',
+ path: '/datacenter/table/tableDetail',
+ component: () => import('#/views/datacenter/DatacenterTableDetail.vue'),
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/demos.ts b/app/src/router/routes/modules/demos.ts
new file mode 100644
index 0000000..4400576
--- /dev/null
+++ b/app/src/router/routes/modules/demos.ts
@@ -0,0 +1,52 @@
+// import type { RouteRecordRaw } from 'vue-router';
+//
+// import { $t } from '#/locales';
+//
+// const routes: RouteRecordRaw[] = [
+// {
+// meta: {
+// icon: 'ic:baseline-view-in-ar',
+// keepAlive: true,
+// order: 1000,
+// title: $t('demos.title'),
+// },
+// name: 'Demos',
+// path: '/demos',
+// children: [
+// {
+// meta: {
+// title: $t('demos.elementPlus'),
+// },
+// name: 'NaiveDemos',
+// path: '/demos/element',
+// component: () => import('#/views/demos/element/index.vue'),
+// },
+// {
+// meta: {
+// title: '卡片组件',
+// },
+// name: 'NaiveDemos1',
+// path: '/demos/cardTest',
+// component: () => import('#/views/demos/cardTest/index.vue'),
+// },
+// {
+// meta: {
+// title: '分类组件',
+// },
+// name: 'NaiveDemos2',
+// path: '/demos/categoryPanel',
+// component: () => import('#/views/demos/categoryPanel/index.vue'),
+// },
+// {
+// meta: {
+// title: $t('demos.form'),
+// },
+// name: 'BasicForm',
+// path: '/demos/form',
+// component: () => import('#/views/demos/form/basic.vue'),
+// },
+// ],
+// },
+// ];
+//
+// export default routes;
diff --git a/app/src/router/routes/modules/document.ts b/app/src/router/routes/modules/document.ts
new file mode 100644
index 0000000..abe9be5
--- /dev/null
+++ b/app/src/router/routes/modules/document.ts
@@ -0,0 +1,21 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ meta: {
+ title: $t('documentCollection.documentManagement'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ fullPathKey: true,
+ activePath: '/ai/documentCollection',
+ },
+ name: 'Document',
+ path: '/ai/documentCollection/document',
+ component: () => import('#/views/ai/documentCollection/Document.vue'),
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/plugins.ts b/app/src/router/routes/modules/plugins.ts
new file mode 100644
index 0000000..49650ea
--- /dev/null
+++ b/app/src/router/routes/modules/plugins.ts
@@ -0,0 +1,32 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ meta: {
+ title: $t('plugin.toolsManagement'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ fullPathKey: true,
+ },
+ name: 'PluginTools',
+ path: '/ai/plugin/tools',
+ component: () => import('#/views/ai/plugin/PluginTools.vue'),
+ },
+ {
+ meta: {
+ title: $t('plugin.toolsManagement'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ fullPathKey: true,
+ },
+ name: 'PluginToolEdit',
+ path: '/ai/plugin/tool/edit',
+ component: () => import('#/views/ai/plugin/PluginToolEdit.vue'),
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/sysFeedback.ts b/app/src/router/routes/modules/sysFeedback.ts
new file mode 100644
index 0000000..720ca1e
--- /dev/null
+++ b/app/src/router/routes/modules/sysFeedback.ts
@@ -0,0 +1,20 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ name: 'SysFeedbackDetail',
+ path: '/sys/sysFeedback/:id',
+ component: () => import('#/views/system/sysFeedback/sysFeedbackDetail.vue'),
+ meta: {
+ title: $t('menus.system.sysFeedback'),
+ hideInMenu: true,
+ hideInBreadcrumb: true,
+ hideInTab: true,
+ activePath: '/sys/sysFeedback',
+ },
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/sysJob.ts b/app/src/router/routes/modules/sysJob.ts
new file mode 100644
index 0000000..a293590
--- /dev/null
+++ b/app/src/router/routes/modules/sysJob.ts
@@ -0,0 +1,20 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ meta: {
+ icon: 'clarity:time-line',
+ title: $t('sysJobLog.title'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ },
+ name: 'SysJobLog',
+ path: '/sys/sysJob/sysJobLog',
+ component: () => import('#/views/system/sysJob/SysJobLogList.vue'),
+ },
+];
+
+export default routes;
diff --git a/app/src/router/routes/modules/workflow.ts b/app/src/router/routes/modules/workflow.ts
new file mode 100644
index 0000000..78168dd
--- /dev/null
+++ b/app/src/router/routes/modules/workflow.ts
@@ -0,0 +1,55 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ meta: {
+ icon: 'ant-design:apartment-outlined',
+ title: $t('datacenterTable.title'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ },
+ name: 'WorkflowDesign',
+ path: '/ai/workflow/design',
+ component: () => import('#/views/ai/workflow/WorkflowDesign.vue'),
+ },
+ {
+ meta: {
+ title: '运行',
+ openInNewWindow: true,
+ noBasicLayout: true,
+ hideInMenu: true,
+ },
+ name: 'RunPage',
+ path: '/ai/workflow/run',
+ component: () => import('#/views/ai/workflow/RunPage.vue'),
+ },
+ {
+ meta: {
+ title: $t('aiWorkflowExecRecord.moduleName'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ },
+ name: 'ExecRecord',
+ path: '/ai/workflow/executeRecords',
+ component: () =>
+ import('#/views/ai/workflow/execute/WorkflowExecResultList.vue'),
+ },
+ {
+ meta: {
+ title: $t('aiWorkflowRecordStep.moduleName'),
+ hideInMenu: true,
+ hideInTab: true,
+ hideInBreadcrumb: true,
+ },
+ name: 'RecordStep',
+ path: '/ai/workflow/executeSteps',
+ component: () =>
+ import('#/views/ai/workflow/execute/WorkflowExecStepList.vue'),
+ },
+];
+
+export default routes;
diff --git a/app/src/shims-vue.d.ts b/app/src/shims-vue.d.ts
new file mode 100644
index 0000000..7c32d10
--- /dev/null
+++ b/app/src/shims-vue.d.ts
@@ -0,0 +1,17 @@
+declare module '#/components/page/PageData.vue' {
+ import type { DefineComponent } from 'vue';
+
+ interface PageDataSlots {
+ default: (props: { pageList: any[] }) => any;
+ }
+
+ const component: DefineComponent