mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-04-23 06:30:00 +08:00
Feat: add agent share team viewer (#6222)
### What problem does this PR solve? Allow member view agent # Canvas editor  # List agent  # Setting  _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe): --------- Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
This commit is contained in:
parent
d17ec26c56
commit
344727f9ba
@ -18,6 +18,7 @@ import traceback
|
|||||||
from flask import request, Response
|
from flask import request, Response
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService
|
from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService
|
||||||
|
from api.db.services.user_service import TenantService
|
||||||
from api.db.services.user_canvas_version import UserCanvasVersionService
|
from api.db.services.user_canvas_version import UserCanvasVersionService
|
||||||
from api.settings import RetCode
|
from api.settings import RetCode
|
||||||
from api.utils import get_uuid
|
from api.utils import get_uuid
|
||||||
@ -25,6 +26,7 @@ from api.utils.api_utils import get_json_result, server_error_response, validate
|
|||||||
from agent.canvas import Canvas
|
from agent.canvas import Canvas
|
||||||
from peewee import MySQLDatabase, PostgresqlDatabase
|
from peewee import MySQLDatabase, PostgresqlDatabase
|
||||||
from api.db.db_models import APIToken
|
from api.db.db_models import APIToken
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@manager.route('/templates', methods=['GET']) # noqa: F821
|
@manager.route('/templates', methods=['GET']) # noqa: F821
|
||||||
@ -86,10 +88,11 @@ def save():
|
|||||||
@manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
|
@manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
|
||||||
@login_required
|
@login_required
|
||||||
def get(canvas_id):
|
def get(canvas_id):
|
||||||
e, c = UserCanvasService.get_by_id(canvas_id)
|
e, c = UserCanvasService.get_by_tenant_id(canvas_id)
|
||||||
|
logging.info(f"get canvas_id: {canvas_id} c: {c}")
|
||||||
if not e:
|
if not e:
|
||||||
return get_data_error_result(message="canvas not found.")
|
return get_data_error_result(message="canvas not found.")
|
||||||
return get_json_result(data=c.to_dict())
|
return get_json_result(data=c)
|
||||||
|
|
||||||
@manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
|
@manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
|
||||||
def getsse(canvas_id):
|
def getsse(canvas_id):
|
||||||
@ -288,10 +291,6 @@ def test_db_connect():
|
|||||||
return get_json_result(data="Database Connection Successful!")
|
return get_json_result(data="Database Connection Successful!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return server_error_response(e)
|
return server_error_response(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#api get list version dsl of canvas
|
#api get list version dsl of canvas
|
||||||
@manager.route('/getlistversion/<canvas_id>', methods=['GET']) # noqa: F821
|
@manager.route('/getlistversion/<canvas_id>', methods=['GET']) # noqa: F821
|
||||||
@login_required
|
@login_required
|
||||||
@ -301,7 +300,6 @@ def getlistversion(canvas_id):
|
|||||||
return get_json_result(data=list)
|
return get_json_result(data=list)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return get_data_error_result(message=f"Error getting history files: {e}")
|
return get_data_error_result(message=f"Error getting history files: {e}")
|
||||||
|
|
||||||
#api get version dsl of canvas
|
#api get version dsl of canvas
|
||||||
@manager.route('/getversion/<version_id>', methods=['GET']) # noqa: F821
|
@manager.route('/getversion/<version_id>', methods=['GET']) # noqa: F821
|
||||||
@login_required
|
@login_required
|
||||||
@ -313,3 +311,42 @@ def getversion( version_id):
|
|||||||
return get_json_result(data=version.to_dict())
|
return get_json_result(data=version.to_dict())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return get_json_result(data=f"Error getting history file: {e}")
|
return get_json_result(data=f"Error getting history file: {e}")
|
||||||
|
@manager.route('/listteam', methods=['GET']) # noqa: F821
|
||||||
|
@login_required
|
||||||
|
def list_kbs():
|
||||||
|
keywords = request.args.get("keywords", "")
|
||||||
|
page_number = int(request.args.get("page", 1))
|
||||||
|
items_per_page = int(request.args.get("page_size", 150))
|
||||||
|
orderby = request.args.get("orderby", "create_time")
|
||||||
|
desc = request.args.get("desc", True)
|
||||||
|
try:
|
||||||
|
tenants = TenantService.get_joined_tenants_by_user_id(current_user.id)
|
||||||
|
kbs, total = UserCanvasService.get_by_tenant_ids(
|
||||||
|
[m["tenant_id"] for m in tenants], current_user.id, page_number,
|
||||||
|
items_per_page, orderby, desc, keywords)
|
||||||
|
return get_json_result(data={"kbs": kbs, "total": total})
|
||||||
|
except Exception as e:
|
||||||
|
return server_error_response(e)
|
||||||
|
@manager.route('/setting', methods=['POST']) # noqa: F821
|
||||||
|
@validate_request("id", "title", "permission")
|
||||||
|
@login_required
|
||||||
|
def setting():
|
||||||
|
req = request.json
|
||||||
|
req["user_id"] = current_user.id
|
||||||
|
e,flow = UserCanvasService.get_by_id(req["id"])
|
||||||
|
if not e:
|
||||||
|
return get_data_error_result(message="canvas not found.")
|
||||||
|
flow = flow.to_dict()
|
||||||
|
flow["title"] = req["title"]
|
||||||
|
if req["description"]:
|
||||||
|
flow["description"] = req["description"]
|
||||||
|
if req["permission"]:
|
||||||
|
flow["permission"] = req["permission"]
|
||||||
|
if req["avatar"]:
|
||||||
|
flow["avatar"] = req["avatar"]
|
||||||
|
if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
|
||||||
|
return get_json_result(
|
||||||
|
data=False, message='Only owner of canvas authorized for this operation.',
|
||||||
|
code=RetCode.OPERATING_ERROR)
|
||||||
|
num= UserCanvasService.update_by_id(req["id"], flow)
|
||||||
|
return get_json_result(data=num)
|
||||||
|
@ -967,11 +967,17 @@ class UserCanvas(DataBaseModel):
|
|||||||
avatar = TextField(null=True, help_text="avatar base64 string")
|
avatar = TextField(null=True, help_text="avatar base64 string")
|
||||||
user_id = CharField(max_length=255, null=False, help_text="user_id", index=True)
|
user_id = CharField(max_length=255, null=False, help_text="user_id", index=True)
|
||||||
title = CharField(max_length=255, null=True, help_text="Canvas title")
|
title = CharField(max_length=255, null=True, help_text="Canvas title")
|
||||||
|
|
||||||
|
permission = CharField(
|
||||||
|
max_length=16,
|
||||||
|
null=False,
|
||||||
|
help_text="me|team",
|
||||||
|
default="me",
|
||||||
|
index=True)
|
||||||
description = TextField(null=True, help_text="Canvas description")
|
description = TextField(null=True, help_text="Canvas description")
|
||||||
canvas_type = CharField(max_length=32, null=True, help_text="Canvas type", index=True)
|
canvas_type = CharField(max_length=32, null=True, help_text="Canvas type", index=True)
|
||||||
dsl = JSONField(null=True, default={})
|
dsl = JSONField(null=True, default={})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "user_canvas"
|
db_table = "user_canvas"
|
||||||
|
|
||||||
@ -1140,3 +1146,11 @@ def migrate_db():
|
|||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
migrate(
|
||||||
|
migrator.add_column("user_canvas", "permission",
|
||||||
|
CharField(max_length=16, null=False, help_text="me|team", default="me", index=True))
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@ -18,12 +18,13 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from agent.canvas import Canvas
|
from agent.canvas import Canvas
|
||||||
from api.db.db_models import DB, CanvasTemplate, UserCanvas, API4Conversation
|
from api.db import TenantPermission
|
||||||
|
from api.db.db_models import DB, CanvasTemplate, User, UserCanvas, API4Conversation
|
||||||
from api.db.services.api_service import API4ConversationService
|
from api.db.services.api_service import API4ConversationService
|
||||||
from api.db.services.common_service import CommonService
|
from api.db.services.common_service import CommonService
|
||||||
from api.db.services.conversation_service import structure_answer
|
from api.db.services.conversation_service import structure_answer
|
||||||
from api.utils import get_uuid
|
from api.utils import get_uuid
|
||||||
|
from peewee import fn
|
||||||
|
|
||||||
class CanvasTemplateService(CommonService):
|
class CanvasTemplateService(CommonService):
|
||||||
model = CanvasTemplate
|
model = CanvasTemplate
|
||||||
@ -50,7 +51,74 @@ class UserCanvasService(CommonService):
|
|||||||
agents = agents.paginate(page_number, items_per_page)
|
agents = agents.paginate(page_number, items_per_page)
|
||||||
|
|
||||||
return list(agents.dicts())
|
return list(agents.dicts())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@DB.connection_context()
|
||||||
|
def get_by_tenant_id(cls, pid):
|
||||||
|
try:
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
cls.model.id,
|
||||||
|
cls.model.avatar,
|
||||||
|
cls.model.title,
|
||||||
|
cls.model.dsl,
|
||||||
|
cls.model.description,
|
||||||
|
cls.model.permission,
|
||||||
|
cls.model.update_time,
|
||||||
|
cls.model.user_id,
|
||||||
|
cls.model.create_time,
|
||||||
|
cls.model.create_date,
|
||||||
|
cls.model.update_date,
|
||||||
|
User.nickname,
|
||||||
|
User.avatar.alias('tenant_avatar'),
|
||||||
|
]
|
||||||
|
angents = cls.model.select(*fields) \
|
||||||
|
.join(User, on=(cls.model.user_id == User.id)) \
|
||||||
|
.where(cls.model.id == pid)
|
||||||
|
# obj = cls.model.query(id=pid)[0]
|
||||||
|
return True, angents.dicts()[0]
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@DB.connection_context()
|
||||||
|
def get_by_tenant_ids(cls, joined_tenant_ids, user_id,
|
||||||
|
page_number, items_per_page,
|
||||||
|
orderby, desc, keywords,
|
||||||
|
):
|
||||||
|
fields = [
|
||||||
|
cls.model.id,
|
||||||
|
cls.model.avatar,
|
||||||
|
cls.model.title,
|
||||||
|
cls.model.dsl,
|
||||||
|
cls.model.description,
|
||||||
|
cls.model.permission,
|
||||||
|
User.nickname,
|
||||||
|
User.avatar.alias('tenant_avatar'),
|
||||||
|
cls.model.update_time
|
||||||
|
]
|
||||||
|
if keywords:
|
||||||
|
angents = cls.model.select(*fields).join(User, on=(cls.model.user_id == User.id)).where(
|
||||||
|
((cls.model.user_id.in_(joined_tenant_ids) & (cls.model.permission ==
|
||||||
|
TenantPermission.TEAM.value)) | (
|
||||||
|
cls.model.user_id == user_id)),
|
||||||
|
(fn.LOWER(cls.model.title).contains(keywords.lower()))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
angents = cls.model.select(*fields).join(User, on=(cls.model.user_id == User.id)).where(
|
||||||
|
((cls.model.user_id.in_(joined_tenant_ids) & (cls.model.permission ==
|
||||||
|
TenantPermission.TEAM.value)) | (
|
||||||
|
cls.model.user_id == user_id))
|
||||||
|
)
|
||||||
|
if desc:
|
||||||
|
angents = angents.order_by(cls.model.getter_by(orderby).desc())
|
||||||
|
else:
|
||||||
|
angents = angents.order_by(cls.model.getter_by(orderby).asc())
|
||||||
|
count = angents.count()
|
||||||
|
angents = angents.paginate(page_number, items_per_page)
|
||||||
|
return list(angents.dicts()), count
|
||||||
|
|
||||||
|
|
||||||
def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
|
def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
|
||||||
e, cvs = UserCanvasService.get_by_id(agent_id)
|
e, cvs = UserCanvasService.get_by_id(agent_id)
|
||||||
|
@ -171,6 +171,27 @@ export const useFetchFlow = (): {
|
|||||||
return { data, loading, refetch };
|
return { data, loading, refetch };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSettingFlow = () => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['SettingFlow'],
|
||||||
|
mutationFn: async (params: any) => {
|
||||||
|
const ret = await flowService.settingCanvas(params);
|
||||||
|
if (ret?.data?.code === 0) {
|
||||||
|
message.success('success');
|
||||||
|
} else {
|
||||||
|
message.error(ret?.data?.data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, settingFlow: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
export const useFetchFlowSSE = (): {
|
export const useFetchFlowSSE = (): {
|
||||||
data: IFlow;
|
data: IFlow;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -244,7 +265,9 @@ export const useDeleteFlow = () => {
|
|||||||
mutationFn: async (canvasIds: string[]) => {
|
mutationFn: async (canvasIds: string[]) => {
|
||||||
const { data } = await flowService.removeCanvas({ canvasIds });
|
const { data } = await flowService.removeCanvas({ canvasIds });
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ['infiniteFetchFlowListTeam'],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return data?.data ?? [];
|
return data?.data ?? [];
|
||||||
},
|
},
|
||||||
|
@ -37,6 +37,8 @@ export declare interface IFlow {
|
|||||||
update_date: string;
|
update_date: string;
|
||||||
update_time: number;
|
update_time: number;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
|
permission: string;
|
||||||
|
nickname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFlowTemplate {
|
export interface IFlowTemplate {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1192,6 +1192,15 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
addCategory: 'Add category',
|
addCategory: 'Add category',
|
||||||
categoryName: 'Category name',
|
categoryName: 'Category name',
|
||||||
nextStep: 'Next step',
|
nextStep: 'Next step',
|
||||||
|
variableExtractDescription:
|
||||||
|
'Extract user information into global variable throughout the conversation',
|
||||||
|
variableExtract: 'Variables',
|
||||||
|
variables: 'Variables',
|
||||||
|
variablesTip: `Set the clear json key variable with a value of empty. e.g.
|
||||||
|
{
|
||||||
|
"UserCode":"",
|
||||||
|
"NumberPhone":""
|
||||||
|
}`,
|
||||||
datatype: 'MINE type of the HTTP request',
|
datatype: 'MINE type of the HTTP request',
|
||||||
insertVariableTip: `Enter / Insert variables`,
|
insertVariableTip: `Enter / Insert variables`,
|
||||||
historyversion: 'History version',
|
historyversion: 'History version',
|
||||||
@ -1204,14 +1213,25 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
version: 'Version',
|
version: 'Version',
|
||||||
select: 'No version selected',
|
select: 'No version selected',
|
||||||
},
|
},
|
||||||
},
|
setting: 'Setting',
|
||||||
footer: {
|
settings: {
|
||||||
profile: 'All rights reserved @ React',
|
upload: 'Upload',
|
||||||
},
|
photo: 'Photo',
|
||||||
layout: {
|
permissions: 'Permission',
|
||||||
file: 'file',
|
permissionsTip: 'You can set the permissions of the team members here.',
|
||||||
knowledge: 'knowledge',
|
me: 'me',
|
||||||
chat: 'chat',
|
team: 'Team',
|
||||||
|
},
|
||||||
|
noMoreData: 'No more data',
|
||||||
|
searchAgentPlaceholder: 'Search agent',
|
||||||
|
footer: {
|
||||||
|
profile: 'All rights reserved @ React',
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
file: 'file',
|
||||||
|
knowledge: 'knowledge',
|
||||||
|
chat: 'chat',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -722,7 +722,8 @@ export default {
|
|||||||
s3: 'S3 上傳',
|
s3: 'S3 上傳',
|
||||||
preview: '預覽',
|
preview: '預覽',
|
||||||
fileError: '文件錯誤',
|
fileError: '文件錯誤',
|
||||||
uploadLimit: '本地部署的單次上傳檔案總大小上限為 1GB,單次批量上傳檔案數不超過 32,單個帳戶不限檔案數量。',
|
uploadLimit:
|
||||||
|
'本地部署的單次上傳檔案總大小上限為 1GB,單次批量上傳檔案數不超過 32,單個帳戶不限檔案數量。',
|
||||||
destinationFolder: '目標資料夾',
|
destinationFolder: '目標資料夾',
|
||||||
},
|
},
|
||||||
flow: {
|
flow: {
|
||||||
|
0
web/src/pages/flow/flow-setting/index.less
Normal file
0
web/src/pages/flow/flow-setting/index.less
Normal file
121
web/src/pages/flow/flow-setting/index.tsx
Normal file
121
web/src/pages/flow/flow-setting/index.tsx
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { useFetchFlow, useSettingFlow } from '@/hooks/flow-hooks';
|
||||||
|
import { normFile } from '@/utils/file-util';
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Form, Input, Modal, Radio, Upload } from 'antd';
|
||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
export function useFlowSettingModal() {
|
||||||
|
const [visibleSettingModal, setVisibleSettingMModal] = React.useState(false);
|
||||||
|
|
||||||
|
return {
|
||||||
|
visibleSettingModal,
|
||||||
|
setVisibleSettingMModal,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlowSettingModalProps = {
|
||||||
|
visible: boolean;
|
||||||
|
hideModal: () => void;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export const FlowSettingModal = ({
|
||||||
|
hideModal,
|
||||||
|
visible,
|
||||||
|
id,
|
||||||
|
}: FlowSettingModalProps) => {
|
||||||
|
const { data, refetch } = useFetchFlow();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { t } = useTranslate('flow.settings');
|
||||||
|
const { loading, settingFlow } = useSettingFlow();
|
||||||
|
// Initialize form with data when it becomes available
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
permission: data.permission,
|
||||||
|
avatar: data.avatar ? [{ thumbUrl: data.avatar }] : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data, form]);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(async () => {
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
const { avatar, ...others } = await form.validateFields();
|
||||||
|
const param = {
|
||||||
|
...others,
|
||||||
|
id,
|
||||||
|
avatar: avatar && avatar.length > 0 ? avatar[0].thumbUrl : '',
|
||||||
|
};
|
||||||
|
settingFlow(param);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Validation failed:', error);
|
||||||
|
}
|
||||||
|
}, [form, id, settingFlow]);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!loading && refetch && visible) {
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
}, [loading, refetch, visible]);
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
confirmLoading={loading}
|
||||||
|
title={'Agent Setting'}
|
||||||
|
open={visible}
|
||||||
|
onCancel={hideModal}
|
||||||
|
onOk={handleSubmit}
|
||||||
|
okText={t('save', { keyPrefix: 'common' })}
|
||||||
|
cancelText={t('cancel', { keyPrefix: 'common' })}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
labelCol={{ span: 6 }}
|
||||||
|
wrapperCol={{ span: 18 }}
|
||||||
|
layout="horizontal"
|
||||||
|
style={{ maxWidth: 600 }}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="title"
|
||||||
|
label="Title"
|
||||||
|
rules={[{ required: true, message: 'Please input a title!' }]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="avatar"
|
||||||
|
label={t('photo')}
|
||||||
|
valuePropName="fileList"
|
||||||
|
getValueFromEvent={normFile}
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
listType="picture-card"
|
||||||
|
maxCount={1}
|
||||||
|
beforeUpload={() => false}
|
||||||
|
showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }}
|
||||||
|
>
|
||||||
|
<button style={{ border: 0, background: 'none' }} type="button">
|
||||||
|
<PlusOutlined />
|
||||||
|
<div style={{ marginTop: 8 }}>{t('upload')}</div>
|
||||||
|
</button>
|
||||||
|
</Upload>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="description" label="Description">
|
||||||
|
<Input.TextArea rows={4} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="permission"
|
||||||
|
label={t('permissions')}
|
||||||
|
tooltip={t('permissionsTip')}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="me">{t('me')}</Radio>
|
||||||
|
<Radio value="team">{t('team')}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
@ -1,3 +1,10 @@
|
|||||||
.flowHeader {
|
.flowHeader {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
.hideRibbon {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon {
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
@ -3,10 +3,13 @@ import { useShowEmbedModal } from '@/components/api-service/hooks';
|
|||||||
import { SharedFrom } from '@/constants/chat';
|
import { SharedFrom } from '@/constants/chat';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useFetchFlow } from '@/hooks/flow-hooks';
|
import { useFetchFlow } from '@/hooks/flow-hooks';
|
||||||
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Space } from 'antd';
|
import { Badge, Button, Flex, Space } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Link, useParams } from 'umi';
|
import { Link, useParams } from 'umi';
|
||||||
|
import { FlowSettingModal, useFlowSettingModal } from '../flow-setting';
|
||||||
import {
|
import {
|
||||||
useGetBeginNodeDataQuery,
|
useGetBeginNodeDataQuery,
|
||||||
useGetBeginNodeDataQueryIsSafe,
|
useGetBeginNodeDataQueryIsSafe,
|
||||||
@ -32,6 +35,8 @@ interface IProps {
|
|||||||
const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
||||||
const { saveGraph } = useSaveGraph();
|
const { saveGraph } = useSaveGraph();
|
||||||
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
|
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
|
||||||
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
|
|
||||||
const { data } = useFetchFlow();
|
const { data } = useFetchFlow();
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
@ -39,6 +44,8 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
|||||||
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
||||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||||
useShowEmbedModal();
|
useShowEmbedModal();
|
||||||
|
const { setVisibleSettingMModal, visibleSettingModal } =
|
||||||
|
useFlowSettingModal();
|
||||||
const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe();
|
const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe();
|
||||||
const { setVisibleHistoryVersionModal, visibleHistoryVersionModal } =
|
const { setVisibleHistoryVersionModal, visibleHistoryVersionModal } =
|
||||||
useHistoryVersionModal();
|
useHistoryVersionModal();
|
||||||
@ -55,6 +62,10 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
|||||||
}
|
}
|
||||||
}, [getBeginNodeDataQuery, handleRun, showChatDrawer]);
|
}, [getBeginNodeDataQuery, handleRun, showChatDrawer]);
|
||||||
|
|
||||||
|
const showSetting = useCallback(() => {
|
||||||
|
setVisibleSettingMModal(true);
|
||||||
|
}, [setVisibleSettingMModal]);
|
||||||
|
|
||||||
const showListVersion = useCallback(() => {
|
const showListVersion = useCallback(() => {
|
||||||
setVisibleHistoryVersionModal(true);
|
setVisibleHistoryVersionModal(true);
|
||||||
}, [setVisibleHistoryVersionModal]);
|
}, [setVisibleHistoryVersionModal]);
|
||||||
@ -66,31 +77,56 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
|||||||
gap={'large'}
|
gap={'large'}
|
||||||
className={styles.flowHeader}
|
className={styles.flowHeader}
|
||||||
>
|
>
|
||||||
|
<Badge.Ribbon
|
||||||
|
text={data?.nickname}
|
||||||
|
style={{ marginRight: -data?.nickname?.length * 5 }}
|
||||||
|
color={userInfo?.nickname === data?.nickname ? '#1677ff' : 'pink'}
|
||||||
|
className={classNames(styles.ribbon, {
|
||||||
|
[styles.hideRibbon]: data.permission !== 'team',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Space className={styles.headerTitle} size={'large'}>
|
||||||
|
<Link to={`/flow`}>
|
||||||
|
<ArrowLeftOutlined />
|
||||||
|
</Link>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold text-[18px]">{data.title}</span>
|
||||||
|
<span className="font-normal text-sm">
|
||||||
|
{t('autosaved')} {time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</Badge.Ribbon>
|
||||||
<Space size={'large'}>
|
<Space size={'large'}>
|
||||||
<Link to={`/flow`}>
|
<Button
|
||||||
<ArrowLeftOutlined />
|
disabled={userInfo.nickname !== data.nickname}
|
||||||
</Link>
|
onClick={handleRunAgent}
|
||||||
<div className="flex flex-col">
|
>
|
||||||
<span className="font-semibold text-[18px]">{data.title}</span>
|
|
||||||
<span className="font-normal text-sm">
|
|
||||||
{t('autosaved')} {time}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Space>
|
|
||||||
<Space size={'large'}>
|
|
||||||
<Button onClick={handleRunAgent}>
|
|
||||||
<b>{t('run')}</b>
|
<b>{t('run')}</b>
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={() => saveGraph()}>
|
<Button
|
||||||
|
disabled={userInfo.nickname !== data.nickname}
|
||||||
|
type="primary"
|
||||||
|
onClick={() => saveGraph()}
|
||||||
|
>
|
||||||
<b>{t('save')}</b>
|
<b>{t('save')}</b>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={handleShowEmbedModal}
|
onClick={handleShowEmbedModal}
|
||||||
disabled={!isBeginNodeDataQuerySafe}
|
disabled={
|
||||||
|
!isBeginNodeDataQuerySafe || userInfo.nickname !== data.nickname
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<b>{t('embedIntoSite', { keyPrefix: 'common' })}</b>
|
<b>{t('embedIntoSite', { keyPrefix: 'common' })}</b>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={userInfo.nickname !== data.nickname}
|
||||||
|
type="primary"
|
||||||
|
onClick={showSetting}
|
||||||
|
>
|
||||||
|
<b>{t('setting')}</b>
|
||||||
|
</Button>
|
||||||
<Button type="primary" onClick={showListVersion}>
|
<Button type="primary" onClick={showListVersion}>
|
||||||
<b>{t('historyversion')}</b>
|
<b>{t('historyversion')}</b>
|
||||||
</Button>
|
</Button>
|
||||||
@ -106,6 +142,13 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
|
|||||||
isAgent
|
isAgent
|
||||||
></EmbedModal>
|
></EmbedModal>
|
||||||
)}
|
)}
|
||||||
|
{visibleSettingModal && (
|
||||||
|
<FlowSettingModal
|
||||||
|
id={id || ''}
|
||||||
|
visible={visibleSettingModal}
|
||||||
|
hideModal={() => setVisibleSettingMModal(false)}
|
||||||
|
></FlowSettingModal>
|
||||||
|
)}
|
||||||
{visibleHistoryVersionModal && (
|
{visibleHistoryVersionModal && (
|
||||||
<HistoryVersionModal
|
<HistoryVersionModal
|
||||||
id={id || ''}
|
id={id || ''}
|
||||||
|
@ -74,3 +74,11 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hideRibbon {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ribbon {
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import { formatDate } from '@/utils/date';
|
import { formatDate } from '@/utils/date';
|
||||||
import { CalendarOutlined } from '@ant-design/icons';
|
import { CalendarOutlined } from '@ant-design/icons';
|
||||||
import { Card, Typography } from 'antd';
|
import { Badge, Card, Typography } from 'antd';
|
||||||
import { useNavigate } from 'umi';
|
import { useNavigate } from 'umi';
|
||||||
|
|
||||||
import OperateDropdown from '@/components/operate-dropdown';
|
import OperateDropdown from '@/components/operate-dropdown';
|
||||||
import { useDeleteFlow } from '@/hooks/flow-hooks';
|
import { useDeleteFlow } from '@/hooks/flow-hooks';
|
||||||
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { IFlow } from '@/interfaces/database/flow';
|
import { IFlow } from '@/interfaces/database/flow';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import GraphAvatar from '../graph-avatar';
|
import GraphAvatar from '../graph-avatar';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
item: IFlow;
|
item: IFlow;
|
||||||
|
onDelete?: (string: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowCard = ({ item }: IProps) => {
|
const FlowCard = ({ item }: IProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { deleteFlow } = useDeleteFlow();
|
const { deleteFlow } = useDeleteFlow();
|
||||||
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
|
|
||||||
const removeFlow = useCallback(() => {
|
const removeFlow = useCallback(() => {
|
||||||
return deleteFlow([item.id]);
|
return deleteFlow([item.id]);
|
||||||
@ -27,33 +31,41 @@ const FlowCard = ({ item }: IProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={styles.card} onClick={handleCardClick}>
|
<Badge.Ribbon
|
||||||
<div className={styles.container}>
|
text={item?.nickname}
|
||||||
<div className={styles.content}>
|
color={userInfo?.nickname === item?.nickname ? '#1677ff' : 'pink'}
|
||||||
<GraphAvatar avatar={item.avatar}></GraphAvatar>
|
className={classNames(styles.ribbon, {
|
||||||
<OperateDropdown deleteItem={removeFlow}></OperateDropdown>
|
[styles.hideRibbon]: item.permission !== 'team',
|
||||||
</div>
|
})}
|
||||||
<div className={styles.titleWrapper}>
|
>
|
||||||
<Typography.Title
|
<Card className={styles.card} onClick={handleCardClick}>
|
||||||
className={styles.title}
|
<div className={styles.container}>
|
||||||
ellipsis={{ tooltip: item.title }}
|
<div className={styles.content}>
|
||||||
>
|
<GraphAvatar avatar={item.avatar}></GraphAvatar>
|
||||||
{item.title}
|
<OperateDropdown deleteItem={removeFlow}></OperateDropdown>
|
||||||
</Typography.Title>
|
</div>
|
||||||
<p>{item.description}</p>
|
<div className={styles.titleWrapper}>
|
||||||
</div>
|
<Typography.Title
|
||||||
<div className={styles.footer}>
|
className={styles.title}
|
||||||
<div className={styles.bottom}>
|
ellipsis={{ tooltip: item.title }}
|
||||||
<div className={styles.bottomLeft}>
|
>
|
||||||
<CalendarOutlined className={styles.leftIcon} />
|
{item.title}
|
||||||
<span className={styles.rightText}>
|
</Typography.Title>
|
||||||
{formatDate(item.update_time)}
|
<p>{item.description}</p>
|
||||||
</span>
|
</div>
|
||||||
|
<div className={styles.footer}>
|
||||||
|
<div className={styles.bottom}>
|
||||||
|
<div className={styles.bottomLeft}>
|
||||||
|
<CalendarOutlined className={styles.leftIcon} />
|
||||||
|
<span className={styles.rightText}>
|
||||||
|
{formatDate(item.update_time)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
</Card>
|
</Badge.Ribbon>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,16 +1,56 @@
|
|||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
import {
|
import { useFetchFlowTemplates, useSetFlow } from '@/hooks/flow-hooks';
|
||||||
useFetchFlowList,
|
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
||||||
useFetchFlowTemplates,
|
import flowService from '@/services/flow-service';
|
||||||
useSetFlow,
|
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||||
} from '@/hooks/flow-hooks';
|
import { useDebounce } from 'ahooks';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useNavigate } from 'umi';
|
import { useNavigate } from 'umi';
|
||||||
|
|
||||||
export const useFetchDataOnMount = () => {
|
export const useFetchDataOnMount = () => {
|
||||||
const { data, loading } = useFetchFlowList();
|
const { searchString, handleInputChange } = useHandleSearchChange();
|
||||||
|
const debouncedSearchString = useDebounce(searchString, { wait: 500 });
|
||||||
|
|
||||||
return { list: data, loading };
|
const PageSize = 30;
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
fetchNextPage,
|
||||||
|
hasNextPage,
|
||||||
|
isFetching,
|
||||||
|
isFetchingNextPage,
|
||||||
|
status,
|
||||||
|
} = useInfiniteQuery({
|
||||||
|
queryKey: ['infiniteFetchFlowListTeam', debouncedSearchString],
|
||||||
|
queryFn: async ({ pageParam }) => {
|
||||||
|
const { data } = await flowService.listCanvasTeam({
|
||||||
|
page: pageParam,
|
||||||
|
page_size: PageSize,
|
||||||
|
keywords: debouncedSearchString,
|
||||||
|
});
|
||||||
|
const list = data?.data ?? [];
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
initialPageParam: 1,
|
||||||
|
getNextPageParam: (lastPage, pages, lastPageParam) => {
|
||||||
|
if (lastPageParam * PageSize <= lastPage.total) {
|
||||||
|
return lastPageParam + 1;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading: isFetching,
|
||||||
|
error,
|
||||||
|
fetchNextPage,
|
||||||
|
hasNextPage,
|
||||||
|
isFetching,
|
||||||
|
isFetchingNextPage,
|
||||||
|
status,
|
||||||
|
handleInputChange,
|
||||||
|
searchString,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSaveFlow = () => {
|
export const useSaveFlow = () => {
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
|
||||||
import { Button, Empty, Flex, Spin } from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Empty,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
Skeleton,
|
||||||
|
Space,
|
||||||
|
Spin,
|
||||||
|
} from 'antd';
|
||||||
import AgentTemplateModal from './agent-template-modal';
|
import AgentTemplateModal from './agent-template-modal';
|
||||||
import FlowCard from './flow-card';
|
import FlowCard from './flow-card';
|
||||||
import { useFetchDataOnMount, useSaveFlow } from './hooks';
|
import { useFetchDataOnMount, useSaveFlow } from './hooks';
|
||||||
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import InfiniteScroll from 'react-infinite-scroll-component';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const FlowList = () => {
|
const FlowList = () => {
|
||||||
@ -17,29 +28,66 @@ const FlowList = () => {
|
|||||||
} = useSaveFlow();
|
} = useSaveFlow();
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
|
|
||||||
const { list, loading } = useFetchDataOnMount();
|
const {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
searchString,
|
||||||
|
handleInputChange,
|
||||||
|
fetchNextPage,
|
||||||
|
hasNextPage,
|
||||||
|
} = useFetchDataOnMount();
|
||||||
|
|
||||||
|
const nextList = useMemo(() => {
|
||||||
|
const list =
|
||||||
|
data?.pages?.flatMap((x) => (Array.isArray(x.kbs) ? x.kbs : [])) ?? [];
|
||||||
|
return list;
|
||||||
|
}, [data?.pages]);
|
||||||
|
|
||||||
|
const total = useMemo(() => {
|
||||||
|
return data?.pages.at(-1).total ?? 0;
|
||||||
|
}, [data?.pages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex className={styles.flowListWrapper} vertical flex={1} gap={'large'}>
|
<Flex className={styles.flowListWrapper} vertical flex={1} gap={'large'}>
|
||||||
<Flex justify={'end'}>
|
<Flex justify={'end'}>
|
||||||
<Button
|
<Space size={'large'}>
|
||||||
type="primary"
|
<Input
|
||||||
icon={<PlusOutlined />}
|
placeholder={t('searchAgentPlaceholder')}
|
||||||
onClick={showFlowSettingModal}
|
value={searchString}
|
||||||
>
|
style={{ width: 220 }}
|
||||||
{t('createGraph')}
|
allowClear
|
||||||
</Button>
|
onChange={handleInputChange}
|
||||||
|
prefix={<SearchOutlined />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={showFlowSettingModal}
|
||||||
|
>
|
||||||
|
{t('createGraph')}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Flex gap={'large'} wrap="wrap" className={styles.flowCardContainer}>
|
<InfiniteScroll
|
||||||
{list.length > 0 ? (
|
dataLength={nextList?.length ?? 0}
|
||||||
list.map((item) => {
|
next={fetchNextPage}
|
||||||
return <FlowCard item={item} key={item.id}></FlowCard>;
|
hasMore={hasNextPage}
|
||||||
})
|
loader={<Skeleton avatar paragraph={{ rows: 1 }} active />}
|
||||||
) : (
|
endMessage={!!total && <Divider plain>{t('noMoreData')} 🤐</Divider>}
|
||||||
<Empty className={styles.knowledgeEmpty}></Empty>
|
scrollableTarget="scrollableDiv"
|
||||||
)}
|
>
|
||||||
</Flex>
|
<Flex gap={'large'} wrap="wrap" className={styles.flowCardContainer}>
|
||||||
|
{nextList.length > 0 ? (
|
||||||
|
nextList.map((item) => {
|
||||||
|
return <FlowCard item={item} key={item.id}></FlowCard>;
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Empty className={styles.knowledgeEmpty}></Empty>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</InfiniteScroll>
|
||||||
</Spin>
|
</Spin>
|
||||||
{flowSettingVisible && (
|
{flowSettingVisible && (
|
||||||
<AgentTemplateModal
|
<AgentTemplateModal
|
||||||
|
@ -16,6 +16,8 @@ const {
|
|||||||
testDbConnect,
|
testDbConnect,
|
||||||
getInputElements,
|
getInputElements,
|
||||||
debug,
|
debug,
|
||||||
|
listCanvasTeam,
|
||||||
|
settingCanvas,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -71,6 +73,14 @@ const methods = {
|
|||||||
url: debug,
|
url: debug,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
},
|
},
|
||||||
|
listCanvasTeam: {
|
||||||
|
url: listCanvasTeam,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
settingCanvas: {
|
||||||
|
url: settingCanvas,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const flowService = registerServer<keyof typeof methods>(methods, request);
|
const flowService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
@ -123,10 +123,12 @@ export default {
|
|||||||
// flow
|
// flow
|
||||||
listTemplates: `${api_host}/canvas/templates`,
|
listTemplates: `${api_host}/canvas/templates`,
|
||||||
listCanvas: `${api_host}/canvas/list`,
|
listCanvas: `${api_host}/canvas/list`,
|
||||||
|
listCanvasTeam: `${api_host}/canvas/listteam`,
|
||||||
getCanvas: `${api_host}/canvas/get`,
|
getCanvas: `${api_host}/canvas/get`,
|
||||||
getCanvasSSE: `${api_host}/canvas/getsse`,
|
getCanvasSSE: `${api_host}/canvas/getsse`,
|
||||||
removeCanvas: `${api_host}/canvas/rm`,
|
removeCanvas: `${api_host}/canvas/rm`,
|
||||||
setCanvas: `${api_host}/canvas/set`,
|
setCanvas: `${api_host}/canvas/set`,
|
||||||
|
settingCanvas: `${api_host}/canvas/setting`,
|
||||||
getListVersion: `${api_host}/canvas/getlistversion`,
|
getListVersion: `${api_host}/canvas/getlistversion`,
|
||||||
getVersion: `${api_host}/canvas/getversion`,
|
getVersion: `${api_host}/canvas/getversion`,
|
||||||
resetCanvas: `${api_host}/canvas/reset`,
|
resetCanvas: `${api_host}/canvas/reset`,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user