feat: 添加 H5 跳转小程序功能

- 后端: 新增 JS-SDK 签名接口和 URL Scheme 生成接口
- 前端: 创建 MiniProgramLauncher 组件,支持环境自适应
  - 微信内 H5: 使用 wx-open-launch-weapp 开放标签
  - 外部浏览器: 使用 URL Scheme 拉起微信
  - PC 端: 显示小程序码引导扫码
- 引入微信 JS-SDK (jweixin-1.6.0.js)
- 新增 miniprogramService 服务层封装 API 调用

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-12 16:56:04 +08:00
parent bbe4cca2d9
commit 9f99ea7aee
8 changed files with 1200 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
/**
* 小程序码显示组件
* 用于 PC 端显示小程序码供用户扫描
*/
import React from 'react';
import {
Box,
VStack,
Text,
Image,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
useDisclosure,
Icon,
} from '@chakra-ui/react';
import { FiSmartphone } from 'react-icons/fi';
// 默认小程序码图片(可替换为实际的小程序码)
// 注意:需要在微信公众平台生成小程序码图片
const DEFAULT_QR_CODE = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=weixin://dl/business/?t=placeholder';
/**
* PC 端小程序码显示组件
* @param {Object} props
* @param {string} [props.path] - 小程序页面路径(用于显示提示)
* @param {string} [props.qrCodeUrl] - 小程序码图片 URL
* @param {React.ReactNode} props.children - 按钮内容
* @param {string} [props.title] - 弹窗标题
* @param {string} [props.description] - 描述文案
* @param {Object} [props.buttonProps] - 按钮属性
*/
const QRCodeDisplay = ({
path = '',
qrCodeUrl,
children,
title = '扫码打开小程序',
description = '请使用微信扫描下方二维码',
buttonProps = {},
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
// 使用传入的二维码或默认二维码
const qrCode = qrCodeUrl || DEFAULT_QR_CODE;
return (
<>
<Button
onClick={onOpen}
colorScheme="green"
leftIcon={<Icon as={FiSmartphone} />}
{...buttonProps}
>
{children || '打开小程序'}
</Button>
<Modal isOpen={isOpen} onClose={onClose} isCentered size="sm">
<ModalOverlay />
<ModalContent>
<ModalHeader textAlign="center">{title}</ModalHeader>
<ModalCloseButton />
<ModalBody pb={6}>
<VStack spacing={4}>
{/* 小程序码图片 */}
<Box
p={4}
bg="white"
borderRadius="xl"
boxShadow="lg"
border="1px solid"
borderColor="gray.100"
>
<Image
src={qrCode}
alt="小程序码"
boxSize="200px"
objectFit="contain"
fallback={
<Box
boxSize="200px"
display="flex"
alignItems="center"
justifyContent="center"
bg="gray.100"
borderRadius="md"
>
<Text color="gray.500" fontSize="sm">
加载中...
</Text>
</Box>
}
/>
</Box>
{/* 说明文案 */}
<VStack spacing={1}>
<Text color="gray.700" fontWeight="medium">
{description}
</Text>
<Text color="gray.500" fontSize="sm">
打开微信扫一扫即可访问
</Text>
</VStack>
{/* 提示信息 */}
<Box
w="100%"
p={3}
bg="green.50"
borderRadius="md"
border="1px solid"
borderColor="green.100"
>
<VStack spacing={1} align="start">
<Text fontSize="xs" color="green.700" fontWeight="medium">
温馨提示
</Text>
<Text fontSize="xs" color="green.600">
请确保手机已安装微信 App
</Text>
<Text fontSize="xs" color="green.600">
扫码后点击"打开小程序"按钮
</Text>
{path && (
<Text fontSize="xs" color="green.600">
将跳转到: {path}
</Text>
)}
</VStack>
</Box>
</VStack>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
export default QRCodeDisplay;