feat: ts报错
This commit is contained in:
parent
a5f6c71d05
commit
aa03a6798a
@ -48,11 +48,6 @@ export default function AskScreen() {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const updateState = (value: string) => {
|
|
||||||
console.log('Received in JS:', value);
|
|
||||||
// 更新 React 状态或路由
|
|
||||||
router.replace(value);
|
|
||||||
};
|
|
||||||
// 右滑
|
// 右滑
|
||||||
const gesture = Gesture.Pan()
|
const gesture = Gesture.Pan()
|
||||||
.onEnd((event) => {
|
.onEnd((event) => {
|
||||||
@ -61,7 +56,7 @@ export default function AskScreen() {
|
|||||||
|
|
||||||
if (translationX > threshold) {
|
if (translationX > threshold) {
|
||||||
// 从左向右滑动,跳转页面
|
// 从左向右滑动,跳转页面
|
||||||
runOnJS(router.replace)("memo-list");
|
runOnJS(router.replace)("/memo-list");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.minPointers(1)
|
.minPointers(1)
|
||||||
|
|||||||
@ -143,7 +143,7 @@ export default function Rights() {
|
|||||||
}
|
}
|
||||||
setConfirmLoading(true);
|
setConfirmLoading(true);
|
||||||
const history = await fetchPurchaseHistory()
|
const history = await fetchPurchaseHistory()
|
||||||
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i) => { return i?.id })
|
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i: any) => { return i?.id })
|
||||||
if (historyIds?.includes(payType)) {
|
if (historyIds?.includes(payType)) {
|
||||||
setConfirmLoading(false);
|
setConfirmLoading(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@ -1,221 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
|
||||||
import {
|
|
||||||
StyleSheet,
|
|
||||||
TextInput,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import { fetchApi } from '@/lib/server-api-util';
|
|
||||||
import { Message } from '@/types/ask';
|
|
||||||
import { RecordingPresets, useAudioRecorder } from 'expo-audio';
|
|
||||||
interface Props {
|
|
||||||
setIsHello: (isHello: boolean) => void,
|
|
||||||
conversationId: string | null,
|
|
||||||
setUserMessages: Dispatch<SetStateAction<Message[]>>;
|
|
||||||
setConversationId: (conversationId: string) => void,
|
|
||||||
}
|
|
||||||
export default function AudioRecordPlay(props: Props) {
|
|
||||||
const { setIsHello, conversationId, setUserMessages, setConversationId } = props;
|
|
||||||
const audioRecorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY);
|
|
||||||
const [isRecording, setIsRecording] = useState(false);
|
|
||||||
const [isVoiceStart, setIsVoiceStart] = useState(false);
|
|
||||||
const [elapsedTime, setElapsedTime] = useState(0);
|
|
||||||
|
|
||||||
// 用户询问
|
|
||||||
const [inputValue, setInputValue] = useState('');
|
|
||||||
const [timerInterval, setTimerInterval] = useState<NodeJS.Timeout | number>(0);
|
|
||||||
|
|
||||||
const formatTime = (ms: number): string => {
|
|
||||||
const totalSeconds = ms / 1000;
|
|
||||||
const minutes = Math.floor(totalSeconds / 60);
|
|
||||||
const seconds = Math.floor(totalSeconds % 60);
|
|
||||||
const milliseconds = Math.floor(ms % 1000);
|
|
||||||
|
|
||||||
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}`;
|
|
||||||
};
|
|
||||||
// 开始录音
|
|
||||||
const record = async () => {
|
|
||||||
await audioRecorder.prepareToRecordAsync();
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
// 每 10ms 更新一次时间
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const elapsed = Date.now() - startTime;
|
|
||||||
setElapsedTime(elapsed);
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
setTimerInterval(interval);
|
|
||||||
setIsVoiceStart(true)
|
|
||||||
audioRecorder.record();
|
|
||||||
setIsRecording(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopRecording = async () => {
|
|
||||||
// The recording will be available on `audioRecorder.uri`.
|
|
||||||
|
|
||||||
|
|
||||||
if (timerInterval) clearInterval(timerInterval);
|
|
||||||
setTimerInterval(0);
|
|
||||||
await audioRecorder.stop();
|
|
||||||
setIsRecording(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// (async () => {
|
|
||||||
// const status = await AudioModule.requestRecordingPermissionsAsync();
|
|
||||||
// if (!status.granted) {
|
|
||||||
// Alert.alert('Permission to access microphone was denied');
|
|
||||||
// }
|
|
||||||
// })();
|
|
||||||
// }, []);
|
|
||||||
// 获取对话信息
|
|
||||||
const createNewConversation = useCallback(async (user_text: string) => {
|
|
||||||
const data = await fetchApi<string>("/chat/new", {
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
setConversationId(data);
|
|
||||||
await getConversation({ session_id: data, user_text });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getConversation = useCallback(async ({ session_id, user_text }: { session_id: string, user_text: string }) => {
|
|
||||||
if (!session_id) return;
|
|
||||||
const response = await fetchApi<Message>(`/chat`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
session_id,
|
|
||||||
user_text
|
|
||||||
})
|
|
||||||
});
|
|
||||||
setUserMessages((prev: Message[]) => [...prev, response]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 使用 useCallback 缓存 handleSubmit
|
|
||||||
const handleSubmit = () => {
|
|
||||||
const text = inputValue;
|
|
||||||
if (text) {
|
|
||||||
setUserMessages(pre => ([...pre, {
|
|
||||||
content: {
|
|
||||||
text: text
|
|
||||||
},
|
|
||||||
role: 'User',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
}
|
|
||||||
]));
|
|
||||||
if (!conversationId) {
|
|
||||||
createNewConversation(text);
|
|
||||||
setIsHello(false);
|
|
||||||
} else {
|
|
||||||
getConversation({
|
|
||||||
session_id: conversationId,
|
|
||||||
user_text: text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setInputValue('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View className="relative w-full">
|
|
||||||
{/* <TouchableOpacity
|
|
||||||
onPress={() => console.log('Left icon pressed')}
|
|
||||||
className={`absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-white rounded-full ${isVoiceStart ? "opacity-100" : "opacity-0"}`} // 使用绝对定位将按钮放在输入框内右侧
|
|
||||||
>
|
|
||||||
<VoiceDeleteSvg />
|
|
||||||
</TouchableOpacity> */}
|
|
||||||
<TextInput
|
|
||||||
style={styles.input}
|
|
||||||
placeholder="Ask MeMo Anything..."
|
|
||||||
placeholderTextColor="#999"
|
|
||||||
className={isVoiceStart ? 'bg-bgPrimary border-none pl-12' : ''}
|
|
||||||
value={isVoiceStart ? `· · · · · · · · · · · · · · ${formatTime(elapsedTime)}` : inputValue}
|
|
||||||
onChangeText={(text: string) => {
|
|
||||||
setInputValue(text);
|
|
||||||
}}
|
|
||||||
onSubmitEditing={handleSubmit}
|
|
||||||
editable={!isVoiceStart}
|
|
||||||
// 调起的键盘类型
|
|
||||||
returnKeyType="send"
|
|
||||||
/>
|
|
||||||
{/* <TouchableOpacity
|
|
||||||
style={styles.voiceButton}
|
|
||||||
className={`absolute right-0 top-1/2 -translate-y-1/2 ${isVoiceStart ? 'bg-white px-8' : 'bg-bgPrimary'}`} // 使用绝对定位将按钮放在输入框内右侧
|
|
||||||
onPress={isVoiceStart ? stopRecording : record}
|
|
||||||
>
|
|
||||||
{isVoiceStart ? <VoiceSendSvg /> : <VoiceSvg />}
|
|
||||||
</TouchableOpacity> */}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
marginBottom: 20,
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
recordButton: {
|
|
||||||
padding: 15,
|
|
||||||
borderRadius: 8,
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
startButton: {
|
|
||||||
backgroundColor: '#ff6b6b',
|
|
||||||
},
|
|
||||||
stopButton: {
|
|
||||||
backgroundColor: '#4CAF50',
|
|
||||||
},
|
|
||||||
buttonText: {
|
|
||||||
color: 'white',
|
|
||||||
fontSize: 16,
|
|
||||||
},
|
|
||||||
listTitle: {
|
|
||||||
fontWeight: 'bold',
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
emptyText: {
|
|
||||||
fontStyle: 'italic',
|
|
||||||
color: '#888',
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
recordingItem: {
|
|
||||||
padding: 10,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: '#eee',
|
|
||||||
},
|
|
||||||
uriText: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#777',
|
|
||||||
},
|
|
||||||
leftIcon: {
|
|
||||||
padding: 10,
|
|
||||||
paddingLeft: 15,
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
borderColor: '#FF9500',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 25,
|
|
||||||
paddingHorizontal: 20,
|
|
||||||
paddingVertical: 12,
|
|
||||||
fontSize: 16,
|
|
||||||
width: '100%', // 确保输入框宽度撑满
|
|
||||||
paddingRight: 50
|
|
||||||
},
|
|
||||||
voiceButton: {
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
borderRadius: 20,
|
|
||||||
backgroundColor: '#FF9500',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: 8, // 添加一点右边距
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -1,14 +1,14 @@
|
|||||||
|
import { requestLocationPermission, requestMediaLibraryPermission } from '@/components/owner/utils';
|
||||||
import { addMaterial, confirmUpload, getUploadUrl } from '@/lib/background-uploader/api';
|
import { addMaterial, confirmUpload, getUploadUrl } from '@/lib/background-uploader/api';
|
||||||
import { ConfirmUpload, ExifData, FileUploadItem, ImagesuploaderProps, UploadResult, UploadTask, defaultExifData } from '@/lib/background-uploader/types';
|
import { ConfirmUpload, ExifData, FileUploadItem, ImagesuploaderProps, UploadResult, UploadTask, defaultExifData } from '@/lib/background-uploader/types';
|
||||||
import { uploadFileWithProgress } from '@/lib/background-uploader/uploader';
|
import { uploadFileWithProgress } from '@/lib/background-uploader/uploader';
|
||||||
import { compressImage } from '@/lib/image-process/imageCompress';
|
import { compressImage } from '@/lib/image-process/imageCompress';
|
||||||
|
import { PermissionService } from '@/lib/PermissionService';
|
||||||
import { createVideoThumbnailFile } from '@/lib/video-process/videoThumbnail';
|
import { createVideoThumbnailFile } from '@/lib/video-process/videoThumbnail';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
import { requestLocationPermission, requestMediaLibraryPermission } from '@/components/owner/utils';
|
|
||||||
import { PermissionService } from '@/lib/PermissionService';
|
|
||||||
import * as MediaLibrary from 'expo-media-library';
|
import * as MediaLibrary from 'expo-media-library';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Alert, Button, Platform, TouchableOpacity, View } from 'react-native';
|
import { Button, Platform, TouchableOpacity, View } from 'react-native';
|
||||||
import UploadPreview from './preview';
|
import UploadPreview from './preview';
|
||||||
|
|
||||||
export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
||||||
@ -48,6 +48,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
|||||||
id: fileId,
|
id: fileId,
|
||||||
uri: asset.uri,
|
uri: asset.uri,
|
||||||
previewUrl: asset.uri, // 使用 asset.uri 作为初始预览
|
previewUrl: asset.uri, // 使用 asset.uri 作为初始预览
|
||||||
|
preview: asset.uri, // 使用 asset.uri 作为初始预览
|
||||||
name: asset.fileName || 'file',
|
name: asset.fileName || 'file',
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: 'uploading',
|
status: 'uploading',
|
||||||
@ -221,7 +222,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
|||||||
const batchResults = await Promise.allSettled(
|
const batchResults = await Promise.allSettled(
|
||||||
batch.map(asset => processSingleAsset(asset))
|
batch.map(asset => processSingleAsset(asset))
|
||||||
);
|
);
|
||||||
|
|
||||||
// 收集成功的结果
|
// 收集成功的结果
|
||||||
for (const result of batchResults) {
|
for (const result of batchResults) {
|
||||||
if (result.status === 'fulfilled' && result.value) {
|
if (result.status === 'fulfilled' && result.value) {
|
||||||
@ -235,7 +236,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
|||||||
for (let i = 0; i < assets.length; i += CONCURRENCY_LIMIT) {
|
for (let i = 0; i < assets.length; i += CONCURRENCY_LIMIT) {
|
||||||
batches.push(assets.slice(i, i + CONCURRENCY_LIMIT));
|
batches.push(assets.slice(i, i + CONCURRENCY_LIMIT));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 并行处理所有批次,但限制并发数量
|
// 并行处理所有批次,但限制并发数量
|
||||||
for (let i = 0; i < batches.length; i += CONCURRENCY_LIMIT) {
|
for (let i = 0; i < batches.length; i += CONCURRENCY_LIMIT) {
|
||||||
const batchGroup = batches.slice(i, i + CONCURRENCY_LIMIT);
|
const batchGroup = batches.slice(i, i + CONCURRENCY_LIMIT);
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { fetchApi } from '@/lib/server-api-util';
|
||||||
|
import { ConfirmUpload, UploadUrlResponse } from '@/types/upload';
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
|
|
||||||
const QUEUE_KEY = 'uploadQueue';
|
const QUEUE_KEY = 'uploadQueue';
|
||||||
@ -34,14 +36,14 @@ export const uploadMediaFile = async (asset: any) => {
|
|||||||
: `video/${filename.split('.').pop()}`;
|
: `video/${filename.split('.').pop()}`;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', { uri, name: filename, type } as any);
|
formData.append('file', { uri, name: filename, type } as unknown as File);
|
||||||
|
|
||||||
await getUploadUrl({
|
await getUploadUrl({
|
||||||
...formData,
|
...formData,
|
||||||
name: filename,
|
name: filename,
|
||||||
type,
|
type,
|
||||||
size: asset.fileSize
|
size: asset.fileSize
|
||||||
}, {}).then((res) => {
|
} as unknown as File, {}).then((res) => {
|
||||||
confirmUpload(res.file_id).then((confirmRes) => {
|
confirmUpload(res.file_id).then((confirmRes) => {
|
||||||
addMaterial(res.file_id, confirmRes.file_id)
|
addMaterial(res.file_id, confirmRes.file_id)
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const Fireworks: React.FC<FireworksProps> = ({
|
|||||||
const [particles, setParticles] = useState<Particle[]>([]);
|
const [particles, setParticles] = useState<Particle[]>([]);
|
||||||
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
||||||
const particleId = useRef(0);
|
const particleId = useRef(0);
|
||||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
// 生成随机位置
|
// 生成随机位置
|
||||||
const getRandomPosition = () => {
|
const getRandomPosition = () => {
|
||||||
@ -112,7 +112,7 @@ export const Fireworks: React.FC<FireworksProps> = ({
|
|||||||
]),
|
]),
|
||||||
// 旋转效果
|
// 旋转效果
|
||||||
Animated.timing(rotation, {
|
Animated.timing(rotation, {
|
||||||
toValue: rotation._value + 360,
|
toValue: (rotation as Animated.Value & { _value: number })._value + 360,
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
easing: Easing.linear,
|
easing: Easing.linear,
|
||||||
useNativeDriver: true,
|
useNativeDriver: true,
|
||||||
|
|||||||
@ -52,7 +52,8 @@ const Login = ({ updateUrlParam, setError, setShowPassword, showPassword }: Logi
|
|||||||
router.replace('/user-message');
|
router.replace('/user-message');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error.message || t('auth.login.loginError', { ns: 'login' }));
|
const errorMessage = error instanceof Error ? error.message : t('auth.login.loginError', { ns: 'login' });
|
||||||
|
setError(errorMessage);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// welcome.tsx (Web 版本)
|
// welcome.tsx (Web 版本)
|
||||||
// 在 Web 端不显示任何内容
|
// 在 Web 端不显示任何内容
|
||||||
|
import { StyleProp, ViewStyle } from "react-native";
|
||||||
|
|
||||||
// 占位符 移动端实际引入文件是 welcome.native.tsx 文件
|
// 占位符 移动端实际引入文件是 welcome.native.tsx 文件
|
||||||
export default function WebLottie(props: { source: string }) {
|
export default function WebLottie(props: { source: string, style?: StyleProp<ViewStyle>, loop?: boolean }) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -142,11 +142,6 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
},
|
},
|
||||||
modalTitle: {
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#4C320C',
|
|
||||||
},
|
|
||||||
closeButton: {
|
closeButton: {
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
color: '#4C320C',
|
color: '#4C320C',
|
||||||
|
|||||||
@ -387,7 +387,7 @@ const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible:
|
|||||||
|
|
||||||
<PrivacyModal modalVisible={privacyModalVisible} setModalVisible={setPrivacyModalVisible} type={modalType} />
|
<PrivacyModal modalVisible={privacyModalVisible} setModalVisible={setPrivacyModalVisible} type={modalType} />
|
||||||
<LcensesModal modalVisible={lcensesModalVisible} setModalVisible={setLcensesModalVisible} />
|
<LcensesModal modalVisible={lcensesModalVisible} setModalVisible={setLcensesModalVisible} />
|
||||||
<DeleteModal modalVisible={deleteModalVisible} setModalVisible={setDeleteModalVisible} setSettingModalVisible={setModalVisible} />
|
<DeleteModal modalVisible={deleteModalVisible} setModalVisible={setDeleteModalVisible} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const reverseGeocode = async (latitude: number, longitude: number): Promi
|
|||||||
try {
|
try {
|
||||||
const addressResults = await fetchApi<Address[]>(`/area/gecoding?latitude=${latitude}&longitude=${longitude}`);
|
const addressResults = await fetchApi<Address[]>(`/area/gecoding?latitude=${latitude}&longitude=${longitude}`);
|
||||||
if (Object.keys(addressResults).length === 0) {
|
if (Object.keys(addressResults).length === 0) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
console.log('地址1:', addressResults);
|
console.log('地址1:', addressResults);
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export const reverseGeocode = async (latitude: number, longitude: number): Promi
|
|||||||
} else {
|
} else {
|
||||||
SecureStore.setItemAsync('location', JSON.stringify(addressResults));
|
SecureStore.setItemAsync('location', JSON.stringify(addressResults));
|
||||||
}
|
}
|
||||||
return addressResults
|
return addressResults as unknown as Address;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('逆地理编码失败:', error);
|
console.log('逆地理编码失败:', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -4,29 +4,31 @@ interface RawData {
|
|||||||
location?: {
|
location?: {
|
||||||
latitude?: string | number;
|
latitude?: string | number;
|
||||||
};
|
};
|
||||||
|
[key: string]: any; // Allow any additional properties
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformData(data: RawData): RawData {
|
export function transformData(data: RawData): Omit<RawData, 'exif'> {
|
||||||
const result = { ...data };
|
const result = { ...data };
|
||||||
|
|
||||||
if (result.exif) {
|
if (result.exif) {
|
||||||
const newExif: Record<string, any> = {};
|
const newExif: Record<string, any> = {};
|
||||||
|
|
||||||
for (const key in result.exif) {
|
for (const key in result.exif) {
|
||||||
const value = result.exif[key];
|
const value: unknown = result.exif[key];
|
||||||
|
|
||||||
// 普通对象:{Exif}, {TIFF}, {XMP} 等
|
// 普通对象:{Exif}, {TIFF}, {XMP} 等
|
||||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||||
|
const obj = value as Record<string, any>;
|
||||||
if (key === '{GPS}') {
|
if (key === '{GPS}') {
|
||||||
// 处理 GPS 字段:所有子字段加前缀 "GPS"
|
// 处理 GPS 字段:所有子字段加前缀 "GPS"
|
||||||
for (const subKey in value) {
|
for (const subKey in obj) {
|
||||||
const newKey = 'GPS' + subKey; // 所有字段都加前缀
|
const newKey = 'GPS' + subKey; // 所有字段都加前缀
|
||||||
newExif[newKey] = value[subKey];
|
newExif[newKey] = obj[subKey];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 其它嵌套对象直接展开字段
|
// 其它嵌套对象直接展开字段
|
||||||
for (const subKey in value) {
|
for (const subKey in obj) {
|
||||||
newExif[subKey] = value[subKey];
|
newExif[subKey] = obj[subKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -35,8 +37,12 @@ export function transformData(data: RawData): RawData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.exif = newExif;
|
// 合并展开的 exif 数据并移除 exif 属性
|
||||||
|
const { exif, ...rest } = result;
|
||||||
|
return { ...rest, ...newExif };
|
||||||
}
|
}
|
||||||
// 最后将result的exif信息平铺
|
|
||||||
return { ...result, ...result.exif, exif: undefined };
|
// 如果没有 exif 数据,直接返回原数据(排除 exif 属性)
|
||||||
|
const { exif, ...rest } = result;
|
||||||
|
return rest;
|
||||||
}
|
}
|
||||||
109
types/works.ts
109
types/works.ts
@ -1,109 +0,0 @@
|
|||||||
import { MaterialItemWithMetadata } from "@/components/waterfall";
|
|
||||||
|
|
||||||
// 模板列表 list
|
|
||||||
export interface NodeTemplates {
|
|
||||||
id: string;
|
|
||||||
node_template_id: string
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
model: string;
|
|
||||||
prompt: string;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
current_version_id: number;
|
|
||||||
version: number;
|
|
||||||
is_current_version: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
//模板详情
|
|
||||||
export interface NodeTemplateDetail extends NodeTemplates {
|
|
||||||
material_infos: MaterialItemWithMetadata[]
|
|
||||||
example_material_ids: string[]
|
|
||||||
available_model_names: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新模板
|
|
||||||
export interface UpdateNodeTemplate {
|
|
||||||
example_material_ids: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Shot {
|
|
||||||
camera_direction: string;
|
|
||||||
camera_movement: string;
|
|
||||||
clip_id: number;
|
|
||||||
composition: string;
|
|
||||||
music_description: string;
|
|
||||||
perspective: string;
|
|
||||||
scene_name: string;
|
|
||||||
scene_sequence: number;
|
|
||||||
shot_description: string;
|
|
||||||
shot_duration: string;
|
|
||||||
shot_name: string;
|
|
||||||
shot_sequence: number;
|
|
||||||
shot_sizes: string;
|
|
||||||
sound_effect: string;
|
|
||||||
transition_in: string;
|
|
||||||
voice_over: string;
|
|
||||||
}
|
|
||||||
// 图片的debug
|
|
||||||
interface CaptionResult {
|
|
||||||
video_shots?: Shot[];
|
|
||||||
caption_result: {
|
|
||||||
common: {
|
|
||||||
background: string;
|
|
||||||
};
|
|
||||||
daily_life: {
|
|
||||||
activity: string;
|
|
||||||
atmosphere: string;
|
|
||||||
extra_elements: string;
|
|
||||||
people_presence: string;
|
|
||||||
scene: string;
|
|
||||||
};
|
|
||||||
person: {
|
|
||||||
person_count: string;
|
|
||||||
person_details: Array<{
|
|
||||||
action: string;
|
|
||||||
age: string;
|
|
||||||
expression: string;
|
|
||||||
gender: string;
|
|
||||||
mood: string;
|
|
||||||
}>;
|
|
||||||
person_identity: string;
|
|
||||||
scene: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface PhotosDebug {
|
|
||||||
id: string,
|
|
||||||
state: string,
|
|
||||||
name: string,
|
|
||||||
context: {
|
|
||||||
input: Object,
|
|
||||||
outputs: CaptionResult,
|
|
||||||
metadata: Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// prompt需要传参
|
|
||||||
export interface PromptParams {
|
|
||||||
name: string,
|
|
||||||
value_type: string,
|
|
||||||
required: boolean,
|
|
||||||
description: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// input output
|
|
||||||
export interface InputOutput {
|
|
||||||
is_async: boolean,
|
|
||||||
name: string,
|
|
||||||
context: {
|
|
||||||
metadata: {},
|
|
||||||
outputs: { raw_output?: JSON, metadata?: {} },
|
|
||||||
inputs: {
|
|
||||||
material_id: string,
|
|
||||||
prompt: string
|
|
||||||
model_name: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user