Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add agent page #14

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/app/agent/components/TagContent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use client';

import React, { useRef, useState, useEffect,useCallback } from 'react';
import { Radio } from 'antd'
import { LeftOutlined, RightOutlined } from '@ant-design/icons'
import { useStyles } from './styles';
import classNames from 'classnames';

interface TagContentProps {
list?: Array<Record<string, any>>;
handleChange?: () => void
}

const TagContent = React.memo<TagContentProps>(props => {
const { list } = props;
const { styles } = useStyles();
const scrollLeftRef = useRef(0)
const currentBtnRef = useRef(null)
const [leftArrowVisible, setLeftArrowVisible] = useState(false)
const [rightArrowVisible, setRightArrowVisible] = useState(false)

const handleScroll = useCallback((direction: string)=>{
const scrollAmount = window.innerWidth - 39; // You can adjust the scroll amount as needed
const buttonList = document.querySelector('#btns');
if (direction === 'left') {
buttonList!.scrollLeft -= scrollAmount;
} else if (direction === 'right') {
buttonList!.scrollLeft += scrollAmount;
}
},[]);

const onChange = (tag) => {
currentBtnRef.current = tag;
// 获取事件源(按钮元素)
const button = document.querySelector(`[id='${tag?.target?.value}']`);
const scrollContainer = document.querySelector('#btns');

if (button && scrollContainer) {
// 获取按钮的位置信息
const rect = button.getBoundingClientRect();
const targetLeft = (rect.left+scrollContainer?.scrollLeft) - (scrollContainer.offsetWidth - rect.width) / 2;
// 滚动到目标位置
scrollContainer.scrollTo({
left: targetLeft,
behavior: 'smooth'
});
}
}

useEffect(() => {
// 获取包含内容的元素,例如 body 或任何带有横向滚动的容器
const scrollContainer = document.querySelector('#btns');
// 添加滚动事件监听器
scrollContainer?.addEventListener('scroll', function () {
// 获取横向滚动的距离
const scrollLeft = scrollContainer?.scrollLeft;
scrollLeftRef.current = scrollLeft;
const visibleWidth = document.documentElement.clientWidth;
// 执行您的滚动事件处理逻辑
if (scrollLeft + visibleWidth >= scrollContainer.scrollWidth) {
setRightArrowVisible(false)
} else {
setRightArrowVisible(true)
if (scrollLeft <= 0) {
setLeftArrowVisible(false)
} else {
setLeftArrowVisible(true)
}
}
});

window.addEventListener('resize',()=>{
const scrollLeft = scrollContainer!.scrollLeft;
if(scrollLeft > 0) {
setLeftArrowVisible(true)

}else {
setLeftArrowVisible(false)
}
// 如果可视页面比容器小,说明需要出滚动条了,那么选中的tag也需要滚动到页面中间
const visibleWidth = document.documentElement.clientWidth;
if(visibleWidth > scrollContainer!.scrollWidth) {
setLeftArrowVisible(false)
setRightArrowVisible(false)
}else {
setRightArrowVisible(true)
onChange(currentBtnRef.current)
}
})
}, [])



return (
<div className={styles.tagContent}>
<div className={styles.arrows}>
<div className={classNames(styles.shadowLeft, (!leftArrowVisible) && styles.hidden)}></div>
<div className={classNames(styles.arrow, styles.left, (!leftArrowVisible) && styles.hidden)}>
<LeftOutlined onClick={() => handleScroll('left')} />
</div>
<div className={classNames(styles.shadowRight, (!rightArrowVisible) && styles.hidden)}></div>
<div className={classNames(styles.arrow, styles.right, (!rightArrowVisible) && styles.hidden)}>
<RightOutlined onClick={() => handleScroll('right')} />
</div>
</div>
<Radio.Group buttonStyle="solid" className={styles.btnList} id='btns' onChange={onChange}>
{
list?.map(item => <Radio.Button className={styles.btn} id={item.tag_id} key={item.tag_id} value={item.tag_id}>{item.tag_name}</Radio.Button>)
}
</Radio.Group>
</div>
);
});

export default TagContent;
68 changes: 68 additions & 0 deletions src/app/agent/components/TagContent/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { createStyles } from 'antd-style';

