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,15 +25,15 @@ 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>({
|
||||||
pagination,
|
pagination,
|
||||||
time_flag:1,
|
time_flag: 1,
|
||||||
...(defaultParams || {})
|
...(defaultParams || {})
|
||||||
});
|
});
|
||||||
const [prevSearchName, setPrevSearchName] = useState<string>(defaultParams?.title||'')
|
const [prevSearchName, setPrevSearchName] = useState<string>(defaultParams?.title || '')
|
||||||
|
|
||||||
const [state, setState] = useSetState<{
|
const [state, setState] = useSetState<{
|
||||||
tag_level_1_id: number;
|
tag_level_1_id: number;
|
||||||
@ -39,11 +41,11 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
subOptions: (string | number)[]
|
subOptions: (string | number)[]
|
||||||
}>({
|
}>({
|
||||||
...DEFAULT_STATE,
|
...DEFAULT_STATE,
|
||||||
...(defaultParams&&defaultParams.tag_level_1_id?{tag_level_1_id:defaultParams.tag_level_1_id}: {}),
|
...(defaultParams && defaultParams.tag_level_1_id ? {tag_level_1_id: defaultParams.tag_level_1_id} : {}),
|
||||||
...(defaultParams&&defaultParams.tag_level_2_id?{tag_level_2_id:defaultParams.tag_level_2_id}: {})
|
...(defaultParams && defaultParams.tag_level_2_id ? {tag_level_2_id: defaultParams.tag_level_2_id} : {})
|
||||||
})
|
})
|
||||||
useEffect(()=>{
|
useEffect(() => {
|
||||||
if(!defaultParams){
|
if (!defaultParams) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const _state = {
|
const _state = {
|
||||||
@ -51,18 +53,18 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
tag_level_2_id: -1,
|
tag_level_2_id: -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(defaultParams.tag_level_1_id){
|
if (defaultParams.tag_level_1_id) {
|
||||||
_state.tag_level_1_id = defaultParams.tag_level_1_id
|
_state.tag_level_1_id = defaultParams.tag_level_1_id
|
||||||
if(tags && tags.length > 0){
|
if (tags && tags.length > 0) {
|
||||||
const tag = tags.find(s => s.value == defaultParams.tag_level_1_id)
|
const tag = tags.find(s => s.value == defaultParams.tag_level_1_id)
|
||||||
setSubOptions(tag?.children || [])
|
setSubOptions(tag?.children || [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(defaultParams.tag_level_2_id){
|
if (defaultParams.tag_level_2_id) {
|
||||||
_state.tag_level_2_id = defaultParams.tag_level_2_id
|
_state.tag_level_2_id = defaultParams.tag_level_2_id
|
||||||
}
|
}
|
||||||
setState(_state)
|
setState(_state)
|
||||||
},[tags])
|
}, [tags])
|
||||||
const [pinnedTag, setPinnedTag] = useLocalStorageState<number[]>(
|
const [pinnedTag, setPinnedTag] = useLocalStorageState<number[]>(
|
||||||
'user-pinned-tag-list',
|
'user-pinned-tag-list',
|
||||||
{
|
{
|
||||||
@ -117,28 +119,28 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
}
|
}
|
||||||
return [] as OptionItem[];
|
return [] as OptionItem[];
|
||||||
}, [pinnedTag, tags])
|
}, [pinnedTag, tags])
|
||||||
const pinnedManagePanel = useRef<HTMLDivElement|null>(null)
|
const pinnedManagePanel = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const togglePinnedManagePanel = useCallback((visible: boolean) => {
|
const togglePinnedManagePanel = useCallback((visible: boolean) => {
|
||||||
if(!pinnedManagePanel.current){
|
if (!pinnedManagePanel.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const _target = pinnedManagePanel.current!;
|
const _target = pinnedManagePanel.current!;
|
||||||
if(visible){
|
if (visible) {
|
||||||
_target.style.height = 'auto'
|
_target.style.height = 'auto'
|
||||||
const {height} = _target.getBoundingClientRect()
|
const {height} = _target.getBoundingClientRect()
|
||||||
_target.style.height = '38px'
|
_target.style.height = '38px'
|
||||||
requestAnimationFrame(()=>{
|
requestAnimationFrame(() => {
|
||||||
_target.style.height = `${height}px`
|
_target.style.height = `${height}px`
|
||||||
})
|
})
|
||||||
}else{
|
} else {
|
||||||
requestAnimationFrame(()=>{
|
requestAnimationFrame(() => {
|
||||||
_target.style.height = '0'
|
_target.style.height = '0'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},[pinnedManagePanel])
|
}, [pinnedManagePanel])
|
||||||
const setTrue = ()=> togglePinnedManagePanel(true)
|
const setTrue = () => togglePinnedManagePanel(true)
|
||||||
const setFalse = ()=>togglePinnedManagePanel(false)
|
const setFalse = () => togglePinnedManagePanel(false)
|
||||||
useClickAway(() => setFalse(), pinnedManagePanel)
|
useClickAway(() => setFalse(), pinnedManagePanel)
|
||||||
|
|
||||||
return (<div className={`${styles.searchPanel} pt-6 pb-2`}>
|
return (<div className={`${styles.searchPanel} pt-6 pb-2`}>
|
||||||
@ -155,12 +157,13 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
/>
|
/>
|
||||||
<TimeSelect
|
<TimeSelect
|
||||||
className="w-[140px] ml-1"
|
className="w-[140px] ml-1"
|
||||||
value={typeof(params.time_flag) != "undefined" ? params.time_flag : 1}
|
value={typeof (params.time_flag) != "undefined" ? params.time_flag : 1}
|
||||||
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">
|
||||||
@ -182,7 +185,7 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="pinned-menu mt-2">
|
<div className="pinned-menu mt-2">
|
||||||
<span className={'cursor-pointer block hover:text-blue-500'} onClick={e=>{
|
<span className={'cursor-pointer block hover:text-blue-500'} onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setTrue();
|
setTrue();
|
||||||
@ -193,56 +196,56 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref={pinnedManagePanel} className={clsx(styles.pinnedManagePanelContainer)}>
|
<div ref={pinnedManagePanel} className={clsx(styles.pinnedManagePanelContainer)}>
|
||||||
{/* 固定新闻来源 */}
|
{/* 固定新闻来源 */}
|
||||||
<div className={clsx(styles.pinnedManagePanel)}>
|
<div className={clsx(styles.pinnedManagePanel)}>
|
||||||
<div className="header flex justify-between">
|
<div className="header flex justify-between">
|
||||||
<div className="title font-bold">{t('news.filter_source')}</div>
|
<div className="title font-bold">{t('news.filter_source')}</div>
|
||||||
<div className={'cursor-pointer block hover:text-blue-500'} onClick={setFalse}>
|
<div className={'cursor-pointer block hover:text-blue-500'} onClick={setFalse}>
|
||||||
<UpOutlined style={{fontSize: 20}}/>
|
<UpOutlined style={{fontSize: 20}}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="tags-list-container">
|
||||||
|
{
|
||||||
|
tags.filter(s => s.value !== 999999).map(it => {
|
||||||
|
const currentPinned = pinnedTag?.includes(Number(it.value));
|
||||||
|
return (<div
|
||||||
|
className={`filter-item border flex items-center px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${currentPinned ? 'bg-gray-100' : ''} hover:border-gray-400`}
|
||||||
|
key={it.value}
|
||||||
|
onClick={() => {
|
||||||
|
const value = Number(it.value)
|
||||||
|
if (pinnedTag && pinnedTag.includes(value)) {
|
||||||
|
setPinnedTag(pinnedTag.filter(s => s != value))
|
||||||
|
} else {
|
||||||
|
setPinnedTag([...(pinnedTag || []), value])
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<span>{it.label}</span>
|
||||||
|
{currentPinned &&
|
||||||
|
<span className={'ml-2'}><IconPin/></span>}
|
||||||
|
</div>)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="tags-list-container">
|
|
||||||
{
|
|
||||||
tags.filter(s => s.value !== 999999).map(it => {
|
|
||||||
const currentPinned = pinnedTag?.includes(Number(it.value));
|
|
||||||
return (<div
|
|
||||||
className={`filter-item border flex items-center px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${currentPinned?'bg-gray-100':''} hover:border-gray-400`}
|
|
||||||
key={it.value}
|
|
||||||
onClick={() => {
|
|
||||||
const value = Number(it.value)
|
|
||||||
if (pinnedTag && pinnedTag.includes(value)) {
|
|
||||||
setPinnedTag(pinnedTag.filter(s => s != value))
|
|
||||||
} else {
|
|
||||||
setPinnedTag([...(pinnedTag || []), value])
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<span>{it.label}</span>
|
|
||||||
{currentPinned &&
|
|
||||||
<span className={'ml-2'}><IconPin/></span>}
|
|
||||||
</div>)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/* 二级目录 */}
|
{/* 二级目录 */}
|
||||||
{state.tag_level_1_id != -1 && subOptions.length > 0 &&
|
{state.tag_level_1_id != -1 && subOptions.length > 0 &&
|
||||||
<div
|
<div
|
||||||
className="absolute news-source-lv-2 flex items-center absolute left-0 right-0">
|
className="absolute news-source-lv-2 flex items-center absolute left-0 right-0">
|
||||||
{
|
{
|
||||||
subOptions.map(it => (
|
subOptions.map(it => (
|
||||||
<div
|
<div
|
||||||
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_2_id == it.value ? 'text-black' : ' text-gray-400 hover:text-gray-600'}`}
|
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_2_id == it.value ? 'text-black' : ' text-gray-400 hover:text-gray-600'}`}
|
||||||
key={it.value}
|
key={it.value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleFilter({tag_level_1_id:state.tag_level_1_id,tag_level_2_id: Number(it.value)})
|
handleFilter({tag_level_1_id: state.tag_level_1_id, tag_level_2_id: Number(it.value)})
|
||||||
}}>{it.label}</div>)
|
}}>{it.label}</div>)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</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