Files
vf_react/src/components/MiniProgramLauncher/UrlSchemeLauncher.js
zdl b2100d6f75 refactor(icons): 迁移 components 目录图标到 lucide-react
- @chakra-ui/icons → lucide-react
- react-icons → lucide-react
- 涉及 49 个组件文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-25 13:00:40 +08:00

210 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* URL Scheme 跳转组件
* 用于外部浏览器跳转小程序
*/
import React, { useState, useCallback } from 'react';
import {
Box,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
ModalCloseButton,
Text,
VStack,
useDisclosure,
useToast,
Icon,
} from '@chakra-ui/react';
import { ExternalLink, Copy, Check } from 'lucide-react';
import { isIOSDevice } from './hooks/useWechatEnvironment';
// 小程序 AppID
const MINIPROGRAM_APPID = 'wx0edeaab76d4fa414';
/**
* 生成明文 URL Scheme
* 格式weixin://dl/business/?appid=APPID&path=PATH&query=QUERY
* 注意path 不需要 URL encode否则微信无法识别
*/
const generatePlainUrlScheme = (path, query) => {
let url = `weixin://dl/business/?appid=${MINIPROGRAM_APPID}&path=${path || ''}`;
if (query) {
url += `&query=${encodeURIComponent(query)}`;
}
return url;
};
/**
* URL Scheme 跳转组件
* @param {Object} props
* @param {string} [props.path] - 小程序页面路径
* @param {string} [props.query] - 页面参数
* @param {React.ReactNode} props.children - 按钮内容
* @param {Function} [props.onSuccess] - 跳转成功回调
* @param {Function} [props.onError] - 跳转失败回调
* @param {Object} [props.buttonProps] - 按钮属性
* @param {Object} [props.buttonStyle] - 按钮内联样式
*/
const UrlSchemeLauncher = ({
path = '',
query = '',
children,
onSuccess,
onError,
buttonProps = {},
buttonStyle = {},
}) => {
const [loading, setLoading] = useState(false);
const [openlink, setOpenlink] = useState(null);
const [copied, setCopied] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
const toast = useToast();
const isIOS = isIOSDevice();
// 处理点击跳转
const handleLaunch = useCallback(async () => {
try {
setLoading(true);
// 生成明文 URL Scheme不需要后端 API
const scheme = generatePlainUrlScheme(path, query);
console.log('[UrlSchemeLauncher] 生成的 scheme:', scheme);
setOpenlink(scheme);
// 尝试直接跳转
window.location.href = scheme;
const success = true;
if (success) {
onSuccess?.();
// iOS 上可能会弹出确认框,显示引导弹窗
if (isIOS) {
setTimeout(() => {
onOpen();
}, 500);
}
} else {
// 跳转失败,显示引导弹窗
onOpen();
}
} catch (error) {
console.error('[UrlSchemeLauncher] error:', error);
toast({
title: '跳转失败',
description: error.message || '请稍后重试',
status: 'error',
duration: 3000,
});
onError?.(error);
} finally {
setLoading(false);
}
}, [path, query, onSuccess, onError, onOpen, toast, isIOS]);
// 复制链接
const handleCopy = useCallback(async () => {
if (!openlink) return;
try {
await navigator.clipboard.writeText(openlink);
setCopied(true);
toast({
title: '已复制',
description: '请在微信中打开',
status: 'success',
duration: 2000,
});
setTimeout(() => setCopied(false), 2000);
} catch (error) {
toast({
title: '复制失败',
description: '请手动复制',
status: 'error',
duration: 2000,
});
}
}, [openlink, toast]);
// 再次尝试跳转
const handleRetry = useCallback(() => {
if (openlink) {
window.location.href = openlink;
}
}, [openlink]);
return (
<>
<Button
onClick={handleLaunch}
isLoading={loading}
loadingText="正在跳转..."
colorScheme="green"
leftIcon={<Icon as={ExternalLink} />}
style={buttonStyle}
{...buttonProps}
>
{children || '打开小程序'}
</Button>
{/* 引导弹窗 */}
<Modal isOpen={isOpen} onClose={onClose} isCentered size="sm">
<ModalOverlay />
<ModalContent mx={4}>
<ModalHeader>打开小程序</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4} align="stretch">
<Text color="gray.600" fontSize="sm">
{isIOS
? '如果没有自动跳转,请点击下方按钮重试'
: '请在弹出的对话框中选择"打开微信"'}
</Text>
<Box
p={3}
bg="gray.50"
borderRadius="md"
fontSize="xs"
color="gray.500"
>
<Text fontWeight="medium" mb={1}>提示</Text>
<Text>1. 确保已安装微信</Text>
<Text>2. 点击"打开微信"按钮</Text>
<Text>3. 如果没有反应请复制链接到微信打开</Text>
</Box>
</VStack>
</ModalBody>
<ModalFooter>
<VStack spacing={2} width="100%">
<Button
colorScheme="green"
width="100%"
onClick={handleRetry}
leftIcon={<Icon as={ExternalLink} />}
>
打开微信
</Button>
<Button
variant="outline"
width="100%"
onClick={handleCopy}
leftIcon={<Icon as={copied ? Check : Copy} />}
>
{copied ? '已复制' : '复制链接'}
</Button>
</VStack>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UrlSchemeLauncher;