export const useStyles = createStyles(({ token }) => ({
tagContent: {
margin: `0 0 20px 0`,
width: `100%`,
},
btnList: {
display: "flex",
overflow: "auto"
},
arrows: {},
arrow: {
position: 'absolute',
alignItems: 'center',
cursor: 'pointer',
display: 'flex',
height: '30px',
justifyContent: 'flex-end',
zIndex: "3",
width: '30px',
'&:hover': {
color: token.colorPrimary,
cursor: 'pointer'
}
},
hidden:{
display:'none'
},
shadowLeft:{
position: 'absolute',
background:`linear-gradient(to left,transparent,#fff 100%)`,
height: "36px",
pointerEvents: "none",
width: "52px",
left:'0',
zIndex: "3",
},
shadowRight:{
position: 'absolute',
background:'linear-gradient(to right,transparent,#fff 100%)',
height: "36px",
pointerEvents: "none",
width: "52px",
right: '10px',
zIndex: "3",
},
left: {
left:'0',
},
right: {
right: '10px'
},
btn: {
whiteSpace: "nowrap",
'&.ant-radio-button-wrapper': {
borderInlineStart: `1px solid #d9d9d9`,
marginRight: 10,
borderRadius: '12px',
},
"&.ant-radio-button-wrapper:not(:first-child)::before": {
backgroundColor: 'transparent'
},
"&.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)": {
borderColor: token.colorPrimary,
}
}
}))
90 changes: 90 additions & 0 deletions src/app/agent/components/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';
import React from 'react';
import classNames from 'classnames';
import { Row, Col, Avatar, Button, Tooltip } from 'antd';
import { FireOutlined } from '@ant-design/icons';
import ReturnBtn from '@/components/ReturnBtn';
import TagContent from './TagContent';
import { useStyles } from './styles';

import data from '../data'

interface ConversationProps {
data?: any;
}
const layout = {
xs: {
span: 24,
},
sm: {
span: 12,
},
md: {
span: 12,
},
lg: {
span: 12,
},
xl: {
span: 8,
},
xxl: {
span: 6
}
}

const Conversation = React.memo<ConversationProps>(() => {
const { styles } = useStyles();
// const [selectedTags, setSelectedTags] = useState<string[]>(['tuijian']);

return (
<div className={styles.agentContainer}>
<div className={styles.agentContent}>
<ReturnBtn
extra={
<Button>登录</Button>
}
isLeftTitle
title="发现AI 智能体"
to='/chat'
/>
<div>
<div className={styles.main}>
<TagContent list={data.tag_list}/>
<div className={classNames(styles.content, 'scrollBar')}>
<Row gutter={[16, 16]} >
{
data.bot_list.map(item => (
<Col {...layout} key={item.bot_id}>
<div className={styles.card}>
<div className={styles.left}>
<Avatar shape="square" size={72} src={item.icon_url} />
</div>
<div className={styles.right}>
<div className={styles.title}>{item.name}</div>
<Tooltip title={item.onboarding.message_list[0]?.ext?.brief}>
<div className={styles.desc}>
{item.onboarding.message_list[0]?.ext?.brief}
</div>
</Tooltip>
<div className={styles.info}>
<span className={styles.heat}><FireOutlined />{item.bot_stats.display_heat_score}</span>
<span className={styles.creator}>@{item.creator_info.creator_handle}</span>
</div>
</div>
</div>
</Col>
))
}

</Row>
</div>

</div>
</div>
</div>
</div >
);
});

export default Conversation;
74 changes: 74 additions & 0 deletions src/app/agent/components/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createStyles } from 'antd-style';

export const useStyles = createStyles(() => ({
agentContainer: {
width:'100%',
'background': '#ffffff',
'position': 'relative',
'& > div': {
height: '100%',
},
},
agentContent :{
maxWidth:'1440px',
margin:'0 auto',
},
main: {
height:'calc(100vh - 84px)',
padding: '64px 15px 24px 24px'
},
content:{

},
tag:{
minWidth:'108px',
textAlign:'center',
height:'36px',
lineHeight:'36px',
border:'1px solid #E5E5E5',
fontSize:'14px',
marginBottom:8,
},
card:{
display:'flex',
backgroundColor:'#EEEEEE',
padding:16,
borderRadius:'16px'
},
left:{
height:"72px",
width:"72px",
marginRight:12
},
right:{
flex:1,
width:`calc(100% - 75px)`,
},
title:{
marginTop:'5px',
fontSize:'15px',
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
fontWeight:700,
},
desc:{
color:'#535353',
fontSize:14,
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
},
info:{
fontSize:12,
marginTop:4,
color:'#959595',
display:'flex',
},
heat:{
width:50,
},
creator:{
flex:1,
}
}));
Loading
Loading