From 99323df02b6da51e21046d2634ec15cf9c1d8999 Mon Sep 17 00:00:00 2001 From: callmeyan Date: Wed, 16 Apr 2025 11:29:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=EF=B8=8F=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AE=A2=E5=8D=95/=E5=9B=9E=E6=94=B6=E7=AB=99=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=20fix:=E7=9B=B4=E6=92=AD=E9=97=B4=E5=9B=9E=E9=80=80?= =?UTF-8?q?=20style:=20=F0=9F=93=9A=EF=B8=8F=E8=B0=83=E6=95=B4=E7=9B=B8?= =?UTF-8?q?=E5=85=B3UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/core.scss | 6 +-- src/assets/images/error/ic_broken_image.png | Bin 0 -> 4089 bytes src/components/video/video-list-item.tsx | 18 +++---- src/pages/live/index.tsx | 16 +++--- src/pages/news/components/search-panel.tsx | 12 ++++- src/pages/news/components/style.module.scss | 18 +++---- src/pages/order/index.tsx | 55 +++++++++++--------- src/pages/recycle/components/video-item.tsx | 8 +-- src/routes/layout/dashboard-layout.tsx | 23 +++++--- src/service/api/live.ts | 3 ++ src/types/api.d.ts | 1 + src/util/strings.ts | 2 +- 12 files changed, 93 insertions(+), 69 deletions(-) create mode 100644 src/assets/images/error/ic_broken_image.png diff --git a/src/assets/core.scss b/src/assets/core.scss index 9fd54bc..92a70d2 100644 --- a/src/assets/core.scss +++ b/src/assets/core.scss @@ -170,7 +170,7 @@ @apply text-sm; background: none; .col{ - @apply text-sm text-gray-800; + @apply text-base text-gray-800; height: 42px; } } @@ -180,7 +180,7 @@ } .col { - @apply flex items-center relative pl-4 text-center justify-center; + @apply flex items-center relative pl-4 text-center justify-center text-sm; height: 60px; &:after { @@ -209,7 +209,7 @@ } .title { - @apply flex-1 text-base; + @apply flex-1; } .generated-time { diff --git a/src/assets/images/error/ic_broken_image.png b/src/assets/images/error/ic_broken_image.png new file mode 100644 index 0000000000000000000000000000000000000000..cb76f979c6671c21982e1fcf087cce3836d4bb32 GIT binary patch literal 4089 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 literal 0 HcmV?d00001 diff --git a/src/components/video/video-list-item.tsx b/src/components/video/video-list-item.tsx index 21ffd28..e09c645 100644 --- a/src/components/video/video-list-item.tsx +++ b/src/components/video/video-list-item.tsx @@ -137,7 +137,7 @@ export const VideoListItem = ( {/* : )}*/} -
+
{downloadUrl && video.status == VideoStatus.Generated && - + } {hideCheckBox ? <> : { diff --git a/src/pages/live/index.tsx b/src/pages/live/index.tsx index f0434ed..4d59381 100644 --- a/src/pages/live/index.tsx +++ b/src/pages/live/index.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; -import {Button, Checkbox, Empty, Modal, Popconfirm, Space} from "antd"; +import {Checkbox, Empty, Popconfirm, Space} from "antd"; import {SortableContext, arrayMove} from '@dnd-kit/sortable'; import {DndContext} from "@dnd-kit/core"; import FlvJs from "flv.js"; @@ -7,16 +7,17 @@ import {useTranslation} from "react-i18next"; import {useSetState} from "ahooks"; import {VideoListItem} from "@/components/video/video-list-item.tsx"; -import {deleteByIds, getList, modifyOrder, playState} from "@/service/api/live.ts"; +import {deleteByIds, getList, modifyOrder, playState, restoreByIds} from "@/service/api/live.ts"; import {showErrorToast, showToast} from "@/components/message.ts"; import ButtonBatch from "@/components/button-batch.tsx"; import {formatDuration} from "@/util/strings.ts"; import {Player, PlayerInstance} from "@/components/video/player.tsx"; -import {IconDelete, IconLocked, IconRollbackCircle, IconWarningCircle} from "@/components/icons"; +import {IconDelete, IconLocked, IconRollbackCircle} from "@/components/icons"; import InfiniteScroller, {InfiniteScrollerRef} from "@/components/scoller/infinite-scroller.tsx"; import ButtonToTop from "@/components/scoller/button-to-top.tsx"; import styles from "./style.module.scss" +import {ModalWarningIcon, ModalWarningTitle} from "@/components/icons/ModalWarning.tsx"; const cache: { flvPlayer?: FlvJs.Player, timerPlayNext?: any, timerLoadState?: any, prevUrl?: string } = {} @@ -25,7 +26,6 @@ export default function LiveIndex() { const player = useRef(null) const [videoData, setVideoData] = useState([]) - const [modal, contextHolder] = Modal.useModal() const [checkedIdArray, setCheckedIdArray] = useState([]) const [editable, setEditable] = useState(false) const scrollerRef = useRef(null) @@ -195,7 +195,7 @@ export default function LiveIndex() { await deleteByIds(delIds) } if(rollbackIds.length > 0) { - showToast('回退暂未实现') + await restoreByIds(rollbackIds) } // 调整排序 await modifyOrder(ids); @@ -356,8 +356,9 @@ export default function LiveIndex() { rootClassName={'popconfirm-main'} placement={'left'} arrow={false} - icon={} - title={t('video.live_rollback_confirm_title')} + icon={} + title={} + description={t('video.live_rollback_confirm_title')} onConfirm={() => handleRollback(v)} >} } @@ -388,6 +389,5 @@ export default function LiveIndex() { }
- {contextHolder}
) } \ No newline at end of file diff --git a/src/pages/news/components/search-panel.tsx b/src/pages/news/components/search-panel.tsx index 920ea90..5e1b931 100644 --- a/src/pages/news/components/search-panel.tsx +++ b/src/pages/news/components/search-panel.tsx @@ -12,6 +12,7 @@ import {IconPin} from "@/components/icons"; import {useTranslation} from "react-i18next"; type SearchPanelProps = { + rootClassName?: string; onSearch?: (params: ApiArticleSearchParams) => void; defaultParams?: Partial; hideNewsSource?: boolean; @@ -25,7 +26,14 @@ const DEFAULT_STATE = { tag_level_2_id: -1, subOptions: [] } -export default function SearchPanel({onSearch, defaultParams, hideNewsSource,rightRender}: SearchPanelProps) { +export default function SearchPanel( + { + onSearch, + defaultParams, + hideNewsSource, + rightRender, + rootClassName + }: SearchPanelProps) { const tags = useArticleTags(); const {t} = useTranslation() const [params, setParams] = useSetState({ @@ -143,7 +151,7 @@ export default function SearchPanel({onSearch, defaultParams, hideNewsSource,rig const setFalse = () => togglePinnedManagePanel(false) useClickAway(() => setFalse(), pinnedManagePanel) - return (
+ return (
- {t('order.left_time')}: {formatDurationToTime(data?.remaining_duration)} -
} - /> -
+ return
+
+
+ {t('order.left_time')}: {formatDurationToTime(data?.remaining_duration)} +
} + /> +
+
{t('order.list.id')}
@@ -39,7 +43,7 @@ function OrderIndex() {
{t('order.list.consume_time')}
{t('order.list.operator')}
-
+
{data?.list.length === 0 &&
} @@ -51,24 +55,23 @@ function OrderIndex() {
- +
+ +
-
{item.title}
+
{item.title}
-
-
{formatTime(item.order_time, 'YYYY-MM-DD HH:mm')}
+
{formatTime(item.order_time, 'YYYY-MM-DD HH:mm')}
-
-
{formatDurationToTime(item.consumption_duration)}
-
-
-
{item.operator}
+
{formatDurationToTime(item.consumption_duration)}
+
{item.operator}
})} diff --git a/src/pages/recycle/components/video-item.tsx b/src/pages/recycle/components/video-item.tsx index ae67107..23ed2cd 100644 --- a/src/pages/recycle/components/video-item.tsx +++ b/src/pages/recycle/components/video-item.tsx @@ -7,7 +7,7 @@ import styles from './style.module.scss' type VideoItemProps = { videoInfo: VideoInfo; onLive?: boolean; - onClick?: (autoPlay:boolean) => void; + onClick?: (autoPlay: boolean) => void; onRemove?: () => void; onCheckedChange?: (checked: boolean) => void; checked?: boolean; @@ -25,14 +25,14 @@ export default function VideoItem(props: VideoItemProps) {
-
props.onClick?.(true)}>
+
props.onClick?.(true)}>
props.onClick?.(false)}>{props.videoInfo.title}
-
{timeFromNow(props.videoInfo.ctime)}
+ onClick={() => props.onClick?.(false)}>{props.videoInfo.title}
+
{timeFromNow(props.videoInfo.d_time)}
{Math.ceil(props.videoInfo.duration / 1000)}s diff --git a/src/routes/layout/dashboard-layout.tsx b/src/routes/layout/dashboard-layout.tsx index f6d03be..902269b 100644 --- a/src/routes/layout/dashboard-layout.tsx +++ b/src/routes/layout/dashboard-layout.tsx @@ -1,6 +1,6 @@ -import {Outlet, useLocation, useNavigate, useSearchParams} from "react-router-dom"; -import {Button, Divider, Dropdown, MenuProps} from "antd"; -import React, {useEffect} from "react"; +import {Outlet, useLocation, useNavigate} from "react-router-dom"; +import {Divider, Dropdown, MenuProps} from "antd"; +import React, {useEffect, useMemo} from "react"; import {useTranslation} from "react-i18next"; import AuthGuard from "@/routes/layout/auth-guard.tsx"; @@ -13,7 +13,6 @@ import useAuth from "@/hooks/useAuth.ts"; import {hidePhone} from "@/util/strings.ts"; import {defaultCache} from "@/hooks/useCache.ts"; import {IconOrderFill, IconRecycleFill} from "@/components/icons"; -import useGlobalConfig from "@/hooks/useGlobalConfig.ts"; import LanguageSwitcher from "@/components/icons/language-switcher.tsx"; @@ -84,13 +83,25 @@ const NavigationUserContainer = () => { : }
) } +const ExtraNavItems = { + '/order':'order.text', + '/recycle':'history.text', +} export const BaseLayout: React.FC = ({children}) => { - + const {pathname} = useLocation() + const {t,i18n} = useTranslation() + const extraNav = useMemo(()=>{ + if(!pathname || !ExtraNavItems[pathname]) return null + return t(ExtraNavItems[pathname]) + },[pathname,i18n.language]) return (
-
+
+ {extraNav &&
+ {extraNav} +
}
diff --git a/src/service/api/live.ts b/src/service/api/live.ts index 735a3e5..a69274c 100644 --- a/src/service/api/live.ts +++ b/src/service/api/live.ts @@ -15,6 +15,9 @@ export function modifyOrder(ids: Id[]) { export function deleteByIds(ids: Id[]) { return post('/room/remove', {ids}) } +export function restoreByIds(ids: Id[]) { + return post('/room/restore', {ids}) +} export function getLiveUrl() { return get<{flv_url:string}>({ diff --git a/src/types/api.d.ts b/src/types/api.d.ts index 10cd5e0..eb96ee7 100644 --- a/src/types/api.d.ts +++ b/src/types/api.d.ts @@ -108,6 +108,7 @@ declare interface VideoInfo { status: number; publish_time?: number|string; ctime?: number|string; + d_time?: number|string; } declare interface VideoListItem extends VideoInfo { playing?: boolean; diff --git a/src/util/strings.ts b/src/util/strings.ts index 2f3bab7..d1b2648 100644 --- a/src/util/strings.ts +++ b/src/util/strings.ts @@ -58,7 +58,7 @@ function getDayjs(time:any){ export function formatDurationToTime(duration?: number|string) { duration = duration ? Number(duration) : 0; if (!duration || isNaN(duration) || duration < 0) return '00:00'; - duration = Math.ceil(duration); + duration = Math.ceil(duration / 1000); // 计算 const hour = Math.floor(duration / 3600); const minute = Math.floor((duration - hour * 3600) / 60);