diff --git a/package.json b/package.json index 645be18..f06f5db 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "dependencies": { "@ant-design/icons": "^6.0.0", "@dckj/react-better-modal": "^0.1.2", - "@haina/utils-commons": "https://research.hainatravel.com/npm/utils-commons-0.1.1.tgz", - "@haina/utils-pagespy": "https://research.hainatravel.com/npm/utils-pagespy-0.1.1.tgz", - "@haina/utils-request": "https://research.hainatravel.com/npm/utils-request-0.1.1.tgz", + "@haina/utils-commons": "https://research.hainatravel.com/npm/utils-commons-0.1.2.tgz", + "@haina/utils-pagespy": "https://research.hainatravel.com/npm/utils-pagespy-0.1.2.tgz", + "@haina/utils-request": "https://research.hainatravel.com/npm/utils-request-0.1.2.tgz", "@lexical/code": "^0.34.0", "@lexical/hashtag": "^0.34.0", "@lexical/html": "^0.34.0", diff --git a/src/components/Shorturlchange.js b/src/components/Shorturlchange.js new file mode 100644 index 0000000..cf03f36 --- /dev/null +++ b/src/components/Shorturlchange.js @@ -0,0 +1,78 @@ +import { message } from 'antd'; + +const Shorturlchange = async (longUrl) => { + const apiPrefix = { + "japanhighlights.com": "https://www.japanhighlights.com/index.php", + "chinahighlights.com": "https://www.chinahighlights.com/guide-use.php", + "highlightstravel.com": "https://www.highlightstravel.com/index.php", + "asiahighlights.com": "https://www.asiahighlights.com/index.php", + "globalhighlights.com": "https://www.globalhighlights.com/index.php", + }; + + const fetchNowConversationsitems = async (base64Url, apiUrl) => { + try { + const formData = new FormData(); + formData.append('url', base64Url); + formData.append('type', 'info'); + const response = await fetch(`${apiUrl}/apps/short_link/index/create`, { + method: 'POST', + body: formData, + }); + const data = await response.json(); + if (data[0].name == 'ok') { + return data[0].value; + } + return null; + } catch (error) { + console.error('获取短链接转换内容失败:', error); + return null; + } + }; + + const urlConversion = async (longUrl) => { + if (!longUrl.trim()) { + message.error('不是有效的长链接'); + return null; + } + + const { base64Url, extracted1, extracted2 } = urlBase64(longUrl); + + if (base64Url && extracted1 && extracted2) { + const apiUrl = apiPrefix[extracted2]; + + const data = await fetchNowConversationsitems(base64Url, apiUrl); + if (data) { + const resultShortUrl = extracted1 + data.isl_link; + message.success('转换成功!'); + return resultShortUrl; + } else { + message.error('转换失败,请检查输入的URL是否正确'); + return null; + } + } else { + message.error('URL格式不正确,请输入完整的URL'); + return null; + } + }; + + const urlBase64 = (longUrl) => { + try { + const extracted1 = longUrl.match(/^.*?com/)?.[0] || ''; + const extracted2 = longUrl.match(/https:\/\/www\.([^\/]+)/)?.[1] || ''; + + const encoder = new TextEncoder(); + const utf8Bytes = encoder.encode(longUrl); + const base64Url = btoa(String.fromCharCode(...utf8Bytes)); + + return { base64Url, extracted1, extracted2 }; + } catch (error) { + message.error('转换失败,请检查输入的URL是否正确'); + console.error('URL转换错误:', error); + return { base64Url: '', extracted1: '', extracted2: '' }; + } + } + + return await urlConversion(longUrl); +} + +export default Shorturlchange; \ No newline at end of file diff --git a/src/stores/UrlStore.js b/src/stores/UrlStore.js new file mode 100644 index 0000000..d282b6c --- /dev/null +++ b/src/stores/UrlStore.js @@ -0,0 +1,21 @@ +import { create } from 'zustand' +import { devtools } from 'zustand/middleware' +const useUrlStore = create(devtools((set, get) => ({ + + drawerOpen: false, + + openDrawer: () => { + set(() => ({ + drawerOpen: true + })) + }, + + closeDrawer: () => { + set(() => ({ + drawerOpen: false + })) + }, + +}), { name: 'urlStore' })) + +export default useUrlStore diff --git a/src/views/AuthApp.jsx b/src/views/AuthApp.jsx index 3e8b56e..0a478ea 100644 --- a/src/views/AuthApp.jsx +++ b/src/views/AuthApp.jsx @@ -25,6 +25,7 @@ import FetchEmailWorker from './../workers/fetchEmailWorker?worker&url' import { useGlobalNotify } from '@/hooks/useGlobalNotify' import GeneratePaymentDrawer from './Conversations/Online/Components/GeneratePaymentDrawer' import GenerateAutoDocDrawer from './Conversations/Online/Components/GenerateAutoDocDrawer' +import GenerateShorturlDrawer from './Conversations/Online/Components/GenerateShorturlDrawer' import LogUploader from '@/components/LogUploader' import { BUILD_VERSION } from '@/config' @@ -162,6 +163,7 @@ function AuthApp() { + diff --git a/src/views/Conversations/Online/Components/GenerateShorturlDrawer.jsx b/src/views/Conversations/Online/Components/GenerateShorturlDrawer.jsx new file mode 100644 index 0000000..ad71941 --- /dev/null +++ b/src/views/Conversations/Online/Components/GenerateShorturlDrawer.jsx @@ -0,0 +1,14 @@ +import { createContext, useEffect, useState } from 'react' +import { Drawer } from 'antd' +import useUrlStore from '@/stores/UrlStore' +import ShorturlConversion from '@/views/accounts/ShorturlConversion' + +const GenerateShorturlDrawer = ({ ...props }) => { + const [openShorturlDrawer, closeShorturlDrawer, shorturlDrawerOpen] = useUrlStore((state) => [state.openDrawer, state.closeDrawer, state.drawerOpen]) + return ( + closeShorturlDrawer()} open={shorturlDrawerOpen}> + + + ) +} +export default GenerateShorturlDrawer \ No newline at end of file diff --git a/src/views/DesktopApp.jsx b/src/views/DesktopApp.jsx index 460b63b..2fd84b6 100644 --- a/src/views/DesktopApp.jsx +++ b/src/views/DesktopApp.jsx @@ -1,6 +1,7 @@ import useAuthStore from '@/stores/AuthStore' import useSnippetStore from '@/stores/SnippetStore' import { useOrderStore } from '@/stores/OrderStore' +import useUrlStore from '@/stores/UrlStore' import useConversationStore from '@/stores/ConversationStore' import { useThemeContext } from '@/stores/ThemeContext' import { DownOutlined } from '@ant-design/icons' @@ -59,12 +60,23 @@ function DesktopApp() { state.closeDrawer, state.drawerOpen, ]) + const [ + openShorturlDrawer, + closeShorturlDrawer, + shorturlDrawerOpen, + ] = useUrlStore((state) => [ + state.openDrawer, + state.closeDrawer, + state.drawerOpen, + ]) const onClick = ({ key }) => { if (key === 'snippet-list') { openSnippetDrawer() } else if (key == 'generate-payment') { openPaymentDrawer() + } else if (key === 'shorturl-conversion') { + openShorturlDrawer() } } @@ -183,10 +195,15 @@ function DesktopApp() { label: 个人资料, key: 'profile', }, + { type: 'divider' }, { label: '支付链接', key: 'generate-payment', }, + { + label: '短链接转换', + key: 'shorturl-conversion', + }, { label: '图文集', key: 'snippet-list', diff --git a/src/views/accounts/ShorturlConversion.jsx b/src/views/accounts/ShorturlConversion.jsx new file mode 100644 index 0000000..bb31d52 --- /dev/null +++ b/src/views/accounts/ShorturlConversion.jsx @@ -0,0 +1,85 @@ +import { Input, Button, Space, message, Typography } from 'antd' +import { CopyOutlined, LinkOutlined } from '@ant-design/icons' +import Shorturlchange from '@/components/Shorturlchange' +import { useState } from 'react' + +const { Title, Text } = Typography + +function ShorturlConversion() { + const [longUrl, setLongUrl] = useState('') + const [shortUrl, setShortUrl] = useState('') + + function copyToClipboard(text) { + const textarea = document.createElement('textarea'); + textarea.value = text; + // 隐藏文本域(避免页面闪烁) + textarea.style.position = 'fixed'; + textarea.style.opacity = 0; + document.body.appendChild(textarea); + // 选中并复制 + textarea.select(); + try { + const successful = document.execCommand('copy'); + if (successful) { + message.success('已复制'); + } else { + throw new Error('复制失败'); + } + } catch (err) { + message.error('复制失败,请手动复制'); + } finally { + // 移除临时文本域 + document.body.removeChild(textarea); + } + } + + const handleConvert = async () => { + const result = await Shorturlchange(longUrl); + if (result) { + setShortUrl(result); + } + }; + + return ( + +
+ 长链接: + setLongUrl(e.target.value)} + prefix={} + size="large" + /> +
+ +
+ 短链接: + } + onClick={() => copyToClipboard(shortUrl)} + /> + ) + } + size="large" + /> +
+
+ ); +} + +export default ShorturlConversion \ No newline at end of file