feat: first commit
This commit is contained in:
3
src/pages/material-upload-video/index.config.ts
Normal file
3
src/pages/material-upload-video/index.config.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '上传录屏',
|
||||
});
|
39
src/pages/material-upload-video/index.less
Normal file
39
src/pages/material-upload-video/index.less
Normal file
@ -0,0 +1,39 @@
|
||||
@import '@/styles/common.less';
|
||||
@import '@/styles/variables.less';
|
||||
|
||||
.page-material-upload-video {
|
||||
// bottom: 40px + 80px + 20px 内边距+按钮高度+按钮上边距
|
||||
padding: 40px 24px 140px;
|
||||
|
||||
&__header-title {
|
||||
font-size: 40px;
|
||||
line-height: 48px;
|
||||
font-weight: 400;
|
||||
color: @blColor;
|
||||
}
|
||||
|
||||
&__header-tips {
|
||||
font-size: 28px;
|
||||
line-height: 32px;
|
||||
font-weight: 400;
|
||||
color: @blColor;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
&__video-list {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
position: fixed;
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
bottom: 0;
|
||||
background: #F5F6FA;
|
||||
}
|
||||
|
||||
&__submit-btn {
|
||||
.button(@width: 100%, @height: 80px, @fontSize: 32px, @fontWeight: 400, @borderRadius: 48px);
|
||||
bottom: 40px;
|
||||
}
|
||||
}
|
204
src/pages/material-upload-video/index.tsx
Normal file
204
src/pages/material-upload-video/index.tsx
Normal file
@ -0,0 +1,204 @@
|
||||
import { Button } from '@tarojs/components';
|
||||
import Taro, { UploadTask, useDidHide, useLoad, useUnload } from '@tarojs/taro';
|
||||
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import MaterialVideoCard from '@/components/material-video-card';
|
||||
import SafeBottomPadding from '@/components/safe-bottom-padding';
|
||||
import { EventName, OpenSource, PageUrl } from '@/constants/app';
|
||||
import { CollectEventName, ReportEventId } from '@/constants/event';
|
||||
import { MaterialVideoInfo } from '@/types/material';
|
||||
import { logWithPrefix } from '@/utils/common';
|
||||
import { collectEvent, reportEvent } from '@/utils/event';
|
||||
import { chooseMedia, postVideos, requestVideoList } from '@/utils/material';
|
||||
import { getPageQuery, navigateBack, navigateTo } from '@/utils/route';
|
||||
import Toast from '@/utils/toast';
|
||||
import { uploadVideo } from '@/utils/video';
|
||||
|
||||
import './index.less';
|
||||
|
||||
// 限制 500M
|
||||
const MAX_FILE_SIZE_LIMIT = 1024 * 1024 * 1000;
|
||||
const PREFIX = 'page-material-upload-video';
|
||||
const log = logWithPrefix(PREFIX);
|
||||
const TEMP_DATA: MaterialVideoInfo = { url: '', coverUrl: '', title: '', type: 'image', isDefault: false };
|
||||
|
||||
export default function MaterialUploadVideo() {
|
||||
const [source, setSource] = useState(OpenSource.None);
|
||||
const [videoList, setVideoList] = useState<MaterialVideoInfo[]>([]);
|
||||
const saveRef = useRef<(videos: MaterialVideoInfo[]) => Promise<void>>();
|
||||
const lastSaveVideosRef = useRef<MaterialVideoInfo[]>([]);
|
||||
|
||||
const initVideoList = useCallback(async () => {
|
||||
try {
|
||||
const res = await requestVideoList();
|
||||
lastSaveVideosRef.current = res;
|
||||
setVideoList(res);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
Toast.error('加载失败请重试');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleClickDelete = useCallback(
|
||||
(video: MaterialVideoInfo) => {
|
||||
log('handleClickDelete', video);
|
||||
const newVideoList = videoList.filter(v => v.coverUrl !== video.coverUrl);
|
||||
setVideoList(newVideoList);
|
||||
},
|
||||
[videoList]
|
||||
);
|
||||
|
||||
const handleDefaultChange = useCallback(
|
||||
(video: MaterialVideoInfo) => {
|
||||
log('handleDefaultChange', video);
|
||||
const newVideoList = videoList.map(v => ({ ...v, isDefault: v.coverUrl === video.coverUrl }));
|
||||
setVideoList(newVideoList);
|
||||
},
|
||||
[videoList]
|
||||
);
|
||||
|
||||
const handleTitleChange = useCallback(
|
||||
(video: MaterialVideoInfo, newTitle: string) => {
|
||||
// log('handleTitleChange', video, newTitle);
|
||||
const newVideoList = [...videoList];
|
||||
const index = newVideoList.findIndex(v => v.coverUrl === video.coverUrl);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
newVideoList.splice(index, 1, { ...video, title: newTitle });
|
||||
setVideoList(newVideoList);
|
||||
},
|
||||
[videoList]
|
||||
);
|
||||
|
||||
const handleClickUpload = useCallback(async () => {
|
||||
log('click upload');
|
||||
let showLoading = false;
|
||||
try {
|
||||
reportEvent(ReportEventId.CLICK_UPLOAD_VIDEO);
|
||||
const media = await chooseMedia();
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
const { type, tempFiles } = media;
|
||||
log('upload result', type, tempFiles);
|
||||
const tempFile = tempFiles[0];
|
||||
if (!tempFile) {
|
||||
throw new Error('tempFile is not exist');
|
||||
}
|
||||
if (tempFile.size > MAX_FILE_SIZE_LIMIT) {
|
||||
Toast.info('视频超过1000m,请更换', 3000);
|
||||
collectEvent(CollectEventName.VIDEO_EXCEEDING_LIMITS);
|
||||
return;
|
||||
}
|
||||
showLoading = true;
|
||||
Taro.showLoading({ title: '准备上传' });
|
||||
const onProgress: UploadTask.OnProgressUpdateCallback = res => {
|
||||
log('上传视频进度', res.progress, '总长度', res.totalBytesExpectedToSend, '已上传的长度', res.totalBytesSent);
|
||||
Taro.showLoading({ title: `上传${res.progress}%` });
|
||||
};
|
||||
const { url, coverUrl } = await uploadVideo(tempFile.tempFilePath, tempFile.fileType, onProgress);
|
||||
const newVideo: MaterialVideoInfo = {
|
||||
title: '',
|
||||
isDefault: false,
|
||||
url: url,
|
||||
coverUrl: coverUrl,
|
||||
type: tempFile.fileType === 'video' ? 'video' : 'image',
|
||||
};
|
||||
setVideoList([...videoList, newVideo]);
|
||||
} catch (e) {
|
||||
console.error('upload fail', e);
|
||||
Toast.error('上传失败');
|
||||
collectEvent(CollectEventName.UPLOAD_VIDEO_FAILED, e);
|
||||
} finally {
|
||||
showLoading && Taro.hideLoading();
|
||||
}
|
||||
}, [videoList]);
|
||||
|
||||
const handleClickSubmit = useCallback(async () => {
|
||||
log('handleClickSubmit', videoList);
|
||||
reportEvent(ReportEventId.CLICK_SAVE_VIDEOS);
|
||||
if (videoList.length < 2) {
|
||||
Toast.info('请至少上传 2 个录屏');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Taro.showLoading();
|
||||
await saveRef.current?.(videoList);
|
||||
Taro.eventCenter.trigger(EventName.CREATE_PROFILE);
|
||||
if (source === OpenSource.None) {
|
||||
navigateTo(PageUrl.MaterialCreateProfile);
|
||||
} else {
|
||||
navigateBack();
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.error('保存失败请重试');
|
||||
collectEvent(CollectEventName.SAVE_VIDEO_LIST_FAILED, e);
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
}
|
||||
}, [videoList, source]);
|
||||
|
||||
useEffect(() => {
|
||||
saveRef.current = async (videos: MaterialVideoInfo[]) => {
|
||||
if (!videos.length) {
|
||||
log('长度为空不保存');
|
||||
return;
|
||||
}
|
||||
if (isEqual(lastSaveVideosRef.current, videos)) {
|
||||
log('没变化不保存');
|
||||
return;
|
||||
}
|
||||
lastSaveVideosRef.current = videos;
|
||||
await postVideos(videos);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLoad(() => {
|
||||
const query = getPageQuery<{ source: OpenSource }>();
|
||||
log('query', query);
|
||||
const { source: openSource } = query;
|
||||
openSource && setSource(openSource);
|
||||
initVideoList();
|
||||
});
|
||||
|
||||
useDidHide(() => {
|
||||
log('didHide', videoList);
|
||||
saveRef.current?.(videoList);
|
||||
});
|
||||
|
||||
useUnload(() => {
|
||||
log('unload', videoList);
|
||||
saveRef.current?.(videoList);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={PREFIX}>
|
||||
<div className={`${PREFIX}__header`}>
|
||||
<div className={`${PREFIX}__header-title`}>上传录屏更易获得企业青睐</div>
|
||||
<div className={`${PREFIX}__header-tips`}>录屏是企业最关注的资料,建议提供多个风格和品类</div>
|
||||
</div>
|
||||
<div className={`${PREFIX}__video-list`}>
|
||||
{videoList.map(video => (
|
||||
<MaterialVideoCard
|
||||
key={video.coverUrl}
|
||||
videoInfo={video}
|
||||
onClickDelete={() => handleClickDelete(video)}
|
||||
onClickSetDefault={() => handleDefaultChange(video)}
|
||||
onTitleChange={(newTitle: string) => handleTitleChange(video, newTitle)}
|
||||
/>
|
||||
))}
|
||||
{videoList.length < 6 && <MaterialVideoCard videoInfo={TEMP_DATA} onClickUpload={handleClickUpload} isTemp />}
|
||||
</div>
|
||||
<SafeBottomPadding />
|
||||
<div className={`${PREFIX}__footer`}>
|
||||
<Button className={`${PREFIX}__submit-btn`} onClick={handleClickSubmit}>
|
||||
{source === OpenSource.None ? '下一步' : '保存'}
|
||||
</Button>
|
||||
<SafeBottomPadding />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user