From 4d2b6e57ad8afac8e7c314f5e88de3abe952f99c Mon Sep 17 00:00:00 2001 From: luxiaotao1123 <t1341870251@163.com> Date: 星期二, 20 二月 2024 10:18:01 +0800 Subject: [PATCH] # --- zy-asrs-flow/src/components/IconSelector/style.less | 137 ++++++++ zy-asrs-flow/src/components/IconSelector/CopyableIcon.tsx | 47 ++ zy-asrs-flow/src/components/IconSelector/Category.tsx | 63 +++ zy-asrs-flow/src/pages/system/menu/components/edit.jsx | 38 ++ zy-asrs-flow/src/components/IconSelector/fields.ts | 223 +++++++++++++ zy-asrs-flow/src/components/IconSelector/themeIcons.tsx | 41 ++ zy-asrs-flow/src/components/IconSelector/IconPicSearcher.tsx | 233 ++++++++++++++ zy-asrs-flow/src/components/IconSelector/index.tsx | 142 ++++++++ 8 files changed, 919 insertions(+), 5 deletions(-) diff --git a/zy-asrs-flow/src/components/IconSelector/Category.tsx b/zy-asrs-flow/src/components/IconSelector/Category.tsx new file mode 100644 index 0000000..dd0e93f --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/Category.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import CopyableIcon from './CopyableIcon'; +import type { ThemeType } from './index'; +import type { CategoriesKeys } from './fields'; +import { useIntl } from '@umijs/max'; +import styles from './style.less'; + +interface CategoryProps { + title: CategoriesKeys; + icons: string[]; + theme: ThemeType; + newIcons: string[]; + onSelect: (type: string, name: string) => any; +} + +const Category: React.FC<CategoryProps> = props => { + + const { icons, title, newIcons, theme } = props; + const intl = useIntl(); + const [justCopied, setJustCopied] = React.useState<string | null>(null); + const copyId = React.useRef<NodeJS.Timeout | null>(null); + const onSelect = React.useCallback((type: string, text: string) => { + const { onSelect } = props; + if (onSelect) { + onSelect(type, text); + } + setJustCopied(type); + copyId.current = setTimeout(() => { + setJustCopied(null); + }, 2000); + }, []); + React.useEffect( + () => () => { + if (copyId.current) { + clearTimeout(copyId.current); + } + }, + [], + ); + + return ( + <div> + <h4>{intl.formatMessage({ + id: `app.docs.components.icon.category.${title}`, + defaultMessage: '淇℃伅', + })}</h4> + <ul className={styles.anticonsList}> + {icons.map(name => ( + <CopyableIcon + key={name} + name={name} + theme={theme} + isNew={newIcons.includes(name)} + justCopied={justCopied} + onSelect={onSelect} + /> + ))} + </ul> + </div> + ); +}; + +export default Category; diff --git a/zy-asrs-flow/src/components/IconSelector/CopyableIcon.tsx b/zy-asrs-flow/src/components/IconSelector/CopyableIcon.tsx new file mode 100644 index 0000000..371cba0 --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/CopyableIcon.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { Tooltip } from 'antd'; +import classNames from 'classnames'; +import * as AntdIcons from '@ant-design/icons'; +import type { ThemeType } from './index'; +import styles from './style.less'; + +const allIcons: { + [key: string]: any; +} = AntdIcons; + +export interface CopyableIconProps { + name: string; + isNew: boolean; + theme: ThemeType; + justCopied: string | null; + onSelect: (type: string, text: string) => any; +} + +const CopyableIcon: React.FC<CopyableIconProps> = ({ + name, + justCopied, + onSelect, + theme, +}) => { + const className = classNames({ + copied: justCopied === name, + [theme]: !!theme, + }); + return ( + <li className={className} + onClick={() => { + if (onSelect) { + onSelect(theme, name); + } + }}> + <Tooltip title={name}> + {React.createElement(allIcons[name], { className: styles.anticon })} + </Tooltip> + {/* <span className={styles.anticonClass}> + <Badge dot={isNew}>{name}</Badge> + </span> */} + </li> + ); +}; + +export default CopyableIcon; diff --git a/zy-asrs-flow/src/components/IconSelector/IconPicSearcher.tsx b/zy-asrs-flow/src/components/IconSelector/IconPicSearcher.tsx new file mode 100644 index 0000000..3a4cf01 --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/IconPicSearcher.tsx @@ -0,0 +1,233 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { Upload, Tooltip, Popover, Modal, Progress, Spin, Result } from 'antd'; +import * as AntdIcons from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import './style.less'; + +const allIcons: { [key: string]: any } = AntdIcons; + +const { Dragger } = Upload; +interface AntdIconClassifier { + load: () => void; + predict: (imgEl: HTMLImageElement) => void; +} +declare global { + interface Window { + antdIconClassifier: AntdIconClassifier; + } +} + +interface PicSearcherState { + loading: boolean; + modalOpen: boolean; + popoverVisible: boolean; + icons: iconObject[]; + fileList: any[]; + error: boolean; + modelLoaded: boolean; +} + +interface iconObject { + type: string; + score: number; +} + +const PicSearcher: React.FC = () => { + const intl = useIntl(); + const {formatMessage} = intl; + const [state, setState] = useState<PicSearcherState>({ + loading: false, + modalOpen: false, + popoverVisible: false, + icons: [], + fileList: [], + error: false, + modelLoaded: false, + }); + const predict = (imgEl: HTMLImageElement) => { + try { + let icons: any[] = window.antdIconClassifier.predict(imgEl); + if (gtag && icons.length) { + gtag('event', 'icon', { + event_category: 'search-by-image', + event_label: icons[0].className, + }); + } + icons = icons.map(i => ({ score: i.score, type: i.className.replace(/\s/g, '-') })); + setState(prev => ({ ...prev, loading: false, error: false, icons })); + } catch { + setState(prev => ({ ...prev, loading: false, error: true })); + } + }; + // eslint-disable-next-line class-methods-use-this + const toImage = (url: string) => + new Promise(resolve => { + const img = new Image(); + img.setAttribute('crossOrigin', 'anonymous'); + img.src = url; + img.onload = () => { + resolve(img); + }; + }); + + const uploadFile = useCallback((file: File) => { + setState(prev => ({ ...prev, loading: true })); + const reader = new FileReader(); + reader.onload = () => { + toImage(reader.result as string).then(predict); + setState(prev => ({ + ...prev, + fileList: [{ uid: 1, name: file.name, status: 'done', url: reader.result }], + })); + }; + reader.readAsDataURL(file); + }, []); + + const onPaste = useCallback((event: ClipboardEvent) => { + const items = event.clipboardData && event.clipboardData.items; + let file = null; + if (items && items.length) { + for (let i = 0; i < items.length; i++) { + if (items[i].type.includes('image')) { + file = items[i].getAsFile(); + break; + } + } + } + if (file) { + uploadFile(file); + } + }, []); + const toggleModal = useCallback(() => { + setState(prev => ({ + ...prev, + modalOpen: !prev.modalOpen, + popoverVisible: false, + fileList: [], + icons: [], + })); + if (!localStorage.getItem('disableIconTip')) { + localStorage.setItem('disableIconTip', 'true'); + } + }, []); + + useEffect(() => { + const script = document.createElement('script'); + script.onload = async () => { + await window.antdIconClassifier.load(); + setState(prev => ({ ...prev, modelLoaded: true })); + document.addEventListener('paste', onPaste); + }; + script.src = 'https://cdn.jsdelivr.net/gh/lewis617/antd-icon-classifier@0.0/dist/main.js'; + document.head.appendChild(script); + setState(prev => ({ ...prev, popoverVisible: !localStorage.getItem('disableIconTip') })); + return () => { + document.removeEventListener('paste', onPaste); + }; + }, []); + + return ( + <div className="iconPicSearcher"> + <Popover + content={formatMessage({id: 'app.docs.components.icon.pic-searcher.intro'})} + open={state.popoverVisible} + > + <AntdIcons.CameraOutlined className="icon-pic-btn" onClick={toggleModal} /> + </Popover> + <Modal + title={intl.formatMessage({ + id: 'app.docs.components.icon.pic-searcher.title', + defaultMessage: '淇℃伅', + })} + open={state.modalOpen} + onCancel={toggleModal} + footer={null} + > + {state.modelLoaded || ( + <Spin + spinning={!state.modelLoaded} + tip={formatMessage({ + id: 'app.docs.components.icon.pic-searcher.modelloading', + + })} + > + <div style={{ height: 100 }} /> + </Spin> + )} + {state.modelLoaded && ( + <Dragger + accept="image/jpeg, image/png" + listType="picture" + customRequest={o => uploadFile(o.file as File)} + fileList={state.fileList} + showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }} + > + <p className="ant-upload-drag-icon"> + <AntdIcons.InboxOutlined /> + </p> + <p className="ant-upload-text"> + {formatMessage({id: 'app.docs.components.icon.pic-searcher.upload-text'})} + </p> + <p className="ant-upload-hint"> + {formatMessage({id: 'app.docs.components.icon.pic-searcher.upload-hint'})} + </p> + </Dragger> + )} + <Spin + spinning={state.loading} + tip={formatMessage({id: 'app.docs.components.icon.pic-searcher.matching'})} + > + <div className="icon-pic-search-result"> + {state.icons.length > 0 && ( + <div className="result-tip"> + {formatMessage({id: 'app.docs.components.icon.pic-searcher.result-tip'})} + </div> + )} + <table> + {state.icons.length > 0 && ( + <thead> + <tr> + <th className="col-icon"> + {formatMessage({id: 'app.docs.components.icon.pic-searcher.th-icon'})} + </th> + <th>{formatMessage({id: 'app.docs.components.icon.pic-searcher.th-score'})}</th> + </tr> + </thead> + )} + <tbody> + {state.icons.map(icon => { + const { type } = icon; + const iconName = `${type + .split('-') + .map(str => `${str[0].toUpperCase()}${str.slice(1)}`) + .join('')}Outlined`; + return ( + <tr key={iconName}> + <td className="col-icon"> + <Tooltip title={icon.type} placement="right"> + {React.createElement(allIcons[iconName])} + </Tooltip> + </td> + <td> + <Progress percent={Math.ceil(icon.score * 100)} /> + </td> + </tr> + ); + })} + </tbody> + </table> + {state.error && ( + <Result + status="500" + title="503" + subTitle={formatMessage({id: 'app.docs.components.icon.pic-searcher.server-error'})} + /> + )} + </div> + </Spin> + </Modal> + </div> + ); +}; + +export default PicSearcher; diff --git a/zy-asrs-flow/src/components/IconSelector/fields.ts b/zy-asrs-flow/src/components/IconSelector/fields.ts new file mode 100644 index 0000000..de37e67 --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/fields.ts @@ -0,0 +1,223 @@ +import * as AntdIcons from '@ant-design/icons/lib/icons'; + +const all = Object.keys(AntdIcons) + .map(n => n.replace(/(Outlined|Filled|TwoTone)$/, '')) + .filter((n, i, arr) => arr.indexOf(n) === i); + +const direction = [ + 'StepBackward', + 'StepForward', + 'FastBackward', + 'FastForward', + 'Shrink', + 'ArrowsAlt', + 'Down', + 'Up', + 'Left', + 'Right', + 'CaretUp', + 'CaretDown', + 'CaretLeft', + 'CaretRight', + 'UpCircle', + 'DownCircle', + 'LeftCircle', + 'RightCircle', + 'DoubleRight', + 'DoubleLeft', + 'VerticalLeft', + 'VerticalRight', + 'VerticalAlignTop', + 'VerticalAlignMiddle', + 'VerticalAlignBottom', + 'Forward', + 'Backward', + 'Rollback', + 'Enter', + 'Retweet', + 'Swap', + 'SwapLeft', + 'SwapRight', + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight', + 'PlayCircle', + 'UpSquare', + 'DownSquare', + 'LeftSquare', + 'RightSquare', + 'Login', + 'Logout', + 'MenuFold', + 'MenuUnfold', + 'BorderBottom', + 'BorderHorizontal', + 'BorderInner', + 'BorderOuter', + 'BorderLeft', + 'BorderRight', + 'BorderTop', + 'BorderVerticle', + 'PicCenter', + 'PicLeft', + 'PicRight', + 'RadiusBottomleft', + 'RadiusBottomright', + 'RadiusUpleft', + 'RadiusUpright', + 'Fullscreen', + 'FullscreenExit', +]; + +const suggestion = [ + 'Question', + 'QuestionCircle', + 'Plus', + 'PlusCircle', + 'Pause', + 'PauseCircle', + 'Minus', + 'MinusCircle', + 'PlusSquare', + 'MinusSquare', + 'Info', + 'InfoCircle', + 'Exclamation', + 'ExclamationCircle', + 'Close', + 'CloseCircle', + 'CloseSquare', + 'Check', + 'CheckCircle', + 'CheckSquare', + 'ClockCircle', + 'Warning', + 'IssuesClose', + 'Stop', +]; + +const editor = [ + 'Edit', + 'Form', + 'Copy', + 'Scissor', + 'Delete', + 'Snippets', + 'Diff', + 'Highlight', + 'AlignCenter', + 'AlignLeft', + 'AlignRight', + 'BgColors', + 'Bold', + 'Italic', + 'Underline', + 'Strikethrough', + 'Redo', + 'Undo', + 'ZoomIn', + 'ZoomOut', + 'FontColors', + 'FontSize', + 'LineHeight', + 'Dash', + 'SmallDash', + 'SortAscending', + 'SortDescending', + 'Drag', + 'OrderedList', + 'UnorderedList', + 'RadiusSetting', + 'ColumnWidth', + 'ColumnHeight', +]; + +const data = [ + 'AreaChart', + 'PieChart', + 'BarChart', + 'DotChart', + 'LineChart', + 'RadarChart', + 'HeatMap', + 'Fall', + 'Rise', + 'Stock', + 'BoxPlot', + 'Fund', + 'Sliders', +]; + +const logo = [ + 'Android', + 'Apple', + 'Windows', + 'Ie', + 'Chrome', + 'Github', + 'Aliwangwang', + 'Dingding', + 'WeiboSquare', + 'WeiboCircle', + 'TaobaoCircle', + 'Html5', + 'Weibo', + 'Twitter', + 'Wechat', + 'Youtube', + 'AlipayCircle', + 'Taobao', + 'Skype', + 'Qq', + 'MediumWorkmark', + 'Gitlab', + 'Medium', + 'Linkedin', + 'GooglePlus', + 'Dropbox', + 'Facebook', + 'Codepen', + 'CodeSandbox', + 'CodeSandboxCircle', + 'Amazon', + 'Google', + 'CodepenCircle', + 'Alipay', + 'AntDesign', + 'AntCloud', + 'Aliyun', + 'Zhihu', + 'Slack', + 'SlackSquare', + 'Behance', + 'BehanceSquare', + 'Dribbble', + 'DribbbleSquare', + 'Instagram', + 'Yuque', + 'Alibaba', + 'Yahoo', + 'Reddit', + 'Sketch', + 'WhatsApp', + 'Dingtalk', +]; + +const datum = [...direction, ...suggestion, ...editor, ...data, ...logo]; + +const other = all.filter(n => !datum.includes(n)); + +export const categories = { + direction, + suggestion, + editor, + data, + logo, + other, +}; + +export default categories; + +export type Categories = typeof categories; +export type CategoriesKeys = keyof Categories; diff --git a/zy-asrs-flow/src/components/IconSelector/index.tsx b/zy-asrs-flow/src/components/IconSelector/index.tsx new file mode 100644 index 0000000..78dc931 --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/index.tsx @@ -0,0 +1,142 @@ +import * as React from 'react'; +import Icon, * as AntdIcons from '@ant-design/icons'; +import { Radio, Input, Empty } from 'antd'; +import type { RadioChangeEvent } from 'antd/es/radio/interface'; +import debounce from 'lodash/debounce'; +import Category from './Category'; +import IconPicSearcher from './IconPicSearcher'; +import { FilledIcon, OutlinedIcon, TwoToneIcon } from './themeIcons'; +import type { CategoriesKeys } from './fields'; +import { categories } from './fields'; +// import { useIntl } from '@umijs/max'; + +export enum ThemeType { + Filled = 'Filled', + Outlined = 'Outlined', + TwoTone = 'TwoTone', +} + +const allIcons: { [key: string]: any } = AntdIcons; + +interface IconSelectorProps { + //intl: any; + onSelect: any; +} + +interface IconSelectorState { + theme: ThemeType; + searchKey: string; +} + +const IconSelector: React.FC<IconSelectorProps> = (props) => { + // const intl = useIntl(); + // const { messages } = intl; + const { onSelect } = props; + const [displayState, setDisplayState] = React.useState<IconSelectorState>({ + theme: ThemeType.Outlined, + searchKey: '', + }); + + const newIconNames: string[] = []; + + const handleSearchIcon = React.useCallback( + debounce((searchKey: string) => { + setDisplayState(prevState => ({ ...prevState, searchKey })); + }), + [], + ); + + const handleChangeTheme = React.useCallback((e: RadioChangeEvent) => { + setDisplayState(prevState => ({ ...prevState, theme: e.target.value as ThemeType })); + }, []); + + const renderCategories = React.useMemo<React.ReactNode | React.ReactNode[]>(() => { + const { searchKey = '', theme } = displayState; + + const categoriesResult = Object.keys(categories) + .map((key: CategoriesKeys) => { + let iconList = categories[key]; + if (searchKey) { + const matchKey = searchKey + // eslint-disable-next-line prefer-regex-literals + .replace(new RegExp(`^<([a-zA-Z]*)\\s/>$`, 'gi'), (_, name) => name) + .replace(/(Filled|Outlined|TwoTone)$/, '') + .toLowerCase(); + iconList = iconList.filter((iconName:string) => iconName.toLowerCase().includes(matchKey)); + } + + // CopyrightCircle is same as Copyright, don't show it + iconList = iconList.filter((icon:string) => icon !== 'CopyrightCircle'); + + return { + category: key, + icons: iconList.map((iconName:string) => iconName + theme).filter((iconName:string) => allIcons[iconName]), + }; + }) + .filter(({ icons }) => !!icons.length) + .map(({ category, icons }) => ( + <Category + key={category} + title={category as CategoriesKeys} + theme={theme} + icons={icons} + newIcons={newIconNames} + onSelect={(type, name) => { + if (onSelect) { + onSelect(name, allIcons[name]); + } + }} + /> + )); + return categoriesResult.length === 0 ? <Empty style={{ margin: '2em 0' }} /> : categoriesResult; + }, [displayState.searchKey, displayState.theme]); + return ( + <> + <div style={{ display: 'flex', justifyContent: 'space-between' }}> + <Radio.Group + value={displayState.theme} + onChange={handleChangeTheme} + size="large" + optionType="button" + buttonStyle="solid" + options={[ + { + label: <Icon component={OutlinedIcon} />, + value: ThemeType.Outlined + }, + { + label: <Icon component={FilledIcon} />, + value: ThemeType.Filled + }, + { + label: <Icon component={TwoToneIcon} />, + value: ThemeType.TwoTone + }, + ]} + > + {/* <Radio.Button value={ThemeType.Outlined}> + <Icon component={OutlinedIcon} /> {messages['app.docs.components.icon.outlined']} + </Radio.Button> + <Radio.Button value={ThemeType.Filled}> + <Icon component={FilledIcon} /> {messages['app.docs.components.icon.filled']} + </Radio.Button> + <Radio.Button value={ThemeType.TwoTone}> + <Icon component={TwoToneIcon} /> {messages['app.docs.components.icon.two-tone']} + </Radio.Button> */} + </Radio.Group> + <Input.Search + // placeholder={messages['app.docs.components.icon.search.placeholder']} + style={{ margin: '0 10px', flex: 1 }} + allowClear + onChange={e => handleSearchIcon(e.currentTarget.value)} + size="large" + autoFocus + suffix={<IconPicSearcher />} + /> + </div> + {renderCategories} + </> + ); +}; + +export default IconSelector diff --git a/zy-asrs-flow/src/components/IconSelector/style.less b/zy-asrs-flow/src/components/IconSelector/style.less new file mode 100644 index 0000000..0a4353d --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/style.less @@ -0,0 +1,137 @@ +.iconPicSearcher { + display: inline-block; + margin: 0 8px; + + .icon-pic-btn { + color: @text-color-secondary; + cursor: pointer; + transition: all 0.3s; + + &:hover { + color: @input-icon-hover-color; + } + } +} + +.icon-pic-preview { + width: 30px; + height: 30px; + margin-top: 10px; + padding: 8px; + text-align: center; + border: 1px solid @border-color-base; + border-radius: 4px; + + > img { + max-width: 50px; + max-height: 50px; + } +} + +.icon-pic-search-result { + min-height: 50px; + padding: 0 10px; + + > .result-tip { + padding: 10px 0; + color: @text-color-secondary; + } + + > table { + width: 100%; + + .col-icon { + width: 80px; + padding: 10px 0; + + > .anticon { + font-size: 30px; + + :hover { + color: @link-hover-color; + } + } + } + } +} + +ul.anticonsList { + margin: 2px 0; + overflow: hidden; + direction: ltr; + list-style: none; + + li { + position: relative; + float: left; + width: 48px; + height: 48px; + margin: 3px 0; + padding: 2px 0 0; + overflow: hidden; + color: #555; + text-align: center; + list-style: none; + background-color: inherit; + border-radius: 4px; + cursor: pointer; + transition: color 0.3s ease-in-out, background-color 0.3s ease-in-out; + + .rtl & { + margin: 3px 0; + padding: 2px 0 0; + } + + .anticon { + margin: 4px 0 2px; + font-size: 24px; + transition: transform 0.3s ease-in-out; + will-change: transform; + } + + .anticonClass { + display: block; + font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + white-space: nowrap; + text-align: center; + transform: scale(0.83); + + .ant-badge { + transition: color 0.3s ease-in-out; + } + } + + &:hover { + color: #fff; + background-color: @primary-color; + + .anticon { + transform: scale(1.4); + } + + .ant-badge { + color: #fff; + } + } + + &.TwoTone:hover { + background-color: #8ecafe; + } + + &.copied:hover { + color: rgba(255, 255, 255, 0.2); + } + + &.copied::after { + top: -2px; + opacity: 1; + } + } +} + +.copied-code { + padding: 2px 4px; + font-size: 12px; + background: #f5f5f5; + border-radius: 2px; +} diff --git a/zy-asrs-flow/src/components/IconSelector/themeIcons.tsx b/zy-asrs-flow/src/components/IconSelector/themeIcons.tsx new file mode 100644 index 0000000..abefe04 --- /dev/null +++ b/zy-asrs-flow/src/components/IconSelector/themeIcons.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; + + +export const FilledIcon: React.FC = props => { + const path = + 'M864 64H160C107 64 64 107 64 160v' + + '704c0 53 43 96 96 96h704c53 0 96-43 96-96V16' + + '0c0-53-43-96-96-96z'; + return ( + <svg {...props} viewBox="0 0 1024 1024"> + <path d={path} /> + </svg> + ); +}; + +export const OutlinedIcon: React.FC = props => { + const path = + 'M864 64H160C107 64 64 107 64 160v7' + + '04c0 53 43 96 96 96h704c53 0 96-43 96-96V160c' + + '0-53-43-96-96-96z m-12 800H172c-6.6 0-12-5.4-' + + '12-12V172c0-6.6 5.4-12 12-12h680c6.6 0 12 5.4' + + ' 12 12v680c0 6.6-5.4 12-12 12z'; + return ( + <svg {...props} viewBox="0 0 1024 1024"> + <path d={path} /> + </svg> + ); +}; + +export const TwoToneIcon: React.FC = props => { + const path = + 'M16 512c0 273.932 222.066 496 496 49' + + '6s496-222.068 496-496S785.932 16 512 16 16 238.' + + '066 16 512z m496 368V144c203.41 0 368 164.622 3' + + '68 368 0 203.41-164.622 368-368 368z'; + return ( + <svg {...props} viewBox="0 0 1024 1024"> + <path d={path} /> + </svg> + ); +}; diff --git a/zy-asrs-flow/src/pages/system/menu/components/edit.jsx b/zy-asrs-flow/src/pages/system/menu/components/edit.jsx index d37e82e..b6168b6 100644 --- a/zy-asrs-flow/src/pages/system/menu/components/edit.jsx +++ b/zy-asrs-flow/src/pages/system/menu/components/edit.jsx @@ -7,12 +7,17 @@ ProFormDateTimePicker, ProFormTreeSelect } from '@ant-design/pro-components'; -import { Form, Modal } from 'antd'; +import { Form, Modal, Col } from 'antd'; import moment from 'moment'; import Http from '@/utils/http'; +import { createIcon } from '@/utils/icon-util' +import IconSelector from '@/components/IconSelector'; const Edit = (props) => { const [menuType, setMenuType] = useState(0); + const [menuIconName, setMenuIconName] = useState(); + const [iconSelectorOpen, setIconSelectorOpen] = useState(false); + const [form] = Form.useForm(); const { } = props; @@ -32,7 +37,7 @@ } const handleFinish = async (values) => { - props.onSubmit({ ...values }); + console.log(values); } return ( @@ -99,7 +104,7 @@ <ProFormSelect name="type" label="绫诲瀷" - colProps={{ md: 12, xl: 12 }} + colProps={{ md: 10, xl: 610 }} placeholder="璇烽�夋嫨" options={[ { label: '鑿滃崟', value: 0 }, @@ -119,12 +124,19 @@ colProps={{ md: 12, xl: 12 }} placeholder="璇疯緭鍏�" /> - <ProFormText + <ProFormSelect name="icon" label="鑿滃崟鍥炬爣" hidden={menuType !== 0} colProps={{ md: 12, xl: 12 }} placeholder="璇疯緭鍏�" + valueEnum={{}} + addonBefore={createIcon(menuIconName)} + fieldProps={{ + onClick: () => { + setIconSelectorOpen(true); + }, + }} /> </ProForm.Group> <ProForm.Group> @@ -147,7 +159,23 @@ /> </ProForm.Group> </ProForm> - </Modal> + <Modal + width={800} + open={iconSelectorOpen} + onCancel={() => { + setIconSelectorOpen(false); + }} + footer={null} + > + <IconSelector + onSelect={(name) => { + form.setFieldsValue({ icon: name }); + setMenuIconName(name); + setIconSelectorOpen(false); + }} + /> + </Modal> + </Modal > </> ) } -- Gitblit v1.9.1