From 527e923b5b2e5d0930164482229cccd380a07bb8 Mon Sep 17 00:00:00 2001 From: Ycc Date: Thu, 3 Jul 2025 14:04:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9B=BE=E7=89=87=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E4=B8=8A=E4=BC=A0=E5=88=B0OSS=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageUploader.jsx | 179 +++++++++++++++++++++++++++++++ src/config.js | 1 + src/stores/Trainticket.js | 1 + src/views/trainticket/plan.jsx | 20 ++-- 4 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 src/components/ImageUploader.jsx diff --git a/src/components/ImageUploader.jsx b/src/components/ImageUploader.jsx new file mode 100644 index 0000000..b08c89b --- /dev/null +++ b/src/components/ImageUploader.jsx @@ -0,0 +1,179 @@ +import { useEffect, useState } from "react"; +import { Upload, List } from "antd"; +import { UploadOutlined } from "@ant-design/icons"; +import { Image } from "antd"; +import { fetchJSON } from "@/utils/request"; +import { HT3_HOST } from "@/config"; + +// 加密函数 +const simple_encrypt = text => { + const key = "TPDa*UU8h5%!zS"; + let encrypted = []; + let keyIndex = 0; + for (let i = 0; i < text.length; i++) { + const charCode = text.charCodeAt(i); + const keyCharCode = key.charCodeAt(keyIndex); + const encryptedChar = charCode ^ keyCharCode; + encrypted.push(encryptedChar); + keyIndex = (keyIndex + 1) % key.length; + } + return encrypted.map(byte => byte.toString(16).padStart(2, "0")).join(""); +}; + +// 获取图片列表 +const getImageList = async key => { + try { + const { errcode, result } = await fetchJSON(`${HT3_HOST}/oss/list_unique_key?key=${key}`); + if (errcode === 0) { + return result + .map(file => ({ + key: file.key, + encrypt_key: file.encrypt_key, + size: file.size, + status: "done", + url: file.url, + last_modified: file.last_modified, + })) + .sort((a, b) => { + const dateA = new Date(a.last_modified); + const dateB = new Date(b.last_modified); + return dateA - dateB; + }); + } + } catch (error) { + console.error("获取图片列表失败", error); + } + return []; +}; + +// 删除图片 +const deleteImage = async key => { + try { + const { errcode } = await fetchJSON(`${HT3_HOST}/oss/delete_unique_key?key=${key}`, { + method: "GET", + }); + return errcode === 0; + } catch (error) { + console.error("删除图片失败", error); + return false; + } +}; + +// 获取上传签名 +const getSignature = async (file, key, onSuccess, onError) => { + try { + const { errcode, result } = await fetchJSON(`${HT3_HOST}/oss/signature_unique_key?key=${key}&filename=${file.name}`); + if (errcode === 0) { + const { method, host, signed_headers } = result; + const response = await fetch(host, { + method, + headers: signed_headers, + body: file, + }); + if (response.ok) { + onSuccess(response, file); + } else { + onError(new Error("图片上传失败")); + } + } + } catch (error) { + console.error("获取签名失败:", error); + onError(error); + } +}; + +const ImageUploader = props => { + const [fileList, setFileList] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [previewOpen, setPreviewOpen] = useState(false); + const [previewImage, setPreviewImage] = useState(""); + const key = simple_encrypt(props.osskey); + + // 组件挂载时获取图片列表 + useEffect(() => { + const loadImages = async () => { + setIsLoading(true); + const images = await getImageList(key); + setFileList(images); + setIsLoading(false); + }; + + if (key) { + loadImages(); + } + }, [key]); + + // 处理删除操作 + const handleDelete = async file => { + const success = await deleteImage(file.encrypt_key); + if (success) { + setFileList(prevList => prevList.filter(item => item.key !== file.key)); + console.log("删除成功"); + } else { + console.error("删除失败"); + } + }; + + // 处理上传操作 + const handleChange = ({ fileList: newFileList }) => { + setFileList(newFileList); + }; + + const handleUploadFile = ({ file, onProgress, onSuccess, onError }) => { + getSignature( + file, + key, + (response, file) => { + getImageList(key).then(newImages => { + setFileList(prevList => { + // 找到当前正在上传的文件并移除 + const index = prevList.findIndex(item => item.status === "uploading"); + if (index !== -1) { + const newPrevList = [...prevList]; + newPrevList.splice(index, 1); + // 找出新增元素 + const newItems = newImages.filter(newItem => !newPrevList.some(prevItem => prevItem.key === newItem.key)); + // 将新增元素添加到过滤后的列表末尾 + return [...newPrevList, ...newItems]; + } + return prevList; + }); + }); + }, + onError + ); + }; + + const handlePreview = file => { + if (!file.url) { + return; + } + setPreviewImage(file.url); + setPreviewOpen(true); + }; + + return ( + <> + +
+ +
上传图片
+
+
+ + {previewImage && ( + setPreviewOpen(visible), + afterOpenChange: visible => !visible && setPreviewImage(""), + }} + src={previewImage} + /> + )} + + ); +}; + +export default ImageUploader; diff --git a/src/config.js b/src/config.js index dc8ad02..25c1040 100644 --- a/src/config.js +++ b/src/config.js @@ -2,6 +2,7 @@ export const PROJECT_NAME = "GHHub"; // mode: test,内部测试使用 export const HT_HOST = import.meta.env.MODE === 'test' ? 'http://120.79.9.217:10024' : import.meta.env.PROD ? 'https://p9axztuwd7x8a7.mycht.cn' : 'http://202.103.68.144:890' +export const HT3_HOST = 'https://hub.globalhighlights.com/ht3.0' export const OVERSEA_HOST = 'https://ht20-p9axztuwd7x8a7.mycht.cn' diff --git a/src/stores/Trainticket.js b/src/stores/Trainticket.js index 8f0a1bf..d83371e 100644 --- a/src/stores/Trainticket.js +++ b/src/stores/Trainticket.js @@ -133,6 +133,7 @@ const trainTicketStore = create((set, get) => ({ const searchParams = { VEI_SN: VEI_SN, GRI_SN: GRI_SN, + ServiceType: 2, }; const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetVeiFlightPlanChange`, searchParams); const _result = errcode !== 0 ? [] : result; diff --git a/src/views/trainticket/plan.jsx b/src/views/trainticket/plan.jsx index dbd899e..7933724 100644 --- a/src/views/trainticket/plan.jsx +++ b/src/views/trainticket/plan.jsx @@ -9,6 +9,8 @@ import trainTicketStore from "@/stores/Trainticket"; import { usingStorage } from "@/hooks/usingStorage"; import BackBtn from "@/components/BackBtn"; import { station_names } from "@/views/trainticket/station_name"; +import { Upload } from "antd"; +import ImageUploader from "@/components/ImageUploader"; const TrainticketPlan = props => { const { coli_sn, gri_sn } = useParams(); @@ -243,7 +245,7 @@ const TrainticketPlan = props => { - + @@ -270,6 +272,15 @@ const TrainticketPlan = props => { + + 车票图片 + + + + + + +
{ const handleDelete = CLC_SN => { deleteFlightCost(CLC_SN) .then(() => { - notification.success({ - message: `成功`, - description: "删除成功!", - placement: "top", - duration: 4, - icon: , - }); getPlanDetail(travelAgencyId, gri_sn); }) .catch(() => {