merge
This commit is contained in:
parent
be34a8bc9b
commit
cea77ea231
@ -26,7 +26,7 @@
|
|||||||
"delete_confirm": "Are you sure you want to delete this video?",
|
"delete_confirm": "Are you sure you want to delete this video?",
|
||||||
"push_success": "Streaming success",
|
"push_success": "Streaming success",
|
||||||
"search_key": "Please enter title keywords",
|
"search_key": "Please enter title keywords",
|
||||||
"text": "Video history"
|
"text": "Recycle Bin"
|
||||||
},
|
},
|
||||||
"history.pushed": "Streaming: {{count}}",
|
"history.pushed": "Streaming: {{count}}",
|
||||||
"live": {
|
"live": {
|
||||||
@ -134,6 +134,18 @@
|
|||||||
"title_word_count": "Word count",
|
"title_word_count": "Word count",
|
||||||
"word_count": "Words"
|
"word_count": "Words"
|
||||||
},
|
},
|
||||||
|
"order": {
|
||||||
|
"left_time": "Remaining time",
|
||||||
|
"list": {
|
||||||
|
"consume_time": "Duration",
|
||||||
|
"cover": "Cover",
|
||||||
|
"id": "No.",
|
||||||
|
"operator": "User",
|
||||||
|
"order_time": "Time stamp",
|
||||||
|
"title": "Title"
|
||||||
|
},
|
||||||
|
"text": "Orders"
|
||||||
|
},
|
||||||
"select": {
|
"select": {
|
||||||
"pushed": "Pushed: {{count}}",
|
"pushed": "Pushed: {{count}}",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"delete_confirm": "是否要删除该视频",
|
"delete_confirm": "是否要删除该视频",
|
||||||
"push_success": "一键推流成功,已推流至数字人直播间,请查看!",
|
"push_success": "一键推流成功,已推流至数字人直播间,请查看!",
|
||||||
"search_key": "请输入视频标题关键字进行信息",
|
"search_key": "请输入视频标题关键字进行信息",
|
||||||
"text": "历史视频"
|
"text": "回收站"
|
||||||
},
|
},
|
||||||
"history.pushed": "已推送 {{count}} 条",
|
"history.pushed": "已推送 {{count}} 条",
|
||||||
"live": {
|
"live": {
|
||||||
@ -134,6 +134,18 @@
|
|||||||
"title_word_count": "字数",
|
"title_word_count": "字数",
|
||||||
"word_count": "字数"
|
"word_count": "字数"
|
||||||
},
|
},
|
||||||
|
"order": {
|
||||||
|
"left_time": "当前剩余时长",
|
||||||
|
"list": {
|
||||||
|
"consume_time": "消费时长",
|
||||||
|
"cover": "缩略图",
|
||||||
|
"id": "订单编号",
|
||||||
|
"operator": "操作人",
|
||||||
|
"order_time": "下单时间",
|
||||||
|
"title": "标题"
|
||||||
|
},
|
||||||
|
"text": "订单记录"
|
||||||
|
},
|
||||||
"select": {
|
"select": {
|
||||||
"pushed": "已推送: {{count}} 条",
|
"pushed": "已推送: {{count}} 条",
|
||||||
"select_all": "全选",
|
"select_all": "全选",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Input} from "antd";
|
import {Input} from "antd";
|
||||||
import {useBoolean, useLocalStorageState, useSetState,useClickAway} from "ahooks";
|
import {useLocalStorageState, useSetState, useClickAway} from "ahooks";
|
||||||
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
|
import React, {useCallback, useEffect, useMemo, useRef, useState} from "react";
|
||||||
import {clsx} from "clsx";
|
import {clsx} from "clsx";
|
||||||
import useArticleTags from "@/hooks/useArticleTags.ts";
|
import useArticleTags from "@/hooks/useArticleTags.ts";
|
||||||
|
|
||||||
@ -14,6 +14,8 @@ import {useTranslation} from "react-i18next";
|
|||||||
type SearchPanelProps = {
|
type SearchPanelProps = {
|
||||||
onSearch?: (params: ApiArticleSearchParams) => void;
|
onSearch?: (params: ApiArticleSearchParams) => void;
|
||||||
defaultParams?: Partial<ApiArticleSearchParams>;
|
defaultParams?: Partial<ApiArticleSearchParams>;
|
||||||
|
hideNewsSource?: boolean;
|
||||||
|
rightRender?: React.ReactNode;
|
||||||
}
|
}
|
||||||
const pagination = {
|
const pagination = {
|
||||||
limit: 12, page: 1
|
limit: 12, page: 1
|
||||||
@ -23,7 +25,7 @@ const DEFAULT_STATE = {
|
|||||||
tag_level_2_id: -1,
|
tag_level_2_id: -1,
|
||||||
subOptions: []
|
subOptions: []
|
||||||
}
|
}
|
||||||
export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps) {
|
export default function SearchPanel({onSearch, defaultParams, hideNewsSource,rightRender}: SearchPanelProps) {
|
||||||
const tags = useArticleTags();
|
const tags = useArticleTags();
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
const [params, setParams] = useSetState<ApiArticleSearchParams>({
|
const [params, setParams] = useSetState<ApiArticleSearchParams>({
|
||||||
@ -159,8 +161,9 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
onChange={handleTimeFilter}
|
onChange={handleTimeFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{rightRender && <div className="right-placeholder">{rightRender}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="filter-container mt-5">
|
{!hideNewsSource && <div className="filter-container mt-5">
|
||||||
<div className="list-container relative">
|
<div className="list-container relative">
|
||||||
<div className="justify-between flex items-start border-b pb-2 overflow-hidden">
|
<div className="justify-between flex items-start border-b pb-2 overflow-hidden">
|
||||||
<div className="pinned-tag-list flex flex-wrap flex-1 min-w-0">
|
<div className="pinned-tag-list flex flex-wrap flex-1 min-w-0">
|
||||||
@ -243,6 +246,6 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>}
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
@ -89,7 +89,11 @@ export default function NewsIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (<div className={'container pb-5'}>
|
return (<div className={'container pb-5'}>
|
||||||
<SearchPanel defaultParams={params} onSearch={setParams}/>
|
<SearchPanel defaultParams={params} onSearch={(params)=>{
|
||||||
|
// 滚动到顶部
|
||||||
|
scrollerRef.current?.scrollToPosition(0)
|
||||||
|
setParams(params)
|
||||||
|
}}/>
|
||||||
{activeNews && <Modal
|
{activeNews && <Modal
|
||||||
rootClassName={'news-detail-modal'}
|
rootClassName={'news-detail-modal'}
|
||||||
closeIcon={null} open={true} width={1000}
|
closeIcon={null} open={true} width={1000}
|
||||||
|
81
src/pages/order/index.tsx
Normal file
81
src/pages/order/index.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import SearchPanel from "@/pages/news/components/search-panel.tsx";
|
||||||
|
import React, {useState} from "react";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import styles from "@/pages/news/components/style.module.scss";
|
||||||
|
|
||||||
|
import {formatDurationToTime, formatTime} from "@/util/strings.ts";
|
||||||
|
import {IconDelete, IconEdit, IconWarningCircle} from "@/components/icons";
|
||||||
|
import {Popconfirm} from "antd";
|
||||||
|
import {useSetState} from "ahooks";
|
||||||
|
|
||||||
|
const mockList: OrderInfo[] = Array(10).fill(0).map((_, id) => (
|
||||||
|
{
|
||||||
|
id: id + 1,
|
||||||
|
cover: "https://staticplus.gachafun.com/fengmang/imgs/20241216/3fa3da5027cce22acb03283e8d688749.jpg",
|
||||||
|
title: `我国成功发射卫星互联网低轨卫星 ${id}`,
|
||||||
|
order_time: "2025-03-25 11:11:11",
|
||||||
|
consume_time: 60,
|
||||||
|
operator: "张三"
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
function OrderIndex() {
|
||||||
|
const {t} = useTranslation()
|
||||||
|
const [params, setParams] = useState<ApiArticleSearchParams>({
|
||||||
|
pagination: {page: 1, limit: 12},
|
||||||
|
time_flag: 1,
|
||||||
|
})
|
||||||
|
const [dataList, setDataList] = useState<OrderInfo[]>([...mockList])
|
||||||
|
const [state, setState] = useSetState({
|
||||||
|
loading: false,
|
||||||
|
leftTime: 2000,
|
||||||
|
})
|
||||||
|
|
||||||
|
return <div className="container pb-5 page-order-index">
|
||||||
|
<SearchPanel
|
||||||
|
hideNewsSource={true} defaultParams={params} onSearch={setParams}
|
||||||
|
rightRender={<div>{t('order.left_time')}: <span className={`${state.leftTime < 3600 ? 'text-red-600':''}`}>{formatDurationToTime(state.leftTime)}</span> </div>}
|
||||||
|
/>
|
||||||
|
<div className=" mt-2">
|
||||||
|
<div className={styles.newListTable}>
|
||||||
|
<div className="header row flex">
|
||||||
|
<div className="col w-[160px]">{t('order.list.id')}</div>
|
||||||
|
<div className="col w-[180px]">{t('order.list.cover')}</div>
|
||||||
|
<div className="col title">{t('order.list.title')}</div>
|
||||||
|
<div className="col w-[180px]">{t('order.list.order_time')}</div>
|
||||||
|
<div className="col w-[180px]">{t('order.list.consume_time')}</div>
|
||||||
|
<div className="col w-[180px]">{t('order.list.operator')}</div>
|
||||||
|
</div>
|
||||||
|
<div className="data-list-container">
|
||||||
|
{dataList?.map((item, i) => {
|
||||||
|
return <div key={i} className="row flex">
|
||||||
|
<div className="col w-[160px] text-center">
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="text-base">{item.id}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col w-[180px]">
|
||||||
|
<img src={item.cover} className="rounded w-[140px] h-[60px]" alt=""/>
|
||||||
|
</div>
|
||||||
|
<div className="col flex-1">
|
||||||
|
<div className="text-sm line-clamp-2">{item.title}</div>
|
||||||
|
</div>
|
||||||
|
<div className="col w-[180px]">
|
||||||
|
<div className="text-sm">{formatTime(item.order_time, 'YYYY-MM-DD HH:mm')}</div>
|
||||||
|
</div>
|
||||||
|
<div className="col w-[180px]">
|
||||||
|
<div
|
||||||
|
className="text-sm">{formatTime(item.consume_time, 'YYYY-MM-DD HH:mm')}</div>
|
||||||
|
</div>
|
||||||
|
<div className="col w-[180px]">
|
||||||
|
<div className="text-sm">{item.operator}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrderIndex
|
@ -249,6 +249,7 @@ export default function VideoIndex() {
|
|||||||
playing={state.playingId == v.id}
|
playing={state.playingId == v.id}
|
||||||
checked={checkedIdArray.includes(v.id)}
|
checked={checkedIdArray.includes(v.id)}
|
||||||
className={`list-item-${index} mt-3 mb-2 list-item-state-${v.status} `}
|
className={`list-item-${index} mt-3 mb-2 list-item-state-${v.status} `}
|
||||||
|
downloadVisible={true}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
setCheckedIdArray(idArray => {
|
setCheckedIdArray(idArray => {
|
||||||
const newArr = checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
|
const newArr = checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
|
||||||
|
@ -9,7 +9,6 @@ import ErrorBoundary from "./error.tsx";
|
|||||||
import Loader from "@/components/loader.tsx";
|
import Loader from "@/components/loader.tsx";
|
||||||
import routes from "@/routes/routes.tsx";
|
import routes from "@/routes/routes.tsx";
|
||||||
import {DocumentTitle} from "@/components/document.tsx";
|
import {DocumentTitle} from "@/components/document.tsx";
|
||||||
import useConfig from "@/hooks/useConfig.ts";
|
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import useGlobalConfig from "@/hooks/useGlobalConfig.ts";
|
import useGlobalConfig from "@/hooks/useGlobalConfig.ts";
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ const AuthGuard = ({ children }:BasicComponentProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInitialized && !isLoggedIn && location.pathname !== '/user') {
|
if (isInitialized && !isLoggedIn && location.pathname !== '/user') {
|
||||||
console.log(location)
|
|
||||||
navigate(`/user?from=${location.pathname}`, {
|
navigate(`/user?from=${location.pathname}`, {
|
||||||
state: {
|
state: {
|
||||||
from: location.pathname
|
from: location.pathname
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {Outlet, useLocation, useNavigate, useSearchParams} from "react-router-dom";
|
import {Outlet, useLocation, useNavigate, useSearchParams} from "react-router-dom";
|
||||||
import {Button, Divider, Dropdown, MenuProps} from "antd";
|
import {Button, Divider, Dropdown, MenuProps} from "antd";
|
||||||
import React, {useEffect} from "react";
|
import React, {useEffect} from "react";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
|
||||||
import AuthGuard from "@/routes/layout/auth-guard.tsx";
|
import AuthGuard from "@/routes/layout/auth-guard.tsx";
|
||||||
import {LogoText} from "@/components/icons/logo.tsx";
|
import {LogoText} from "@/components/icons/logo.tsx";
|
||||||
@ -12,8 +13,6 @@ import useAuth from "@/hooks/useAuth.ts";
|
|||||||
import {hidePhone} from "@/util/strings.ts";
|
import {hidePhone} from "@/util/strings.ts";
|
||||||
import {defaultCache} from "@/hooks/useCache.ts";
|
import {defaultCache} from "@/hooks/useCache.ts";
|
||||||
import {IconVideo} from "@/components/icons";
|
import {IconVideo} from "@/components/icons";
|
||||||
import {useTranslation} from "react-i18next";
|
|
||||||
import useConfig from "@/hooks/useConfig.ts";
|
|
||||||
|
|
||||||
|
|
||||||
type LayoutProps = {
|
type LayoutProps = {
|
||||||
@ -29,12 +28,19 @@ const NavigationUserContainer = () => {
|
|||||||
}
|
}
|
||||||
const items: MenuProps['items'] = [
|
const items: MenuProps['items'] = [
|
||||||
{
|
{
|
||||||
key: 'profile',
|
key: 'history',
|
||||||
label: <div className="nav-item" onClick={() => navigate('/history')}>
|
label: <div className="nav-item" onClick={() => navigate('/history')}>
|
||||||
<IconVideo />
|
<IconVideo />
|
||||||
<span className={"nav-text"}>{t('history.text')}</span>
|
<span className={"nav-text"}>{t('history.text')}</span>
|
||||||
</div>,
|
</div>,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'order',
|
||||||
|
label: <div className="nav-item" onClick={() => navigate('/order')}>
|
||||||
|
<IconVideo />
|
||||||
|
<span className={"nav-text"}>{t('order.text')}</span>
|
||||||
|
</div>,
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// key: 'logout',
|
// key: 'logout',
|
||||||
// label: <div onClick={handleLogout}>退出</div>,
|
// label: <div onClick={handleLogout}>退出</div>,
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import {RouteObject} from "react-router-dom";
|
import {RouteObject} from "react-router-dom";
|
||||||
import ErrorBoundary from "@/routes/error.tsx";
|
|
||||||
|
|
||||||
;
|
|
||||||
import DashboardLayout from "@/routes/layout/dashboard-layout.tsx";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import ErrorBoundary from "@/routes/error.tsx";
|
||||||
|
import DashboardLayout from "@/routes/layout/dashboard-layout.tsx";
|
||||||
|
|
||||||
const UserAuth = React.lazy(() => import("@/pages/user"))
|
const UserAuth = React.lazy(() => import("@/pages/user"))
|
||||||
const CreateVideoIndex = React.lazy(() => import("@/pages/video"))
|
const CreateVideoIndex = React.lazy(() => import("@/pages/video"))
|
||||||
const LibraryIndex = React.lazy(() => import("@/pages/library"))
|
const LibraryIndex = React.lazy(() => import("@/pages/library"))
|
||||||
const LiveIndex = React.lazy(() => import("@/pages/live"))
|
const LiveIndex = React.lazy(() => import("@/pages/live"))
|
||||||
const NewsIndex = React.lazy(() => import("@/pages/news"))
|
const NewsIndex = React.lazy(() => import("@/pages/news"))
|
||||||
const NewsEdit = React.lazy(() => import("@/pages/news/edit.tsx"))
|
const NewsEdit = React.lazy(() => import("@/pages/news/edit.tsx"))
|
||||||
|
const OrderIndex = React.lazy(() => import("@/pages/order/index.tsx"))
|
||||||
|
|
||||||
const routes: RouteObject[] = [
|
const routes: RouteObject[] = [
|
||||||
|
|
||||||
@ -39,6 +39,10 @@ const routes: RouteObject[] = [
|
|||||||
path: 'history',
|
path: 'history',
|
||||||
element: <LibraryIndex/>
|
element: <LibraryIndex/>
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'order',
|
||||||
|
element: <OrderIndex/>
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'live',
|
path: 'live',
|
||||||
element: <LiveIndex/>
|
element: <LiveIndex/>
|
||||||
|
13
src/types/api.d.ts
vendored
13
src/types/api.d.ts
vendored
@ -127,3 +127,16 @@ declare interface LiveState{
|
|||||||
id: number;
|
id: number;
|
||||||
live_start_time: number;
|
live_start_time: number;
|
||||||
}
|
}
|
||||||
|
declare interface OrderInfo {
|
||||||
|
id: number| string;
|
||||||
|
// 缩略图
|
||||||
|
cover: string;
|
||||||
|
// 标题
|
||||||
|
title: string;
|
||||||
|
// 下单时间
|
||||||
|
order_time: number | string;
|
||||||
|
// 消费时长
|
||||||
|
consume_time: number;
|
||||||
|
// 操作人
|
||||||
|
operator: string;
|
||||||
|
}
|
||||||
|
@ -54,6 +54,16 @@ function getDayjs(time:any){
|
|||||||
}
|
}
|
||||||
return dayjs(time);
|
return dayjs(time);
|
||||||
}
|
}
|
||||||
|
// 将时长(秒)转换成时间
|
||||||
|
export function formatDurationToTime(duration: number) {
|
||||||
|
if (duration < 0 || isNaN(duration)) return '00:00';
|
||||||
|
duration = Math.ceil(duration);
|
||||||
|
const hour = Math.floor(duration / 3600);
|
||||||
|
const minute = Math.floor((duration - hour * 3600) / 60);
|
||||||
|
const second = duration - hour * 3600 - minute * 60;
|
||||||
|
// 需要补0
|
||||||
|
return padStart(hour.toString(), 2, '0') + ':' + padStart(minute.toString(), 2, '0') + ':' + padStart(second.toString(), 2, '0')
|
||||||
|
}
|
||||||
|
|
||||||
export function formatTime(time: any, template: 'min' | 'date' | string = 'YYYY-MM-DD HH:mm:ss') {
|
export function formatTime(time: any, template: 'min' | 'date' | string = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
if (!time) return '-';
|
if (!time) return '-';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user