添加8.9页面
This commit is contained in:
109
electron/main.js
109
electron/main.js
@@ -1,8 +1,12 @@
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const { app, BrowserWindow, ipcMain } = require("electron");
|
||||
const path = require("path");
|
||||
const https = require("https");
|
||||
const http = require("http");
|
||||
|
||||
let mainWindow;
|
||||
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1080,
|
||||
height: 1920,
|
||||
webPreferences: {
|
||||
@@ -17,14 +21,109 @@ function createWindow() {
|
||||
const isDev = !app.isPackaged;
|
||||
|
||||
if (isDev) {
|
||||
win.loadURL("http://localhost:5173");
|
||||
mainWindow.loadURL("http://localhost:5173");
|
||||
// 打开开发者工具
|
||||
win.webContents.openDevTools();
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
win.loadFile(path.join(__dirname, "../dist/index.html"));
|
||||
mainWindow.loadFile(path.join(__dirname, "../dist/index.html"));
|
||||
}
|
||||
}
|
||||
|
||||
// 处理PDF获取请求(绕过CORS)
|
||||
ipcMain.handle("fetch-pdf", async (event, pdfUrl) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const protocol = pdfUrl.startsWith("https") ? https : http;
|
||||
|
||||
protocol
|
||||
.get(pdfUrl, (response) => {
|
||||
// 处理重定向
|
||||
if (response.statusCode === 301 || response.statusCode === 302) {
|
||||
const redirectUrl = response.headers.location;
|
||||
protocol
|
||||
.get(redirectUrl, (redirectResponse) => {
|
||||
const chunks = [];
|
||||
redirectResponse.on("data", (chunk) => chunks.push(chunk));
|
||||
redirectResponse.on("end", () => {
|
||||
const buffer = Buffer.concat(chunks);
|
||||
resolve({
|
||||
success: true,
|
||||
data: buffer.toString("base64"),
|
||||
});
|
||||
});
|
||||
redirectResponse.on("error", (error) => {
|
||||
reject({ success: false, error: error.message });
|
||||
});
|
||||
})
|
||||
.on("error", (error) => {
|
||||
reject({ success: false, error: error.message });
|
||||
});
|
||||
} else {
|
||||
const chunks = [];
|
||||
response.on("data", (chunk) => chunks.push(chunk));
|
||||
response.on("end", () => {
|
||||
const buffer = Buffer.concat(chunks);
|
||||
resolve({
|
||||
success: true,
|
||||
data: buffer.toString("base64"),
|
||||
});
|
||||
});
|
||||
response.on("error", (error) => {
|
||||
reject({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
})
|
||||
.on("error", (error) => {
|
||||
reject({ success: false, error: error.message });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 处理PDF打印请求
|
||||
ipcMain.handle("print-pdf", async (event, pdfUrl) => {
|
||||
try {
|
||||
// 创建一个隐藏的窗口用于加载PDF
|
||||
const printWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 加载PDF
|
||||
await printWindow.loadURL(pdfUrl);
|
||||
|
||||
// 等待页面加载完成
|
||||
await new Promise((resolve) => {
|
||||
printWindow.webContents.on("did-finish-load", resolve);
|
||||
});
|
||||
|
||||
// 静默打印(直接调用系统打印对话框)
|
||||
printWindow.webContents.print(
|
||||
{
|
||||
silent: false, // 显示打印对话框
|
||||
printBackground: true,
|
||||
margins: {
|
||||
marginType: "none",
|
||||
},
|
||||
},
|
||||
(success, errorType) => {
|
||||
printWindow.close();
|
||||
if (!success) {
|
||||
console.error("Print failed:", errorType);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Print error:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
contextBridge.exposeInMainWorld("electronAPI", {
|
||||
// 在这里暴露安全的 API 给渲染进程
|
||||
// example: sendMessage: (message) => ipcRenderer.send('message', message)
|
||||
// 获取PDF(绕过CORS)
|
||||
fetchPdf: (pdfUrl) => ipcRenderer.invoke("fetch-pdf", pdfUrl),
|
||||
// 打印PDF
|
||||
printPdf: (pdfUrl) => ipcRenderer.invoke("print-pdf", pdfUrl),
|
||||
});
|
||||
|
||||
1807
package-lock.json
generated
1807
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,13 @@
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^7.9.6"
|
||||
"react-pdf": "5.7.2",
|
||||
"react-router-dom": "6.11.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.6",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-pdf": "^6.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"concurrently": "^8.2.2",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
@@ -10,6 +10,8 @@ import U4 from "./pages/U4/u4";
|
||||
|
||||
import UI6 from "./pages/UI6/UI6";
|
||||
import UI7 from "./pages/UI7/UI7";
|
||||
import UI8 from "./pages/UI8/UI8";
|
||||
import UI9 from "./pages/UI9/UI9";
|
||||
|
||||
function App() {
|
||||
const [time, setTime] = useState<string>(() => formatDate(new Date()));
|
||||
@@ -54,6 +56,8 @@ function App() {
|
||||
<Route path="/u4" element={<U4 />} />
|
||||
<Route path="/UI6" element={<UI6 />} />
|
||||
<Route path="/UI7" element={<UI7 />} />
|
||||
<Route path="/UI8" element={<UI8 />} />
|
||||
<Route path="/UI9" element={<UI9 />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
src/assets/UI9A.png
Normal file
BIN
src/assets/UI9A.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/UI9B.png
Normal file
BIN
src/assets/UI9B.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
BIN
src/assets/ui8A.png
Normal file
BIN
src/assets/ui8A.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/ui8B.png
Normal file
BIN
src/assets/ui8B.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
10
src/electron.d.ts
vendored
Normal file
10
src/electron.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Electron API 类型声明
|
||||
interface ElectronAPI {
|
||||
fetchPdf: (pdfUrl: string) => Promise<{ success: boolean; data?: string; error?: string }>;
|
||||
printPdf: (pdfUrl: string) => Promise<{ success: boolean; error?: string }>;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
electronAPI?: ElectronAPI;
|
||||
}
|
||||
|
||||
64
src/main.tsx
64
src/main.tsx
@@ -1,3 +1,67 @@
|
||||
// Polyfill for Promise.withResolvers (ES2024) - needed for Electron 22
|
||||
if (!(Promise as any).withResolvers) {
|
||||
(Promise as any).withResolvers = function <T>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||
let reject!: (reason?: any) => void;
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
};
|
||||
}
|
||||
|
||||
// Polyfill for URL.parse (Node.js url.parse compatibility) - needed for Electron 22
|
||||
// This creates a global url object with parse method for pdfjs-dist compatibility
|
||||
if (typeof URL !== "undefined" && !(URL as any).parse) {
|
||||
(URL as any).parse = function (urlString: string) {
|
||||
try {
|
||||
const urlObj = new URL(urlString, typeof window !== "undefined" ? window.location.href : undefined);
|
||||
const params: Record<string, string> = {};
|
||||
urlObj.searchParams.forEach((value, key) => {
|
||||
params[key] = value;
|
||||
});
|
||||
return {
|
||||
protocol: urlObj.protocol.replace(":", ""),
|
||||
slashes: true,
|
||||
auth: urlObj.username && urlObj.password ? `${urlObj.username}:${urlObj.password}` : null,
|
||||
host: urlObj.host,
|
||||
hostname: urlObj.hostname,
|
||||
hash: urlObj.hash || null,
|
||||
search: urlObj.search || null,
|
||||
query: params,
|
||||
pathname: urlObj.pathname,
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
href: urlObj.href,
|
||||
port: urlObj.port || null,
|
||||
};
|
||||
} catch (_err) {
|
||||
const match = urlString.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/);
|
||||
if (!match) return null;
|
||||
const params: Record<string, string> = {};
|
||||
if (match[7]) {
|
||||
new URLSearchParams(match[7]).forEach((value, key) => {
|
||||
params[key] = value;
|
||||
});
|
||||
}
|
||||
return {
|
||||
protocol: match[2] || null,
|
||||
slashes: !!match[3],
|
||||
auth: null,
|
||||
host: match[4] || null,
|
||||
hostname: match[4] ? match[4].split(":")[0] : null,
|
||||
hash: match[8] || null,
|
||||
search: match[6] || null,
|
||||
query: params,
|
||||
pathname: match[5] || null,
|
||||
path: (match[5] || "") + (match[6] || ""),
|
||||
href: urlString,
|
||||
port: match[4] && match[4].includes(":") ? match[4].split(":")[1] : null,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { HashRouter } from "react-router-dom";
|
||||
|
||||
223
src/pages/UI8/UI8.css
Normal file
223
src/pages/UI8/UI8.css
Normal file
@@ -0,0 +1,223 @@
|
||||
.ui8-table-container {
|
||||
width: 754px;
|
||||
max-height: 881px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
/* 隐藏滚动条但保持滚动功能 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 和 Edge */
|
||||
}
|
||||
|
||||
.ui8-table-container::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
.ui8-table {
|
||||
width: 754px;
|
||||
border-collapse: collapse;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.ui8-table-header {
|
||||
color: rgba(0, 45, 93, 1);
|
||||
font-size: 32px;
|
||||
font-family: NotoSansCJKsc-Bold;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(0, 45, 93, 0.2);
|
||||
background-color: rgba(233, 242, 245, 1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ui8-table-dept {
|
||||
width: 200px;
|
||||
background-color: #b12651;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ui8-table-project {
|
||||
width: 554px;
|
||||
background-color: #053875;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ui8-table-row {
|
||||
border-bottom: 2px solid #d3d3d3;
|
||||
border-right: 2px solid #d3d3d3;
|
||||
}
|
||||
|
||||
.ui8-table-dept-cell {
|
||||
color: black;
|
||||
background-color: #daeef2;
|
||||
font-size: 24px;
|
||||
font-family: NotoSansCJKsc-Medium;
|
||||
font-weight: 500;
|
||||
padding-left: 20px;
|
||||
border-right: 2px solid #d3d3d3;
|
||||
border-left: 2px solid #d3d3d3;
|
||||
}
|
||||
|
||||
.ui8-table-project-cell {
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
padding: 0;
|
||||
font-family: NotoSansCJKsc-Regular;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ui8-project-item {
|
||||
border-bottom: 2px solid #d3d3d3;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ui8-project-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* PDF 展示容器 */
|
||||
.ui8-pdf-container {
|
||||
height: 1000px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
touch-action: pan-y; /* 允许垂直触摸滑动 */
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
.ui8-pdf-page-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
max-height: 1200px;
|
||||
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* 向上滑动动画(下一页) */
|
||||
.ui8-pdf-page-wrapper.slide-up {
|
||||
animation: slideUpOut 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUpOut {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 向下滑动动画(上一页) */
|
||||
.ui8-pdf-page-wrapper.slide-down {
|
||||
animation: slideDownOut 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideDownOut {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui8-pdf-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ui8-pdf-page canvas {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes swipeHint {
|
||||
0%, 100% {
|
||||
opacity: 0.65;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
/* PDF 翻页控制 */
|
||||
.ui8-pdf-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ui8-page-info {
|
||||
font-size: 26px;
|
||||
font-family: NotoSansCJKsc-Bold;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 45, 93, 1);
|
||||
min-width: 140px;
|
||||
text-align: center;
|
||||
letter-spacing: 1px;
|
||||
transition: transform 0.2s ease;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.ui8-loading,
|
||||
.ui8-error {
|
||||
padding: 40px;
|
||||
font-size: 28px;
|
||||
font-family: NotoSansCJKsc-Medium;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: rgba(0, 45, 93, 1);
|
||||
}
|
||||
|
||||
.ui8-error {
|
||||
color: #b12651;
|
||||
}
|
||||
|
||||
.ui8-right-section {
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ui8-right-section :last-child {
|
||||
margin-top: 10px;
|
||||
margin-left: 100px;
|
||||
}
|
||||
229
src/pages/UI8/UI8.tsx
Normal file
229
src/pages/UI8/UI8.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
|
||||
import "./UI8.css";
|
||||
import "../../assets/css/basic.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import BackButton from "../../components/BackButton";
|
||||
import ConfirmButton from "../../components/ConfirmButton";
|
||||
import ui8A from "../../assets/ui8A.png";
|
||||
import ui8B from "../../assets/ui8B.png";
|
||||
|
||||
|
||||
// 设置 PDF.js worker(react-pdf 5.7.2 使用 pdfjs-dist 2.12.313)
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js`;
|
||||
|
||||
const PDF_URL = "https://alist.ambigrat.com/d/cos/test/testPdf.pdf?sign=mELe-vb-ShXHDCtZrP2Hw5nlOvEMEsNkJzaGUUyqDg4=:0";
|
||||
|
||||
const UI8: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [numPages, setNumPages] = useState<number>(0);
|
||||
const [pageNumber, setPageNumber] = useState<number>(1);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [isPrinting, setIsPrinting] = useState<boolean>(false);
|
||||
const [pdfData, setPdfData] = useState<string | null>(null);
|
||||
const [isAnimating, setIsAnimating] = useState<boolean>(false);
|
||||
const [animationDirection, setAnimationDirection] = useState<"up" | "down" | null>(null);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const touchStartRef = useRef<{ x: number; y: number } | null>(null);
|
||||
|
||||
// 加载PDF数据(绕过CORS)
|
||||
useEffect(() => {
|
||||
const loadPdf = async () => {
|
||||
if (!window.electronAPI?.fetchPdf) {
|
||||
// 非Electron环境,直接使用URL
|
||||
setPdfData(PDF_URL);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await window.electronAPI.fetchPdf(PDF_URL);
|
||||
if (result.success && result.data) {
|
||||
// 将base64转换为data URL
|
||||
setPdfData(`data:application/pdf;base64,${result.data}`);
|
||||
} else {
|
||||
setError(`PDF加载失败: ${result.error || "未知错误"}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("PDF fetch error:", err);
|
||||
setError("PDF加载失败,请检查网络连接");
|
||||
}
|
||||
};
|
||||
|
||||
loadPdf();
|
||||
}, []);
|
||||
|
||||
const handleBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
// 打印PDF功能
|
||||
const handleConfirm = async () => {
|
||||
if (!window.electronAPI?.printPdf) {
|
||||
alert("打印功能不可用,请在 Electron 环境中运行");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPrinting(true);
|
||||
try {
|
||||
const result = await window.electronAPI.printPdf(PDF_URL);
|
||||
if (!result.success) {
|
||||
alert(`打印失败: ${result.error || "未知错误"}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Print error:", error);
|
||||
alert("打印失败,请重试");
|
||||
} finally {
|
||||
setIsPrinting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
|
||||
setNumPages(numPages);
|
||||
setLoading(false);
|
||||
setError("");
|
||||
};
|
||||
|
||||
const onDocumentLoadError = (error: Error) => {
|
||||
setError("PDF 加载失败,请检查网络连接");
|
||||
setLoading(false);
|
||||
console.error("PDF load error:", error);
|
||||
};
|
||||
|
||||
const goToPrevPage = () => {
|
||||
if (isAnimating || pageNumber <= 1) return;
|
||||
setIsAnimating(true);
|
||||
setAnimationDirection("down");
|
||||
setTimeout(() => {
|
||||
setPageNumber((prev) => Math.max(1, prev - 1));
|
||||
setIsAnimating(false);
|
||||
setAnimationDirection(null);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const goToNextPage = () => {
|
||||
if (isAnimating || pageNumber >= numPages) return;
|
||||
setIsAnimating(true);
|
||||
setAnimationDirection("up");
|
||||
setTimeout(() => {
|
||||
setPageNumber((prev) => Math.min(numPages, prev + 1));
|
||||
setIsAnimating(false);
|
||||
setAnimationDirection(null);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 监听触摸事件实现上下滑动翻页
|
||||
useEffect(() => {
|
||||
const container = scrollContainerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const handleTouchStart = (e: TouchEvent) => {
|
||||
const touch = e.touches[0];
|
||||
touchStartRef.current = {
|
||||
x: touch.clientX,
|
||||
y: touch.clientY,
|
||||
};
|
||||
};
|
||||
|
||||
const handleTouchEnd = (e: TouchEvent) => {
|
||||
if (!touchStartRef.current) return;
|
||||
|
||||
const touch = e.changedTouches[0];
|
||||
const deltaX = touch.clientX - touchStartRef.current.x;
|
||||
const deltaY = touch.clientY - touchStartRef.current.y;
|
||||
|
||||
// 判断是否为有效滑动(至少50px,且垂直滑动大于水平滑动)
|
||||
const minSwipeDistance = 50;
|
||||
if (Math.abs(deltaY) > minSwipeDistance && Math.abs(deltaY) > Math.abs(deltaX)) {
|
||||
if (deltaY < 0) {
|
||||
// 向上滑动 = 下一页
|
||||
goToNextPage();
|
||||
} else if (deltaY > 0) {
|
||||
// 向下滑动 = 上一页
|
||||
goToPrevPage();
|
||||
}
|
||||
}
|
||||
|
||||
touchStartRef.current = null;
|
||||
};
|
||||
|
||||
const handleTouchCancel = () => {
|
||||
touchStartRef.current = null;
|
||||
};
|
||||
|
||||
container.addEventListener("touchstart", handleTouchStart, { passive: true });
|
||||
container.addEventListener("touchend", handleTouchEnd, { passive: true });
|
||||
container.addEventListener("touchcancel", handleTouchCancel, { passive: true });
|
||||
|
||||
return () => {
|
||||
container.removeEventListener("touchstart", handleTouchStart);
|
||||
container.removeEventListener("touchend", handleTouchEnd);
|
||||
container.removeEventListener("touchcancel", handleTouchCancel);
|
||||
};
|
||||
}, [numPages, pageNumber]);
|
||||
|
||||
return (
|
||||
<div className="basic-root">
|
||||
<div className="basic-white-block">
|
||||
<div className="basic-content">
|
||||
<div className="ui8-pdf-container" ref={scrollContainerRef}>
|
||||
{/* {loading && <div className="ui8-loading">加载中...</div>} */}
|
||||
{error && <div className="ui8-error">{error}</div>}
|
||||
|
||||
{pdfData && (
|
||||
<Document
|
||||
file={pdfData}
|
||||
onLoadSuccess={onDocumentLoadSuccess}
|
||||
onLoadError={onDocumentLoadError}
|
||||
loading=""
|
||||
>
|
||||
<div
|
||||
className={`ui8-pdf-page-wrapper ${
|
||||
animationDirection === "up" ? "slide-up" :
|
||||
animationDirection === "down" ? "slide-down" : ""
|
||||
}`}
|
||||
>
|
||||
<Page
|
||||
pageNumber={pageNumber}
|
||||
renderTextLayer={false}
|
||||
renderAnnotationLayer={false}
|
||||
className="ui8-pdf-page"
|
||||
width={920}
|
||||
/>
|
||||
</div>
|
||||
</Document>
|
||||
)}
|
||||
|
||||
{/* {numPages > 0 && (
|
||||
<>
|
||||
|
||||
<div className="ui8-pdf-controls">
|
||||
<span className="ui8-page-info">
|
||||
{pageNumber} / {numPages}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)} */}
|
||||
</div>
|
||||
|
||||
<div className="ui8-right-section">
|
||||
<img src={ui8A} alt="" />
|
||||
<img src={ui8B} alt="" />
|
||||
</div>
|
||||
|
||||
<div className="basic-confirm-section">
|
||||
<BackButton text="返回" onClick={handleBack} />
|
||||
<ConfirmButton
|
||||
text={isPrinting ? "打印中..." : "打印"}
|
||||
onClick={handleConfirm}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UI8;
|
||||
68
src/pages/UI9/UI9.css
Normal file
68
src/pages/UI9/UI9.css
Normal file
@@ -0,0 +1,68 @@
|
||||
.ui9-root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 45%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ui9-title {
|
||||
width: 690px;
|
||||
height: 88px;
|
||||
overflow-wrap: break-word;
|
||||
color: rgba(0, 45, 93, 1);
|
||||
font-size: 92px;
|
||||
font-family: NotoSansCJKsc-Bold;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui9-text {
|
||||
width: 628px;
|
||||
overflow-wrap: break-word;
|
||||
color: rgba(0, 45, 93, 1);
|
||||
font-size: 57px;
|
||||
font-family: NotoSansCJKsc-Bold;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
.ui9-instruction {
|
||||
height: 34px;
|
||||
overflow-wrap: break-word;
|
||||
color: rgba(0, 45, 93, 1);
|
||||
font-size: 35px;
|
||||
font-family: NotoSansCJKsc-Medium;
|
||||
font-weight: Medium;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
line-height: 46px;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
.ui9-confirm-section {
|
||||
width: 896px;
|
||||
margin: 32px 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ui9-vip-img {
|
||||
margin: 20px 0 0px 0;
|
||||
}
|
||||
|
||||
.ui9-qrcode {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.ui9-success-img {
|
||||
width: 358px;
|
||||
height: 358px;
|
||||
margin: 110px 0;
|
||||
}
|
||||
55
src/pages/UI9/UI9.tsx
Normal file
55
src/pages/UI9/UI9.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from "react";
|
||||
import "./UI9.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import DecorLine from "../../components/DecorLine";
|
||||
|
||||
import BackButton from "../../components/BackButton";
|
||||
import ConfirmButton from "../../components/ConfirmButton";
|
||||
import success from "../../assets/success.png";
|
||||
import UI9A from "../../assets/ui9A.png";
|
||||
import UI9B from "../../assets/ui9B.png";
|
||||
const UI9: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
// 是否认证成功
|
||||
const isAuthenticated = false;
|
||||
const handleBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
// 是否套餐待定
|
||||
const isPackageUndecided = true;
|
||||
if (isPackageUndecided) {
|
||||
//navigate("/u4");
|
||||
} else {
|
||||
//navigate("/u5");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ui9-root">
|
||||
<span className="ui9-title">太平VIP客户认证</span>
|
||||
<DecorLine />
|
||||
{isAuthenticated ? (
|
||||
<>
|
||||
<span className="ui9-text">认证成功</span>
|
||||
<img className="ui9-success-img" src={success} alt="success" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="ui9-text">一对一专属服务</span>
|
||||
<img className="ui9-vip-img" src={UI9A} alt="vip" />
|
||||
{/* 认证二维码 */}
|
||||
<img className="ui9-qrcode" src={UI9B} alt="二维码位置" />
|
||||
<span className="ui9-instruction">如您体检方面有任何问题,可以联系咨询企业微信客服</span>
|
||||
</>
|
||||
)}
|
||||
<div className="ui9-confirm-section">
|
||||
<BackButton text="返回" onClick={handleBack} />
|
||||
<ConfirmButton text="确认" onClick={handleConfirm} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UI9;
|
||||
Reference in New Issue
Block a user