| 1 | import React, { ForwardedRef, forwardRef, memo, useMemo } from 'react'; |
| 2 | import { IVideoChooseItem } from '@/views/publish/children/videoPage/videoPage'; |
| 3 | import { Alert, Avatar, Button, Tooltip } from 'antd'; |
| 4 | import { AccountPlatInfoMap } from '@/views/account/comment'; |
| 5 | import { |
| 6 | CaretRightOutlined, |
| 7 | DeleteOutlined, |
| 8 | EditOutlined, |
| 9 | PlusOutlined, |
| 10 | VideoCameraFilled, |
| 11 | SwapOutlined, |
| 12 | ExclamationCircleOutlined, |
| 13 | } from '@ant-design/icons'; |
| 14 | import { useVideoPageStore } from '@/views/publish/children/videoPage/useVideoPageStore'; |
| 15 | import { useShallow } from 'zustand/react/shallow'; |
| 16 | import VideoChoose from '@/components/Choose/VideoChoose'; |
| 17 | import { formatSeconds } from '@/utils'; |
| 18 | import { AccountStatus } from '../../../../../../commont/AccountEnum'; |
| 19 | import { AccountChooseType } from '../page'; |
| 20 | |
| 21 | export interface IVideoChooseItemRef {} |
| 22 | |
| 23 | export interface IVideoChooseItemProps { |
| 24 | videoChooseItem: IVideoChooseItem; |
| 25 | // 用户单选触发 |
| 26 | onAccountOneChoose: (id: string, type: AccountChooseType) => void; |
| 27 | } |
| 28 | |
| 29 | // 选择视频完成后渲染的列表 item |
| 30 | const VideoChooseItem = memo( |
| 31 | forwardRef( |
| 32 | ( |
| 33 | { videoChooseItem, onAccountOneChoose }: IVideoChooseItemProps, |
| 34 | ref: ForwardedRef<IVideoChooseItemRef>, |
| 35 | ) => { |
| 36 | // 数据是否准备完毕 |
| 37 | const prepareEnd = !!(videoChooseItem.video && videoChooseItem.account); |
| 38 | const { |
| 39 | deleteAloneVideo, |
| 40 | aloneAdd, |
| 41 | deleteData, |
| 42 | setCurrChooseAccountId, |
| 43 | setVideoPubSetModalOpen, |
| 44 | setLoadingPageLoading, |
| 45 | accountRestart, |
| 46 | } = useVideoPageStore( |
| 47 | useShallow((state) => ({ |
| 48 | deleteAloneVideo: state.deleteAloneVideo, |
| 49 | aloneAdd: state.aloneAdd, |
| 50 | deleteData: state.deleteData, |
| 51 | setCurrChooseAccountId: state.setCurrChooseAccountId, |
| 52 | setVideoPubSetModalOpen: state.setVideoPubSetModalOpen, |
| 53 | setLoadingPageLoading: state.setLoadingPageLoading, |
| 54 | accountRestart: state.accountRestart, |
| 55 | })), |
| 56 | ); |
| 57 | |
| 58 | // 分辨率是否合规 |
| 59 | const isDpiCompliance = useMemo(() => { |
| 60 | if (!videoChooseItem.video) return true; |
| 61 | return ( |
| 62 | videoChooseItem.video.height < 640 || |
| 63 | videoChooseItem.video.width < 480 |
| 64 | ); |
| 65 | }, []); |
| 66 | |
| 67 | return ( |
| 68 | <div className="videoChooseItem"> |
| 69 | <div className="videoChooseItem-core"> |
| 70 | <div |
| 71 | className="videoChooseItem-left" |
| 72 | key={videoChooseItem.video?.videoPath} |
| 73 | > |
| 74 | {videoChooseItem.video ? ( |
| 75 | <> |
| 76 | <div className="videoChooseItem-video"> |
| 77 | <div |
| 78 | className="videoChooseItem-video-close" |
| 79 | onClick={() => deleteAloneVideo(videoChooseItem.id)} |
| 80 | > |
| 81 | x |
| 82 | </div> |
| 83 | <div className="videoChooseItem-video-play"> |
| 84 | <img src={videoChooseItem.video.cover.imgUrl} /> |
| 85 | {prepareEnd && ( |
| 86 | <div |
| 87 | className="videoChooseItem-video-play-click" |
| 88 | onClick={() => { |
| 89 | setVideoPubSetModalOpen(true); |
| 90 | setCurrChooseAccountId(videoChooseItem.id); |
| 91 | }} |
| 92 | > |
| 93 | <CaretRightOutlined /> |
| 94 | </div> |
| 95 | )} |
| 96 | </div> |
| 97 | </div> |
| 98 | <div className="videoChooseItem-left-info"> |
| 99 | <p |
| 100 | className="videoChooseItem-left-title" |
| 101 | title={videoChooseItem.video?.filename} |
| 102 | > |
| 103 | {videoChooseItem.video?.filename} |
| 104 | </p> |
| 105 | <div className="videoChooseItem-left-bottom"> |
| 106 | <div className="videoChooseItem-left-bottom-item"> |
| 107 | <label>时长</label> |
| 108 | <span> |
| 109 | {formatSeconds(videoChooseItem.video.duration)} |
| 110 | </span> |
| 111 | </div> |
| 112 | <div |
| 113 | className={[ |
| 114 | 'videoChooseItem-left-bottom-item', |
| 115 | isDpiCompliance && |
| 116 | 'videoChooseItem-left-bottom-item--warning', |
| 117 | ].join(' ')} |
| 118 | > |
| 119 | <label>分辨率</label> |
| 120 | <span> |
| 121 | {videoChooseItem.video.width}* |
| 122 | {videoChooseItem.video.height} |
| 123 | </span> |
| 124 | {isDpiCompliance && ( |
| 125 | <Tooltip title="视频清晰度过低,有概率被平台限流或不能发布,推荐您上传 460*640 分辨率以上的视频"> |
| 126 | <ExclamationCircleOutlined /> |
| 127 | </Tooltip> |
| 128 | )} |
| 129 | </div> |
| 130 | </div> |
| 131 | </div> |
| 132 | </> |
| 133 | ) : ( |
| 134 | <div className="videoChooseItem-left-chooseVideo"> |
| 135 | <VideoChoose |
| 136 | onChoose={(videoFile) => { |
| 137 | setLoadingPageLoading(false); |
| 138 | aloneAdd({ |
| 139 | video: videoFile, |
| 140 | id: videoChooseItem.id, |
| 141 | }); |
| 142 | }} |
| 143 | onStartShoose={() => { |
| 144 | setLoadingPageLoading(true); |
| 145 | }} |
| 146 | > |
| 147 | <VideoCameraFilled /> |
| 148 | <span className="videoChooseItem-left-chooseVideo-name"> |
| 149 | 添加视频 |
| 150 | </span> |
| 151 | </VideoChoose> |
| 152 | </div> |
| 153 | )} |
| 154 | </div> |
| 155 | |
| 156 | <div className="videoChooseItem-right"> |
| 157 | {videoChooseItem.account ? ( |
| 158 | <div className="videoChooseItem-account"> |
| 159 | <div className="videoChooseItem-account-avatar"> |
| 160 | <Avatar src={videoChooseItem.account.avatar} size="large" /> |
| 161 | <div |
| 162 | className="videoChooseItem-account-avatar-replace" |
| 163 | onClick={() => { |
| 164 | onAccountOneChoose( |
| 165 | videoChooseItem.id, |
| 166 | AccountChooseType.Replace, |
| 167 | ); |
| 168 | }} |
| 169 | > |
| 170 | <SwapOutlined /> |
| 171 | </div> |
| 172 | </div> |
| 173 | <div className="videoChooseItem-account-con"> |
| 174 | <div className="videoChooseItem-account-top"> |
| 175 | <Tooltip title={videoChooseItem.account.nickname}> |
| 176 | <span className="videoChooseItem-account-top-name"> |
| 177 | {videoChooseItem.account.nickname} |
| 178 | </span> |
| 179 | </Tooltip> |
| 180 | <img |
| 181 | src={ |
| 182 | AccountPlatInfoMap.get(videoChooseItem.account.type) |
| 183 | ?.icon |
| 184 | } |
| 185 | /> |
| 186 | </div> |
| 187 | {videoChooseItem.account.status === |
| 188 | AccountStatus.DISABLE && ( |
| 189 | <Alert |
| 190 | message="登录失效" |
| 191 | type="error" |
| 192 | showIcon |
| 193 | onClick={() => { |
| 194 | accountRestart(videoChooseItem.account!.type); |
| 195 | }} |
| 196 | /> |
| 197 | )} |
| 198 | </div> |
| 199 | </div> |
| 200 | ) : ( |
| 201 | <div |
| 202 | className="videoChooseItem-noAccount" |
| 203 | onClick={() => { |
| 204 | onAccountOneChoose( |
| 205 | videoChooseItem.id, |
| 206 | AccountChooseType.Radio, |
| 207 | ); |
| 208 | }} |
| 209 | > |
| 210 | <div className="videoChooseItem-noAccount-icon"> |
| 211 | <PlusOutlined /> |
| 212 | </div> |
| 213 | <span>添加账号</span> |
| 214 | </div> |
| 215 | )} |
| 216 | </div> |
| 217 | </div> |
| 218 | |
| 219 | <div className="videoChooseItem-options"> |
| 220 | <Button |
| 221 | color="primary" |
| 222 | type="text" |
| 223 | size="small" |
| 224 | icon={<EditOutlined />} |
| 225 | disabled={!prepareEnd} |
| 226 | onClick={() => { |
| 227 | setVideoPubSetModalOpen(true); |
| 228 | setCurrChooseAccountId(videoChooseItem.id); |
| 229 | }} |
| 230 | > |
| 231 | 发布设置 |
| 232 | </Button> |
| 233 | <Button |
| 234 | onClick={() => { |
| 235 | deleteData(videoChooseItem.id); |
| 236 | }} |
| 237 | type="text" |
| 238 | size="small" |
| 239 | danger |
| 240 | icon={<DeleteOutlined />} |
| 241 | > |
| 242 | 删除 |
| 243 | </Button> |
| 244 | </div> |
| 245 | </div> |
| 246 | ); |
| 247 | }, |
| 248 | ), |
| 249 | ); |
| 250 | VideoChooseItem.displayName = 'VideoChooseItem'; |
| 251 | |
| 252 | export default VideoChooseItem; |
| 253 |