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¨
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
F{|}z~jQ#)s
literal 0
HcmV?d00001
diff --git a/src/asset/tag/other.svg b/src/asset/tag/other.svg
new file mode 100644
index 0000000..8a65f70
--- /dev/null
+++ b/src/asset/tag/other.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/src/asset/tag/other_check.svg b/src/asset/tag/other_check.svg
new file mode 100644
index 0000000..8f3f5ad
--- /dev/null
+++ b/src/asset/tag/other_check.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/src/asset/tag/red_like.png b/src/asset/tag/red_like.png
new file mode 100644
index 0000000000000000000000000000000000000000..ee015f1fc2cccf7f5e6a10e793992ec2b1a0e151
GIT binary patch
literal 1282
zcmV+d1^xPoP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR919H0XL1ONa40RR918~^|S0A#)Nq5uE{Y)M2xR7efIR?SmeRTRJX-S?7E
z^7wcrp;)w~gCz825?Wf@sdWb3vFoO*ZXiU)(Q)U-UH%6*4r1uWjr;-bG%R$Z6mSR-
zWZHz%p(Y^#i6nVn*LNP?*OwR?%8X|5&N;vHmE8L~=LMlwpDuY?0^A#%`jCK9FDzF<
zx_viXAdwWFdJDXk!2ru(XpbC=a?E{@POf5ccrrvWe)Hm0?@v}$=6&$sJMzpB?Kz|?=#RTStTcC>D|HDO;D^8@Quv$J0o;NyTIn*p&{MH$`B46=mww&;SHE|2NcgN<);EEqZpys%wP&+ae`iNmpbZ$v-7
zWCLv!xHl+7l>Ox$B1Nxv_T8ej!qQ=zJhY>GKL8fkao&;Xa3CD-vw;>0J0x(~#6p~5
z7&`aTZjtDOY~Cq_l>+CBjCJ}(yY7VVm$KQ^KL?MHU!J_k`NnP#0qxZD%Vj8FCpxQv
z4lMKbkD_XR<1gjR_S%z;r-
zHFcHJV
z+>*5@fDJ6DodlKv+pNP`Utd3SqN3KW3ND)CfIc}bcduP1@WUBUN^ogy{+Sgo24g`s
zp|2|NFUlQdk{LF=rzKiE=D0lbwL%LW8i60
zio^$$x^Ayth;w_~nf=wTo}ZS}jGu`xi9~{XoF9BwQ)xdE$X;b5`HQl@^3>IT)1xcu
z6;I#wz!#t1-%F)Zw7EgE-S!uX!!tgL)rh%(sY|afmA1ca*ay7i$U)C9p
zyMysT9qEG>fQcbcRN{FpFZ-_)Q>lc;0$)c0VbX=HPt*8-ijjnx5i6>mSoSs{;
z?@$~w$x%5WhX*GA*5u$zB-%OhhV+m`7?2(OZ7`
z6YqH&LN3&RT2PZUgL-yDFcuaTbc$#RbZTfK$n$(yF4TgW&KeC3osR!Xw|8Q@217&U
zj9#ev&6lI!93$(v2_hAa#|7!woVD$jv-YZ5HfdN8yikr+U1qi|?HP6D11Mk}dmiN_
sO;Z|X&5k#f3j#^!*XI6kV*k7TH_N%Z@WydzU;qFB07*qoM6N<$f-TQZc>n+a
literal 0
HcmV?d00001
diff --git a/src/asset/tag/red_like_check.png b/src/asset/tag/red_like_check.png
new file mode 100644
index 0000000000000000000000000000000000000000..22a7ccfab700121d970b73da2d5167c9a58f438c
GIT binary patch
literal 1336
zcmV-81;_e{P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR919H0XL1ONa40RR918~^|S0A#)Nq5uE{qDe$SR7efIR?BN#WfVW>-bva-
z3`jLl3nFBtf=d?){sBHJqGs|mh%U?`#iHP*3yXqC9|^YBCYD;IF|C-!ByE%~x)5~X
zQm8w@F06|*Em>qT$=sRa@0{;3_fDqOl^@)E-+BFh=bY~`A!-?{vdj$#t#uIH?vRo>
zn9xa3e&!Un)LJ6`^z?)|$nwAeU9c?nyI6#j$lTz%R-%M%cTSr^mH8nT21U*eOVF__
z4IHEjAE%9kXj8w`eHA8`;K|p|Q6(931~^m_q%v)`fI>1~z7Yj1;O2(Rqr6J;h|G*k
z1s%%|<)3MTpEf?gMd+=L;Sz0-Hy{xt(M*bF6Mpp~YXZ)o4nCoyGwhmGX{H}b0kJnK
zmy3v;Fcxd^Y0Ap*<5bD6Q-*+yHhE;OJ5&(nLZGa?@eplR{-j#cO*NSG%X9`njo;&U
zzB(2IWu^Zws?o#F$sAmO1*r^I7nlR_smk2oEd=gy=$^rtJZl8;a@x5MD>r0%^a9Y!
z?a(b79~}1Bbxvb9lP%izh}sl&G-p2t*%QIT4HP~`)zUgu(@V5_PY-`|hWV2(n2*h1
zGt(h#;vK4X91i|>o#p%f8}gky{Oz2O$Ic-OoDygC@2v0E`pB6#`sR+1XW0qNBO{)tAUy2$Jd6C)_K|nYKB8=8xFkI&T7Z&
z+U?{Q{@~0rESGWVRhi%LLQvb&bF>8Jx)}nkMia8q
zVbpeq>O90B<7OP0VV=VK+TGKGz)Sen`P_-OLtvy1ihIqnSRSW_oT3K!>hNsflXRR1
zXHVZNE^qr!gt9WRi?&yPrA(fr211m`W~`N1lHaKJfv5Pn6I*%kcI$s7l%?_uIIYV@
zVBnr4U!|@aA|T$+<6mEHM-+4gLb*DCSD9W$C9fd!XHiL7s6?x>R$`88K-F`yU@csS
z?kD)n$*W6QuAiKf<3Kz?sFzACE
zFP_(hD)5@A}n@Tz|#%~p7yJva`0A>}aPOEBH^<8ugV6}{$
zg!-uJkDrlXZY3$SPkr%O5}J%(hol7YEn}yqmR_;;=!+d*Z99Qng}jIte3X*iy;R!O
ui(l
+
\ No newline at end of file
diff --git a/src/asset/tag/text_check.svg b/src/asset/tag/text_check.svg
new file mode 100644
index 0000000..7431db7
--- /dev/null
+++ b/src/asset/tag/text_check.svg
@@ -0,0 +1,26 @@
+
+
\ No newline at end of file
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 942405d..682f820 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -105,7 +105,7 @@ const ButtonStyle: { [key in ButtonType]: object } = {
},
},
highlight: {
- background: 'linear-gradient(270deg, #11AF60 0%, #52C41A 100%)',
+ background: 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)',
boxShadow: '0px 10px 30px 0px rgba(82,196,26,0.2)',
color: '#fff',
},
diff --git a/src/components/LikeIcon/index.tsx b/src/components/LikeIcon/index.tsx
new file mode 100644
index 0000000..fed025d
--- /dev/null
+++ b/src/components/LikeIcon/index.tsx
@@ -0,0 +1,50 @@
+import RedTag from '@/asset/tag/red_like.png';
+import RedTagCheck from '@/asset/tag/red_like_check.png';
+import HoverRedTagCheck from '@/asset/tag/hover_red_like.svg';
+import { LikeContext } from '@/hooks/useLikeList';
+import React, { useContext, useMemo } from 'react';
+import { Box } from '@mui/material';
+
+const LikeIcon: React.FC<{ path: string, style: React.CSSProperties }> = (props) => {
+ const { path, style = {} } = props
+ const { likeList, updateLikeList } = useContext(LikeContext)
+
+ const hanldeLike = (event: React.MouseEvent, path: string) => {
+ event.preventDefault()
+ if (typeof window !== 'undefined' && window.localStorage) {
+ try {
+ let newLike = []
+ if (likeList?.includes(path)) {
+ newLike = likeList.filter(item => item !== path)
+ } else {
+ newLike = [...(likeList || []), path]
+ }
+ updateLikeList(newLike)
+ } finally {
+ console.log('Get like list error')
+ }
+ }
+ }
+ const isCheck = useMemo(() => {
+ return likeList?.includes(path)
+ }, [likeList, path])
+
+ return (
+ hanldeLike(event, path)}/>
+ );
+};
+
+export default LikeIcon;
diff --git a/src/components/MenuView/components.tsx b/src/components/MenuView/components.tsx
deleted file mode 100644
index 966894d..0000000
--- a/src/components/MenuView/components.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import { defaultText, grayBg, grayBorder, grayText } from '@/constant';
-import { Box } from '@mui/material';
-import { styled } from '@mui/material/styles';
-
-export const MenuPage = styled(Box)(() => ({
- display: 'flex',
- height: '100% ',
- backgroundColor: grayBg,
- borderTop: `1px solid ${grayBorder}`,
- paddingBottom: '16px',
- paddingRight: '16px',
- '& .Mui-selected': {
- backgroundColor: 'rgba(0, 0, 0, 0.04)!important',
- },
-}));
-
-export const Main = styled('div')(() => ({
- flex: 1,
- padding: '24px',
- paddingBottom: '0',
- overflowY: 'auto',
- display: 'flex',
- flexDirection: 'column',
- gap: '24px',
- '& code, & code *': {
- fontFamily: 'Mono',
- },
- '& code .linenumber': {
- backgroundColor: 'rgba(82, 196, 26, 0.08)',
- marginRight: '24px',
- paddingRight: '5px!important',
- minWidth: '2em!important',
- },
- '& pre': {
- borderRadius: '4px',
- minHeight: '150px',
- },
-}));
-
-export const SideMenu = styled('div')(() => ({
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'stretch',
- padding: '16px 16px 0 16px',
- gap: '8px',
- height: '100%',
-}));
-
-export const SubMenu = styled('div', {
- shouldForwardProp: (prop) => prop !== 'expand' && prop !== 'height',
-})<{ expand: string; height: number | string }>(({ expand, height }) => ({
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'stretch',
- gap: '12px',
- height: 0,
- transition: 'height 0.2s linear',
- overflow: 'hidden',
- ...(expand === 'true' && {
- height: height ?? 'auto',
- transition: 'height 0.2s linear',
- }),
-}));
-
-export const Breadcrumbs = styled('div')(() => ({
- display: 'flex',
- alignItems: 'center',
- width: '100%',
- gap: '4px',
- marginTop: '16px',
-}));
-
-export const BreadcrumbsItem = styled(Box)(() => ({
- color: grayText,
- cursor: 'pointer',
- '&:hover': {
- color: defaultText,
- },
- fontSize: '14px',
-}));
-
-export const ToolsIcon = styled(Box)(() => ({
- width: '60px',
- height: '60px',
- background: 'linear-gradient(180deg, #230970 0%, #1C0254 100%)',
- borderRadius: '8px',
-}));
-
-export const Tag = styled('div', {
- shouldForwardProp: (prop) => prop !== 'highlight',
-})<{ highlight: 'true' | 'false' }>(({ highlight }) => ({
- display: 'inline-block',
- width: '24px',
- lineHeight: '14px',
- fontSize: '10px',
- borderRadius: '4px',
- textAlign: 'center',
- color: '#fff',
- backgroundColor: '#FF665D',
- ...(highlight === 'true' && { color: '#FF665D', backgroundColor: '#fff' }),
-}));
-
-export const ScrollY = styled('div')(() => ({
- height: '100%',
- overflow: 'auto',
- ...(navigator?.userAgent?.indexOf('Chrome') != -1 && { overflow: 'overlay' }),
-}));
-
-export const SubTitle = styled('div')(() => ({
- height: '20px',
- padding: '12px',
- background: 'rgba(30, 111, 255, 0.1)',
- borderRadius: '4px',
-}));
-
-export const Container = styled(Box)(() => ({
- background: '#fff',
- borderRadius: '4px',
- boxShadow: '0px 4px 10px 0px rgba(145,158,171,0.1)',
- overflow: 'auto',
- minHeight: '100%',
- '& > *': {
- '@media(min-width: 1520px)': {
- marginLeft: 'auto',
- marginRight: 'auto',
- },
- },
- '& > .MuiGrid-container': {
- '@media(min-width: 1520px)': {
- marginLeft: 'auto!important',
- marginRight: 'auto!important',
- '& > .MuiGrid-item:first-of-type': {
- paddingLeft: '0!important',
- },
- },
- },
-}));
diff --git a/src/components/MenuView/index.tsx b/src/components/MenuView/index.tsx
index 74758ed..b4f65ee 100644
--- a/src/components/MenuView/index.tsx
+++ b/src/components/MenuView/index.tsx
@@ -1,25 +1,14 @@
-import { defaultTextClick, secondaryClick } from '@/constant';
+import { grayText } from '@/constant';
import { usePath } from '@/hooks';
-import { AllTags, Tags, Tool, routesMenu } from '@/utils/tools';
-import ErrorIcon from '@mui/icons-material/Error';
-import SearchIcon from '@mui/icons-material/Search';
+import { allTags } from '@/utils/tags';
+import { Tool, allTools } from '@/utils/tools';
import {
Box,
- Button,
- Grid,
- IconButton,
- InputBase,
- ListItemText,
Paper,
- Typography,
+ Stack,
+ Typography
} from '@mui/material';
-import List from '@mui/material/List';
-import ListItemButton from '@mui/material/ListItemButton';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import React, { useEffect, useMemo } from 'react';
-import { Container, Main, MenuPage, SideMenu } from './components';
-import Link from 'next/link';
+import React from 'react';
export interface MenuProps {
children: React.ReactElement;
@@ -31,224 +20,41 @@ const ifChecked = (currentPath: string, itemPath: string) => {
const MenuView: React.FC = ({ children }) => {
const { path } = usePath();
- const [tags, setTags] = React.useState([]);
- const [tools, setTools] = React.useState(routesMenu);
- const [searchText, setSearchText] = React.useState('');
- const [openStaus, setOpenStatus] = React.useState(
- routesMenu.map((item) => true)
- );
- const router = useRouter();
-
- const currentItem = useMemo(() => {
- const _item = routesMenu.find((item) => item.path === path);
- if (_item) return _item;
- }, [path]);
- const checkTags = (tag: Tags) => {
- const _index = tags.findIndex((item) => item === tag);
- const _tags = [...tags];
- if (_index >= 0) {
- _tags.splice(_index, 1);
- } else {
- _tags.push(tag);
- }
- setTags(_tags);
- };
-
- useEffect(() => {
- let toolsFilter: Tool[] = [];
- if (tags.length)
- toolsFilter = routesMenu.filter((item) =>
- item.tags.some((tag) => tags.includes(tag))
- );
- else toolsFilter = routesMenu;
- setTools(
- toolsFilter.filter((item) => {
- return (
- item.label.toUpperCase().includes(searchText?.toUpperCase()) ||
- item.subTitle.toUpperCase().includes(searchText?.toUpperCase())
- );
- })
- );
- }, [tags, searchText]);
+ const [tool] = React.useState(allTools.find(item => item.path === path));
return (
- <>
-
- {currentItem?.label + ' - 长亭百川云工具库'}
-
-
-
-
-
+
+
+
-
-
-
-
- setSearchText(event.target.value)}
- placeholder='输入关键词搜索工具'
- inputProps={{ 'aria-label': 'search icons' }}
- sx={{ grow: 1, fontSize: '12px' }}
- />
-
-
- {AllTags.map((item) => (
-
- ))}
-
-
-
- {tools.map((item, index) => (
-
-
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
- {currentItem?.label}
-
-
-
- {currentItem?.subTitle ? (
-
-
- {currentItem?.subTitle}
-
- ) : null}
-
-
-
-
- {children}
-
-
-
- >
+ {tool?.label}
+
+
+
+ {allTags.filter(tag => tool?.tags.includes(tag.name)).map(tag => {tag.label})}
+
+
+ {tool?.subTitle}
+ {children}
+
);
};
diff --git a/src/components/ToolCard/index.tsx b/src/components/ToolCard/index.tsx
new file mode 100644
index 0000000..6aac4bd
--- /dev/null
+++ b/src/components/ToolCard/index.tsx
@@ -0,0 +1,34 @@
+import { Tag } from "@/utils/tags"
+import { Tool } from "@/utils/tools"
+import { Avatar, CardHeader, Typography } from "@mui/material"
+import LikeIcon from "../LikeIcon"
+import { grayText2 } from "@/constant"
+
+export const ToolCard = (props: { tool: Tool, tag?: Tag, showStar?: boolean }) => {
+ const { tool, tag, showStar = true } = props
+ return (
+ <>
+
+ {tool.label[0]}
+
+ }
+ action={
+
+ }
+ title={tool.label}
+ />
+ {tool.subTitle}
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/components/Tools/home.tsx b/src/components/Tools/home.tsx
deleted file mode 100644
index 3b7da73..0000000
--- a/src/components/Tools/home.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import {
- Autocomplete,
- Box,
- Button,
- IconButton,
- Paper,
- Stack,
- TextField,
- Typography,
-} from '@mui/material';
-
-import { primary } from '@/constant';
-import { AllTags, routesMenu, type Tool } from '@/utils/tools';
-import SearchIcon from '@mui/icons-material/Search';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useState } from 'react';
-
-export default function App() {
- const router = useRouter();
-
- // const [searchText, setSearchText] = useState('')
- const [tools] = useState(routesMenu);
- const [tags] = useState<(typeof AllTags)[number][]>(AllTags);
-
- return (
-
-
-
-
- X
-
- TOOLS
-
-
-
- menu.filter((item) => {
- return (
- item.label
- .toUpperCase()
- .includes(state.inputValue?.toUpperCase()) ||
- item.subTitle
- .toUpperCase()
- .includes(state.inputValue?.toUpperCase())
- );
- })
- }
- onInputChange={(v: any) => {
- setTimeout(() => {
- const item = routesMenu.find((item) =>
- [v.target.textContent, v.target.value].includes(item.label)
- );
- if (item) {
- router.push(item.path);
- }
- });
- }}
- options={routesMenu}
- sx={{ height: '100%' }}
- renderInput={(params) => (
-
-
-
-
-
-
- )}
- />
-
-
-
- {tags.map((section) => [
-
-
- {section.label}
- ,
-
- {tools
- .filter((tool) => tool.tags.includes(section.name))
- .map((item) => (
-
-
-
- ))}
- ,
- ])}
-
-
- );
-}
diff --git a/src/components/Tools/index.tsx b/src/components/Tools/index.tsx
index 3c4b0eb..2934244 100644
--- a/src/components/Tools/index.tsx
+++ b/src/components/Tools/index.tsx
@@ -18,7 +18,7 @@ export const ToolsForm = styled(Box)(() => ({
pt: '10px',
display: 'flex',
flexDirection: 'column',
- width: '838px',
+
margin: 'auto',
gap: 16,
'& .MuiOutlinedInput-root': {
@@ -35,7 +35,6 @@ export const ToolsForm = styled(Box)(() => ({
width: '155px',
color: defaultTextClick,
fontSize: '14px',
- maxWidth: '838px',
},
'& .MuiOutlinedInput-notchedOutline': {
borderColor: '#E3E8EF',
diff --git a/src/constant/color.ts b/src/constant/color.ts
index 404b5c6..bda3d69 100644
--- a/src/constant/color.ts
+++ b/src/constant/color.ts
@@ -1,4 +1,4 @@
-export const primary = '#52C41A';
+export const primary = 'rgba(52, 90, 255, 1)';
export const primaryHover = '#73D13C';
export const primaryClick = '#389E0E';
@@ -10,7 +10,7 @@ export const secondary = '#ECF9E6';
export const secondaryHover = '#F3FDEE';
export const secondaryClick = '#DAE8D4';
-export const special = 'linear-gradient(270deg, #11AF60 0%, #52C41A 100%)';
+export const special = 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)';
export const dangerHover = 'linear-gradient(225deg, #FF1F1F 0%, #F78900 100%)';
export const disabled = '#F7F7F7';
@@ -31,12 +31,12 @@ export const leastTextClick = '#737373';
export const disabledText = '#BFBFBF';
export const specialText = '#737971';
-export const success = '#52C41A';
+export const success = 'rgba(52, 90, 255, 1)';
export const warning = '#FFBF00';
export const error = '#FF1F1F';
-export const grayText = '#0B310440';
-export const grayText2 = '#97A4B0';
+export const grayText = '#0B2562';
+export const grayText2 = 'rgba(11,37,98,0.5)';
export const errorText = '#FF1F1F';
export const infoText = '#2F7CE9';
diff --git a/src/hooks/useAnchor.tsx b/src/hooks/useAnchor.tsx
new file mode 100644
index 0000000..13bc791
--- /dev/null
+++ b/src/hooks/useAnchor.tsx
@@ -0,0 +1,22 @@
+import { useLocalStorageState } from 'ahooks';
+import { createContext, useState } from "react";
+
+export interface Anchor {
+ anchor: string;
+ updateAnchor: (value: string) => void;
+}
+
+export const AnchorContext = createContext(null as any);
+
+export const AnchorContextProvider = ({ children }: any) => {
+ const [anchor, setAnchor] = useState('');
+ const updateAnchor = (newValue: string) => {
+ setAnchor(newValue)
+ };
+ const contextValue = {
+ anchor,
+ updateAnchor,
+ };
+
+ return {children};
+};
\ No newline at end of file
diff --git a/src/hooks/useLikeList.tsx b/src/hooks/useLikeList.tsx
new file mode 100644
index 0000000..a195037
--- /dev/null
+++ b/src/hooks/useLikeList.tsx
@@ -0,0 +1,24 @@
+import { useLocalStorageState } from 'ahooks';
+import { createContext, useState } from "react";
+
+export interface Like {
+ likeList: string[];
+ updateLikeList: (value: string[]) => void;
+}
+
+export const LikeContext = createContext(null as any);
+
+export const LikeContextProvider = ({ children }: any) => {
+ const [localList = [], setLocalList] = useLocalStorageState('like_list', { defaultValue: [] })
+ const [likeList, setValue] = useState(localList);
+ const updateLikeList = (newValue: string[]) => {
+ setValue(newValue);
+ setLocalList(newValue)
+ };
+ const contextValue = {
+ likeList,
+ updateLikeList,
+ };
+
+ return {children};
+};
\ No newline at end of file
diff --git a/src/icon/Copy.tsx b/src/icon/Copy.tsx
index 355e8e5..78820d2 100644
--- a/src/icon/Copy.tsx
+++ b/src/icon/Copy.tsx
@@ -10,7 +10,7 @@ export const Copy = (props: SvgIconProps) => {
({
+ background: 'linear-gradient(180deg, rgba(255, 182, 85, 0.2) 0%, rgba(255, 58, 116, 0.2) 100%)',
+ filter: 'blur(1px)',
+ width: '14px',
+ height: '14px',
+ borderRadius: '50%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginLeft: 'auto',
+}));
+export const IssueIcon = styled(Box)(() => ({
+ width: '8px',
+ height: '8px',
+ borderRadius: '50%',
+ background: 'linear-gradient(141deg, #FFB655 0%, #FF3A74 100%)',
+}));
diff --git a/src/layouts/Header/index.tsx b/src/layouts/Header/index.tsx
new file mode 100644
index 0000000..b066be4
--- /dev/null
+++ b/src/layouts/Header/index.tsx
@@ -0,0 +1,151 @@
+import { usePath } from '@/hooks';
+import { Tags } from '@/utils/tags';
+import { Tool, allTools } from '@/utils/tools';
+import GitHubIcon from '@mui/icons-material/GitHub';
+import SearchIcon from '@mui/icons-material/Search';
+import {
+ Autocomplete,
+ Box,
+ Paper,
+ Stack,
+ TextField,
+ Typography
+} from '@mui/material';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import React, { useEffect, useMemo } from 'react';
+import { IssueIcon, IssueIconWrap } from './components';
+import { ToolCard } from '@/components/ToolCard';
+
+const ifChecked = (currentPath: string, itemPath: string) => {
+ return currentPath === itemPath;
+};
+
+const Header: React.FC<{}> = () => {
+ const { path } = usePath();
+ const [tags, setTags] = React.useState([]);
+ const [tools, setTools] = React.useState(allTools);
+ const [openSearch, setOpenSearch] = React.useState(false);
+ const [searchText, setSearchText] = React.useState('');
+
+ const [openStaus, setOpenStatus] = React.useState(
+ allTools.map((item) => true)
+ );
+ const router = useRouter();
+
+ const currentItem = useMemo(() => {
+ const _item = allTools.find((item) => item.path === path);
+ if (_item) return _item;
+ }, [path]);
+ const checkTags = (tag: Tags) => {
+ const _index = tags.findIndex((item) => item === tag);
+ const _tags = [...tags];
+ if (_index >= 0) {
+ _tags.splice(_index, 1);
+ } else {
+ _tags.push(tag);
+ }
+ setTags(_tags);
+ };
+
+ useEffect(() => {
+ let toolsFilter: Tool[] = [];
+ if (tags.length)
+ toolsFilter = allTools.filter((item) =>
+ item.tags.some((tag) => tags.includes(tag))
+ );
+ else toolsFilter = allTools;
+ setTools(
+ toolsFilter.filter((item) => {
+ return (
+ item.label.toUpperCase().includes(searchText?.toUpperCase()) ||
+ item.subTitle.toUpperCase().includes(searchText?.toUpperCase())
+ );
+ })
+ );
+ }, [tags, searchText]);
+
+ return (
+
+
+ 百川在线工具箱
+
+
+
+
+
+ 工具不够?提个需求
+
+
+ setOpenSearch(false)}
+ onOpen={() => setOpenSearch(true)}
+ filterOptions={(menu, state) =>
+ menu.filter((item) => {
+ return (
+ item.label
+ .toUpperCase()
+ .includes(state.inputValue?.toUpperCase()) ||
+ item.subTitle
+ .toUpperCase()
+ .includes(state.inputValue?.toUpperCase())
+ );
+ })
+ }
+ onInputChange={(v: any) => {
+ const item = allTools.find((item) =>
+ [v.target.textContent, v.target.value].includes(item.label)
+ );
+ }}
+ options={allTools}
+ renderOption={(props: object, option: Tool, state: object, ownerState: object) => (
+
+ setOpenSearch(false)}>
+
+
+
+ )}
+ sx={{ height: '100%' }}
+ renderInput={(params) => (
+
+
+
+
+ )}
+ />
+
+
+ );
+};
+
+export default Header;
diff --git a/src/layouts/LoginLayout/copyright.tsx b/src/layouts/LoginLayout/copyright.tsx
deleted file mode 100644
index b4d0468..0000000
--- a/src/layouts/LoginLayout/copyright.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React, { useCallback } from 'react';
-import emblem from '@/asset/img/emblem.webp';
-import { ImageIcon } from '@/icon';
-import { Stack, SxProps } from '@mui/material';
-import Text from '@/components/Text';
-import { useMobileView } from '@/hooks';
-import { Side_Margin } from '@/styles/colors';
-
-const Copyright: React.FC<{ sx?: SxProps; fontColor?: string }> = ({
- sx,
- fontColor,
-}) => {
- const isMobile = useMobileView();
-
- const openBeianLink = useCallback(() => {
- window.open('https://beian.miit.gov.cn/#/Integrated/recordQuery', '_blank');
- }, []);
-
- return (
-
-
- Copyright ©2022 北京长亭未来科技有限公司
-
- a': { color: 'inherit' },
- }}
- onClick={openBeianLink}
- >
-
- 京ICP备 19035216号-1
-
-
- );
-};
-
-export default Copyright;
diff --git a/src/layouts/LoginLayout/index.tsx b/src/layouts/LoginLayout/index.tsx
deleted file mode 100644
index 9b1a5a8..0000000
--- a/src/layouts/LoginLayout/index.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import Navbar from '@/layouts/Navbar';
-import { Stack } from '@mui/material';
-import Copyright from './copyright';
-import { useMobileView } from '@/hooks';
-import banner1 from '@/asset/img/banner1.webp';
-import mobile_login from '@/asset/img/mobile_login.webp';
-import Image from 'next/image';
-
-const LoginLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
- const isMobile = useMobileView();
- return (
- <>
-
-
-
- {children}
-
-
- >
- );
-};
-
-export default LoginLayout;
diff --git a/src/layouts/Logo/index.tsx b/src/layouts/Logo/index.tsx
deleted file mode 100644
index 60234b5..0000000
--- a/src/layouts/Logo/index.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React, { useCallback } from 'react';
-import { styled } from '@mui/material/styles';
-import { useNavigateParams } from '@/hooks';
-import { Logo as CompanyLogo } from '@/icon';
-import Text from '@/components/Text';
-import { useMobileView } from '@/hooks';
-import { primary, defaultText } from '@/styles/colors';
-
-const LogoComponent = styled('a')(({ theme }) => ({
- display: 'flex',
- userSelect: 'none',
- margin: '8px',
- marginRight: '60px',
- fontSize: '20px',
- cursor: 'pointer',
- [theme.breakpoints.down('sm')]: {
- margin: '24px 8px',
- marginRight: '0px',
- },
-}));
-
-const Logo: React.FC = () => {
- const navigate = useNavigateParams();
- const isMobile = useMobileView();
-
- const toHome = useCallback(() => navigate('/'), [navigate]);
-
- return (
-
-
-
- 长亭百川云平台
-
-
- );
-};
-
-export default Logo;
diff --git a/src/layouts/Navbar/components.tsx b/src/layouts/Navbar/components.tsx
deleted file mode 100644
index 6a7f8d1..0000000
--- a/src/layouts/Navbar/components.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import { styled } from '@mui/material/styles';
-
-export interface BarProps {
- children: React.ReactNode;
- view?: 'default' | 'login';
- id?: string;
-}
-
-const BarBase = styled('div', {
- shouldForwardProp: (prop) => prop !== 'view',
-})<{ view?: 'default' | 'login' }>(({ theme, view = 'default' }) => ({
- width: '100%',
- position: 'fixed',
- top: '0px',
- zIndex: theme.zIndex.drawer + 3,
- // boxShadow: theme.shadows[4],
- backgroundColor: view === 'default' ? 'rgba(255,255,255,0.6)' : 'transparent',
- backdropFilter: 'blur(4px)',
- [theme.breakpoints.down('sm')]: {
- zIndex: theme.zIndex.drawer - 1,
- },
-}));
-const BarContent = styled('div')(({ theme }) => ({
- display: 'flex',
- alignItems: 'center',
- height: '64px',
- justifyContent: 'flex-start',
- margin: `0 28px`,
- marginRight: '0',
- [theme.breakpoints.down('sm')]: { marginLeft: '12px' },
-}));
-
-const Bar: React.FC = (props) => {
- const { children, view, id } = props;
- return (
-
- {children}
-
- );
-};
-
-export default Bar;
diff --git a/src/layouts/Navbar/index.tsx b/src/layouts/Navbar/index.tsx
deleted file mode 100644
index b81f207..0000000
--- a/src/layouts/Navbar/index.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import Bar from './components';
-import Logo from '@/layouts/Logo';
-import NavMenu from './items/menu';
-import { ApplicationInfo } from '@/types';
-
-interface NavBarMenuData {
- apps: ApplicationInfo[];
-}
-
-const renderNavContent = (menuData: ApplicationInfo[]) => {
- return [, ];
-};
-
-const DefaultNavbar: React.FC = ({ apps }) => {
- return (
-
- {renderNavContent(apps)}
-
- );
-};
-
-const LoginNavbar: React.FC<{}> = () => {
- return (
-
-
-
- );
-};
-
-const Navbar = { default: DefaultNavbar, login: LoginNavbar };
-
-export default Navbar;
diff --git a/src/layouts/Navbar/items/menu/index.tsx b/src/layouts/Navbar/items/menu/index.tsx
deleted file mode 100644
index 8ec0e2b..0000000
--- a/src/layouts/Navbar/items/menu/index.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { useMobileView } from '@/hooks';
-import { ApplicationInfo, NavMenuProps } from '@/types';
-import DehazeIcon from '@mui/icons-material/Dehaze';
-import { styled } from '@mui/material/styles';
-import { useRouter } from 'next/router';
-import React, { useCallback, useState } from 'react';
-import MenuItem from './item';
-import MobilPanel from './mobileView';
-import NavPanel from './panel';
-
-const NavMenuComponent = styled('div')(() => ({
- display: 'flex',
- gap: '40px',
-}));
-
-const menu: NavMenuProps[] = [
- { title: '最新活动', link: '/' },
- { title: '产品', expand: true },
-];
-
-const NavMenu: React.FC<{ apps: ApplicationInfo[] }> = ({ apps }) => {
- const isMobile = useMobileView();
- const router = useRouter();
- const [panelContent, setPanelContent] = useState();
- const [openMobile, setOpenMobile] = useState(false);
- const [openPanel, setOpenPanel] = useState(false);
-
- const handleOpen = useCallback(
- (open: boolean) => () => {
- setOpenMobile(open);
- },
- []
- );
-
- const handleClickMenu = useCallback(
- (link?: string) => () => {
- if (link) {
- router.push(link);
- }
- },
- [router]
- );
-
- const handleShowPanel = useCallback(
- (menuContent: NavMenuProps) => () => {
- if (menuContent?.expand) setOpenPanel(true);
- },
- []
- );
-
- const handleLeavePanel = useCallback(() => {
- setOpenPanel(false);
- }, []);
-
- return (
- <>
- {isMobile ? (
-
- ) : (
-
- {menu?.map((menuitem, index) => (
-
- ))}
-
- )}
-
- {isMobile && (
-
- )}
- >
- );
-};
-
-export default NavMenu;
diff --git a/src/layouts/Navbar/items/menu/item.tsx b/src/layouts/Navbar/items/menu/item.tsx
deleted file mode 100644
index 92a9b27..0000000
--- a/src/layouts/Navbar/items/menu/item.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { ArrowDown } from '@/icon';
-import { defaultText, primary, secondaryText } from '@/styles/colors';
-import { styled, SxProps } from '@mui/material/styles';
-import React from 'react';
-
-export interface MenuItemProps {
- title: string | React.ReactElement;
- subTitle?: string;
- highlight?: string | boolean;
- hoverview?: string | boolean;
- expand?: string | boolean;
- sx?: SxProps;
- icon?: React.ReactElement;
- onClick?: () => void;
- onHover?: () => void;
- onLeave?: () => void;
-}
-
-const MenuItemComponent = styled('div', {
- shouldForwardProp: (prop) => prop !== 'hightlight' && prop !== 'expand',
-})>(
- ({ highlight, hoverview }) => ({
- fontSize: 16,
- fontWeight: 'bold',
- color: highlight === 'true' ? primary : defaultText,
- lineHeight: '22px',
- ...(hoverview === 'true' && {
- transition: 'color 0.1s linear',
- }),
- transition: 'color 0.1s linear',
- display: 'flex',
- alignItems: 'center',
-
- '& > svg': {
- display: 'inline-block',
- marginLeft: '8px',
- transformOrigin: 'center',
- transition: 'transform 0.1s linear',
- },
- '&:hover': {
- ...(hoverview === 'true' && {
- color: primary,
- transition: 'color 0.1s linear',
- }),
- '& > svg': {
- transform: 'rotate(0.5turn)',
- transition: 'transform 0.1s linear',
- },
- },
- })
-);
-
-const MenuItemContent = styled('div')(() => ({
- fontSize: 'inherit',
- color: secondaryText,
-}));
-
-const MenuItemWrapper = styled('div')(() => ({
- display: 'flex',
- flexDirection: 'column',
- cursor: 'pointer',
-}));
-
-const MenuItem: React.FC = (props) => {
- const {
- title,
- subTitle,
- highlight,
- expand,
- sx,
- onClick,
- onHover,
- onLeave,
- hoverview,
- icon,
- } = props;
- return (
-
-
- {icon}
- {title}
- {expand && }
-
- {subTitle && {subTitle}}
-
- );
-};
-
-export default MenuItem;
diff --git a/src/layouts/Navbar/items/menu/mobileView.tsx b/src/layouts/Navbar/items/menu/mobileView.tsx
deleted file mode 100644
index a93400b..0000000
--- a/src/layouts/Navbar/items/menu/mobileView.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import search_icon from '@/asset/img/search_icon.webp';
-import { useDebounce } from '@/hooks';
-import { Side_Margin, grayLight, primary } from '@/styles/colors';
-import { ApplicationInfo } from '@/types';
-import ClearIcon from '@mui/icons-material/Clear';
-import { Drawer, TextField } from '@mui/material';
-import { styled } from '@mui/material/styles';
-import Image, { StaticImageData } from 'next/image';
-import { useRouter } from 'next/router';
-import React, { useCallback, useEffect, useState } from 'react';
-import MenuItem from './item';
-import { NavPanelProps } from './panel';
-
-const PanelContent = styled('div')(() => ({
- display: 'flex',
- flexDirection: 'column',
- width: '100%',
- padding: `${parseInt(Side_Margin) / 2}% ${Side_Margin}`,
- justifyContent: 'flex-start',
- gap: 20,
-}));
-
-const Content = styled('div')(() => ({
- display: 'flex',
- gap: 40,
-}));
-
-const Header = styled('div')(() => ({
- display: 'flex',
- justifyContent: 'flex-end',
- alignItems: 'center',
-}));
-
-const ContentCol = styled('div')(() => ({
- display: 'flex',
- flexDirection: 'column',
- height: '100%',
- justifyContent: 'flex-start',
- alignItems: 'stretch',
- gap: 20,
-}));
-
-export const Icon = ({
- style,
- src,
-}: {
- style?: React.CSSProperties;
- src: string | StaticImageData;
-}) => {
- return (
-
- );
-};
-
-const ContentCard = styled('div')(() => ({
- padding: '12px 24px',
- transition: 'background-color 0.2s linear',
- '&:hover': {
- backgroundColor: grayLight,
- transition: 'background-color 0.2s linear',
- },
-}));
-
-const NavPanel: React.FC = (props) => {
- const { open, onClose, apps } = props;
- const [menuContent, setMenuContent] = useState([]);
- const [input, setInput] = useState('');
- const debounceInput = useDebounce(input, 500);
- const router = useRouter();
-
- const handleSearch = useCallback(
- (search: string) => {
- if (apps && search.length > 0) {
- const data = JSON.parse(JSON.stringify(apps)) as ApplicationInfo[];
- const searchResult = data?.filter((item) => item.name.includes(search));
- setMenuContent([...searchResult]);
- } else {
- setMenuContent(apps);
- }
- },
- [apps]
- );
-
- const toWorkbench = useCallback(
- (app: ApplicationInfo) => () => {
- if (app?.scope) {
- router.push(`/landing/${app?.scope}`);
- } else {
- router.push('/workbench');
- }
- onClose?.();
- },
- [onClose, router]
- );
-
- const handleSetInput = useCallback((event: React.ChangeEvent) => {
- const target = event.target as HTMLInputElement;
- const input = target?.value?.trim();
- setInput(input ?? '');
- }, []);
-
- useEffect(() => {
- handleSearch(debounceInput);
- }, [debounceInput, handleSearch]);
-
- return (
-
-
-
-
- ),
- }}
- />
-
-
- {menuContent?.map((contentItem, index) => (
-
-
-
- ))}
-
-
-
-
- );
-};
-
-export default NavPanel;
diff --git a/src/layouts/Navbar/items/menu/panel.tsx b/src/layouts/Navbar/items/menu/panel.tsx
deleted file mode 100644
index 3296f8c..0000000
--- a/src/layouts/Navbar/items/menu/panel.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import search_icon from '@/asset/img/search_icon.webp';
-import { useDebounce } from '@/hooks';
-import { Side_Margin, grayLight } from '@/styles/colors';
-import { ApplicationInfo } from '@/types';
-import { Drawer, TextField } from '@mui/material';
-import { styled } from '@mui/material/styles';
-import Image, { StaticImageData } from 'next/image';
-import { useRouter } from 'next/router';
-import React, { useCallback, useEffect, useState } from 'react';
-import MenuItem from './item';
-
-export interface NavPanelProps {
- open: boolean;
- onClose?: () => void;
- apps: ApplicationInfo[];
-}
-
-const PanelContent = styled('div')(() => ({
- display: 'flex',
- flexDirection: 'column',
- width: '100%',
- padding: `${parseInt(Side_Margin) / 1.5}% ${Side_Margin}`,
- paddingTop: `calc(${parseInt(Side_Margin) / 4}% + 64px)`,
- justifyContent: 'flex-start',
- gap: 20,
-}));
-
-const Content = styled('div')(() => ({
- display: 'flex',
- gap: 40,
- flexWrap: 'wrap',
- alignItems: 'stretch',
-}));
-
-export const Icon = ({
- style,
- src,
-}: {
- style?: React.CSSProperties;
- src: string | StaticImageData;
-}) => {
- return (
-
- );
-};
-
-const ContentCard = styled('div')(() => ({
- flex: '0 0 220px',
- padding: '12px 24px',
- transition: 'background-color 0.2s linear',
- '&:hover': {
- backgroundColor: grayLight,
- transition: 'background-color 0.2s linear',
- },
-}));
-
-const NavPanel: React.FC = (props) => {
- const { open, onClose, apps } = props;
- const [menuContent, setMenuContent] = useState(apps ?? []);
- const [input, setInput] = useState('');
- const router = useRouter();
- const debounceInput = useDebounce(input, 500);
-
- const handleSearch = useCallback(
- (search: string) => {
- if (apps && search.length > 0) {
- const data = JSON.parse(JSON.stringify(apps)) as ApplicationInfo[];
- const searchResult = data?.filter((item) => item.name.includes(search));
- setMenuContent([...searchResult]);
- } else {
- setMenuContent(apps);
- }
- },
- [apps]
- );
-
- const handleSetInput = useCallback((event: React.ChangeEvent) => {
- const target = event.target as HTMLInputElement;
- const input = target?.value?.trim();
- setInput(input ?? '');
- }, []);
-
- const toWorkbench = useCallback(
- (app: ApplicationInfo) => () => {
- if (app?.scope) {
- router.push(`/landing/${app?.scope}`);
- } else {
- router.push('/workbench');
- }
- onClose?.();
- },
- [router, onClose]
- );
-
- useEffect(() => {
- handleSearch(debounceInput);
- }, [debounceInput, handleSearch]);
-
- return (
-
-
-
- ),
- }}
- />
-
- {menuContent?.map((item, index) => (
-
-
-
- ))}
-
-
-
- );
-};
-
-export default NavPanel;
diff --git a/src/layouts/SideBar/components.tsx b/src/layouts/SideBar/components.tsx
new file mode 100644
index 0000000..0ab79a7
--- /dev/null
+++ b/src/layouts/SideBar/components.tsx
@@ -0,0 +1,20 @@
+import { defaultText, grayBg, grayBorder, grayText } from '@/constant';
+import { Box, Stack } from '@mui/material';
+import { styled } from '@mui/material/styles';
+
+export const IssueIconWrap = styled(Stack)(() => ({
+ background: 'linear-gradient(180deg, rgba(255, 182, 85, 0.2) 0%, rgba(255, 58, 116, 0.2) 100%)',
+ filter: 'blur(1px)',
+ width: '14px',
+ height: '14px',
+ borderRadius: '50%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginLeft: 'auto',
+}));
+export const IssueIcon = styled(Box)(() => ({
+ width: '8px',
+ height: '8px',
+ borderRadius: '50%',
+ background: 'linear-gradient(141deg, #FFB655 0%, #FF3A74 100%)',
+}));
diff --git a/src/layouts/SideBar/index.tsx b/src/layouts/SideBar/index.tsx
new file mode 100644
index 0000000..5dc185d
--- /dev/null
+++ b/src/layouts/SideBar/index.tsx
@@ -0,0 +1,47 @@
+import { grayText } from '@/constant/color';
+import { AnchorContext } from '@/hooks/useAnchor';
+import { allTags } from '@/utils/tags';
+import {
+ Box,
+ Button,
+ Link,
+ Paper,
+ Stack,
+ Typography
+} from '@mui/material';
+import Image from 'next/image';
+import React, { useContext, useEffect, useState } from 'react';
+
+const SideBar: React.FC<{}> = () => {
+ const { anchor } = useContext(AnchorContext)
+ const [checkAnchor, setChectAnchor] = useState('')
+ useEffect(() => {
+ setChectAnchor(anchor)
+ }, [anchor])
+ return (
+
+
+ {allTags.map(item =>
+ setChectAnchor(item.name)} href={'/tools#' + item.name} sx={{ alignSelf: 'stretch' }} className='custom-link'>
+
+
+ )}
+
+
+ );
+};
+
+export default SideBar;
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index d6e40c1..320eec4 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -7,11 +7,19 @@ import '@/styles/static/swiper.css';
import theme from '@/styles/theme';
import createEmotionCache from '@/utils/emotionCache';
import { CacheProvider } from '@emotion/react';
-import { ThemeProvider } from '@mui/material';
+import { Box, Stack, ThemeProvider } from '@mui/material';
import CssBaseline from '@mui/material/CssBaseline';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import PropTypes from 'prop-types';
+import Header from '@/layouts/Header';
+import SideBar from '@/layouts/SideBar';
+import { LikeContextProvider } from '@/hooks/useLikeList';
+import { AnchorContextProvider } from '@/hooks/useAnchor';
+import { usePath } from '@/hooks';
+import { useMemo } from 'react';
+import { allTools } from '@/utils/tools';
+import Head from 'next/head';
const clientSideEmotionCache = createEmotionCache();
@@ -19,24 +27,45 @@ const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
});
+
export default function App({
Component,
emotionCache = clientSideEmotionCache,
}: any) {
+ const { path } = usePath()
+
+ const currentItem = useMemo(() => {
+ return allTools.find(item => item.path === path)
+ }, [path])
return (
<>
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
>
);
}
diff --git a/src/pages/aes.tsx b/src/pages/aes.tsx
index 02f3284..fc40a3a 100644
--- a/src/pages/aes.tsx
+++ b/src/pages/aes.tsx
@@ -101,7 +101,7 @@ const AES: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/ascii.tsx b/src/pages/ascii.tsx
index b1f107e..f5f0bea 100644
--- a/src/pages/ascii.tsx
+++ b/src/pages/ascii.tsx
@@ -112,7 +112,7 @@ const Hash: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/base64.tsx b/src/pages/base64.tsx
index c46dbdb..124bce2 100644
--- a/src/pages/base64.tsx
+++ b/src/pages/base64.tsx
@@ -66,9 +66,9 @@ const Base64: React.FC = () => {
sx={{
mt: '24px',
gap: '18px',
- maxWidth: '1020px',
+ // maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/case_convert.tsx b/src/pages/case_convert.tsx
index 1c7f6cf..ed66d6c 100644
--- a/src/pages/case_convert.tsx
+++ b/src/pages/case_convert.tsx
@@ -16,7 +16,7 @@ const CaseConvert: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/hash.tsx b/src/pages/hash.tsx
index 7ba6a68..4265836 100644
--- a/src/pages/hash.tsx
+++ b/src/pages/hash.tsx
@@ -85,7 +85,7 @@ const Hash: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/img2base64.tsx b/src/pages/img2base64.tsx
index f8ed443..5796440 100644
--- a/src/pages/img2base64.tsx
+++ b/src/pages/img2base64.tsx
@@ -90,7 +90,7 @@ const ImgBase64: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 35e8521..844f1fe 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,3 +1,99 @@
-import Home from '@/components/Tools/home';
+import {
+ Avatar,
+ Box,
+ Card,
+ CardHeader,
+ Paper,
+ Stack,
+ Typography
+} from '@mui/material';
-export default Home;
+import LikeIcon from '@/components/LikeIcon';
+import { grayText2 } from '@/constant';
+import { LikeContext } from '@/hooks/useLikeList';
+import { Tag, Tags, allTags } from '@/utils/tags';
+import { Tool, allTools } from '@/utils/tools';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useContext, useEffect, useMemo, useRef } from 'react';
+
+import NoLike from '@/asset/tag/no_like.png';
+import { AnchorContext } from '@/hooks/useAnchor';
+import { ToolCard } from '@/components/ToolCard';
+
+export default function App() {
+ const { updateAnchor } = useContext(AnchorContext)
+ const { likeList } = useContext(LikeContext)
+ const mainPageRef = useRef(null)
+
+ useEffect(() => {
+ const handleScroll = () => {
+ if (!mainPageRef.current?.children) return
+ const scrollPosition = mainPageRef?.current.scrollTop;
+ for (let liElement of mainPageRef?.current.children) {
+ const liTop = liElement.offsetTop;
+ const liBottom = liTop + liElement.offsetHeight;
+
+ if (scrollPosition >= liTop - 100 && scrollPosition < liBottom - 100) {
+ const liId = liElement.getAttribute('id');
+ updateAnchor(liId)
+ break;
+ }
+ }
+ };
+ mainPageRef.current?.addEventListener('scroll', handleScroll);
+ return () => {
+ mainPageRef.current?.removeEventListener('scroll', handleScroll);
+ updateAnchor('')
+ };
+ }, []);
+
+ const tagAndTools = useMemo(() => {
+ const addLikeTagForTools = allTools.map(item => {
+ if (likeList?.includes(item.path)) return ({ ...item, tags: [...item.tags, Tags.LIKE] })
+ return item
+ })
+ return allTags.map(tag => {
+ (tag as any).tools = addLikeTagForTools.filter(tool => tool.tags.includes(tag.name))
+ return tag
+ })
+ }, [likeList])
+
+ return (
+
+ {
+ tagAndTools.map(tag => (
+
+ {tag.label}
+
+ {
+ ((tag as any).tools.length ?
+ (tag as any).tools.map((tool: any) => (
+
+
+
+
+
+ )) :
+
+ )
+ }
+
+
+ )
+ )
+ }
+
+ );
+}
diff --git a/src/pages/jsontocsv.tsx b/src/pages/jsontocsv.tsx
index a07398b..5da4fde 100644
--- a/src/pages/jsontocsv.tsx
+++ b/src/pages/jsontocsv.tsx
@@ -136,7 +136,7 @@ const JSONToCSV = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
'.rc-table': {
border: '1px solid #eee',
diff --git a/src/pages/radix_convert.tsx b/src/pages/radix_convert.tsx
index fb93db6..7ba05d0 100644
--- a/src/pages/radix_convert.tsx
+++ b/src/pages/radix_convert.tsx
@@ -146,7 +146,7 @@ const RadixConvert: React.FC = () => {
gap: '18px',
maxWidth: '1020px',
fontFamily: 'Mono',
- width: '838px',
+
mx: 'auto',
}}
>
diff --git a/src/pages/random.tsx b/src/pages/random.tsx
index 92683e2..eb3fb8f 100644
--- a/src/pages/random.tsx
+++ b/src/pages/random.tsx
@@ -107,8 +107,8 @@ const Random: React.FC = () => {
return (
-
-
+
+