@yoka-ui/ui 1.1.0 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@Docs-yoka/exports.generated.md +53 -4
- package/LICENSE +21 -0
- package/dist/es/assets/image/skills.zip +0 -0
- package/dist/es/business/AiChat/aichat-logo.svg +1 -0
- package/dist/es/business/AiChat/index.js +20 -45
- package/dist/es/business/AiChat/index.js.map +2 -2
- package/dist/es/business/AiChat/index.module.less +24 -0
- package/dist/es/business/AiChat/useAiChat.js +41 -24
- package/dist/es/business/AiChat/useAiChat.js.map +2 -2
- package/dist/es/business/Editor/index.d.ts +2 -2
- package/dist/es/business/Editor/index.js.map +2 -2
- package/dist/es/business/Empty/index.d.ts +1 -1
- package/dist/es/business/Empty/index.js.map +1 -1
- package/dist/es/business/ModCommonFilter/index.d.ts +1 -0
- package/dist/es/business/ModCommonFilter/index.js.map +2 -2
- package/dist/es/business/YkLoginModule/index.d.ts +1 -0
- package/dist/es/business/YkLoginModule/index.js.map +2 -2
- package/dist/es/business/YkPorjectSelect/index.d.ts +3 -3
- package/dist/es/business/YkPorjectSelect/index.js +37 -51
- package/dist/es/business/YkPorjectSelect/index.js.map +2 -2
- package/dist/es/business/YkSqlEdit/index.d.ts +1 -0
- package/dist/es/business/YkSqlEdit/index.js.map +2 -2
- package/dist/es/components/Clock/index.d.ts +2 -2
- package/dist/es/components/Clock/index.js.map +2 -2
- package/dist/es/components/DebounceInput/index.d.ts +2 -2
- package/dist/es/components/DebounceInput/index.js.map +2 -2
- package/dist/es/components/MultipleSelect/index.d.ts +2 -2
- package/dist/es/components/MultipleSelect/index.js.map +2 -2
- package/dist/es/components/RefreshButton/index.d.ts +2 -2
- package/dist/es/components/RefreshButton/index.js.map +2 -2
- package/dist/es/components/SearchWithHistory/index.d.ts +1 -1
- package/dist/es/components/SearchWithHistory/index.js.map +1 -1
- package/dist/es/components/TextWithInput/index.d.ts +2 -5
- package/dist/es/components/TextWithInput/index.js.map +2 -2
- package/dist/es/components/TextWithToolTip/index.d.ts +2 -2
- package/dist/es/components/TextWithToolTip/index.js.map +2 -2
- package/dist/es/components/TreeTransfer/index.d.ts +1 -0
- package/dist/es/components/TreeTransfer/index.js.map +2 -2
- package/dist/es/index.d.ts +29 -0
- package/dist/es/index.js.map +2 -2
- package/dist/es/index.less +1 -0
- package/dist/lib/assets/image/skills.zip +0 -0
- package/dist/lib/business/AiChat/aichat-logo.svg +1 -0
- package/dist/lib/business/AiChat/index.js +20 -45
- package/dist/lib/business/AiChat/index.js.map +3 -3
- package/dist/lib/business/AiChat/index.module.less +24 -0
- package/dist/lib/business/AiChat/useAiChat.js +40 -22
- package/dist/lib/business/AiChat/useAiChat.js.map +2 -2
- package/dist/lib/business/Editor/index.d.ts +2 -2
- package/dist/lib/business/Editor/index.js.map +2 -2
- package/dist/lib/business/Empty/index.d.ts +1 -1
- package/dist/lib/business/Empty/index.js.map +1 -1
- package/dist/lib/business/ModCommonFilter/index.d.ts +1 -0
- package/dist/lib/business/ModCommonFilter/index.js.map +2 -2
- package/dist/lib/business/YkLoginModule/index.d.ts +1 -0
- package/dist/lib/business/YkLoginModule/index.js.map +2 -2
- package/dist/lib/business/YkPorjectSelect/index.d.ts +3 -3
- package/dist/lib/business/YkPorjectSelect/index.js +33 -27
- package/dist/lib/business/YkPorjectSelect/index.js.map +2 -2
- package/dist/lib/business/YkSqlEdit/index.d.ts +1 -0
- package/dist/lib/business/YkSqlEdit/index.js.map +2 -2
- package/dist/lib/components/Clock/index.d.ts +2 -2
- package/dist/lib/components/Clock/index.js.map +2 -2
- package/dist/lib/components/DebounceInput/index.d.ts +2 -2
- package/dist/lib/components/DebounceInput/index.js.map +2 -2
- package/dist/lib/components/MultipleSelect/index.d.ts +2 -2
- package/dist/lib/components/MultipleSelect/index.js.map +2 -2
- package/dist/lib/components/RefreshButton/index.d.ts +2 -2
- package/dist/lib/components/RefreshButton/index.js.map +2 -2
- package/dist/lib/components/SearchWithHistory/index.d.ts +1 -1
- package/dist/lib/components/SearchWithHistory/index.js.map +1 -1
- package/dist/lib/components/TextWithInput/index.d.ts +2 -5
- package/dist/lib/components/TextWithInput/index.js.map +2 -2
- package/dist/lib/components/TextWithToolTip/index.d.ts +2 -2
- package/dist/lib/components/TextWithToolTip/index.js.map +2 -2
- package/dist/lib/components/TreeTransfer/index.d.ts +1 -0
- package/dist/lib/components/TreeTransfer/index.js.map +2 -2
- package/dist/lib/index.d.ts +29 -0
- package/dist/lib/index.js.map +2 -2
- package/dist/lib/index.less +1 -0
- package/package.json +7 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/business/YkPorjectSelect/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { SearchOutlined } from '@ant-design/icons';\nimport type { InputRef } from 'antd';\nimport { ConfigProvider, Empty, Flex, Input, Popover, Rate, Tabs, Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport React, { type FC, useEffect, useMemo, useRef, useState } from 'react';\nimport { Scrollbars } from 'react-custom-scrollbars-2';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport iconProductDefault from './icon-product.png';\nimport styles from './index.module.less';\n\n/** 项目选项项 */\ntype OptionItem = {\n /** 项目名称 */\n label: string;\n /** 项目 ID */\n value: string | number;\n /** 项目图标 URL */\n icon: string;\n /** 是否已关注 */\n followed?: boolean;\n /** 关注排序索引 */\n follow_index?: number;\n /** 是否最近访问 */\n recent_visit?: boolean;\n /** 是否已关服 */\n closed?: boolean;\n};\n\n/** 项目选择器 Props */\ntype PageTypes = {\n /** 当前选中的项目 ID */\n value: string | number;\n /** 项目选项列表 */\n options: OptionItem[];\n /** 选中项目变化时的回调 */\n onChange: (value: string | number) => void;\n /** 关注/取消关注时的回调 */\n followedCallback?: (item: OptionItem, bool: boolean) => void;\n /** 弹层内自定义插槽,渲染在搜索框下方、关注项目上方 */\n slot?: React.ReactNode;\n /** 自定义触发元素,替代默认的「图标+名称+下拉箭头」展示 */\n customShow?: React.ReactNode;\n};\n\n/**\n * 项目选择器\n *\n * 支持搜索、关注/取消关注(最多6个)、在营/关服切换。\n * 弹层展开时自动聚焦搜索框,使用 preventScroll 避免页面滚动。\n *\n * @example\n * ```tsx\n * <YkPorjectSelect\n * value={projectId}\n * options={options}\n * onChange={setProjectId}\n * followedCallback={(item, followed) => api.follow(item.value, followed)}\n * />\n * ```\n */\nconst YkPorjectSelect: FC<PageTypes> = ({ value, options, onChange, followedCallback, slot, customShow }) => {\n const [open, setOpen] = useState(false);\n const [status, setStatus] = useState<string>('1');\n const [searchKey, setSearchKey] = useState('');\n const [pageHeight, setPageHeight] = useState<number>(1000);\n const [localOptions, setLocalOptions] = useState<OptionItem[]>(options);\n const searchInputRef = useRef<InputRef>(null);\n\n useEffect(() => {\n setLocalOptions(options);\n }, [options]);\n\n /** 弹层展开时聚焦搜索框,使用 preventScroll 避免浏览器滚动到顶部;延迟等待 Popover 内容挂载 */\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (open) {\n timer = setTimeout(() => {\n searchInputRef.current?.input?.focus({ preventScroll: true });\n }, 0);\n }\n return () => {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n };\n }, [open]);\n\n /** 在营/关服数量统计 */\n const stateCount = useMemo(() => {\n return localOptions.reduce(\n (acc, item) => {\n const statusClosed = item.closed ? 2 : 1;\n if (!acc[statusClosed]) {\n acc[statusClosed] = 0;\n }\n acc[statusClosed] += 1;\n return acc;\n },\n {} as { [key: string]: number },\n );\n }, [localOptions]);\n\n /** 在营/关服 Tab 配置 */\n const tabs = [\n { key: '1', label: `在营(${stateCount['1'] || 0})` },\n { key: '2', label: `关服(${stateCount['2'] || 0})` },\n ];\n\n /** 当前选中的项目(用于展示图标与名称) */\n const activeItem = useMemo(() => {\n return localOptions.find((item) => item.value === value);\n }, [localOptions, value]);\n\n /** 已关注的项目列表(顶部展示、用于计算关注数限制) */\n const followedOptions = useMemo(() => {\n return localOptions.filter((item) => item.followed);\n }, [localOptions]);\n\n // 切换在营/关服\n const handleTabChange = (tabKey: string) => {\n setStatus(tabKey);\n };\n\n // 点击关注或取消关注\n const handleStarChange = async (item: OptionItem, val: number) => {\n void followedCallback?.(item, val === 1);\n setLocalOptions((prev) =>\n prev.map((p) => {\n if (p.value === item.value) {\n return { ...p, followed: val === 1 };\n }\n return p;\n }),\n );\n };\n\n const handleResize = () => {\n const height = window.innerHeight;\n setPageHeight(height);\n };\n\n useEffect(() => {\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, []);\n\n /** 渲染单个项目行(名称、ID、关注星标) */\n const renderItem = (item: OptionItem) => {\n const isDisabled = !item.followed && followedOptions.length >= 6;\n return (\n <Flex justify='space-between' align='center'>\n <span className={styles.contentItemSpan} onClick={() => onChange(item.value)}>\n <img className={styles.contentItemSpanIcon} src={item?.icon ? item.icon : iconProductDefault} />\n <div className={styles.contentItemSpanDev}>\n <div className={styles.contentItemSpanProName}>\n <TextWithTooltip\n text={item.label}\n width='100%'\n maxWidth={175}\n arrow={true}\n color='#fff'\n styles={{ body: { color: '#333' } }}\n ></TextWithTooltip>\n {item.recent_visit ? <span className={styles.recentVisit}>最近访问</span> : null}\n </div>\n <div className={styles.contentItemSpanProId}>ID: {item.value}</div>\n </div>\n </span>\n <Tooltip\n title={isDisabled ? '最多关注6个项目' : item.followed ? '取消关注' : '关注'}\n color='#fff'\n placement='right'\n styles={{ body: { color: '#333' } }}\n >\n <label\n className={classNames(styles.contentItemStar, isDisabled && styles.contentItemStarDisabled)}\n onClick={() => {\n if (isDisabled) {\n return;\n }\n void handleStarChange(item, item.followed ? 0 : 1);\n }}\n >\n <ConfigProvider\n theme={{\n components: {\n Rate: {\n starBg: '#dcdbdb',\n },\n },\n }}\n >\n <Rate\n style={{\n fontSize: 14,\n color: isDisabled ? 'rgba(0, 0, 0, 0.06)' : '#FFB401',\n opacity: item.followed || followedOptions.length === 0 ? 1 : 0,\n }}\n value={item.followed ? 1 : 0}\n count={1}\n />\n </ConfigProvider>\n </label>\n </Tooltip>\n </Flex>\n );\n };\n\n /** 弹层内容:搜索、slot、关注区、Tab、项目列表 */\n const content = (\n <div className={styles.ykPorjectSelectContent}>\n <div className={styles.ykPorjectSelectHeader}>\n <Input\n ref={searchInputRef}\n className={styles.ykPorjectSelectSearchInput}\n prefix={<SearchOutlined style={{ color: '#999', marginRight: '10px' }} />}\n value={searchKey}\n onChange={(e) => setSearchKey(e.target.value)}\n allowClear\n placeholder='请输入项目ID/名称'\n />\n </div>\n {slot && <div className={styles.ykPorjectSelectSoltContainer}>{slot}</div>}\n <div className={styles.ykPorjectSelectFollowed}>\n <div className={styles.contentHeader}>关注项目</div>\n <div className={styles.ykPorjectSelectFollowedList}>\n {followedOptions.map((item) => (\n <Tooltip\n key={item.value}\n title={\n <>\n <div style={{ fontSize: 14, color: '#555555' }}>{item.label}</div>\n <div style={{ fontSize: 12, color: '#999' }}>ID: {item.value}</div>\n </>\n }\n color='#fff'\n placement='bottomLeft'\n styles={{ body: { color: '#333' } }}\n >\n <div key={item.value} className={styles.ykPorjectSelectFollowedItem}>\n <img className={styles.ykPorjectSelectFollowedItemIcon} src={item.icon} alt={item.label} />\n <div className={styles.closeIcon} onClick={() => void handleStarChange(item, 0)} />\n </div>\n </Tooltip>\n ))}\n </div>\n </div>\n <Tabs\n className={styles.ykPorjectSelectProductPanelTabs}\n centered\n onChange={handleTabChange}\n activeKey={status}\n items={tabs}\n ></Tabs>\n {localOptions.length === 0 ? (\n <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />\n ) : (\n <div className={styles.ykPorjectSelectListSection}>\n <div className={styles.contentHeader}>全部项目</div>\n <Scrollbars\n className={styles.ykPorjectSelectScrollBarsBox}\n autoHeight\n autoHide\n autoHeightMax={Math.min(450, pageHeight - 310)}\n renderThumbVertical={({ style, ...props }) => (\n <div {...props} style={{ ...style, background: '#EEEEEE', borderRadius: 3, width: 6 }} />\n )}\n >\n {localOptions\n .sort((a, b) => (b.recent_visit ? 1 : 0) - (a.recent_visit ? 1 : 0))\n .map((item) => (\n <div\n key={item.value}\n className={classNames(styles.contentItem, item.value === value ? styles.contentItemActive : '')}\n >\n {renderItem(item)}\n </div>\n ))}\n </Scrollbars>\n </div>\n )}\n </div>\n );\n\n return (\n <Popover\n className={styles.ykPorjectSelect}\n styles={{ body: { borderRadius: 4, padding: 0 } }}\n content={content}\n placement='bottom'\n trigger='click'\n open={open}\n onOpenChange={setOpen}\n getPopupContainer={(triggerNode: HTMLElement) => triggerNode.parentNode as HTMLElement}\n autoAdjustOverflow={false}\n >\n {customShow ? (\n customShow\n ) : (\n <div className={styles.ykPorjectSelectShow}>\n <img className={styles.ykPorjectSelectShowIcon} src={activeItem?.icon} alt={activeItem?.label} />\n <span className={styles.ykPorjectSelectShowText}>{activeItem?.label}</span>\n <i className='iconfont icon-xiala' />\n </div>\n )}\n </Popover>\n );\n};\n\nexport default YkPorjectSelect;\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { SearchOutlined } from '@ant-design/icons';\nimport type { InputRef } from 'antd';\nimport { ConfigProvider, Empty, Flex, Input, Popover, Rate, Tabs, Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport React, { type FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Scrollbars } from 'react-custom-scrollbars-2';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport iconProductDefault from './icon-product.png';\nimport styles from './index.module.less';\n\n/** 项目选项项 */\nexport type OptionItem = {\n /** 项目名称 */\n label: string;\n /** 项目 ID */\n value: string | number;\n /** 项目图标 URL */\n icon: string;\n /** 是否已关注 */\n followed?: boolean;\n /** 关注排序索引 */\n follow_index?: number;\n /** 是否最近访问 */\n recent_visit?: boolean;\n /** 是否已关服 */\n closed?: boolean;\n};\n\n/** 项目选择器 Props */\nexport type YkPorjectSelectProps = {\n /** 当前选中的项目 ID */\n value: string | number;\n /** 项目选项列表 */\n options: OptionItem[];\n /** 选中项目变化时的回调 */\n onChange: (value: string | number) => void;\n /** 关注/取消关注时的回调 */\n followedCallback?: (item: OptionItem, bool: boolean) => void;\n /** 弹层内自定义插槽,渲染在搜索框下方、关注项目上方 */\n slot?: React.ReactNode;\n /** 自定义触发元素,替代默认的「图标+名称+下拉箭头」展示 */\n customShow?: React.ReactNode;\n};\n\n/**\n * 项目选择器\n *\n * 支持搜索、关注/取消关注(最多6个)、在营/关服切换。\n * 弹层展开时自动聚焦搜索框,使用 preventScroll 避免页面滚动。\n *\n * @example\n * ```tsx\n * <YkPorjectSelect\n * value={projectId}\n * options={options}\n * onChange={setProjectId}\n * followedCallback={(item, followed) => api.follow(item.value, followed)}\n * />\n * ```\n */\nconst YkPorjectSelect: FC<YkPorjectSelectProps> = ({ value, options, onChange, followedCallback, slot, customShow }) => {\n const [open, setOpen] = useState(false);\n const [status, setStatus] = useState<string>('1');\n const [searchKey, setSearchKey] = useState('');\n const [pageHeight, setPageHeight] = useState<number>(typeof window !== 'undefined' ? window.innerHeight : 1000);\n const [localOptions, setLocalOptions] = useState<OptionItem[]>(options);\n const searchInputRef = useRef<InputRef>(null);\n\n useEffect(() => {\n setLocalOptions(options);\n }, [options]);\n\n /** 弹层展开时聚焦搜索框,使用 preventScroll 避免浏览器滚动到顶部;延迟等待 Popover 内容挂载 */\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (open) {\n timer = setTimeout(() => {\n searchInputRef.current?.input?.focus({ preventScroll: true });\n }, 0);\n }\n return () => {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n };\n }, [open]);\n\n /** 在营/关服数量统计 */\n const stateCount = useMemo(() => {\n return localOptions.reduce(\n (acc, item) => {\n const statusClosed = item.closed ? 2 : 1;\n if (!acc[statusClosed]) {\n acc[statusClosed] = 0;\n }\n acc[statusClosed] += 1;\n return acc;\n },\n {} as { [key: string]: number },\n );\n }, [localOptions]);\n\n /** 在营/关服 Tab 配置 */\n const tabs = [\n { key: '1', label: `在营(${stateCount['1'] || 0})` },\n { key: '2', label: `关服(${stateCount['2'] || 0})` },\n ];\n\n /** 当前选中的项目(用于展示图标与名称) */\n const activeItem = useMemo(() => {\n return localOptions.find((item) => item.value === value);\n }, [localOptions, value]);\n\n /** 已关注的项目列表(顶部展示、用于计算关注数限制) */\n const followedOptions = useMemo(() => {\n return localOptions.filter((item) => item.followed);\n }, [localOptions]);\n\n // 切换在营/关服\n const handleTabChange = (tabKey: string) => {\n setStatus(tabKey);\n };\n\n // 点击关注或取消关注\n const handleStarChange = (item: OptionItem, val: number) => {\n followedCallback?.(item, val === 1);\n setLocalOptions((prev) =>\n prev.map((p) => {\n if (p.value === item.value) {\n return { ...p, followed: val === 1 };\n }\n return p;\n }),\n );\n };\n\n const handleResize = useCallback(() => {\n setPageHeight(window.innerHeight);\n }, []);\n\n useEffect(() => {\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [handleResize]);\n\n /** 渲染单个项目行(名称、ID、关注星标) */\n const renderItem = (item: OptionItem) => {\n const isDisabled = !item.followed && followedOptions.length >= 6;\n return (\n <Flex justify='space-between' align='center'>\n <span className={styles.contentItemSpan} onClick={() => onChange(item.value)}>\n <img className={styles.contentItemSpanIcon} src={item?.icon ? item.icon : iconProductDefault} />\n <div className={styles.contentItemSpanDev}>\n <div className={styles.contentItemSpanProName}>\n <TextWithTooltip\n text={item.label}\n width='100%'\n maxWidth={175}\n arrow={true}\n color='#fff'\n styles={{ body: { color: '#333' } }}\n ></TextWithTooltip>\n {item.recent_visit ? <span className={styles.recentVisit}>最近访问</span> : null}\n </div>\n <div className={styles.contentItemSpanProId}>ID: {item.value}</div>\n </div>\n </span>\n <Tooltip\n title={isDisabled ? '最多关注6个项目' : item.followed ? '取消关注' : '关注'}\n color='#fff'\n placement='right'\n styles={{ body: { color: '#333' } }}\n >\n <label\n className={classNames(styles.contentItemStar, isDisabled && styles.contentItemStarDisabled)}\n onClick={() => {\n if (isDisabled) {\n return;\n }\n handleStarChange(item, item.followed ? 0 : 1);\n }}\n >\n <ConfigProvider\n theme={{\n components: {\n Rate: {\n starBg: '#dcdbdb',\n },\n },\n }}\n >\n <Rate\n style={{\n fontSize: 14,\n color: isDisabled ? 'rgba(0, 0, 0, 0.06)' : '#FFB401',\n opacity: item.followed || followedOptions.length === 0 ? 1 : 0,\n }}\n value={item.followed ? 1 : 0}\n count={1}\n />\n </ConfigProvider>\n </label>\n </Tooltip>\n </Flex>\n );\n };\n\n /** 弹层内容:搜索、slot、关注区、Tab、项目列表 */\n const content = (\n <div className={styles.ykPorjectSelectContent}>\n <div className={styles.ykPorjectSelectHeader}>\n <Input\n ref={searchInputRef}\n className={styles.ykPorjectSelectSearchInput}\n prefix={<SearchOutlined style={{ color: '#999', marginRight: '10px' }} />}\n value={searchKey}\n onChange={(e) => setSearchKey(e.target.value)}\n allowClear\n placeholder='请输入项目ID/名称'\n />\n </div>\n {slot && <div className={styles.ykPorjectSelectSoltContainer}>{slot}</div>}\n <div className={styles.ykPorjectSelectFollowed}>\n <div className={styles.contentHeader}>关注项目</div>\n <div className={styles.ykPorjectSelectFollowedList}>\n {followedOptions.map((item) => (\n <Tooltip\n key={item.value}\n title={\n <>\n <div style={{ fontSize: 14, color: '#555555' }}>{item.label}</div>\n <div style={{ fontSize: 12, color: '#999' }}>ID: {item.value}</div>\n </>\n }\n color='#fff'\n placement='bottomLeft'\n styles={{ body: { color: '#333' } }}\n >\n <div key={item.value} className={styles.ykPorjectSelectFollowedItem}>\n <img className={styles.ykPorjectSelectFollowedItemIcon} src={item.icon || iconProductDefault} alt={item.label} />\n <div className={styles.closeIcon} onClick={() => handleStarChange(item, 0)} />\n </div>\n </Tooltip>\n ))}\n </div>\n </div>\n <Tabs\n className={styles.ykPorjectSelectProductPanelTabs}\n centered\n onChange={handleTabChange}\n activeKey={status}\n items={tabs}\n ></Tabs>\n {(() => {\n /** 搜索过滤:匹配项目名称或 ID */\n const searched = searchKey\n ? localOptions.filter(\n (item) =>\n item.label.toLowerCase().includes(searchKey.toLowerCase()) ||\n String(item.value).includes(searchKey),\n )\n : localOptions;\n /** Tab 筛选:搜索时忽略 Tab 限制以展示所有匹配结果 */\n const filtered = searchKey\n ? searched\n : searched.filter((item) => (status === '1' ? !item.closed : item.closed));\n /** 按「最近访问」排序,使用展开运算符避免就地修改原数组 */\n const sorted = [...filtered].sort((a, b) => (b.recent_visit ? 1 : 0) - (a.recent_visit ? 1 : 0));\n\n return sorted.length === 0 ? (\n <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />\n ) : (\n <div className={styles.ykPorjectSelectListSection}>\n <div className={styles.contentHeader}>全部项目</div>\n <Scrollbars\n className={styles.ykPorjectSelectScrollBarsBox}\n autoHeight\n autoHide\n autoHeightMax={Math.min(450, pageHeight - 310)}\n renderThumbVertical={({ style, ...props }) => (\n <div {...props} style={{ ...style, background: '#EEEEEE', borderRadius: 3, width: 6 }} />\n )}\n >\n {sorted.map((item) => (\n <div\n key={item.value}\n className={classNames(styles.contentItem, item.value === value ? styles.contentItemActive : '')}\n >\n {renderItem(item)}\n </div>\n ))}\n </Scrollbars>\n </div>\n );\n })()}\n </div>\n );\n\n return (\n <Popover\n className={styles.ykPorjectSelect}\n styles={{ body: { borderRadius: 4, padding: 0 } }}\n content={content}\n placement='bottom'\n trigger='click'\n open={open}\n onOpenChange={setOpen}\n getPopupContainer={(triggerNode: HTMLElement) => triggerNode.parentNode as HTMLElement}\n autoAdjustOverflow={false}\n >\n {customShow ? (\n customShow\n ) : (\n <div className={styles.ykPorjectSelectShow}>\n <img className={styles.ykPorjectSelectShowIcon} src={activeItem?.icon || iconProductDefault} alt={activeItem?.label} />\n <span className={styles.ykPorjectSelectShowText}>{activeItem?.label}</span>\n <i className='iconfont icon-xiala' />\n </div>\n )}\n </Popover>\n );\n};\n\nexport default YkPorjectSelect;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { type SqlDialectType } from './sql-language';
|
|
4
|
+
export type { SqlDialectType } from './sql-language';
|
|
4
5
|
export type YkSqlEditProps = {
|
|
5
6
|
value: string;
|
|
6
7
|
previewValue?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/business/YkSqlEdit/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n autocompletion,\n type Completion,\n type CompletionContext,\n type CompletionResult,\n} from '@codemirror/autocomplete';\nimport { MySQL, sql } from '@codemirror/lang-sql';\nimport { Decoration, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\nimport CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror';\nimport React from 'react';\nimport customStyles from './code-mirror-custom.module.less';\nimport { type SqlDialectType, sqlFunctions, sqlKeywords } from './sql-language';\n\nexport type YkSqlEditProps = {\n value: string;\n previewValue?: string;\n preview?: boolean;\n placeholder?: string;\n height?: string;\n width?: string;\n onChange: (value: string) => void;\n\n datasourceType?: SqlDialectType;\n keywordOptions?: {\n keywords: string[];\n functions: string[];\n };\n dataTestId?: string;\n};\n\n// 动态参数高亮插件\nfunction dynamicParamHighlighter() {\n return ViewPlugin.fromClass(\n class {\n decorations;\n constructor(view: EditorView) {\n this.decorations = this.buildDecorations(view);\n }\n update(update: ViewUpdate) {\n if (update.docChanged || update.viewportChanged) {\n this.decorations = this.buildDecorations(update.view);\n }\n }\n buildDecorations(view: EditorView) {\n const widgets = [];\n const regex = /\\{\\{.*?\\}\\}/g;\n for (const { from, to } of view.visibleRanges) {\n const text = view.state.doc.sliceString(from, to);\n let match;\n while ((match = regex.exec(text)) !== null) {\n const start = from + match.index;\n const end = start + match[0].length;\n widgets.push(Decoration.mark({ class: 'cm-dynamic-param' }).range(start, end));\n }\n }\n return Decoration.set(widgets, true);\n }\n },\n {\n decorations: (v) => v.decorations,\n },\n );\n}\n\nfunction customKeywordHighlighter(allKeywords: string[]) {\n return ViewPlugin.fromClass(\n class {\n decorations;\n constructor(view: EditorView) {\n this.decorations = this.buildDecorations(view);\n }\n update(update: ViewUpdate) {\n if (update.docChanged || update.viewportChanged) {\n this.decorations = this.buildDecorations(update.view);\n }\n }\n buildDecorations(view: EditorView) {\n if (!allKeywords.length) return Decoration.none;\n\n const widgets = [];\n const regex = new RegExp(`\\\\b(${allKeywords.join('|')})\\\\b`, 'gi');\n\n for (const { from, to } of view.visibleRanges) {\n const text = view.state.doc.sliceString(from, to);\n let match;\n while ((match = regex.exec(text)) !== null) {\n const start = from + match.index;\n const end = start + match[0].length;\n widgets.push(\n Decoration.mark({\n class: 'cm-custom-keyword',\n attributes: { 'data-keyword': match[0] },\n }).range(start, end),\n );\n }\n }\n return Decoration.set(widgets, true);\n }\n },\n {\n decorations: (v) => v.decorations,\n },\n );\n}\n\nfunction buildCompletions(keywordOptions?: YkSqlEditProps['keywordOptions']) {\n const keywords = keywordOptions?.keywords ?? [];\n const functions = keywordOptions?.functions ?? [];\n const allKeywords = [...new Set([...keywords, ...functions])];\n\n return (context: CompletionContext): CompletionResult | null => {\n const word = context.matchBefore(/\\w*/);\n if (word?.from === word?.to && !context.explicit) return null;\n\n const search = (word?.text ?? '').toLowerCase();\n const options: Completion[] = allKeywords\n .filter((kw) => kw.toLowerCase().includes(search))\n .map((kw) => ({\n label: kw,\n detail: keywords.includes(kw) ? '关键字' : functions.includes(kw) ? '函数' : '',\n boost: kw.toLowerCase().startsWith(search) ? 10 : 1,\n }))\n .sort((a, b) => (b.boost ?? 0) - (a.boost ?? 0));\n\n return {\n from: word?.from ?? 0,\n options: options.slice(0, 50),\n };\n };\n}\n\nconst YkSqlEdit = React.forwardRef<ReactCodeMirrorRef, YkSqlEditProps>((props, ref) => {\n const {\n value,\n previewValue,\n preview,\n placeholder = '请输入查询语句',\n height = '100%',\n width = '100%',\n onChange,\n datasourceType,\n keywordOptions,\n dataTestId = 'native-query-editor',\n } = props;\n\n const resolvedKeywordOptions = React.useMemo(() => {\n if (keywordOptions) return keywordOptions;\n if (!datasourceType) return { keywords: [], functions: [] };\n return {\n keywords: sqlKeywords(datasourceType),\n functions: sqlFunctions(datasourceType),\n };\n }, [datasourceType, keywordOptions]);\n\n const allKeywords = React.useMemo(() => {\n const keywords = resolvedKeywordOptions.keywords ?? [];\n const functions = resolvedKeywordOptions.functions ?? [];\n return [...new Set([...keywords, ...functions])];\n }, [resolvedKeywordOptions]);\n\n const customCompletions = React.useMemo(() => buildCompletions(resolvedKeywordOptions), [resolvedKeywordOptions]);\n\n return (\n <CodeMirror\n ref={ref}\n value={preview ? (previewValue ?? value) : value}\n className={`${customStyles.codeMirrorCustom} ${preview ? customStyles.disabled : ''}`}\n data-testid={dataTestId}\n placeholder={placeholder}\n height={height}\n width={width}\n editable={!preview}\n autoCorrect='off'\n extensions={[\n sql({ dialect: MySQL }),\n dynamicParamHighlighter(),\n customKeywordHighlighter(allKeywords),\n autocompletion({\n override: [customCompletions],\n }),\n ]}\n onChange={(next) => onChange(next)}\n basicSetup={{\n closeBrackets: true,\n autocompletion: true,\n bracketMatching: true,\n highlightSpecialChars: true,\n searchKeymap: true,\n history: true,\n historyKeymap: true,\n drawSelection: true,\n syntaxHighlighting: true,\n defaultKeymap: true,\n dropCursor: true,\n highlightSelectionMatches: true,\n completionKeymap: true,\n highlightActiveLineGutter: true,\n }}\n />\n );\n});\n\nYkSqlEdit.displayName = 'YkSqlEdit';\n\nexport default YkSqlEdit;\n"],
|
|
5
|
-
"mappings": ";AAAA;AAAA,EACE;AAAA,OAIK;AACP,SAAS,OAAO,WAAW;AAC3B,SAAS,YAA6B,kBAAmC;AACzE,OAAO,gBAA6C;AACpD,OAAO,WAAW;AAClB,OAAO,kBAAkB;AACzB,SAA8B,cAAc,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import {\n autocompletion,\n type Completion,\n type CompletionContext,\n type CompletionResult,\n} from '@codemirror/autocomplete';\nimport { MySQL, sql } from '@codemirror/lang-sql';\nimport { Decoration, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\nimport CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror';\nimport React from 'react';\nimport customStyles from './code-mirror-custom.module.less';\nimport { type SqlDialectType, sqlFunctions, sqlKeywords } from './sql-language';\n\nexport type { SqlDialectType } from './sql-language';\n\nexport type YkSqlEditProps = {\n value: string;\n previewValue?: string;\n preview?: boolean;\n placeholder?: string;\n height?: string;\n width?: string;\n onChange: (value: string) => void;\n\n datasourceType?: SqlDialectType;\n keywordOptions?: {\n keywords: string[];\n functions: string[];\n };\n dataTestId?: string;\n};\n\n// 动态参数高亮插件\nfunction dynamicParamHighlighter() {\n return ViewPlugin.fromClass(\n class {\n decorations;\n constructor(view: EditorView) {\n this.decorations = this.buildDecorations(view);\n }\n update(update: ViewUpdate) {\n if (update.docChanged || update.viewportChanged) {\n this.decorations = this.buildDecorations(update.view);\n }\n }\n buildDecorations(view: EditorView) {\n const widgets = [];\n const regex = /\\{\\{.*?\\}\\}/g;\n for (const { from, to } of view.visibleRanges) {\n const text = view.state.doc.sliceString(from, to);\n let match;\n while ((match = regex.exec(text)) !== null) {\n const start = from + match.index;\n const end = start + match[0].length;\n widgets.push(Decoration.mark({ class: 'cm-dynamic-param' }).range(start, end));\n }\n }\n return Decoration.set(widgets, true);\n }\n },\n {\n decorations: (v) => v.decorations,\n },\n );\n}\n\nfunction customKeywordHighlighter(allKeywords: string[]) {\n return ViewPlugin.fromClass(\n class {\n decorations;\n constructor(view: EditorView) {\n this.decorations = this.buildDecorations(view);\n }\n update(update: ViewUpdate) {\n if (update.docChanged || update.viewportChanged) {\n this.decorations = this.buildDecorations(update.view);\n }\n }\n buildDecorations(view: EditorView) {\n if (!allKeywords.length) return Decoration.none;\n\n const widgets = [];\n const regex = new RegExp(`\\\\b(${allKeywords.join('|')})\\\\b`, 'gi');\n\n for (const { from, to } of view.visibleRanges) {\n const text = view.state.doc.sliceString(from, to);\n let match;\n while ((match = regex.exec(text)) !== null) {\n const start = from + match.index;\n const end = start + match[0].length;\n widgets.push(\n Decoration.mark({\n class: 'cm-custom-keyword',\n attributes: { 'data-keyword': match[0] },\n }).range(start, end),\n );\n }\n }\n return Decoration.set(widgets, true);\n }\n },\n {\n decorations: (v) => v.decorations,\n },\n );\n}\n\nfunction buildCompletions(keywordOptions?: YkSqlEditProps['keywordOptions']) {\n const keywords = keywordOptions?.keywords ?? [];\n const functions = keywordOptions?.functions ?? [];\n const allKeywords = [...new Set([...keywords, ...functions])];\n\n return (context: CompletionContext): CompletionResult | null => {\n const word = context.matchBefore(/\\w*/);\n if (word?.from === word?.to && !context.explicit) return null;\n\n const search = (word?.text ?? '').toLowerCase();\n const options: Completion[] = allKeywords\n .filter((kw) => kw.toLowerCase().includes(search))\n .map((kw) => ({\n label: kw,\n detail: keywords.includes(kw) ? '关键字' : functions.includes(kw) ? '函数' : '',\n boost: kw.toLowerCase().startsWith(search) ? 10 : 1,\n }))\n .sort((a, b) => (b.boost ?? 0) - (a.boost ?? 0));\n\n return {\n from: word?.from ?? 0,\n options: options.slice(0, 50),\n };\n };\n}\n\nconst YkSqlEdit = React.forwardRef<ReactCodeMirrorRef, YkSqlEditProps>((props, ref) => {\n const {\n value,\n previewValue,\n preview,\n placeholder = '请输入查询语句',\n height = '100%',\n width = '100%',\n onChange,\n datasourceType,\n keywordOptions,\n dataTestId = 'native-query-editor',\n } = props;\n\n const resolvedKeywordOptions = React.useMemo(() => {\n if (keywordOptions) return keywordOptions;\n if (!datasourceType) return { keywords: [], functions: [] };\n return {\n keywords: sqlKeywords(datasourceType),\n functions: sqlFunctions(datasourceType),\n };\n }, [datasourceType, keywordOptions]);\n\n const allKeywords = React.useMemo(() => {\n const keywords = resolvedKeywordOptions.keywords ?? [];\n const functions = resolvedKeywordOptions.functions ?? [];\n return [...new Set([...keywords, ...functions])];\n }, [resolvedKeywordOptions]);\n\n const customCompletions = React.useMemo(() => buildCompletions(resolvedKeywordOptions), [resolvedKeywordOptions]);\n\n return (\n <CodeMirror\n ref={ref}\n value={preview ? (previewValue ?? value) : value}\n className={`${customStyles.codeMirrorCustom} ${preview ? customStyles.disabled : ''}`}\n data-testid={dataTestId}\n placeholder={placeholder}\n height={height}\n width={width}\n editable={!preview}\n autoCorrect='off'\n extensions={[\n sql({ dialect: MySQL }),\n dynamicParamHighlighter(),\n customKeywordHighlighter(allKeywords),\n autocompletion({\n override: [customCompletions],\n }),\n ]}\n onChange={(next) => onChange(next)}\n basicSetup={{\n closeBrackets: true,\n autocompletion: true,\n bracketMatching: true,\n highlightSpecialChars: true,\n searchKeymap: true,\n history: true,\n historyKeymap: true,\n drawSelection: true,\n syntaxHighlighting: true,\n defaultKeymap: true,\n dropCursor: true,\n highlightSelectionMatches: true,\n completionKeymap: true,\n highlightActiveLineGutter: true,\n }}\n />\n );\n});\n\nYkSqlEdit.displayName = 'YkSqlEdit';\n\nexport default YkSqlEdit;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["_a", "_b"]
|
|
7
7
|
}
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* 倒计时时钟组件的 Props
|
|
4
4
|
*/
|
|
5
|
-
type
|
|
5
|
+
export type ClockProps = {
|
|
6
6
|
/** 根节点类名,可选,用于样式覆盖 */
|
|
7
7
|
className?: string;
|
|
8
8
|
/** 秒数后的文案,默认「秒后更新数据」,展示为「{time} {text}」 */
|
|
@@ -15,5 +15,5 @@ type props = {
|
|
|
15
15
|
/**
|
|
16
16
|
* 倒计时时钟:展示距下一整分钟的剩余秒数,带环形进度条,剩 1 秒时调用 cellBack。
|
|
17
17
|
*/
|
|
18
|
-
declare const Clock: React.FC<
|
|
18
|
+
declare const Clock: React.FC<ClockProps>;
|
|
19
19
|
export default Clock;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/Clock/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import classNames from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\n\n/**\n * 倒计时时钟组件的 Props\n */\
|
|
5
|
-
"mappings": ";AAAA,OAAO,gBAAgB;AACvB,OAAO,SAAS,WAAW,QAAQ,gBAAgB;AAmBnD,IAAM,
|
|
4
|
+
"sourcesContent": ["import classNames from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\n\n/**\n * 倒计时时钟组件的 Props\n */\nexport type ClockProps = {\n /** 根节点类名,可选,用于样式覆盖 */\n className?: string;\n /** 秒数后的文案,默认「秒后更新数据」,展示为「{time} {text}」 */\n text?: string;\n /** 是否隐藏文案,默认 false */\n hideText?: boolean;\n /** 剩余 1 秒时触发的回调,常用于刷新数据 */\n cellBack: () => void;\n};\n\n/**\n * 倒计时时钟:展示距下一整分钟的剩余秒数,带环形进度条,剩 1 秒时调用 cellBack。\n */\nconst Clock: React.FC<ClockProps> = ({ className, text = '秒后更新数据', hideText = false, cellBack }) => {\n /** 计算距下一整分钟的秒数:60 - 当前秒数 */\n const formatTime = (date: Date) => {\n return 60 - date.getSeconds();\n };\n\n /** 当前剩余秒数,每秒更新 */\n const [time, setTime] = useState(formatTime(new Date()));\n const cellBackRef = useRef(cellBack);\n\n /** 保证 interval 内始终调用到最新的 cellBack,避免闭包旧引用 */\n useEffect(() => {\n cellBackRef.current = cellBack;\n }, [cellBack]);\n\n /** 每秒更新剩余秒数,并在剩 1 秒时执行 cellBack */\n useEffect(() => {\n const timer = setInterval(() => {\n const newTime = formatTime(new Date());\n setTime(newTime);\n\n if (newTime === 1) {\n cellBackRef.current();\n }\n }, 1000);\n\n return () => {\n clearInterval(timer);\n };\n }, []);\n\n /** 进度条百分比(0~100),时间越少进度越少 */\n const progress = (time / 60) * 100;\n const radius = 16 / 2;\n const circumference = 2 * Math.PI * radius;\n\n return (\n <div\n className={classNames('op-clock', className)}\n style={{ display: 'flex', alignItems: 'baseline', marginLeft: 15 }}\n >\n <div className='op-clock-icon'>\n <svg style={{ width: 18, height: 18, transform: 'translateY(20%)' }}>\n <circle r={radius} cx='9' cy='9' stroke='#EDEFF4' strokeWidth='2' fill='transparent' />\n <circle\n r={radius}\n cx='9'\n cy='9'\n stroke='#65DAAA'\n strokeWidth='2'\n fill='transparent'\n strokeDasharray={circumference}\n strokeDashoffset={`${(circumference * (100 - progress)) / 100}`}\n transform='rotate(-90 9 9)'\n />\n </svg>\n </div>\n <div style={{ color: '#6b7981', fontSize: 12, marginLeft: 7 }}>{hideText ? '' : `${time} ${text}`}</div>\n </div>\n );\n};\n\nexport default Clock;\n"],
|
|
5
|
+
"mappings": ";AAAA,OAAO,gBAAgB;AACvB,OAAO,SAAS,WAAW,QAAQ,gBAAgB;AAmBnD,IAAM,QAA8B,CAAC,EAAE,WAAW,OAAO,UAAU,WAAW,OAAO,SAAS,MAAM;AAElG,QAAM,aAAa,CAAC,SAAe;AACjC,WAAO,KAAK,KAAK,WAAW;AAAA,EAC9B;AAGA,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,WAAW,oBAAI,KAAK,CAAC,CAAC;AACvD,QAAM,cAAc,OAAO,QAAQ;AAGnC,YAAU,MAAM;AACd,gBAAY,UAAU;AAAA,EACxB,GAAG,CAAC,QAAQ,CAAC;AAGb,YAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,YAAM,UAAU,WAAW,oBAAI,KAAK,CAAC;AACrC,cAAQ,OAAO;AAEf,UAAI,YAAY,GAAG;AACjB,oBAAY,QAAQ;AAAA,MACtB;AAAA,IACF,GAAG,GAAI;AAEP,WAAO,MAAM;AACX,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM,WAAY,OAAO,KAAM;AAC/B,QAAM,SAAS,KAAK;AACpB,QAAM,gBAAgB,IAAI,KAAK,KAAK;AAEpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,WAAW,YAAY,SAAS;AAAA,MAC3C,OAAO,EAAE,SAAS,QAAQ,YAAY,YAAY,YAAY,GAAG;AAAA;AAAA,IAEjE,oCAAC,SAAI,WAAU,mBACb,oCAAC,SAAI,OAAO,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,kBAAkB,KAChE,oCAAC,YAAO,GAAG,QAAQ,IAAG,KAAI,IAAG,KAAI,QAAO,WAAU,aAAY,KAAI,MAAK,eAAc,GACrF;AAAA,MAAC;AAAA;AAAA,QACC,GAAG;AAAA,QACH,IAAG;AAAA,QACH,IAAG;AAAA,QACH,QAAO;AAAA,QACP,aAAY;AAAA,QACZ,MAAK;AAAA,QACL,iBAAiB;AAAA,QACjB,kBAAkB,GAAI,iBAAiB,MAAM,YAAa;AAAA,QAC1D,WAAU;AAAA;AAAA,IACZ,CACF,CACF;AAAA,IACA,oCAAC,SAAI,OAAO,EAAE,OAAO,WAAW,UAAU,IAAI,YAAY,EAAE,KAAI,WAAW,KAAK,GAAG,QAAQ,MAAO;AAAA,EACpG;AAEJ;AAEA,IAAO,gBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
type
|
|
2
|
+
export type DebounceInputProps = {
|
|
3
3
|
noDebounced?: boolean;
|
|
4
4
|
currentValue: string | undefined;
|
|
5
5
|
placeholder?: string;
|
|
@@ -12,5 +12,5 @@ type PropsType = {
|
|
|
12
12
|
disabled?: boolean;
|
|
13
13
|
icon?: React.ReactNode;
|
|
14
14
|
};
|
|
15
|
-
declare const DebouncedInput: React.FC<
|
|
15
|
+
declare const DebouncedInput: React.FC<DebounceInputProps>;
|
|
16
16
|
export default DebouncedInput;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/DebounceInput/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * 组件名称:DebouncedInput\n * 组件描述:一个用于搜索的输入框组件,支持防抖\n * 组件使用场景:在需要搜索的场景中使用,如筛选、排序等\n * 组件参数说明:\n * - noDebounced: 是否不使用防抖\n * - currentValue: 当前值\n * - placeholder: 输入框提示文字\n * - onChange: 值改变时的回调\n * - delay: 防抖时间\n * - inputClass: 输入框样式类名\n * - allowClear: 是否允许清空\n * - style: 组件样式\n */\nimport { useDebounceFn } from 'ahooks';\nimport { Input } from 'antd';\nimport classNames from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\nimport styles from './index.module.less';\n\
|
|
5
|
-
"mappings": ";AAcA,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,OAAO,gBAAgB;AACvB,OAAO,SAAS,WAAW,QAAQ,gBAAgB;AACnD,OAAO,YAAY;AAenB,IAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * 组件名称:DebouncedInput\n * 组件描述:一个用于搜索的输入框组件,支持防抖\n * 组件使用场景:在需要搜索的场景中使用,如筛选、排序等\n * 组件参数说明:\n * - noDebounced: 是否不使用防抖\n * - currentValue: 当前值\n * - placeholder: 输入框提示文字\n * - onChange: 值改变时的回调\n * - delay: 防抖时间\n * - inputClass: 输入框样式类名\n * - allowClear: 是否允许清空\n * - style: 组件样式\n */\nimport { useDebounceFn } from 'ahooks';\nimport { Input } from 'antd';\nimport classNames from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\nimport styles from './index.module.less';\n\nexport type DebounceInputProps = {\n noDebounced?: boolean;\n currentValue: string | undefined;\n placeholder?: string;\n onChange: (value: string) => void;\n delay?: number;\n inputClass?: any;\n allowClear?: boolean;\n style?: React.CSSProperties;\n noPrefixIcon?: boolean;\n disabled?: boolean;\n icon?: React.ReactNode;\n};\nconst DebouncedInput: React.FC<DebounceInputProps> = ({\n noDebounced = false,\n currentValue = '',\n placeholder = '搜索',\n onChange = (value: string) => {},\n delay = 150,\n inputClass,\n allowClear = true,\n style = {},\n noPrefixIcon = false,\n disabled = false,\n icon,\n}) => {\n const [value, setValue] = useState(currentValue);\n const lock = useRef(false);\n const changeFlag = useRef(false);\n useEffect(() => {\n if (changeFlag.current) {\n changeFlag.current = false;\n return;\n }\n setValue(currentValue);\n }, [currentValue]);\n\n const { run } = useDebounceFn(\n () => {\n onChange(value);\n },\n { wait: delay },\n );\n function handleChange(e: any) {\n const type = e.type;\n if (type === 'compositionstart') {\n lock.current = true;\n return;\n }\n const value = e.target.value;\n setValue(value);\n if (e.type === 'compositionend') {\n lock.current = false;\n }\n if (!lock.current) {\n changeFlag.current = true;\n if (noDebounced) {\n onChange(value);\n } else {\n run();\n }\n }\n }\n return (\n <Input\n allowClear={allowClear}\n style={style}\n className={classNames(styles.search, inputClass)}\n placeholder={placeholder}\n prefix={\n !noPrefixIcon &&\n (icon || <i className='iconfont icon-sousuo' style={{ fontSize: 14, lineHeight: '28px', color: '#999' }} />)\n }\n value={value}\n onChange={handleChange}\n onCompositionStart={handleChange}\n onCompositionEnd={handleChange}\n disabled={disabled}\n />\n );\n};\n\nexport default DebouncedInput;\n"],
|
|
5
|
+
"mappings": ";AAcA,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,OAAO,gBAAgB;AACvB,OAAO,SAAS,WAAW,QAAQ,gBAAgB;AACnD,OAAO,YAAY;AAenB,IAAM,iBAA+C,CAAC;AAAA,EACpD,cAAc;AAAA,EACd,eAAe;AAAA,EACf,cAAc;AAAA,EACd,WAAW,CAAC,UAAkB;AAAA,EAAC;AAAA,EAC/B,QAAQ;AAAA,EACR;AAAA,EACA,aAAa;AAAA,EACb,QAAQ,CAAC;AAAA,EACT,eAAe;AAAA,EACf,WAAW;AAAA,EACX;AACF,MAAM;AACJ,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,OAAO,OAAO,KAAK;AACzB,QAAM,aAAa,OAAO,KAAK;AAC/B,YAAU,MAAM;AACd,QAAI,WAAW,SAAS;AACtB,iBAAW,UAAU;AACrB;AAAA,IACF;AACA,aAAS,YAAY;AAAA,EACvB,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,EAAE,IAAI,IAAI;AAAA,IACd,MAAM;AACJ,eAAS,KAAK;AAAA,IAChB;AAAA,IACA,EAAE,MAAM,MAAM;AAAA,EAChB;AACA,WAAS,aAAa,GAAQ;AAC5B,UAAM,OAAO,EAAE;AACf,QAAI,SAAS,oBAAoB;AAC/B,WAAK,UAAU;AACf;AAAA,IACF;AACA,UAAMA,SAAQ,EAAE,OAAO;AACvB,aAASA,MAAK;AACd,QAAI,EAAE,SAAS,kBAAkB;AAC/B,WAAK,UAAU;AAAA,IACjB;AACA,QAAI,CAAC,KAAK,SAAS;AACjB,iBAAW,UAAU;AACrB,UAAI,aAAa;AACf,iBAASA,MAAK;AAAA,MAChB,OAAO;AACL,YAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,WAAW,WAAW,OAAO,QAAQ,UAAU;AAAA,MAC/C;AAAA,MACA,QACE,CAAC,iBACA,QAAQ,oCAAC,OAAE,WAAU,wBAAuB,OAAO,EAAE,UAAU,IAAI,YAAY,QAAQ,OAAO,OAAO,GAAG;AAAA,MAE3G;AAAA,MACA,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB,kBAAkB;AAAA,MAClB;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,wBAAQ;",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { FC } from 'react';
|
|
16
16
|
import React from 'react';
|
|
17
|
-
type
|
|
17
|
+
export type MultipleSelectProps = {
|
|
18
18
|
name?: string;
|
|
19
19
|
value: (string | number)[];
|
|
20
20
|
onChange: (value: (string | number)[]) => void;
|
|
@@ -33,5 +33,5 @@ type PageTypes = {
|
|
|
33
33
|
noAllSelect?: boolean;
|
|
34
34
|
autoConfirm?: boolean;
|
|
35
35
|
};
|
|
36
|
-
declare const MultipleSelect: FC<
|
|
36
|
+
declare const MultipleSelect: FC<MultipleSelectProps>;
|
|
37
37
|
export default MultipleSelect;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/MultipleSelect/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * 组件名称:MultipleSelect\n * 组件描述:一个用于多选的组件,支持搜索和选择\n * 组件使用场景:在需要选择多个值的场景中使用,如筛选、排序等\n * 组件参数说明:\n * - name: 组件名称\n * - value: 选中的值\n * - onChange: 值改变时的回调\n * - options: 选项列表\n * - style: 组件样式\n * - popoverStyle: 弹窗样式\n * - icon: 组件图标\n * - autoConfirm: 是否自动确认,默认为true。如果为false,则显示确定和取消按钮,点击确定后触发onChange\n */\n\nimport { Button, Checkbox, ConfigProvider, Flex, message, Popover, Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport type { FC } from 'react';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { AutoSizer, List } from 'react-virtualized';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport DebounceInput from '../DebounceInput';\nimport styles from './index.module.less';\n\
|
|
5
|
-
"mappings": ";AAeA,SAAS,QAAQ,UAAU,gBAAgB,MAAM,SAAS,SAAS,eAAe;AAClF,OAAO,gBAAgB;AAEvB,OAAO,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AAC5D,SAAS,WAAW,YAAY;AAChC,OAAO,qBAAqB;AAC5B,OAAO,mBAAmB;AAC1B,OAAO,YAAY;AAkBnB,IAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * 组件名称:MultipleSelect\n * 组件描述:一个用于多选的组件,支持搜索和选择\n * 组件使用场景:在需要选择多个值的场景中使用,如筛选、排序等\n * 组件参数说明:\n * - name: 组件名称\n * - value: 选中的值\n * - onChange: 值改变时的回调\n * - options: 选项列表\n * - style: 组件样式\n * - popoverStyle: 弹窗样式\n * - icon: 组件图标\n * - autoConfirm: 是否自动确认,默认为true。如果为false,则显示确定和取消按钮,点击确定后触发onChange\n */\n\nimport { Button, Checkbox, ConfigProvider, Flex, message, Popover, Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport type { FC } from 'react';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { AutoSizer, List } from 'react-virtualized';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport DebounceInput from '../DebounceInput';\nimport styles from './index.module.less';\n\nexport type MultipleSelectProps = {\n name?: string;\n value: (string | number)[];\n onChange: (value: (string | number)[]) => void;\n options: { label: string; value: string | number; isSelectAll?: boolean }[];\n style?: React.CSSProperties;\n popoverStyle?: React.CSSProperties;\n icon?: React.ReactNode;\n noXiala?: boolean;\n minCount?: number;\n disabledItems?: (string | number)[] | undefined; // 禁用选项\n disabledMessages?: string; // 禁用提示\n noAllSelect?: boolean; // 是否不显示全选选项\n autoConfirm?: boolean; // 是否自动确认,默认为true,如果为false则显示确定和取消按钮\n};\n\nconst MultipleSelect: FC<MultipleSelectProps> = ({\n name,\n value = [],\n onChange,\n options = [],\n style,\n popoverStyle,\n icon,\n noXiala = false,\n minCount = 0,\n disabledItems,\n disabledMessages,\n noAllSelect = false,\n autoConfirm = true,\n}) => {\n const [searchKey, setSearchKey] = useState('');\n const [open, setOpen] = useState(false);\n const [tempValue, setTempValue] = useState<(string | number)[]>([]); // 临时存储选中的值\n const listRef = useRef(null);\n\n useEffect(() => {\n setTimeout(() => {\n setSearchKey('');\n }, 200);\n }, [open]);\n\n // 当弹窗打开时,初始化临时值\n useEffect(() => {\n if (open) {\n setTempValue(value);\n }\n }, [open, value]);\n\n const handlePopOpen = (visible: boolean) => {\n setOpen(visible);\n };\n\n const itemClick = (v: string | number, checked: boolean) => {\n const currentValue = autoConfirm ? value : tempValue;\n\n if (checked && currentValue.filter((f) => f !== v).length < minCount) {\n message.warning(`至少选择${minCount}个`);\n return;\n }\n\n const newValue = checked ? currentValue?.filter((f) => f !== v) : [...currentValue, v];\n\n if (autoConfirm) {\n // 自动确认模式,直接触发onChange\n onChange(newValue);\n } else {\n // 非自动确认模式,更新临时值\n setTempValue(newValue);\n }\n };\n\n const filterOptions = useMemo(() => {\n const key = searchKey.trim().toUpperCase();\n const filtered = options.filter((m) => {\n if (key) {\n return m.label.toUpperCase().includes(key);\n }\n return true;\n });\n // 添加全选选项到列表开头\n return noAllSelect ? filtered : [{ label: '全选', value: '__selectAll__', isSelectAll: true }, ...filtered];\n }, [searchKey, options]);\n\n const rowRenderer = ({ index, key, style }: { index: number; key: string; style: React.CSSProperties }) => {\n const m = filterOptions[index];\n const currentValue = autoConfirm ? value : tempValue;\n\n // 处理全选选项\n if (m.isSelectAll) {\n return (\n <div key={key} style={style} className={styles.checkAll}>\n <ConfigProvider\n theme={{\n token: {\n controlInteractiveSize: 14,\n },\n }}\n >\n <Checkbox\n checked={currentValue.length === options.length}\n indeterminate={currentValue.length > 0 && currentValue.length < options.length}\n onChange={(e) => {\n const newValue = e.target.checked ? options.map((m) => m.value) : [];\n if (autoConfirm) {\n onChange(newValue);\n } else {\n setTempValue(newValue);\n }\n }}\n >\n 全选\n </Checkbox>\n </ConfigProvider>\n </div>\n );\n }\n\n // 处理普通选项\n return (\n <Tooltip\n title={disabledItems?.includes(m.value) ? disabledMessages || '该选项不能改变状态' : ''}\n color='#fff'\n styles={{ body: { color: '#333', fontSize: 12 } }}\n key={key}\n style={style}\n >\n <div\n style={style}\n className={classNames(styles.optionItem, currentValue?.includes(m.value) ? styles.active : null)}\n // 可以保留整行点击\n onClick={() => {\n if (disabledItems?.includes(m.value)) {\n return;\n }\n itemClick(m.value, currentValue?.includes(m.value));\n }}\n >\n <ConfigProvider\n theme={{\n token: {\n controlInteractiveSize: 14,\n },\n }}\n >\n <Checkbox\n disabled={disabledItems?.includes(m.value)}\n checked={currentValue?.includes(m.value)}\n onChange={() => {\n itemClick(m.value, currentValue?.includes(m.value));\n }}\n // 阻止点击Checkbox时冒泡到整行\n onClick={(e) => e.stopPropagation()}\n style={{ marginRight: 8 }}\n />\n </ConfigProvider>\n <TextWithTooltip\n text={m.label}\n style={{ color: disabledItems?.includes(m.value) ? '#999' : '#333' }}\n width='100%'\n maxWidth='100%'\n className={styles.label}\n highlight={searchKey}\n />\n </div>\n </Tooltip>\n );\n };\n\n const PopoverContent = (\n <div className={styles.popoverContent} style={popoverStyle}>\n <DebounceInput\n allowClear={true}\n inputClass={styles.searchInput}\n placeholder='搜索'\n currentValue={searchKey}\n onChange={(value) => {\n setSearchKey(value);\n }}\n ></DebounceInput>\n <div className={styles.popoverContentList}>\n {filterOptions.length === 0 ? (\n <div className={styles.emptyItem}>当前搜索条件无数据</div>\n ) : (\n <AutoSizer\n style={{\n background: '#fff',\n width: '100%',\n // maxHeight: 400,\n minHeight: 35,\n height: 'auto',\n }}\n >\n {({ width }: { width: number }) => {\n let height = 35;\n if (filterOptions.length <= 10) {\n height = 35 * filterOptions.length || 35;\n } else {\n height = 35 * 10;\n }\n return (\n <List\n ref={listRef}\n overscanRowCount={5}\n width={width}\n height={height}\n rowCount={filterOptions.length}\n rowHeight={35}\n rowRenderer={rowRenderer}\n />\n );\n }}\n </AutoSizer>\n )}\n </div>\n {!autoConfirm && (\n <div className={styles.popoverFooter}>\n <Button\n size='small'\n type='primary'\n onClick={() => {\n onChange(tempValue);\n setOpen(false);\n }}\n >\n 确定\n </Button>\n <Button\n size='small'\n // className={styles.cancelButton}\n onClick={() => {\n setTempValue(value);\n setOpen(false);\n }}\n >\n 取消\n </Button>\n </div>\n )}\n </div>\n );\n\n return (\n <Popover\n open={open}\n onOpenChange={handlePopOpen}\n fresh={true}\n title={false}\n arrow={false}\n content={PopoverContent}\n trigger='click'\n placement='bottomLeft'\n styles={{ body: { borderRadius: 2, padding: 0 } }}\n >\n <Button\n className={styles.button}\n style={style}\n icon={\n noXiala ? null : <i className='iconfont icon-xiala1' style={open ? { transform: 'rotate(180deg)' } : {}} />\n }\n iconPosition='end'\n onClick={() => {\n setOpen(!open);\n }}\n >\n <Flex justify='space-between' style={{ flex: 1, overflow: 'hidden' }}>\n {icon && icon}\n {name && <span className={styles.nameText}>{name}:</span>}\n <span className={styles.valueText} style={{ flex: 1 }}>\n {value?.length === 1 && options?.length === 1\n ? options?.find((m) => m.value === value?.[0])?.label\n : (value?.length || 0) + '/' + options?.length}\n </span>\n </Flex>\n </Button>\n </Popover>\n );\n};\n\nexport default MultipleSelect;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["style", "m", "value"]
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
type
|
|
2
|
+
export type RefreshButtonProps = {
|
|
3
3
|
handleRefresh: () => void;
|
|
4
4
|
text?: string;
|
|
5
5
|
style?: React.CSSProperties;
|
|
6
6
|
};
|
|
7
|
-
declare const RefreshButton: React.FC<
|
|
7
|
+
declare const RefreshButton: React.FC<RefreshButtonProps>;
|
|
8
8
|
export default RefreshButton;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/RefreshButton/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Button } from 'antd';\nimport React from 'react';\n\
|
|
5
|
-
"mappings": ";AAAA,SAAS,cAAc;AACvB,OAAO,WAAW;AAQlB,IAAM,
|
|
4
|
+
"sourcesContent": ["import { Button } from 'antd';\nimport React from 'react';\n\nexport type RefreshButtonProps = {\n handleRefresh: () => void;\n text?: string;\n style?: React.CSSProperties;\n};\n\nconst RefreshButton: React.FC<RefreshButtonProps> = ({ handleRefresh, text = '刷新', style }) => {\n return (\n <Button\n style={style}\n onClick={() => {\n handleRefresh();\n }}\n icon={<i className='iconfont icon-refresh' style={{ color: '#3B86F9', fontSize: 14 }} />}\n >\n {text}\n </Button>\n );\n};\n\nexport default RefreshButton;\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,cAAc;AACvB,OAAO,WAAW;AAQlB,IAAM,gBAA8C,CAAC,EAAE,eAAe,OAAO,MAAM,MAAM,MAAM;AAC7F,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAS,MAAM;AACb,sBAAc;AAAA,MAChB;AAAA,MACA,MAAM,oCAAC,OAAE,WAAU,yBAAwB,OAAO,EAAE,OAAO,WAAW,UAAU,GAAG,GAAG;AAAA;AAAA,IAErF;AAAA,EACH;AAEJ;AAEA,IAAO,wBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/SearchWithHistory/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { ClockCircleOutlined, SearchOutlined } from '@ant-design/icons';\nimport { useLocalStorageState } from 'ahooks';\nimport { ConfigProvider, Input, Popover } from 'antd';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport styles from './index.module.less';\n\
|
|
4
|
+
"sourcesContent": ["import { ClockCircleOutlined, SearchOutlined } from '@ant-design/icons';\nimport { useLocalStorageState } from 'ahooks';\nimport { ConfigProvider, Input, Popover } from 'antd';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport TextWithTooltip from '@/components/TextWithToolTip';\nimport styles from './index.module.less';\n\nexport type SearchWithHistoryProps = {\n /** localStorage 存储 key */\n localstorageKey: string;\n /** 可搜索的选项列表(含菜单路由、看板等) */\n list: { label: string; value: string; tag?: string }[];\n placeholder?: string;\n /** 选中项回调,value 为路由 path 或看板 id 等 */\n onClick: (value: string) => void;\n};\n\nconst INPUT_THEME = {\n token: { colorText: 'rgba(191, 199, 209, 1)' },\n components: {\n Input: {\n colorBgContainer: 'rgba(45, 56, 86, 1)',\n colorBorder: '#2D3856',\n borderRadius: 18,\n boxShadow: 'inset 0px -0.5px 0px 0px rgba(20, 31, 62, 0.5)',\n hoverBorderColor: 'rgba(0, 85, 255, 1)',\n paddingBlock: 6,\n },\n },\n};\n\n/**\n * 带历史记录的搜索弹层\n * - 无输入时展示最近搜索(localStorage 持久化)\n * - 有输入时展示匹配的菜单/看板列表\n */\nconst SearchWithHistory: React.FC<SearchWithHistoryProps> = ({\n localstorageKey = 'yk_search_history',\n list,\n placeholder = '搜索',\n onClick,\n}) => {\n const [open, setOpen] = useState(false);\n const [searchValue, setSearchValue] = useState('');\n const [historyList, setHistoryList] = useLocalStorageState<{ label: string; value: string; tag?: string }[]>(\n localstorageKey,\n { defaultValue: [] },\n );\n\n /** 选中项:执行回调并更新历史(同 value 则置顶) */\n const menuItemClick = useCallback(\n (item: { label: string; value: string; tag?: string }) => {\n onClick(item.value);\n setOpen(false);\n setHistoryList((prev = []) => [item, ...prev.filter((h) => h.value !== item.value)]);\n },\n [onClick, setHistoryList],\n );\n\n /** 清空历史 */\n const clearHistory = useCallback(() => {\n setOpen(false);\n setTimeout(() => setHistoryList([]), 100);\n }, [setHistoryList]);\n\n /** 匹配的选项列表(按 label 包含搜索词过滤) */\n const menuList = useMemo(() => list.filter((item) => item.label.includes(searchValue)), [list, searchValue]);\n\n /** 最近搜索列表 DOM */\n const historyContent = useMemo(\n () => (\n <div>\n <div className={styles.historyHeader}>\n <ClockCircleOutlined style={{ marginRight: 10, transform: 'translateY(-1px)' }} />\n <span>最近搜索</span>\n <i className='iconfont icon-lajitong' onClick={clearHistory} />\n </div>\n <div className={styles.historyList}>\n {historyList.map((item) => (\n <div className={styles.item} key={item.value} onClick={() => menuItemClick(item)}>\n <TextWithTooltip width={160} text={item.label} />\n {item.tag && <div className={styles.tag}>{item.tag}</div>}\n </div>\n ))}\n </div>\n </div>\n ),\n [historyList, clearHistory, menuItemClick],\n );\n\n /** 搜索结果列表 DOM */\n const listContent = useMemo(\n () => (\n <div>\n {menuList.map((item) => (\n <div key={item.value} className={styles.item} onClick={() => menuItemClick(item)}>\n <TextWithTooltip width={160} text={item.label} highlight={searchValue} />\n {item.tag && <div className={styles.tag}>{item.tag}</div>}\n </div>\n ))}\n </div>\n ),\n [menuList, searchValue, menuItemClick],\n );\n\n /**\n * 弹层内容:根据输入与历史决定\n * 1. 无历史 → null(不展示弹层)\n * 2. 有历史且无输入 → 最近搜索\n * 3. 有输入且无匹配 → 暂无搜索结果\n * 4. 有输入且匹配 → 菜单列表\n */\n const content = useMemo(() => {\n if (!searchValue) {\n if (historyList.length === 0) return null;\n return <div>{historyContent}</div>;\n }\n if (menuList.length === 0) return <div style={{ color: '#999' }}>暂无搜索结果</div>;\n return listContent;\n }, [searchValue, historyList, historyContent, menuList, listContent]);\n\n /** 内容为空时关闭弹层(如清空输入且无历史) */\n useEffect(() => {\n if (!content) setOpen(false);\n }, [content]);\n\n /** 有输入或有历史时显示弹层 */\n const shouldOpen = (value: string) => {\n if (value) return true;\n return historyList.length > 0;\n };\n\n return (\n <Popover\n open={open}\n onOpenChange={(nextOpen) => {\n if (nextOpen && !content) return; // 无内容时不打开,避免空白弹层\n setOpen(nextOpen);\n }}\n className={styles.popover}\n styles={{ body: { borderRadius: 4, width: 250, paddingRight: 0 } }}\n fresh\n title={false}\n trigger='click'\n placement='bottomLeft'\n arrow={false}\n content={content}\n getPopupContainer={(triggerNode: HTMLElement) => triggerNode.parentNode as HTMLElement}\n autoAdjustOverflow={false}\n >\n <ConfigProvider theme={INPUT_THEME}>\n <Input\n className={styles.inputContainer}\n classNames={{ input: styles.input }}\n placeholder={placeholder}\n prefix={<SearchOutlined />}\n value={searchValue}\n onFocus={() => historyList.length > 0 && setOpen(true)}\n onChange={(e) => {\n const val = e.target.value;\n setSearchValue(val);\n setOpen(shouldOpen(val));\n }}\n />\n </ConfigProvider>\n </Popover>\n );\n};\n\nexport default SearchWithHistory;\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
* 文本可编辑组件的 Props
|
|
4
|
-
*/
|
|
5
|
-
type PropsType = {
|
|
2
|
+
export type TextWithInputProps = {
|
|
6
3
|
/** 当前展示/提交的文本值,受控 */
|
|
7
4
|
value: string;
|
|
8
5
|
/** 确认编辑后的回调,参数为输入框内的新值 */
|
|
@@ -11,5 +8,5 @@ type PropsType = {
|
|
|
11
8
|
/**
|
|
12
9
|
* 文本 + 内联编辑:默认展示文案与编辑图标,点击图标切为输入框+确认/取消,确认时调用 callback。
|
|
13
10
|
*/
|
|
14
|
-
declare const TextWithInput: React.FC<
|
|
11
|
+
declare const TextWithInput: React.FC<TextWithInputProps>;
|
|
15
12
|
export default TextWithInput;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/TextWithInput/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { CheckOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';\nimport { Button, Input } from 'antd';\nimport React, { useState } from 'react';\n\
|
|
5
|
-
"mappings": ";AAAA,SAAS,eAAe,eAAe,oBAAoB;AAC3D,SAAS,QAAQ,aAAa;AAC9B,OAAO,SAAS,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { CheckOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';\nimport { Button, Input } from 'antd';\nimport React, { useState } from 'react';\n\nexport type TextWithInputProps = {\n /** 当前展示/提交的文本值,受控 */\n value: string;\n /** 确认编辑后的回调,参数为输入框内的新值 */\n callback: (value: string) => void;\n};\n\n/**\n * 文本 + 内联编辑:默认展示文案与编辑图标,点击图标切为输入框+确认/取消,确认时调用 callback。\n */\nconst TextWithInput: React.FC<TextWithInputProps> = ({ value, callback }) => {\n /** 是否处于编辑态(显示输入框与确认/取消按钮) */\n const [isEdit, setIsEdit] = useState(false);\n /** 编辑态下的输入内容,进入编辑时从 value 同步 */\n const [localValue, setLocalValue] = useState(value);\n\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>\n {isEdit ? (\n <>\n <Input value={localValue} style={{ borderRadius: 2 }} onChange={(e) => setLocalValue(e.target.value)} />\n <Button\n size='small'\n type='primary'\n style={{ width: 30, height: 30 }}\n onClick={() => {\n callback(localValue);\n setIsEdit(false);\n }}\n >\n <CheckOutlined />\n </Button>\n <Button size='small' style={{ width: 30, height: 30 }} onClick={() => setIsEdit(false)}>\n <CloseOutlined />\n </Button>\n </>\n ) : (\n <span>\n {value}\n <EditOutlined\n style={{ marginLeft: 10, cursor: 'pointer', color: '#333' }}\n onClick={() => {\n setLocalValue(value);\n setIsEdit(true);\n }}\n />\n </span>\n )}\n </div>\n );\n};\n\nexport default TextWithInput;\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,eAAe,eAAe,oBAAoB;AAC3D,SAAS,QAAQ,aAAa;AAC9B,OAAO,SAAS,gBAAgB;AAYhC,IAAM,gBAA8C,CAAC,EAAE,OAAO,SAAS,MAAM;AAE3E,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,SACE,oCAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,EAAE,KACzD,SACC,0DACE,oCAAC,SAAM,OAAO,YAAY,OAAO,EAAE,cAAc,EAAE,GAAG,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK,GAAG,GACtG;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,OAAO,EAAE,OAAO,IAAI,QAAQ,GAAG;AAAA,MAC/B,SAAS,MAAM;AACb,iBAAS,UAAU;AACnB,kBAAU,KAAK;AAAA,MACjB;AAAA;AAAA,IAEA,oCAAC,mBAAc;AAAA,EACjB,GACA,oCAAC,UAAO,MAAK,SAAQ,OAAO,EAAE,OAAO,IAAI,QAAQ,GAAG,GAAG,SAAS,MAAM,UAAU,KAAK,KACnF,oCAAC,mBAAc,CACjB,CACF,IAEA,oCAAC,cACE,OACD;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,YAAY,IAAI,QAAQ,WAAW,OAAO,OAAO;AAAA,MAC1D,SAAS,MAAM;AACb,sBAAc,KAAK;AACnB,kBAAU,IAAI;AAAA,MAChB;AAAA;AAAA,EACF,CACF,CAEJ;AAEJ;AAEA,IAAO,wBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type TooltipProps } from 'antd';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
type
|
|
3
|
+
export type TextWithTooltipProps = {
|
|
4
4
|
text: number | string | React.ReactNode;
|
|
5
5
|
placement?: 'top' | 'left' | 'right' | 'bottom';
|
|
6
6
|
zIndex?: number;
|
|
@@ -13,5 +13,5 @@ type PropsType = {
|
|
|
13
13
|
highlight?: string;
|
|
14
14
|
arrow?: boolean;
|
|
15
15
|
};
|
|
16
|
-
declare const TextWithTooltip: React.FC<
|
|
16
|
+
declare const TextWithTooltip: React.FC<TextWithTooltipProps>;
|
|
17
17
|
export default TextWithTooltip;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/TextWithToolTip/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Tooltip, type TooltipProps } from 'antd';\nimport React, { useRef, useState } from 'react';\n\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAkC;AAC3C,OAAO,SAAS,QAAQ,gBAAgB;AAgBxC,IAAM,
|
|
4
|
+
"sourcesContent": ["import { Tooltip, type TooltipProps } from 'antd';\nimport React, { useRef, useState } from 'react';\n\nexport type TextWithTooltipProps = {\n text: number | string | React.ReactNode;\n placement?: 'top' | 'left' | 'right' | 'bottom';\n zIndex?: number;\n color?: string;\n styles?: TooltipProps['styles'];\n width: number | string;\n maxWidth?: number | string;\n className?: string;\n style?: React.CSSProperties;\n highlight?: string; // 高亮的关键字\n arrow?: boolean;\n};\n\nconst TextWithTooltip: React.FC<TextWithTooltipProps> = ({\n text,\n placement = 'top',\n zIndex,\n width,\n maxWidth,\n className,\n style = {},\n color,\n styles,\n highlight = '',\n arrow = false,\n}) => {\n const [showTooltip, setShowTooltip] = useState(false);\n const textRef = useRef(null);\n\n const handleMouseEnter = () => {\n if (textRef.current) {\n const range = document.createRange();\n range.selectNodeContents(textRef.current);\n const rangeWidth = range.getBoundingClientRect().width;\n const actualWidth = (textRef.current as HTMLElement).getBoundingClientRect().width;\n setShowTooltip(rangeWidth > actualWidth);\n }\n };\n\n // 将text按照高亮关键字拆分成数组\n function splitString(str: string, delimiter: string) {\n const result = [];\n let match;\n const escapedDelimiter = delimiter.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n const regex = new RegExp(`(${escapedDelimiter})`, 'gi');\n let remaining = str; // 新建变量存储剩余字符串\n\n while ((match = regex.exec(remaining)) !== null) {\n const index = match.index;\n if (index !== 0) {\n result.push(remaining.slice(0, index));\n }\n result.push(match[1]);\n remaining = remaining.slice(index + match[1].length);\n regex.lastIndex = 0;\n }\n if (remaining.length > 0) {\n result.push(remaining);\n }\n return result;\n }\n\n // text是ReactNode时,不处理高亮\n const highlightedText = (text: number | string | React.ReactNode): React.ReactNode => {\n if (highlight && (typeof text === 'string' || typeof text === 'number')) {\n return splitString(text.toString(), highlight).map((part, index) => {\n if (part.toUpperCase() === highlight.toUpperCase()) {\n return (\n <span key={index} style={{ color: '#ffb401' }}>\n {part}\n </span>\n );\n } else {\n return <span key={index}>{part}</span>;\n }\n });\n } else {\n return text;\n }\n };\n\n // 处理宽度值,确保字符串数字能正确转换为带单位的 CSS 值\n const formatWidth = (value: number | string): number | string => {\n if (typeof value === 'string' && /^\\d+$/.test(value)) {\n // 如果是纯数字字符串,添加 px 单位\n return `${value}px`;\n }\n return value;\n };\n\n return (\n <Tooltip\n open={showTooltip}\n title={text}\n placement={placement}\n arrow={arrow}\n destroyOnHidden={true}\n color={color ? color : '#fff'}\n styles={styles ? styles : { body: { color: '#333', fontSize: 12 } }}\n {...(zIndex ? { zIndex } : {})}\n >\n <span\n ref={textRef}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={() => setShowTooltip(false)}\n className={className}\n style={{\n display: 'inline-block',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n verticalAlign: 'bottom',\n ...style,\n ...(maxWidth ? { maxWidth: formatWidth(maxWidth) } : { width: formatWidth(width) }),\n }}\n >\n {highlightedText(text)}\n </span>\n </Tooltip>\n );\n};\n\nexport default TextWithTooltip;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["text"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type TreeTransferProps } from './types';
|
|
3
|
+
export type { TreeTransferProps } from './types';
|
|
3
4
|
import './index.less';
|
|
4
5
|
declare function TreeTransfer<T>({ leftDataSource, rightDataSource, loading, leftTitle, rightTitle, pageSize, columns, customRender, searchFields, searchPlaceholder, onChange, }: TreeTransferProps<T>): React.JSX.Element;
|
|
5
6
|
export default TreeTransfer;
|