diff --git a/.dumi/theme/common/Sim/index.tsx b/.dumi/theme/common/Sim/index.tsx index fc0b397b8..03c7e45d6 100644 --- a/.dumi/theme/common/Sim/index.tsx +++ b/.dumi/theme/common/Sim/index.tsx @@ -44,7 +44,7 @@ function buildUrl( const DefaultPlatform = 'alipay'; function getSupportPlatform(platform: string) { - const supportWechat = true + const supportWechat = true; if (!supportWechat && platform === 'wechat') { return { platform: DefaultPlatform, @@ -83,7 +83,10 @@ const Previewer: React.FC = () => { const matchedRoute = useMatchedRoute(); const isShowSim = useMemo(() => { - return matchedRoute?.meta?.frontmatter?.nav?.path === '/components'; + return ( + matchedRoute?.meta?.frontmatter?.nav?.path === '/components' || + matchedRoute?.meta?.frontmatter?.nav?.path === '/copilot' + ); }, [matchedRoute]); const basicUrl = diff --git a/.dumi/theme/slots/Content/index.tsx b/.dumi/theme/slots/Content/index.tsx index 320c5e817..1dbb6c57a 100644 --- a/.dumi/theme/slots/Content/index.tsx +++ b/.dumi/theme/slots/Content/index.tsx @@ -110,12 +110,14 @@ const useStyle = ({ isOverview, isShowSim }) => { const Content: React.FC<{ children: React.ReactNode }> = ({ children }) => { const isOverview = window.location.pathname === '/components/overview' || - window.location.pathname === '/components/overview-en'; + window.location.pathname === '/components/overview-en' || + window.location.pathname === '/copilots/copilot-overview' || + window.location.pathname === '/copilots/copilot-overview-en'; const matchedRoute = useMatchedRoute(); const isShowSim = useMemo(() => { - return matchedRoute?.meta?.frontmatter?.nav?.path === '/components'; + return matchedRoute?.meta?.frontmatter?.nav?.path === '/components' || matchedRoute?.meta?.frontmatter?.nav?.path === '/copilot'; }, [matchedRoute]); const meta = useRouteMeta(); diff --git a/.dumi/theme/slots/Header/Navigation.tsx b/.dumi/theme/slots/Header/Navigation.tsx index 700a65de7..9843149f2 100644 --- a/.dumi/theme/slots/Header/Navigation.tsx +++ b/.dumi/theme/slots/Header/Navigation.tsx @@ -1,5 +1,6 @@ import { MenuFoldOutlined } from '@ant-design/icons'; import { css } from '@emotion/react'; +import { Player } from '@galacean/effects'; import type { MenuProps } from 'antd'; import { Menu } from 'antd'; import { @@ -11,7 +12,8 @@ import { useSiteData, } from 'dumi'; import { INavItem } from 'dumi/dist/client/theme-api/types'; -import { useCallback, useContext } from 'react'; +import { motion } from 'framer-motion'; +import { useCallback, useContext, useEffect, useRef } from 'react'; import useAdditionalThemeConfig from '../../hooks/useAdditionalThemeConfig'; import useLocaleValue from '../../hooks/useLocaleValue'; import useSiteToken from '../../hooks/useSiteToken'; @@ -79,6 +81,27 @@ const useStyle = () => { text-align: center; } `, + navItem: css` + position: relative; + display: flex; + align-items: center; + justify-content: center; + `, + newPlayer: css` + width: 100px; + height: 100px; + position: absolute; + top: 12px; + left: 50%; + transform: translateX(-50%); + z-index: 9; + pointer-events: none; + `, + rightIcon: css` + margin-left: 8px; + width: 16px; + height: 16px; + `, popoverMenuNav: css` ${antCls}-menu-item, ${antCls}-menu-submenu { @@ -112,8 +135,40 @@ export default function Navigation({ isMobile, responsive }: NavigationProps) { const locale = useLocale(); const moreLinks = useLocaleValue('moreLinks'); const activeMenuItem = pathname.split('/').slice(0, 2).join('/'); + const playerDom = useRef(null); + const timers = useRef([]); + + useEffect(() => { + if (!playerDom.current) return; + + const player = new Player({ + container: playerDom.current, + }); + + const timer1 = setTimeout(() => { + player.loadScene( + 'https://mdn.alipayobjects.com/mars/afts/file/A*wKDRRKoA9fcAAAAAAAAAAAAADlB4AQ' + ); + player.play(); + const timer2 = setTimeout(() => { + player.loadScene( + 'https://mdn.alipayobjects.com/mars/afts/file/A*wKDRRKoA9fcAAAAAAAAAAAAADlB4AQ' + ); + player.play(); + timers.current.push(timer2); + }, 10000); + }, 3000); + timers.current.push(timer1); + return () => { + timers.current.forEach((t) => { + clearTimeout(t); + }); + timers.current = []; + }; + }, []); const createMenuItems = (navs: INavItem[]) => { + const style = useStyle(); return navs.map((navItem: INavItem) => { const linkKeyValue = (navItem.link ?? '') .split('/') @@ -121,14 +176,43 @@ export default function Navigation({ isMobile, responsive }: NavigationProps) { .join('/'); return { // eslint-disable-next-line no-nested-ternary - label: navItem.children ? ( - navItem.title - ) : isExternalLinks(navItem.link) ? ( - - {navItem.title} - - ) : ( - {navItem.title} + label: ( +
+ {navItem.children ? ( + navItem.title + ) : isExternalLinks(navItem.link) ? ( + + {navItem.title} + + ) : ( + {navItem.title} + )} + {navItem.isNew ? ( +
+ ) : null} + {navItem.rightIcon && ( + + + + )} +
), key: isExternalLinks(navItem.link) ? navItem.link : linkKeyValue, children: navItem.children ? createMenuItems(navItem.children) : null, diff --git a/.dumi/theme/slots/Sidebar/index.tsx b/.dumi/theme/slots/Sidebar/index.tsx index 9d6028778..5fcd69a37 100644 --- a/.dumi/theme/slots/Sidebar/index.tsx +++ b/.dumi/theme/slots/Sidebar/index.tsx @@ -191,7 +191,7 @@ const useStyle = (isShowPlatfromSwitch) => { padding: 3px; .item { height: 27px; - border-radius:1px; + border-radius: 1px; display: flex; align-items: center; justify-content: center; @@ -250,7 +250,9 @@ const Sidebar: FC = () => { const { pathname } = window.location; return ( pathname.startsWith('/components/') || - matchedRoute?.meta?.frontmatter?.nav?.path === '/components' + matchedRoute?.meta?.frontmatter?.nav?.path === '/components' || + pathname.startsWith('/copilots/') || + matchedRoute?.meta?.frontmatter?.nav?.path === '/copilot' ); }, [matchedRoute]); diff --git a/.dumirc.ts b/.dumirc.ts index 776ff6776..9a8dd2664 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -56,6 +56,14 @@ export default defineConfig({ title: '组件', link: '/components/overview', }, + { + title: 'Copilot', + link: '/copilots/bubble', + // @ts-ignore + isNew: true, + rightIcon: + 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*iiKoT73_9ucAAAAAAAAAAAAAetF8AQ/original', + }, { title: '资源', link: '/resources', @@ -78,6 +86,10 @@ export default defineConfig({ title: 'Components', link: '/components/overview-en', }, + { + title: 'Copilot', + link: '/copilots/conversations-en', + }, { title: 'Resources', link: '/resources-en', @@ -563,4 +575,16 @@ export default defineConfig({ { id: 'zh-CN', name: '中文', suffix: '' }, { id: 'en', name: 'English', suffix: '-en' }, ], + resolve: { + atomDirs: [ + { + type: 'component', + dir: 'src', + }, + { + type: 'copilot', + dir: 'copilot', + }, + ], + }, }); diff --git a/copilot-demo/pages/Actions/index.axml b/copilot-demo/pages/Actions/index.axml new file mode 100644 index 000000000..a709e99a2 --- /dev/null +++ b/copilot-demo/pages/Actions/index.axml @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/copilot-demo/pages/Actions/index.json5 b/copilot-demo/pages/Actions/index.json5 new file mode 100644 index 000000000..af43a7916 --- /dev/null +++ b/copilot-demo/pages/Actions/index.json5 @@ -0,0 +1,16 @@ +{ + /// #if WECHAT + "navigationBarTitleText": "Actions", + /// #endif + + /// #if ALIPAY + "defaultTitle": "Actions", + /// #endif + + "usingComponents": { + "ant-actions": "../../../src/Actions/index", + "ant-bubble": "../../../src/Bubble/index", + "ant-switch": "../../../src/Switch/index", + "ant-container": "../../../src/Container/index" + } +} diff --git a/copilot-demo/pages/Actions/index.less b/copilot-demo/pages/Actions/index.less new file mode 100644 index 000000000..e69de29bb diff --git a/copilot-demo/pages/Actions/index.ts b/copilot-demo/pages/Actions/index.ts new file mode 100644 index 000000000..ba99068f8 --- /dev/null +++ b/copilot-demo/pages/Actions/index.ts @@ -0,0 +1,94 @@ +Page({ + data: { + liked: false, + loaded: false, + showBubble: true, + basicActions: [ + { + label: 'copy', + icon: 'TextOutline', + }, + { + label: 'like', + icon: 'LikeOutline', + }, + { + label: 'collect', + icon: 'StarOutline', + }, + { + label: 'edit', + icon: 'EditSOutline', + }, + ], + bubbleActions: [ + { + label: 'copy', + icon: 'TextOutline', + }, + { + label: 'collect', + icon: 'StarOutline', + }, + ], + }, + + handleTapAction(item) { + /// #if WECHAT + // @ts-ignore + item = item.detail; + /// #endif + const basicActions = [...this.data.basicActions]; + if (item.label === 'like') { + this.setData({ + liked: !this.data.liked, + }); + basicActions.find((action) => action.label === 'like').icon = this.data.liked + ? 'https://mdn.alipayobjects.com/huamei_bsws4g/afts/img/JmVoRp-2UwMAAAAAAAAAAAAADoEQAQFr/original' + : 'LikeOutline'; + } else { + /// #if ALIPAY + my.showToast({ + content: `${item.label} tapped`, + }); + /// #endif + /// #if WECHAT + // @ts-ignore + wx.showToast({ + title: `${item.label} tapped`, + }); + /// #endif + } + this.setData({ + basicActions, + }); + }, + handleBubbleAction(item) { + /// #if WECHAT + // @ts-ignore + item = item.detail; + /// #endif + /// #if ALIPAY + my.showToast({ + content: `${item.label} tapped`, + }); + /// #endif + /// #if WECHAT + // @ts-ignore + wx.showToast({ + title: `${item.label} tapped`, + }); + /// #endif + }, + onTypingComplete() { + this.setData({ + loaded: true, + }); + }, + toggleBubble() { + this.setData({ + showBubble: !this.data.showBubble, + loaded: false, + }); + }, +}); diff --git a/copilot-demo/pages/Bubble/index.axml b/copilot-demo/pages/Bubble/index.axml new file mode 100644 index 000000000..4eed39c6f --- /dev/null +++ b/copilot-demo/pages/Bubble/index.axml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ant Design Mini + + + + + + + + + + + + + + + + 这是一段图文组合消息 + + + + diff --git a/copilot-demo/pages/Bubble/index.json b/copilot-demo/pages/Bubble/index.json new file mode 100644 index 000000000..4732546fb --- /dev/null +++ b/copilot-demo/pages/Bubble/index.json @@ -0,0 +1,8 @@ +{ + "usingComponents": { + "ant-bubble": "../../../src/Bubble/index", + "ant-actions": "../../../src/Actions/index", + "ant-container": "../../../src/Container/index", + "ant-switch": "../../../src/Switch/index" + } +} diff --git a/copilot-demo/pages/Bubble/index.less b/copilot-demo/pages/Bubble/index.less new file mode 100644 index 000000000..8ebc1031e --- /dev/null +++ b/copilot-demo/pages/Bubble/index.less @@ -0,0 +1,16 @@ +.navigation-bar { + height: var(--tf-status-bar-height); +} + +.ant-copilot-bubble-name { + font-weight: 400; + font-size: 24rpx; + color: #737373; + line-height: 40rpx; +} + +.ant-copilot-bubble-image { + width: 400rpx; + height: 400rpx; + border-radius: 10rpx; +} diff --git a/copilot-demo/pages/Bubble/index.ts b/copilot-demo/pages/Bubble/index.ts new file mode 100644 index 000000000..6d2f4d235 --- /dev/null +++ b/copilot-demo/pages/Bubble/index.ts @@ -0,0 +1,46 @@ +Page({ + data: { + loading: true, + showTyping: true, + basicActions: [ + { + label: 'copy', + icon: 'TextOutline', + }, + { + label: 'like', + icon: 'LikeOutline', + }, + ], + }, + onTypingComplete() { + console.log('typing complete at', Date.now()); + }, + onTypingChange(e) { + this.setData({ + showTyping: !this.data.showTyping, + }); + }, + onSwitchChange() { + this.setData({ + loading: !this.data.loading, + }); + }, + handleTapAction(item) { + /// #if WECHAT + // @ts-ignore + item = item.detail; + /// #endif + /// #if ALIPAY + my.showToast({ + content: `${item.label} tapped`, + }); + /// #endif + /// #if WECHAT + // @ts-ignore + wx.showToast({ + title: `${item.label} tapped`, + }); + /// #endif + }, +}); diff --git a/copilot-demo/pages/Conversations/index.axml b/copilot-demo/pages/Conversations/index.axml new file mode 100644 index 000000000..48dab7ea1 --- /dev/null +++ b/copilot-demo/pages/Conversations/index.axml @@ -0,0 +1,46 @@ + + + + + + + + + + + {{props.item.label}} + {{props.item.description}} + + + + + + + 历史记录 + + + + diff --git a/copilot-demo/pages/Conversations/index.json5 b/copilot-demo/pages/Conversations/index.json5 new file mode 100644 index 000000000..ef6c2e923 --- /dev/null +++ b/copilot-demo/pages/Conversations/index.json5 @@ -0,0 +1,18 @@ +{ + /// #if WECHAT + "navigationBarTitleText": "Conversations", + /// #endif + + /// #if ALIPAY + "defaultTitle": "Conversations", + "transparentTitle": "auto", + "titlePenetrate": "YES", + /// #endif + + "usingComponents": { + "ant-container": "../../../src/Container/index", + "ant-conversations": "../../../src/Conversations/index", + "ant-popup": "../../../src/Popup/index", + "ant-button": "../../../src/Button/index" + } +} diff --git a/copilot-demo/pages/Conversations/index.less b/copilot-demo/pages/Conversations/index.less new file mode 100644 index 000000000..6918bdab7 --- /dev/null +++ b/copilot-demo/pages/Conversations/index.less @@ -0,0 +1,7 @@ +.history-popup { + .ant-popup-transform-left { + /// #if ALIPAY + padding-top: 90px; + /// #endif + } +} diff --git a/copilot-demo/pages/Conversations/index.ts b/copilot-demo/pages/Conversations/index.ts new file mode 100644 index 000000000..de9de6121 --- /dev/null +++ b/copilot-demo/pages/Conversations/index.ts @@ -0,0 +1,100 @@ +Page({ + data: { + visible: false, + items: [ + { + key: '1', + label: '给我推荐一首歌', + description: '这是会话的部分富文本信息描述', + icon: 'https://randomuser.me/api/portraits/thumb/women/4.jpg', + timestamp: '10:23', + disabled: false, + }, + { + key: '2', + label: '请根据图片里的提示,写一篇小学二年级的数学题目', + description: + '这首歌来自英国歌手艾德·希兰旋律轻快,歌曲写自上个世纪落日', + icon: '', + timestamp: '10:22', + disabled: false, + }, + { + key: '3', + label: '禁用无法点击此条', + description: '这是会话的部分富文本信息描述', + icon: '', + timestamp: '10:21', + disabled: true, + }, + { + key: '4', + label: '菜单禁用无法滑动', + description: '这是会话的部分富文本信息描述', + icon: '', + timestamp: '10:18', + disabled: false, + disabledMenu: true, + }, + { + key: '5', + label: '给我推荐一首歌', + description: '这是会话的部分富文本信息描述', + icon: '', + timestamp: '09:11', + disabled: false, + }, + ], + menus: [ + { + text: '编辑', + bgColor: '#FFA91B', + color: '#fff', + width: 160, + }, + { + text: '删除', + bgColor: '#FF2B00', + color: '#fff', + width: 160, + }, + ], + }, + handleItemTap(i) { + let item = i; + /// #if ALIPAY + console.log(item); + my.showToast({ content: item.key }); + /// #endif + /// #if WECHAT + item = i.detail[0]; + console.log(item); + // @ts-ignore + wx.showToast({ title: item.key }); + /// #endif + }, + handleMenuItemTap(menuItem, item) { + /// #if ALIPAY + console.log(menuItem, item); + my.showToast({ content: `菜单${menuItem.index}_列表项${item.key}` }); + /// #endif + /// #if WECHAT + console.log(menuItem.detail[0], menuItem.detail[1]); + // @ts-ignore + wx.showToast({ + title: `菜单${menuItem.detail[0].index}_列表项${menuItem.detail[1].key}`, + }); + /// #endif + }, + + handleOpenHistory() { + this.setData({ + visible: true, + }); + }, + handlePopupClose() { + this.setData({ + visible: false, + }); + }, +}); diff --git a/copilot-demo/pages/Prompts/index.axml b/copilot-demo/pages/Prompts/index.axml new file mode 100644 index 000000000..fe0194619 --- /dev/null +++ b/copilot-demo/pages/Prompts/index.axml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + 自定义:{{ props.item.label }} + + + + + + + + 我可以帮助您: + + + + + + + + 自定义提示项:{{ props.item.label }} + + + + diff --git a/copilot-demo/pages/Prompts/index.json5 b/copilot-demo/pages/Prompts/index.json5 new file mode 100644 index 000000000..d75d2df73 --- /dev/null +++ b/copilot-demo/pages/Prompts/index.json5 @@ -0,0 +1,16 @@ +{ + /// #if WECHAT + "navigationBarTitleText": "Prompts", + /// #endif + + /// #if ALIPAY + "defaultTitle": "Prompts", + "transparentTitle": "auto", + "titlePenetrate": "YES", + /// #endif + + "usingComponents": { + "ant-container": "../../../src/Container/index", + "ant-prompts": "../../../src/Prompts/index" + } +} diff --git a/copilot-demo/pages/Prompts/index.less b/copilot-demo/pages/Prompts/index.less new file mode 100644 index 000000000..3f2d70864 --- /dev/null +++ b/copilot-demo/pages/Prompts/index.less @@ -0,0 +1,27 @@ +page { + padding: 12rpx; +} + +.customizeStyle { + .ant-copilot-prompts-list-item-content { + font-family: PingFangSC-Light; + font-size: 28rpx; + color: #bbbbbb; + line-height: 44rpx; + } +} + +.customize-prompts-title { + color: #b76b01; + font: 500; + font-size: 36rpx; + padding-bottom: 24rpx; +} + +.customize-prompts-item { + color: #ffffff; + font-size: 24rpx; + padding: 24rpx; + background: #aaaaaa; + border-radius: 16rpx; +} diff --git a/copilot-demo/pages/Prompts/index.ts b/copilot-demo/pages/Prompts/index.ts new file mode 100644 index 000000000..1cb5d3310 --- /dev/null +++ b/copilot-demo/pages/Prompts/index.ts @@ -0,0 +1,108 @@ +Page({ + data: { + promptsTitle: '我可以帮您:', + baseList: [ + { + label: 'Ant Design X 全新升级了什么?', + key: '1', + }, + { + label: 'Ant Design X 组件资产内容', + key: '2', + }, + { + label: '快速实现安装和引入组件', + key: '3', + }, + ], + arrowList: [ + { + showArrow: true, + label: '热门话题:', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*wJ5aRpr2q1wAAAAAAAAAAAAADkx8AQ/original', + description: 'Ant Design X 全新升级', + key: '1', + }, + { + showArrow: true, + label: '组件查询:', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*R9L1RLo4x58AAAAAAAAAAAAADkx8AQ/original', + description: '欢迎组件', + key: '2', + }, + { + showArrow: true, + label: '新手帮助:', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*7eRqQ4JpdccAAAAAAAAAAAAADkx8AQ/original', + description: '如何实现快速安装和引入', + key: '3', + }, + ], + styleList: [ + { + label: '我的日程', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*LzX3QIQ3EXMAAAAAAAAAAAAADkx8AQ/original', + description: '每日日程提醒,不错过每一个日程', + key: '1', + }, + { + label: '组件查询', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*2TQ2QaONoRYAAAAAAAAAAAAADkx8AQ/original', + description: '每日审批提醒,不漏信息', + key: '2', + }, + { + label: '会议邀约', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*Fw-tQIDYFvsAAAAAAAAAAAAADkx8AQ/original', + description: '实时会邀提醒,不错过重要会议', + key: '3', + }, + { + label: '更多', + icon: 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*YDWcQbavX8QAAAAAAAAAAAAADkx8AQ/original', + description: '点击了解更多技能', + key: '4', + }, + ], + horizontalList: [ + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*AdC9RZfDwicAAAAAAAAAAAAADtF8AQ/original', + label: '我的日程', + value: 'schedule', + key: '1', + }, + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*Mbi1R5eGTecAAAAAAAAAAAAADtF8AQ/original', + label: '快捷功能', + value: 'suggestion', + key: '2', + }, + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*WqBJSZEpPbwAAAAAAAAAAAAADtF8AQ/original', + label: '诊断分析', + value: 'analyze', + key: '3', + }, + { + // 支持 antd-icon + icon: 'MoreOutline', + label: '更多', + value: 'more', + key: '4', + }, + ], + }, + onItemTap(i) { + let item = i; + /// #if ALIPAY + console.log(item); + my.showToast({ content: item.key }); + /// #endif + /// #if WECHAT + item = i.detail[0]; + console.log(item); + // @ts-ignore + wx.showToast({ title: item.key }); + /// #endif + }, +}); diff --git a/copilot-demo/pages/Sender/index.axml b/copilot-demo/pages/Sender/index.axml new file mode 100644 index 000000000..d14828770 --- /dev/null +++ b/copilot-demo/pages/Sender/index.axml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/copilot-demo/pages/Sender/index.json b/copilot-demo/pages/Sender/index.json new file mode 100644 index 000000000..51c933503 --- /dev/null +++ b/copilot-demo/pages/Sender/index.json @@ -0,0 +1,9 @@ +{ + "usingComponents": { + "ant-sender": "../../../src/Sender/index", + "ant-upload": "../../../src/ImageUpload/index", + "ant-prompts": "../../../src/Prompts/index", + "ant-bubble": "../../../src/Bubble/index", + "ant-container": "../../../src/Container/index" + } +} diff --git a/copilot-demo/pages/Sender/index.less b/copilot-demo/pages/Sender/index.less new file mode 100644 index 000000000..68bf2c5dd --- /dev/null +++ b/copilot-demo/pages/Sender/index.less @@ -0,0 +1,15 @@ +.page { + min-height: 100vh; + background: var(--color-background); + box-sizing: border-box; + padding-bottom: 200rpx; +} + +.navigation-bar { + margin-bottom: 24rpx; + height: var(--tf-status-bar-height); +} + +.content { + padding: 24rpx; +} diff --git a/copilot-demo/pages/Sender/index.ts b/copilot-demo/pages/Sender/index.ts new file mode 100644 index 000000000..39970aea8 --- /dev/null +++ b/copilot-demo/pages/Sender/index.ts @@ -0,0 +1,119 @@ +const mockResponse = [ + '很高兴见到你!让我们开始愉快的对话吧。', + '你说得很有意思,能详细说说吗?', + '这是个很好的问题,我来仔细想想...', + '我明白你的想法,确实值得深入讨论。', + '感谢分享,这让我想到了一些新的观点。', + '你提到的这点很有启发性,我们可以进一步探讨。', +]; +Page({ + senderTimer: null, + data: { + value: '', + value1: '', + value3: '', + value4: '', + loading: false, + scrollToView: '', + bubbleList: [{ content: '你好,世界!' }], + horizontalList: [ + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*AdC9RZfDwicAAAAAAAAAAAAADtF8AQ/original', + label: '我的日程', + value: 'schedule', + key: '1', + }, + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*Mbi1R5eGTecAAAAAAAAAAAAADtF8AQ/original', + label: '快捷功能', + value: 'suggestion', + key: '2', + }, + { + icon: 'https://mdn.alipayobjects.com/huamei_2jrq4g/afts/img/A*WqBJSZEpPbwAAAAAAAAAAAAADtF8AQ/original', + label: '诊断分析', + value: 'analyze', + key: '3', + }, + { + // 支持 antd-icon + icon: 'MoreOutline', + label: '更多', + value: 'more', + key: '4', + }, + ], + }, + /** 基础用法 */ + handleChange(value: string) { + /// #if WECHAT + // @ts-ignore + value = value.detail; + /// #endif + this.setData({ value }); + }, + handleSend() { + this.setData({ value: '' }); + }, + /** 发送loading状态 */ + handleChange1(value: string) { + /// #if WECHAT + // @ts-ignore + value = value.detail; + /// #endif + this.setData({ value1: value }); + }, + handleSend1() { + this.setData({ loading: true }); + this.senderTimer = setTimeout(() => { + this.setData({ value1: '', loading: false }); + }, 2000); + }, + handleCancel() { + clearTimeout(this.senderTimer); + this.setData({ loading: false }); + }, + /** 配合upload组件 */ + handleChange3(value: string) { + /// #if WECHAT + // @ts-ignore + value = value.detail; + /// #endif + this.setData({ value3: value }); + }, + handleSend3() { + this.setData({ value3: '' }); + }, + /** 配合bubble和prompts */ + addBubble(value: string) { + const bubbleList = [...this.data.bubbleList]; + bubbleList.push({ content: value }); + this.setData({ bubbleList }, () => { + this.setData({ scrollToView: `msg-${bubbleList.length - 1}` }); + }); + }, + handleChange4(value: string) { + /// #if WECHAT + // @ts-ignore + value = value.detail; + /// #endif + this.setData({ value4: value }); + }, + onItemTap(e) { + /// #if WECHAT + // @ts-ignore + e = e.detail; + /// #endif + this.handleSend4(e.label) + }, + handleSend4(val: string) { + if (!this.data.value4 && !val) return; + this.addBubble(val || this.data.value4); + this.setData({ value4: '' }); + setTimeout(() => { + this.addBubble( + mockResponse[Math.floor(Math.random() * mockResponse.length)] + ); + }, 500); + }, +}); diff --git a/copilot-demo/pages/ThoughtChain/index.axml b/copilot-demo/pages/ThoughtChain/index.axml new file mode 100644 index 000000000..b6ac3fd18 --- /dev/null +++ b/copilot-demo/pages/ThoughtChain/index.axml @@ -0,0 +1,44 @@ + + + + + + + + + + + {{module.content}} + + + + + + + + + + + {{item}} + + + + {{secondModule.content}} + + + + {{module.content}} + + + + + + + + + + diff --git a/copilot-demo/pages/ThoughtChain/index.json5 b/copilot-demo/pages/ThoughtChain/index.json5 new file mode 100644 index 000000000..11535eafa --- /dev/null +++ b/copilot-demo/pages/ThoughtChain/index.json5 @@ -0,0 +1,15 @@ +{ + /// #if WECHAT + "navigationBarTitleText": "ThoughtChain 组件", + /// #endif + + /// #if ALIPAY + "defaultTitle": "ThoughtChain 组件", + /// #endif + + "usingComponents": { + "ant-container": "../../../src/Container/index", + "ant-thought-chain": "../../../src/ThoughtChain/index" + }, + "transparentTitle": "always" +} diff --git a/copilot-demo/pages/ThoughtChain/index.less b/copilot-demo/pages/ThoughtChain/index.less new file mode 100644 index 000000000..798ee8647 --- /dev/null +++ b/copilot-demo/pages/ThoughtChain/index.less @@ -0,0 +1,31 @@ +page { + // background: #f5f5f5; + padding: 12rpx; + /* 浅色模式的css变量 */ +@media (prefers-color-scheme: light) { + --background-color: #f5f5f5; + --title-color: #000; + --content-color: #999; +} + +/* 深色模式的css变量 */ +@media (prefers-color-scheme: dark) { + --background-color: #0A0A0A; + --title-color: #fff; + --content-color: #666; +} + .secondLevel { + padding: 24rpx; + background-color: var(--background-color); + border-radius: 24rpx; + margin-left: -58rpx; + } + .second-thoughtchain { + .ant-copilot-thoughtchain-node-title { + color: var(--title-color); + } + .second-thoughtchain-content { + color: var(--content-color); + } + } +} diff --git a/copilot-demo/pages/ThoughtChain/index.ts b/copilot-demo/pages/ThoughtChain/index.ts new file mode 100644 index 000000000..8b4e5f96c --- /dev/null +++ b/copilot-demo/pages/ThoughtChain/index.ts @@ -0,0 +1,63 @@ +//@ts-ignore + +Page({ + data: { + basicList: [ + { + title: '理解问题', + content: '3A游戏', + icon: 'CheckCircleOutline', + }, + { + title: '没有在本地找到结果', + content: '当前主流显卡', + icon: 'CheckCircleOutline', + }, + { + title: '在互联网上搜索问题', + content: '黑神话悟空所需显卡', + icon: 'MoreOutline', + }, + ], + customList: [ + { + title: '理解问题', + content: '解析语法结构', + }, + { + status: 'loading', + title: '搜索问题', + content: [ + { + title: '理解问题', + }, + { + title: '联网搜索', + content: '1. 黑神话悟空介绍', + }, + { + title: '已根据搜索结果精选3篇资料', + content: [ + '1. 黑神话悟空介绍', + '2. 对于1080p显示器,4060ti和4060该选哪个?', + '3. 黑神话悟空官方建议配置', + ], + }, + { + title: '联想更多结果', + }, + ], + }, + ], + }, + onContentItemTap(e) { + const { content } = e.currentTarget.dataset || {}; + /// #if ALIPAY + my.showToast({ content: content }); + /// #endif + /// #if WECHAT + // @ts-ignore + wx.showToast({ title: content }); + /// #endif + }, +}); diff --git a/copilot-demo/pages/Welcome/index.axml b/copilot-demo/pages/Welcome/index.axml new file mode 100644 index 000000000..3a4002c5a --- /dev/null +++ b/copilot-demo/pages/Welcome/index.axml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/copilot-demo/pages/Welcome/index.json5 b/copilot-demo/pages/Welcome/index.json5 new file mode 100644 index 000000000..4e71e1115 --- /dev/null +++ b/copilot-demo/pages/Welcome/index.json5 @@ -0,0 +1,15 @@ +{ + /// #if WECHAT + "navigationBarTitleText": "Welcome 组件", + /// #endif + + /// #if ALIPAY + "defaultTitle": "Welcome 组件", + /// #endif + + "usingComponents": { + "ant-container": "../../../src/Container/index", + "ant-welcome": "../../../src/Welcome/index" + }, + "transparentTitle": "always" +} diff --git a/copilot-demo/pages/Welcome/index.less b/copilot-demo/pages/Welcome/index.less new file mode 100644 index 000000000..e30dab536 --- /dev/null +++ b/copilot-demo/pages/Welcome/index.less @@ -0,0 +1,18 @@ +page { + // background: #f5f5f5; + padding: 12rpx; + .secondLevel { + padding: 24rpx; + background-color: #f5f5f5; + border-radius: 24rpx; + margin-left: -58rpx; + } + .second-thoughtchain { + .ant-copilot-thoughtchain-node-title { + color: #000; + } + .second-thoughtchain-content { + color: #999; + } + } +} diff --git a/copilot-demo/pages/Welcome/index.ts b/copilot-demo/pages/Welcome/index.ts new file mode 100644 index 000000000..73505e5fb --- /dev/null +++ b/copilot-demo/pages/Welcome/index.ts @@ -0,0 +1,59 @@ +//@ts-ignore + +Page({ + data: { + basicList: [ + { + title: '理解问题', + content: '3A游戏', + icon: 'CheckCircleOutline' + }, + { + title: '没有在本地找到结果', + content: '当前主流显卡', + icon: 'CheckCircleOutline' + }, + { + title: '在互联网上搜索问题', + content: '黑神话悟空所需显卡', + icon: 'MoreOutline', + }, + ], + customList: [ + { + title: '理解问题', + content: '解析语法结构', + }, + { + status: 'loading', + title: '搜索问题', + content: [ + { + title: '理解问题', + }, + { + title: '联网搜索', + content: '1. 黑神话悟空介绍', + }, + { + title: '已根据搜索结果精选3篇资料', + content: [ + '1. 黑神话悟空介绍', + '2. 对于1080p显示器,4060ti和4060该选哪个?', + '3. 黑神话悟空官方建议配置', + ], + }, + { + title: '联想更多结果', + }, + ], + }, + ], + }, + onContentItemTap(e) { + const { content } = e.target.dataset || {}; + my.alert({ + content: `点击了内容「${content}」`, + }); + }, +}); diff --git a/copilot/Actions/check.sjs b/copilot/Actions/check.sjs new file mode 100644 index 000000000..b83bb02ac --- /dev/null +++ b/copilot/Actions/check.sjs @@ -0,0 +1,14 @@ +/// #if ALIPAY +function checkIcon(icon = '') { + return getRegExp('^[a-zA-Z]+$').test(icon); +} +export default { checkIcon }; +/// #endif +/// #if WECHAT +function checkIcon(icon = '') { + return getRegExp('^[a-zA-Z]+$').test(icon); +} +module.exports = { + checkIcon +} +/// #endif diff --git a/copilot/Actions/index.axml b/copilot/Actions/index.axml new file mode 100644 index 000000000..c73dbb99e --- /dev/null +++ b/copilot/Actions/index.axml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/copilot/Actions/index.en.md b/copilot/Actions/index.en.md new file mode 100644 index 000000000..11cfcccdd --- /dev/null +++ b/copilot/Actions/index.en.md @@ -0,0 +1,50 @@ +--- +nav: + path: /copilot +group: + title: Express + order: 3 +toc: 'content' +--- + +# Actions + +## When to use + +Use when you need to show a set of action options. + +## Code Sample + +### Basic Usage + + + +## API + +### Property + +| Property | Description | Type | Default Value | +| --- | --- | --- | --- | +| className | Class Name | string | - | +| style | Style | string | - | +| items | Action Item List | ActionItem[] | [] | +| onItemTap | Triggered when an action item is clicked | (item: ActionItem) => void | - | + +### ActionItem + +| Property | Description | Type | Default Value | +| --- | --- | --- | --- | +| label | Action Item Text | string | - | +| icon | Action Item Icon | string | - | + +### Theme customization + +#### Style Variables + +Component provides the following CSS variables, which can be used to customize styles. For more information, see ConfigProvider Components. + +| Variable name | Default Value | Description | +| --- | --- | --- | +| --actions-background-color | #F7F7F7 | Background Color | +| --actions-item-hover-background-color | #EEEEEE | Click background color | + diff --git a/copilot/Actions/index.json b/copilot/Actions/index.json new file mode 100644 index 000000000..c6980434f --- /dev/null +++ b/copilot/Actions/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "ant-icon": "../../src/Icon/index" + } +} diff --git a/copilot/Actions/index.less b/copilot/Actions/index.less new file mode 100644 index 000000000..717dadfc8 --- /dev/null +++ b/copilot/Actions/index.less @@ -0,0 +1,31 @@ +@import (reference) './variable.less'; +@import (reference) '../../src/style/mixins/hairline.less'; + +@prefixCls: ant-copilot-actions; + +.@{prefixCls} { + display: inline-flex; + justify-content: space-between; + gap: @size-3; + background: @actions-background-color; + padding: @size-2 @size-3; + border-radius: @corner-radius-xl; + + &-item { + display: flex; + align-items: center; + justify-content: center; + width: @size-7; + height: @size-7; + border-radius: @corner-radius-md-plus; + &:active { + background: @actions-item-hover-background-color; + } + &-icon { + font-size: @size-5; + width: @size-5; + height: @size-5; + } + } +} + diff --git a/copilot/Actions/index.md b/copilot/Actions/index.md new file mode 100644 index 000000000..adde19628 --- /dev/null +++ b/copilot/Actions/index.md @@ -0,0 +1,50 @@ +--- +nav: + path: /copilot +group: + title: 表达 + order: 3 +toc: 'content' +--- + +# Actions 操作列表 + +## 何时使用 + +需要展示一组操作选项时使用。 + +## 代码示例 + +### 基础用法 + + + +## API + +### 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| className | 类名 | string | - | +| style | 样式 | string | - | +| items | 动作项列表 | ActionItem[] | [] | +| onItemTap | 点击动作项时触发 | (item: ActionItem) => void | - | + +### ActionItem + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| label | 动作项文本 | string | - | +| icon | 动作项图标 | string | - | + +### 主题定制 + +#### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 ConfigProvider 组件。 + +| 变量名 | 默认值 | 说明 | +| --- | --- | --- | +| --actions-background-color | #F7F7F7 | 背景色 | +| --actions-item-hover-background-color | #EEEEEE | 点击态背景色 | + diff --git a/copilot/Actions/index.ts b/copilot/Actions/index.ts new file mode 100644 index 000000000..e6448ee5c --- /dev/null +++ b/copilot/Actions/index.ts @@ -0,0 +1,9 @@ +import { Component, triggerEvent } from '../../src/_util/simply'; +import { ActionsProps } from './props'; + +Component(ActionsProps, { + handleTapAction(e) { + const { action } = e.currentTarget.dataset; + triggerEvent(this, 'itemTap', action); + }, +}); diff --git a/copilot/Actions/props.ts b/copilot/Actions/props.ts new file mode 100644 index 000000000..b37d4719c --- /dev/null +++ b/copilot/Actions/props.ts @@ -0,0 +1,30 @@ +import { IBaseProps } from '../../src/_util/base'; + +interface IActionItem { + /** + * @description 操作文本 + */ + label: string; + /** + * @description 图标地址 + */ + icon?: string; +} + +interface IActionsProps extends IBaseProps { + /** + * @description 操作列表 + */ + items: IActionItem[]; + /** + * @description 点击操作的回调 + */ + onItemTap?: (action: IActionItem) => void; +} + +export const ActionsProps: IActionsProps = { + items: [], + onItemTap: () => {}, +}; + +export type { IActionsProps }; diff --git a/copilot/Actions/variable.less b/copilot/Actions/variable.less new file mode 100644 index 000000000..3982c4fae --- /dev/null +++ b/copilot/Actions/variable.less @@ -0,0 +1,7 @@ +@import (reference) '../../src/style/variables.less'; +@import (reference) '../../src/style/themes/index.less'; + +// 背景色 +@actions-background-color: var(--actions-background-color, #F7F7F7); +@actions-item-hover-background-color: var(--actions-item-hover-background-color, #EEEEEE); + diff --git a/copilot/Bubble/index.axml b/copilot/Bubble/index.axml new file mode 100644 index 000000000..264d5c4c1 --- /dev/null +++ b/copilot/Bubble/index.axml @@ -0,0 +1,15 @@ + + + + + + + {{bubbleText}} + + + + + diff --git a/copilot/Bubble/index.en.md b/copilot/Bubble/index.en.md new file mode 100644 index 000000000..2fa9581e8 --- /dev/null +++ b/copilot/Bubble/index.en.md @@ -0,0 +1,74 @@ +--- +nav: + path: /copilot +group: + title: General + order: 1 +toc: 'content' +--- + +# Bubble + +## When to use + +When you need to display the content of the dialogue, it supports a variety of bubble styles, typing animation and other effects. + +## Code Sample + +### Basic Usage + + + +## API + +### Property + +| Property | Description | Type | Default Value | +| --- | --- | --- | --- | +| className | Class Name | string | - | +| style | Style | string | - | +| content | Bubble Content | string | - | +| placement | Bubble position | 'start' \| 'end' | 'end' | +| shape | Bubble shape | 'round' \| 'corner' \| 'default' | 'default' | +| variant | Bubble Style | 'filled' \| 'borderless' \| 'outlined' \| 'shadow' | 'filled' | +| avatar | avatar address | string | - | +| loading | Loading | boolean | false | +| typing | Typing Animation Configuration | boolean \| { step?: number, interval?: number } | false | +| onTypingComplete | Triggers when typing animation completes | () => void | - | + +### Slots + +| Name | Description | +| --- | --- | +| default | Custom bubble content | +| header | Custom header content (such as user name) | +| footer | Custom footer content (such as custom operations) | + +### Theme customization + +#### Style Variables + +Component provides the following CSS variables, which can be used to customize styles. For more information, see ConfigProvider Components. + +| Variable name | Default Value | Description | +| --- | --- | --- | +| --bubble-background-color | #F7F7F7 | Bubble background color | +| --bubble-user-background-color | #1677FF | User message bubble background color | +| --bubble-outlined-color | #EBEBEB | The border color of the stroke style | +| --bubble-text-color | rgba(0,0,0,0.88) | Text Color | +| --bubble-text-color-user | #ffffff | User Message Text Color | + +## FAQ + +### 1. How to achieve the typewriter effect? + +The typing animation can be configured via the typing property: +- Setup `typing="{{true}}"` Use default configuration +- Setup `typing="{{{ step: 1, interval: 50 }}}"` Customize the number of characters per print and the interval +- By `onTypingComplete` Monitor typing animation complete + +### 2. What bubble styles are supported? + +A variety of bubble styles can be achieved through the combination of shape and variant attributes: +- shape: control bubble shape, support default/round/corner +- variant: control bubble appearance, support filled/borderless/outlined/shadow diff --git a/copilot/Bubble/index.json b/copilot/Bubble/index.json new file mode 100644 index 000000000..034855fc1 --- /dev/null +++ b/copilot/Bubble/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "ant-loading": "../../src/Loading" + } +} diff --git a/copilot/Bubble/index.less b/copilot/Bubble/index.less new file mode 100644 index 000000000..ba4385b77 --- /dev/null +++ b/copilot/Bubble/index.less @@ -0,0 +1,64 @@ +@import (reference) './variable.less'; + +@prefixCls: ant-copilot-bubble; + +.@{prefixCls} { + display: flex; + align-items: flex-start; + padding: 24rpx; + + &-avatar { + margin-top: 8rpx; + width: @bubble-avatar-size; + height: @bubble-avatar-size; + border-radius: @corner-radius-circle; + flex-shrink: 0; + } + + &-container { + margin: 0 24rpx; + } + + &-content { + padding: @size-3 @size-4; + margin: @size-1 0; + border-radius: @corner-radius-xl; + background: @bubble-background-color; + font-size: var(--size-font6); + color: @bubble-text-color; + line-height: 1.5; + word-break: break-all; + &.round { + border-radius: @corner-radius-xxl; + } + &.corner { + border-radius: @corner-radius-sm @corner-radius-xl @corner-radius-xl @corner-radius-xl; + } + // &.filled { + // background: @bubble-background-color; + // } + &.borderless { + background: transparent; + } + &.outlined { + background: transparent; + border: 1rpx solid @bubble-outlined-color; + } + &.shadow { + box-shadow: 0 0 4rpx 0 rgba(0,0,0,0.05), 0 0 12rpx -8rpx rgba(0,0,0,0.04), 0 4rpx 8rpx 0 rgba(0,0,0,0.04); + } + } + + &-end { + flex-direction: row-reverse; + + .@{prefixCls}-content { + background: @bubble-user-background-color; + color: @bubble-text-color-user; + &.corner { + border-radius: @corner-radius-xl @corner-radius-sm @corner-radius-xl @corner-radius-xl; + } + } + } +} + diff --git a/copilot/Bubble/index.md b/copilot/Bubble/index.md new file mode 100644 index 000000000..0bb2142ff --- /dev/null +++ b/copilot/Bubble/index.md @@ -0,0 +1,74 @@ +--- +nav: + path: /copilot +group: + title: 通用 + order: 1 +toc: 'content' +--- + +# Bubble 对话气泡 + +## 何时使用 + +需要展示对话内容时使用,支持多种气泡样式、打字动画等效果。 + +## 代码示例 + +### 基础用法 + + + +## API + +### 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| className | 类名 | string | - | +| style | 样式 | string | - | +| content | 气泡内容 | string | - | +| placement | 气泡位置 | 'start' \| 'end' | 'end' | +| shape | 气泡形状 | 'round' \| 'corner' \| 'default' | 'default' | +| variant | 气泡样式 | 'filled' \| 'borderless' \| 'outlined' \| 'shadow' | 'filled' | +| avatar | 头像地址 | string | - | +| loading | 是否加载中 | boolean | false | +| typing | 打字动画配置 | boolean \| { step?: number, interval?: number } | false | +| onTypingComplete | 打字动画完成时触发 | () => void | - | + +### Slots + +| 名称 | 说明 | +| --- | --- | +| default | 自定义气泡内容 | +| header | 自定义头部内容(如用户名) | +| footer | 自定义底部内容(如自定义操作等) | + +### 主题定制 + +#### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 ConfigProvider 组件。 + +| 变量名 | 默认值 | 说明 | +| --- | --- | --- | +| --bubble-background-color | #F7F7F7 | 气泡背景色 | +| --bubble-user-background-color | #1677FF | 用户消息气泡背景色 | +| --bubble-outlined-color | #EBEBEB | 描边样式的边框颜色 | +| --bubble-text-color | rgba(0,0,0,0.88) | 文本颜色 | +| --bubble-text-color-user | #ffffff | 用户消息文本颜色 | + +## FAQ + +### 1. 如何实现打字机效果? + +可以通过 typing 属性配置打字动画: +- 设置 `typing="{{true}}"` 使用默认配置 +- 设置 `typing="{{{ step: 1, interval: 50 }}}"` 自定义每次打印字符数和间隔时间 +- 通过 `onTypingComplete` 监听打字动画完成 + +### 2. 支持哪些气泡样式? + +通过 shape 和 variant 属性组合可以实现多种气泡样式: +- shape: 控制气泡形状,支持 default/round/corner +- variant: 控制气泡外观,支持 filled/borderless/outlined/shadow diff --git a/copilot/Bubble/index.ts b/copilot/Bubble/index.ts new file mode 100644 index 000000000..a42680640 --- /dev/null +++ b/copilot/Bubble/index.ts @@ -0,0 +1,77 @@ +import { Component, getValueFromProps, triggerEventOnly } from '../../src/_util/simply'; +import { BubbleProps } from './props'; + +Component( + BubbleProps, + { + startTyping() { + const [loading, typing, content] = getValueFromProps(this, [ + 'loading', + 'typing', + 'content', + ]); + if (loading) { + return; + } + if (content) { + const typingOptions = typing + ? typing === true + ? { step: 1, interval: 100 } + : typing + : false; + if (typingOptions) { + const { step = 1, interval = 100 } = typingOptions; + const bubbleText = content; + // todo 待优化 + const typingLoop = (length: number, typedLength: number) => { + const typingText = bubbleText.slice(0, typedLength); + this.setData({ + bubbleText: typingText, + }); + if (typedLength < length) { + setTimeout(() => { + typingLoop(length, typedLength + step); + }, interval); + } else { + triggerEventOnly(this, 'typingComplete') + } + }; + typingLoop(bubbleText.length, step); + } else { + this.setData({ + bubbleText: content, + }); + triggerEventOnly(this, 'typingComplete') + } + } + }, + }, + { + bubbleText: '', + }, + [], + { + options: { + // 使用基础库内置的数据变化观测器 + observers: true, + multipleSlots: true, + }, + observers: { + loading: function (loading: boolean) { + if (!loading) { + this.startTyping(); + } + }, + }, +/// #if ALIPAY + didMount() { + this.startTyping(); + }, +/// #endif +/// #if WECHAT + attached() { + this.startTyping(); + }, +/// #endif + } +); diff --git a/copilot/Bubble/props.ts b/copilot/Bubble/props.ts new file mode 100644 index 000000000..8869e7c00 --- /dev/null +++ b/copilot/Bubble/props.ts @@ -0,0 +1,53 @@ +import { IBaseProps } from '../../src/_util/base'; + +interface IBubbleProps extends IBaseProps { + /** + * @description 气泡内容 + */ + content: string; + /** + * @description 是否为用户消息(右侧) + * @default start + */ + placement?: 'start' | 'end'; + /** + * @description 气泡形状 + * @default 'default' + */ + shape?: 'round' | 'corner' | 'default'; + /** + * @description 气泡样式 + * @default filled + */ + variant?: 'filled' | 'borderless' | 'outlined' | 'shadow'; + /** + * @description 头像地址 + */ + avatar?: string; + /** + * @description 是否正在加载中 + * @default false + */ + loading?: boolean; + /** + * @description 设置聊天内容打字动画 + * @default false + */ + typing?: boolean | { step?: number, interval?: number }; + /** + * @description 打字动画完成时触发 + */ + onTypingComplete?: () => void; +} + +export const BubbleProps: IBubbleProps = { + content: '', + placement: 'start', + loading: false, + typing: false, + shape: 'default', + variant: 'filled', + avatar: '', +}; + +export type { IBubbleProps }; diff --git a/copilot/Bubble/typing.sjs b/copilot/Bubble/typing.sjs new file mode 100644 index 000000000..be01c878e --- /dev/null +++ b/copilot/Bubble/typing.sjs @@ -0,0 +1 @@ +// 实现类似打字机的效果 diff --git a/copilot/Bubble/variable.less b/copilot/Bubble/variable.less new file mode 100644 index 000000000..8964a192a --- /dev/null +++ b/copilot/Bubble/variable.less @@ -0,0 +1,16 @@ +@import (reference) '../../src/style/variables.less'; +@import (reference) '../../src/style/themes/index.less'; + +// 背景色 +@bubble-background-color: var(--bubble-background-color, #F7F7F7); +@bubble-user-background-color: var(--bubble-user-background-color, #1677FF); + +// 边框颜色 +@bubble-outlined-color: var(--bubble-outlined-color, #EBEBEB); + +// 文本颜色 +@bubble-text-color: var(--bubble-text-color, rgba(0,0,0,0.88)); +@bubble-text-color-user: var(--bubble-text-color-user, #ffffff); + +// 头像尺寸 +@bubble-avatar-size: 64rpx; diff --git a/copilot/Conversations/index.axml b/copilot/Conversations/index.axml new file mode 100644 index 000000000..873bf4b25 --- /dev/null +++ b/copilot/Conversations/index.axml @@ -0,0 +1,44 @@ + + + + + + + + + + + + {{item.label}} + {{item.timestamp}} + + {{item.description}} + + + + + + + + + diff --git a/copilot/Conversations/index.en.md b/copilot/Conversations/index.en.md new file mode 100644 index 000000000..19811d160 --- /dev/null +++ b/copilot/Conversations/index.en.md @@ -0,0 +1,132 @@ +--- +nav: + path: /copilot +group: + title: General + order: 1 +toc: 'content' +--- + +# Conversations + +## Introduction + +In `index.json` Introducing Components in + +```json +"usingComponents": { +#if ALIPAY + "ant-conversations": "antd-mini/es/Conversations/index" +#endif +#if WECHAT + "ant-conversations": "antd-mini/Conversations/index" +#endif +} +``` + +## Code Sample + +### Basic use + +```xml + +``` + +```js +Page({ + data: { + items: [ + { key: '1', label: '给我推荐一首歌', description: '这是会话的部分富文本信息Description', icon: 'https://randomuser.me/api/portraits/thumb/women/4.jpg', timestamp: '10:23', disabled: false }, + { key: '2', label: '请根据图片里的提示,写一篇小学二年级的数学题目', description: '这首歌来自英国歌手艾德·希兰旋律轻快,歌曲写自上个世纪落日', icon: '', timestamp: '10:22', disabled: false }, + { key: '3', label: '禁用无法点击此条', description: '这是会话的部分富文本信息Description', icon: '', timestamp: '10:21', disabled: true }, + { key: '4', label: '菜单禁用无法滑动', description: '这是会话的部分富文本信息Description', icon: '', timestamp: '10:18', disabled: false, disabledMenu: true }, + { key: '5', label: '给我推荐一首歌', description: '这是会话的部分富文本信息Description', icon: '', timestamp: '09:11', disabled: false } + ], + menus: [ + { text: '编辑', bgColor: '#FFA91B', color: '#fff', width: 160 }, + { text: '删除', bgColor: '#FF2B00', color: '#fff', width: 160 } + ] + }, + handleItemTap(i) { + let item = i; +#if ALIPAY + my.showToast({ content: item.key }); +#endif +#if WECHAT + item = i.detail[0]; + wx.showToast({ title: item.key }); +#endif + }, + handleMenuItemTap(menuItem, item) { +#if ALIPAY + console.log(menuItem, item); + my.showToast({ content: `菜单${menuItem.index}_列表项${item.key}` }); +#endif +#if WECHAT + console.log(menuItem.detail[0], menuItem.detail[1]); + wx.showToast({ + title: `菜单${menuItem.detail[0].index}_列表项${menuItem.detail[1].key}`, + }); +#endif + }, +}); +``` + +### Demo Code + + + +## API + +### Conversations + +The following table describes the API properties for Conversations components: + +| Property | Description | Type | Default Value | +| -------------------------- | ------------ | ---------------------------------------------------------------------------------------- | ------ | +| className | Class Name | string | - | +| items | Session List | [Conversation](#conversation)[] | [] | +| menus | Session Action Menu | [MenuItem](#menuitem) [] | [] | +| #if ALIPAY onItemTap | Prompt for click callback | (item: [Conversation](#conversation),index:number) => void; | - | +| #if WECHAT binditemtap | Prompt for click callback | (item: [Conversation](#conversation),index:number) => void; | - | +| #if ALIPAY onMenuItemTap | Prompt for click callback | (item: {menuInfo:[MenuItem](#menuitem),itemInfo:[Conversation](#conversation)}) => void; | - | +| #if WECHAT bindmenuitemtap | Prompt for click callback | (item: {menuInfo:[MenuItem](#menuitem),itemInfo:[Conversation](#conversation)}) => void; | - | + +### Conversation + +| Property | Description | Type | Default Value | +| ------------ | ------------------------ | ------- | ------ | +| key | Unique identification to distinguish each session | string | - | +| label | Session Name | string | - | +| description | Description | string | - | +| icon | Session icon | string | - | +| timestamp | Session timestamp | string | - | +| disabled | Whether to disable click | boolean | false | +| disabledMenu | Whether the menu disables sliding | boolean | false | + +### MenuItem + +| Property | Description | Type | Default Value | +| ----------- | ----------------------------------------- | ------ | ------ | +| text | Button Text | string | - | +| width | Button width | number | 150 | +| bgColor | Button background color | string | - | +| color | Button Font Color | string | #fff | +| confirmText | Copy description for secondary confirmation; if blank, display `text` | string | - | + +### Slot + +| Slot Name | Description | +| -------- | -------------- | +| Default slot | List Item Content Slot | diff --git a/copilot/Conversations/index.json b/copilot/Conversations/index.json new file mode 100644 index 000000000..3eeb3cea8 --- /dev/null +++ b/copilot/Conversations/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "ant-swipe-action": "../../src/SwipeAction/index" + } +} diff --git a/copilot/Conversations/index.less b/copilot/Conversations/index.less new file mode 100644 index 000000000..6aba60cd5 --- /dev/null +++ b/copilot/Conversations/index.less @@ -0,0 +1,61 @@ +@import (reference) './variable.less'; + +@buttonPrefix: ant-copilot-conversations; + +.@{buttonPrefix} { + &-hover { + opacity: 0.6; + } + &-swipe-item { + height: @conversations-swipe-item-height; + &-view { + height: 100%; + width: @conversations-swipe-item-view-width; + padding: 24 * @rpx 32 * @rpx; + box-sizing: border-box; + display: flex; + align-items: center; + &-main { + flex-grow: 1; + overflow: hidden; + &-top { + display: flex; + align-items: center; + justify-content: space-between; + } + } + &-icon { + flex-shrink: 0; + width: 72 * @rpx; + height: 72 * @rpx; + border-radius: 50%; + margin-right: 24 * @rpx; + } + &-label { + font-size: 28 * @rpx; + line-height: 40 * @rpx; + height: 40 * @rpx; + color: @conversations-swipe-item-view-label-color; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + &-timestamp { + flex-shrink: 0; + margin-left: 28 * @rpx; + font-size: 28 * @rpx; + line-height: 44 * @rpx; + color: @conversations-swipe-item-view-timestamp-color; + } + &-desc { + font-size: 24 * @rpx; + line-height: 48 * @rpx; + height: 48 * @rpx; + color: @conversations-swipe-item-view-desc-color; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} diff --git a/copilot/Conversations/index.md b/copilot/Conversations/index.md new file mode 100644 index 000000000..c3cbbaad0 --- /dev/null +++ b/copilot/Conversations/index.md @@ -0,0 +1,132 @@ +--- +nav: + path: /copilot +group: + title: 通用 + order: 1 +toc: 'content' +--- + +# Conversations 管理会话 + +## 引入 + +在 `index.json` 中引入组件 + +```json +"usingComponents": { +#if ALIPAY + "ant-conversations": "antd-mini/es/Conversations/index" +#endif +#if WECHAT + "ant-conversations": "antd-mini/Conversations/index" +#endif +} +``` + +## 代码示例 + +### 基本使用 + +```xml + +``` + +```js +Page({ + data: { + items: [ + { key: '1', label: '给我推荐一首歌', description: '这是会话的部分富文本信息描述', icon: 'https://randomuser.me/api/portraits/thumb/women/4.jpg', timestamp: '10:23', disabled: false }, + { key: '2', label: '请根据图片里的提示,写一篇小学二年级的数学题目', description: '这首歌来自英国歌手艾德·希兰旋律轻快,歌曲写自上个世纪落日', icon: '', timestamp: '10:22', disabled: false }, + { key: '3', label: '禁用无法点击此条', description: '这是会话的部分富文本信息描述', icon: '', timestamp: '10:21', disabled: true }, + { key: '4', label: '菜单禁用无法滑动', description: '这是会话的部分富文本信息描述', icon: '', timestamp: '10:18', disabled: false, disabledMenu: true }, + { key: '5', label: '给我推荐一首歌', description: '这是会话的部分富文本信息描述', icon: '', timestamp: '09:11', disabled: false } + ], + menus: [ + { text: '编辑', bgColor: '#FFA91B', color: '#fff', width: 160 }, + { text: '删除', bgColor: '#FF2B00', color: '#fff', width: 160 } + ] + }, + handleItemTap(i) { + let item = i; +#if ALIPAY + my.showToast({ content: item.key }); +#endif +#if WECHAT + item = i.detail[0]; + wx.showToast({ title: item.key }); +#endif + }, + handleMenuItemTap(menuItem, item) { +#if ALIPAY + console.log(menuItem, item); + my.showToast({ content: `菜单${menuItem.index}_列表项${item.key}` }); +#endif +#if WECHAT + console.log(menuItem.detail[0], menuItem.detail[1]); + wx.showToast({ + title: `菜单${menuItem.detail[0].index}_列表项${menuItem.detail[1].key}`, + }); +#endif + }, +}); +``` + +### Demo 代码 + + + +## API + +### Conversations + +以下表格介绍了 Conversations 组件的 API 属性: + +| 属性 | 说明 | 类型 | 默认值 | +| -------------------------- | ------------ | ---------------------------------------------------------------------------------------- | ------ | +| className | 类名 | string | - | +| items | 会话列表 | [Conversation](#conversation)[] | [] | +| menus | 会话操作菜单 | [MenuItem](#menuitem) [] | [] | +| #if ALIPAY onItemTap | 提示点击回调 | (item: [Conversation](#conversation),index:number) => void; | - | +| #if WECHAT binditemtap | 提示点击回调 | (item: [Conversation](#conversation),index:number) => void; | - | +| #if ALIPAY onMenuItemTap | 提示点击回调 | (item: {menuInfo:[MenuItem](#menuitem),itemInfo:[Conversation](#conversation)}) => void; | - | +| #if WECHAT bindmenuitemtap | 提示点击回调 | (item: {menuInfo:[MenuItem](#menuitem),itemInfo:[Conversation](#conversation)}) => void; | - | + +### Conversation + +| 属性 | 说明 | 类型 | 默认值 | +| ------------ | ------------------------ | ------- | ------ | +| key | 唯一标识用于区分每个会话 | string | - | +| label | 会话名称 | string | - | +| description | 描述 | string | - | +| icon | 会话图标 | string | - | +| timestamp | 会话时间戳 | string | - | +| disabled | 是否禁用点击 | boolean | false | +| disabledMenu | 是否菜单禁用滑动 | boolean | false | + +### MenuItem + +| 属性 | 说明 | 类型 | 默认值 | +| ----------- | ----------------------------------------- | ------ | ------ | +| text | 按钮文字 | string | - | +| width | 按钮宽度 | number | 150 | +| bgColor | 按钮背景颜色 | string | - | +| color | 按钮字体颜色 | string | #fff | +| confirmText | 二次确认的文案描述;若为空,则展示 `text` | string | - | + +### 插槽 slot + +| 插槽名称 | 说明 | +| -------- | -------------- | +| 默认插槽 | 列表项内容插槽 | diff --git a/copilot/Conversations/index.ts b/copilot/Conversations/index.ts new file mode 100644 index 000000000..24f69877d --- /dev/null +++ b/copilot/Conversations/index.ts @@ -0,0 +1,42 @@ +import { + Component, + getValueFromProps, + triggerEventValues, +} from '../../src/_util/simply'; +import { ConversationsProps } from './props'; + +const maxTouchMove = 10; + +Component(ConversationsProps, { + onTouchStart(e) { + // 这里需要通过 Touch 来实现点击效果,因为微信 movable-view 中的内容都不支持Tap + const touch = e.touches[0]; + this.touchStartX = touch.clientX; + this.touchStartY = touch.clientY; + }, + onItemTap(e) { + const touchEndX = e.changedTouches[0].clientX; + const touchEndY = e.changedTouches[0].clientY; + const deltaX = Math.abs(touchEndX - this.touchStartX); + const deltaY = Math.abs(touchEndY - this.touchStartY); + if (deltaX < maxTouchMove && deltaY < maxTouchMove) { + const { item, index } = e.currentTarget.dataset; + triggerEventValues(this, 'itemTap', [item, index], e); + } + }, + onButtonTap(menu, e) { + const menus = getValueFromProps(this, 'menus'); + let menuInfo, itemInfo; + /// #if ALIPAY + const { item, index } = e.currentTarget.dataset; + menuInfo = { ...menus[menu.btnIdx], index: menu.btnIdx }; + itemInfo = { ...item, index }; + /// #endif + /// #if WECHAT + const { item: itemW, index: indexW } = menu.currentTarget.dataset; + menuInfo = { ...menus[menu.detail.btnIdx], index: menu.detail.btnIdx }; + itemInfo = { ...itemW, index: indexW }; + /// #endif + triggerEventValues(this, 'menuItemTap', [menuInfo, itemInfo], e); + }, +}); diff --git a/copilot/Conversations/props.ts b/copilot/Conversations/props.ts new file mode 100644 index 000000000..de51b8256 --- /dev/null +++ b/copilot/Conversations/props.ts @@ -0,0 +1,94 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { IBaseProps } from '../../src/_util/base'; + +interface Conversation { + /** + * @description 唯一标识用于区分每个会话 + */ + key: string; + /** + * @description 会话名称 + */ + label?: string; + /** + * @description 描述 + */ + description?: string; + /** + * @description 会话图标 + */ + icon?: string; + /** + * @description 会话时间戳 + */ + timestamp?: string; + /** + * @description 是否禁用点击 + */ + disabled?: boolean; + /** + * @description 是否菜单禁用滑动 + */ + disabledMenu?: boolean; +} + +interface MenuItem { + /** + * @description 按钮文字 + */ + text: string; + /** + * @description 按钮宽度 + * @default 150 + */ + width?: number; + /** + * @description 按钮背景颜色 + */ + bgColor?: string; + /** + * @description 按钮字体颜色 + * @default "#fff" + */ + color?: string; + /** + * @description 二次确认的文案描述;若为空,则展示 text + */ + confirmText?: string; +} + +export interface IConversationsProps extends IBaseProps { + /** + * @description 会话列表 + * @default [] + */ + items?: Conversation[]; + + /** + * @description 会话操作菜单 + * @default [] + */ + menus?: MenuItem[]; + + /** + * @description 点击会话项之后的回调 + */ + onItemTap?: (item: Conversation, index: number) => void; + + /** + * @description 点击菜单项之后的回调 + */ + onMenuItemTap?: (item: { + menuInfo: MenuItem; + itemInfo: Conversation; + }) => void; +} + +export const ConversationsProps: Partial = { + className: '', + style: '', + items: [], + menus: [], + onItemTap() {}, + onMenuItemTap() {}, +}; diff --git a/copilot/Conversations/variable.less b/copilot/Conversations/variable.less new file mode 100644 index 000000000..33ecc3ec3 --- /dev/null +++ b/copilot/Conversations/variable.less @@ -0,0 +1,26 @@ +@import (reference) '../../src/style/variables.less'; +@import (reference) '../../src/style/themes/index.less'; + +@conversations-swipe-item-height: var( + --conversations-swipe-item-height, + 140 * @rpx +); +@conversations-swipe-item-view-width: var( + --conversations-swipe-item-view-width, + calc(100% - 300rpx) +); + +@conversations-swipe-item-view-label-color: var( + --conversations-swipe-item-view-label-color, + rgba(0, 0, 0, 0.88) +); + +@conversations-swipe-item-view-timestamp-color: var( + --conversations-swipe-item-view-timestamp-color, + #848484 +); + +@conversations-swipe-item-view-desc-color: var( + --conversations-swipe-item-view-desc-color, + rgba(0, 0, 0, 0.45) +); diff --git a/copilot/Prompts/index.axml b/copilot/Prompts/index.axml new file mode 100644 index 000000000..bb2a6ed41 --- /dev/null +++ b/copilot/Prompts/index.axml @@ -0,0 +1,61 @@ + + + + + + + + {{ title }} + + + + + + + + + + + + + + {{ item.label }} + {{ item.description }} + + + + + + + + + + + + + + + {{item.label}} + + + + + + + + diff --git a/copilot/Prompts/index.en.md b/copilot/Prompts/index.en.md new file mode 100644 index 000000000..ca0765fa7 --- /dev/null +++ b/copilot/Prompts/index.en.md @@ -0,0 +1,215 @@ +--- +nav: + path: /copilot +group: + title: Wake + order: 2 +toc: 'content' +--- + +# Prompts + +It is used to allow users who are exposed to AI products for the first time to quickly understand what AI can do, and can clearly convey to users the scope of intent and expected functions that AI can achieve. The use of appropriate welcome recommend components can effectively reduce user learning costs, allowing users to quickly understand and start smoothly. + +## Introduction + +In `index.json` Introducing Components in + +```json +"usingComponents": { +#if ALIPAY + "ant-prompts": "antd-mini/es/Prompts/index" +#endif +#if WECHAT + "ant-prompts": "antd-mini/Prompts/index" +#endif +} +``` + +## Code Sample + +### Basic use + +```xml + +``` + +```js +Page({ + data: { + promptsTitle: '我可以帮您:', + baseList: [ + { + title: 'Ant Design X 全新升级了什么?', + }, + { + title: 'Ant Design X 组件资产内容', + }, + { + title: '快速实现安装和Introduction组件', + }, + ], + }, + onItemTap(item) { + let item = i; +#if ALIPAY + my.alert({ + content: `点击了 ${item.title || ''} ${item.content || ''}`, + }); +#endif +#if WECHAT + item = i.detail; + wx.showToast({ title: `点击了 ${item.title || ''} ${item.content || ''}` }); +#endif + }, +}); +``` + +### Tip with arrow + +```xml + +``` + +```js +Page({ + data: { + promptsTitle: '我可以帮您:', + arrowList: [ + { + showArrow: true, + title: '热门话题:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*wJ5aRpr2q1wAAAAAAAAAAAAADkx8AQ/original', + content: 'Ant Design X 全新升级', + }, + { + showArrow: true, + title: '组件查询:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*R9L1RLo4x58AAAAAAAAAAAAADkx8AQ/original', + content: '欢迎组件', + }, + { + showArrow: true, + title: '新手帮助:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*7eRqQ4JpdccAAAAAAAAAAAAADkx8AQ/original', + content: '如何实现快速安装和Introduction', + }, + ], + }, +}); +``` + +### Custom Style + +```xml + +``` + +```css +.customizeStyle .ant-copilot-prompts-list-item-content { + font-family: PingFangSC-Light; + font-size: 28rpx; + color: rgba(0, 0, 0, 0.45); + line-height: 44rpx; +} +``` + +### Custom Prompt Title + +```xml + + + Custom prompt title: + + +``` + +### Custom Prompt Item + +```xml + + + Custom prompt items :{{ props.item.title}} + + +``` + +### Landscape mode + +```xml + + + + Custom:{{ props.item.label}} + +``` + +### Demo Code + + + +## API + +### Prompts + +The following table describes the API properties for Prompts components: + +| Property | Description | Type | Default Value | +| ---------------------- | -------------------- | --------------------------------------------------------- | ------ | +| className | Class Name | string | - | +| items | Prompt Items | [PromptsItem](#promptsitem)[] | - | +| title | Prompt Title | string | - | +| vertical | Under horizontal layout, automatic line wrap | boolean | true | +| wrap | Class Name | boolean | false | +| #if ALIPAY onItemTap | Prompt for click callback | (item: [PromptsItem](#promptsitem),index:number) => void; | - | +| #if WECHAT binditemtap | Prompt for click callback | (item: [PromptsItem](#promptsitem),index:number) => void; | - | + +### PromptsItem + +| Property | Description | Type | Default Value | +| ----------- | ---------------------------------------- | ------- | ------ | +| key | Unique identification is used to distinguish each prompt item | string | - | +| icon | The icon image link in front supports incoming antd-icon. | string | - | +| label | The prompt label displays the main content of the prompt | string | - | +| description | Prompt description provides additional information | string | - | +| showArrow | Prompt whether to show the right arrow | boolean | - | + +### Slot + +| Slot Name | Description | +| ------------- | -------------- | +| prompts-title | Title of Prompt Item | +| prompts-item | Prompt Subkey | diff --git a/copilot/Prompts/index.json b/copilot/Prompts/index.json new file mode 100644 index 000000000..695b86193 --- /dev/null +++ b/copilot/Prompts/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "ant-icon": "../../src/Icon/index", + "ant-typography": "../../src/Typography" + } +} diff --git a/copilot/Prompts/index.less b/copilot/Prompts/index.less new file mode 100644 index 000000000..566e405b2 --- /dev/null +++ b/copilot/Prompts/index.less @@ -0,0 +1,111 @@ +@import (reference) './variable.less'; + +@buttonPrefix: ant-copilot-prompts; + +.@{buttonPrefix} { + &-title { + font-weight: 400; + font-size: @prompts-title-size; + color: @prompts-title-color; + line-height: @prompts-title-line-height; + padding-bottom: @prompts-title-padding-bottom; + } + &-vertical-list { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: @prompts-list-gap; + &-item { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + + border: 2rpx solid @prompts-item-border-color; + border-radius: 50vh; + padding: 20rpx 32rpx; + + gap: @prompts-item-gap; + + &-icon { + font-size: 32 * @rpx; + } + + &-image { + width: 40rpx; + height: 40rpx; + flex-shrink: 0; + } + + &-label { + font-weight: 500; + font-size: 28rpx; + color: @prompts-item-label-color; + line-height: @prompts-item-description-line-height; + text-wrap: nowrap; + } + + &-description { + font-weight: 400; + font-size: 28rpx; + color: @prompts-item-description-color; + line-height: @prompts-item-description-line-height; + } + + &-arrow { + font-size: 32rpx; + color: @prompts-item-arrow-color; + } + } + } + + &-horizontal-list { + box-sizing: border-box; + padding: 24 * @rpx 24 * @rpx 0 24 * @rpx; + display: flex; + &::-webkit-scrollbar { + display: none; + } + /// #if WECHAT + height: 106 * @rpx; + /// #endif + &.wrap { + flex-wrap: wrap; + /// #if WECHAT + height: unset; + /// #endif + } + &-item { + &:last-of-type { + margin-right: 0; + } + /// #if WECHAT + height: 58 * @rpx; + box-sizing: border-box; + /// #endif + flex-shrink: 0; + padding: 8 * @rpx 24 * @rpx; + border: 2 * @rpx solid @prompts-item-border-color; + border-radius: 30 * @rpx; + margin-right: 16 * @rpx; + display: flex; + align-items: center; + margin-bottom: 24rpx; + &-typography { + .ant-typography-icon-image { + width: 40 * @rpx; + height: 40 * @rpx; + } + .ant-typography-icon { + font-size: 32 * @rpx; + } + .ant-typography-text { + font-size: 30 * @rpx; + line-height: 42 * @rpx; + color: @prompts-item-label-color; + } + } + } + } +} diff --git a/copilot/Prompts/index.md b/copilot/Prompts/index.md new file mode 100644 index 000000000..8ac78b2e7 --- /dev/null +++ b/copilot/Prompts/index.md @@ -0,0 +1,215 @@ +--- +nav: + path: /copilot +group: + title: 唤醒 + order: 2 +toc: 'content' +--- + +# Prompts 提示列表 + +用于让首次接触 AI 产品的用户快速理解 AI 能做什么,可以清晰传达给用户 AI 可实现的意图范围和预期功能。使用合适的欢迎推荐组件,可以有效降低用户学习成本,让用户快速了解并顺利开始。 + +## 引入 + +在 `index.json` 中引入组件 + +```json +"usingComponents": { +#if ALIPAY + "ant-prompts": "antd-mini/es/Prompts/index" +#endif +#if WECHAT + "ant-prompts": "antd-mini/Prompts/index" +#endif +} +``` + +## 代码示例 + +### 基本使用 + +```xml + +``` + +```js +Page({ + data: { + promptsTitle: '我可以帮您:', + baseList: [ + { + title: 'Ant Design X 全新升级了什么?', + }, + { + title: 'Ant Design X 组件资产内容', + }, + { + title: '快速实现安装和引入组件', + }, + ], + }, + onItemTap(item) { + let item = i; +#if ALIPAY + my.alert({ + content: `点击了 ${item.title || ''} ${item.content || ''}`, + }); +#endif +#if WECHAT + item = i.detail; + wx.showToast({ title: `点击了 ${item.title || ''} ${item.content || ''}` }); +#endif + }, +}); +``` + +### 提示有箭头 + +```xml + +``` + +```js +Page({ + data: { + promptsTitle: '我可以帮您:', + arrowList: [ + { + showArrow: true, + title: '热门话题:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*wJ5aRpr2q1wAAAAAAAAAAAAADkx8AQ/original', + content: 'Ant Design X 全新升级', + }, + { + showArrow: true, + title: '组件查询:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*R9L1RLo4x58AAAAAAAAAAAAADkx8AQ/original', + content: '欢迎组件', + }, + { + showArrow: true, + title: '新手帮助:', + image: + 'https://mdn.alipayobjects.com/huamei_upibrs/afts/img/A*7eRqQ4JpdccAAAAAAAAAAAAADkx8AQ/original', + content: '如何实现快速安装和引入', + }, + ], + }, +}); +``` + +### 自定义样式 + +```xml + +``` + +```css +.customizeStyle .ant-copilot-prompts-list-item-content { + font-family: PingFangSC-Light; + font-size: 28rpx; + color: rgba(0, 0, 0, 0.45); + line-height: 44rpx; +} +``` + +### 自定义提示标题 + +```xml + + + 自定义提示标题: + + +``` + +### 自定义提示项 + +```xml + + + 自定义提示项:{{ props.item.title }} + + +``` + +### 横向模式 + +```xml + + + + 自定义:{{ props.item.label }} + +``` + +### Demo 代码 + + + +## API + +### Prompts + +以下表格介绍了 Prompts 组件的 API 属性: + +| 属性 | 说明 | 类型 | 默认值 | +| ---------------------- | -------------------- | --------------------------------------------------------- | ------ | +| className | 类名 | string | - | +| items | 提示列表 | [PromptsItem](#promptsitem)[] | - | +| title | 提示标题 | string | - | +| vertical | 横向布局下,自动换行 | boolean | true | +| wrap | 类名 | boolean | false | +| #if ALIPAY onItemTap | 提示点击回调 | (item: [PromptsItem](#promptsitem),index:number) => void; | - | +| #if WECHAT binditemtap | 提示点击回调 | (item: [PromptsItem](#promptsitem),index:number) => void; | - | + +### PromptsItem + +| 属性 | 说明 | 类型 | 默认值 | +| ----------- | ---------------------------------------- | ------- | ------ | +| key | 唯一标识用于区分每个提示项 | string | - | +| icon | 前面的 icon 图片链接,支持传入 antd-icon | string | - | +| label | 提示标签显示提示的主要内容 | string | - | +| description | 提示描述提供额外的信息 | string | - | +| showArrow | 提示是否展示右边的箭头 | boolean | - | + +### 插槽 slot + +| 插槽名称 | 说明 | +| ------------- | -------------- | +| prompts-title | 提示项的标题题 | +| prompts-item | 提示子项项 | diff --git a/copilot/Prompts/index.sjs.ts b/copilot/Prompts/index.sjs.ts new file mode 100644 index 000000000..45f21ca03 --- /dev/null +++ b/copilot/Prompts/index.sjs.ts @@ -0,0 +1,10 @@ +function isUrl(string) { + if (!string) return; + return !!( + string.indexOf('http://') === 0 || string.indexOf('https://') === 0 + ); +} + +export default { + isUrl, +}; diff --git a/copilot/Prompts/index.ts b/copilot/Prompts/index.ts new file mode 100644 index 000000000..808e0f61c --- /dev/null +++ b/copilot/Prompts/index.ts @@ -0,0 +1,9 @@ +import { Component, triggerEventValues } from '../../src/_util/simply'; +import { PromptsProps } from './props'; + +Component(PromptsProps, { + onItemTap(e) { + const { item, index } = e.currentTarget.dataset; + triggerEventValues(this, 'itemTap', [item, index], e); + }, +}); diff --git a/copilot/Prompts/props.ts b/copilot/Prompts/props.ts new file mode 100644 index 000000000..732450e35 --- /dev/null +++ b/copilot/Prompts/props.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { IBaseProps } from '../../src/_util/base'; + +interface IPromptsItem { + /** + * @description 唯一标识用于区分每个提示项 + */ + key: string; + /** + * @description 前面的icon图片链接 + */ + icon?: string; + /** + * @description 提示标签显示提示的主要内容 + */ + label?: string; + /** + * @description 提示描述提供额外的信息 + */ + description: string; + /** + * @description 提示是否展示右边的箭头 + */ + showArrow?: string; +} + +/** + * @description 按钮,用户只需单击一下即可执行操作并做出选择。 + * 常用于表单提交、界面跳转、模块引导点击。具体用法和小程序框架中 button 保持一致,在 button 基础上做了样式的封装。 + * 封装后的按钮可改变按钮形态、增加 loading,以及内置了几种不同样式的按钮。 + */ + +export interface IPromptsProps extends IBaseProps { + /** + * @description 提示列表 + * @default [] + */ + items?: IPromptsItem[]; + + /** + * @description 提示列表的标题 + * @default '' + */ + title?: string; + + /** + * @description 纵向布局 + * @default true + */ + vertical?: boolean; + + /** + * @description 横向布局下,自动换行 + * @default false + */ + wrap?: boolean; + + /** + * @description 点击选项之后的回调 + */ + onItemTap?: (item: IPromptsItem, index: number) => void; +} + +export const PromptsProps: Partial = { + items: [], + className: '', + title: '', + vertical: true, + wrap: false, + onItemTap() {}, +}; diff --git a/copilot/Prompts/variable.less b/copilot/Prompts/variable.less new file mode 100644 index 000000000..c8ab09e70 --- /dev/null +++ b/copilot/Prompts/variable.less @@ -0,0 +1,35 @@ +@import (reference) '../../src/style/variables.less'; +@import (reference) '../../src/style/themes/index.less'; + +/* size 提示列表标题行高 */ +@prompts-title-line-height: var(--prompts-title-line-height, @size-6); +/* size 提示列表标题行高 */ +@prompts-title-size: var(--prompts-title-size, @size-4); +/* color 提示列表标题颜色 */ +@prompts-title-color: var(--prompts-title-color, rgba(0, 0, 0, 0.25)); +/* size 提示列表标题行高 */ +@prompts-title-padding-bottom: var(--prompts-title-padding-bottom, @size-2); +/* size 列表的间距 */ +@prompts-list-gap: var(--prompts-list-gap, @size-3); + +/* size 提示项内容行高 */ +@prompts-item-description-line-height: var( + --prompts-item-description-line-height, + 44 * @rpx +); +/* size 提示子项内容的间距 */ +@prompts-item-gap: var(--prompts-item-gap, @size-2); +/* color 提示子项边框颜色 */ +@prompts-item-border-color: var( + --prompts-item-border-color, + rgba(0, 0, 0, 0.1) +); +/* color 提示子项标题颜色 */ +@prompts-item-label-color: var(--prompts-item-label-color, rgba(0, 0, 0, 0.88)); +/* color 提示子项内容颜色 */ +@prompts-item-description-color: var( + --prompts-item-description-color, + rgba(0, 0, 0, 0.88) +); +/* color 提示子项箭头颜色 */ +@prompts-item-arrow-color: var(--prompts-item-arrow-color, rgba(0, 0, 0, 0.45)); diff --git a/copilot/Sender/index.axml b/copilot/Sender/index.axml new file mode 100644 index 000000000..0720fa0fe --- /dev/null +++ b/copilot/Sender/index.axml @@ -0,0 +1,26 @@ + + + + + + + +