feat: update eslint & prettier rules

This commit is contained in:
HYana
2025-04-30 17:36:46 +08:00
parent d4af2d4326
commit 460e065eed
53 changed files with 2241 additions and 2102 deletions
-1
View File
@@ -1,3 +1,2 @@
cd web cd web
pnpm lint-staged pnpm lint-staged
pnpm test
+2 -2
View File
@@ -1,3 +1,3 @@
{ {
"*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix", "eslint"] "*.{js,jsx,ts,tsx}": ["eslint --fix", "eslint"]
} }
+15 -2
View File
@@ -3,7 +3,20 @@
* @type {import("prettier").Config} * @type {import("prettier").Config}
*/ */
const config = { const config = {
trailingComma: "none", // 单行长度
printWidth: 80,
// 缩进
tabWidth: 2,
// 使用空格代替tab缩进
useTabs: false,
// 句末使用分号
semi: true,
// 使用单引号
singleQuote: true,
// 大括号前后空格
bracketSpacing: true,
attributeVerticalAlignment: 'auto',
trailingComma: 'all',
}; };
export default config; export default config;
+6 -4
View File
@@ -1,6 +1,7 @@
import { dirname } from "path"; import { dirname } from 'path';
import { fileURLToPath } from "url"; import { fileURLToPath } from 'url';
import { FlatCompat } from "@eslint/eslintrc"; import { FlatCompat } from '@eslint/eslintrc';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@@ -10,7 +11,8 @@ const compat = new FlatCompat({
}); });
const eslintConfig = [ const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"), ...compat.extends('next/core-web-vitals', 'next/typescript'),
eslintPluginPrettierRecommended,
]; ];
export default eslintConfig; export default eslintConfig;
+2 -2
View File
@@ -1,8 +1,8 @@
import type { NextConfig } from "next"; import type { NextConfig } from 'next';
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ /* config options here */
output: 'export' output: 'export',
}; };
export default nextConfig; export default nextConfig;
+159 -46
View File
@@ -25,10 +25,13 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.2.4", "eslint-config-next": "15.2.4",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.6",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.1", "lint-staged": "^15.5.1",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"typescript": "^5" "typescript": "^5.8.3",
"typescript-eslint": "^8.31.1"
} }
}, },
"node_modules/@ant-design/colors": { "node_modules/@ant-design/colors": {
@@ -906,6 +909,18 @@
"node": ">=12.4.0" "node": ">=12.4.0"
} }
}, },
"node_modules/@pkgr/core": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/pkgr"
}
},
"node_modules/@rc-component/async-validator": { "node_modules/@rc-component/async-validator": {
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz", "resolved": "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
@@ -1133,16 +1148,16 @@
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz",
"integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==", "integrity": "sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.28.0", "@typescript-eslint/scope-manager": "8.31.1",
"@typescript-eslint/type-utils": "8.28.0", "@typescript-eslint/type-utils": "8.31.1",
"@typescript-eslint/utils": "8.28.0", "@typescript-eslint/utils": "8.31.1",
"@typescript-eslint/visitor-keys": "8.28.0", "@typescript-eslint/visitor-keys": "8.31.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.3.1", "ignore": "^5.3.1",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@@ -1162,15 +1177,15 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.1.tgz",
"integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", "integrity": "sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.28.0", "@typescript-eslint/scope-manager": "8.31.1",
"@typescript-eslint/types": "8.28.0", "@typescript-eslint/types": "8.31.1",
"@typescript-eslint/typescript-estree": "8.28.0", "@typescript-eslint/typescript-estree": "8.31.1",
"@typescript-eslint/visitor-keys": "8.28.0", "@typescript-eslint/visitor-keys": "8.31.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@@ -1186,13 +1201,13 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz",
"integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", "integrity": "sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.28.0", "@typescript-eslint/types": "8.31.1",
"@typescript-eslint/visitor-keys": "8.28.0" "@typescript-eslint/visitor-keys": "8.31.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1203,13 +1218,13 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz",
"integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==", "integrity": "sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.28.0", "@typescript-eslint/typescript-estree": "8.31.1",
"@typescript-eslint/utils": "8.28.0", "@typescript-eslint/utils": "8.31.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.0.1" "ts-api-utils": "^2.0.1"
}, },
@@ -1226,9 +1241,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.1.tgz",
"integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", "integrity": "sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1239,13 +1254,13 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz",
"integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", "integrity": "sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.28.0", "@typescript-eslint/types": "8.31.1",
"@typescript-eslint/visitor-keys": "8.28.0", "@typescript-eslint/visitor-keys": "8.31.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@@ -1317,15 +1332,15 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.1.tgz",
"integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", "integrity": "sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.28.0", "@typescript-eslint/scope-manager": "8.31.1",
"@typescript-eslint/types": "8.28.0", "@typescript-eslint/types": "8.31.1",
"@typescript-eslint/typescript-estree": "8.28.0" "@typescript-eslint/typescript-estree": "8.31.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1340,12 +1355,12 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.28.0", "version": "8.31.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz",
"integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", "integrity": "sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.28.0", "@typescript-eslint/types": "8.31.1",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.0"
}, },
"engines": { "engines": {
@@ -2640,6 +2655,18 @@
} }
} }
}, },
"node_modules/eslint-config-prettier": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz",
"integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
"version": "0.3.9", "version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -2800,6 +2827,36 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
} }
}, },
"node_modules/eslint-plugin-prettier": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz",
"integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.11.0"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.37.4", "version": "7.37.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz",
@@ -2992,6 +3049,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -4715,6 +4778,18 @@
"url": "https://github.com/prettier/prettier?sponsor=1" "url": "https://github.com/prettier/prettier?sponsor=1"
} }
}, },
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -6117,6 +6192,22 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/synckit": {
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz",
"integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==",
"dev": true,
"dependencies": {
"@pkgr/core": "^0.2.3",
"tslib": "^2.8.1"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/synckit"
}
},
"node_modules/throttle-debounce": { "node_modules/throttle-debounce": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz", "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
@@ -6300,9 +6391,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.8.2", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true, "dev": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
@@ -6312,6 +6403,28 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/typescript-eslint": {
"version": "8.31.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.1.tgz",
"integrity": "sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==",
"dev": true,
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.31.1",
"@typescript-eslint/parser": "8.31.1",
"@typescript-eslint/utils": "8.31.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+4 -1
View File
@@ -28,9 +28,12 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.2.4", "eslint-config-next": "15.2.4",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.6",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.1", "lint-staged": "^15.5.1",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"typescript": "^5" "typescript": "^5.8.3",
"typescript-eslint": "^8.31.1"
} }
} }
@@ -1,39 +1,33 @@
import {BotCardVO} from "@/app/home/bots/components/bot-card/BotCardVO"; import { BotCardVO } from '@/app/home/bots/components/bot-card/BotCardVO';
import styles from "./botCard.module.css"; import styles from './botCard.module.css';
export default function BotCard({ export default function BotCard({ botCardVO }: { botCardVO: BotCardVO }) {
botCardVO return (
}: { <div className={`${styles.cardContainer}`}>
botCardVO: BotCardVO; {/* icon和基本信息 */}
}) { <div className={`${styles.iconBasicInfoContainer}`}>
return ( {/* icon */}
<div className={`${styles.cardContainer}`}> <div className={`${styles.icon}`}>ICO</div>
{/* icon和基本信息 */} {/* bot基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}> <div className={`${styles.basicInfoContainer}`}>
{/* icon */} <div className={`${styles.basicInfoText} ${styles.bigText}`}>
<div className={`${styles.icon}`}> {botCardVO.name}
ICO </div>
</div> <div className={`${styles.basicInfoText}`}>
{/* bot基本信息 */} {botCardVO.adapter}
<div className={`${styles.basicInfoContainer}`}> </div>
<div className={`${styles.basicInfoText} ${styles.bigText}`}> <div className={`${styles.basicInfoText}`}>
{botCardVO.name} 线{botCardVO.pipelineName}
</div> </div>
<div className={`${styles.basicInfoText}`}> </div>
{botCardVO.adapter} </div>
</div> {/* 描述和创建时间 */}
<div className={`${styles.basicInfoText}`}> <div className={`${styles.urlAndUpdateText}`}>
线{botCardVO.pipelineName} {botCardVO.description}
</div> </div>
</div> {/* <div className={`${styles.urlAndUpdateText}`}>
</div>
{/* 描述和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
{botCardVO.description}
</div>
{/* <div className={`${styles.urlAndUpdateText}`}>
更新时间:{botCardVO.updateTime} 更新时间:{botCardVO.updateTime}
</div> */} </div> */}
</div> </div>
) );
} }
@@ -1,28 +1,26 @@
export interface IBotCardVO { export interface IBotCardVO {
id: string; id: string;
name: string; name: string;
adapter: string; adapter: string;
description: string; description: string;
updateTime: string; updateTime: string;
pipelineName: string; pipelineName: string;
} }
export class BotCardVO implements IBotCardVO { export class BotCardVO implements IBotCardVO {
id: string; id: string;
adapter: string; adapter: string;
description: string; description: string;
name: string; name: string;
updateTime: string; updateTime: string;
pipelineName: string; pipelineName: string;
constructor(props: IBotCardVO) {
constructor(props: IBotCardVO) { this.id = props.id;
this.id = props.id; this.name = props.name;
this.name = props.name; this.adapter = props.adapter;
this.adapter = props.adapter; this.description = props.description;
this.description = props.description; this.updateTime = props.updateTime;
this.updateTime = props.updateTime; this.pipelineName = props.pipelineName;
this.pipelineName = props.pipelineName; }
} }
}
@@ -1,24 +1,24 @@
import { import {
BotFormEntity, BotFormEntity,
IBotFormEntity IBotFormEntity,
} from "@/app/home/bots/components/bot-form/BotFormEntity"; } from '@/app/home/bots/components/bot-form/BotFormEntity';
import { Button, Form, Input, notification, Select, Space } from "antd"; import { Button, Form, Input, notification, Select, Space } from 'antd';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { IChooseAdapterEntity } from "@/app/home/bots/components/bot-form/ChooseAdapterEntity"; import { IChooseAdapterEntity } from '@/app/home/bots/components/bot-form/ChooseAdapterEntity';
import { import {
DynamicFormItemConfig, DynamicFormItemConfig,
IDynamicFormItemConfig, IDynamicFormItemConfig,
parseDynamicFormItemType parseDynamicFormItemType,
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
import { UUID } from "uuidjs"; import { UUID } from 'uuidjs';
import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent"; import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { Bot } from "@/app/infra/api/api-types"; import { Bot } from '@/app/infra/api/api-types';
export default function BotForm({ export default function BotForm({
initBotId, initBotId,
onFormSubmit, onFormSubmit,
onFormCancel onFormCancel,
}: { }: {
initBotId?: string; initBotId?: string;
onFormSubmit: (value: IBotFormEntity) => void; onFormSubmit: (value: IBotFormEntity) => void;
@@ -55,9 +55,9 @@ export default function BotForm({
rawAdapterList.adapters.map((item) => { rawAdapterList.adapters.map((item) => {
return { return {
label: item.label.zh_CN, label: item.label.zh_CN,
value: item.name value: item.name,
}; };
}) }),
); );
// 初始化适配器表单map // 初始化适配器表单map
rawAdapterList.adapters.forEach((rawAdapter) => { rawAdapterList.adapters.forEach((rawAdapter) => {
@@ -71,9 +71,9 @@ export default function BotForm({
label: item.label, label: item.label,
name: item.name, name: item.name,
required: item.required, required: item.required,
type: parseDynamicFormItemType(item.type) type: parseDynamicFormItemType(item.type),
}) }),
) ),
); );
}); });
// 拉取初始化表单信息 // 拉取初始化表单信息
@@ -99,12 +99,12 @@ export default function BotForm({
adapter: bot.adapter, adapter: bot.adapter,
description: bot.description, description: bot.description,
name: bot.name, name: bot.name,
adapter_config: bot.adapter_config adapter_config: bot.adapter_config,
}); });
} }
function handleAdapterSelect(adapterName: string) { function handleAdapterSelect(adapterName: string) {
console.log("Select adapter: ", adapterName); console.log('Select adapter: ', adapterName);
if (adapterName) { if (adapterName) {
const dynamicFormConfigList = const dynamicFormConfigList =
adapterNameToDynamicConfigMap.get(adapterName); adapterNameToDynamicConfigMap.get(adapterName);
@@ -129,33 +129,33 @@ export default function BotForm({
// 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里
function onDynamicFormSubmit(value: object) { function onDynamicFormSubmit(value: object) {
setIsLoading(true); setIsLoading(true);
console.log("set loading", true); console.log('set loading', true);
if (initBotId) { if (initBotId) {
// 编辑提交 // 编辑提交
console.log("submit edit", form.getFieldsValue(), value); console.log('submit edit', form.getFieldsValue(), value);
const updateBot: Bot = { const updateBot: Bot = {
uuid: initBotId, uuid: initBotId,
name: form.getFieldsValue().name, name: form.getFieldsValue().name,
description: form.getFieldsValue().description, description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter, adapter: form.getFieldsValue().adapter,
adapter_config: value adapter_config: value,
}; };
httpClient httpClient
.updateBot(initBotId, updateBot) .updateBot(initBotId, updateBot)
.then((res) => { .then((res) => {
// TODO success toast // TODO success toast
console.log("update bot success", res); console.log('update bot success', res);
onFormSubmit(form.getFieldsValue()); onFormSubmit(form.getFieldsValue());
notification.success({ notification.success({
message: "更新成功", message: '更新成功',
description: "机器人更新成功" description: '机器人更新成功',
}); });
}) })
.catch(() => { .catch(() => {
// TODO error toast // TODO error toast
notification.error({ notification.error({
message: "更新失败", message: '更新失败',
description: "机器人更新失败" description: '机器人更新失败',
}); });
}) })
.finally(() => { .finally(() => {
@@ -165,20 +165,20 @@ export default function BotForm({
}); });
} else { } else {
// 创建提交 // 创建提交
console.log("submit create", form.getFieldsValue(), value); console.log('submit create', form.getFieldsValue(), value);
const newBot: Bot = { const newBot: Bot = {
name: form.getFieldsValue().name, name: form.getFieldsValue().name,
description: form.getFieldsValue().description, description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter, adapter: form.getFieldsValue().adapter,
adapter_config: value adapter_config: value,
}; };
httpClient httpClient
.createBot(newBot) .createBot(newBot)
.then((res) => { .then((res) => {
// TODO success toast // TODO success toast
notification.success({ notification.success({
message: "创建成功", message: '创建成功',
description: "机器人创建成功" description: '机器人创建成功',
}); });
console.log(res); console.log(res);
onFormSubmit(form.getFieldsValue()); onFormSubmit(form.getFieldsValue());
@@ -186,8 +186,8 @@ export default function BotForm({
.catch(() => { .catch(() => {
// TODO error toast // TODO error toast
notification.error({ notification.error({
message: "创建失败", message: '创建失败',
description: "机器人创建失败" description: '机器人创建失败',
}); });
}) })
.finally(() => { .finally(() => {
@@ -197,7 +197,7 @@ export default function BotForm({
}); });
} }
setShowDynamicForm(false); setShowDynamicForm(false);
console.log("set loading", false); console.log('set loading', false);
// TODO 刷新bot列表 // TODO 刷新bot列表
// TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)? // TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)?
} }
@@ -217,9 +217,9 @@ export default function BotForm({
disabled={isLoading} disabled={isLoading}
> >
<Form.Item<IBotFormEntity> <Form.Item<IBotFormEntity>
label={"机器人名称"} label={'机器人名称'}
name={"name"} name={'name'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Input <Input
placeholder="为机器人取个好听的名字吧~" placeholder="为机器人取个好听的名字吧~"
@@ -228,17 +228,17 @@ export default function BotForm({
</Form.Item> </Form.Item>
<Form.Item<IBotFormEntity> <Form.Item<IBotFormEntity>
label={"描述"} label={'描述'}
name={"description"} name={'description'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Input placeholder="简单描述一下这个机器人"></Input> <Input placeholder="简单描述一下这个机器人"></Input>
</Form.Item> </Form.Item>
<Form.Item<IBotFormEntity> <Form.Item<IBotFormEntity>
label={"平台/适配器选择"} label={'平台/适配器选择'}
name={"adapter"} name={'adapter'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Select <Select
style={{ width: 220 }} style={{ width: 220 }}
@@ -1,20 +1,20 @@
export interface IBotFormEntity { export interface IBotFormEntity {
name: string, name: string;
description: string, description: string;
adapter: string, adapter: string;
adapter_config: object; adapter_config: object;
} }
export class BotFormEntity implements IBotFormEntity { export class BotFormEntity implements IBotFormEntity {
adapter: string; adapter: string;
description: string; description: string;
name: string; name: string;
adapter_config: object; adapter_config: object;
constructor(props: IBotFormEntity) { constructor(props: IBotFormEntity) {
this.adapter = props.adapter; this.adapter = props.adapter;
this.description = props.description; this.description = props.description;
this.name = props.name; this.name = props.name;
this.adapter_config = props.adapter_config; this.adapter_config = props.adapter_config;
} }
} }
@@ -1,4 +1,4 @@
export interface IChooseAdapterEntity { export interface IChooseAdapterEntity {
label: string label: string;
value: string value: string;
} }
+29 -29
View File
@@ -1,21 +1,21 @@
"use client"; 'use client';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import styles from "./botConfig.module.css"; import styles from './botConfig.module.css';
import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent"; import EmptyAndCreateComponent from '@/app/home/components/empty-and-create-component/EmptyAndCreateComponent';
import { useRouter } from "next/navigation"; import { useRouter } from 'next/navigation';
import { BotCardVO } from "@/app/home/bots/components/bot-card/BotCardVO"; import { BotCardVO } from '@/app/home/bots/components/bot-card/BotCardVO';
import { Modal, notification, Spin } from "antd"; import { Modal, notification, Spin } from 'antd';
import BotForm from "@/app/home/bots/components/bot-form/BotForm"; import BotForm from '@/app/home/bots/components/bot-form/BotForm';
import BotCard from "@/app/home/bots/components/bot-card/BotCard"; import BotCard from '@/app/home/bots/components/bot-card/BotCard';
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { Bot } from "@/app/infra/api/api-types"; import { Bot } from '@/app/infra/api/api-types';
export default function BotConfigPage() { export default function BotConfigPage() {
const router = useRouter(); const router = useRouter();
const [pageShowRule, setPageShowRule] = useState<BotConfigPageShowRule>( const [pageShowRule, setPageShowRule] = useState<BotConfigPageShowRule>(
BotConfigPageShowRule.NO_BOT BotConfigPageShowRule.NO_BOT,
); );
const [modalOpen, setModalOpen] = useState<boolean>(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [botList, setBotList] = useState<BotCardVO[]>([]); const [botList, setBotList] = useState<BotCardVO[]>([]);
@@ -49,10 +49,10 @@ export default function BotConfigPage() {
return new BotCardVO({ return new BotCardVO({
adapter: bot.adapter, adapter: bot.adapter,
description: bot.description, description: bot.description,
id: bot.uuid || "", id: bot.uuid || '',
name: bot.name, name: bot.name,
updateTime: bot.updated_at || "", updateTime: bot.updated_at || '',
pipelineName: bot.use_pipeline_name || "" pipelineName: bot.use_pipeline_name || '',
}); });
}); });
if (botList.length === 0) { if (botList.length === 0) {
@@ -63,12 +63,12 @@ export default function BotConfigPage() {
setBotList(botList); setBotList(botList);
}) })
.catch((err) => { .catch((err) => {
console.error("get bot list error", err); console.error('get bot list error', err);
// TODO HACK: need refactor to hook mode Notification, but it's not working under render // TODO HACK: need refactor to hook mode Notification, but it's not working under render
notification.error({ notification.error({
message: "获取机器人列表失败", message: '获取机器人列表失败',
description: err.message, description: err.message,
placement: "bottomRight" placement: 'bottomRight',
}); });
}) })
.finally(() => { .finally(() => {
@@ -89,7 +89,7 @@ export default function BotConfigPage() {
function selectBot(cardVO: BotCardVO) { function selectBot(cardVO: BotCardVO) {
setIsEditForm(true); setIsEditForm(true);
setNowSelectedCard(cardVO); setNowSelectedCard(cardVO);
console.log("set now vo", cardVO); console.log('set now vo', cardVO);
setModalOpen(true); setModalOpen(true);
} }
@@ -97,7 +97,7 @@ export default function BotConfigPage() {
<div className={styles.configPageContainer}> <div className={styles.configPageContainer}>
<Spin spinning={isLoading} tip="加载中..." size="large"> <Spin spinning={isLoading} tip="加载中..." size="large">
<Modal <Modal
title={isEditForm ? "编辑机器人" : "创建机器人"} title={isEditForm ? '编辑机器人' : '创建机器人'}
centered centered
open={modalOpen} open={modalOpen}
onOk={() => setModalOpen(false)} onOk={() => setModalOpen(false)}
@@ -117,20 +117,20 @@ export default function BotConfigPage() {
</Modal> </Modal>
{pageShowRule === BotConfigPageShowRule.NO_LLM && ( {pageShowRule === BotConfigPageShowRule.NO_LLM && (
<EmptyAndCreateComponent <EmptyAndCreateComponent
title={"需要先创建大模型才能配置机器人哦~"} title={'需要先创建大模型才能配置机器人哦~'}
subTitle={"快去创建一个吧!"} subTitle={'快去创建一个吧!'}
buttonText={"创建大模型 GO"} buttonText={'创建大模型 GO'}
onButtonClick={() => { onButtonClick={() => {
router.push("/home/models"); router.push('/home/models');
}} }}
/> />
)} )}
{pageShowRule === BotConfigPageShowRule.NO_BOT && ( {pageShowRule === BotConfigPageShowRule.NO_BOT && (
<EmptyAndCreateComponent <EmptyAndCreateComponent
title={"您还未配置机器人哦~"} title={'您还未配置机器人哦~'}
subTitle={"快去创建一个吧!"} subTitle={'快去创建一个吧!'}
buttonText={"创建机器人 +"} buttonText={'创建机器人 +'}
onButtonClick={handleCreateBotClick} onButtonClick={handleCreateBotClick}
/> />
)} )}
@@ -164,5 +164,5 @@ export default function BotConfigPage() {
enum BotConfigPageShowRule { enum BotConfigPageShowRule {
NO_LLM, NO_LLM,
NO_BOT, NO_BOT,
HAVE_BOT HAVE_BOT,
} }
@@ -1,30 +1,21 @@
import {IDynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; import { IDynamicFormItemConfig } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
import {Form, FormInstance} from "antd"; import { Form, FormInstance } from 'antd';
import DynamicFormItemComponent from "@/app/home/components/dynamic-form/DynamicFormItemComponent"; import DynamicFormItemComponent from '@/app/home/components/dynamic-form/DynamicFormItemComponent';
export default function DynamicFormComponent({ export default function DynamicFormComponent({
form, form,
itemConfigList, itemConfigList,
onSubmit, onSubmit,
}: { }: {
form: FormInstance<object> form: FormInstance<object>;
itemConfigList: IDynamicFormItemConfig[] itemConfigList: IDynamicFormItemConfig[];
onSubmit?: (val: object) => unknown onSubmit?: (val: object) => unknown;
}) { }) {
return ( return (
<Form <Form form={form} onFinish={onSubmit} layout={'vertical'}>
form={form} {itemConfigList.map((config) => (
onFinish={onSubmit} <DynamicFormItemComponent key={config.id} config={config} />
layout={"vertical"} ))}
> </Form>
{ );
itemConfigList.map(config => }
<DynamicFormItemComponent
key={config.id}
config={config}
/>
)
}
</Form>
)
}
@@ -1,38 +1,30 @@
import {Form, Input, InputNumber, Select, Switch} from "antd"; import { Form, Input, InputNumber, Select, Switch } from 'antd';
import {DynamicFormItemType, IDynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; import {
DynamicFormItemType,
IDynamicFormItemConfig,
} from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export default function DynamicFormItemComponent({ export default function DynamicFormItemComponent({
config config,
}: { }: {
config: IDynamicFormItemConfig config: IDynamicFormItemConfig;
}) { }) {
return ( return (
<Form.Item <Form.Item
label={config.label.zh_CN} label={config.label.zh_CN}
name={config.name} name={config.name}
rules={[{required: config.required, message: "该项为必填项哦~"}]} rules={[{ required: config.required, message: '该项为必填项哦~' }]}
initialValue={config.default} initialValue={config.default}
> >
{ {config.type === DynamicFormItemType.INT && <InputNumber />}
config.type === DynamicFormItemType.INT &&
<InputNumber/>
}
{ {config.type === DynamicFormItemType.STRING && <Input />}
config.type === DynamicFormItemType.STRING &&
<Input/>
}
{ {config.type === DynamicFormItemType.BOOLEAN && <Switch defaultChecked />}
config.type === DynamicFormItemType.BOOLEAN &&
<Switch defaultChecked/>
}
{ {config.type === DynamicFormItemType.STRING_ARRAY && (
config.type === DynamicFormItemType.STRING_ARRAY && <Select options={[]} />
<Select options={[]}/> )}
} </Form.Item>
</Form.Item> );
) }
}
@@ -1,51 +1,54 @@
export interface IDynamicFormItemConfig { export interface IDynamicFormItemConfig {
id: string; id: string;
default: string | number | boolean | Array<unknown>; default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel; label: IDynamicFormItemLabel;
name: string; name: string;
required: boolean; required: boolean;
type: DynamicFormItemType type: DynamicFormItemType;
description?: IDynamicFormItemLabel; description?: IDynamicFormItemLabel;
} }
export class DynamicFormItemConfig implements IDynamicFormItemConfig { export class DynamicFormItemConfig implements IDynamicFormItemConfig {
id: string; id: string;
name: string; name: string;
default: string | number | boolean | Array<unknown>; default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel; label: IDynamicFormItemLabel;
required: boolean; required: boolean;
type: DynamicFormItemType; type: DynamicFormItemType;
description?: IDynamicFormItemLabel; description?: IDynamicFormItemLabel;
constructor(params: IDynamicFormItemConfig) {
this.id = params.id;
this.name = params.name;
this.default = params.default;
this.label = params.label;
this.required = params.required;
this.type = params.type;
this.description = params.description;
}
constructor(params: IDynamicFormItemConfig) {
this.id = params.id;
this.name = params.name;
this.default = params.default;
this.label = params.label;
this.required = params.required;
this.type = params.type;
this.description = params.description;
}
} }
export interface IDynamicFormItemLabel { export interface IDynamicFormItemLabel {
en_US: string, en_US: string;
zh_CN: string, zh_CN: string;
} }
export enum DynamicFormItemType { export enum DynamicFormItemType {
INT = "integer", INT = 'integer',
STRING = "string", STRING = 'string',
BOOLEAN = "boolean", BOOLEAN = 'boolean',
STRING_ARRAY = "array[string]", STRING_ARRAY = 'array[string]',
UNKNOWN = "unknown", UNKNOWN = 'unknown',
} }
export function isDynamicFormItemType(value: string): value is DynamicFormItemType { export function isDynamicFormItemType(
return Object.values(DynamicFormItemType).includes(value as DynamicFormItemType); value: string,
): value is DynamicFormItemType {
return Object.values(DynamicFormItemType).includes(
value as DynamicFormItemType,
);
} }
export function parseDynamicFormItemType(value: string): DynamicFormItemType { export function parseDynamicFormItemType(value: string): DynamicFormItemType {
return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN; return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN;
} }
@@ -1,41 +1,41 @@
import { import {
DynamicFormItemConfig, DynamicFormItemConfig,
DynamicFormItemType, DynamicFormItemType,
IDynamicFormItemConfig IDynamicFormItemConfig,
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export const testDynamicConfigList: IDynamicFormItemConfig[] = [ export const testDynamicConfigList: IDynamicFormItemConfig[] = [
new DynamicFormItemConfig({ new DynamicFormItemConfig({
default: "", default: '',
id: "111", id: '111',
label: { label: {
zh_CN: "测试字段string", zh_CN: '测试字段string',
en_US: "eng test" en_US: 'eng test',
}, },
name: "string_test", name: 'string_test',
required: false, required: false,
type: DynamicFormItemType.STRING type: DynamicFormItemType.STRING,
}), }),
new DynamicFormItemConfig({ new DynamicFormItemConfig({
default: "", default: '',
id: "222", id: '222',
label: { label: {
zh_CN: "测试字段int", zh_CN: '测试字段int',
en_US: "int eng test" en_US: 'int eng test',
}, },
name: "int_test", name: 'int_test',
required: true, required: true,
type: DynamicFormItemType.INT type: DynamicFormItemType.INT,
}), }),
new DynamicFormItemConfig({ new DynamicFormItemConfig({
default: "", default: '',
id: "333", id: '333',
label: { label: {
zh_CN: "测试字段boolean", zh_CN: '测试字段boolean',
en_US: "boolean eng test" en_US: 'boolean eng test',
}, },
name: "boolean_test", name: 'boolean_test',
required: false, required: false,
type: DynamicFormItemType.BOOLEAN type: DynamicFormItemType.BOOLEAN,
}), }),
] ];
@@ -1,35 +1,27 @@
import styles from "./emptyAndCreate.module.css"; import styles from './emptyAndCreate.module.css';
export default function EmptyAndCreateComponent({ export default function EmptyAndCreateComponent({
title, title,
subTitle, subTitle,
buttonText, buttonText,
onButtonClick, onButtonClick,
}: { }: {
title: string, title: string;
subTitle: string, subTitle: string;
buttonText: string, buttonText: string;
onButtonClick: () => void, onButtonClick: () => void;
}) { }) {
return (
return ( <div className={`${styles.emptyPageContainer}`}>
<div className={`${styles.emptyPageContainer}`}> <div className={`${styles.emptyContainer}`}>
<div className={`${styles.emptyContainer}`}> <div className={`${styles.emptyInfoContainer}`}>
<div className={`${styles.emptyInfoContainer}`}> <div className={`${styles.emptyInfoText}`}>{title}</div>
<div className={`${styles.emptyInfoText}`}> <div className={`${styles.emptyInfoSubText}`}>{subTitle}</div>
{title}
</div>
<div className={`${styles.emptyInfoSubText}`}>
{subTitle}
</div>
</div>
<div
className={`${styles.emptyCreateButton}`}
onClick={onButtonClick}
>
{buttonText}
</div>
</div>
</div> </div>
) <div className={`${styles.emptyCreateButton}`} onClick={onButtonClick}>
} {buttonText}
</div>
</div>
</div>
);
}
@@ -1,17 +1,17 @@
"use client"; 'use client';
import styles from "./HomeSidebar.module.css"; import styles from './HomeSidebar.module.css';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { import {
SidebarChild, SidebarChild,
SidebarChildVO SidebarChildVO,
} from "@/app/home/components/home-sidebar/HomeSidebarChild"; } from '@/app/home/components/home-sidebar/HomeSidebarChild';
import { useRouter, usePathname } from "next/navigation"; import { useRouter, usePathname } from 'next/navigation';
import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList"; import { sidebarConfigList } from '@/app/home/components/home-sidebar/sidbarConfigList';
// TODO 侧边导航栏要加动画 // TODO 侧边导航栏要加动画
export default function HomeSidebar({ export default function HomeSidebar({
onSelectedChangeAction onSelectedChangeAction,
}: { }: {
onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void; onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
}) { }) {
@@ -24,13 +24,13 @@ export default function HomeSidebar({
}, [pathname]); }, [pathname]);
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>( const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(
sidebarConfigList[0] sidebarConfigList[0],
); );
useEffect(() => { useEffect(() => {
console.log("HomeSidebar挂载完成"); console.log('HomeSidebar挂载完成');
initSelect(); initSelect();
return () => console.log("HomeSidebar卸载"); return () => console.log('HomeSidebar卸载');
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@@ -52,14 +52,14 @@ export default function HomeSidebar({
function handleRouteChange(pathname: string) { function handleRouteChange(pathname: string) {
// TODO 这段逻辑并不好,未来router封装好后改掉 // TODO 这段逻辑并不好,未来router封装好后改掉
// 判断在home下,并且路由更改的是自己的路由子组件则更新UI // 判断在home下,并且路由更改的是自己的路由子组件则更新UI
const routeList = pathname.split("/"); const routeList = pathname.split('/');
if ( if (
routeList[1] === "home" && routeList[1] === 'home' &&
sidebarConfigList.find((childConfig) => childConfig.route === pathname) sidebarConfigList.find((childConfig) => childConfig.route === pathname)
) { ) {
console.log("find success"); console.log('find success');
const routeSelectChild = sidebarConfigList.find( const routeSelectChild = sidebarConfigList.find(
(childConfig) => childConfig.route === pathname (childConfig) => childConfig.route === pathname,
); );
if (routeSelectChild) { if (routeSelectChild) {
setSelectedChild(routeSelectChild); setSelectedChild(routeSelectChild);
@@ -67,42 +67,34 @@ export default function HomeSidebar({
} }
} }
return (
return ( <div className={`${styles.sidebarContainer}`}>
<div className={`${styles.sidebarContainer}`}> {/* LangBot、ICON区域 */}
{/* LangBot、ICON区域 */} <div className={`${styles.langbotIconContainer}`}>
<div className={`${styles.langbotIconContainer}`}> {/* icon */}
{/* icon */} <div className={`${styles.langbotIcon}`}>L</div>
<div className={`${styles.langbotIcon}`}> <div className={`${styles.langbotText}`}>Langbot</div>
L </div>
</div> {/* 菜单列表,后期可升级成配置驱动 */}
<div className={`${styles.langbotText}`}> <div>
Langbot {sidebarConfigList.map((config) => {
</div> return (
<div
key={config.id}
onClick={() => {
console.log('click:', config.id);
handleChildClick(config);
}}
>
<SidebarChild
isSelected={selectedChild.id === config.id}
icon={config.icon}
name={config.name}
/>
</div> </div>
{/* 菜单列表,后期可升级成配置驱动 */} );
<div> })}
{ </div>
sidebarConfigList.map(config => { </div>
return ( );
<div }
key={config.id}
onClick={() => {
console.log('click:', config.id)
handleChildClick(config)
}}
>
<SidebarChild
isSelected={selectedChild.id === config.id}
icon={config.icon}
name={config.name}
/>
</div>
)
})
}
</div>
</div>
);
}
@@ -1,4 +1,4 @@
import styles from "./HomeSidebar.module.css"; import styles from './HomeSidebar.module.css';
export interface ISidebarChildVO { export interface ISidebarChildVO {
id: string; id: string;
@@ -24,7 +24,7 @@ export class SidebarChildVO {
export function SidebarChild({ export function SidebarChild({
icon, icon,
name, name,
isSelected isSelected,
}: { }: {
icon: string; icon: string;
name: string; name: string;
@@ -1,28 +1,28 @@
import {SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild"; import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild';
export const sidebarConfigList = [ export const sidebarConfigList = [
new SidebarChildVO({ new SidebarChildVO({
id: "models", id: 'models',
name: "模型配置", name: '模型配置',
icon: "", icon: '',
route: "/home/models", route: '/home/models',
}), }),
new SidebarChildVO({ new SidebarChildVO({
id: "bots", id: 'bots',
name: "机器人", name: '机器人',
icon: "", icon: '',
route: "/home/bots", route: '/home/bots',
}), }),
new SidebarChildVO({ new SidebarChildVO({
id: "pipelines", id: 'pipelines',
name: "流水线", name: '流水线',
icon: "", icon: '',
route: "/home/pipelines", route: '/home/pipelines',
}), }),
new SidebarChildVO({ new SidebarChildVO({
id: "plugins", id: 'plugins',
name: "插件管理", name: '插件管理',
icon: "", icon: '',
route: "/home/plugins", route: '/home/plugins',
}), }),
] ];
@@ -1,16 +1,9 @@
import styles from "./HomeTittleBar.module.css" import styles from './HomeTittleBar.module.css';
export default function HomeTitleBar({ title }: { title: string }) {
export default function HomeTitleBar({ return (
title, <div className={`${styles.titleBarContainer}`}>
}: { <div className={`${styles.titleText}`}>{title}</div>
title: string </div>
}) { );
return ( }
<div className={`${styles.titleBarContainer}`}>
<div
className={`${styles.titleText}`}
>{title}</div>
</div>
);
}
+12 -16
View File
@@ -1,35 +1,31 @@
"use client" 'use client';
import '@ant-design/v5-patch-for-react-19'; import '@ant-design/v5-patch-for-react-19';
import styles from "./layout.module.css" import styles from './layout.module.css';
import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar"; import HomeSidebar from '@/app/home/components/home-sidebar/HomeSidebar';
import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar"; import HomeTitleBar from '@/app/home/components/home-titlebar/HomeTitleBar';
import React, { useState } from "react"; import React, { useState } from 'react';
import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild"; import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild';
import { useRouter } from 'next/navigation';
import { Layout } from 'antd'; import { Layout } from 'antd';
const { Sider, Content } = Layout; const { Sider, Content } = Layout;
export default function HomeLayout({ export default function HomeLayout({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const router = useRouter(); const [title, setTitle] = useState<string>('');
const [title, setTitle] = useState<string>("")
const onSelectedChangeAction = (child: SidebarChildVO) => { const onSelectedChangeAction = (child: SidebarChildVO) => {
setTitle(child.name) setTitle(child.name);
} };
return ( return (
<Layout className={styles.homeLayoutContainer}> <Layout className={styles.homeLayoutContainer}>
{/* homeLayoutContainer 是整个容器的入口,使用 flex 的左右布局 */} {/* homeLayoutContainer 是整个容器的入口,使用 flex 的左右布局 */}
<Sider className="left"> <Sider className="left">
<HomeSidebar <HomeSidebar onSelectedChangeAction={onSelectedChangeAction} />
onSelectedChangeAction={onSelectedChangeAction}
/>
{/* HomeSidebar 为侧边栏 */} {/* HomeSidebar 为侧边栏 */}
</Sider> </Sider>
@@ -44,5 +40,5 @@ export default function HomeLayout({
</Content> </Content>
</Layout> </Layout>
</Layout> </Layout>
) );
} }
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,8 +1,8 @@
export interface ICreateLLMField { export interface ICreateLLMField {
name: string; name: string;
model_provider: string; model_provider: string;
url: string; url: string;
api_key: string; api_key: string;
abilities: string[]; abilities: string[];
extra_args: string[]; extra_args: string[];
} }
@@ -1,33 +1,25 @@
import styles from "../../LLMConfig.module.css" import styles from '../../LLMConfig.module.css';
import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO"; import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO';
export default function LLMCard({ export default function LLMCard({ cardVO }: { cardVO: LLMCardVO }) {
cardVO return (
}: { <div className={`${styles.cardContainer}`}>
cardVO: LLMCardVO {/* icon和基本信息 */}
}) { <div className={`${styles.iconBasicInfoContainer}`}>
return ( {/* icon */}
<div className={`${styles.cardContainer}`}> <div className={`${styles.icon}`}>ICO</div>
{/* icon和基本信息 */} {/* bot基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}> <div className={`${styles.basicInfoContainer}`}>
{/* icon */} <div className={`${styles.basicInfoText} ${styles.bigText}`}>
<div className={`${styles.icon}`}> {cardVO.name}
ICO </div>
</div> <div className={`${styles.basicInfoText}`}>
{/* bot基本信息 */} {cardVO.company}
<div className={`${styles.basicInfoContainer}`}> </div>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{cardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{cardVO.company}
</div>
</div>
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
URL{cardVO.URL}
</div>
</div> </div>
); </div>
} {/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>URL{cardVO.URL}</div>
</div>
);
}
@@ -1,21 +1,20 @@
export interface ILLMCardVO { export interface ILLMCardVO {
id: string; id: string;
name: string; name: string;
company: string; company: string;
URL: string; URL: string;
} }
export class LLMCardVO implements ILLMCardVO { export class LLMCardVO implements ILLMCardVO {
id: string; id: string;
name: string; name: string;
company: string; company: string;
URL: string; URL: string;
constructor(props: ILLMCardVO) { constructor(props: ILLMCardVO) {
this.id = props.id; this.id = props.id;
this.name = props.name; this.name = props.name;
this.company = props.company; this.company = props.company;
this.URL = props.URL; this.URL = props.URL;
} }
}
}
@@ -1,4 +1,4 @@
export interface IChooseRequesterEntity { export interface IChooseRequesterEntity {
label: string label: string;
value: string value: string;
} }
@@ -1,18 +1,18 @@
import styles from "@/app/home/models/LLMConfig.module.css"; import styles from '@/app/home/models/LLMConfig.module.css';
import { Button, Form, Input, Select, SelectProps, Space, Modal } from "antd"; import { Button, Form, Input, Select, SelectProps, Space, Modal } from 'antd';
import { ICreateLLMField } from "@/app/home/models/ICreateLLMField"; import { ICreateLLMField } from '@/app/home/models/ICreateLLMField';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { IChooseRequesterEntity } from "@/app/home/models/component/llm-form/ChooseAdapterEntity"; import { IChooseRequesterEntity } from '@/app/home/models/component/llm-form/ChooseAdapterEntity';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel } from "@/app/infra/api/api-types"; import { LLMModel } from '@/app/infra/api/api-types';
import { UUID } from "uuidjs"; import { UUID } from 'uuidjs';
export default function LLMForm({ export default function LLMForm({
editMode, editMode,
initLLMId, initLLMId,
onFormSubmit, onFormSubmit,
onFormCancel, onFormCancel,
onLLMDeleted onLLMDeleted,
}: { }: {
editMode: boolean; editMode: boolean;
initLLMId?: string; initLLMId?: string;
@@ -21,18 +21,18 @@ export default function LLMForm({
onLLMDeleted: () => void; onLLMDeleted: () => void;
}) { }) {
const [form] = Form.useForm<ICreateLLMField>(); const [form] = Form.useForm<ICreateLLMField>();
const extraOptions: SelectProps["options"] = []; const extraOptions: SelectProps['options'] = [];
const [initValue] = useState<ICreateLLMField>(); const [initValue] = useState<ICreateLLMField>();
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false); const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
const abilityOptions: SelectProps["options"] = [ const abilityOptions: SelectProps['options'] = [
{ {
label: "函数调用", label: '函数调用',
value: "func_call" value: 'func_call',
}, },
{ {
label: "图像识别", label: '图像识别',
value: "vision" value: 'vision',
} },
]; ];
const [requesterNameList, setRequesterNameList] = useState< const [requesterNameList, setRequesterNameList] = useState<
IChooseRequesterEntity[] IChooseRequesterEntity[]
@@ -56,9 +56,9 @@ export default function LLMForm({
requesterNameList.requesters.map((item) => { requesterNameList.requesters.map((item) => {
return { return {
label: item.label.zh_CN, label: item.label.zh_CN,
value: item.name value: item.name,
}; };
}) }),
); );
} }
@@ -76,7 +76,7 @@ export default function LLMForm({
url: llmModel.model.requester_config?.base_url, url: llmModel.model.requester_config?.base_url,
api_key: llmModel.model.api_keys[0], api_key: llmModel.model.api_keys[0],
abilities: llmModel.model.abilities, abilities: llmModel.model.abilities,
extra_args: fakeExtraArgs extra_args: fakeExtraArgs,
}; };
} }
@@ -110,19 +110,19 @@ export default function LLMForm({
// } // }
function onCreateLLM(value: ICreateLLMField) { function onCreateLLM(value: ICreateLLMField) {
console.log("create llm", value); console.log('create llm', value);
const requestParam: LLMModel = { const requestParam: LLMModel = {
uuid: UUID.generate(), uuid: UUID.generate(),
name: value.name, name: value.name,
description: "", description: '',
requester: value.model_provider, requester: value.model_provider,
requester_config: { requester_config: {
base_url: value.url, base_url: value.url,
timeout: 120 timeout: 120,
}, },
extra_args: value.extra_args, extra_args: value.extra_args,
api_keys: [value.api_key], api_keys: [value.api_key],
abilities: value.abilities abilities: value.abilities,
// created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', // created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
// updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800', // updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
}; };
@@ -145,15 +145,15 @@ export default function LLMForm({
<div className={styles.modalContainer}> <div className={styles.modalContainer}>
<Modal <Modal
open={showDeleteConfirmModal} open={showDeleteConfirmModal}
title={"删除确认"} title={'删除确认'}
onCancel={() => setShowDeleteConfirmModal(false)} onCancel={() => setShowDeleteConfirmModal(false)}
footer={ footer={
<div <div
style={{ style={{
width: "170px", width: '170px',
display: "flex", display: 'flex',
flexDirection: "row", flexDirection: 'row',
justifyContent: "space-between" justifyContent: 'space-between',
}} }}
> >
<Button <Button
@@ -183,27 +183,27 @@ export default function LLMForm({
wrapperCol={{ span: 14 }} wrapperCol={{ span: 14 }}
layout="horizontal" layout="horizontal"
initialValues={{ initialValues={{
...initValue ...initValue,
}} }}
onFinish={handleFormSubmit} onFinish={handleFormSubmit}
clearOnDestroy={true} clearOnDestroy={true}
disabled={editMode} disabled={editMode}
> >
<Form.Item<ICreateLLMField> <Form.Item<ICreateLLMField>
label={"模型名称"} label={'模型名称'}
name={"name"} name={'name'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Input <Input
placeholder={"为自己的大模型取个好听的名字~"} placeholder={'为自己的大模型取个好听的名字~'}
style={{ width: 260 }} style={{ width: 260 }}
></Input> ></Input>
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> <Form.Item<ICreateLLMField>
label={"模型供应商"} label={'模型供应商'}
name={"model_provider"} name={'model_provider'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Select <Select
style={{ width: 120 }} style={{ width: 120 }}
@@ -213,9 +213,9 @@ export default function LLMForm({
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> <Form.Item<ICreateLLMField>
label={"请求URL"} label={'请求URL'}
name={"url"} name={'url'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Input <Input
placeholder="请求地址,一般是API提供商提供的URL" placeholder="请求地址,一般是API提供商提供的URL"
@@ -224,14 +224,14 @@ export default function LLMForm({
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> <Form.Item<ICreateLLMField>
label={"API Key"} label={'API Key'}
name={"api_key"} name={'api_key'}
rules={[{ required: true, message: "该项为必填项哦~" }]} rules={[{ required: true, message: '该项为必填项哦~' }]}
> >
<Input placeholder="你的API Key" style={{ width: 500 }}></Input> <Input placeholder="你的API Key" style={{ width: 500 }}></Input>
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> label={"开启能力"} name={"abilities"}> <Form.Item<ICreateLLMField> label={'开启能力'} name={'abilities'}>
<Select <Select
mode="tags" mode="tags"
style={{ width: 500 }} style={{ width: 500 }}
@@ -241,7 +241,7 @@ export default function LLMForm({
/> />
</Form.Item> </Form.Item>
<Form.Item<ICreateLLMField> label={"其他参数"} name={"extra_args"}> <Form.Item<ICreateLLMField> label={'其他参数'} name={'extra_args'}>
<Select <Select
mode="tags" mode="tags"
style={{ width: 500 }} style={{ width: 500 }}
+114 -107
View File
@@ -1,116 +1,123 @@
"use client" 'use client';
import {useState, useEffect} from "react"; import { useState, useEffect } from 'react';
import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO"; import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO';
import styles from "./LLMConfig.module.css" import styles from './LLMConfig.module.css';
import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent"; import EmptyAndCreateComponent from '@/app/home/components/empty-and-create-component/EmptyAndCreateComponent';
import {Modal} from "antd"; import { Modal } from 'antd';
import LLMCard from "@/app/home/models/component/llm-card/LLMCard"; import LLMCard from '@/app/home/models/component/llm-card/LLMCard';
import LLMForm from "@/app/home/models/component/llm-form/LLMForm"; import LLMForm from '@/app/home/models/component/llm-form/LLMForm';
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel } from "@/app/infra/api/api-types"; import { LLMModel } from '@/app/infra/api/api-types';
export default function LLMConfigPage() { export default function LLMConfigPage() {
const [cardList, setCardList] = useState<LLMCardVO[]>([]) const [cardList, setCardList] = useState<LLMCardVO[]>([]);
const [modalOpen, setModalOpen] = useState<boolean>(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false) const [isEditForm, setIsEditForm] = useState(false);
const [nowSelectedLLM, setNowSelectedLLM] = useState<LLMCardVO | null>(null) const [nowSelectedLLM, setNowSelectedLLM] = useState<LLMCardVO | null>(null);
useEffect(() => { useEffect(() => {
getLLMModelList() getLLMModelList();
}, []) }, []);
function getLLMModelList() { function getLLMModelList() {
httpClient.getProviderLLMModels().then((resp) => { httpClient
const llmModelList: LLMCardVO[] = resp.models.map((model: LLMModel) => { .getProviderLLMModels()
console.log("model", model) .then((resp) => {
return new LLMCardVO({ const llmModelList: LLMCardVO[] = resp.models.map((model: LLMModel) => {
id: model.uuid, console.log('model', model);
name: model.name, return new LLMCardVO({
company: model.requester, id: model.uuid,
URL: model.requester_config?.base_url, name: model.name,
}) company: model.requester,
}) URL: model.requester_config?.base_url,
console.log("get llmModelList", llmModelList) });
setCardList(llmModelList) });
}).catch((err) => { console.log('get llmModelList', llmModelList);
// TODO error toast setCardList(llmModelList);
console.error("get LLM model list error", err) })
}) .catch((err) => {
} // TODO error toast
console.error('get LLM model list error', err);
});
}
function selectLLM(cardVO: LLMCardVO) { function selectLLM(cardVO: LLMCardVO) {
setIsEditForm(true) setIsEditForm(true);
setNowSelectedLLM(cardVO) setNowSelectedLLM(cardVO);
console.log("set now vo", cardVO) console.log('set now vo', cardVO);
setModalOpen(true) setModalOpen(true);
} }
function handleCreateModelClick() { function handleCreateModelClick() {
setIsEditForm(false) setIsEditForm(false);
setNowSelectedLLM(null) setNowSelectedLLM(null);
setModalOpen(true); setModalOpen(true);
} }
return ( return (
<div className={styles.configPageContainer}> <div className={styles.configPageContainer}>
<Modal <Modal
title={isEditForm ? "预览模型" : "创建模型"} title={isEditForm ? '预览模型' : '创建模型'}
centered centered
open={modalOpen} open={modalOpen}
destroyOnClose={true} destroyOnClose={true}
onOk={() => setModalOpen(false)} onOk={() => setModalOpen(false)}
onCancel={() => setModalOpen(false)} onCancel={() => setModalOpen(false)}
width={700} width={700}
footer={null} footer={null}
> >
<LLMForm <LLMForm
editMode={isEditForm} editMode={isEditForm}
initLLMId={nowSelectedLLM?.id} initLLMId={nowSelectedLLM?.id}
onFormSubmit={() => { onFormSubmit={() => {
setModalOpen(false); setModalOpen(false);
getLLMModelList() getLLMModelList();
}} }}
onFormCancel={() => { onFormCancel={() => {
setModalOpen(false); setModalOpen(false);
}} }}
onLLMDeleted={() => { onLLMDeleted={() => {
setModalOpen(false) setModalOpen(false);
getLLMModelList() getLLMModelList();
}} }}
/> />
</Modal> </Modal>
{ {cardList.length > 0 && (
cardList.length > 0 && <div className={`${styles.modelListContainer}`}>
<div className={`${styles.modelListContainer}`} {cardList.map((cardVO) => {
> return (
{cardList.map(cardVO => { <div
return <div key={cardVO.id} onClick={() => {selectLLM(cardVO)}}> key={cardVO.id}
<LLMCard cardVO={cardVO}></LLMCard> onClick={() => {
</div> selectLLM(cardVO);
})} }}
<CreateCardComponent >
width={360} <LLMCard cardVO={cardVO}></LLMCard>
height={200} </div>
plusSize={90} );
onClick={handleCreateModelClick} })}
/> <CreateCardComponent
</div> width={360}
} height={200}
plusSize={90}
{ onClick={handleCreateModelClick}
cardList.length === 0 && />
<div className={`${styles.emptyContainer}`}>
<EmptyAndCreateComponent
title={"模型列表空空如也~"}
subTitle={"快去创建一个吧!"}
buttonText={"创建模型 +"}
onButtonClick={() => {
handleCreateModelClick()
}}
/>
</div>
}
</div> </div>
) )}
{cardList.length === 0 && (
<div className={`${styles.emptyContainer}`}>
<EmptyAndCreateComponent
title={'模型列表空空如也~'}
subTitle={'快去创建一个吧!'}
buttonText={'创建模型 +'}
onButtonClick={() => {
handleCreateModelClick();
}}
/>
</div>
)}
</div>
);
} }
+1 -4
View File
@@ -1,6 +1,3 @@
export default function Home() { export default function Home() {
return ( return <div className={``}></div>;
<div className={``}>
</div>
);
} }
@@ -1,33 +1,29 @@
import styles from "./pipelineCard.module.css"; import styles from './pipelineCard.module.css';
import {PipelineCardVO} from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO"; import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
export default function PipelineCardComponent({ export default function PipelineCardComponent({
cardVO cardVO,
}: { }: {
cardVO: PipelineCardVO cardVO: PipelineCardVO;
}) { }) {
return ( return (
<div className={`${styles.cardContainer}`}> <div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */} {/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}> <div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */} {/* icon */}
<div className={`${styles.icon}`}> <div className={`${styles.icon}`}>ICO</div>
ICO {/* 基本信息 */}
</div> <div className={`${styles.basicInfoContainer}`}>
{/* 基本信息 */} <div className={`${styles.basicInfoText} ${styles.bigText}`}>
<div className={`${styles.basicInfoContainer}`}> {cardVO.name}
<div className={`${styles.basicInfoText} ${styles.bigText}`}> </div>
{cardVO.name} <div className={`${styles.basicInfoText}`}>
</div> {cardVO.description}
<div className={`${styles.basicInfoText}`}> </div>
{cardVO.description}
</div>
</div>
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
{cardVO.version}
</div>
</div> </div>
); </div>
} {/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>{cardVO.version}</div>
</div>
);
}
@@ -1,23 +1,23 @@
export interface IPipelineCardVO { export interface IPipelineCardVO {
id: string; id: string;
name: string; name: string;
description: string; description: string;
createTime: string; createTime: string;
version: string; version: string;
} }
export class PipelineCardVO implements IPipelineCardVO { export class PipelineCardVO implements IPipelineCardVO {
createTime: string; createTime: string;
description: string; description: string;
id: string; id: string;
name: string; name: string;
version: string; version: string;
constructor(props: IPipelineCardVO) { constructor(props: IPipelineCardVO) {
this.id = props.id; this.id = props.id;
this.name = props.name; this.name = props.name;
this.description = props.description; this.description = props.description;
this.createTime = props.createTime; this.createTime = props.createTime;
this.version = props.version; this.version = props.version;
} }
} }
@@ -1,4 +1,4 @@
import { DynamicFormItemConfig } from "@/app/home/components/dynamic-form/DynamicFormItemConfig"; import { DynamicFormItemConfig } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export interface IPipelineChildFormEntity { export interface IPipelineChildFormEntity {
name: string; name: string;
@@ -5,15 +5,15 @@ import {
Select, Select,
Input, Input,
InputNumber, InputNumber,
SelectProps SelectProps,
} from "antd"; } from 'antd';
import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons"; import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import styles from "./pipelineFormStyle.module.css"; import styles from './pipelineFormStyle.module.css';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel, Pipeline } from "@/app/infra/api/api-types"; import { LLMModel, Pipeline } from '@/app/infra/api/api-types';
import { UUID } from "uuidjs"; import { UUID } from 'uuidjs';
import {PipelineFormEntity} from "@/app/home/pipelines/components/pipeline-form/PipelineFormEntity"; import { PipelineFormEntity } from '@/app/home/pipelines/components/pipeline-form/PipelineFormEntity';
export default function PipelineFormComponent({ export default function PipelineFormComponent({
initValues, initValues,
@@ -30,15 +30,15 @@ export default function PipelineFormComponent({
onFinish: () => void; onFinish: () => void;
}) { }) {
const [nowFormIndex, setNowFormIndex] = useState<number>(0); const [nowFormIndex, setNowFormIndex] = useState<number>(0);
const [nowAIRunner, setNowAIRunner] = useState(""); const [nowAIRunner, setNowAIRunner] = useState('');
const [llmModelList, setLlmModelList] = useState<SelectProps["options"]>([]); const [llmModelList, setLlmModelList] = useState<SelectProps['options']>([]);
// 这里不好,可以改成enum等 // 这里不好,可以改成enum等
const formLabelList: FormLabel[] = [ const formLabelList: FormLabel[] = [
{ label: "基础", name: "basic" }, { label: '基础', name: 'basic' },
{ label: "AI能力", name: "ai" }, { label: 'AI能力', name: 'ai' },
{ label: "触发条件", name: "trigger" }, { label: '触发条件', name: 'trigger' },
{ label: "安全能力", name: "safety" }, { label: '安全能力', name: 'safety' },
{ label: "输出处理", name: "output" } { label: '输出处理', name: 'output' },
]; ];
const [basicForm] = Form.useForm(); const [basicForm] = Form.useForm();
const [aiForm] = Form.useForm(); const [aiForm] = Form.useForm();
@@ -51,7 +51,7 @@ export default function PipelineFormComponent({
}, []); }, []);
useEffect(() => { useEffect(() => {
console.log("initValues change: ", initValues); console.log('initValues change: ', initValues);
if (initValues) { if (initValues) {
basicForm.setFieldsValue(initValues.basic); basicForm.setFieldsValue(initValues.basic);
aiForm.setFieldsValue(initValues.ai); aiForm.setFieldsValue(initValues.ai);
@@ -59,8 +59,7 @@ export default function PipelineFormComponent({
safetyForm.setFieldsValue(initValues.safety); safetyForm.setFieldsValue(initValues.safety);
outputForm.setFieldsValue(initValues.output); outputForm.setFieldsValue(initValues.output);
} }
}, [initValues]); }, [aiForm, basicForm, initValues, outputForm, safetyForm, triggerForm]);
function getLLMModelList() { function getLLMModelList() {
httpClient httpClient
@@ -70,13 +69,13 @@ export default function PipelineFormComponent({
resp.models.map((model: LLMModel) => { resp.models.map((model: LLMModel) => {
return { return {
value: model.uuid, value: model.uuid,
label: model.name label: model.name,
}; };
}) }),
); );
}) })
.catch((err) => { .catch((err) => {
console.error("get LLM model list error", err); console.error('get LLM model list error', err);
}); });
} }
@@ -114,9 +113,9 @@ export default function PipelineFormComponent({
function handleCommit() { function handleCommit() {
if (isEditMode) { if (isEditMode) {
handleModify() handleModify();
} else { } else {
handleCreate() handleCreate();
} }
} }
@@ -126,15 +125,15 @@ export default function PipelineFormComponent({
aiForm.validateFields(), aiForm.validateFields(),
triggerForm.validateFields(), triggerForm.validateFields(),
safetyForm.validateFields(), safetyForm.validateFields(),
outputForm.validateFields() outputForm.validateFields(),
]) ])
.then(() => { .then(() => {
const pipeline = assembleForm(); const pipeline = assembleForm();
httpClient.createPipeline(pipeline).then(() => onFinish()); httpClient.createPipeline(pipeline).then(() => onFinish());
}) })
.catch((e) => { .catch((e) => {
console.error(e); console.error(e);
}); });
} }
function handleModify() { function handleModify() {
@@ -143,61 +142,63 @@ export default function PipelineFormComponent({
aiForm.validateFields(), aiForm.validateFields(),
triggerForm.validateFields(), triggerForm.validateFields(),
safetyForm.validateFields(), safetyForm.validateFields(),
outputForm.validateFields() outputForm.validateFields(),
]) ])
.then(() => { .then(() => {
const pipeline = assembleForm(); const pipeline = assembleForm();
httpClient.updatePipeline(pipelineId || '', pipeline).then(() => onFinish()); httpClient
}) .updatePipeline(pipelineId || '', pipeline)
.catch((e) => { .then(() => onFinish());
console.error(e); })
}); .catch((e) => {
console.error(e);
});
} }
// TODO 类型混乱,需要优化 // TODO 类型混乱,需要优化
function assembleForm(): Pipeline { function assembleForm(): Pipeline {
console.log("basicForm:", basicForm.getFieldsValue()); console.log('basicForm:', basicForm.getFieldsValue());
console.log("aiForm:", aiForm.getFieldsValue()); console.log('aiForm:', aiForm.getFieldsValue());
console.log("triggerForm:", triggerForm.getFieldsValue()); console.log('triggerForm:', triggerForm.getFieldsValue());
console.log("safetyForm:", safetyForm.getFieldsValue()); console.log('safetyForm:', safetyForm.getFieldsValue());
console.log("outputForm:", outputForm.getFieldsValue()); console.log('outputForm:', outputForm.getFieldsValue());
const config: object = { const config: object = {
ai: aiForm.getFieldsValue(), ai: aiForm.getFieldsValue(),
trigger: triggerForm.getFieldsValue(), trigger: triggerForm.getFieldsValue(),
safety: safetyForm.getFieldsValue(), safety: safetyForm.getFieldsValue(),
output: outputForm.getFieldsValue() output: outputForm.getFieldsValue(),
}; };
return { return {
config, config,
created_at: "", created_at: '',
description: basicForm.getFieldsValue().description, description: basicForm.getFieldsValue().description,
for_version: "", for_version: '',
name: basicForm.getFieldsValue().name, name: basicForm.getFieldsValue().name,
stages: [], stages: [],
updated_at: "", updated_at: '',
uuid: UUID.generate() uuid: UUID.generate(),
}; };
} }
return ( return (
<div style={{ maxHeight: "70vh", overflowY: "auto" }}> <div style={{ maxHeight: '70vh', overflowY: 'auto' }}>
<h1>{getNowFormLabel().label}</h1> <h1>{getNowFormLabel().label}</h1>
<Form <Form
layout={"vertical"} layout={'vertical'}
style={{ style={{
display: getNowFormLabel().name === "basic" ? "block" : "none" display: getNowFormLabel().name === 'basic' ? 'block' : 'none',
}} }}
form={basicForm} form={basicForm}
disabled={disableForm} disabled={disableForm}
> >
<Form.Item <Form.Item
label="流水线名称" label="流水线名称"
name={"name"} name={'name'}
rules={[ rules={[
{ {
required: true required: true,
} },
]} ]}
> >
<Input /> <Input />
@@ -205,11 +206,11 @@ export default function PipelineFormComponent({
<Form.Item <Form.Item
label="流水线描述" label="流水线描述"
name={"description"} name={'description'}
rules={[ rules={[
{ {
required: true required: true,
} },
]} ]}
> >
<Input /> <Input />
@@ -217,8 +218,8 @@ export default function PipelineFormComponent({
</Form> </Form>
{/* AI能力表单 ai */} {/* AI能力表单 ai */}
<Form <Form
layout={"vertical"} layout={'vertical'}
style={{ display: getNowFormLabel().name === "ai" ? "block" : "none" }} style={{ display: getNowFormLabel().name === 'ai' ? 'block' : 'none' }}
form={aiForm} form={aiForm}
disabled={disableForm} disabled={disableForm}
> >
@@ -226,26 +227,26 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}></div> <div className={`${styles.formItemSubtitle}`}></div>
<Form.Item <Form.Item
label="运行器" label="运行器"
name={["runner", "runner"]} name={['runner', 'runner']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "内置 Agent", value: "local-agent" }, { label: '内置 Agent', value: 'local-agent' },
{ label: "Dify 服务 API", value: "dify-service-api" }, { label: 'Dify 服务 API', value: 'dify-service-api' },
{ label: "阿里云百炼平台 API", value: "dashscope-app-api" } { label: '阿里云百炼平台 API', value: 'dashscope-app-api' },
]} ]}
onChange={(value) => setNowAIRunner(value)} onChange={(value) => setNowAIRunner(value)}
/> />
</Form.Item> </Form.Item>
{/* 内置 Agent 配置区块 */} {/* 内置 Agent 配置区块 */}
{nowAIRunner === "local-agent" && ( {nowAIRunner === 'local-agent' && (
<> <>
<div className={`${styles.formItemSubtitle}`}>Agent</div> <div className={`${styles.formItemSubtitle}`}>Agent</div>
<Form.Item <Form.Item
label="模型" label="模型"
name={["local-agent", "model"]} name={['local-agent', 'model']}
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip="从模型库中选择" tooltip="从模型库中选择"
> >
@@ -257,11 +258,11 @@ export default function PipelineFormComponent({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="最大回合数" label="最大回合数"
name={["local-agent", "max-round"]} name={['local-agent', 'max-round']}
rules={[ rules={[
{ {
required: true required: true,
} },
]} ]}
> >
<InputNumber precision={0} /> <InputNumber precision={0} />
@@ -269,7 +270,7 @@ export default function PipelineFormComponent({
{/* TODO 这里要做转换处理 */} {/* TODO 这里要做转换处理 */}
<Form.Item <Form.Item
label="提示词" label="提示词"
name={["local-agent", "prompt"]} name={['local-agent', 'prompt']}
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip="按JSON格式输入" tooltip="按JSON格式输入"
> >
@@ -281,91 +282,91 @@ export default function PipelineFormComponent({
</> </>
)} )}
{/* Dify 服务 API 区块 */} {/* Dify 服务 API 区块 */}
{nowAIRunner === "dify-service-api" && ( {nowAIRunner === 'dify-service-api' && (
<> <>
<div className={`${styles.formItemSubtitle}`}>Dify服务API</div> <div className={`${styles.formItemSubtitle}`}>Dify服务API</div>
<Form.Item <Form.Item
label="基础 URL" label="基础 URL"
name={["dify-service-api", "base-url"]} name={['dify-service-api', 'base-url']}
rules={[ rules={[
{ required: true }, { required: true },
{ type: "url", message: "请输入有效的URL地址" } { type: 'url', message: '请输入有效的URL地址' },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="应用类型" label="应用类型"
name={["dify-service-api", "app-type"]} name={['dify-service-api', 'app-type']}
initialValue={"chat"} initialValue={'chat'}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "聊天(包括Chatflow", value: "chat" }, { label: '聊天(包括Chatflow', value: 'chat' },
{ label: "Agent", value: "agent" }, { label: 'Agent', value: 'agent' },
{ label: "工作流", value: "workflow" } { label: '工作流', value: 'workflow' },
]} ]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="API 密钥" label="API 密钥"
name={["dify-service-api", "api-key"]} name={['dify-service-api', 'api-key']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input.Password visibilityToggle={false} /> <Input.Password visibilityToggle={false} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="思维链转换" label="思维链转换"
name={["dify-service-api", "thinking-convert"]} name={['dify-service-api', 'thinking-convert']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "转换成 \<think\>...\<\/think\>", value: "plain" }, { label: '转换成 \<think\>...\<\/think\>', value: 'plain' },
{ label: "原始", value: "original" }, { label: '原始', value: 'original' },
{ label: "移除", value: "remove" } { label: '移除', value: 'remove' },
]} ]}
/> />
</Form.Item> </Form.Item>
</> </>
)} )}
{/* 阿里云百炼区块 */} {/* 阿里云百炼区块 */}
{nowAIRunner === "dashscope-app-api" && ( {nowAIRunner === 'dashscope-app-api' && (
<> <>
<div className={`${styles.formItemSubtitle}`}> <div className={`${styles.formItemSubtitle}`}>
API API
</div> </div>
<Form.Item <Form.Item
label="应用类型" label="应用类型"
name={["dashscope-app-api", "app-type"]} name={['dashscope-app-api', 'app-type']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "Agent", value: "agent" }, { label: 'Agent', value: 'agent' },
{ label: "工作流", value: "workflow" } { label: '工作流', value: 'workflow' },
]} ]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="API 密钥" label="API 密钥"
name={["dashscope-app-api", "api-key"]} name={['dashscope-app-api', 'api-key']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input.Password visibilityToggle={false} /> <Input.Password visibilityToggle={false} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="应用 ID" label="应用 ID"
name={["dashscope-app-api", "app-id"]} name={['dashscope-app-api', 'app-id']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="引用文本" label="引用文本"
name={["dashscope-app-api", "references_quote"]} name={['dashscope-app-api', 'references_quote']}
initialValue={"参考资料来自:"} initialValue={'参考资料来自:'}
> >
<Input.TextArea rows={2} /> <Input.TextArea rows={2} />
</Form.Item> </Form.Item>
@@ -375,9 +376,9 @@ export default function PipelineFormComponent({
{/* 触发条件表单 trigger */} {/* 触发条件表单 trigger */}
<Form <Form
layout={"vertical"} layout={'vertical'}
style={{ style={{
display: getNowFormLabel().name === "trigger" ? "block" : "none" display: getNowFormLabel().name === 'trigger' ? 'block' : 'none',
}} }}
form={triggerForm} form={triggerForm}
disabled={disableForm} disabled={disableForm}
@@ -385,15 +386,15 @@ export default function PipelineFormComponent({
{/* 群响应规则块 */} {/* 群响应规则块 */}
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label={"是否在消息@机器人时触发"} label={'是否在消息@机器人时触发'}
name={["group-respond-rules", "at"]} name={['group-respond-rules', 'at']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"消息前缀"} label={'消息前缀'}
name={["group-respond-rules", "prefix"]} name={['group-respond-rules', 'prefix']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
@@ -401,76 +402,76 @@ export default function PipelineFormComponent({
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"正则表达式"} label={'正则表达式'}
name={["group-respond-rules", "regexp"]} name={['group-respond-rules', 'regexp']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select mode="tags" options={[]} /> <Select mode="tags" options={[]} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"随机"} label={'随机'}
name={["group-respond-rules", "random"]} name={['group-respond-rules', 'random']}
rules={[{ required: false }]} rules={[{ required: false }]}
> >
<InputNumber max={1} min={0} step={0.05} /> <InputNumber max={1} min={0} step={0.05} />
</Form.Item> </Form.Item>
<div className={`${styles.formItemSubtitle}`}> 访 </div> <div className={`${styles.formItemSubtitle}`}> 访 </div>
<Form.Item <Form.Item
label={"模式"} label={'模式'}
name={["access-control", "mode"]} name={['access-control', 'mode']}
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip={"访问控制模式"} tooltip={'访问控制模式'}
> >
<Select <Select
options={[ options={[
{ label: "黑名单", value: "blacklist" }, { label: '黑名单', value: 'blacklist' },
{ label: "白名单", value: "Whitelist" } { label: '白名单', value: 'Whitelist' },
]} ]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"黑名单"} label={'黑名单'}
name={["access-control", "blacklist"]} name={['access-control', 'blacklist']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select mode={"tags"} options={[]} /> <Select mode={'tags'} options={[]} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"白名单"} label={'白名单'}
name={["access-control", "whitelist"]} name={['access-control', 'whitelist']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select mode={"tags"} options={[]} /> <Select mode={'tags'} options={[]} />
</Form.Item> </Form.Item>
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label={"前缀"} label={'前缀'}
name={["ignore-rules", "whitelist"]} name={['ignore-rules', 'whitelist']}
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip={"消息前缀"} tooltip={'消息前缀'}
> >
<Select mode={"tags"} options={[]} /> <Select mode={'tags'} options={[]} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"正则表达式"} label={'正则表达式'}
name={["ignore-rules", "regexp"]} name={['ignore-rules', 'regexp']}
rules={[{ required: true }]} rules={[{ required: true }]}
tooltip={"消息正则表达式"} tooltip={'消息正则表达式'}
> >
<Select mode={"tags"} options={[]} /> <Select mode={'tags'} options={[]} />
</Form.Item> </Form.Item>
</Form> </Form>
{/* 安全控制表单 safety */} {/* 安全控制表单 safety */}
<Form <Form
layout={"vertical"} layout={'vertical'}
style={{ style={{
display: getNowFormLabel().name === "safety" ? "block" : "none" display: getNowFormLabel().name === 'safety' ? 'block' : 'none',
}} }}
form={safetyForm} form={safetyForm}
disabled={disableForm} disabled={disableForm}
@@ -478,22 +479,22 @@ export default function PipelineFormComponent({
{/* 内容过滤块 content-filter */} {/* 内容过滤块 content-filter */}
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label={"检查范围"} label={'检查范围'}
name={["content-filter", "scope"]} name={['content-filter', 'scope']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "全部", value: "all" }, { label: '全部', value: 'all' },
{ label: "传入消息(用户消息)", value: "income-msg" }, { label: '传入消息(用户消息)', value: 'income-msg' },
{ label: "传出消息(机器人消息)", value: "output-msg" } { label: '传出消息(机器人消息)', value: 'output-msg' },
]} ]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"检查敏感词"} label={'检查敏感词'}
name={["content-filter", "check-sensitive-words"]} name={['content-filter', 'check-sensitive-words']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Switch /> <Switch />
@@ -502,31 +503,31 @@ export default function PipelineFormComponent({
{/* 速率限制块 rate-limit */} {/* 速率限制块 rate-limit */}
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label={"窗口长度(秒)"} label={'窗口长度(秒)'}
name={["rate-limit", "window-length"]} name={['rate-limit', 'window-length']}
rules={[{ required: true }]} rules={[{ required: true }]}
initialValue={60} initialValue={60}
> >
<InputNumber></InputNumber> <InputNumber></InputNumber>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"限制次数"} label={'限制次数'}
name={["rate-limit", "limitation"]} name={['rate-limit', 'limitation']}
rules={[{ required: true }]} rules={[{ required: true }]}
initialValue={60} initialValue={60}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={"策略"} label={'策略'}
name={["rate-limit", "strategy"]} name={['rate-limit', 'strategy']}
rules={[{ required: true }]} rules={[{ required: true }]}
initialValue={"drop"} initialValue={'drop'}
> >
<Select <Select
options={[ options={[
{ label: "丢弃", value: "drop" }, { label: '丢弃', value: 'drop' },
{ label: "等待", value: "wait" } { label: '等待', value: 'wait' },
]} ]}
/> />
</Form.Item> </Form.Item>
@@ -534,9 +535,9 @@ export default function PipelineFormComponent({
{/* 输出处理控制表单 output */} {/* 输出处理控制表单 output */}
<Form <Form
layout={"vertical"} layout={'vertical'}
style={{ style={{
display: getNowFormLabel().name === "output" ? "block" : "none" display: getNowFormLabel().name === 'output' ? 'block' : 'none',
}} }}
form={outputForm} form={outputForm}
disabled={disableForm} disabled={disableForm}
@@ -545,26 +546,26 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label="阈值" label="阈值"
name={["long-text-processing", "threshold"]} name={['long-text-processing', 'threshold']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="策略" label="策略"
name={["long-text-processing", "strategy"]} name={['long-text-processing', 'strategy']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
options={[ options={[
{ label: "转发消息组件", value: "forward" }, { label: '转发消息组件', value: 'forward' },
{ label: "转换为图片", value: "image" } { label: '转换为图片', value: 'image' },
]} ]}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="字体路径" label="字体路径"
name={["long-text-processing", "font-path"]} name={['long-text-processing', 'font-path']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input /> <Input />
@@ -574,14 +575,14 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label="最小秒数" label="最小秒数"
name={["force-delay", "min"]} name={['force-delay', 'min']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="最大秒数" label="最大秒数"
name={["force-delay", "max"]} name={['force-delay', 'max']}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<InputNumber /> <InputNumber />
@@ -591,7 +592,7 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div> <div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item <Form.Item
label="不输出异常信息给用户" label="不输出异常信息给用户"
name={["misc", "hide-exception"]} name={['misc', 'hide-exception']}
rules={[{ required: true }]} rules={[{ required: true }]}
valuePropName="checked" valuePropName="checked"
> >
@@ -599,7 +600,7 @@ export default function PipelineFormComponent({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="在回复中@发送者" label="在回复中@发送者"
name={["misc", "at-sender"]} name={['misc', 'at-sender']}
rules={[{ required: true }]} rules={[{ required: true }]}
valuePropName="checked" valuePropName="checked"
> >
@@ -607,7 +608,7 @@ export default function PipelineFormComponent({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="引用原文" label="引用原文"
name={["misc", "quote-origin"]} name={['misc', 'quote-origin']}
rules={[{ required: true }]} rules={[{ required: true }]}
valuePropName="checked" valuePropName="checked"
> >
@@ -615,7 +616,7 @@ export default function PipelineFormComponent({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="跟踪函数调用" label="跟踪函数调用"
name={["misc", "track-function-calls"]} name={['misc', 'track-function-calls']}
rules={[{ required: true }]} rules={[{ required: true }]}
valuePropName="checked" valuePropName="checked"
> >
@@ -630,16 +631,16 @@ export default function PipelineFormComponent({
onClick={reduceFormLabelIndex} onClick={reduceFormLabelIndex}
disabled={!getPreFormLabel()} disabled={!getPreFormLabel()}
> >
{getPreFormLabel()?.label || "暂无更多"} {getPreFormLabel()?.label || '暂无更多'}
</Button> </Button>
<Button <Button
type="primary" type="primary"
icon={<CaretRightOutlined />} icon={<CaretRightOutlined />}
onClick={addFormLabelIndex} onClick={addFormLabelIndex}
disabled={!getNextFormLabel()} disabled={!getNextFormLabel()}
iconPosition={"end"} iconPosition={'end'}
> >
{getNextFormLabel()?.label || "暂无更多"} {getNextFormLabel()?.label || '暂无更多'}
</Button> </Button>
<Button type="primary" onClick={handleCommit}> <Button type="primary" onClick={handleCommit}>
@@ -1,7 +1,7 @@
export interface PipelineFormEntity { export interface PipelineFormEntity {
basic: object, basic: object;
ai: object, ai: object;
trigger: object, trigger: object;
safety: object, safety: object;
output: object, output: object;
} }
+34 -33
View File
@@ -1,26 +1,27 @@
"use client"; 'use client';
import { Modal } from "antd"; import { Modal } from 'antd';
import { useState, useEffect } from "react"; import { useState, useEffect } from 'react';
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent"; import PipelineFormComponent from './components/pipeline-form/PipelineFormComponent';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import { PipelineCardVO } from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO"; import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card/PipelineCardComponent"; import PipelineCardComponent from '@/app/home/pipelines/components/pipeline-card/PipelineCardComponent';
import {PipelineFormEntity} from "@/app/home/pipelines/components/pipeline-form/PipelineFormEntity"; import { PipelineFormEntity } from '@/app/home/pipelines/components/pipeline-form/PipelineFormEntity';
export default function PluginConfigPage() { export default function PluginConfigPage() {
const [modalOpen, setModalOpen] = useState<boolean>(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false); const [isEditForm, setIsEditForm] = useState(false);
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]); const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
const [selectedPipelineId, setSelectedPipelineId] = useState("") const [selectedPipelineId, setSelectedPipelineId] = useState('');
const [selectedPipelineFormValue, setSelectedPipelineFormValue] = useState<PipelineFormEntity>({ const [selectedPipelineFormValue, setSelectedPipelineFormValue] =
useState<PipelineFormEntity>({
basic: {}, basic: {},
ai: {}, ai: {},
trigger: {}, trigger: {},
safety: {}, safety: {},
output: {}, output: {},
}) });
const [disableForm, setDisableForm] = useState(false) const [disableForm, setDisableForm] = useState(false);
useEffect(() => { useEffect(() => {
getPipelines(); getPipelines();
@@ -36,7 +37,7 @@ export default function PluginConfigPage() {
description: pipeline.description, description: pipeline.description,
id: pipeline.uuid, id: pipeline.uuid,
name: pipeline.name, name: pipeline.name,
version: pipeline.for_version version: pipeline.for_version,
}); });
}); });
setPipelineList(pipelineList); setPipelineList(pipelineList);
@@ -48,25 +49,25 @@ export default function PluginConfigPage() {
} }
function getSelectedPipelineForm(id?: string) { function getSelectedPipelineForm(id?: string) {
httpClient.getPipeline(id ?? selectedPipelineId).then((value) => { httpClient.getPipeline(id ?? selectedPipelineId).then((value) => {
setSelectedPipelineFormValue({ setSelectedPipelineFormValue({
ai: value.pipeline.config.ai, ai: value.pipeline.config.ai,
basic: { basic: {
description: value.pipeline.description, description: value.pipeline.description,
name: value.pipeline.name, name: value.pipeline.name,
}, },
output: value.pipeline.config.output, output: value.pipeline.config.output,
safety: value.pipeline.config.safety, safety: value.pipeline.config.safety,
trigger: value.pipeline.config.trigger, trigger: value.pipeline.config.trigger,
}) });
setDisableForm(false) setDisableForm(false);
}) });
} }
return ( return (
<div className={``}> <div className={``}>
<Modal <Modal
title={isEditForm ? "编辑流水线" : "创建流水线"} title={isEditForm ? '编辑流水线' : '创建流水线'}
centered centered
open={modalOpen} open={modalOpen}
destroyOnClose={true} destroyOnClose={true}
@@ -94,11 +95,11 @@ export default function PluginConfigPage() {
<div <div
key={pipeline.id} key={pipeline.id}
onClick={() => { onClick={() => {
setDisableForm(true) setDisableForm(true);
setIsEditForm(true); setIsEditForm(true);
setModalOpen(true); setModalOpen(true);
setSelectedPipelineId(pipeline.id) setSelectedPipelineId(pipeline.id);
getSelectedPipelineForm(pipeline.id); getSelectedPipelineForm(pipeline.id);
}} }}
> >
<PipelineCardComponent cardVO={pipeline} /> <PipelineCardComponent cardVO={pipeline} />
+34 -30
View File
@@ -1,37 +1,41 @@
"use client" 'use client';
import { Radio } from 'antd'; import { Radio } from 'antd';
import { useState } from "react"; import { useState } from 'react';
import PluginInstalledComponent from "@/app/home/plugins/plugin-installed/PluginInstalledComponent"; import PluginInstalledComponent from '@/app/home/plugins/plugin-installed/PluginInstalledComponent';
import PluginMarketComponent from "@/app/home/plugins/plugin-market/PluginMarketComponent"; import PluginMarketComponent from '@/app/home/plugins/plugin-market/PluginMarketComponent';
import styles from './plugins.module.css' import styles from './plugins.module.css';
export default function PluginConfigPage() { export default function PluginConfigPage() {
enum PageType { enum PageType {
INSTALLED = "installed", INSTALLED = 'installed',
MARKET = 'market' MARKET = 'market',
} }
const [nowPageType, setNowPageType] = useState(PageType.INSTALLED) const [nowPageType, setNowPageType] = useState(PageType.INSTALLED);
return ( return (
<div className={styles.pageContainer}> <div className={styles.pageContainer}>
<Radio.Group <Radio.Group
block block
options={[ options={[
{ label: '已安装', value: PageType.INSTALLED }, { label: '已安装', value: PageType.INSTALLED },
{ label: '插件市场', value: PageType.MARKET }, { label: '插件市场', value: PageType.MARKET },
]} ]}
defaultValue={PageType.INSTALLED} defaultValue={PageType.INSTALLED}
value={nowPageType} value={nowPageType}
optionType="button" optionType="button"
buttonStyle="solid" buttonStyle="solid"
style={{ marginBottom: '20px' }} style={{ marginBottom: '20px' }}
onChange={(e) => { onChange={(e) => {
setNowPageType(e.target.value as PageType) setNowPageType(e.target.value as PageType);
}} }}
/> />
{nowPageType === PageType.INSTALLED ? <PluginInstalledComponent /> : <PluginMarketComponent />} {nowPageType === PageType.INSTALLED ? (
</div> <PluginInstalledComponent />
); ) : (
<PluginMarketComponent />
)}
</div>
);
} }
@@ -1,27 +1,26 @@
export interface IPluginCardVO { export interface IPluginCardVO {
author: string, author: string;
version: string, version: string;
name: string, name: string;
description: string, description: string;
handlerCount: number, handlerCount: number;
isInitialized: boolean, isInitialized: boolean;
} }
export class PluginCardVO implements IPluginCardVO { export class PluginCardVO implements IPluginCardVO {
description: string; description: string;
handlerCount: number; handlerCount: number;
name: string; name: string;
author: string; author: string;
version: string; version: string;
isInitialized: boolean; isInitialized: boolean;
constructor(prop: IPluginCardVO) {
this.description = prop.description
this.handlerCount = prop.handlerCount
this.name = prop.name
this.author = prop.author
this.version = prop.version
this.isInitialized = prop.isInitialized
}
constructor(prop: IPluginCardVO) {
this.description = prop.description;
this.handlerCount = prop.handlerCount;
this.name = prop.name;
this.author = prop.author;
this.version = prop.version;
this.isInitialized = prop.isInitialized;
}
} }
@@ -1,18 +1,18 @@
"use client"; 'use client';
import { useState, useEffect } from "react"; import { useState, useEffect } from 'react';
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent"; import PluginCardComponent from '@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent';
import styles from "@/app/home/plugins/plugins.module.css"; import styles from '@/app/home/plugins/plugins.module.css';
import { Modal, Input } from "antd"; import { Modal, Input } from 'antd';
import { GithubOutlined } from "@ant-design/icons"; import { GithubOutlined } from '@ant-design/icons';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
export default function PluginInstalledComponent() { export default function PluginInstalledComponent() {
const [pluginList, setPluginList] = useState<PluginCardVO[]>([]); const [pluginList, setPluginList] = useState<PluginCardVO[]>([]);
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [githubURL, setGithubURL] = useState(""); const [githubURL, setGithubURL] = useState('');
useEffect(() => { useEffect(() => {
initData(); initData();
@@ -33,9 +33,9 @@ export default function PluginInstalledComponent() {
handlerCount: 0, handlerCount: 0,
name: plugin.name, name: plugin.name,
version: plugin.version, version: plugin.version,
isInitialized: plugin.status === "initialized" isInitialized: plugin.status === 'initialized',
}); });
}) }),
); );
}); });
} }
@@ -53,7 +53,7 @@ export default function PluginInstalledComponent() {
getPluginList(); getPluginList();
}) })
.catch((err) => { .catch((err) => {
console.log("error when install plugin:", err); console.log('error when install plugin:', err);
}); });
} }
return ( return (
@@ -63,8 +63,8 @@ export default function PluginInstalledComponent() {
<div className={`${styles.modalTitle}`}> <div className={`${styles.modalTitle}`}>
<GithubOutlined <GithubOutlined
style={{ style={{
fontSize: "30px", fontSize: '30px',
marginRight: "20px" marginRight: '20px',
}} }}
/> />
</div> </div>
@@ -1,12 +1,12 @@
import styles from "./pluginCard.module.css"; import styles from './pluginCard.module.css';
import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
import { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons"; import { GithubOutlined, LinkOutlined, ToolOutlined } from '@ant-design/icons';
import { Switch, Tag } from "antd"; import { Switch, Tag } from 'antd';
import { useState } from "react"; import { useState } from 'react';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
export default function PluginCardComponent({ export default function PluginCardComponent({
cardVO cardVO,
}: { }: {
cardVO: PluginCardVO; cardVO: PluginCardVO;
}) { }) {
@@ -21,7 +21,7 @@ export default function PluginCardComponent({
setInitialized(!initialized); setInitialized(!initialized);
}) })
.catch((err) => { .catch((err) => {
console.log("error: ", err); console.log('error: ', err);
}) })
.finally(() => { .finally(() => {
setSwitchEnable(true); setSwitchEnable(true);
@@ -35,7 +35,7 @@ export default function PluginCardComponent({
<div className={`${styles.fontGray}`}>{cardVO.author}</div> <div className={`${styles.fontGray}`}>{cardVO.author}</div>
{/* right icon & version */} {/* right icon & version */}
<div className={`${styles.iconVersionContainer}`}> <div className={`${styles.iconVersionContainer}`}>
<GithubOutlined style={{ fontSize: "26px" }} type="setting" /> <GithubOutlined style={{ fontSize: '26px' }} type="setting" />
<Tag color="#108ee9">v{cardVO.version}</Tag> <Tag color="#108ee9">v{cardVO.version}</Tag>
</div> </div>
</div> </div>
@@ -49,10 +49,10 @@ export default function PluginCardComponent({
<div className={`${styles.footerContainer}`}> <div className={`${styles.footerContainer}`}>
<div className={`${styles.linkAndToolContainer}`}> <div className={`${styles.linkAndToolContainer}`}>
<div className={`${styles.link}`}> <div className={`${styles.link}`}>
<LinkOutlined style={{ fontSize: "22px" }} /> <LinkOutlined style={{ fontSize: '22px' }} />
<span>1</span> <span>1</span>
</div> </div>
<ToolOutlined style={{ fontSize: "22px" }} /> <ToolOutlined style={{ fontSize: '22px' }} />
</div> </div>
<div className={`${styles.switchContainer}`}> <div className={`${styles.switchContainer}`}>
<Switch <Switch
@@ -1,107 +1,122 @@
"use client"; 'use client';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import styles from "@/app/home/plugins/plugins.module.css"; import styles from '@/app/home/plugins/plugins.module.css';
import { PluginMarketCardVO } from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; import { PluginMarketCardVO } from '@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO';
import PluginMarketCardComponent from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent"; import PluginMarketCardComponent from '@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent';
import { Input, Pagination } from "antd"; import { Input, Pagination } from 'antd';
import { spaceClient } from "@/app/infra/http/HttpClient"; import { spaceClient } from '@/app/infra/http/HttpClient';
export default function PluginMarketComponent() { export default function PluginMarketComponent() {
const [marketPluginList, setMarketPluginList] = useState< const [marketPluginList, setMarketPluginList] = useState<
PluginMarketCardVO[] PluginMarketCardVO[]
>([]); >([]);
const [totalCount, setTotalCount] = useState(0); const [totalCount, setTotalCount] = useState(0);
const [nowPage, setNowPage] = useState(1); const [nowPage, setNowPage] = useState(1);
const [searchKeyword, setSearchKeyword] = useState(""); const [searchKeyword, setSearchKeyword] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const pageSize = 10; const pageSize = 10;
useEffect(() => { useEffect(() => {
initData(); initData();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
function initData() { function initData() {
getPluginList(); getPluginList();
} }
function onInputSearchKeyword(keyword: string) { function onInputSearchKeyword(keyword: string) {
// 这里记得加防抖,暂时没加 // 这里记得加防抖,暂时没加
setSearchKeyword(keyword); setSearchKeyword(keyword);
setNowPage(1); setNowPage(1);
getPluginList(1, keyword); getPluginList(1, keyword);
} }
function getPluginList( function getPluginList(
page: number = nowPage, page: number = nowPage,
keyword: string = searchKeyword keyword: string = searchKeyword,
) { ) {
setLoading(true); setLoading(true);
spaceClient.getMarketPlugins(page, pageSize, keyword).then((res) => { spaceClient
setMarketPluginList( .getMarketPlugins(page, pageSize, keyword)
res.plugins.map( .then((res) => {
(marketPlugin) => setMarketPluginList(
new PluginMarketCardVO({ res.plugins.map(
author: marketPlugin.author, (marketPlugin) =>
description: marketPlugin.description, new PluginMarketCardVO({
githubURL: marketPlugin.repository, author: marketPlugin.author,
name: marketPlugin.name, description: marketPlugin.description,
pluginId: String(marketPlugin.ID), githubURL: marketPlugin.repository,
starCount: marketPlugin.stars, name: marketPlugin.name,
version: "version" in marketPlugin ? String(marketPlugin.version) : "1.0.0", // Default version if not provided pluginId: String(marketPlugin.ID),
}) starCount: marketPlugin.stars,
) version:
); 'version' in marketPlugin
setTotalCount(res.total); ? String(marketPlugin.version)
setLoading(false); : '1.0.0', // Default version if not provided
console.log("market plugins:", res); }),
}).catch(error => { ),
console.error("获取插件列表失败:", error); );
setLoading(false); setTotalCount(res.total);
}); setLoading(false);
} console.log('market plugins:', res);
})
.catch((error) => {
console.error('获取插件列表失败:', error);
setLoading(false);
});
}
function handlePageChange(page: number) { function handlePageChange(page: number) {
setNowPage(page); setNowPage(page);
getPluginList(page); getPluginList(page);
} }
return ( return (
<div className={`${styles.marketComponentBody}`}> <div className={`${styles.marketComponentBody}`}>
<Input <Input
style={{ style={{
width: '300px', width: '300px',
marginBottom: '10px', marginBottom: '10px',
}} }}
value={searchKeyword} value={searchKeyword}
placeholder="搜索插件" placeholder="搜索插件"
onChange={(e) => onInputSearchKeyword(e.target.value)} onChange={(e) => onInputSearchKeyword(e.target.value)}
/> />
<div className={`${styles.pluginListContainer}`}> <div className={`${styles.pluginListContainer}`}>
{loading ? ( {loading ? (
<div style={{ textAlign: 'center', padding: '20px' }}>...</div> <div style={{ textAlign: 'center', padding: '20px' }}>...</div>
) : marketPluginList.length === 0 ? ( ) : marketPluginList.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}></div> <div style={{ textAlign: 'center', padding: '20px' }}>
) : (
marketPluginList.map((vo, index) => ( </div>
<div key={`${vo.pluginId}-${index}`}> ) : (
<PluginMarketCardComponent cardVO={vo} /> marketPluginList.map((vo, index) => (
</div> <div key={`${vo.pluginId}-${index}`}>
)) <PluginMarketCardComponent cardVO={vo} />
)}
</div> </div>
{totalCount > 0 && ( ))
<div style={{ display: 'flex', justifyContent: 'center', width: '100%', marginTop: '20px' }}> )}
<Pagination </div>
current={nowPage} {totalCount > 0 && (
total={totalCount} <div
pageSize={pageSize} style={{
onChange={handlePageChange} display: 'flex',
showSizeChanger={false} justifyContent: 'center',
/> width: '100%',
</div> marginTop: '20px',
)} }}
>
<Pagination
current={nowPage}
total={totalCount}
pageSize={pageSize}
onChange={handlePageChange}
showSizeChanger={false}
/>
</div> </div>
) )}
</div>
);
} }
@@ -1,15 +1,15 @@
import styles from "./pluginMarketCard.module.css"; import styles from './pluginMarketCard.module.css';
import { GithubOutlined, StarOutlined } from "@ant-design/icons"; import { GithubOutlined, StarOutlined } from '@ant-design/icons';
import { PluginMarketCardVO } from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; import { PluginMarketCardVO } from '@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO';
import { Button } from "antd"; import { Button } from 'antd';
export default function PluginMarketCardComponent({ export default function PluginMarketCardComponent({
cardVO cardVO,
}: { }: {
cardVO: PluginMarketCardVO; cardVO: PluginMarketCardVO;
}) { }) {
function handleInstallClick(pluginId: string) { function handleInstallClick(pluginId: string) {
console.log("Install plugin: ", pluginId); console.log('Install plugin: ', pluginId);
} }
return ( return (
@@ -19,7 +19,7 @@ export default function PluginMarketCardComponent({
{/* left author */} {/* left author */}
<div className={`${styles.fontGray}`}>{cardVO.author}</div> <div className={`${styles.fontGray}`}>{cardVO.author}</div>
{/* right icon */} {/* right icon */}
<GithubOutlined style={{ fontSize: "26px" }} type="setting" /> <GithubOutlined style={{ fontSize: '26px' }} type="setting" />
</div> </div>
{/* content */} {/* content */}
<div className={`${styles.cardContent}`}> <div className={`${styles.cardContent}`}>
@@ -30,13 +30,13 @@ export default function PluginMarketCardComponent({
<div className={`${styles.cardFooter}`}> <div className={`${styles.cardFooter}`}>
<div className={`${styles.linkSettingContainer}`}> <div className={`${styles.linkSettingContainer}`}>
<div className={`${styles.link}`}> <div className={`${styles.link}`}>
<StarOutlined style={{ fontSize: "22px" }} /> <StarOutlined style={{ fontSize: '22px' }} />
<span style={{ paddingLeft: "5px" }}>{cardVO.starCount}</span> <span style={{ paddingLeft: '5px' }}>{cardVO.starCount}</span>
</div> </div>
</div> </div>
<Button <Button
type="primary" type="primary"
size={"small"} size={'small'}
onClick={() => { onClick={() => {
handleInstallClick(cardVO.pluginId); handleInstallClick(cardVO.pluginId);
}} }}
@@ -1,29 +1,29 @@
export interface IPluginMarketCardVO { export interface IPluginMarketCardVO {
pluginId: string; pluginId: string;
author: string, author: string;
name: string, name: string;
description: string, description: string;
starCount: number, starCount: number;
githubURL: string, githubURL: string;
version: string, version: string;
} }
export class PluginMarketCardVO implements IPluginMarketCardVO { export class PluginMarketCardVO implements IPluginMarketCardVO {
pluginId: string; pluginId: string;
description: string; description: string;
name: string; name: string;
author: string; author: string;
githubURL: string; githubURL: string;
starCount: number; starCount: number;
version: string; version: string;
constructor(prop: IPluginMarketCardVO) { constructor(prop: IPluginMarketCardVO) {
this.description = prop.description this.description = prop.description;
this.name = prop.name this.name = prop.name;
this.author = prop.author this.author = prop.author;
this.githubURL = prop.githubURL this.githubURL = prop.githubURL;
this.starCount = prop.starCount this.starCount = prop.starCount;
this.pluginId = prop.pluginId this.pluginId = prop.pluginId;
this.version = prop.version this.version = prop.version;
} }
} }
+33 -35
View File
@@ -57,7 +57,6 @@ export interface ApiRespPipelines {
pipelines: Pipeline[]; pipelines: Pipeline[];
} }
export interface Pipeline { export interface Pipeline {
uuid: string; uuid: string;
name: string; name: string;
@@ -203,7 +202,7 @@ export interface MarketPlugin {
artifacts_path: string; artifacts_path: string;
stars: number; stars: number;
downloads: number; downloads: number;
status: "initialized" | "mounted"; // 可根据实际状态值扩展联合类型 status: 'initialized' | 'mounted'; // 可根据实际状态值扩展联合类型
synced_at: string; synced_at: string;
pushed_at: string; // 最后一次代码推送时间 pushed_at: string; // 最后一次代码推送时间
} }
@@ -213,24 +212,23 @@ export interface MarketPluginResponse {
total: number; total: number;
} }
interface GetPipelineConfig { interface GetPipelineConfig {
ai: { ai: {
"dashscope-app-api": { 'dashscope-app-api': {
"api-key": string; 'api-key': string;
"app-id": string; 'app-id': string;
"app-type": "agent" | "workflow"; 'app-type': 'agent' | 'workflow';
"references-quote"?: string; 'references-quote'?: string;
}; };
"dify-service-api": { 'dify-service-api': {
"api-key": string; 'api-key': string;
"app-type": "chat" | "agent" | "workflow"; 'app-type': 'chat' | 'agent' | 'workflow';
"base-url": string; 'base-url': string;
"thinking-convert": "plain" | "original" | "remove"; 'thinking-convert': 'plain' | 'original' | 'remove';
timeout?: number; timeout?: number;
}; };
"local-agent": { 'local-agent': {
"max-round": number; 'max-round': number;
model: string; model: string;
prompt: Array<{ prompt: Array<{
content: string; content: string;
@@ -238,50 +236,50 @@ interface GetPipelineConfig {
}>; }>;
}; };
runner: { runner: {
runner: "local-agent" | "dify-service-api" | "dashscope-app-api"; runner: 'local-agent' | 'dify-service-api' | 'dashscope-app-api';
}; };
}; };
output: { output: {
"force-delay": { 'force-delay': {
max: number; max: number;
min: number; min: number;
}; };
"long-text-processing": { 'long-text-processing': {
"font-path": string; 'font-path': string;
strategy: "forward" | "image"; strategy: 'forward' | 'image';
threshold: number; threshold: number;
}; };
misc: { misc: {
"at-sender": boolean; 'at-sender': boolean;
"hide-exception": boolean; 'hide-exception': boolean;
"quote-origin": boolean; 'quote-origin': boolean;
"track-function-calls": boolean; 'track-function-calls': boolean;
}; };
}; };
safety: { safety: {
"content-filter": { 'content-filter': {
"check-sensitive-words": boolean; 'check-sensitive-words': boolean;
scope: "all" | "income-msg" | "output-msg"; scope: 'all' | 'income-msg' | 'output-msg';
}; };
"rate-limit": { 'rate-limit': {
limitation: number; limitation: number;
strategy: "drop" | "wait"; strategy: 'drop' | 'wait';
"window-length": number; 'window-length': number;
}; };
}; };
trigger: { trigger: {
"access-control": { 'access-control': {
blacklist: string[]; blacklist: string[];
mode: "blacklist" | "whitelist"; mode: 'blacklist' | 'whitelist';
whitelist: string[]; whitelist: string[];
}; };
"group-respond-rules": { 'group-respond-rules': {
at: boolean; at: boolean;
prefix: string[]; prefix: string[];
random: number; random: number;
regexp: string[]; regexp: string[];
}; };
"ignore-rules": { 'ignore-rules': {
prefix: string[]; prefix: string[];
regexp: string[]; regexp: string[];
}; };
@@ -302,4 +300,4 @@ interface GetPipeline {
export interface GetPipelineResponseData { export interface GetPipelineResponseData {
pipeline: GetPipeline; pipeline: GetPipeline;
} }
@@ -1,25 +1,25 @@
import styles from "./createCartComponent.module.css"; import styles from './createCartComponent.module.css';
export default function CreateCardComponent({ export default function CreateCardComponent({
height, height,
plusSize, plusSize,
onClick, onClick,
}: { }: {
height: number; height: number;
plusSize: number; plusSize: number;
onClick: () => void onClick: () => void;
}) { }) {
return ( return (
<div <div
className={`${styles.cardContainer} ${styles.createCardContainer} `} className={`${styles.cardContainer} ${styles.createCardContainer} `}
style={{ style={{
width: `100%`, width: `100%`,
height: `${height}px`, height: `${height}px`,
fontSize: `${plusSize}px` fontSize: `${plusSize}px`,
}} }}
onClick={onClick} onClick={onClick}
> >
+ +
</div> </div>
) );
} }
+3 -3
View File
@@ -1,4 +1,4 @@
export interface I18NText { export interface I18NText {
en_US: string; en_US: string;
zh_CN: string; zh_CN: string;
} }
+63 -63
View File
@@ -2,8 +2,8 @@ import axios, {
AxiosInstance, AxiosInstance,
AxiosRequestConfig, AxiosRequestConfig,
AxiosResponse, AxiosResponse,
AxiosError AxiosError,
} from "axios"; } from 'axios';
import { import {
ApiRespProviderRequesters, ApiRespProviderRequesters,
ApiRespProviderRequester, ApiRespProviderRequester,
@@ -11,7 +11,6 @@ import {
ApiRespProviderLLMModel, ApiRespProviderLLMModel,
LLMModel, LLMModel,
ApiRespPipelines, ApiRespPipelines,
ApiRespPipeline,
Pipeline, Pipeline,
ApiRespPlatformAdapters, ApiRespPlatformAdapters,
ApiRespPlatformAdapter, ApiRespPlatformAdapter,
@@ -27,9 +26,10 @@ import {
ApiRespAsyncTasks, ApiRespAsyncTasks,
ApiRespAsyncTask, ApiRespAsyncTask,
ApiRespUserToken, ApiRespUserToken,
MarketPluginResponse, GetPipelineResponseData MarketPluginResponse,
} from "../api/api-types"; GetPipelineResponseData,
import { notification } from "antd"; } from '../api/api-types';
import { notification } from 'antd';
type JSONValue = string | number | boolean | JSONObject | JSONArray | null; type JSONValue = string | number | boolean | JSONObject | JSONArray | null;
interface JSONObject { interface JSONObject {
@@ -60,8 +60,8 @@ class HttpClient {
baseURL: baseURL || this.getBaseUrl(), baseURL: baseURL || this.getBaseUrl(),
timeout: 15000, timeout: 15000,
headers: { headers: {
"Content-Type": "application/json" 'Content-Type': 'application/json',
} },
}); });
this.disableToken = disableToken || false; this.disableToken = disableToken || false;
this.initInterceptors(); this.initInterceptors();
@@ -69,26 +69,26 @@ class HttpClient {
// 兜底URL,如果使用未配置会走到这里 // 兜底URL,如果使用未配置会走到这里
private getBaseUrl(): string { private getBaseUrl(): string {
return "http://localhost:5300"; return 'http://localhost:5300';
// NOT IMPLEMENT // NOT IMPLEMENT
if (typeof window === "undefined") { if (typeof window === 'undefined') {
// 服务端环境 // 服务端环境
return ""; return '';
} }
// 客户端环境 // 客户端环境
return ""; return '';
} }
// 获取Session // 获取Session
private async getSession() { private async getSession() {
// NOT IMPLEMENT // NOT IMPLEMENT
return ""; return '';
} }
// 同步获取Session // 同步获取Session
private getSessionSync() { private getSessionSync() {
// NOT IMPLEMENT // NOT IMPLEMENT
return localStorage.getItem("token"); return localStorage.getItem('token');
} }
// 拦截器配置 // 拦截器配置
@@ -103,14 +103,14 @@ class HttpClient {
// config.headers.Cookie = cookies().toString() // config.headers.Cookie = cookies().toString()
// 客户端添加认证头 // 客户端添加认证头
if (typeof window !== "undefined" && !this.disableToken) { if (typeof window !== 'undefined' && !this.disableToken) {
const session = this.getSessionSync(); const session = this.getSessionSync();
config.headers.Authorization = `Bearer ${session}`; config.headers.Authorization = `Bearer ${session}`;
} }
return config; return config;
}, },
(error) => Promise.reject(error) (error) => Promise.reject(error),
); );
// 响应拦截 // 响应拦截
@@ -128,36 +128,36 @@ class HttpClient {
switch (status) { switch (status) {
case 401: case 401:
window.location.href = "/login"; window.location.href = '/login';
break; break;
case 403: case 403:
console.error("Permission denied:", errMessage); console.error('Permission denied:', errMessage);
break; break;
case 500: case 500:
// TODO 弹Toast窗 // TODO 弹Toast窗
// NOTE: move to component layer for customized message? // NOTE: move to component layer for customized message?
notification.error({ notification.error({
message: "服务器错误", message: '服务器错误',
description: errMessage, description: errMessage,
placement: "bottomRight" placement: 'bottomRight',
}); });
console.error("Server error:", errMessage); console.error('Server error:', errMessage);
break; break;
} }
return Promise.reject({ return Promise.reject({
code: data?.code || status, code: data?.code || status,
message: errMessage, message: errMessage,
data: data?.data || null data: data?.data || null,
}); });
} }
return Promise.reject({ return Promise.reject({
code: -1, code: -1,
message: error.message || "Network Error", message: error.message || 'Network Error',
data: null data: null,
}); });
} },
); );
} }
@@ -165,10 +165,10 @@ class HttpClient {
private convertKeysToCamel(obj: JSONValue): JSONValue { private convertKeysToCamel(obj: JSONValue): JSONValue {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return obj.map((v) => this.convertKeysToCamel(v)); return obj.map((v) => this.convertKeysToCamel(v));
} else if (obj !== null && typeof obj === "object") { } else if (obj !== null && typeof obj === 'object') {
return Object.keys(obj).reduce((acc, key) => { return Object.keys(obj).reduce((acc, key) => {
const camelKey = key.replace(/_([a-z])/g, (_, letter) => const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
letter.toUpperCase() letter.toUpperCase(),
); );
acc[camelKey] = this.convertKeysToCamel((obj as JSONObject)[key]); acc[camelKey] = this.convertKeysToCamel((obj as JSONObject)[key]);
return acc; return acc;
@@ -191,7 +191,7 @@ class HttpClient {
private handleError(error: object): never { private handleError(error: object): never {
if (axios.isCancel(error)) { if (axios.isCancel(error)) {
throw { code: -2, message: "Request canceled", data: null }; throw { code: -2, message: 'Request canceled', data: null };
} }
throw error; throw error;
} }
@@ -200,27 +200,27 @@ class HttpClient {
public get<T = unknown>( public get<T = unknown>(
url: string, url: string,
params?: object, params?: object,
config?: RequestConfig config?: RequestConfig,
) { ) {
return this.request<T>({ method: "get", url, params, ...config }); return this.request<T>({ method: 'get', url, params, ...config });
} }
public post<T = unknown>(url: string, data?: object, config?: RequestConfig) { public post<T = unknown>(url: string, data?: object, config?: RequestConfig) {
return this.request<T>({ method: "post", url, data, ...config }); return this.request<T>({ method: 'post', url, data, ...config });
} }
public put<T = unknown>(url: string, data?: object, config?: RequestConfig) { public put<T = unknown>(url: string, data?: object, config?: RequestConfig) {
return this.request<T>({ method: "put", url, data, ...config }); return this.request<T>({ method: 'put', url, data, ...config });
} }
public delete<T = unknown>(url: string, config?: RequestConfig) { public delete<T = unknown>(url: string, config?: RequestConfig) {
return this.request<T>({ method: "delete", url, ...config }); return this.request<T>({ method: 'delete', url, ...config });
} }
// real api request implementation // real api request implementation
// ============ Provider API ============ // ============ Provider API ============
public getProviderRequesters(): Promise<ApiRespProviderRequesters> { public getProviderRequesters(): Promise<ApiRespProviderRequesters> {
return this.get("/api/v1/provider/requesters"); return this.get('/api/v1/provider/requesters');
} }
public getProviderRequester(name: string): Promise<ApiRespProviderRequester> { public getProviderRequester(name: string): Promise<ApiRespProviderRequester> {
@@ -233,7 +233,7 @@ class HttpClient {
// ============ Provider Model LLM ============ // ============ Provider Model LLM ============
public getProviderLLMModels(): Promise<ApiRespProviderLLMModels> { public getProviderLLMModels(): Promise<ApiRespProviderLLMModels> {
return this.get("/api/v1/provider/models/llm"); return this.get('/api/v1/provider/models/llm');
} }
public getProviderLLMModel(uuid: string): Promise<ApiRespProviderLLMModel> { public getProviderLLMModel(uuid: string): Promise<ApiRespProviderLLMModel> {
@@ -241,7 +241,7 @@ class HttpClient {
} }
public createProviderLLMModel(model: LLMModel): Promise<object> { public createProviderLLMModel(model: LLMModel): Promise<object> {
return this.post("/api/v1/provider/models/llm", model); return this.post('/api/v1/provider/models/llm', model);
} }
public deleteProviderLLMModel(uuid: string): Promise<object> { public deleteProviderLLMModel(uuid: string): Promise<object> {
@@ -251,11 +251,11 @@ class HttpClient {
// ============ Pipeline API ============ // ============ Pipeline API ============
public getGeneralPipelineMetadata(): Promise<object> { public getGeneralPipelineMetadata(): Promise<object> {
// as designed, this method will be deprecated, and only for developer to check the prefered config schema // as designed, this method will be deprecated, and only for developer to check the prefered config schema
return this.get("/api/v1/pipelines/_/metadata"); return this.get('/api/v1/pipelines/_/metadata');
} }
public getPipelines(): Promise<ApiRespPipelines> { public getPipelines(): Promise<ApiRespPipelines> {
return this.get("/api/v1/pipelines"); return this.get('/api/v1/pipelines');
} }
public getPipeline(uuid: string): Promise<GetPipelineResponseData> { public getPipeline(uuid: string): Promise<GetPipelineResponseData> {
@@ -263,7 +263,7 @@ class HttpClient {
} }
public createPipeline(pipeline: Pipeline): Promise<object> { public createPipeline(pipeline: Pipeline): Promise<object> {
return this.post("/api/v1/pipelines", pipeline); return this.post('/api/v1/pipelines', pipeline);
} }
public updatePipeline(uuid: string, pipeline: Pipeline): Promise<object> { public updatePipeline(uuid: string, pipeline: Pipeline): Promise<object> {
@@ -276,7 +276,7 @@ class HttpClient {
// ============ Platform API ============ // ============ Platform API ============
public getAdapters(): Promise<ApiRespPlatformAdapters> { public getAdapters(): Promise<ApiRespPlatformAdapters> {
return this.get("/api/v1/platform/adapters"); return this.get('/api/v1/platform/adapters');
} }
public getAdapter(name: string): Promise<ApiRespPlatformAdapter> { public getAdapter(name: string): Promise<ApiRespPlatformAdapter> {
@@ -289,7 +289,7 @@ class HttpClient {
// ============ Platform Bots ============ // ============ Platform Bots ============
public getBots(): Promise<ApiRespPlatformBots> { public getBots(): Promise<ApiRespPlatformBots> {
return this.get("/api/v1/platform/bots"); return this.get('/api/v1/platform/bots');
} }
public getBot(uuid: string): Promise<ApiRespPlatformBot> { public getBot(uuid: string): Promise<ApiRespPlatformBot> {
@@ -297,7 +297,7 @@ class HttpClient {
} }
public createBot(bot: Bot): Promise<object> { public createBot(bot: Bot): Promise<object> {
return this.post("/api/v1/platform/bots", bot); return this.post('/api/v1/platform/bots', bot);
} }
public updateBot(uuid: string, bot: Bot): Promise<object> { public updateBot(uuid: string, bot: Bot): Promise<object> {
@@ -310,7 +310,7 @@ class HttpClient {
// ============ Plugins API ============ // ============ Plugins API ============
public getPlugins(): Promise<ApiRespPlugins> { public getPlugins(): Promise<ApiRespPlugins> {
return this.get("/api/v1/plugins"); return this.get('/api/v1/plugins');
} }
public getPlugin(author: string, name: string): Promise<ApiRespPlugin> { public getPlugin(author: string, name: string): Promise<ApiRespPlugin> {
@@ -319,7 +319,7 @@ class HttpClient {
public getPluginConfig( public getPluginConfig(
author: string, author: string,
name: string name: string,
): Promise<ApiRespPluginConfig> { ): Promise<ApiRespPluginConfig> {
return this.get(`/api/v1/plugins/${author}/${name}/config`); return this.get(`/api/v1/plugins/${author}/${name}/config`);
} }
@@ -327,7 +327,7 @@ class HttpClient {
public updatePluginConfig( public updatePluginConfig(
author: string, author: string,
name: string, name: string,
config: object config: object,
): Promise<object> { ): Promise<object> {
return this.put(`/api/v1/plugins/${author}/${name}/config`, config); return this.put(`/api/v1/plugins/${author}/${name}/config`, config);
} }
@@ -335,20 +335,20 @@ class HttpClient {
public togglePlugin( public togglePlugin(
author: string, author: string,
name: string, name: string,
target_enabled: boolean target_enabled: boolean,
): Promise<object> { ): Promise<object> {
return this.put(`/api/v1/plugins/${author}/${name}/toggle`, { return this.put(`/api/v1/plugins/${author}/${name}/toggle`, {
target_enabled target_enabled,
}); });
} }
public reorderPlugins(plugins: PluginReorderElement[]): Promise<object> { public reorderPlugins(plugins: PluginReorderElement[]): Promise<object> {
return this.post("/api/v1/plugins/reorder", plugins); return this.post('/api/v1/plugins/reorder', plugins);
} }
public updatePlugin( public updatePlugin(
author: string, author: string,
name: string name: string,
): Promise<AsyncTaskCreatedResp> { ): Promise<AsyncTaskCreatedResp> {
return this.post(`/api/v1/plugins/${author}/${name}/update`); return this.post(`/api/v1/plugins/${author}/${name}/update`);
} }
@@ -356,36 +356,36 @@ class HttpClient {
public getMarketPlugins( public getMarketPlugins(
page: number, page: number,
page_size: number, page_size: number,
query: string query: string,
): Promise<MarketPluginResponse> { ): Promise<MarketPluginResponse> {
return this.post(`/api/v1/market/plugins`, { return this.post(`/api/v1/market/plugins`, {
page, page,
page_size, page_size,
query, query,
sort_by: "stars", sort_by: 'stars',
sort_order: "DESC" sort_order: 'DESC',
}); });
} }
public installPluginFromGithub( public installPluginFromGithub(
source: string source: string,
): Promise<AsyncTaskCreatedResp> { ): Promise<AsyncTaskCreatedResp> {
return this.post("/api/v1/plugins/install/github", { source }); return this.post('/api/v1/plugins/install/github', { source });
} }
public removePlugin( public removePlugin(
author: string, author: string,
name: string name: string,
): Promise<AsyncTaskCreatedResp> { ): Promise<AsyncTaskCreatedResp> {
return this.delete(`/api/v1/plugins/${author}/${name}`); return this.delete(`/api/v1/plugins/${author}/${name}`);
} }
// ============ System API ============ // ============ System API ============
public getSystemInfo(): Promise<ApiRespSystemInfo> { public getSystemInfo(): Promise<ApiRespSystemInfo> {
return this.get("/api/v1/system/info"); return this.get('/api/v1/system/info');
} }
public getAsyncTasks(): Promise<ApiRespAsyncTasks> { public getAsyncTasks(): Promise<ApiRespAsyncTasks> {
return this.get("/api/v1/system/tasks"); return this.get('/api/v1/system/tasks');
} }
public getAsyncTask(id: number): Promise<ApiRespAsyncTask> { public getAsyncTask(id: number): Promise<ApiRespAsyncTask> {
@@ -394,23 +394,23 @@ class HttpClient {
// ============ User API ============ // ============ User API ============
public checkIfInited(): Promise<{ initialized: boolean }> { public checkIfInited(): Promise<{ initialized: boolean }> {
return this.get("/api/v1/user/init"); return this.get('/api/v1/user/init');
} }
public initUser(user: string, password: string): Promise<object> { public initUser(user: string, password: string): Promise<object> {
return this.post("/api/v1/user/init", { user, password }); return this.post('/api/v1/user/init', { user, password });
} }
public authUser(user: string, password: string): Promise<ApiRespUserToken> { public authUser(user: string, password: string): Promise<ApiRespUserToken> {
return this.post("/api/v1/user/auth", { user, password }); return this.post('/api/v1/user/auth', { user, password });
} }
public checkUserToken(): Promise<ApiRespUserToken> { public checkUserToken(): Promise<ApiRespUserToken> {
return this.get("/api/v1/user/check-token"); return this.get('/api/v1/user/check-token');
} }
} }
export const httpClient = new HttpClient("https://version-4.langbot.dev"); export const httpClient = new HttpClient('https://version-4.langbot.dev');
// 临时写法,未来两种Client都继承自HttpClient父类,不允许共享方法 // 临时写法,未来两种Client都继承自HttpClient父类,不允许共享方法
export const spaceClient = new HttpClient("https://space.langbot.app"); export const spaceClient = new HttpClient('https://space.langbot.app');
+5 -8
View File
@@ -1,10 +1,9 @@
import "./global.css" import './global.css';
import type { Metadata } from "next"; import type { Metadata } from 'next';
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: 'Create Next App',
description: "Generated by create next app", description: 'Generated by create next app',
}; };
export default function RootLayout({ export default function RootLayout({
@@ -14,9 +13,7 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body className={``}> <body className={``}>{children}</body>
{children}
</body>
</html> </html>
); );
} }
+18 -18
View File
@@ -1,26 +1,26 @@
'use client'; 'use client';
import React from "react"; import React from 'react';
import { ConfigProvider, theme } from 'antd'; import { ConfigProvider, theme } from 'antd';
export default function LoginLayout({ export default function LoginLayout({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<ConfigProvider <ConfigProvider
theme={{ theme={{
token: { token: {
colorPrimary: '#2288ee', colorPrimary: '#2288ee',
borderRadius: 6, borderRadius: 6,
}, },
algorithm: theme.defaultAlgorithm, algorithm: theme.defaultAlgorithm,
}} }}
> >
<div style={{ width: '100%', height: '100%' }}> <div style={{ width: '100%', height: '100%' }}>
<main style={{ width: '100%', height: '100%' }}>{children}</main> <main style={{ width: '100%', height: '100%' }}>{children}</main>
</div> </div>
</ConfigProvider> </ConfigProvider>
) );
} }
+23 -23
View File
@@ -1,17 +1,17 @@
"use client"; 'use client';
import { Button, Input, Form, Checkbox, Divider } from "antd"; import { Button, Input, Form, Checkbox, Divider } from 'antd';
import { import {
GoogleOutlined, GoogleOutlined,
LockOutlined, LockOutlined,
UserOutlined, UserOutlined,
QqOutlined QqOutlined,
} from "@ant-design/icons"; } from '@ant-design/icons';
import styles from "./login.module.css"; import styles from './login.module.css';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { httpClient } from "@/app/infra/http/HttpClient"; import { httpClient } from '@/app/infra/http/HttpClient';
import "@ant-design/v5-patch-for-react-19"; import '@ant-design/v5-patch-for-react-19';
import { useRouter } from "next/navigation"; import { useRouter } from 'next/navigation';
export default function Home() { export default function Home() {
const router = useRouter(); const router = useRouter();
@@ -32,7 +32,7 @@ export default function Home() {
setIsInitialized(res.initialized); setIsInitialized(res.initialized);
}) })
.catch((err) => { .catch((err) => {
console.log("error at getIsInitialized: ", err); console.log('error at getIsInitialized: ', err);
}); });
} }
@@ -48,10 +48,10 @@ export default function Home() {
httpClient httpClient
.initUser(username, password) .initUser(username, password)
.then((res) => { .then((res) => {
console.log("init user success: ", res); console.log('init user success: ', res);
}) })
.catch((err) => { .catch((err) => {
console.log("init user error: ", err); console.log('init user error: ', err);
}); });
} }
@@ -59,12 +59,12 @@ export default function Home() {
httpClient httpClient
.authUser(username, password) .authUser(username, password)
.then((res) => { .then((res) => {
localStorage.setItem("token", res.token); localStorage.setItem('token', res.token);
console.log("login success: ", res); console.log('login success: ', res);
router.push("/home"); router.push('/home');
}) })
.catch((err) => { .catch((err) => {
console.log("login error: ", err); console.log('login error: ', err);
}); });
} }
@@ -94,8 +94,8 @@ export default function Home() {
<Form.Item <Form.Item
name="email" name="email"
rules={[ rules={[
{ required: true, message: "请输入邮箱!" }, { required: true, message: '请输入邮箱!' },
{ type: "email", message: "请输入有效的邮箱地址!" } { type: 'email', message: '请输入有效的邮箱地址!' },
]} ]}
> >
<Input <Input
@@ -107,7 +107,7 @@ export default function Home() {
<Form.Item <Form.Item
name="password" name="password"
rules={[{ required: true, message: "请输入密码!" }]} rules={[{ required: true, message: '请输入密码!' }]}
> >
<Input.Password <Input.Password
placeholder="输入密码" placeholder="输入密码"
@@ -162,9 +162,9 @@ export default function Home() {
> >
{isRegisterMode {isRegisterMode
? isInitialized ? isInitialized
? "暂不提供注册" ? '暂不提供注册'
: "注册" : '注册'
: "登录"} : '登录'}
</Button> </Button>
<Divider className={styles.divider}></Divider> <Divider className={styles.divider}></Divider>
@@ -179,7 +179,7 @@ export default function Home() {
使 使
</Button> </Button>
</div> </div>
<div style={{ height: "10px" }}></div> <div style={{ height: '10px' }}></div>
<div className={styles.socialLogin}> <div className={styles.socialLogin}>
<Button <Button
className={styles.socialButton} className={styles.socialButton}
+119 -66
View File
@@ -1,78 +1,131 @@
"use client"; 'use client';
import { Button, Typography, Space, Layout, Row, Col, Result } from 'antd'; import { Button, Typography, Space, Layout, Row, Col, Result } from 'antd';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import Image from 'next/image';
const { Title, Paragraph } = Typography; const { Title, Paragraph } = Typography;
export default function NotFound() { export default function NotFound() {
const router = useRouter(); const router = useRouter();
return ( return (
<Layout style={{ minHeight: '100vh', background: 'white' }}> <Layout style={{ minHeight: '100vh', background: 'white' }}>
<Row justify="center" align="middle" style={{ minHeight: '100vh' }}> <Row justify="center" align="middle" style={{ minHeight: '100vh' }}>
<Col xs={22} sm={20} md={18} lg={14} xl={10}> <Col xs={22} sm={20} md={18} lg={14} xl={10}>
<div className="error-container" style={{ width: '100%', padding: '20px 0', textAlign: 'center' }}> <div
<div className="error-card" style={{ className="error-container"
display: 'flex', style={{ width: '100%', padding: '20px 0', textAlign: 'center' }}
flexDirection: 'column', >
alignItems: 'center', <div
padding: '24px' className="error-card"
}}> style={{
{/* Ant Design 图标,可以换成 Langbot 的 Logo */} display: 'flex',
<div style={{ marginBottom: '20px', maxWidth: '100%', height: 'auto' }}> flexDirection: 'column',
<Result alignItems: 'center',
status="404" padding: '24px',
title={null} }}
subTitle={null} >
style={{ padding: 0 }} {/* Ant Design 图标,可以换成 Langbot 的 Logo */}
/> <div
</div> style={{
marginBottom: '20px',
maxWidth: '100%',
height: 'auto',
}}
>
<Result
status="404"
title={null}
subTitle={null}
style={{ padding: 0 }}
/>
</div>
<div className="error-text" style={{ textAlign: 'center', marginBottom: '24px' }}> <div
<Title level={1} style={{ margin: '0 0 16px 0', fontSize: '72px', fontWeight: 'bold', color: '#333' }}> className="error-text"
404 style={{ textAlign: 'center', marginBottom: '24px' }}
</Title> >
<Title level={3} style={{ margin: '0 0 8px 0', fontWeight: 'normal', color: '#333' }}> <Title
level={1}
</Title> style={{
<Paragraph style={{ fontSize: '16px', color: '#666', maxWidth: '450px', margin: '0 auto 32px auto' }}> margin: '0 0 16px 0',
URL fontSize: '72px',
</Paragraph> fontWeight: 'bold',
</div> color: '#333',
}}
>
404
</Title>
<Title
level={3}
style={{
margin: '0 0 8px 0',
fontWeight: 'normal',
color: '#333',
}}
>
</Title>
<Paragraph
style={{
fontSize: '16px',
color: '#666',
maxWidth: '450px',
margin: '0 auto 32px auto',
}}
>
URL
</Paragraph>
</div>
<div className="error-button" style={{ marginBottom: '24px' }}> <div className="error-button" style={{ marginBottom: '24px' }}>
<Space> <Space>
<Button type="primary" style={{ <Button
backgroundColor: '#2288ee', type="primary"
borderColor: '#2288ee', style={{
borderRadius: '4px', backgroundColor: '#2288ee',
height: '36px', borderColor: '#2288ee',
padding: '0 16px' borderRadius: '4px',
}} onClick={() => router.back()}> height: '36px',
padding: '0 16px',
</Button> }}
<Button style={{ onClick={() => router.back()}
borderColor: '#d9d9d9', >
borderRadius: '4px',
height: '36px', </Button>
padding: '0 16px' <Button
}} onClick={() => router.push('/')}> style={{
borderColor: '#d9d9d9',
</Button> borderRadius: '4px',
</Space> height: '36px',
</div> padding: '0 16px',
}}
onClick={() => router.push('/')}
>
</Button>
</Space>
</div>
<div className="error-support" style={{ textAlign: 'center', marginTop: '16px' }}> <div
<Paragraph style={{ fontSize: '14px', color: '#666' }}> className="error-support"
<a href="mailto:support@qq.com" style={{ color: '#000', textDecoration: 'none' }}>support@qq.com</a> style={{ textAlign: 'center', marginTop: '16px' }}
</Paragraph> >
</div> <Paragraph style={{ fontSize: '14px', color: '#666' }}>
</div> {' '}
</div> <a
</Col> href="mailto:support@qq.com"
</Row> style={{ color: '#000', textDecoration: 'none' }}
</Layout> >
); support@qq.com
</a>
</Paragraph>
</div>
</div>
</div>
</Col>
</Row>
</Layout>
);
} }
+1 -5
View File
@@ -1,7 +1,3 @@
export default function Home() { export default function Home() {
return ( return <div className={``}></div>;
<div className={``}>
</div>
);
} }