From 61d3bb21ad4d9d6e5cef8ea89231fc864664dd9c Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Tue, 19 Mar 2024 18:34:45 +0800 Subject: [PATCH] Feat: store - support multiple platforms --- .env.development | 1 + .env.production | 1 + .env.testing | 1 + build/resolvers/antd.ts | 6 +- package-lock.json | 7 + package.json | 1 + src/main/index.ts | 1 + .../src/assets/css/pages/tools/store.scss | 10 +- .../src/assets/css/pages/tools/user.scss | 10 +- src/renderer/src/assets/svg/browser.svg | 1 + src/renderer/src/assets/svg/desktop.svg | 1 + src/renderer/src/assets/svg/info.svg | 1 + src/renderer/src/assets/svg/mobile.svg | 1 + src/renderer/src/main.tsx | 5 +- src/renderer/src/pages/Tools/Store.tsx | 266 ++++++++++++++---- src/renderer/src/pages/Tools/User.tsx | 229 ++++++++++++--- 16 files changed, 433 insertions(+), 109 deletions(-) create mode 100644 src/renderer/src/assets/svg/browser.svg create mode 100644 src/renderer/src/assets/svg/desktop.svg create mode 100644 src/renderer/src/assets/svg/info.svg create mode 100644 src/renderer/src/assets/svg/mobile.svg diff --git a/.env.development b/.env.development index 944601c..9d59c2a 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,5 @@ VITE_PLATFORM=DESKTOP +VITE_PROTOCOL=oxygen VITE_API_URL=${DEV_API_URL} VITE_API_TOKEN_URL=${VITE_API_URL}/token VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY} diff --git a/.env.production b/.env.production index 707322a..3fa1999 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,5 @@ VITE_PLATFORM=DESKTOP +VITE_PROTOCOL=oxygen VITE_API_URL=${PRODUCT_API_URL} VITE_API_TOKEN_URL=${VITE_API_URL}/token VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY} diff --git a/.env.testing b/.env.testing index e48a6b6..75a1cda 100644 --- a/.env.testing +++ b/.env.testing @@ -1,5 +1,6 @@ NODE_ENV=development VITE_PLATFORM=DESKTOP +VITE_PROTOCOL=oxygen VITE_API_URL=${TEST_API_URL} VITE_API_TOKEN_URL=${VITE_API_URL}/token VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY} diff --git a/build/resolvers/antd.ts b/build/resolvers/antd.ts index 0bb5132..4506424 100644 --- a/build/resolvers/antd.ts +++ b/build/resolvers/antd.ts @@ -149,7 +149,10 @@ const matchComponents: IMatcher[] = [ pattern: /^Mentions/, styleDir: 'mentions' }, - + { + pattern: /^QRCode/, + styleDir: 'qr-code' + }, { pattern: /^Step/, styleDir: 'steps' @@ -337,6 +340,7 @@ const primitiveNames = [ 'Rate', 'Result', 'Row', + 'QRCode', 'Select', 'SelectOptGroup', 'SelectOption', diff --git a/package-lock.json b/package-lock.json index ff04d47..97ec8d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@vitejs/plugin-react": "^4.2.1", "antd": "^5.15.2", "axios": "^1.6.7", + "custom-protocol-check": "^1.4.0", "dayjs": "^1.11.10", "echarts": "^5.5.0", "electron": "^28.2.0", @@ -4375,6 +4376,12 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/custom-protocol-check": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/custom-protocol-check/-/custom-protocol-check-1.4.0.tgz", + "integrity": "sha512-eMTyp8AKnE5eo+mKNqG3743eb5ZND5LhBgf9F8BN2tVdhSBnOCHH7me7iTcv0BUDhUW2dBQiHWLWMy776yZW1A==", + "dev": true + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", diff --git a/package.json b/package.json index c157f01..ee5530b 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@vitejs/plugin-react": "^4.2.1", "antd": "^5.15.2", "axios": "^1.6.7", + "custom-protocol-check": "^1.4.0", "dayjs": "^1.11.10", "echarts": "^5.5.0", "electron": "^28.2.0", diff --git a/src/main/index.ts b/src/main/index.ts index c953f1a..b97451d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -18,6 +18,7 @@ if (!app.isPackaged) { } args.push('--') app.setAsDefaultProtocolClient(import.meta.env.VITE_PROTOCOL, process.execPath, args) +// app.removeAsDefaultProtocolClient(import.meta.env.VITE_PROTOCOL, process.execPath, args) const handleArgv = (argv: string[]) => { const prefix = `${import.meta.env.VITE_PROTOCOL}:` diff --git a/src/renderer/src/assets/css/pages/tools/store.scss b/src/renderer/src/assets/css/pages/tools/store.scss index ffc4ab0..3bffda7 100644 --- a/src/renderer/src/assets/css/pages/tools/store.scss +++ b/src/renderer/src/assets/css/pages/tools/store.scss @@ -99,10 +99,12 @@ } .operation { + display: flex; position: absolute; - top: 6px; + top: 10px; right: 12px; font-size: 1.6em; + gap: 4px; > *:hover { color: constants.$font-secondary-color; @@ -141,4 +143,10 @@ color: constants.$font-secondary-color; } } + + .android-qrcode { + align-items: center; + transform: translateX(-16px); + gap: 20px; + } } diff --git a/src/renderer/src/assets/css/pages/tools/user.scss b/src/renderer/src/assets/css/pages/tools/user.scss index f0d02a1..05f0cc5 100644 --- a/src/renderer/src/assets/css/pages/tools/user.scss +++ b/src/renderer/src/assets/css/pages/tools/user.scss @@ -141,10 +141,12 @@ } .operation { + display: flex; position: absolute; - top: 6px; + top: 10px; right: 12px; font-size: 1.6em; + gap: 4px; > *:hover { color: constants.$font-secondary-color; @@ -185,4 +187,10 @@ } } } + + .android-qrcode { + align-items: center; + transform: translateX(-16px); + gap: 20px; + } } diff --git a/src/renderer/src/assets/svg/browser.svg b/src/renderer/src/assets/svg/browser.svg new file mode 100644 index 0000000..a4f2afe --- /dev/null +++ b/src/renderer/src/assets/svg/browser.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/assets/svg/desktop.svg b/src/renderer/src/assets/svg/desktop.svg new file mode 100644 index 0000000..67759d5 --- /dev/null +++ b/src/renderer/src/assets/svg/desktop.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/assets/svg/info.svg b/src/renderer/src/assets/svg/info.svg new file mode 100644 index 0000000..d0312db --- /dev/null +++ b/src/renderer/src/assets/svg/info.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/assets/svg/mobile.svg b/src/renderer/src/assets/svg/mobile.svg new file mode 100644 index 0000000..e6de593 --- /dev/null +++ b/src/renderer/src/assets/svg/mobile.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index c8665a4..84f0207 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -10,7 +10,10 @@ createRoot(document.getElementById('root')!).render( { const navigate = useNavigate() + const [modal, contextHolder] = AntdModal.useModal() const cardRef = useRef(null) useEffect(() => { @@ -53,7 +58,31 @@ const CommonCard = ({ }, [options]) const handleCardOnClick = () => { - url && navigate(url) + if (!checkDesktop() && platform === 'DESKTOP') { + void message.warning('此应用需要桌面端环境,请在桌面端打开') + return + } + if (platform === 'ANDROID') { + void modal.info({ + icon: , + title: 'Android 端', + centered: true, + maskClosable: true, + content: ( + + + 请使用手机端扫描上方二维码 + + ) + }) + return + } + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) } const handleOnClickAuthor = (e: MouseEvent) => { @@ -63,50 +92,130 @@ const CommonCard = ({ const handleOnSourceBtnClick = (e: MouseEvent) => { e.stopPropagation() - navigate(`/source/${authorUsername}/${toolId}`) + navigate( + `/source/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) + } + + const handleOnAndroidBtnClick = (e: MouseEvent) => { + e.stopPropagation() + void modal.info({ + icon: , + title: 'Android 端', + centered: true, + maskClosable: true, + content: ( + + + 请使用手机端扫描上方二维码 + + ) + }) + } + + const handleOnDesktopBtnClick = (e: MouseEvent) => { + e.stopPropagation() + if (!checkDesktop()) { + void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 }) + protocolCheck( + `oxygen://openurl/view/${authorUsername}/${toolId}`, + () => { + void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') + void message.destroy('LOADING') + }, + () => { + void message.destroy('LOADING') + }, + 2000, + () => { + void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') + void message.destroy('LOADING') + } + ) + return + } + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) + } + + const handleOnWebBtnClick = (e: MouseEvent) => { + e.stopPropagation() + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) } return ( - - -
{icon}
-
- V{ver} -
-
-
{toolName}
-
{`ID: ${toolId}`}
- {toolDesc &&
{`简介:${toolDesc}`}
} -
-
-
- - } - style={{ background: COLOR_BACKGROUND }} - /> + <> + + +
{icon}
+
+ + {platform.slice(0, 1)}-{ver} +
- -
{authorName}
-
-
-
- - - -
- - +
+
{toolName}
+
{`ID: ${toolId}`}
+ {toolDesc &&
{`简介:${toolDesc}`}
} +
+
+
+ + } + style={{ background: COLOR_BACKGROUND }} + /> +
+ +
{authorName}
+
+
+
+ {platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && ( + + + + )} + {platform === 'DESKTOP' && supportPlatform.includes('WEB') && ( + + + + )} + {platform === 'WEB' && supportPlatform.includes('DESKTOP') && ( + + + + )} + + + +
+ + + {contextHolder} + ) } @@ -231,25 +340,58 @@ const Store = () => {
{!toolData.length &&
未找到任何工具
} - {toolData?.map((value) => ( - + {toolData + ?.reduce((previousValue: ToolVo[], currentValue) => { + if ( + !previousValue.some( + (value) => + value.author.id === currentValue.author.id && + value.toolId === currentValue.toolId + ) + ) { + previousValue.push(currentValue) } - toolName={value.name} - toolId={value.toolId} - toolDesc={value.description} - url={`/view/${value.author.username}/${value.toolId}`} - authorName={value.author.userInfo.nickname} - authorAvatar={value.author.userInfo.avatar} - authorUsername={value.author.username} - ver={value.ver} - /> - ))} + return previousValue + }, []) + .map((item) => { + const tools = toolData.filter( + (value) => + value.author.id === item.author.id && + value.toolId === item.toolId + ) + const webTool = tools.find((value) => value.platform === 'WEB') + const desktopTool = tools.find( + (value) => value.platform === 'DESKTOP' + ) + const androidTool = tools.find( + (value) => value.platform === 'ANDROID' + ) + const firstTool = + (checkDesktop() + ? desktopTool || webTool + : webTool || desktopTool) || androidTool + + return ( + + } + toolName={firstTool!.name} + toolId={firstTool!.toolId} + toolDesc={firstTool!.description} + authorName={firstTool!.author.userInfo.nickname} + authorAvatar={firstTool!.author.userInfo.avatar} + authorUsername={firstTool!.author.username} + ver={firstTool!.ver} + platform={firstTool!.platform} + supportPlatform={tools.map((value) => value.platform)} + /> + ) + })} {hasNextPage && }
diff --git a/src/renderer/src/pages/Tools/User.tsx b/src/renderer/src/pages/Tools/User.tsx index 67d2508..2d1c9e5 100644 --- a/src/renderer/src/pages/Tools/User.tsx +++ b/src/renderer/src/pages/Tools/User.tsx @@ -1,12 +1,15 @@ import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode } from 'react' import VanillaTilt, { TiltOptions } from 'vanilla-tilt' +import protocolCheck from 'custom-protocol-check' import Icon from '@ant-design/icons' import '@/assets/css/pages/tools/user.scss' import { COLOR_BACKGROUND, + COLOR_MAIN, DATABASE_NO_RECORD_FOUND, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants' +import { checkDesktop } from '@/util/common' import { r_sys_user_info_get_basic } from '@/services/system' import { r_tool_store_get_by_username } from '@/services/tool' import FitFullscreen from '@/components/common/FitFullscreen' @@ -21,9 +24,10 @@ interface CommonCardProps toolId: string toolDesc: string options?: TiltOptions - url: string authorUsername: string ver: string + platform: Platform + supportPlatform: Platform[] } const CommonCard = ({ @@ -41,12 +45,14 @@ const CommonCard = ({ ['max-glare']: 0.3, scale: 1.03 }, - url, authorUsername, ver, + platform, + supportPlatform, ...props }: CommonCardProps) => { const navigate = useNavigate() + const [modal, contextHolder] = AntdModal.useModal() const cardRef = useRef(null) useEffect(() => { @@ -54,38 +60,142 @@ const CommonCard = ({ }, [options]) const handleCardOnClick = () => { - url && navigate(url) + if (!checkDesktop() && platform === 'DESKTOP') { + void message.warning('此应用需要桌面端环境,请在桌面端打开') + return + } + if (platform === 'ANDROID') { + void modal.info({ + icon: , + title: 'Android 端', + centered: true, + maskClosable: true, + content: ( + + + 请使用手机端扫描上方二维码 + + ) + }) + return + } + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) } const handleOnSourceBtnClick = (e: MouseEvent) => { e.stopPropagation() - navigate(`/source/${authorUsername}/${toolId}`) + navigate( + `/source/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) + } + + const handleOnAndroidBtnClick = (e: MouseEvent) => { + e.stopPropagation() + void modal.info({ + icon: , + title: 'Android 端', + centered: true, + maskClosable: true, + content: ( + + + 请使用手机端扫描上方二维码 + + ) + }) + } + + const handleOnDesktopBtnClick = (e: MouseEvent) => { + e.stopPropagation() + if (!checkDesktop()) { + void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 }) + protocolCheck( + `oxygen://openurl/view/${authorUsername}/${toolId}`, + () => { + void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') + void message.destroy('LOADING') + }, + () => { + void message.destroy('LOADING') + }, + 2000, + () => { + void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') + void message.destroy('LOADING') + } + ) + return + } + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) + } + + const handleOnWebBtnClick = (e: MouseEvent) => { + e.stopPropagation() + navigate( + `/view/${authorUsername}/${toolId}${platform !== import.meta.env.VITE_PLATFORM ? `?platform=${platform}` : ''}` + ) } return ( - - -
{icon}
-
- V{ver} -
-
-
{toolName}
-
{`ID: ${toolId}`}
- {toolDesc &&
{`简介:${toolDesc}`}
} -
-
- - - -
-
-
+ <> + + +
{icon}
+
+ + {platform.slice(0, 1)}-{ver} + +
+
+
{toolName}
+
{`ID: ${toolId}`}
+ {toolDesc &&
{`简介:${toolDesc}`}
} +
+
+ {platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && ( + + + + )} + {platform === 'DESKTOP' && supportPlatform.includes('WEB') && ( + + + + )} + {platform === 'WEB' && supportPlatform.includes('DESKTOP') && ( + + + + )} + + + +
+
+
+ {contextHolder} + ) } @@ -266,23 +376,56 @@ const User = () => { {!toolData.length && (
该开发者暂未发布任何工具
)} - {toolData?.map((value) => ( - + {toolData + ?.reduce((previousValue: ToolVo[], currentValue) => { + if ( + !previousValue.some( + (value) => + value.author.id === currentValue.author.id && + value.toolId === currentValue.toolId + ) + ) { + previousValue.push(currentValue) } - toolName={value.name} - toolId={value.toolId} - toolDesc={value.description} - url={`/view/${value.author.username}/${value.toolId}`} - authorUsername={value.author.username} - ver={value.ver} - /> - ))} + return previousValue + }, []) + .map((item) => { + const tools = toolData.filter( + (value) => + value.author.id === item.author.id && + value.toolId === item.toolId + ) + const webTool = tools.find((value) => value.platform === 'WEB') + const desktopTool = tools.find( + (value) => value.platform === 'DESKTOP' + ) + const androidTool = tools.find( + (value) => value.platform === 'ANDROID' + ) + const firstTool = + (checkDesktop() + ? desktopTool || webTool + : webTool || desktopTool) || androidTool + + return ( + + } + toolName={firstTool!.name} + toolId={firstTool!.toolId} + toolDesc={firstTool!.description} + authorUsername={firstTool!.author.username} + ver={firstTool!.ver} + platform={firstTool!.platform} + supportPlatform={tools.map((value) => value.platform)} + /> + ) + })} {hasNextPage && }