committed by
GitHub
43 changed files with 976 additions and 1232 deletions
@ -0,0 +1,27 @@ |
|||||
|
name: React Doctor |
||||
|
|
||||
|
on: |
||||
|
pull_request: |
||||
|
types: [opened, synchronize, reopened, ready_for_review] |
||||
|
push: |
||||
|
branches: [master] |
||||
|
|
||||
|
permissions: |
||||
|
contents: read |
||||
|
pull-requests: write |
||||
|
issues: write |
||||
|
statuses: write |
||||
|
|
||||
|
concurrency: |
||||
|
group: react-doctor-${{ github.event.pull_request.number || github.ref }} |
||||
|
cancel-in-progress: true |
||||
|
|
||||
|
jobs: |
||||
|
react-doctor: |
||||
|
runs-on: ubuntu-latest |
||||
|
steps: |
||||
|
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 |
||||
|
with: |
||||
|
fetch-depth: 0 |
||||
|
persist-credentials: false |
||||
|
- uses: millionco/react-doctor@0b4f4f4bd248a154e64eb508a48347f71154b3f3 # v2 |
||||
@ -0,0 +1,46 @@ |
|||||
|
{ |
||||
|
"ignore": { |
||||
|
"files": [ |
||||
|
"config/**", |
||||
|
"mock/**", |
||||
|
"public/**", |
||||
|
"scripts/**", |
||||
|
"types/**", |
||||
|
".worktrees/**", |
||||
|
"cloudflare-worker/**", |
||||
|
"src/.umi*/**", |
||||
|
"src/services/**", |
||||
|
"src/service-worker.js", |
||||
|
"**/_mock.ts", |
||||
|
"**/*.md", |
||||
|
"**/*.less", |
||||
|
"**/*.css" |
||||
|
], |
||||
|
"overrides": [ |
||||
|
{ |
||||
|
"files": [ |
||||
|
"src/global.tsx", |
||||
|
"src/global.style.ts", |
||||
|
"src/loading.tsx", |
||||
|
"src/locales/**", |
||||
|
"src/pages/**", |
||||
|
"tests/__mocks__/**" |
||||
|
], |
||||
|
"rules": ["deslop/unused-file"] |
||||
|
}, |
||||
|
{ |
||||
|
"files": ["package.json"], |
||||
|
"rules": ["deslop/unused-dev-dependency"] |
||||
|
}, |
||||
|
{ |
||||
|
"files": [ |
||||
|
"src/pages/form/advanced-form/index.tsx", |
||||
|
"src/pages/table-list/index.tsx" |
||||
|
], |
||||
|
"rules": ["react-doctor/no-giant-component"] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
"blocking": "error", |
||||
|
"adoptExistingLintConfig": true |
||||
|
} |
||||
File diff suppressed because it is too large
@ -1,21 +0,0 @@ |
|||||
{ |
|
||||
"ignore": { |
|
||||
"files": [ |
|
||||
"config/**", |
|
||||
"mock/**", |
|
||||
"public/**", |
|
||||
"scripts/**", |
|
||||
"types/**", |
|
||||
"cloudflare-worker/**", |
|
||||
"src/.umi*/**", |
|
||||
"src/services/**", |
|
||||
"src/service-worker.js", |
|
||||
"**/_mock.ts", |
|
||||
"**/*.md", |
|
||||
"**/*.less", |
|
||||
"**/*.css" |
|
||||
] |
|
||||
}, |
|
||||
"failOn": "error", |
|
||||
"adoptExistingLintConfig": true |
|
||||
} |
|
||||
@ -0,0 +1,63 @@ |
|||||
|
import { CheckOutlined, GlobalOutlined } from '@ant-design/icons'; |
||||
|
import { getAllLocales, getLocale, setLocale } from '@umijs/max'; |
||||
|
import type { MenuProps } from 'antd'; |
||||
|
import { Button } from 'antd'; |
||||
|
import { useMemo } from 'react'; |
||||
|
import HeaderDropdown from '../HeaderDropdown'; |
||||
|
import useHeaderActionStyles from './style'; |
||||
|
|
||||
|
const localeLabelMap: Record<string, { emoji: string; label: string }> = { |
||||
|
'zh-CN': { emoji: '🇨🇳', label: '简体中文' }, |
||||
|
'zh-TW': { emoji: '🇭🇰', label: '繁體中文' }, |
||||
|
'en-US': { emoji: '🇺🇸', label: 'English' }, |
||||
|
'ja-JP': { emoji: '🇯🇵', label: '日本語' }, |
||||
|
'pt-BR': { emoji: '🇧🇷', label: 'Português' }, |
||||
|
'id-ID': { emoji: '🇮🇩', label: 'Bahasa Indonesia' }, |
||||
|
'fa-IR': { emoji: '🇮🇷', label: 'فارسی' }, |
||||
|
'bn-BD': { emoji: '🇧🇩', label: 'বাংলা' }, |
||||
|
}; |
||||
|
|
||||
|
const onLangClick: MenuProps['onClick'] = ({ key }) => { |
||||
|
if (key.startsWith('lang-')) { |
||||
|
setLocale(key.replace('lang-', ''), false); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const LangDropdown: React.FC = () => { |
||||
|
const { styles } = useHeaderActionStyles(); |
||||
|
const allLocales = useMemo(() => getAllLocales(), []); |
||||
|
const currentLocale = getLocale(); |
||||
|
const supportLocales = allLocales.filter((l) => l in localeLabelMap); |
||||
|
|
||||
|
if (supportLocales.length <= 1) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
const langItems: MenuProps['items'] = supportLocales.map((locale) => ({ |
||||
|
key: `lang-${locale}`, |
||||
|
icon: |
||||
|
locale === currentLocale ? ( |
||||
|
<CheckOutlined style={{ color: '#52c41a' }} /> |
||||
|
) : ( |
||||
|
<span style={{ display: 'inline-block', width: 14 }} /> |
||||
|
), |
||||
|
label: `${localeLabelMap[locale]?.emoji ?? ''} ${localeLabelMap[locale]?.label ?? locale}`, |
||||
|
})); |
||||
|
|
||||
|
return ( |
||||
|
<HeaderDropdown |
||||
|
placement="bottomRight" |
||||
|
arrow |
||||
|
menu={{ |
||||
|
selectedKeys: [`lang-${currentLocale}`], |
||||
|
onClick: onLangClick, |
||||
|
items: langItems, |
||||
|
style: { minWidth: 180 }, |
||||
|
}} |
||||
|
> |
||||
|
<Button type="text" className={styles.action} aria-label="语言切换"> |
||||
|
<GlobalOutlined /> |
||||
|
</Button> |
||||
|
</HeaderDropdown> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,36 @@ |
|||||
|
import { ForkOutlined } from '@ant-design/icons'; |
||||
|
import type { MenuProps } from 'antd'; |
||||
|
import { Button } from 'antd'; |
||||
|
import HeaderDropdown from '../HeaderDropdown'; |
||||
|
import useHeaderActionStyles from './style'; |
||||
|
|
||||
|
const versionItems: MenuProps['items'] = [ |
||||
|
{ key: 'https://v5.pro.ant.design', label: 'v5' }, |
||||
|
{ key: 'https://v4.pro.ant.design', label: 'v4' }, |
||||
|
{ key: 'https://v2.pro.ant.design', label: 'v2' }, |
||||
|
{ key: 'https://v1.pro.ant.design', label: 'v1' }, |
||||
|
]; |
||||
|
|
||||
|
const onVersionClick: MenuProps['onClick'] = ({ key }) => { |
||||
|
window.open(key, '_blank', 'noopener,noreferrer'); |
||||
|
}; |
||||
|
|
||||
|
export const VersionDropdown: React.FC = () => { |
||||
|
const { styles } = useHeaderActionStyles(); |
||||
|
return ( |
||||
|
<HeaderDropdown |
||||
|
placement="bottomRight" |
||||
|
arrow |
||||
|
menu={{ |
||||
|
selectedKeys: [], |
||||
|
onClick: onVersionClick, |
||||
|
items: versionItems, |
||||
|
style: { minWidth: 100 }, |
||||
|
}} |
||||
|
> |
||||
|
<Button type="text" className={styles.action} aria-label="历史版本"> |
||||
|
<ForkOutlined /> |
||||
|
</Button> |
||||
|
</HeaderDropdown> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { createStyles } from 'antd-style'; |
||||
|
|
||||
|
const useHeaderActionStyles = createStyles(({ token, css }) => ({ |
||||
|
action: css` |
||||
|
display: inline-flex !important; |
||||
|
align-items: center !important; |
||||
|
justify-content: center !important; |
||||
|
height: 36px !important; |
||||
|
min-width: 36px; |
||||
|
padding-inline: 8px !important; |
||||
|
padding-block: 0 !important; |
||||
|
border-radius: ${token.borderRadius}px !important; |
||||
|
`,
|
||||
|
})); |
||||
|
|
||||
|
export default useHeaderActionStyles; |
||||
@ -1,11 +0,0 @@ |
|||||
import { formatYuan } from '@/utils/format'; |
|
||||
import ChartCard from './ChartCard'; |
|
||||
import Field from './Field'; |
|
||||
|
|
||||
const Charts = { |
|
||||
yuan: formatYuan, |
|
||||
ChartCard, |
|
||||
Field, |
|
||||
}; |
|
||||
|
|
||||
export { ChartCard, Charts as default, Field, formatYuan as yuan }; |
|
||||
@ -1,17 +1,7 @@ |
|||||
import { useEffect, useRef } from 'react'; |
import { formatYuan } from '@/utils/format'; |
||||
import { yuan } from '../components/Charts'; |
|
||||
|
|
||||
/** 减少使用 dangerouslySetInnerHTML */ |
|
||||
const Yuan: React.FC<{ children: string | number }> = ({ children }) => { |
const Yuan: React.FC<{ children: string | number }> = ({ children }) => { |
||||
const spanRef = useRef<HTMLSpanElement>(null); |
return <span>{formatYuan(children)}</span>; |
||||
|
|
||||
useEffect(() => { |
|
||||
if (spanRef.current) { |
|
||||
spanRef.current.innerHTML = yuan(children); |
|
||||
} |
|
||||
}, [children]); |
|
||||
|
|
||||
return <span ref={spanRef} />; |
|
||||
}; |
}; |
||||
|
|
||||
export default Yuan; |
export default Yuan; |
||||
|
|||||
Loading…
Reference in new issue