WSn:Wn].gs/+"v:q_Q*An~zF*g-@j@jtSTv5H/,S-3:R?r9R}.'
diff --git a/service-web/client/src/pages/ai/knowledge/DataDetail.tsx b/service-web/client/src/pages/ai/knowledge/DataDetail.tsx
new file mode 100644
index 0000000..8f60a39
--- /dev/null
+++ b/service-web/client/src/pages/ai/knowledge/DataDetail.tsx
@@ -0,0 +1,105 @@
+import React from 'react'
+import {useParams} from 'react-router'
+import {amisRender, crudCommonOptions} from '../../../util/amis.tsx'
+
+const DataDetail: React.FC = () => {
+ const {name} = useParams()
+ return (
+
+ {amisRender(
+ {
+ className: 'h-full',
+ type: 'page',
+ title: `数据详情 (知识库:${name})`,
+ size: 'lg',
+ actions: [],
+ body: [
+ {
+ type: 'crud',
+ api: {
+ url: 'http://127.0.0.1:8080/knowledge/list_points?name=${name}',
+ headers: {
+ 'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
+ },
+ },
+ ...crudCommonOptions(),
+ headerToolbar: [
+ 'reload',
+ ],
+ columns: [
+ {
+ name: 'id',
+ hidden: true,
+ },
+ {
+ name: 'text',
+ label: '内容',
+ },
+ {
+ type: 'operation',
+ label: '操作',
+ width: 100,
+ buttons: [
+ {
+ type: 'action',
+ label: '编辑',
+ level: 'link',
+ size: 'lg',
+ actionType: 'dialog',
+ dialog: {
+ title: '编辑文段',
+ size: 'md',
+ body: {
+ type: 'form',
+ body: [
+ {
+ type: 'input-text',
+ name: 'id',
+ disabled: true,
+ label: '文段ID',
+ },
+ {
+ type: 'editor',
+ label: '内容',
+ name: 'text',
+ language: 'plaintext',
+ options: {
+ lineNumbers: 'off',
+ wordWrap: 'bounded',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: 'action',
+ label: '删除',
+ className: 'text-danger hover:text-red-600',
+ level: 'link',
+ size: 'xs',
+ actionType: 'ajax',
+ api: {
+ method: 'get',
+ headers: {
+ 'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
+ },
+ },
+ confirmText: '确认删除',
+ confirmTitle: '删除',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: name,
+ },
+ )}
+
+ )
+}
+
+export default DataDetail
\ No newline at end of file
diff --git a/service-web/client/src/pages/ai/knowledge/DataImport.tsx b/service-web/client/src/pages/ai/knowledge/DataImport.tsx
new file mode 100644
index 0000000..91a3307
--- /dev/null
+++ b/service-web/client/src/pages/ai/knowledge/DataImport.tsx
@@ -0,0 +1,144 @@
+import React from 'react'
+import {useParams} from 'react-router'
+import styled from 'styled-components'
+import {amisRender} from '../../../util/amis.tsx'
+
+const ImportDataDiv = styled.div`
+ .antd-EditorControl {
+ min-height: 500px !important;
+ }
+`
+
+const DataImport: React.FC = () => {
+ const {name} = useParams()
+ return (
+
+ {amisRender({
+ type: 'page',
+ title: `数据导入 (知识库:${name})`,
+ body: [
+ [
+ {
+ className: 'h-full',
+ type: 'grid',
+ columns: [
+ {
+ body: [
+ {
+ type: 'form',
+ wrapWithPanel: false,
+ mode: 'horizontal',
+ actions: [],
+ body: [
+ {
+ name: 'mode',
+ type: 'radios',
+ label: '解析模式',
+ value: 'normal',
+ options: [
+ {
+ value: 'normal',
+ label: '常规模式',
+ },
+ {
+ value: 'llm',
+ label: '智能模式',
+ },
+ {
+ value: 'qa',
+ label: 'Q/A模式',
+ },
+ ],
+ },
+ {
+ name: 'type',
+ type: 'radios',
+ label: '数据形式',
+ value: 'text',
+ options: [
+ {
+ value: 'text',
+ label: '文本',
+ },
+ {
+ value: 'file',
+ label: '文件',
+ },
+ ],
+ },
+ {
+ visibleOn: 'type === \'text\'',
+ type: 'editor',
+ label: '数据内容',
+ name: 'content',
+ language: 'plaintext',
+ options: {
+ lineNumbers: 'off',
+ wordWrap: 'bounded',
+ },
+ },
+ {
+ visibleOn: 'type === \'file\'',
+ type: 'input-file',
+ name: 'files',
+ label: '数据文件',
+ accept: '.txt,.csv',
+ autoUpload: false,
+ drag: true,
+ multiple: true,
+ },
+ {
+ className: 'text-right',
+ type: 'button-toolbar',
+ buttons: [
+ {
+ type: 'action',
+ label: '预览',
+ },
+ {
+ type: 'submit',
+ label: '提交',
+ level: 'primary',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ body: [
+ {
+ type: 'card',
+ className: 'h-full',
+ header: {
+ title: '解析预览',
+ subTitle: '截取部份文本进行解析预览',
+ },
+ body: [
+ {
+ type: 'list',
+ source: '${rows}',
+ listItem: [
+ {
+ body: {
+ type: 'tpl',
+ tpl: '${content}',
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ ],
+ })}
+
+ )
+}
+
+export default DataImport
\ No newline at end of file
diff --git a/service-web/client/src/pages/ai/knowledge/Knowledge.tsx b/service-web/client/src/pages/ai/knowledge/Knowledge.tsx
new file mode 100644
index 0000000..9643f81
--- /dev/null
+++ b/service-web/client/src/pages/ai/knowledge/Knowledge.tsx
@@ -0,0 +1,176 @@
+import React from 'react'
+import {useNavigate} from 'react-router'
+import {amisRender, crudCommonOptions, mappingField, mappingItem} from '../../../util/amis.tsx'
+
+const strategyMapping = [
+ mappingItem('文本', 'Cosine'),
+ mappingItem('图片', 'Euclid'),
+]
+
+const statusMapping = [
+ mappingItem('正常', 'Green', 'label-success'),
+ mappingItem('优化中', 'Yellow', 'label-warning'),
+ mappingItem('错误', 'Red', 'label-danger'),
+ mappingItem('等待中', 'Grey', 'label-primary'),
+]
+
+const Knowledge: React.FC = () => {
+ const navigate = useNavigate()
+ return (
+
+ {amisRender(
+ {
+ type: 'page',
+ title: '知识库',
+ body: [
+ {
+ type: 'crud',
+ api: {
+ url: 'http://127.0.0.1:8080/knowledge/list',
+ headers: {
+ 'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
+ },
+ },
+ ...crudCommonOptions(),
+ headerToolbar: [
+ 'reload',
+ {
+ type: 'action',
+ label: '',
+ icon: 'fa fa-plus',
+ actionType: 'dialog',
+ dialog: {
+ title: '新增知识库',
+ size: 'md',
+ body: {
+ type: 'form',
+ api: {
+ url: 'http://127.0.0.1:8080/knowledge/add',
+ dataType: 'form',
+ headers: {
+ 'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
+ },
+ },
+ body: [
+ {
+ type: 'input-text',
+ name: 'name',
+ label: '名称',
+ },
+ {
+ type: 'select',
+ name: 'strategy',
+ label: '类型',
+ value: 'Cosine',
+ options: [
+ {
+ label: '文本',
+ value: 'Cosine',
+ },
+ {
+ label: '图片',
+ value: 'Euclid',
+ disabled: true,
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ ],
+ columns: [
+ {
+ name: 'name',
+ label: '名称',
+ },
+ {
+ label: '类型',
+ width: 80,
+ align: 'center',
+ ...mappingField('strategy', strategyMapping),
+ },
+ {
+ name: 'points',
+ label: '文本数',
+ width: 80,
+ align: 'center',
+ },
+ {
+ label: '状态',
+ width: 80,
+ align: 'center',
+ ...mappingField('status', statusMapping),
+ },
+ {
+ type: 'operation',
+ label: '操作',
+ width: 150,
+ buttons: [
+ {
+ type: 'action',
+ label: '详情',
+ level: 'link',
+ size: 'xs',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'custom',
+ // @ts-ignore
+ script: (context, action, event) => {
+ navigate(`/ai/knowledge/detail/${context.props.data['name']}`)
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: 'action',
+ label: '导入',
+ level: 'link',
+ size: 'xs',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'custom',
+ // @ts-ignore
+ script: (context, action, event) => {
+ navigate(`/ai/knowledge/import/${context.props.data['name']}`)
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: 'action',
+ label: '删除',
+ className: 'text-danger hover:text-red-600',
+ level: 'link',
+ size: 'xs',
+ actionType: 'ajax',
+ api: {
+ method: 'get',
+ url: 'http://127.0.0.1:8080/knowledge/delete?name=${name}',
+ headers: {
+ 'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
+ },
+ },
+ confirmText: '确认删除',
+ confirmTitle: '删除',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ )}
+
+ )
+}
+
+export default Knowledge
\ No newline at end of file
diff --git a/service-web/client/src/pages/overview/Cloud.tsx b/service-web/client/src/pages/overview/Cloud.tsx
index d7c6583..2ae64e3 100644
--- a/service-web/client/src/pages/overview/Cloud.tsx
+++ b/service-web/client/src/pages/overview/Cloud.tsx
@@ -6,7 +6,7 @@ import {
serviceLogByAppName,
serviceLogByAppNameAndHost,
time,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
const cloudCrud = (title: string, path: string) => {
return {
diff --git a/service-web/client/src/pages/overview/Overview.tsx b/service-web/client/src/pages/overview/Overview.tsx
index 1baf313..9180985 100644
--- a/service-web/client/src/pages/overview/Overview.tsx
+++ b/service-web/client/src/pages/overview/Overview.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {amisRender, commonInfo, crudCommonOptions, readOnlyDialogOptions} from '../../util/amis.ts'
+import {amisRender, commonInfo, crudCommonOptions, readOnlyDialogOptions} from '../../util/amis.tsx'
const color = (number: number) => {
let color = 'text-success'
diff --git a/service-web/client/src/pages/overview/Queue.tsx b/service-web/client/src/pages/overview/Queue.tsx
index 7d0581e..dea1640 100644
--- a/service-web/client/src/pages/overview/Queue.tsx
+++ b/service-web/client/src/pages/overview/Queue.tsx
@@ -6,7 +6,7 @@ import {
paginationCommonOptions,
time,
yarnQueueCrud,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
const queueCrud = (name: string) => {
return {
diff --git a/service-web/client/src/pages/overview/Table.tsx b/service-web/client/src/pages/overview/Table.tsx
index 01b1903..5d8b299 100644
--- a/service-web/client/src/pages/overview/Table.tsx
+++ b/service-web/client/src/pages/overview/Table.tsx
@@ -14,7 +14,7 @@ import {
tableMetaDialog,
tableRunningStateMapping,
timeAndFrom,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
function Table() {
return (
diff --git a/service-web/client/src/pages/overview/Task.tsx b/service-web/client/src/pages/overview/Task.tsx
index 62a61c6..ef5fb64 100644
--- a/service-web/client/src/pages/overview/Task.tsx
+++ b/service-web/client/src/pages/overview/Task.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {amisRender, commonInfo, paginationCommonOptions, serviceLogByAppName, yarnCrudColumns} from '../../util/amis.ts'
+import {amisRender, commonInfo, paginationCommonOptions, serviceLogByAppName, yarnCrudColumns} from '../../util/amis.tsx'
const Task: React.FC = () => {
return (
diff --git a/service-web/client/src/pages/overview/Tool.tsx b/service-web/client/src/pages/overview/Tool.tsx
index af86bb1..b33dafd 100644
--- a/service-web/client/src/pages/overview/Tool.tsx
+++ b/service-web/client/src/pages/overview/Tool.tsx
@@ -10,7 +10,7 @@ import {
paginationCommonOptions,
readOnlyDialogOptions,
timelineColumns,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
const Tool: React.FC = () => {
return (
diff --git a/service-web/client/src/pages/overview/Version.tsx b/service-web/client/src/pages/overview/Version.tsx
index b04a443..61cc835 100644
--- a/service-web/client/src/pages/overview/Version.tsx
+++ b/service-web/client/src/pages/overview/Version.tsx
@@ -10,7 +10,7 @@ import {
paginationCommonOptions,
tableMetaDialog,
versionUpdateStateMapping,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
function Version() {
return (
diff --git a/service-web/client/src/pages/overview/Yarn.tsx b/service-web/client/src/pages/overview/Yarn.tsx
index 3803b3d..9c54aac 100644
--- a/service-web/client/src/pages/overview/Yarn.tsx
+++ b/service-web/client/src/pages/overview/Yarn.tsx
@@ -7,7 +7,7 @@ import {
paginationCommonOptions,
yarnCrudColumns,
yarnQueueCrud,
-} from '../../util/amis.ts'
+} from '../../util/amis.tsx'
const Yarn: React.FC = () => {
const {clusters, queue, search} = useParams()
diff --git a/service-web/client/src/pages/overview/YarnCluster.tsx b/service-web/client/src/pages/overview/YarnCluster.tsx
index bdabe67..3c4e959 100644
--- a/service-web/client/src/pages/overview/YarnCluster.tsx
+++ b/service-web/client/src/pages/overview/YarnCluster.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {amisRender, commonInfo, yarnQueueCrud} from '../../util/amis.ts'
+import {amisRender, commonInfo, yarnQueueCrud} from '../../util/amis.tsx'
const YarnCluster: React.FC = () => {
return (
diff --git a/service-web/client/src/route.tsx b/service-web/client/src/route.tsx
index b9ad255..288632b 100644
--- a/service-web/client/src/route.tsx
+++ b/service-web/client/src/route.tsx
@@ -3,6 +3,7 @@ import {
CloudOutlined,
ClusterOutlined,
CompressOutlined,
+ DatabaseOutlined,
InfoCircleOutlined,
OpenAIOutlined,
QuestionOutlined,
@@ -14,6 +15,9 @@ import {
import {Navigate, type RouteObject} from 'react-router'
import Conversation from './pages/ai/Conversation.tsx'
import Inspection from './pages/ai/Inspection.tsx'
+import DataDetail from './pages/ai/knowledge/DataDetail.tsx'
+import DataImport from './pages/ai/knowledge/DataImport.tsx'
+import Knowledge from './pages/ai/knowledge/Knowledge.tsx'
import App from './pages/App.tsx'
import Cloud from './pages/overview/Cloud.tsx'
import Overview from './pages/overview/Overview.tsx'
@@ -24,7 +28,7 @@ import Tool from './pages/overview/Tool.tsx'
import Version from './pages/overview/Version.tsx'
import Yarn from './pages/overview/Yarn.tsx'
import YarnCluster from './pages/overview/YarnCluster.tsx'
-import {commonInfo} from './util/amis.ts'
+import {commonInfo} from './util/amis.tsx'
export const routes: RouteObject[] = [
{
@@ -86,6 +90,18 @@ export const routes: RouteObject[] = [
path: 'conversation',
Component: Conversation,
},
+ {
+ path: 'knowledge',
+ Component: Knowledge,
+ },
+ {
+ path: 'knowledge/import/:name',
+ Component: DataImport,
+ },
+ {
+ path: 'knowledge/detail/:name',
+ Component: DataDetail,
+ },
],
},
],
@@ -179,6 +195,11 @@ export const menus = {
name: '智能巡检',
icon: ,
},
+ {
+ path: '/ai/knowledge',
+ name: '知识库',
+ icon: ,
+ },
],
},
],
diff --git a/service-web/client/src/util/amis.ts b/service-web/client/src/util/amis.tsx
similarity index 96%
rename from service-web/client/src/util/amis.ts
rename to service-web/client/src/util/amis.tsx
index 0584098..a41397b 100644
--- a/service-web/client/src/util/amis.ts
+++ b/service-web/client/src/util/amis.tsx
@@ -1,4 +1,4 @@
-import {attachmentAdpator, makeTranslator, render, type Schema} from 'amis'
+import {AlertComponent, attachmentAdpator, makeTranslator, render, type Schema, ToastComponent} from 'amis'
import 'amis/lib/themes/antd.css'
import 'amis/lib/helper.css'
@@ -58,85 +58,99 @@ const responseAdaptor = () => (response: any) => {
}
}
-export const amisRender = (schema: Schema) => {
- return render(
- schema,
- {
- theme: 'antd',
- },
- {
- fetcher: async (api: any) => {
- let {url, method, data, responseType, config, headers} = api
- config = config || {}
- config.url = url
- config.withCredentials = true
- responseType && (config.responseType = responseType)
+export const amisRender = (schema: Schema, data: Record = {}) => {
+ const theme = 'antd'
+ const locale = 'zh-CN'
+ return (
+ <>
+
+
+ {render(
+ schema,
+ {
+ data: data,
+ theme: theme,
+ },
+ {
+ fetcher: async (api: any) => {
+ let {url, method, data, responseType, config, headers} = api
+ config = config || {}
+ config.url = url
+ config.withCredentials = true
+ responseType && (config.responseType = responseType)
- if (config.cancelExecutor) {
- config.cancelToken = new (axios as any).CancelToken(
- config.cancelExecutor,
- )
- }
-
- config.headers = headers || {}
- config.method = method
- config.data = data
-
- if (method === 'get' && data) {
- config.params = data
- } else if (data && data instanceof FormData) {
- // config.headers['Content-Type'] = 'multipart/form-data';
- } else if (
- data &&
- typeof data !== 'string' &&
- !(data instanceof Blob) &&
- !(data instanceof ArrayBuffer)
- ) {
- data = JSON.stringify(data)
- config.headers['Content-Type'] = 'application/json'
- }
-
- // 支持返回各种报错信息
- config.validateStatus = function () {
- return true
- }
-
- let response = await axios(config)
- response = await attachmentAdpator(response, __, api)
- response = responseAdaptor()(response)
-
- if (response.status >= 400) {
- if (response.data) {
- // 主要用于 raw: 模式下,后端自己校验登录,
- if (
- response.status === 401 &&
- response.data.location &&
- response.data.location.startsWith('http')
- ) {
- location.href = response.data.location.replace(
- '{{redirect}}',
- encodeURIComponent(location.href),
- )
- return new Promise(() => {
- })
- } else if (response.data.msg) {
- throw new Error(response.data.msg)
- } else {
- throw new Error(
- 'System.requestError' + JSON.stringify(response.data, null, 2),
+ if (config.cancelExecutor) {
+ config.cancelToken = new (axios as any).CancelToken(
+ config.cancelExecutor,
)
}
- } else {
- throw new Error(
- `${'System.requestErrorStatus'} ${response.status}`,
- )
- }
- }
- return response
- },
- isCancel: (value: any) => (axios as any).isCancel(value),
- },
+ config.headers = headers || {}
+ config.method = method
+ config.data = data
+
+ if (method === 'get' && data) {
+ config.params = data
+ } else if (data && data instanceof FormData) {
+ // config.headers['Content-Type'] = 'multipart/form-data';
+ } else if (
+ data &&
+ typeof data !== 'string' &&
+ !(data instanceof Blob) &&
+ !(data instanceof ArrayBuffer)
+ ) {
+ data = JSON.stringify(data)
+ config.headers['Content-Type'] = 'application/json'
+ }
+
+ // 支持返回各种报错信息
+ config.validateStatus = function () {
+ return true
+ }
+
+ let response = await axios(config)
+ response = await attachmentAdpator(response, __, api)
+ response = responseAdaptor()(response)
+
+ if (response.status >= 400) {
+ if (response.data) {
+ // 主要用于 raw: 模式下,后端自己校验登录,
+ if (
+ response.status === 401 &&
+ response.data.location &&
+ response.data.location.startsWith('http')
+ ) {
+ location.href = response.data.location.replace(
+ '{{redirect}}',
+ encodeURIComponent(location.href),
+ )
+ return new Promise(() => {
+ })
+ } else if (response.data.msg) {
+ throw new Error(response.data.msg)
+ } else {
+ throw new Error(
+ 'System.requestError' + JSON.stringify(response.data, null, 2),
+ )
+ }
+ } else {
+ throw new Error(
+ `${'System.requestErrorStatus'} ${response.status}`,
+ )
+ }
+ }
+
+ return response
+ },
+ isCancel: (value: any) => (axios as any).isCancel(value),
+ },
+ )}
+ >
)
}