feat: init
This commit is contained in:
12
projects/translate-h5/src/hooks/i18n.ts
Normal file
12
projects/translate-h5/src/hooks/i18n.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import zhCN from "@/locales/zh_CN.json";
|
||||
import enUS from "@/locales/en_US.json";
|
||||
import { useI18nStore } from "@/store/i18n";
|
||||
|
||||
export default function useI18n() {
|
||||
const { lang } = useI18nStore();
|
||||
const locales = lang === "en_US" ? enUS : zhCN;
|
||||
|
||||
return (name: string) => {
|
||||
return locales[name as keyof typeof locales] || name;
|
||||
};
|
||||
}
|
||||
63
projects/translate-h5/src/hooks/location.ts
Normal file
63
projects/translate-h5/src/hooks/location.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import useI18n from "./i18n";
|
||||
|
||||
// 定义位置坐标的类型
|
||||
export interface Coordinates {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
|
||||
// 定义返回的位置状态的类型
|
||||
export interface LocationState {
|
||||
loaded: boolean;
|
||||
coordinates: Coordinates | null;
|
||||
}
|
||||
|
||||
// 定义错误状态的类型
|
||||
export type ErrorState = string | null;
|
||||
|
||||
export const useLocation = (): {
|
||||
location: LocationState;
|
||||
error: ErrorState;
|
||||
} => {
|
||||
const t = useI18n();
|
||||
|
||||
// 用于存储位置信息和加载状态的状态
|
||||
const [location, setLocation] = useState<LocationState>({
|
||||
loaded: false,
|
||||
coordinates: null,
|
||||
});
|
||||
|
||||
// 用于存储任何错误消息的状态
|
||||
const [error, setError] = useState<ErrorState>(null);
|
||||
|
||||
// 地理位置成功处理函数
|
||||
const onSuccess = (location: GeolocationPosition) => {
|
||||
setLocation({
|
||||
loaded: true,
|
||||
coordinates: {
|
||||
lat: location.coords.latitude,
|
||||
lng: location.coords.longitude,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 地理位置错误处理函数
|
||||
const onError = (error: GeolocationPositionError) => {
|
||||
setError(error.message);
|
||||
};
|
||||
|
||||
// 使用 useEffect 在组件挂载时执行地理位置请求
|
||||
useEffect(() => {
|
||||
// 检查浏览器是否支持地理位置
|
||||
if (!navigator.geolocation) {
|
||||
setError(t("hooks.location.unsupported"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 请求用户的当前位置
|
||||
navigator.geolocation.getCurrentPosition(onSuccess, onError);
|
||||
}, []);
|
||||
|
||||
return { location, error };
|
||||
};
|
||||
28
projects/translate-h5/src/hooks/session.ts
Normal file
28
projects/translate-h5/src/hooks/session.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
function useSessionStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {
|
||||
// 初始化状态
|
||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||
const item = sessionStorage.getItem(key);
|
||||
if (item !== null) {
|
||||
// 如果 sessionStorage 中有数据,则使用现有数据
|
||||
return JSON.parse(item);
|
||||
} else {
|
||||
// 当 sessionStorage 中没有相应的键时,使用 initialValue 初始化,并写入 sessionStorage
|
||||
sessionStorage.setItem(key, JSON.stringify(initialValue));
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听并保存变化到 sessionStorage
|
||||
useEffect(() => {
|
||||
if (!isEqual(JSON.parse(sessionStorage.getItem(key) || 'null'), storedValue)) {
|
||||
sessionStorage.setItem(key, JSON.stringify(storedValue));
|
||||
}
|
||||
}, [key, storedValue]);
|
||||
|
||||
return [storedValue, setStoredValue];
|
||||
}
|
||||
|
||||
export default useSessionStorage;
|
||||
198
projects/translate-h5/src/hooks/useFileUpload.ts
Normal file
198
projects/translate-h5/src/hooks/useFileUpload.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
// hooks/useFileUpload.ts (更新)
|
||||
import { useState, useCallback } from "react";
|
||||
import {
|
||||
UploadConfig,
|
||||
UploadProgress,
|
||||
UploadResponse,
|
||||
VoiceUploadStatus,
|
||||
} from "../types/upload";
|
||||
|
||||
interface UseFileUploadReturn {
|
||||
uploadStatus: VoiceUploadStatus;
|
||||
uploadFile: (
|
||||
file: Blob,
|
||||
fileName: string,
|
||||
config: UploadConfig
|
||||
) => Promise<UploadResponse>;
|
||||
resetUpload: () => void;
|
||||
}
|
||||
|
||||
export const useFileUpload = (): UseFileUploadReturn => {
|
||||
const [uploadStatus, setUploadStatus] = useState<VoiceUploadStatus>({
|
||||
status: "idle",
|
||||
});
|
||||
|
||||
// 检测文件类型并转换文件名
|
||||
const getFileExtension = (mimeType: string): string => {
|
||||
const mimeToExt: Record<string, string> = {
|
||||
"audio/webm": ".webm",
|
||||
"audio/mp4": ".m4a",
|
||||
"audio/aac": ".aac",
|
||||
"audio/wav": ".wav",
|
||||
"audio/ogg": ".ogg",
|
||||
"audio/mpeg": ".mp3",
|
||||
};
|
||||
|
||||
// 处理带codecs的MIME类型
|
||||
const baseMimeType = mimeType.split(";")[0];
|
||||
return mimeToExt[baseMimeType] || ".webm";
|
||||
};
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (
|
||||
file: Blob,
|
||||
fileName: string,
|
||||
config: UploadConfig
|
||||
): Promise<UploadResponse> => {
|
||||
// 检查文件大小
|
||||
if (config.maxFileSize && file.size > config.maxFileSize) {
|
||||
const error = `文件大小超过限制 (${Math.round(
|
||||
config.maxFileSize / 1024 / 1024
|
||||
)}MB)`;
|
||||
setUploadStatus({
|
||||
status: "error",
|
||||
error,
|
||||
});
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
// 更宽松的文件类型检查,支持iOS格式
|
||||
const allowedTypes = config.allowedTypes || [
|
||||
"audio/webm",
|
||||
"audio/mp4",
|
||||
"audio/aac",
|
||||
"audio/wav",
|
||||
"audio/ogg",
|
||||
"audio/mpeg",
|
||||
];
|
||||
|
||||
const baseMimeType = file.type.split(";")[0];
|
||||
const isTypeAllowed = allowedTypes.some(
|
||||
(type) => baseMimeType === type || baseMimeType === type.split(";")[0]
|
||||
);
|
||||
|
||||
if (!isTypeAllowed) {
|
||||
console.warn(`文件类型 ${file.type} 不在允许列表中,但继续上传`);
|
||||
}
|
||||
|
||||
// 根据实际MIME类型调整文件名
|
||||
const extension = getFileExtension(file.type);
|
||||
const adjustedFileName = fileName.replace(/\.[^/.]+$/, "") + extension;
|
||||
|
||||
setUploadStatus({
|
||||
status: "uploading",
|
||||
progress: { loaded: 0, total: file.size, percentage: 0 },
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const formData = new FormData();
|
||||
formData.append(config.fieldName || "file", file, adjustedFileName);
|
||||
|
||||
// 添加额外的元数据
|
||||
formData.append("fileName", adjustedFileName);
|
||||
formData.append("originalFileName", fileName);
|
||||
formData.append("fileSize", file.size.toString());
|
||||
formData.append("fileType", file.type);
|
||||
formData.append("uploadTime", new Date().toISOString());
|
||||
formData.append("userAgent", navigator.userAgent);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// 上传进度监听
|
||||
xhr.upload.addEventListener("progress", (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const progress: UploadProgress = {
|
||||
loaded: event.loaded,
|
||||
total: event.total,
|
||||
percentage: Math.round((event.loaded / event.total) * 100),
|
||||
};
|
||||
|
||||
setUploadStatus({
|
||||
status: "uploading",
|
||||
progress,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 上传完成监听
|
||||
xhr.addEventListener("load", () => {
|
||||
try {
|
||||
const response: UploadResponse = JSON.parse(xhr.responseText);
|
||||
|
||||
if (xhr.status >= 200 && xhr.status < 300 && response.success) {
|
||||
setUploadStatus({
|
||||
status: "success",
|
||||
response,
|
||||
progress: {
|
||||
loaded: file.size,
|
||||
total: file.size,
|
||||
percentage: 100,
|
||||
},
|
||||
});
|
||||
resolve(response);
|
||||
} else {
|
||||
const error = response.error || `上传失败: ${xhr.status}`;
|
||||
setUploadStatus({
|
||||
status: "error",
|
||||
error,
|
||||
});
|
||||
reject(new Error(error));
|
||||
}
|
||||
} catch (parseError) {
|
||||
const error = "服务器响应格式错误";
|
||||
setUploadStatus({
|
||||
status: "error",
|
||||
error,
|
||||
});
|
||||
reject(new Error(error));
|
||||
}
|
||||
});
|
||||
|
||||
// 上传错误监听
|
||||
xhr.addEventListener("error", () => {
|
||||
const error = "网络错误,上传失败";
|
||||
setUploadStatus({
|
||||
status: "error",
|
||||
error,
|
||||
});
|
||||
reject(new Error(error));
|
||||
});
|
||||
|
||||
// 上传中断监听
|
||||
xhr.addEventListener("abort", () => {
|
||||
const error = "上传已取消";
|
||||
setUploadStatus({
|
||||
status: "error",
|
||||
error,
|
||||
});
|
||||
reject(new Error(error));
|
||||
});
|
||||
|
||||
// 设置请求头
|
||||
const headers = {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
...config.headers,
|
||||
};
|
||||
|
||||
Object.entries(headers).forEach(([key, value]) => {
|
||||
xhr.setRequestHeader(key, value);
|
||||
});
|
||||
|
||||
// 发送请求
|
||||
xhr.open(config.method || "POST", config.url);
|
||||
xhr.send(formData);
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const resetUpload = useCallback(() => {
|
||||
setUploadStatus({ status: "idle" });
|
||||
}, []);
|
||||
|
||||
return {
|
||||
uploadStatus,
|
||||
uploadFile,
|
||||
resetUpload,
|
||||
};
|
||||
};
|
||||
@@ -11,12 +11,7 @@ interface MainLayoutProps {
|
||||
onLink?: () => void;
|
||||
}
|
||||
|
||||
const MainLayout: React.FC<MainLayoutProps> = ({
|
||||
isShowNavBar,
|
||||
children,
|
||||
onLink,
|
||||
title,
|
||||
}) => {
|
||||
const MainLayout: React.FC<MainLayoutProps> = ({ isShowNavBar, children, onLink, title }) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { pathname } = location;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import React, { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { AppRoute } from "./routes";
|
||||
|
||||
interface AuthRouteProps {
|
||||
children: React.ReactNode;
|
||||
auth?: boolean;
|
||||
children: React.ReactNode;
|
||||
auth?: boolean;
|
||||
path: string;
|
||||
meta?: {
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12,18 +17,24 @@ interface AuthRouteProps {
|
||||
* @param auth 是否需要认证
|
||||
* @constructor 认证路由组件
|
||||
*/
|
||||
const AuthRoute: React.FC<AuthRouteProps> = ({children, auth}) => {
|
||||
const navigate = useNavigate();
|
||||
const token = localStorage.getItem('token'); // 或者其他认证令牌的获取方式
|
||||
const isAuthenticated = Boolean(token); // 认证逻辑
|
||||
const AuthRoute: React.FC<AuthRouteProps> = ({ children, auth, meta }) => {
|
||||
const navigate = useNavigate();
|
||||
const token = localStorage.getItem("token"); // 或者其他认证令牌的获取方式
|
||||
const isAuthenticated = Boolean(token); // 认证逻辑
|
||||
console.log(auth);
|
||||
useEffect(() => {
|
||||
if (meta?.title) {
|
||||
document.title = meta.title;
|
||||
}
|
||||
}, [meta]);
|
||||
useEffect(() => {
|
||||
// 检查角色权限
|
||||
if (auth && !isAuthenticated) {
|
||||
navigate("/login"); // 如果未认证且路由需要认证,则重定向到登录
|
||||
}
|
||||
}, [auth, isAuthenticated, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (auth && !isAuthenticated) {
|
||||
navigate('/login'); // 如果未认证且路由需要认证,则重定向到登录
|
||||
}
|
||||
}, [auth, isAuthenticated, navigate]);
|
||||
|
||||
return <>{children}</>;
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default AuthRoute;
|
||||
|
||||
@@ -1,27 +1,48 @@
|
||||
import {Route, Routes} from 'react-router-dom';
|
||||
import {routes, AppRoute} from './routes';
|
||||
import AuthRoute from './auth.tsx';
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { routes, AppRoute } from "./routes";
|
||||
import AuthRoute from "./auth.tsx";
|
||||
import { Suspense } from "react";
|
||||
import { DotLoading } from "antd-mobile";
|
||||
|
||||
/**
|
||||
* 渲染路由
|
||||
* @constructor RenderRoutes
|
||||
*/
|
||||
export const RenderRoutes = () => {
|
||||
const renderRoutes = (routes: AppRoute[]) => {
|
||||
return routes.map(route => (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
element={
|
||||
<AuthRoute auth={route.auth}>
|
||||
{route.element}
|
||||
</AuthRoute>
|
||||
}
|
||||
>
|
||||
{route.children && renderRoutes(route.children)}
|
||||
</Route>
|
||||
));
|
||||
};
|
||||
const renderRoutes = (routes: AppRoute[]) => {
|
||||
return routes.map((route) => (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
element={
|
||||
<AuthRoute auth={route.auth} path={route.path} meta={route.meta}>
|
||||
{route.element}
|
||||
</AuthRoute>
|
||||
}
|
||||
>
|
||||
{route.children && renderRoutes(route.children)}
|
||||
</Route>
|
||||
));
|
||||
};
|
||||
|
||||
return <Routes>{renderRoutes(routes)}</Routes>;
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
<DotLoading />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Routes>{renderRoutes(routes)}</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
import React from "react";
|
||||
import Home from "@/view/home";
|
||||
import TranslateDetail from "@/view/home/detail";
|
||||
import Setting from "@/view/setting";
|
||||
import Page404 from "@/view/error/page404";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { lazy } from "react";
|
||||
|
||||
export interface AppRoute {
|
||||
path: string;
|
||||
element: React.ReactNode;
|
||||
auth?: boolean;
|
||||
children?: AppRoute[];
|
||||
meta?: {
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
||||
const Home = lazy(() => import("@/view/home"));
|
||||
const Page404 = lazy(() => import("@/view/error/page404"));
|
||||
const TranslateDetail = lazy(() => import("@/view/home/detail"));
|
||||
export const routes: AppRoute[] = [
|
||||
{ path: "/", element: <Home />, auth: false },
|
||||
{ path: "/set", element: <Setting />, auth: false },
|
||||
{ path: "/detail", element: <TranslateDetail />, auth: false },
|
||||
{ path: "/mood", element: <Setting />, auth: false },
|
||||
{
|
||||
path: "/",
|
||||
element: <Navigate to="/translate" replace />,
|
||||
auth: false,
|
||||
meta: {
|
||||
title: "宠物翻译",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/translate",
|
||||
element: <Home />,
|
||||
auth: false,
|
||||
meta: {
|
||||
title: "宠物翻译",
|
||||
},
|
||||
},
|
||||
{ path: "/translate/detail", element: <TranslateDetail />, auth: false },
|
||||
{ path: "*", element: <Page404 />, auth: false },
|
||||
];
|
||||
|
||||
19
projects/translate-h5/src/types/global.d.ts
vendored
Normal file
19
projects/translate-h5/src/types/global.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// src/types/global.d.ts
|
||||
declare module "*.module.less" {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module "*.module.css" {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
// 其他资源文件
|
||||
declare module "*.png";
|
||||
declare module "*.jpg";
|
||||
declare module "*.jpeg";
|
||||
declare module "*.gif";
|
||||
declare module "*.svg";
|
||||
declare module "*.ico";
|
||||
declare module "*.webp";
|
||||
@@ -15,7 +15,7 @@ interface PropsConfig {
|
||||
handleAllAni: () => void;
|
||||
}
|
||||
const allAni = ["全部宠物", "丑丑", "胖胖", "可可"];
|
||||
function Index(props: PropsConfig) {
|
||||
function SearchCom(props: PropsConfig) {
|
||||
const [aniName, setAniName] = useState<string>("全部宠物");
|
||||
const animenuRef = useRef<DropdownRef>(null);
|
||||
const handleAniSelect = (val: RadioValue) => {
|
||||
@@ -84,4 +84,4 @@ function Index(props: PropsConfig) {
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
||||
export default SearchCom;
|
||||
|
||||
@@ -1,39 +1,41 @@
|
||||
.home {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.adm-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.adm-tabs-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
}
|
||||
.adm-tabs-header {
|
||||
border: 0 none;
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
background: #fff;
|
||||
z-index: 99;
|
||||
}
|
||||
// .adm-tabs {
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// height: 100%;
|
||||
// }
|
||||
// .adm-tabs-content {
|
||||
// flex: 1;
|
||||
// overflow: hidden;
|
||||
// padding: 0px;
|
||||
// }
|
||||
// .adm-tabs-header {
|
||||
// border: 0 none;
|
||||
// position: sticky;
|
||||
// top: 0px;
|
||||
// background: #fff;
|
||||
// z-index: 99;
|
||||
// }
|
||||
|
||||
.adm-tabs-tab {
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
font-weight: 600;
|
||||
&.adm-tabs-tab-active {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
.adm-tabs-tab-line {
|
||||
height: 0px;
|
||||
}
|
||||
// .adm-tabs-tab {
|
||||
// font-size: 20px;
|
||||
// color: rgba(0, 0, 0, 0.25);
|
||||
// font-weight: 600;
|
||||
// &.adm-tabs-tab-active {
|
||||
// color: #000;
|
||||
// }
|
||||
// }
|
||||
// .adm-tabs-tab-line {
|
||||
// height: 0px;
|
||||
// }
|
||||
|
||||
.translate-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.header {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,18 @@ function Index() {
|
||||
return (
|
||||
<MainLayout>
|
||||
<div className="home">
|
||||
<Tabs stretch={false}>
|
||||
<div className="header">
|
||||
<h3>宠物翻译</h3>
|
||||
<div></div>
|
||||
</div>
|
||||
{/* <Tabs stretch={false}>
|
||||
<Tabs.Tab title="宠物翻译" key="1">
|
||||
<Translate />
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab title="宠物档案" key="2">
|
||||
2
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
</Tabs> */}
|
||||
</div>
|
||||
</MainLayout>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,7 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { Image, Toast } from "antd-mobile";
|
||||
import MessageCom from "./component/message";
|
||||
import VoiceRecord from "./component/voice";
|
||||
import {
|
||||
XPopup,
|
||||
FloatingMenu,
|
||||
type FloatMenuItemConfig,
|
||||
} from "@workspace/shared";
|
||||
import { XPopup, FloatingMenu, type FloatMenuItemConfig } from "@workspace/shared";
|
||||
import type { Message } from "./types";
|
||||
|
||||
import { mockTranslateAudio } from "@/utils/voice";
|
||||
@@ -14,21 +10,14 @@ import dogSvg from "@/assets/translate/dog.svg";
|
||||
import catSvg from "@/assets/translate/cat.svg";
|
||||
import pigSvg from "@/assets/translate/pig.svg";
|
||||
import { MoreTwo } from "@icon-park/react";
|
||||
import SearchCom from "./component/search";
|
||||
interface DefinedProps {}
|
||||
const menuItems: FloatMenuItemConfig[] = [
|
||||
{ icon: <Image src={dogSvg} />, type: "dog" },
|
||||
{ icon: <Image src={catSvg} />, type: "cat" },
|
||||
{ icon: <Image src={pigSvg} />, type: "pig" },
|
||||
{
|
||||
icon: (
|
||||
<MoreTwo
|
||||
theme="outline"
|
||||
size="24"
|
||||
fill="#666"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="butt"
|
||||
/>
|
||||
),
|
||||
icon: <MoreTwo theme="outline" size="24" fill="#666" strokeWidth={3} strokeLinecap="butt" />,
|
||||
type: "add",
|
||||
},
|
||||
];
|
||||
@@ -85,9 +74,7 @@ function Index(props: DefinedProps) {
|
||||
|
||||
setMessages((prev) =>
|
||||
prev.map((msg) =>
|
||||
msg.id === messageId
|
||||
? { ...msg, translatedText, isTranslating: false }
|
||||
: msg
|
||||
msg.id === messageId ? { ...msg, translatedText, isTranslating: false } : msg
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -124,17 +111,17 @@ function Index(props: DefinedProps) {
|
||||
|
||||
return (
|
||||
<div className="translate-container">
|
||||
<div className="header">
|
||||
<SearchCom handleAllAni={() => {}} />
|
||||
</div>
|
||||
|
||||
<MessageCom data={messages} isRecording={isRecording}></MessageCom>
|
||||
<VoiceRecord
|
||||
onRecordingComplete={onRecordingComplete}
|
||||
isRecording={isRecording}
|
||||
onSetIsRecording={onSetIsRecording}
|
||||
/>
|
||||
<FloatingMenu
|
||||
menuItems={menuItems}
|
||||
value={currentLanguage}
|
||||
onChange={onLanguage}
|
||||
/>
|
||||
<FloatingMenu menuItems={menuItems} value={currentLanguage} onChange={onLanguage} />
|
||||
<XPopup
|
||||
title="选择翻译语种"
|
||||
visible={visible}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import useI18n from "@/hooks/i18n.ts";
|
||||
import MainLayout from "@/layout/main/mainLayout";
|
||||
import { Button } from "antd-mobile";
|
||||
import { useI18nStore } from "@/store/i18n.ts";
|
||||
|
||||
function Index() {
|
||||
const t = useI18n();
|
||||
const i18nStore = useI18nStore();
|
||||
|
||||
return <MainLayout>qq</MainLayout>;
|
||||
}
|
||||
|
||||
export default Index;
|
||||
Reference in New Issue
Block a user