ERPTurbo_Client/packages/app-client/src/components/biz/ImagePreview.tsx
shenyifei e3b6f056b8 feat(components): 添加图片预览组件并重构交付单预览功能
- 新增 ImagePreview 组件支持图片缩放和拖拽功能
- 将 Step2Preview 中的预览逻辑提取到 ImagePreview 组件
- 移除 Step3Success 中的重复利润计算代码
- 添加利润明细表预览功能到成本差异模块
- 优化上传组件支持 PDF 文件类型显示
- 修复 OCR 相机页面闪关灯默认状态问题
- 更新供应商信息识别结果字段映射
- 调整版本号至 v0.0.75
2026-01-14 11:35:00 +08:00

140 lines
4.1 KiB
TypeScript

import React, { useState } from "react";
import { Text, View } from "@tarojs/components";
import { Button } from "@nutui/nutui-react-taro";
interface IImagePreview {
children: React.ReactNode;
}
export default function ImagePreview(props: IImagePreview) {
const { children } = props;
const [scale, setScale] = useState(0.5); // 缩放比例
const [touchInfo, setTouchInfo] = useState({
startDistance: 0,
startScale: 0.5,
}); // 触摸信息
const [position, setPosition] = useState({ x: 0, y: -(29.7 * 37.8 * 0.5) }); // 当前位置
const [startPosition, setStartPosition] = useState({ x: 0, y: 0 }); // 起始位置
// 放大
const zoomIn = () => {
setScale((prev) => Math.min(prev + 0.1, 3)); // 最大放大到3倍
};
// 缩小
const zoomOut = () => {
setScale((prev) => Math.max(prev - 0.1, 0.5)); // 最小缩小到0.5倍
};
// 重置缩放
const resetZoom = () => {
setScale(0.5);
setPosition({ x: 0, y: -(29.7 * 37.8 * 0.5) }); // 重置位置
};
// 计算两点间距离
const getDistance = (touches: any) => {
const dx = touches[0].clientX - touches[1].clientX;
const dy = touches[0].clientY - touches[1].clientY;
return Math.sqrt(dx * dx + dy * dy);
};
// 处理触摸开始事件
const handleTouchStart = (e: any) => {
const touches = e.touches;
if (touches.length === 2) {
// 双指触摸,记录初始距离和当前缩放值
const distance = getDistance(touches);
setTouchInfo({
startDistance: distance,
startScale: scale,
});
} else if (touches.length === 1) {
// 单指触摸,记录起始位置
setStartPosition({
x: touches[0].clientX - position.x,
y: touches[0].clientY - position.y,
});
}
};
// 处理触摸移动事件
const handleTouchMove = (e: any) => {
const touches = e.touches;
e.preventDefault(); // 阻止默认滚动行为
if (touches.length === 2) {
// 双指触摸,计算缩放比例
const currentDistance = getDistance(touches);
const newScale =
touchInfo.startScale * (currentDistance / touchInfo.startDistance);
// 限制缩放范围在0.5到3之间
setScale(Math.min(Math.max(0.5, newScale), 3));
} else if (touches.length === 1 && scale > 0.5) {
// 单指触摸且已放大,允许拖动
setPosition({
x: touches[0].clientX - startPosition.x,
y: touches[0].clientY - startPosition.y,
});
}
};
// 处理触摸结束事件
const handleTouchEnd = (e: any) => {
// 可以在这里添加触摸结束后的处理逻辑
console.log("Touch ended", e);
};
return (
<View className="flex flex-1 flex-col items-center overflow-hidden">
{/* 缩放控制按钮 */}
<View className="mb-2 flex w-full justify-center gap-2 bg-white p-2">
<Button size="small" onClick={zoomOut}>
-
</Button>
<Button size="small" onClick={resetZoom}>
</Button>
<Button size="small" onClick={zoomIn}>
+
</Button>
<Text className="ml-2 flex items-center">
{Math.round(scale * 100)}%
</Text>
</View>
{/* 预览区域 */}
<View
className="flex-1 overflow-auto"
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
overflow: "hidden",
}}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
<View
id="preview"
className="rounded-lg bg-white shadow-md"
style={{
width: "21cm",
padding: "2cm 1cm 0 1cm",
margin: "0 auto",
height: "29.7cm", // A4纸高度
transform: `scale(${scale}) translate(${position.x}px, ${position.y}px)`,
transformOrigin: "center center",
transition:
scale === touchInfo.startScale ? "transform 0.2s ease" : "none",
}}
>
{children}
</View>
</View>
</View>
);
}