Commit e039c38f authored by 文乙冲's avatar 文乙冲

init

parents
{
"extends": "next/core-web-vitals"
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
{
}
\ No newline at end of file
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
//next-i18next.config.js
/**
* @type {import('next-i18next').UserConfig}
*/
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh'],
localeDetection: false
},
// localePath:
// typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n',
// reloadOnPrerender: process.env.NODE_ENV === 'development'
};
const { i18n } = require('./next-i18next.config');
const path = require('path');
const fs = require('fs');
const isDev = process.env.NODE_ENV === 'development';
/** @type {import('next').NextConfig} */
const nextConfig = {
i18n,
output: 'standalone',
reactStrictMode: isDev ? false : true,
compress: true,
webpack(config, { isServer, nextRuntime }) {
Object.assign(config.resolve.alias, {
'@mongodb-js/zstd': false,
'@aws-sdk/credential-providers': false,
snappy: false,
aws4: false,
'mongodb-client-encryption': false,
kerberos: false,
'supports-color': false,
'bson-ext': false,
'pg-native': false
});
config.module = {
...config.module,
rules: config.module.rules.concat([
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
},
{
test: /\.node$/,
use: [{ loader: 'nextjs-node-loader' }]
}
]),
exprContextCritical: false,
unknownContextCritical: false
};
if (!config.externals) {
config.externals = [];
}
if (isServer) {
// config.externals.push('@zilliz/milvus2-sdk-node');
// if (nextRuntime === 'nodejs') {
// const oldEntry = config.entry;
// config = {
// ...config,
// async entry(...args) {
// const entries = await oldEntry(...args);
// return {
// ...entries,
// // ...getWorkerConfig(),
// 'worker/systemPluginRun': path.resolve(
// process.cwd(),
// './src/app/plugins/runtime/worker.ts'
// )
// };
// }
// };
// }
} else {
config.resolve = {
...config.resolve,
fallback: {
...config.resolve.fallback,
fs: false
}
};
}
config.experiments = {
asyncWebAssembly: true,
layers: true
};
return config;
},
transpilePackages: ['ahooks'],
experimental: {
// 优化 Server Components 的构建和运行,避免不必要的客户端打包。
serverComponentsExternalPackages: ['mongoose', 'pg', '@node-rs/jieba', 'duck-duck-scrape','@js-preview/docx'],
outputFileTracingRoot: path.join(__dirname, './output')
}
};
module.exports = nextConfig;
// function getWorkerConfig() {
// const result = fs.readdirSync(path.resolve(__dirname, './packages/service/worker'));
// // 获取所有的目录名
// const folderList = result.filter((item) => {
// return fs
// .statSync(path.resolve(__dirname, '../../packages/service/worker', item))
// .isDirectory();
// });
// /*
// {
// 'worker/htmlStr2Md': path.resolve(
// process.cwd(),
// '../../packages/service/worker/htmlStr2Md/index.ts'
// ),
// 'worker/countGptMessagesTokens': path.resolve(
// process.cwd(),
// '../../packages/service/worker/countGptMessagesTokens/index.ts'
// ),
// 'worker/readFile': path.resolve(
// process.cwd(),
// '../../packages/service/worker/readFile/index.ts'
// )
// }
// */
// const workerConfig = folderList.reduce((acc, item) => {
// acc[`worker/${item}`] = path.resolve(
// process.cwd(),
// `../../packages/service/worker/${item}/index.ts`
// );
// return acc;
// }, {});
// return workerConfig;
// }
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;
This diff is collapsed.
{
"name": "contractv2",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "18.3.1",
"react-dom": "18.3.1",
"next": "14.2.5",
"next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"@chakra-ui/anatomy": "2.2.1",
"@chakra-ui/icons": "2.1.1",
"@chakra-ui/next-js": "2.1.5",
"@chakra-ui/react": "2.8.1",
"@chakra-ui/styled-system": "2.9.1",
"@chakra-ui/system": "2.6.1",
"@js-preview/docx": "^1.6.0",
"@types/nprogress": "^0.2.0",
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0",
"axios": "^1.5.1",
"jsdiff-esm": "^1.0.1",
"mammoth": "^1.6.0",
"mongoose": "^7.0.2",
"multer": "1.4.5-lts.1",
"sass": "^1.58.3",
"zustand": "^4.3.5",
"openai": "4.57.0",
"dayjs": "^1.11.7",
"chalk": "^5.3.0",
"i18next": "23.11.5",
"nprogress": "^0.2.0",
"use-context-selector": "^1.4.4",
"jschardet": "3.1.1",
"react-hook-form": "7.43.1",
"ahooks": "^3.7.11",
"@tanstack/react-query": "^4.24.10"
},
"devDependencies": {
"typescript": "^5.1.3",
"@types/node": "^20.14.2",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "8.56.0",
"eslint-config-next": "14.2.3",
"@svgr/webpack": "^6.5.1",
"@eslint/eslintrc": "^3",
"@types/multer": "^1.4.10"
}
}
This diff is collapsed.
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
\ No newline at end of file
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 502000 */
export enum AppErrEnum {
unExist = 'appUnExist',
unAuthApp = 'unAuthApp',
invalidOwner = 'invalidOwner',
invalidAppType = 'invalidAppType'
}
const appErrList = [
{
statusText: AppErrEnum.unExist,
message: i18nT('common:code_error.app_error.not_exist')
},
{
statusText: AppErrEnum.unAuthApp,
message: i18nT('common:code_error.app_error.un_auth_app')
},
{
statusText: AppErrEnum.invalidOwner,
message: i18nT('common:code_error.app_error.invalid_owner')
},
{
statusText: AppErrEnum.invalidAppType,
message: i18nT('common:code_error.app_error.invalid_app_type')
}
];
export default appErrList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 502000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${AppErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 504000 */
export enum ChatErrEnum {
unAuthChat = 'unAuthChat'
}
const errList = [
{
statusText: ChatErrEnum.unAuthChat,
message: i18nT('common:code_error.chat_error.un_auth')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 504000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${ChatErrEnum}`>);
import { ErrType } from '../errorCode';
/* dataset: 507000 */
const startCode = 507000;
export enum CommonErrEnum {
fileNotFound = 'fileNotFound',
unAuthFile = 'unAuthFile',
missingParams = 'missingParams',
inheritPermissionError = 'inheritPermissionError'
}
const datasetErr = [
{
statusText: CommonErrEnum.fileNotFound,
message: 'error.fileNotFound'
},
{
statusText: CommonErrEnum.unAuthFile,
message: 'error.unAuthFile'
},
{
statusText: CommonErrEnum.missingParams,
message: 'error.missingParams'
},
{
statusText: CommonErrEnum.inheritPermissionError,
message: 'error.inheritPermissionError'
}
];
export default datasetErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: startCode + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${CommonErrEnum}`>);
import { ErrType } from '../errorCode';
/* dataset: 501000 */
export enum DatasetErrEnum {
unExist = 'unExistDataset',
unAuthDataset = 'unAuthDataset',
unCreateCollection = 'unCreateCollection',
unAuthDatasetCollection = 'unAuthDatasetCollection',
unAuthDatasetData = 'unAuthDatasetData',
unAuthDatasetFile = 'unAuthDatasetFile',
unLinkCollection = 'unLinkCollection',
invalidVectorModelOrQAModel = 'invalidVectorModelOrQAModel'
}
const datasetErr = [
{
statusText: DatasetErrEnum.unExist,
message: 'core.dataset.error.unExistDataset'
},
{
statusText: DatasetErrEnum.unAuthDataset,
message: 'core.dataset.error.unAuthDataset'
},
{
statusText: DatasetErrEnum.unAuthDatasetCollection,
message: 'core.dataset.error.unAuthDatasetCollection'
},
{
statusText: DatasetErrEnum.unAuthDatasetData,
message: 'core.dataset.error.unAuthDatasetData'
},
{
statusText: DatasetErrEnum.unAuthDatasetFile,
message: 'core.dataset.error.unAuthDatasetFile'
},
{
statusText: DatasetErrEnum.unCreateCollection,
message: 'core.dataset.error.unCreateCollection'
},
{
statusText: DatasetErrEnum.unLinkCollection,
message: 'core.dataset.error.unLinkCollection'
},
{
statusText: DatasetErrEnum.invalidVectorModelOrQAModel,
message: 'core.dataset.error.invalidVectorModelOrQAModel'
}
];
export default datasetErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 501000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${DatasetErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 506000 */
export enum OpenApiErrEnum {
unExist = 'openapiUnExist',
unAuth = 'openapiUnAuth',
exceedLimit = 'openapiExceedLimit'
}
const errList = [
{
statusText: OpenApiErrEnum.unExist,
message: i18nT('common:code_error.openapi_error.api_key_not_exist')
},
{
statusText: OpenApiErrEnum.unAuth,
message: i18nT('common:code_error.openapi_error.un_auth')
},
{
statusText: OpenApiErrEnum.exceedLimit,
message: i18nT('common:code_error.openapi_error.exceed_limit')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 506000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${OpenApiErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 505000 */
export enum OutLinkErrEnum {
unExist = 'outlinkUnExist',
unAuthLink = 'unAuthLink',
linkUnInvalid = 'linkUnInvalid',
unAuthUser = 'unAuthUser'
}
const errList = [
{
statusText: OutLinkErrEnum.unExist,
message: i18nT('common:code_error.outlink_error.link_not_exist')
},
{
statusText: OutLinkErrEnum.unAuthLink,
message: i18nT('common:code_error.outlink_error.invalid_link')
},
{
code: 501,
statusText: OutLinkErrEnum.linkUnInvalid,
message: i18nT('common:code_error.outlink_error.invalid_link') // 使用相同的错误消息
},
{
statusText: OutLinkErrEnum.unAuthUser,
message: i18nT('common:code_error.outlink_error.un_auth_user')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: cur?.code || 505000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${OutLinkErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 508000 */
export enum PluginErrEnum {
unExist = 'pluginUnExist',
unAuth = 'pluginUnAuth'
}
const errList = [
{
statusText: PluginErrEnum.unExist,
message: i18nT('common:code_error.plugin_error.not_exist')
},
{
statusText: PluginErrEnum.unAuth,
message: i18nT('common:code_error.plugin_error.un_auth')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 508000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${PluginErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 509000 */
export enum SystemErrEnum {
communityVersionNumLimit = 'communityVersionNumLimit'
}
const systemErr = [
{
statusText: SystemErrEnum.communityVersionNumLimit,
message: i18nT('common:code_error.system_error.community_version_num_limit')
}
];
export default systemErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 509000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${SystemErrEnum}`>);
import {ErrType} from '../errorCode';
import {i18nT} from '../../../../web/i18n/utils';
/* team: 500000 */
export enum TeamErrEnum {
teamOverSize = 'teamOverSize',
unAuthTeam = 'unAuthTeam',
aiPointsNotEnough = 'aiPointsNotEnough',
datasetSizeNotEnough = 'datasetSizeNotEnough',
datasetAmountNotEnough = 'datasetAmountNotEnough',
appAmountNotEnough = 'appAmountNotEnough',
pluginAmountNotEnough = 'pluginAmountNotEnough',
websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough'
}
const teamErr = [
{
statusText: TeamErrEnum.teamOverSize,
message: i18nT('common:code_error.team_error.over_size')
},
{
statusText: TeamErrEnum.unAuthTeam,
message: i18nT('common:code_error.team_error.un_auth')
},
{
statusText: TeamErrEnum.aiPointsNotEnough,
message: i18nT('common:code_error.team_error.ai_points_not_enough')
}, // 需要定义或留空
{
statusText: TeamErrEnum.datasetSizeNotEnough,
message: i18nT('common:code_error.team_error.dataset_size_not_enough')
},
{
statusText: TeamErrEnum.datasetAmountNotEnough,
message: i18nT('common:code_error.team_error.dataset_amount_not_enough')
},
{
statusText: TeamErrEnum.appAmountNotEnough,
message: i18nT('common:code_error.team_error.app_amount_not_enough')
},
{
statusText: TeamErrEnum.pluginAmountNotEnough,
message: i18nT('common:code_error.team_error.plugin_amount_not_enough')
},
{
statusText: TeamErrEnum.websiteSyncNotEnough,
message: i18nT('common:code_error.team_error.website_sync_not_enough')
},
{
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
}
];
export default teamErr.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 500000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${TeamErrEnum}`>);
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */
export enum UserErrEnum {
unAuthUser = 'unAuthUser',
unAuthRole = 'unAuthRole',
binVisitor = 'binVisitor',
balanceNotEnough = 'balanceNotEnough'
}
const errList = [
{
statusText: UserErrEnum.unAuthUser,
message: i18nT('common:code_error.user_error.un_auth_user')
},
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor')
}, // 身份校验未通过
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor_guest')
}, // 游客身份
{
statusText: UserErrEnum.balanceNotEnough,
message: i18nT('common:code_error.user_error.balance_not_enough')
}
];
export default errList.reduce((acc, cur, index) => {
return {
...acc,
[cur.statusText]: {
code: 503000 + index,
statusText: cur.statusText,
message: cur.message,
data: null
}
};
}, {} as ErrType<`${UserErrEnum}`>);
import appErr from './code/app';
import chatErr from './code/chat';
import datasetErr from './code/dataset';
import openapiErr from './code/openapi';
import pluginErr from './code/plugin';
import outLinkErr from './code/outLink';
import teamErr from './code/team';
import userErr from './code/user';
import commonErr from './code/common';
import SystemErrEnum from './code/system';
import { i18nT } from '../../../web/i18n/utils';
export const ERROR_CODE: { [key: number]: string } = {
400: i18nT('common:code_error.error_code.400'),
401: i18nT('common:code_error.error_code.401'),
403: i18nT('common:code_error.error_code.403'),
404: i18nT('common:code_error.error_code.404'),
405: i18nT('common:code_error.error_code.405'),
406: i18nT('common:code_error.error_code.406'),
410: i18nT('common:code_error.error_code.410'),
422: i18nT('common:code_error.error_code.422'),
500: i18nT('common:code_error.error_code.500'),
502: i18nT('common:code_error.error_code.502'),
503: i18nT('common:code_error.error_code.503'),
504: i18nT('common:code_error.error_code.504')
};
export const TOKEN_ERROR_CODE: Record<number, string> = {
403: i18nT('common:code_error.token_error_code.403')
};
export const proxyError: Record<string, boolean> = {
ECONNABORTED: true,
ECONNRESET: true
};
export enum ERROR_ENUM {
unAuthorization = 'unAuthorization',
insufficientQuota = 'insufficientQuota',
unAuthModel = 'unAuthModel',
unAuthApiKey = 'unAuthApiKey',
unAuthFile = 'unAuthFile'
}
export type ErrType<T> = Record<
string,
{
code: number;
statusText: T;
message: string;
data: null;
}
>;
export const ERROR_RESPONSE: Record<
any,
{
code: number;
statusText: string;
message: string;
data?: any;
}
> = {
[ERROR_ENUM.unAuthorization]: {
code: 403,
statusText: ERROR_ENUM.unAuthorization,
message: i18nT('common:code_error.error_message.403'),
data: null
},
[ERROR_ENUM.insufficientQuota]: {
code: 510,
statusText: ERROR_ENUM.insufficientQuota,
message: i18nT('common:code_error.error_message.510'),
data: null
},
[ERROR_ENUM.unAuthModel]: {
code: 511,
statusText: ERROR_ENUM.unAuthModel,
message: i18nT('common:code_error.error_message.511'),
data: null
},
[ERROR_ENUM.unAuthFile]: {
code: 513,
statusText: ERROR_ENUM.unAuthFile,
message: i18nT('common:code_error.error_message.513'),
data: null
},
[ERROR_ENUM.unAuthApiKey]: {
code: 514,
statusText: ERROR_ENUM.unAuthApiKey,
message: i18nT('common:code_error.error_message.514'),
data: null
},
...appErr,
...chatErr,
...datasetErr,
...openapiErr,
...outLinkErr,
...teamErr,
...userErr,
...pluginErr,
...commonErr,
...SystemErrEnum
};
import { replaceSensitiveText } from '../string/tools';
export const getErrText = (err: any, def = ''): any => {
const msg: string =
typeof err === 'string'
? err
: err?.response?.data?.message || err?.response?.message || err?.message || def;
msg && console.log('error =>', msg);
return replaceSensitiveText(msg);
};
/* mongo fs bucket */
export enum BucketNameEnum {
dataset = 'dataset',
chat = 'chat'
}
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: 'file:bucket_file',
previewExpireMinutes: 30 // 30 minutes
},
[BucketNameEnum.chat]: {
label: 'file:bucket_chat',
previewExpireMinutes: 7 * 24 * 60 // 7 days
}
};
export const ReadFileBaseUrl = '/api/common/file/read';
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
This diff is collapsed.
import crypto from 'crypto';
import { customAlphabet } from 'nanoid';
import {SECTOR_CODE_ENUM} from "../../support/sector/type";
/* check string is a mongo objectId */
export function strIsMongoId(id?: string): boolean {
if (!id) return false
const mongoIdPattern = /^[0-9a-fA-F]{24}$/;
return mongoIdPattern.test(id);
}
/* check string is a web link */
export function strIsLink(str?: string) {
if (!str) return false;
if (/^((http|https)?:\/\/|www\.|\/)[^\s/$.?#].[^\s]*$/i.test(str)) return true;
return false;
}
/* hash string */
export const hashStr = (str: string) => {
return crypto.createHash('sha256').update(str).digest('hex');
};
/* simple text, remove chinese space and extra \n */
export const simpleText = (text = '') => {
text = text.trim();
text = text.replace(/([\u4e00-\u9fa5])[\s&&[^\n]]+([\u4e00-\u9fa5])/g, '$1$2');
text = text.replace(/\r\n|\r/g, '\n');
text = text.replace(/\n{3,}/g, '\n\n');
text = text.replace(/[\s&&[^\n]]{2,}/g, ' ');
text = text.replace(/[\x00-\x08]/g, ' ');
return text;
};
/*
replace {{variable}} to value
*/
export function replaceVariable(text: any, obj: Record<string, string | number>) {
if (!(typeof text === 'string')) return text;
for (const key in obj) {
const val = obj[key];
if (!['string', 'number'].includes(typeof val)) continue;
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), String(val));
}
return text || '';
}
/* replace sensitive text */
export const replaceSensitiveText = (text: string) => {
// 1. http link
text = text.replace(/(?<=https?:\/\/)[^\s]+/g, 'xxx');
// 2. nx-xxx 全部替换成xxx
text = text.replace(/ns-[\w-]+/g, 'xxx');
return text;
};
/* Make sure the first letter is definitely lowercase */
export const getNanoid = (size = 12) => {
const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
if (size === 1) return firstChar;
const randomsStr = customAlphabet(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
size - 1
)();
return `${firstChar}${randomsStr}`;
};
/* Custom text to reg, need to replace special chats */
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export const getRegQueryStr = (text: string, flags = 'i') => {
const formatText = replaceRegChars(text);
const chars = formatText.split('');
const regexPattern = chars.join('.*');
return new RegExp(regexPattern, flags);
};
/* slice json str */
export const sliceJsonStr = (str: string) => {
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
const jsonRegex = /{(?:[^{}]|{(?:[^{}]|{[^{}]*})*})*}/g;
const matches = str.match(jsonRegex);
if (!matches) {
return '';
}
// 找到第一个完整的 JSON 字符串
const jsonStr = matches[0];
return jsonStr;
};
export const sliceStrStartEnd = (str: string, start: number, end: number) => {
const overSize = str.length > start + end;
if (!overSize) return str;
const startContent = str.slice(0, start);
const endContent = overSize ? str.slice(-end) : '';
return `${startContent}${overSize ? `\n\n...[hide ${str.length - start - end} chars]...\n\n` : ''}${endContent}`;
};
export const getDevice = (device?: string) => {
if (!device) return 'fbc';
const lowDevice = device.toLowerCase();
if (lowDevice.includes('woa')) {
if (lowDevice.includes('android') || lowDevice.includes('ios')) {
return 'app';
} else {
return 'itw';
}
} else {
return 'fbc';
}
};
export const getSectorName = (code:SECTOR_CODE_ENUM)=>{
const SECTOR_MAP = {
"T": '通威集团',
"G": '通威股份',
"S": '通威农发',
"Y": '通威太阳能',
"X": '通威新能源',
"P": '通威食品',
"Z": '通威组件',
"L": '通威永祥'
};
return SECTOR_MAP[code]
}
\ No newline at end of file
import { i18nT } from '../../../web/i18n/utils';
export enum FlowNodeTemplateTypeEnum {
systemInput = 'systemInput',
ai = 'ai',
function = 'function',
tools = 'tools',
interactive = 'interactive',
search = 'search',
multimodal = 'multimodal',
communication = 'communication',
other = 'other',
teamApp = 'teamApp'
}
export enum WorkflowIOValueTypeEnum {
string = 'string',
number = 'number',
boolean = 'boolean',
object = 'object',
arrayString = 'arrayString',
arrayNumber = 'arrayNumber',
arrayBoolean = 'arrayBoolean',
arrayObject = 'arrayObject',
arrayAny = 'arrayAny',
any = 'any',
chatHistory = 'chatHistory',
datasetQuote = 'datasetQuote',
dynamic = 'dynamic',
// plugin special type
selectApp = 'selectApp',
selectDataset = 'selectDataset'
}
export const toolValueTypeList = [
{
label: WorkflowIOValueTypeEnum.string,
value: WorkflowIOValueTypeEnum.string,
jsonSchema: {
type: 'string'
}
},
{
label: WorkflowIOValueTypeEnum.number,
value: WorkflowIOValueTypeEnum.number,
jsonSchema: {
type: 'number'
}
},
{
label: WorkflowIOValueTypeEnum.boolean,
value: WorkflowIOValueTypeEnum.boolean,
jsonSchema: {
type: 'boolean'
}
},
{
label: 'array<string>',
value: WorkflowIOValueTypeEnum.arrayString,
jsonSchema: {
type: 'array',
items: {
type: 'string'
}
}
},
{
label: 'array<number>',
value: WorkflowIOValueTypeEnum.arrayNumber,
jsonSchema: {
type: 'array',
items: {
type: 'number'
}
}
},
{
label: 'array<boolean>',
value: WorkflowIOValueTypeEnum.arrayBoolean,
jsonSchema: {
type: 'array',
items: {
type: 'boolean'
}
}
}
];
/* reg: modulename key */
export enum NodeInputKeyEnum {
// old
welcomeText = 'welcomeText',
switch = 'switch', // a trigger switch
history = 'history',
answerText = 'text',
// system config
questionGuide = 'questionGuide',
tts = 'tts',
whisper = 'whisper',
variables = 'variables',
scheduleTrigger = 'scheduleTrigger',
chatInputGuide = 'chatInputGuide',
// plugin config
instruction = 'instruction',
// entry
userChatInput = 'userChatInput',
inputFiles = 'inputFiles',
agents = 'agents', // cq agent key
// latest
// common
aiModel = 'model',
aiSystemPrompt = 'systemPrompt',
description = 'description',
anyInput = 'system_anyInput',
textareaInput = 'system_textareaInput',
addInputParam = 'system_addInputParam',
// history
historyMaxAmount = 'maxContext',
// ai chat
aiChatTemperature = 'temperature',
aiChatMaxToken = 'maxToken',
aiChatSettingModal = 'aiSettings',
aiChatIsResponseText = 'isResponseAnswerText',
aiChatQuoteRole = 'aiChatQuoteRole',
aiChatQuoteTemplate = 'quoteTemplate',
aiChatQuotePrompt = 'quotePrompt',
aiChatDatasetQuote = 'quoteQA',
aiChatVision = 'aiChatVision',
stringQuoteText = 'stringQuoteText',
aiChatReasoning = 'aiChatReasoning',
// dataset
datasetSelectList = 'datasets',
datasetSimilarity = 'similarity',
datasetMaxTokens = 'limit',
datasetSearchMode = 'searchMode',
datasetSearchUsingReRank = 'usingReRank',
datasetSearchUsingExtensionQuery = 'datasetSearchUsingExtensionQuery',
datasetSearchExtensionModel = 'datasetSearchExtensionModel',
datasetSearchExtensionBg = 'datasetSearchExtensionBg',
collectionFilterMatch = 'collectionFilterMatch',
// concat dataset
datasetQuoteList = 'system_datasetQuoteList',
// context extract
contextExtractInput = 'content',
extractKeys = 'extractKeys',
// http
httpReqUrl = 'system_httpReqUrl',
httpHeaders = 'system_httpHeader',
httpMethod = 'system_httpMethod',
httpParams = 'system_httpParams',
httpJsonBody = 'system_httpJsonBody',
httpFormBody = 'system_httpFormBody',
httpContentType = 'system_httpContentType',
httpTimeout = 'system_httpTimeout',
abandon_httpUrl = 'url',
// app
runAppSelectApp = 'app',
// plugin
pluginId = 'pluginId',
pluginStart = 'pluginStart',
// if else
condition = 'condition',
ifElseList = 'ifElseList',
// variable update
updateList = 'updateList',
// code
code = 'code',
codeType = 'codeType', // js|py
userInputForms = 'userInputForms',
// read files
fileUrlList = 'fileUrlList',
// user select
userSelectOptions = 'userSelectOptions'
}
export enum NodeOutputKeyEnum {
// common
userChatInput = 'userChatInput',
history = 'history',
answerText = 'answerText', // module answer. the value will be show and save to history
reasoningText = 'reasoningText', // node reasoning. the value will be show but not save to history
success = 'success',
failed = 'failed',
error = 'error',
text = 'system_text',
addOutputParam = 'system_addOutputParam',
rawResponse = 'system_rawResponse',
// start
userFiles = 'userFiles',
// dataset
datasetQuoteQA = 'quoteQA',
// classify
cqResult = 'cqResult',
// context extract
contextExtractFields = 'fields',
// tf switch
resultTrue = 'system_resultTrue',
resultFalse = 'system_resultFalse',
// tools
selectedTools = 'selectedTools',
// http
httpRawResponse = 'httpRawResponse',
// plugin
pluginStart = 'pluginStart',
// if else
ifElseResult = 'ifElseResult',
//user select
selectResult = 'selectResult',
// form input
formInputResult = 'formInputResult'
}
export enum VariableInputEnum {
input = 'input',
textarea = 'textarea',
select = 'select',
custom = 'custom'
}
export const variableMap = {
[VariableInputEnum.input]: {
icon: 'core/app/variable/input',
title: i18nT('common:core.module.variable.input type'),
desc: ''
},
[VariableInputEnum.textarea]: {
icon: 'core/app/variable/textarea',
title: i18nT('common:core.module.variable.textarea type'),
desc: i18nT('app:variable.textarea_type_desc')
},
[VariableInputEnum.select]: {
icon: 'core/app/variable/select',
title: i18nT('common:core.module.variable.select type'),
desc: ''
},
[VariableInputEnum.custom]: {
icon: 'core/app/variable/external',
title: i18nT('common:core.module.variable.Custom type'),
desc: i18nT('app:variable.select type_desc')
}
};
/* run time */
export enum RuntimeEdgeStatusEnum {
'waiting' = 'waiting',
'active' = 'active',
'skipped' = 'skipped'
}
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
export const DYNAMIC_INPUT_REFERENCE_KEY = 'DYNAMIC_INPUT_REFERENCE_KEY';
// http node body content type
export enum ContentTypes {
none = 'none',
formData = 'form-data',
xWwwFormUrlencoded = 'x-www-form-urlencoded',
json = 'json',
xml = 'xml',
raw = 'raw-text'
}
import { WorkflowIOValueTypeEnum } from '../constants';
import { i18nT } from '../../../../web/i18n/utils';
export enum FlowNodeInputTypeEnum { // render ui
reference = 'reference', // reference to other node output
input = 'input', // one line input
numberInput = 'numberInput',
switch = 'switch', // true/false
select = 'select',
// editor
textarea = 'textarea',
JSONEditor = 'JSONEditor',
addInputParam = 'addInputParam', // params input
// special input
selectApp = 'selectApp',
customVariable = 'customVariable',
// ai model select
selectLLMModel = 'selectLLMModel',
settingLLMModel = 'settingLLMModel',
// dataset special input
selectDataset = 'selectDataset',
selectDatasetParamsModal = 'selectDatasetParamsModal',
settingDatasetQuotePrompt = 'settingDatasetQuotePrompt',
hidden = 'hidden',
custom = 'custom',
fileSelect = 'fileSelect'
}
export const FlowNodeInputMap: Record<
FlowNodeInputTypeEnum,
{
icon: string;
}
> = {
[FlowNodeInputTypeEnum.reference]: {
icon: 'core/workflow/inputType/reference'
},
[FlowNodeInputTypeEnum.input]: {
icon: 'core/workflow/inputType/input'
},
[FlowNodeInputTypeEnum.numberInput]: {
icon: 'core/workflow/inputType/numberInput'
},
[FlowNodeInputTypeEnum.select]: {
icon: 'core/workflow/inputType/option'
},
[FlowNodeInputTypeEnum.switch]: {
icon: 'core/workflow/inputType/switch'
},
[FlowNodeInputTypeEnum.textarea]: {
icon: 'core/workflow/inputType/textarea'
},
[FlowNodeInputTypeEnum.JSONEditor]: {
icon: 'core/workflow/inputType/jsonEditor'
},
[FlowNodeInputTypeEnum.addInputParam]: {
icon: 'core/workflow/inputType/dynamic'
},
[FlowNodeInputTypeEnum.selectApp]: {
icon: 'core/workflow/inputType/selectApp'
},
[FlowNodeInputTypeEnum.selectLLMModel]: {
icon: 'core/workflow/inputType/selectLLM'
},
[FlowNodeInputTypeEnum.settingLLMModel]: {
icon: 'core/workflow/inputType/selectLLM'
},
[FlowNodeInputTypeEnum.selectDataset]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.selectDatasetParamsModal]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.settingDatasetQuotePrompt]: {
icon: 'core/workflow/inputType/selectDataset'
},
[FlowNodeInputTypeEnum.hidden]: {
icon: 'core/workflow/inputType/select'
},
[FlowNodeInputTypeEnum.customVariable]: {
icon: 'core/workflow/inputType/customVariable'
},
[FlowNodeInputTypeEnum.custom]: {
icon: 'core/workflow/inputType/custom'
},
[FlowNodeInputTypeEnum.fileSelect]: {
icon: 'core/workflow/inputType/file'
}
};
export enum FlowNodeOutputTypeEnum {
hidden = 'hidden',
source = 'source',
static = 'static',
dynamic = 'dynamic'
}
export enum FlowNodeTypeEnum {
emptyNode = 'emptyNode',
systemConfig = 'userGuide',
pluginConfig = 'pluginConfig',
globalVariable = 'globalVariable',
workflowStart = 'workflowStart',
chatNode = 'chatNode',
datasetSearchNode = 'datasetSearchNode',
datasetConcatNode = 'datasetConcatNode',
answerNode = 'answerNode',
classifyQuestion = 'classifyQuestion',
contentExtract = 'contentExtract',
httpRequest468 = 'httpRequest468',
runApp = 'app',
appModule = 'appModule',
pluginModule = 'pluginModule',
pluginInput = 'pluginInput',
pluginOutput = 'pluginOutput',
queryExtension = 'cfr',
tools = 'tools',
stopTool = 'stopTool',
ifElseNode = 'ifElseNode',
variableUpdate = 'variableUpdate',
code = 'code',
textEditor = 'textEditor',
customFeedback = 'customFeedback',
readFiles = 'readFiles',
userSelect = 'userSelect',
formInput = 'formInput'
}
// node IO value type
export const FlowValueTypeMap = {
[WorkflowIOValueTypeEnum.string]: {
label: 'string',
value: WorkflowIOValueTypeEnum.string
},
[WorkflowIOValueTypeEnum.number]: {
label: 'number',
value: WorkflowIOValueTypeEnum.number
},
[WorkflowIOValueTypeEnum.boolean]: {
label: 'boolean',
value: WorkflowIOValueTypeEnum.boolean
},
[WorkflowIOValueTypeEnum.object]: {
label: 'object',
value: WorkflowIOValueTypeEnum.object
},
[WorkflowIOValueTypeEnum.arrayString]: {
label: 'array<string>',
value: WorkflowIOValueTypeEnum.arrayString
},
[WorkflowIOValueTypeEnum.arrayNumber]: {
label: 'array<number>',
value: WorkflowIOValueTypeEnum.arrayNumber
},
[WorkflowIOValueTypeEnum.arrayBoolean]: {
label: 'array<boolean>',
value: WorkflowIOValueTypeEnum.arrayBoolean
},
[WorkflowIOValueTypeEnum.arrayObject]: {
label: 'array<object>',
value: WorkflowIOValueTypeEnum.arrayObject
},
[WorkflowIOValueTypeEnum.arrayAny]: {
label: 'Array',
value: WorkflowIOValueTypeEnum.arrayAny
},
[WorkflowIOValueTypeEnum.any]: {
label: 'any',
value: WorkflowIOValueTypeEnum.any
},
[WorkflowIOValueTypeEnum.chatHistory]: {
label: i18nT('common:core.chat.History'),
value: WorkflowIOValueTypeEnum.chatHistory
},
[WorkflowIOValueTypeEnum.datasetQuote]: {
label: i18nT('common:core.workflow.Dataset quote'),
value: WorkflowIOValueTypeEnum.datasetQuote
},
[WorkflowIOValueTypeEnum.selectApp]: {
label: i18nT('common:plugin.App'),
value: WorkflowIOValueTypeEnum.selectApp
},
[WorkflowIOValueTypeEnum.selectDataset]: {
label: i18nT('common:core.chat.Select dataset'),
value: WorkflowIOValueTypeEnum.selectDataset
},
[WorkflowIOValueTypeEnum.dynamic]: {
label: i18nT('common:core.workflow.dynamic_input'),
value: WorkflowIOValueTypeEnum.dynamic
}
};
export const EDGE_TYPE = 'default';
export const defaultNodeVersion = '481';
export const chatHistoryValueDesc = `{
obj: System | Human | AI;
value: string;
}[]`;
export const datasetQuoteValueDesc = `{
id: string;
datasetId: string;
collectionId: string;
sourceName: string;
sourceId?: string;
q: string;
a: string
}[]`;
import { FlowNodeInputTypeEnum } from '../node/constant';
export enum SseResponseEventEnum {
error = 'error',
answer = 'answer', // animation stream
fastAnswer = 'fastAnswer', // direct answer text, not animation
flowNodeStatus = 'flowNodeStatus', // update node status
toolCall = 'toolCall', // tool start
toolParams = 'toolParams', // tool params return
toolResponse = 'toolResponse', // tool response return
flowResponses = 'flowResponses', // sse response request
updateVariables = 'updateVariables',
interactive = 'interactive' // user select
}
export enum DispatchNodeResponseKeyEnum {
skipHandleId = 'skipHandleId', // skip handle id
nodeResponse = 'responseData', // run node response
nodeDispatchUsages = 'nodeDispatchUsages', // the node bill.
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses', // assistant response
rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite
interactive = 'INTERACTIVE', // is interactive
runTimes = 'runTimes', // run times
newVariables = 'newVariables' // new variables
}
export const needReplaceReferenceInputTypeList = [
FlowNodeInputTypeEnum.reference,
FlowNodeInputTypeEnum.settingDatasetQuotePrompt,
FlowNodeInputTypeEnum.addInputParam,
FlowNodeInputTypeEnum.custom
] as string[];
export type SECTOR_CODE_ENUM = "T"| "G"| "S"| "Y"|"X"| "P"| "Z"
export interface SectorSchema {
_id: string;
sectorName: string;
sectorCode: SECTOR_CODE_ENUM;
avatar: string;
status: 'active' | 'disable';
createTime: Date;
updateTime: Date;
adminList: { username: string; userId: string; trueName: string }[];
}
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
import { useMediaQuery } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
export const useSystem = () => {
const [isPc] = useMediaQuery('(min-width: 900px)');
return { isPc };
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
import { PluginTemplateType } from '@eagic/global/core/plugin/type.d';
import { SystemPluginTemplateItemType } from '@eagic/global/core/workflow/type';
export type SystemPluginResponseType = Promise<Record<string, any>>;
declare global {
var systemPlugins: SystemPluginTemplateItemType[];
var systemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
}
// import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose';
export default mongoose;
export * from 'mongoose';
export const connectionMongo = (() => {
if (!global.mongodb) {
global.mongodb = mongoose;
}
return global.mongodb;
})();
const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [
/^find/,
'save',
'create',
/^update/,
/^delete/,
'aggregate',
'count',
'countDocuments',
'estimatedDocumentCount',
'distinct',
'insertMany'
];
operations.forEach((op: any) => {
schema.pre(op, function (this: any, next) {
this._startTime = Date.now();
this._query = this.getQuery ? this.getQuery() : null;
next();
});
schema.post(op, function (this: any, result: any, next) {
if (this._startTime) {
const duration = Date.now() - this._startTime;
const warnLogData = {
query: this._query,
op,
duration
};
if (duration > 1000) {
// addLog.warn(`Slow operation ${duration}ms`, warnLogData);
}
}
next();
});
});
return schema;
};
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
console.log('Load model======', name);
addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema);
if (process.env.SYNC_INDEX !== '0') {
try {
model.syncIndexes({ background: true });
} catch (error) {
// addLog.error('Create index error', error);
}
}
return model;
};
export const ReadPreference = connectionMongo.mongo?.ReadPreference;
import { delay } from '@src/utils/utils';
// import { addLog } from '../system/log';
import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose';
const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20));
/**
* connect MongoDB and init data
*/
export async function connectMongo(): Promise<Mongoose> {
/* Connecting, connected will return */
if (connectionMongo.connection.readyState !== 0) {
return connectionMongo;
}
console.log('mongo start connect');
try {
connectionMongo.set('strictQuery', true);
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
});
connectionMongo.connection.on('disconnected', () => {
console.log('mongo disconnected');
});
console.log(process.env.MONGODB_URI);
await connectionMongo.connect(process.env.MONGODB_URI as string, {
bufferCommands: true,
maxConnecting: maxConnecting,
maxPoolSize: maxConnecting,
minPoolSize: 20,
connectTimeoutMS: 60000,
waitQueueTimeoutMS: 60000,
socketTimeoutMS: 60000,
maxIdleTimeMS: 300000,
retryWrites: true,
retryReads: true
// readPreference: 'secondaryPreferred',
// readConcern: { level: 'local' },
// writeConcern: { w: 'majority', j: true }
});
console.log('mongo connected');
return connectionMongo;
} catch (error) {
// addLog.error('mongo connect error', error);
await connectionMongo.disconnect();
await delay(1000);
return connectMongo();
}
}
// import { addLog } from '../system/log';
import { connectionMongo, ClientSession } from './index';
const timeout = 60000;
export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession) => Promise<T>) => {
const session = await connectionMongo.startSession();
try {
session.startTransaction({
maxCommitTimeMS: timeout
});
const result = await fn(session);
await session.commitTransaction();
return result as T;
} catch (error) {
if (!session.transaction.isCommitted) {
await session.abortTransaction();
} else {
// addLog.warn('Un catch mongo session error', { error });
}
return Promise.reject(error);
} finally {
await session.endSession();
}
};
import type { Mongoose } from 'mongoose';
import type { Logger } from 'winston';
declare global {
var mongodb: Mongoose | undefined;
}
import { ReadPreference } from './index';
export const readFromSecondary = {
readPreference: ReadPreference.SECONDARY_PREFERRED, // primary | primaryPreferred | secondary | secondaryPreferred | nearest
readConcern: 'local' as any // local | majority | linearizable | available
};
import type { NextApiResponse } from 'next';
import { SseResponseEventEnum } from '@eagic/global/core/workflow/runtime/constants';
import { proxyError, ERROR_RESPONSE, ERROR_ENUM } from '@eagic/global/common/error/errorCode';
import { addLog } from '../system/log';
import { replaceSensitiveText } from '@eagic/global/common/string/tools';
export interface ResponseType<T = any> {
code: number;
message: string;
data: T;
}
export const jsonRes = <T = any>(
res: NextApiResponse,
props?: {
code?: number;
message?: string;
data?: T;
error?: any;
url?: string;
}
) => {
const { code = 200, message = '', data = null, error, url } = props || {};
const errResponseKey = typeof error === 'string' ? error : error?.message;
// Specified error
if (ERROR_RESPONSE[errResponseKey]) {
// login is expired
if (errResponseKey === ERROR_ENUM.unAuthorization) {
// clearCookie(res);
}
return res.json(ERROR_RESPONSE[errResponseKey]);
}
// another error
let msg = '';
if ((code < 200 || code >= 400) && !message) {
msg = error?.response?.statusText || error?.message || '请求错误';
if (typeof error === 'string') {
msg = error;
} else if (proxyError[error?.code]) {
msg = '网络连接异常';
} else if (error?.response?.data?.error?.message) {
msg = error?.response?.data?.error?.message;
} else if (error?.error?.message) {
msg = error?.error?.message;
}
addLog.error(`Api response error: ${url}, ${msg}`, error);
}
res.status(code).json({
code,
statusText: '',
message: replaceSensitiveText(message || msg),
data: data !== undefined ? data : null
});
};
export const sseErrRes = (res: NextApiResponse, error: any) => {
const errResponseKey = typeof error === 'string' ? error : error?.message;
// Specified error
if (ERROR_RESPONSE[errResponseKey]) {
// login is expired
if (errResponseKey === ERROR_ENUM.unAuthorization) {
// clearCookie(res);
}
return responseWrite({
res,
event: SseResponseEventEnum.error,
data: JSON.stringify(ERROR_RESPONSE[errResponseKey])
});
}
let msg = error?.response?.statusText || error?.message || '请求错误';
if (typeof error === 'string') {
msg = error;
} else if (proxyError[error?.code]) {
msg = '网络连接异常';
} else if (error?.response?.data?.error?.message) {
msg = error?.response?.data?.error?.message;
} else if (error?.error?.message) {
msg = `${error?.error?.code} ${error?.error?.message}`;
}
addLog.error(`sse error: ${msg}`, error);
responseWrite({
res,
event: SseResponseEventEnum.error,
data: JSON.stringify({ message: replaceSensitiveText(msg) })
});
};
export function responseWriteController({
res,
readStream
}: {
res: NextApiResponse;
readStream: any;
}) {
res.on('drain', () => {
readStream?.resume?.();
});
return (text: string | Buffer) => {
const writeResult = res.write(text);
if (!writeResult) {
readStream?.pause?.();
}
};
}
export function responseWrite({
res,
write,
event,
data
}: {
res?: NextApiResponse;
write?: (text: string) => void;
event?: string;
data: string;
}) {
const Write = write || res?.write;
if (!Write) return;
event && Write(`event: ${event}\n`);
Write(`data: ${data}\n\n`);
}
export const responseWriteNodeStatus = ({
res,
status = 'running',
name
}: {
res?: NextApiResponse;
status?: 'running';
name: string;
}) => {
responseWrite({
res,
event: SseResponseEventEnum.flowNodeStatus,
data: JSON.stringify({
status,
name
})
});
};
import dayjs from 'dayjs';
import chalk from 'chalk';
import { LogLevelEnum } from './log/constant';
import { connectionMongo } from '../mongo/index';
import { getMongoLog } from './log/schema';
const logMap = {
[LogLevelEnum.debug]: {
levelLog: chalk.green('[Debug]')
},
[LogLevelEnum.info]: {
levelLog: chalk.blue('[Info]')
},
[LogLevelEnum.warn]: {
levelLog: chalk.yellow('[Warn]')
},
[LogLevelEnum.error]: {
levelLog: chalk.red('[Error]')
}
};
const envLogLevelMap: Record<string, number> = {
debug: LogLevelEnum.debug,
info: LogLevelEnum.info,
warn: LogLevelEnum.warn,
error: LogLevelEnum.error
};
const { LOG_LEVEL, STORE_LOG_LEVEL } = (() => {
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
const STORE_LOG_LEVEL = (process.env.STORE_LOG_LEVEL || '').toLocaleLowerCase();
return {
LOG_LEVEL: envLogLevelMap[LOG_LEVEL] ?? LogLevelEnum.info,
STORE_LOG_LEVEL: envLogLevelMap[STORE_LOG_LEVEL] ?? 99
};
})();
/* add logger */
export const addLog = {
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
if (level < LOG_LEVEL) return;
const stringifyObj = JSON.stringify(obj);
const isEmpty = Object.keys(obj).length === 0;
console.log(
`${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${
level !== LogLevelEnum.error && !isEmpty ? stringifyObj : ''
}`
);
level === LogLevelEnum.error && console.error(obj);
// store
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log
getMongoLog().create({
text: msg,
level,
metadata: obj
});
}
},
debug(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.debug, msg, obj);
},
info(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.info, msg, obj);
},
warn(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.warn, msg, obj);
},
error(msg: string, error?: any) {
this.log(LogLevelEnum.error, msg, {
message: error?.message || error,
stack: error?.stack,
...(error?.config && {
config: {
headers: error.config.headers,
url: error.config.url,
data: error.config.data
}
}),
...(error?.response && {
response: {
status: error.response.status,
statusText: error.response.statusText
}
})
});
}
};
export enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
export enum LogSignEnum {
slowOperation = 'slowOperation'
}
import { getMongoModel, Schema } from '../../../common/mongo';
import { SystemLogType } from './type';
import { LogLevelEnum } from './constant';
export const LogCollectionName = 'system_logs';
export const getMongoLog = () => {
const SystemLogSchema = new Schema({
text: {
type: String,
required: true
},
level: {
type: String,
required: true,
enum: Object.values(LogLevelEnum)
},
time: {
type: Date,
default: () => new Date()
},
metadata: Object
});
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
SystemLogSchema.index({ level: 1 });
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
};
import { LogLevelEnum, LogSignEnum } from './constant';
export type SystemLogType = {
_id: string;
text: string;
level: LogLevelEnum;
time: Date;
metadata?: Record<string, any>;
};
// import type { UserModelSchema } from '@eagic/global/support/user/type';
import OpenAI from 'openai';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
import type { Agent } from 'http';
export const getAIApi = () => {
const baseUrl = process.env.ONEAPI_URL || openaiBaseUrl;
const apiKey = process.env.CHAT_API_KEY || '';
console.log(baseUrl);
console.log(apiKey);
return new OpenAI({
baseURL: baseUrl,
apiKey,
httpAgent: global.httpsAgent,
timeout:30000 ,
maxRetries: 2
});
};
import type { Agent } from 'http';
declare global {
var httpsAgent: Agent;
}
import axios, {
Method,
InternalAxiosRequestConfig,
AxiosResponse,
AxiosProgressEvent
} from 'axios';
interface ConfigType {
headers?: { [key: string]: string };
timeout?: number;
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
cancelToken?: AbortController;
maxQuantity?: number; // The maximum number of simultaneous requests, usually used to cancel old requests
withCredentials?: boolean;
}
interface ResponseDataType {
code: number;
message: string;
data: any;
}
const maxQuantityMap: Record<
string,
{
amount: number;
sign: AbortController;
}
> = {};
function checkMaxQuantity({ url, maxQuantity }: { url: string; maxQuantity?: number }) {
if (maxQuantity) {
const item = maxQuantityMap[url];
const controller = new AbortController();
if (item) {
if (item.amount >= maxQuantity) {
item.sign?.abort?.();
maxQuantityMap[url] = {
amount: 1,
sign: controller
};
} else {
item.amount++;
}
} else {
maxQuantityMap[url] = {
amount: 1,
sign: controller
};
}
return controller;
}
}
function requestFinish({ url }: { url: string }) {
const item = maxQuantityMap[url];
if (item) {
item.amount--;
if (item.amount <= 0) {
delete maxQuantityMap[url];
}
}
}
/**
* 请求开始
*/
function startInterceptors(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
if (config.headers) {
}
return config;
}
/**
* 请求成功,检查请求头
*/
function responseSuccess(response: AxiosResponse<ResponseDataType>) {
return response;
}
/**
* 响应数据检查
*/
function checkRes(data: ResponseDataType) {
if (data === undefined) {
console.log('error->', data, 'data is empty');
return Promise.reject('服务器异常');
} else if (data.code < 200 || data.code >= 400) {
return Promise.reject(data);
}
return data.data;
}
/**
* 响应错误
*/
function responseError(err: any) {
console.log('error->', '请求错误', err);
if (!err) {
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject({ message: err });
}
if (err?.response?.data) {
return Promise.reject(err?.response?.data);
}
return Promise.reject(err);
}
/* 创建请求实例 */
const instance = axios.create({
timeout: 60000, // 超时时间
headers: {
'content-type': 'application/json'
}
});
/* 请求拦截 */
instance.interceptors.request.use(startInterceptors, (err) => Promise.reject(err));
/* 响应拦截 */
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
function request(
url: string,
data: any,
{ cancelToken, maxQuantity, withCredentials, ...config }: ConfigType,
method: Method
): any {
/* 去空 */
for (const key in data) {
if (data[key] === undefined) {
delete data[key];
}
}
const controller = checkMaxQuantity({ url, maxQuantity });
return instance
.request({
baseURL: '/api',
url,
method,
data: ['POST', 'PUT'].includes(method) ? data : null,
params: !['POST', 'PUT'].includes(method) ? data : null,
signal: cancelToken?.signal ?? controller?.signal,
withCredentials,
...config // 用户自定义配置,可以覆盖前面的配置
})
.then((res) => checkRes(res.data))
.catch((err) => responseError(err))
.finally(() => requestFinish({ url }));
}
/**
* api请求方式
* @param {String} url
* @param {Any} params
* @param {Object} config
* @returns
*/
export function GET<T = undefined>(url: string, params = {}, config: ConfigType = {}): Promise<T> {
return request(url, params, config, 'GET');
}
export function POST<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'POST');
}
export function PUT<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'PUT');
}
export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'DELETE');
}
import React, { useRef, useCallback } from 'react';
import { Box } from '@chakra-ui/react';
import { useToast } from '@/web/common/hooks/useToast';
export const useSelectFile = (props?: {
fileType?: string;
multiple?: boolean;
maxCount?: number;
}) => {
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
const { toast } = useToast();
const SelectFileDom = useRef<HTMLInputElement>(null);
const openSign = useRef<any>();
const File = useCallback(
({ onSelect }: { onSelect: (e: File[], sign?: any) => void }) => (
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
<input
ref={SelectFileDom}
type="file"
accept={fileType}
multiple={multiple}
onChange={(e) => {
const files = e.target.files;
if (!files || files?.length === 0) return;
let fileList = Array.from(files);
if (fileList.length > maxCount) {
toast({
status: 'warning',
title:'select_file_amount_limit'
});
fileList = fileList.slice(0, maxCount);
}
onSelect(fileList, openSign.current);
e.target.value = '';
}}
/>
</Box>
),
[fileType, maxCount, multiple, toast]
);
const onOpen = useCallback((sign?: any) => {
openSign.current = sign;
SelectFileDom.current && SelectFileDom.current.click();
}, []);
return {
File,
onOpen
};
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
import { diffDetailType } from '@src/pages/api/contractCompare/diffHandler';
// 转义正则表达式的关键字
export const escapeRegExp = (input: string) => {
// 列出正则表达式的关键字
const regexKeywords = /[-/\\^$*+?.()|[\]{}]/g;
// 使用replace方法来转义关键字
return input.replace(regexKeywords, '\\$&');
};
// 分割字符串,每n个一组
export const splitString = (inputString: string, chunkSize: number) => {
const result = [];
for (let i = 0; i < inputString.length; i += chunkSize) {
result.push(inputString.slice(i, i + chunkSize));
}
return result;
};
interface returnObj {
contractHtml: string;
diffCount: number;
}
interface DiffObj {
added?: boolean;
removed?: boolean;
count: number;
value: string;
lastIndex: number;
strLastIndexList: number[];
}
interface FragmentObj {
first: string;
table: string;
last: string;
}
// 拼接多页内容,用--PAGEEND--作为分割符
const concatPage = (list: HTMLElement[]) => {
const strList = list.map((item) => item.innerHTML);
return strList.join('--PAGEEND--');
};
// 比较新老内容,处理差异
export const diffDocument = (newDocxNode: Element, diffDetail: diffDetailType): returnObj => {
let addNum = 0;
let delNum = 0;
let cycleInedx = 1;
const handleDiff = (newHtml: string, diffList: any) => {
// 区分新增和删除,并获取每个字的位置
let reg = new RegExp('', 'g');
let lastIndexOld = 0;
// 记录未改变的字符数/新增字符数/删除字符数
// let newHtml = newNodeStr;
const regex = /[^ \t\r\n\v\f]/;
for (const item of diffList) {
const strArr = item.value.split(''); // 拆分成单个字符
const strLastIndexList: number[] = []; // 每个字符的lastIndex
if (item.removed) {
if (!regex.test(item.value)) {
continue;
}
newHtml =
newHtml.slice(0, lastIndexOld) +
`<s id='diff-${cycleInedx}' style="background-color:#fac5cd;">${item.value}</s>` +
newHtml.slice(lastIndexOld);
//标签字符串长度 + 移除项的长度 + 循环索引长度
lastIndexOld += 52 + item.value.length + cycleInedx.toString().length;
delNum += item.value.length;
cycleInedx += 1;
continue;
}
// 获取每个字的位置
strArr.forEach((s: string) => {
reg = new RegExp(`(?<!<[^>]*)(${escapeRegExp(s)})(?![^<]*>)`, 'gi');
reg.lastIndex = lastIndexOld; // 在上次正则位置基础上,继续匹配
reg.exec(newHtml);
if (reg.lastIndex != 0) {
lastIndexOld = reg.lastIndex; // 记录本次位置
strLastIndexList.push(reg.lastIndex - 1);
}
});
if (item.added && regex.test(item.value)) {
const len = strLastIndexList.length;
newHtml =
newHtml.slice(0, strLastIndexList[0]) +
`<span id='diff-${cycleInedx}' style="background-color:#c7f0d2;">${item.value}</span>` +
newHtml.slice(strLastIndexList[strLastIndexList.length - 1] + 1);
//首个字符索引位置 - 1 + 新增字符串长度 + 循环索引长度 + 标签字符串长度
lastIndexOld = strLastIndexList[0] - 1 + len + cycleInedx.toString().length + 52;
cycleInedx += 1;
addNum += 1;
}
}
return newHtml;
};
(newDocxNode.querySelector('.vue-office-docx') as HTMLElement).style.height = 'auto';
const docxWrapper = newDocxNode.querySelector('.docx-wrapper') as HTMLElement;
docxWrapper.style.backgroundColor = '#fff';
docxWrapper.style.padding = '0';
// 使用 forEach 遍历并设置样式
docxWrapper.querySelectorAll('.docx').forEach((docx) => {
(docx as HTMLElement).style.width = '100%';
(docx as HTMLElement).style.marginBottom = '0';
(docx as HTMLElement).style.position = 'static';
});
// 使用 forEach 遍历并设置样式
docxWrapper.querySelectorAll('footer svg').forEach((svg) => {
(svg as HTMLElement).style.height = '1in';
});
const newNodeList: HTMLElement[] = Array.from(
newDocxNode.querySelectorAll('.docx-wrapper > .docx > article')
);
if (newNodeList.length === 0) {
return { contractHtml: '', diffCount: 0 };
}
let newNodeStr = concatPage(newNodeList);
const reg1 = /<span\s+style="([^"]+)">/g;
// 去除文档原本的字体颜色和背景色
const deleteColor = (match: any, styleContent: string) => {
// 分割 style 属性中的各个 CSS 规则
const styles = styleContent.split(';').map((style) => style.trim());
// 过滤掉 background-color 和 color 属性
const filteredStyles = styles.filter(
(style) => !style.startsWith('background-color:') && !style.startsWith('color:')
);
// 将过滤后的样式重新组合成一个字符串
const newStyleContent = filteredStyles.join('; ');
// 如果没有剩余样式,返回没有 style 属性的 <span> 标签
if (newStyleContent) {
return `<span style="${newStyleContent}">`;
} else {
return '<span>';
}
};
newNodeStr = newNodeStr.replace(reg1, deleteColor);
const newFragments: FragmentObj = {
first: '',
table: '',
last: ''
};
const tableReg = /<table[^>]*>.*?<\/table>/g;
tableReg.lastIndex = 0;
const matchNews = newNodeStr.match(tableReg);
if (matchNews) {
let matchNew = matchNews.filter(
(match) => match.includes('产品名称') && match.includes('金额')
)[0];
const tableIndex = newNodeStr.indexOf(matchNew);
newFragments.first = newNodeStr.slice(0, tableIndex);
newFragments.table = matchNew;
newFragments.last = newNodeStr.slice(tableIndex + matchNew.length);
}
newFragments.first = handleDiff(newFragments.first, diffDetail.fristFragmentDiff);
newFragments.table = handleDiff(newFragments.table, diffDetail.tableDiff);
newFragments.last = handleDiff(newFragments.last, diffDetail.lastFragmentDiff);
if (diffDetail.outherDiff.deliveryTimeDiff) {
let diffArr: number[] = [];
// console.log("diffArr====================", diffArr);
// console.log(' reg.exec(handledStr) ====================', handledStr);
// let reg = new RegExp(/于(<[^>]*>)*[0-9]{4}.*?由/);
let reg = new RegExp(/不(<[^>]*>)*晚(<[^>]*>)*于.*?由/);
reg.lastIndex = 0;
reg.exec(newFragments.last);
// let low = 0;
// let high = diffArr.length - 1;
// let result = -1; // 如果没有找到,返回-1
// // 找到应该插入的位置
// while (low <= high) {
// const mid = low + Math.floor((high - low) / 2);
// if (diffArr[mid] > reg.lastIndex) {
// result = mid;
// high = mid - 1; // 继续在左半部分查找,看是否有更小的数
// } else {
// low = mid + 1; // 继续在右半部分查找
// }
// }
// let diffTemp = result
// while (diffTemp < diffArr.length - 1) {
// // 其他差异后移
// reg = new RegExp(`diff-${diffTemp}`);
// handledStr.replace(reg, `diff-${diffTemp + 1}`)
// }
newFragments.last = newFragments.last.replace(reg, (word) => {
word = word.replace(/<[^>]*>/g, '');
return `不晚于<span id='diff-${cycleInedx}' style="background-color:yellow;">${word.substring(
3,
word.length - 1
)}</span>由`;
});
}
let handledStr = Object.values(newFragments).join('');
// 根据分割符重新分割每页
const newHtmlList = handledStr.split('--PAGEEND--');
newNodeList.forEach((node, i) => {
node.innerHTML = newHtmlList[i];
});
return {
contractHtml: newHtmlList.join(''),
diffCount: cycleInedx - 1
};
};
export const modifyDateStyle = (setAgain: Function) => {
const newDocx = document.querySelector('.new-docx');
if (newDocx) {
setAgain(true);
const newNodeList: HTMLElement[] = Array.from(
newDocx.shadowRoot?.querySelectorAll('.docx-wrapper > .docx > article') || []
);
if (newNodeList.length === 0) {
return 1;
}
// 拼接多页内容,用--PAGEEND--作为分割符
let newNodeStr = concatPage(newNodeList);
// let newContentStr = newNodeStr.replace(/<[^>]*>/g, "")
// let reg = new RegExp(/预计发货时间(<[^>]*>)*.*?金额/);
// newNodeStr = newNodeStr.replace(reg, (word) => {
// word = word.replace(/<[^>]*>/g, "");
// return `于<span style="background-color:yellow;">${word.substring(1, word.length - 1)}</span>由`
// })
let reg = new RegExp(/于(<[^>]*>)*[0-9]{4}.*?由/);
newNodeStr = newNodeStr.replace(reg, (word) => {
word = word.replace(/<[^>]*>/g, '');
return `于<span style="background-color:yellow;">${word.substring(
1,
word.length - 1
)}</span>由`;
});
const newHtmlList = newNodeStr.split('--PAGEEND--');
newNodeList.forEach((node, i) => {
node.innerHTML = newHtmlList[i];
});
}
};
export const editFocusDiffStyle = (pre: number, next: number) => {
const newDocx = document.querySelector('.new-docx');
if (newDocx) {
let doc = newDocx.shadowRoot?.getElementById(`diff-${next}`);
doc && doc.classList.add('diff-focus');
if (pre !== 0) {
let preDoc = newDocx.shadowRoot?.getElementById(`diff-${pre}`);
preDoc && preDoc.classList.remove('diff-focus');
}
doc && doc.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
};
// const getTable = async (content: string) => {
// return
// }
import React from 'react';
import { Box, Flex, Image } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react';
// import { LOGO_ICON } from '@eagic/global/common/system/constants';
import MyIcon from '../Icon';
import { iconPaths } from '../Icon/constants';
const LOGO_ICON = `/icon/logo2.png`;
const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
// @ts-ignore
const isIcon = !!iconPaths[src as any];
return isIcon ? (
<Box display={'inline-flex'} {...props}>
<MyIcon name={src as any} w={w} borderRadius={props.borderRadius} />
</Box>
) : (
<Image
fallbackSrc={LOGO_ICON}
fallbackStrategy={'onError'}
objectFit={'contain'}
alt=""
w={w}
h={w}
src={src || LOGO_ICON}
{...props}
/>
);
};
export default Avatar;
// @ts-nocheck
export const iconPaths = {
addSvg: () => import('./icons/core/chat/add.svg'),
deleteSvg: () => import('./icons/core/chat/delete.svg')
};
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#D9F7D0"/>
<path d="M20 31.0944C20.373 31.0944 20.7306 30.9462 20.9944 30.6825C21.2581 30.4188 21.4062 30.0611 21.4062 29.6881V10.3119C21.4062 9.93893 21.2581 9.58124 20.9944 9.31752C20.7306 9.0538 20.373 8.90564 20 8.90564C19.627 8.90564 19.2694 9.0538 19.0056 9.31752C18.7419 9.58124 18.5938 9.93893 18.5938 10.3119V29.6881C18.5938 30.0611 18.7419 30.4188 19.0056 30.6825C19.2694 30.9462 19.627 31.0944 20 31.0944Z" fill="#6ADE46"/>
<path d="M10.312 21.4062H29.6882C30.0612 21.4062 30.4189 21.2581 30.6826 20.9944C30.9463 20.7306 31.0945 20.373 31.0945 20C31.0945 19.627 30.9463 19.2694 30.6826 19.0056C30.4189 18.7419 30.0612 18.5938 29.6882 18.5938H10.312C9.93902 18.5938 9.58134 18.7419 9.31761 19.0056C9.05389 19.2694 8.90574 19.627 8.90574 20C8.90574 20.373 9.05389 20.7306 9.31761 20.9944C9.58134 21.2581 9.93902 21.4062 10.312 21.4062Z" fill="#6ADE46"/>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#DE4E46" fill-opacity="0.35"/>
<path d="M27.5789 14.6667C27.6906 14.6667 27.7977 14.7135 27.8767 14.7968C27.9556 14.8802 28 14.9932 28 15.1111V16C28 16.1179 27.9556 16.2309 27.8767 16.3143C27.7977 16.3976 27.6906 16.4444 27.5789 16.4444H25.8947V26.2222C25.8947 27.2124 25.0863 27.9538 24.1318 27.9978L24.0421 28H15.9579C14.9954 28 14.1575 27.2889 14.1078 26.3142L14.1053 26.2222V16.4444H12.4211C12.3094 16.4444 12.2023 16.3976 12.1233 16.3143C12.0444 16.2309 12 16.1179 12 16V15.1111C12 14.9932 12.0444 14.8802 12.1233 14.7968C12.2023 14.7135 12.3094 14.6667 12.4211 14.6667H27.5789ZM24.2105 16.4444H15.7895V26.1729L15.8063 26.1853C15.8395 26.204 15.8758 26.2156 15.9133 26.2196L15.9579 26.2222H24.0421C24.0948 26.2236 24.1469 26.2109 24.1937 26.1853L24.2105 26.1724V16.4444ZM18.7368 18.6667C18.8485 18.6667 18.9556 18.7135 19.0346 18.7968C19.1135 18.8802 19.1579 18.9932 19.1579 19.1111V23.5556C19.1579 23.6734 19.1135 23.7865 19.0346 23.8698C18.9556 23.9532 18.8485 24 18.7368 24H17.8947C17.7831 24 17.676 23.9532 17.597 23.8698C17.518 23.7865 17.4737 23.6734 17.4737 23.5556V19.1111C17.4737 18.9932 17.518 18.8802 17.597 18.7968C17.676 18.7135 17.7831 18.6667 17.8947 18.6667H18.7368ZM22.1053 18.6667C22.2169 18.6667 22.324 18.7135 22.403 18.7968C22.482 18.8802 22.5263 18.9932 22.5263 19.1111V23.5556C22.5263 23.6734 22.482 23.7865 22.403 23.8698C22.324 23.9532 22.2169 24 22.1053 24H21.2632C21.1515 24 21.0444 23.9532 20.9654 23.8698C20.8865 23.7865 20.8421 23.6734 20.8421 23.5556V19.1111C20.8421 18.9932 20.8865 18.8802 20.9654 18.7968C21.0444 18.7135 21.1515 18.6667 21.2632 18.6667H22.1053ZM22.1053 12C22.1606 12 22.2153 12.0115 22.2664 12.0338C22.3175 12.0562 22.3639 12.0889 22.403 12.1302C22.4421 12.1714 22.4731 12.2204 22.4943 12.2744C22.5154 12.3283 22.5263 12.3861 22.5263 12.4444V13.3333C22.5263 13.4512 22.482 13.5643 22.403 13.6476C22.324 13.731 22.2169 13.7778 22.1053 13.7778H17.8947C17.7831 13.7778 17.676 13.731 17.597 13.6476C17.518 13.5643 17.4737 13.4512 17.4737 13.3333V12.4444C17.4737 12.3861 17.4846 12.3283 17.5057 12.2744C17.5269 12.2204 17.5579 12.1714 17.597 12.1302C17.6361 12.0889 17.6825 12.0562 17.7336 12.0338C17.7847 12.0115 17.8394 12 17.8947 12H22.1053Z" fill="#F22828"/>
</svg>
import React, { useEffect, useState } from 'react';
import type { IconProps } from '@chakra-ui/react';
import { Box, Icon } from '@chakra-ui/react';
import { iconPaths } from './constants';
import type { IconNameType } from './type.d';
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
const [IconComponent, setIconComponent] = useState<any>(null);
useEffect(() => {
console.log(name);
iconPaths[name]?.()
.then((icon) => {
setIconComponent({ as: icon.default });
})
.catch((error) => console.log(error));
}, [name]);
return !!IconComponent ? (
<Icon
{...IconComponent}
w={w}
h={h}
boxSizing={'content-box'}
verticalAlign={'top'}
fill={'currentcolor'}
{...props}
/>
) : (
<Box w={w} h={'1px'}></Box>
);
};
export default React.memo(MyIcon);
import { iconPaths } from './constants';
export type IconNameType = keyof typeof iconPaths;
import React from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
const FormLabel = ({
children,
required,
...props
}: BoxProps & {
required?: boolean;
children: React.ReactNode;
}) => {
return (
<Box color={'myGray.900'} fontSize={'sm'} position={'relative'} {...props}>
{required && (
<Box color={'red.600'} position={'absolute'} top={'-4px'} left={'-6px'}>
*
</Box>
)}
{children}
</Box>
);
};
export default FormLabel;
import React, { forwardRef } from 'react';
import { Box, BoxProps, SpinnerProps } from '@chakra-ui/react';
import Loading from '../MyLoading';
type Props = BoxProps & {
isLoading?: boolean;
text?: string;
size?: SpinnerProps['size'];
};
const MyBox = ({ text, isLoading, children, size, ...props }: Props, ref: any) => {
return (
<Box ref={ref} position={isLoading ? 'relative' : 'unset'} {...props}>
{isLoading && <Loading fixed={false} text={text} size={size} />}
{children}
</Box>
);
};
export default forwardRef(MyBox);
import React from 'react';
import { Spinner, Flex, Box, SpinnerProps } from '@chakra-ui/react';
const Loading = ({
fixed = true,
text = '',
bg = 'rgba(255,255,255,0.5)',
zIndex = 1000,
size = 'lg'
}: {
fixed?: boolean;
text?: string;
bg?: string;
zIndex?: number;
size?: SpinnerProps['size'];
}) => {
return (
<Flex
position={fixed ? 'fixed' : 'absolute'}
zIndex={fixed ? zIndex : 10}
bg={bg}
borderRadius={'md'}
top={0}
left={0}
right={0}
bottom={0}
alignItems={'center'}
justifyContent={'center'}
flexDirection={'column'}
>
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="myGray.100"
color="primary.500"
size={size}
/>
{text && (
<Box mt={2} color="primary.600" fontWeight={'bold'}>
{text}
</Box>
)}
</Flex>
);
};
export default Loading;
import React, { useMemo } from 'react';
import { ModalFooter, ModalBody, Input, Button, Box, Textarea } from '@chakra-ui/react';
import MyModal from './index';
import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@eagic/web/hooks/useRequest';
import FormLabel from '../MyBox/FormLabel';
import { useForm } from 'react-hook-form';
export type EditFolderFormType = {
id?: string;
name?: string;
intro?: string;
};
type CommitType = {
name: string;
intro?: string;
};
const EditFolderModal = ({
onClose,
onCreate,
onEdit,
id,
name,
intro
}: EditFolderFormType & {
onClose: () => void;
onCreate: (data: CommitType) => any;
onEdit: (data: CommitType & { id: string }) => any;
}) => {
const { t } = useTranslation();
const isEdit = !!id;
const { register, handleSubmit } = useForm<EditFolderFormType>({
defaultValues: {
name,
intro
}
});
const typeMap = useMemo(
() =>
isEdit
? {
title: t('common:dataset.Edit Folder')
}
: {
title: t('common:dataset.Create Folder')
},
[isEdit, t]
);
const { run: onSave, loading } = useRequest2(
({ name = '', intro }: EditFolderFormType) => {
if (!name) return;
if (isEdit) return onEdit({ id, name, intro });
return onCreate({ name, intro });
},
{
onSuccess: (res) => {
onClose();
}
}
);
return (
<MyModal isOpen onClose={onClose} iconSrc="common/folderFill" title={typeMap.title}>
<ModalBody>
<Box>
<FormLabel mb={1}>{t('common:common.Input name')}</FormLabel>
<Input
{...register('name', { required: true })}
bg={'myGray.50'}
autoFocus
maxLength={20}
/>
</Box>
<Box mt={4}>
<FormLabel mb={1}>{t('common:common.Input folder description')}</FormLabel>
<Textarea {...register('intro')} bg={'myGray.50'} maxLength={200} />
</Box>
</ModalBody>
<ModalFooter>
<Button isLoading={loading} onClick={handleSubmit(onSave)} px={6}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default EditFolderModal;
import React from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalContentProps,
Box
} from '@chakra-ui/react';
import MyBox from '../MyBox';
import { useSystem } from '../../../../hooks/useSystem';
import Avatar from '../Avatar';
export interface MyModalProps extends ModalContentProps {
iconSrc?: string;
iconColor?: string;
title?: any;
isCentered?: boolean;
isLoading?: boolean;
isOpen?: boolean;
onClose?: () => void;
closeOnOverlayClick?: boolean;
size?: 'md' | 'lg';
}
const MyModal = ({
isOpen = true,
onClose,
iconSrc,
title,
children,
isCentered,
isLoading,
w = 'auto',
maxW = ['90vw', '600px'],
closeOnOverlayClick = true,
iconColor,
size = 'md',
...props
}: MyModalProps) => {
const { isPc } = useSystem();
return (
<Modal
isOpen={isOpen}
onClose={() => onClose && onClose()}
size={size}
autoFocus={false}
isCentered={isPc ? isCentered : true}
blockScrollOnMount={false}
closeOnOverlayClick={closeOnOverlayClick}
>
<ModalOverlay />
<ModalContent
w={w}
minW={['90vw', '400px']}
maxW={maxW}
position={'relative'}
maxH={'85vh'}
boxShadow={'7'}
{...props}
>
{!title && onClose && <ModalCloseButton zIndex={1} />}
{!!title && (
<ModalHeader
display={'flex'}
alignItems={'center'}
background={'#FBFBFC'}
borderBottom={'1px solid #F4F6F8'}
roundedTop={'lg'}
py={'10px'}
fontSize={'md'}
fontWeight={'bold'}
>
{iconSrc && (
<>
<Avatar
color={iconColor}
objectFit={'contain'}
alt=""
src={iconSrc}
w={'1.5rem'}
borderRadius={'md'}
/>
</>
)}
<Box ml={3} color={'myGray.900'} fontWeight={'500'}>
{title}
</Box>
<Box flex={1} />
{onClose && (
<ModalCloseButton position={'relative'} fontSize={'xs'} top={0} right={0} />
)}
</ModalHeader>
)}
<MyBox
isLoading={isLoading}
overflow={props.overflow || 'overlay'}
h={'100%'}
display={'flex'}
flexDirection={'column'}
>
{children}
</MyBox>
</ModalContent>
</Modal>
);
};
export default React.memo(MyModal);
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react';
import { theme } from '../styles/theme';
import { Router } from 'next/router';
import { ReactNode } from 'react';
import NProgress from 'nprogress'; //nprogress module
import 'nprogress/nprogress.css';
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
export const ChakraUIContext = ({ children }: { children: ReactNode }) => {
return (
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{children}
</ChakraProvider>
);
};
export default ChakraUIContext;
import { createContext, useContextSelector } from 'use-context-selector';
import { useTranslation } from 'next-i18next';
import { TFunction } from 'i18next';
type I18nContextType = {
commonT: TFunction<['common'], undefined>;
appT: TFunction<['app'], undefined>;
datasetT: TFunction<['dataset'], undefined>;
fileT: TFunction<['file'], undefined>;
publishT: TFunction<['publish'], undefined>;
workflowT: TFunction<['workflow'], undefined>;
userT: TFunction<['user'], undefined>;
chatT: TFunction<['chat'], undefined>;
};
export const I18nContext = createContext<I18nContextType>({
// @ts-ignore
commonT: undefined
});
const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
const { t: commonT } = useTranslation('common');
const { t: appT } = useTranslation('app');
const { t: datasetT } = useTranslation('dataset');
const { t: fileT } = useTranslation('file');
const { t: publishT } = useTranslation('publish');
const { t: workflowT } = useTranslation('workflow');
const { t: userT } = useTranslation('user');
const { t: chatT } = useTranslation('chat');
return (
<I18nContext.Provider
value={{
commonT,
appT,
datasetT,
fileT,
publishT,
workflowT,
userT,
chatT
}}
>
{children}
</I18nContext.Provider>
);
};
export default I18nContextProvider;
export const useI18n = () => {
return useContextSelector(I18nContext, (ctx) => ctx);
};
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactNode } from 'react';
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
keepPreviousData: true,
refetchOnWindowFocus: false,
retry: false,
cacheTime: 10,
networkMode: 'always'
}
}
});
const QueryClientContext = ({ children }: { children: ReactNode }) => {
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
};
export default QueryClientContext;
import { useToast } from './useToast';
import { useMutation } from '@tanstack/react-query';
import type { UseMutationOptions } from '@tanstack/react-query';
import { getErrText } from '@eagic/global/common/error/utils';
import { useTranslation } from 'next-i18next';
import { useRequest as ahooksUseRequest } from 'ahooks';
interface Props extends UseMutationOptions<any, any, any, any> {
successToast?: string | null;
errorToast?: string | null;
}
export const useRequest = ({ successToast, errorToast, onSuccess, onError, ...props }: Props) => {
const { toast } = useToast();
const { t } = useTranslation();
const mutation = useMutation<unknown, unknown, any, unknown>({
...props,
onSuccess(res, variables: void, context: unknown) {
onSuccess?.(res, variables, context);
successToast &&
toast({
title: successToast,
status: 'success'
});
},
onError(err: any, variables: void, context: unknown) {
onError?.(err, variables, context);
if (errorToast !== undefined) {
const errText = t(getErrText(err, errorToast || '') as any);
if (errText) {
toast({
title: errText,
status: 'error'
});
}
}
}
});
return mutation;
};
type UseRequestFunProps<TData, TParams extends any[]> = Parameters<
typeof ahooksUseRequest<TData, TParams>
>;
export const useRequest2 = <TData, TParams extends any[]>(
server: UseRequestFunProps<TData, TParams>[0],
options: UseRequestFunProps<TData, TParams>[1] & {
errorToast?: string;
successToast?: string;
} = {},
plugin?: UseRequestFunProps<TData, TParams>[2]
) => {
const { t } = useTranslation();
const { errorToast = 'Error', successToast, ...rest } = options || {};
const { toast } = useToast();
const res = ahooksUseRequest<TData, TParams>(
server,
{
manual: true,
...rest,
onError: (err, params) => {
rest?.onError?.(err, params);
if (errorToast !== undefined) {
const errText = t(getErrText(err, errorToast || '') as any);
if (errText) {
toast({
title: errText,
status: 'error'
});
}
}
},
onSuccess: (res, params) => {
rest?.onSuccess?.(res, params);
if (successToast) {
toast({
title: successToast,
status: 'success'
});
}
}
},
plugin
);
return res;
};
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
import { useCallback, useMemo } from 'react';
export const useToast = (props?: UseToastOptions) => {
const toast = uToast({
position: 'top',
duration: 2000,
containerStyle: {
fontSize: 'sm'
},
...props
});
const myToast = useCallback(
(options?: UseToastOptions) => {
if (options?.title || options?.description) {
toast(options);
}
},
[props]
);
return {
toast: myToast
};
};
This diff is collapsed.
{
"Delete_all": "Clear All Lexicon",
"ai_reasoning": "Thinking process",
"chat_history": "Conversation History",
"chat_input_guide_lexicon_is_empty": "Lexicon not configured yet",
"citations": "{{num}} References",
"click_contextual_preview": "Click to see contextual preview",
"config_input_guide": "Set Up Input Guide",
"config_input_guide_lexicon": "Set Up Lexicon",
"config_input_guide_lexicon_title": "Set Up Lexicon",
"content_empty": "No Content",
"contextual": "{{num}} Contexts",
"contextual_preview": "Contextual Preview {{num}} Items",
"csv_input_lexicon_tip": "Only CSV batch import is supported, click to download the template",
"custom_input_guide_url": "Custom Lexicon URL",
"delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?",
"empty_directory": "This directory is empty~",
"file_amount_over": "Exceeded maximum file quantity {{max}}",
"in_progress": "In Progress",
"input_guide": "Input Guide",
"input_guide_lexicon": "Lexicon",
"input_guide_tip": "You can set up some preset questions. When the user inputs a question, related questions from these presets will be suggested.",
"insert_input_guide,_some_data_already_exists": "Duplicate data detected, automatically filtered, {{len}} items inserted",
"is_chatting": "Chatting in progress... please wait until it finishes",
"items": "Items",
"module_runtime_and": "Total Module Runtime",
"multiple_AI_conversations": "Multiple AI Conversations",
"new_input_guide_lexicon": "New Lexicon",
"no_workflow_response": "No workflow data",
"plugins_output": "Plugin Output",
"question_tip": "From top to bottom, the response order of each module",
"response": {
"node_inputs": "Node Inputs"
},
"select_file": "Select File",
"select_img": "Select Image",
"stream_output": "Stream Output",
"view_citations": "View References",
"web_site_sync": "Web Site Sync"
}
\ No newline at end of file
This diff is collapsed.
{
"Enable": "Enable",
"collection": {
"Create update time": "Creation/Update Time",
"Training type": "Training Mode"
},
"collection_tags": "Collection Tags",
"common_dataset": "General Dataset",
"common_dataset_desc": "Build a Dataset by importing files, web links, or manual input.",
"confirm_to_rebuild_embedding_tip": "Are you sure you want to switch the index for the Dataset?\nSwitching the index is a significant operation that requires re-indexing all data in your Dataset, which may take a long time. Please ensure your account has sufficient remaining points.\n\nAdditionally, you need to update the applications that use this Dataset to avoid conflicts with other indexed model Datasets.",
"dataset": {
"no_collections": "No datasets available",
"no_tags": "No tags available"
},
"external_file": "External File Library",
"external_file_dataset_desc": "Import files from an external file library to build a Dataset. The files will not be stored again.",
"external_id": "File Reading ID",
"external_read_url": "External Preview URL",
"external_read_url_tip": "Configure the reading URL of your file library for user authentication. Use the {{fileId}} variable to refer to the external file ID.",
"external_url": "File Access URL",
"file_model_function_tip": "Enhances indexing and QA generation",
"filename": "Filename",
"folder_dataset": "Folder",
"permission": {
"des": {
"manage": "Can manage the entire knowledge base data and information",
"read": "View knowledge base content",
"write": "Ability to add and change knowledge base content"
}
},
"rebuild_embedding_start_tip": "Index model switching task has started",
"rebuilding_index_count": "Number of indexes being rebuilt: {{count}}",
"tag": {
"Add New": "Add New",
"Add_new_tag": "Add New Tag",
"Edit_tag": "Edit Tag",
"add": "Create",
"cancel": "Cancel",
"delete_tag_confirm": "Confirm to delete the tag?",
"manage": "Tagging",
"searchOrAddTag": "Search or Add Tag",
"tags": "Tags",
"total_tags": "Total {{total}} tags"
},
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "The Dataset has indexes that are being trained or rebuilt",
"website_dataset": "Website Sync",
"website_dataset_desc": "Website sync allows you to build a Dataset directly using a web link."
}
\ No newline at end of file
{
"bucket_chat": "Conversation Files",
"bucket_file": "Dataset Documents",
"click_to_view_raw_source": "Click to View Original Source",
"file_name": "Filename",
"file_size": "Filesize",
"release_the_mouse_to_upload_the_file": "Release Mouse to Upload File",
"select_and_drag_file_tip": "Click or Drag Files Here to Upload",
"select_file_amount_limit": "You can select up to {{max}} files",
"some_file_count_exceeds_limit": "Exceeded {{maxCount}} files, automatically truncated",
"some_file_size_exceeds_limit": "Some files exceed {{maxSize}}, filtered out",
"support_file_type": "Supports {{fileType}} file types",
"support_max_count": "Supports up to {{maxCount}} files",
"support_max_size": "Maximum file size is {{maxSize}}",
"upload_failed": "Upload Failed",
"reached_max_file_count": "Maximum file count reached",
"upload_error_description": "Only multiple files or a single folder can be uploaded at a time"
}
\ No newline at end of file
{
"Login": "Login",
"forget_password": "Find password",
"login_failed": "Login failed",
"login_success": "Login successful",
"password_condition": "Password maximum 60 characters",
"policy_tip": "By useing, you agree to our",
"privacy": "Privacy policy",
"register": "Register",
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
"terms": "Terms",
"use_root_login": "Log in as root user"
}
\ No newline at end of file
{
"app_key_tips": "These keys are already linked to the current application. Check the documentation for detailed usage.",
"basic_info": "Basic Info",
"copy_link_hint": "Copy the link below to the specified location",
"create_api_key": "Create New Key",
"create_link": "Create Link",
"edit_api_key": "Edit Key Details",
"edit_feishu_bot": "Edit Feishu Bot",
"edit_link": "Edit",
"feishu_api": "Feishu API",
"feishu_bot": "Feishu Bot",
"feishu_bot_desc": "Connect to Feishu Bot directly via API",
"key_alias": "Key alias, for display only",
"key_tips": "You can use the API key to access specific interfaces (cannot access the application, use the in-app API key for that)",
"link_name": "Share Link Name",
"new_feishu_bot": "Add New Feishu Bot",
"official_account": {
"create_modal_title": "Create WeChat Official Account Integration",
"desc": "Connect to WeChat Official Account directly via API",
"edit_modal_title": "Edit WeChat Official Account Integration",
"name": "WeChat Official Account Integration",
"params": "WeChat Official Account Parameters"
},
"publish_name": "Name",
"qpm_is_empty": "QPM cannot be empty",
"qpm_tips": "Maximum number of queries per minute per IP",
"request_address": "Request URL",
"show_share_link_modal_title": "Get Started",
"token_auth": "Token Authentication",
"token_auth_tips": "Token authentication server URL. If provided, a request will be sent to the specified server for authentication before each conversation.",
"token_auth_use_cases": "View Token Authentication Guide",
"wecom": {
"api": "WeCom API",
"bot": "WeCom Bot",
"bot_desc": "Connect to WeCom Bot directly via API",
"create_modal_title": "Create WeCom Bot",
"edit_modal_title": "Edit WeCom Bot",
"title": "Publish to WeCom Bot"
}
}
\ No newline at end of file
{
"bill": {
"balance": "Balance",
"buy_plan": "Purchase Plan",
"contact_customer_service": "Contact Support",
"conversion": "Conversion",
"convert_error": "Conversion Failed",
"convert_success": "Conversion Successful",
"current_token_price": "Current Token Price",
"not_need_invoice": "Balance payment, invoice not available",
"price": "Price",
"renew_plan": "Renew Plan",
"standard_valid_tip": "Plan Usage Rules: Higher-level plans will be used first. Unused plans will be activated later.",
"token_expire_1year": "Tokens are valid for one year",
"tokens": "Tokens",
"use_balance": "Use Balance",
"use_balance_hint": "Due to system upgrade, the 'Auto-renewal from balance' mode is canceled, and the balance recharge option is closed. Your balance can be used to purchase tokens.",
"valid_time": "Effective Time",
"you_can_convert": "You can convert",
"yuan": "Yuan"
},
"bill_and_invoices": "Bill & Invoice",
"bind_inform_account_error": "Failed to Bind Notification Account",
"bind_inform_account_success": "Notification Account Bound Successfully",
"delete": {
"admin_failed": "Failed to Delete Admin",
"admin_success": "Admin Deleted Successfully"
},
"has_chosen": "Selected",
"individuation": "Individuation",
"login": {
"error": "Login Error",
"password_condition": "Password can be up to 60 characters",
"success": "Login Successful"
},
"name": "Name",
"notice": "Notice",
"notification": {
"Bind Notification Pipe Hint": "Please bind a notification receiving account to ensure you receive notifications such as plan expiration reminders, ensuring your service runs smoothly.",
"remind_owner_bind": "Please remind the creator to bind a notification account"
},
"operations": "Actions",
"password": {
"code_required": "Verification Code Required",
"code_send_error": "Failed to Send Verification Code",
"code_sended": "Verification Code Sent",
"confirm": "Confirm Password",
"email_phone_error": "Invalid Email/Phone Number Format",
"email_phone_void": "Email/Phone Number Cannot Be Empty",
"get_code": "Get Verification Code",
"get_code_again": "Get Again in s",
"new_password": "New Password (4-20 characters)",
"not_match": "Passwords Do Not Match",
"password_condition": "Password must be between 4 and 20 characters",
"password_required": "Password Cannot Be Empty",
"retrieve": "Retrieve Password",
"retrieved": "Password Retrieved",
"retrieved_account": "Retrieve {{account}} Account",
"to_login": "Go to Login",
"verification_code": "Verification Code"
},
"permission": {
"Manage": "Admin",
"Manage tip": "Team admin with full permissions",
"Read": "Read Only",
"Read desc": "Members can only read related resources, cannot create new resources",
"Write": "Write",
"Write tip": "In addition to read access, can create new resources",
"only_collaborators": "Collaborators Only",
"team_read": "Team Read Access",
"team_write": "Team Write Access"
},
"permission_des": {
"manage": "Can create resources, invite, and delete members",
"read": "Members can only read related resources and cannot create new resources.",
"write": "In addition to readable resources, you can also create new resources"
},
"permissions": "Permissions",
"personal_information": "Me",
"personalization": "Personalization",
"promotion_records": "Promotion",
"register": {
"confirm": "Confirm Registration",
"register_account": "Register {{account}} Account",
"success": "Registration Successful",
"to_login": "Already have an account? Login"
},
"search_user": "Search Username",
"sign_out": "Sign out",
"synchronization": {
"button": "Sync Now",
"placeholder": "Enter Sync Tag",
"title": "Enter the sync tag link and click the sync button to synchronize"
},
"team": {
"Add manager": "Add Admin",
"add_collaborator": "Add Collaborator",
"manage_collaborators": "Manage Collaborators",
"no_collaborators": "No Collaborators"
},
"usage": {
"feishu": "Feishu",
"official_account": "Official Account",
"share": "Share Link",
"wecom": "WeCom"
},
"usage_record": "Usages"
}
\ No newline at end of file
This diff is collapsed.
import { I18nKeyFunction } from '../types/i18next';
export const i18nT: I18nKeyFunction = (key) => key;
{
"Run": "运行",
"ai_settings": "AI 配置",
"all_apps": "全部应用",
"app": {
"Version name": "版本名称",
"modules": {
"click to update": "点击更新",
"has new version": "有新版本"
},
"version_back": "回到初始状态",
"version_copy": "副本",
"version_current": "当前版本",
"version_initial": "初始版本",
"version_initial_copy": "副本-初始状态",
"version_name_tips": "版本名称不能为空",
"version_past": "发布过",
"version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本"
},
"app_detail": "应用详情",
"chat_debug": "调试预览",
"chat_logs": "对话日志",
"chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录",
"config_file_upload": "点击配置文件上传规则",
"confirm_copy_app_tip": "系统将为您创建一个相同配置应用,请确认!",
"confirm_del_app_tip": "确认删除该应用及其所有聊天记录?",
"confirm_delete_folder_tip": "确认删除该文件夹?将会删除它下面所有应用及对应的聊天记录,请确认!",
"copy_one_app": "创建副本",
"create_copy_success": "创建副本成功",
"create_empty_app": "创建空白应用",
"create_empty_plugin": "创建空白插件",
"create_empty_workflow": "创建空白工作流",
"cron": {
"every_day": "每天执行",
"every_month": "每月执行",
"every_week": "每周执行",
"interval": "间隔执行"
},
"current_settings": "当前配置",
"day": "日",
"document_quote": "文档引用",
"document_quote_tip": "通常用于接受用户上传的文档内容(这需要文档解析),也可以用于引用其他字符串数据。",
"document_upload": "文档上传",
"edit_app": "编辑应用",
"edit_info": "编辑信息",
"execute_time": "执行时间",
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
"export_configs": "导出配置",
"feedback_count": "用户反馈",
"file_recover": "文件将覆盖当前内容",
"file_upload": "文件上传",
"file_upload_tip": "开启后,可以上传文档/图片。文档保留7天,图片保留15天。使用该功能可能产生较多额外费用。为保证使用体验,使用该功能时,请选择上下文长度较大的AI模型。",
"go_to_chat": "去对话",
"go_to_run": "去运行",
"image_upload": "图片上传",
"image_upload_tip": "请确保选择可处理图片的视觉模型",
"import_configs": "导入配置",
"import_configs_failed": "导入配置失败,请确保配置正常!",
"import_configs_success": "导入成功",
"initial_form": "初始状态",
"interval": {
"12_hours": "每12小时",
"2_hours": "每2小时",
"3_hours": "每3小时",
"4_hours": "每4小时",
"6_hours": "每6小时",
"per_hour": "每小时"
},
"intro": "是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!",
"llm_not_support_vision": "该模型不支持图片识别",
"llm_use_vision": "启用图片识别",
"llm_use_vision_tip": "启用图片识别后,该模型会自动接收来自“对话框上传”的图片,以及“用户问题”中的图片链接。",
"logs_empty": "还没有日志噢~",
"logs_message_total": "消息总数",
"logs_title": "标题",
"logs_chat_user": "使用者",
"mark_count": "标注答案数量",
"module": {
"Confirm Sync": "将会更新至最新的模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
"Custom Title Tip": "该标题名字会展示在对话过程中",
"No Modules": "没找到插件",
"type": "\"{{type}}\"类型\n{{description}}"
},
"modules": {
"Title is required": "模块名不能为空"
},
"month": {
"unit": "号"
},
"move_app": "移动应用",
"not_json_file": "请选择JSON文件",
"paste_config": "粘贴配置",
"or_drag_JSON": "或拖入JSON文件",
"plugin_cost_per_times": "{{cost}}/次",
"plugin_dispatch": "插件调用",
"plugin_dispatch_tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件,知识库调用将自动作为一个特殊的插件。",
"publish_channel": "发布渠道",
"publish_success": "发布成功",
"saved_success": "保存成功",
"search_app": "搜索应用,按Enter开始搜索",
"setting_app": "应用配置",
"setting_plugin": "插件配置",
"template": {
"simple_robot": "简易机器人",
"standard_template": "标准模板",
"standard_template_des": "标准提示词,用于结构不固定的知识库。",
"qa_template": "问答模板",
"qa_template_des": "适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答",
"standard_strict": "标准严格模板",
"standard_strict_des": "在标准模板基础上,对模型的回答做更严格的要求。",
"hard_strict": "严格问答模板",
"hard_strict_des": "在问答模板基础上,对模型的回答做更严格的要求。"
},
"templateMarket": {
"Search_template": "搜索模板",
"Template_market": "模板市场",
"Use": "使用",
"no_intro": "还没有介绍~",
"templateTags": {
"Image_generation": "图片生成",
"Office_services": "办公服务",
"Recommendation": "推荐",
"Roleplay": "角色扮演",
"Web_search": "联网搜索",
"Writing": "文本创作"
}
},
"template_market": "模板市场",
"template_market_description": "在模板市场探索更多玩法,配置教程与使用引导,带你理解并上手各种应用",
"template_market_empty_data": "找不到合适的模板",
"time_zone": "时区",
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
"transition_to_workflow": "转成工作流",
"transition_to_workflow_create_new_placeholder": "创建一个新的应用,而不是修改当前应用",
"transition_to_workflow_create_new_tip": "转化成工作流后,将无法转化回简易模式,请确认!",
"type": {
"All": "全部",
"Create http plugin tip": "通过 OpenAPI Schema 批量创建插件,兼容 GPTs 格式",
"Create one plugin tip": "可以自定义输入和输出的工作流,通常用于封装重复使用的工作流",
"Create plugin bot": "创建插件",
"Create simple bot": "创建简易应用",
"Create simple bot tip": "通过填表单形式,创建简单的 AI 应用,适合新手",
"Create workflow bot": "创建工作流",
"Create workflow tip": "通过低代码的方式,构建逻辑复杂的多轮对话 AI 应用,推荐高级玩家使用",
"Http plugin": "HTTP 插件",
"Plugin": "插件",
"Simple bot": "简易应用",
"Workflow bot": "工作流"
},
"upload_file_max_amount": "最大文件数量",
"upload_file_max_amount_tip": "1.单次上传文件的最大数量。\n2.对话窗口记忆的最大文件数量:每轮对话会自动获取历史中的文件,超出范围的文件会被遗忘。",
"variable": {
"select type_desc": "可以定义一个无需用户填写的全局变量。\n该变量的值可以来自于 API 接口,分享链接的 Query 或通过【变量更新】模块进行赋值。",
"textarea_type_desc": "允许用户最多输入4000字的对话框。"
},
"version": {
"Revert success": "回滚成功"
},
"vision_model_title": "启用图片识别",
"week": {
"Friday": "星期五",
"Monday": "星期一",
"Saturday": "星期六",
"Sunday": "星期日",
"Thursday": "星期四",
"Tuesday": "星期二",
"Wednesday": "星期三"
},
"workflow": {
"Input guide": "填写说明",
"file_url": "文档链接",
"read_files": "文档解析",
"form_input": "表单输入",
"form_input_description_placeholder": "例如:\n补充您的信息",
"form_input_tip": "该模块可以配置多种输入,引导用户输入特定内容。",
"input_description_tip": "你可以添加一段说明文字,用以向用户说明需要输入的内容",
"read_files_result": "文档解析结果",
"read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。",
"read_files_tip": "解析对话中所有上传的文档,并返回对应文档内容",
"select_description": "说明文字",
"select_description_placeholder": "例如: \n冰箱里是否有西红柿?",
"select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。",
"select_result": "选择的结果",
"template": {
"communication": "通信"
},
"user_file_input": "文件链接",
"user_file_input_desc": "用户上传的文档和图片链接",
"user_select": "用户选择",
"user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
},
"permission": {
"des": {
"read": "可使用该应用进行对话",
"write": "可查看和编辑应用",
"manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限"
}
}
}
{
"Delete_all": "清空词库",
"ai_reasoning": "思考过程",
"chat_history": "聊天记录",
"chat_input_guide_lexicon_is_empty": "还没有配置词库",
"citations": "{{num}}条引用",
"click_contextual_preview": "点击查看上下文预览",
"config_input_guide": "配置输入引导",
"config_input_guide_lexicon": "配置词库",
"config_input_guide_lexicon_title": "配置词库",
"content_empty": "内容为空",
"contextual": "{{num}}条上下文",
"contextual_preview": "上下文预览 {{num}} 条",
"csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板",
"custom_input_guide_url": "自定义词库地址",
"delete_all_input_guide_confirm": "确定要清空输入引导词库吗?",
"empty_directory": "这个目录已经没东西可选了~",
"file_amount_over": "超出最大文件数量 {{max}}",
"file_input": "系统文件",
"file_input_tip": "可通过【插件开始】节点的“文件链接”获取对应文件的链接",
"in_progress": "进行中",
"input_guide": "输入引导",
"input_guide_lexicon": "词库",
"input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。",
"insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据",
"is_chatting": "正在聊天中...请等待结束",
"items": "条",
"module_runtime_and": "模块运行时间和",
"multiple_AI_conversations": "多组 AI 对话",
"new_input_guide_lexicon": "新词库",
"no_workflow_response": "没有运行数据",
"plugins_output": "插件输出",
"question_tip": "从上到下,为各个模块的响应顺序",
"response": {
"node_inputs": "节点输入"
},
"select": "选择",
"select_file": "选择文件",
"select_img": "选择图片",
"select_file_img": "上传文件/图片",
"stream_output": "流输出",
"view_citations": "查看引用",
"web_site_sync": "Web站点同步"
}
\ No newline at end of file
This diff is collapsed.
{
"Enable": "启用",
"collection": {
"Create update time": "创建/更新时间",
"Training type": "训练模式"
},
"collection_tags": "集合标签",
"common_dataset": "通用知识库",
"common_dataset_desc": "可通过导入文件、网页链接或手动录入形式构建知识库",
"confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作,需要对您知识库内所有数据进行重新索引,时间可能较长,请确保账号内剩余积分充足。\n\n此外,你还需要注意修改选择该知识库的应用,避免它们与其他索引模型知识库混用。",
"dataset": {
"no_collections": "暂无数据集",
"no_tags": "暂无标签"
},
"external_file": "外部文件库",
"external_file_dataset_desc": "可以从外部文件库导入文件构建知识库,文件不会进行二次存储",
"external_id": "文件阅读 ID",
"external_read_url": "外部预览地址",
"external_read_url_tip": "可以配置你文件库的阅读地址。便于对用户进行阅读鉴权操作。目前可以使用 {{fileId}} 变量来指代外部文件 ID。",
"external_url": "文件访问 URL",
"file_model_function_tip": "用于增强索引和 QA 生成",
"filename": "文件名",
"folder_dataset": "文件夹",
"rebuild_embedding_start_tip": "切换索引模型任务已开始",
"rebuilding_index_count": "重建中索引数量:{{count}}",
"tag": {
"Add New": "新建",
"Add_new_tag": "新建标签",
"Edit_tag": "编辑标签",
"add": "创建",
"cancel": "取消选择",
"delete_tag_confirm": "确定删除标签?",
"manage": "标签管理",
"searchOrAddTag": "搜索或添加标签",
"tags": "标签",
"total_tags": "共{{total}}个标签"
},
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "知识库有训练中或正在重建的索引",
"website_dataset": "Web 站点同步",
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
"permission": {
"des": {
"read": "可查看知识库内容",
"write": "可增加和变更知识库内容",
"manage": "可管理整个知识库数据和信息"
}
}
}
\ No newline at end of file
{
"bucket_chat": "对话文件",
"bucket_file": "知识库文件",
"click_to_view_raw_source": "点击查看来源",
"file_name": "文件名",
"file_size": "文件大小",
"release_the_mouse_to_upload_the_file": "松开鼠标上传文件",
"select_and_drag_file_tip": "点击或拖动文件到此处上传",
"select_file_amount_limit": "最多选择 {{max}} 个文件",
"some_file_count_exceeds_limit": "超出 {{maxCount}} 个文件,已自动截取",
"some_file_size_exceeds_limit": "部分文件超出 {{maxSize}},已被过滤",
"support_file_type": "支持 {{fileType}} 类型文件",
"support_max_count": "最多支持 {{maxCount}} 个文件",
"support_max_size": "单个文件最大 {{maxSize}}",
"upload_failed": "上传异常",
"reached_max_file_count": "已达到最大文件数量",
"upload_error_description": "单次只支持上传多个文件或者一个文件夹"
}
\ No newline at end of file
{
"Login": "登录",
"forget_password": "忘记密码?",
"login_failed": "登录异常",
"login_success": "登录成功",
"password_condition": "密码最多 60 位",
"policy_tip": "使用即代表你同意我们的",
"privacy": "隐私协议",
"register": "注册账号",
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
"terms": "服务协议",
"use_root_login": "使用 root 用户登录"
}
\ No newline at end of file
{
"app_key_tips": "这些 key 已有当前应用标识,具体使用可参考文档:",
"basic_info": "基本信息",
"copy_link_hint": "将下面链接复制到指定位置",
"create_api_key": "创建新 key",
"create_link": "创建链接",
"edit_api_key": "编辑 key 信息",
"edit_feishu_bot": "编辑飞书机器人",
"edit_link": "编辑",
"feishu_api": "飞书接口",
"feishu_bot": "飞书机器人",
"feishu_bot_desc": "通过 API 直接接入飞书机器人",
"key_alias": "key 的别名,仅用于展示",
"key_tips": "你可以使用 API 密钥访问一些特定的接口(无法访问应用,访问应用需使用应用内的 API key)",
"link_name": "分享链接的名字",
"new_feishu_bot": "新增飞书机器人",
"official_account": {
"create_modal_title": "创建微信公众号接入",
"desc": "通过 API 直接接入微信公众号",
"edit_modal_title": "编辑微信公众号接入",
"name": "微信公众号接入",
"params": "微信公众号参数"
},
"publish_name": "名称",
"qpm_is_empty": "QPM 不能为空",
"qpm_tips": "每个 IP 每分钟最多提问多少次",
"request_address": "请求地址",
"show_share_link_modal_title": "开始使用",
"token_auth": "身份验证",
"token_auth_tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token_auth_use_cases": "查看身份验证使用说明",
"wecom": {
"api": "企微 API",
"bot": "企业微信机器人",
"bot_desc": "通过 API 直接接入企业微信机器人",
"create_modal_title": "创建企微机器人",
"edit_modal_title": "编辑企微机器人",
"title": "发布到企业微信机器人"
}
}
\ No newline at end of file
{
"bill": {
"balance": "余额",
"buy_plan": "购买套餐",
"contact_customer_service": "联系客服",
"conversion": "兑换",
"convert_error": "兑换失败",
"convert_success": "兑换成功",
"current_token_price": "当前积分价格",
"not_need_invoice": "余额支付,无法开票",
"price": "价格",
"renew_plan": "续费套餐",
"standard_valid_tip": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
"token_expire_1year": "积分有效期一年",
"tokens": "积分",
"use_balance": "使用余额",
"use_balance_hint": "由于系统升级,原“自动续费从余额扣款”模式取消,余额充值入口关闭。您的余额可用于购买积分",
"valid_time": "生效时间",
"you_can_convert": "您可兑换",
"yuan": "元"
},
"bill_and_invoices": "账单",
"bind_inform_account_error": "绑定通知账号异常",
"bind_inform_account_success": "绑定通知账号成功",
"delete": {
"admin_failed": "删除管理员失败",
"admin_success": "删除管理员成功"
},
"has_chosen": "已选择",
"individuation": "个性化",
"login": {
"error": "登录异常",
"password_condition": "密码最多 60 位",
"success": "登录成功"
},
"name": "名称",
"notice": "通知",
"notification": {
"Bind Notification Pipe Hint": "请绑定通知接收账号,以确保您能正常接收套餐过期提醒等通知,保障您的服务正常运行。",
"remind_owner_bind": "请提醒创建者绑定通知账号"
},
"operations": "操作",
"password": {
"code_required": "验证码不能为空",
"code_send_error": "验证码发送异常",
"code_sended": "验证码已发送",
"confirm": "确认密码",
"email_phone_error": "邮箱/手机号格式错误",
"email_phone_void": "邮箱/手机号不能为空",
"get_code": "获取验证码",
"get_code_again": "s后重新获取",
"new_password": "新密码(4~20位)",
"not_match": "两次密码不一致",
"password_condition": "密码最少 4 位最多 20 位",
"password_required": "密码不能为空",
"retrieve": "找回密码",
"retrieved": "密码已找回",
"retrieved_account": "找回 {{account}} 账号",
"to_login": "去登录",
"verification_code": "验证码"
},
"permission": {
"Manage": "管理员",
"Manage tip": "团队管理员,拥有全部权限",
"Read": "仅读",
"Read desc": "成员仅可阅读相关资源,无法新建资源",
"Write": "可写",
"Write tip": "除了可读资源外,还可以新建新的资源",
"only_collaborators": "仅协作者访问",
"team_read": "团队可访问",
"team_write": "团队可编辑"
},
"permission_des": {
"manage": "可创建资源、邀请、删除成员",
"read": "成员仅可阅读相关资源,无法新建资源",
"write": "除了可读资源外,还可以新建新的资源"
},
"permissions": "权限",
"personal_information": "个人信息",
"personalization": "个性化",
"promotion_records": "推广记录",
"register": {
"confirm": "确认注册",
"register_account": "注册 {{account}} 账号",
"success": "注册成功",
"to_login": "已有账号,去登录"
},
"search_user": "搜索用户名",
"sign_out": "登出",
"synchronization": {
"button": "立即同步",
"placeholder": "请输入同步标签",
"title": "填写标签同步链接,点击同步按钮即可同步"
},
"team": {
"Add manager": "添加管理员",
"add_collaborator": "添加协作者",
"manage_collaborators": "管理协作者",
"no_collaborators": "暂无协作者"
},
"usage": {
"feishu": "飞书",
"official_account": "公众号",
"share": "分享链接",
"wecom": "企业微信"
},
"usage_record": "使用记录"
}
This diff is collapsed.
body input,
body select {
--input-font-size: var(--chakra-fontSizes-sm) !important;
}
.chakra-tooltip {
font-size: var(--chakra-fontSizes-xs) !important;
}
#nprogress .bar {
background: '#1237b3' !important; //自定义颜色
}
.textEllipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.textEllipsis2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.textEllipsis3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.grecaptcha-badge {
display: none !important;
}
.textlg {
background: linear-gradient(to bottom right, #1237b3 0%, #3370ff 40%, #4e83fd 80%, #85b1ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
span[tabindex='0'] {
line-height: 1;
}
@keyframes zoomStopIcon {
0% {
transform: scale(0.8);
}
100% {
transform: scale(1.2);
}
}
.react-flow__panel.react-flow__attribution {
z-index: 0;
left: 0;
background: transparent;
}
.react-flow__handle {
&.connecting {
border-color: #039855 !important;
& .flow-handle {
border-color: #039855 !important;
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment