fixed: ts build error

This commit is contained in:
LittleBoy 2024-12-16 22:17:22 +08:00
parent 4d5449364a
commit 1acdc2a99d
20 changed files with 89 additions and 235 deletions

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import clsx from "clsx"; import clsx from "clsx";
import {Popconfirm, Space} from "antd"; import {Popconfirm} from "antd";
import {IconAdd, IconDelete} from "@/components/icons"; import {IconAdd, IconDelete} from "@/components/icons";
import ImageList from "@/components/article/list.tsx"; import ImageList from "@/components/article/list.tsx";
@ -10,7 +10,7 @@ import styles from './article.module.scss'
type Props = { type Props = {
children?: React.ReactNode; children?: React.ReactNode;
index?: number; index: number;
className?: string; className?: string;
blocks: BlockContent[]; blocks: BlockContent[];
editable?: boolean; editable?: boolean;
@ -51,22 +51,6 @@ export default function ArticleBlock(
errorMessage errorMessage
}: Props) { }: Props) {
const blocks = rebuildBlockArray(defaultBlocks) const blocks = rebuildBlockArray(defaultBlocks)
const handleBlockRemove = (index: number) => {
// 删除当前项
onChange?.(blocks.filter((_, idx) => index !== idx))
}
const firstTextBlockIndex = blocks.findIndex(it => it.type === 'text')
// 新增
const handleAddBlock = (type: 'text' | 'image', insertIndex: number = -1) => {
const newBlock: BlockContent = type === 'text' ? {type: 'text', content: ''} : {type: 'image', content: ''};
const _blocks = [...blocks]
if (insertIndex == -1 || insertIndex >= blocks.length) { // -1或者越界表示新增
_blocks.push(newBlock)
} else {
_blocks.splice(insertIndex, 0, newBlock)
}
onChange?.(_blocks)
}
const handleBlockChange = (index: number, block: BlockContent) => { const handleBlockChange = (index: number, block: BlockContent) => {
const _blocks = [...blocks] const _blocks = [...blocks]

View File

@ -39,7 +39,7 @@ export default function ArticleEditModal(props: Props) {
} }
const save = props.type == 'news' ? article.save : regenerate const save = props.type == 'news' ? article.save : regenerate
setState({loading: true}) setState({loading: true})
save(title, groups, props.id > 0 ? props.id : undefined).then(() => { save(title, groups, props.id && props.id > 0 ? props.id : undefined).then(() => {
props.onClose?.(true) props.onClose?.(true)
}).catch(e=>{ }).catch(e=>{
setState({error: e.data || '保存失败,请重试!'}) setState({error: e.data || '保存失败,请重试!'})
@ -65,7 +65,7 @@ export default function ArticleEditModal(props: Props) {
return (<Modal return (<Modal
title={'编辑文章'} title={'编辑文章'}
open={props.id >= 0} open={!!props.id && props.id >= 0}
maskClosable={false} maskClosable={false}
keyboard={false} keyboard={false}
width={800} width={800}

View File

@ -14,7 +14,7 @@ type Props = {
function pushBlocksToGroup(blocks: BlockContent[],groups: BlockContent[][]){ function pushBlocksToGroup(blocks: BlockContent[],groups: BlockContent[][]){
const lastGroup = groups[groups.length - 1] const lastGroup = groups[groups.length - 1]
if (lastGroup && lastGroup.filter(s=>s.type == 'text') == 0) { if (lastGroup && lastGroup.filter(s=>s.type == 'text').length == 0) {
// 如果上一个group中没有文本则直接合并 // 如果上一个group中没有文本则直接合并
lastGroup.push(...blocks) lastGroup.push(...blocks)
} else { } else {

View File

@ -1,6 +1,6 @@
import React, {useState} from "react"; import React, {useState} from "react";
import {Input, Popconfirm, Spin, Upload, UploadProps} from "antd"; import {Input, Popconfirm, Spin, Upload, UploadProps} from "antd";
import {CloseOutlined,CloudUploadOutlined} from "@ant-design/icons"; import {CloseOutlined} from "@ant-design/icons";
import {clsx} from "clsx"; import {clsx} from "clsx";
import styles from './article.module.scss' import styles from './article.module.scss'

View File

@ -1,5 +1,3 @@
import React from "react";
import {BlockImage} from "@/components/article/item.tsx"; import {BlockImage} from "@/components/article/item.tsx";
import styles from './article.module.scss' import styles from './article.module.scss'

View File

@ -2,13 +2,14 @@ import React, {useState} from "react";
import {Button, Modal} from "antd"; import {Button, Modal} from "antd";
import {ButtonType} from "antd/es/button"; import {ButtonType} from "antd/es/button";
import {showErrorToast, showToast} from "@/components/message.ts"; import {showErrorToast, showToast} from "@/components/message.ts";
import {BizError} from "@/service/types.ts";
type Props = { type Props = {
selected: any[], selected: any[],
type?: ButtonType; type?: ButtonType;
emptyMessage: string, emptyMessage: string,
confirmMessage: React.ReactNode, confirmMessage: React.ReactNode,
onProcess: (ids: Id[]) => Promise<void> onProcess: (ids: Id[]) => Promise<any|void>
successMessage?: string; successMessage?: string;
onSuccess?: () => void; onSuccess?: () => void;
children?: React.ReactNode children?: React.ReactNode
@ -32,7 +33,7 @@ export default function ButtonBatch(
onSuccess() onSuccess()
} }
} catch (e) { } catch (e) {
showErrorToast(e) showErrorToast(e as unknown as BizError)
} finally { } finally {
setLoading(false) setLoading(false)
} }

View File

@ -2,7 +2,7 @@ import {useSortable} from "@dnd-kit/sortable";
import {useSetState} from "ahooks"; import {useSetState} from "ahooks";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import {clsx} from "clsx"; import {clsx} from "clsx";
import {Image, Popconfirm} from "antd"; import {Popconfirm} from "antd";
import {CheckCircleFilled, MenuOutlined, MinusCircleFilled} from "@ant-design/icons"; import {CheckCircleFilled, MenuOutlined, MinusCircleFilled} from "@ant-design/icons";
import ImageCover from '@/assets/images/cover.png' import ImageCover from '@/assets/images/cover.png'
@ -51,7 +51,7 @@ export const VideoListItem = (
className={`video-item-info flex gap-2 flex-1 bg-gray-100 h-[80px] overflow-hidden rounded-lg p-3 shadow-blue-500 ${active ? 'video-item-shadow' : ''}`}> className={`video-item-info flex gap-2 flex-1 bg-gray-100 h-[80px] overflow-hidden rounded-lg p-3 shadow-blue-500 ${active ? 'video-item-shadow' : ''}`}>
<div className={'video-title leading-7 flex-1'}>{video.title || video.video_title}</div> <div className={'video-title leading-7 flex-1'}>{video.title || video.video_title}</div>
<div className={'video-item-cover bg-white rounded-md overflow-hidden'}> <div className={'video-item-cover bg-white rounded-md overflow-hidden'}>
<img className="w-[100px] h-[56px] object-cover" src={video.cover || ImageCover} alt={video.video_title}/> <img className="w-[100px] h-[56px] object-cover" src={video.cover || ImageCover} />
</div> </div>
</div> </div>
<div className="operation flex items-center ml-2 gap-3 text-lg text-gray-400"> <div className="operation flex items-center ml-2 gap-3 text-lg text-gray-400">

View File

@ -2,19 +2,10 @@ import React, {createContext, useEffect, useReducer} from "react";
import Loader from "@/components/loader"; import Loader from "@/components/loader";
import {getAuthToken, setAuthToken} from "@/hooks/useAuth.ts"; import {getAuthToken, setAuthToken} from "@/hooks/useAuth.ts";
import {auth, getUserInfo} from "@/service/api/user.ts"; import {auth} from "@/service/api/user.ts";
const UserRoleStorageKey = 'user-current-role'; const UserRoleStorageKey = 'user-current-role';
function getCurrentRole() {
return (localStorage.getItem(UserRoleStorageKey)) as UserRole
}
export function setCurrentRole(role: UserRole) {
localStorage.setItem(UserRoleStorageKey, role)
}
function removeRoleStorage() { function removeRoleStorage() {
localStorage.removeItem(UserRoleStorageKey) localStorage.removeItem(UserRoleStorageKey)
} }
@ -87,7 +78,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
} }
// 登出 // 登出
const logout = async () => { const logout = async () => {
setAuthToken(null) setAuthToken(null,null,-1)
removeRoleStorage() removeRoleStorage()
dispatch({ dispatch({
action: 'logout', action: 'logout',

View File

@ -18,7 +18,7 @@ const clearAuth = () => {
localStorage.removeItem(AppConfig.AUTH_TOKEN_KEY); localStorage.removeItem(AppConfig.AUTH_TOKEN_KEY);
localStorage.removeItem(AppConfig.AUTHED_PERSON_DATA_KEY); localStorage.removeItem(AppConfig.AUTHED_PERSON_DATA_KEY);
} }
export const setAuthToken = (token: string | null,profileData:UserProfile, expiry_time = -1) => { export const setAuthToken = (token: string | null,profileData:UserProfile|null, expiry_time = -1) => {
if (!token) { if (!token) {
clearAuth(); clearAuth();
return; return;

View File

@ -1,4 +1,3 @@
import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import App from './App.tsx' import App from './App.tsx'

View File

@ -2,7 +2,6 @@ import {Button, Modal} from "antd";
import {Player} from "@/components/video/player.tsx"; import {Player} from "@/components/video/player.tsx";
import { ArticleGroupList } from "@/_local/mock-data";
import ArticleGroup from "@/components/article/group"; import ArticleGroup from "@/components/article/group";
type Props = { type Props = {
@ -31,7 +30,7 @@ export default function VideoDetail({video, onClose}: Props) {
<span>标题: xxxxxxxx</span> <span>标题: xxxxxxxx</span>
</div> </div>
<div className="content-container max-h-[500px] overflow-auto pr-1 mt-4"> <div className="content-container max-h-[500px] overflow-auto pr-1 mt-4">
<ArticleGroup groups={ArticleGroupList}/> <ArticleGroup groups={[]}/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,122 +0,0 @@
import React, {useState} from 'react';
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
useSortable, arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
type TestData = {
id: number;
}
type SortableItemProps = {
data: TestData;
active?: boolean;
id: number;
}
function SortableItem(props: SortableItemProps) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({
resizeObserverConfig: {},
id: props.data.id
});
return (
<div ref={setNodeRef} style={{
transform: `translateY(${transform ? transform?.y:0}px)`,
transition,
// marginTop:10,
// marginBottom:10
}} className={props.active ? 'drop-shadow shadow-blue-400 drop-shadow-md' : ''}>
<div className="h-[100px] mb-5 border p-5 rounded bg-white flex justify-between items-center">
<div className="flex-1">
<div>
{JSON.stringify(props.data)}
</div>
<div>{JSON.stringify(transform)}</div>
</div>
<button {...attributes} {...listeners} className="cursor-move">Move</button>
</div>
</div>
);
}
export function SortDemo() {
const [items, setItems] = useState<TestData[]>([
{id: 1},
{id: 2},
{id: 3},
{id: 4},
{id: 5},
]);
const [activeId, setActiveId] = useState<number>();
function handleDragEnd(event) {
const {active, over} = event;
setActiveId(undefined)
console.log(JSON.stringify({
items,
active: active.id,
over: over.id,
}))
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.findIndex(s=>s.id == active.id);
const newIndex = items.findIndex(s=>s.id == over.id);
const _newArr = arrayMove(items, oldIndex, newIndex);
console.log(JSON.stringify({
_newArr,
items,
oldIndex,
newIndex
}))
return _newArr;
});
}
}
// const sensors = useSensors(
// useSensor(PointerSensor),
// useSensor(KeyboardSensor, {
// coordinateGetter: sortableKeyboardCoordinates,
// })
// );
return (
<div>
<div>{JSON.stringify(items)}</div>
<DndContext
modifiers={[restrictToVerticalAxis]}
onDragEnd={handleDragEnd}
onDragStart={e => {
if (e.active && e.active.id) {
setActiveId(Number(e.active.id))
}
}}
>
<SortableContext
items={items}
>
{items.map(it => <SortableItem active={it.id == activeId} key={it.id} data={it} id={it.id}/>)}
</SortableContext>
</DndContext>
</div>
);
}

View File

@ -139,7 +139,7 @@ export default function LiveIndex() {
return clearAllTimer; return clearAllTimer;
}, []) }, [])
const processDeleteVideo = async (ids: number[]) => { const processDeleteVideo = async (ids: Id[]) => {
deleteByIds(ids).then(() => { deleteByIds(ids).then(() => {
showToast('删除成功!', 'success') showToast('删除成功!', 'success')
loadList() loadList()

View File

@ -4,63 +4,63 @@ import React, {useEffect, useMemo} from "react";
const prevSelectValues: Id[][] = []; const prevSelectValues: Id[][] = [];
function buildValues(options: OptionItem[], selectedValues: Id[][], allValue = -1) { // function buildValues(options: OptionItem[], selectedValues: Id[][], allValue = -1) {
const values: Id[][] = [] // const values: Id[][] = []
selectedValues.forEach(item => { // selectedValues.forEach(item => {
if (item.length === 0) { // if (item.length === 0) {
return; // return;
} // }
if (item.length == 1) { // if (item.length == 1) {
if (item[0] == allValue) { // if (item[0] == allValue) {
values.push([allValue]); // values.push([allValue]);
return; // return;
} // }
// 只有1个值 表示选择了一级分类下的所有二级分类 // // 只有1个值 表示选择了一级分类下的所有二级分类
const op = options.find(option => option.value === item[0]); // const op = options.find(option => option.value === item[0]);
if (!op || !op.children || op.children.length === 0) { // if (!op || !op.children || op.children.length === 0) {
// 没有找到或者没有二级分类 // // 没有找到或者没有二级分类
return; // return;
} // }
// 只有一级分类 // // 只有一级分类
op.children.forEach(child => { // op.children.forEach(child => {
values.push([item[0], child.value]); // values.push([item[0], child.value]);
}) // })
return; // return;
} // }
values.push(item) // values.push(item)
}) // })
return values // return values
} // }
// 获取两个数组的差集 // 获取两个数组的差集
function getValuesDiff(values: Id[][], prevValues: Id[][]) { // function getValuesDiff(values: Id[][], prevValues: Id[][]) {
if (values.length != prevValues.length) { // if (values.length != prevValues.length) {
const moreItems = values.length > prevValues.length ? values : prevValues; // const moreItems = values.length > prevValues.length ? values : prevValues;
const lessItems = values.length > prevValues.length ? prevValues : values; // const lessItems = values.length > prevValues.length ? prevValues : values;
const lessItemsKeys = lessItems.map(s => s.join('-')); // const lessItemsKeys = lessItems.map(s => s.join('-'));
for (let i = 0; i < moreItems.length; i++) { // for (let i = 0; i < moreItems.length; i++) {
const item = moreItems[i], index = lessItemsKeys.indexOf(item.join('-')); // const item = moreItems[i], index = lessItemsKeys.indexOf(item.join('-'));
if (index === -1) { // if (index === -1) {
return item; // return item;
} // }
} // }
} // }
return null; // return null;
} // }
//
function getAllValue(options: OptionItem[]) { // function getAllValue(options: OptionItem[]) {
const values: Id[][] = [] // const values: Id[][] = []
options.forEach(option => { // options.forEach(option => {
if (option.children && option.children.length > 0) { // if (option.children && option.children.length > 0) {
option.children.forEach(child => { // option.children.forEach(child => {
values.push([option.value, child.value]); // values.push([option.value, child.value]);
}) // })
} else { // } else {
values.push([option.value]); // values.push([option.value]);
} // }
}) // })
return values // return values
} // }
export default function ArticleCascader(props: { export default function ArticleCascader(props: {
options: OptionItem[]; options: OptionItem[];
@ -71,9 +71,9 @@ export default function ArticleCascader(props: {
// 清除上一次的选中值 // 清除上一次的选中值
prevSelectValues.length = 0; prevSelectValues.length = 0;
}, []) }, [])
const allOptionValue = useMemo(() => { // const allOptionValue = useMemo(() => {
return getAllValue(props.options) // return getAllValue(props.options)
}, [props.options]) // }, [props.options])
const setSelectValues = (value: Id[][]) => { const setSelectValues = (value: Id[][]) => {
_setSelectValues(value) _setSelectValues(value)

View File

@ -1,13 +1,14 @@
import {Button, Pagination, Table, TableColumnsType, TableProps, Typography} from "antd"; import {Button, Pagination, Table, TableColumnsType, TableProps, Typography} from "antd";
import {Card} from "@/components/card"; import {Card} from "@/components/card";
import React, {useEffect, useState} from "react"; import React, {useState} from "react";
import {useRequest} from "ahooks"; import {useRequest} from "ahooks";
import {formatTime} from "@/util/strings.ts"; import {formatTime} from "@/util/strings.ts";
import ArticleEditModal from "@/components/article/edit-modal.tsx"; import ArticleEditModal from "@/components/article/edit-modal.tsx";
import {getList} from "@/service/api/article.ts"; import {getList} from "@/service/api/article.ts";
import EditSearchForm from "@/pages/news/components/edit-search-form.tsx"; import EditSearchForm from "@/pages/news/components/edit-search-form.tsx";
import ButtonPush2Video from "@/pages/news/components/button-push2video.tsx"; import ButtonPush2Video from "@/pages/news/components/button-push2video.tsx";
import {Key} from "antd/es/table/interface";
export default function NewEdit() { export default function NewEdit() {
@ -55,8 +56,8 @@ export default function NewEdit() {
]; ];
const rowSelection: TableProps<ListArticleItem>['rowSelection'] = { const rowSelection: TableProps<ListArticleItem>['rowSelection'] = {
onChange: (selectedRowKeys: Id[]) => { onChange: (selectedRowKeys: Key[]) => {
setSelectedRowKeys(selectedRowKeys) setSelectedRowKeys(selectedRowKeys as Id[])
}, },
}; };
@ -75,7 +76,7 @@ export default function NewEdit() {
bordered bordered
pagination={false} pagination={false}
/> />
{data?.pagination.total > 0 && <div className="footer flex justify-between items-center mt-5"> {data?.pagination && data?.pagination.total > 0 && <div className="footer flex justify-between items-center mt-5">
<Pagination <Pagination
current={params.pagination.page} current={params.pagination.page}
total={data?.pagination.total} total={data?.pagination.total}

View File

@ -20,7 +20,7 @@ export default function NewsIndex() {
limit: 10 limit: 10
} }
}) })
const [checkedId, setCheckedId] = useState<number[]>([]) const [checkedId, setCheckedId] = useState<Id[]>([])
const [activeNews, setActiveNews] = useState<NewsInfo>() const [activeNews, setActiveNews] = useState<NewsInfo>()
const [state, setState] = useState<{ const [state, setState] = useState<{
@ -66,7 +66,7 @@ export default function NewsIndex() {
<Checkbox checked={state.checkAll} onChange={e => { <Checkbox checked={state.checkAll} onChange={e => {
setState({checkAll: e.target.checked}) setState({checkAll: e.target.checked})
if (e.target.checked) { if (e.target.checked) {
setCheckedId(data.list.map(item => item.id)) setCheckedId(data?.list?.map(item => item.id) || [])
} else { } else {
setCheckedId([]) setCheckedId([])
} }
@ -118,7 +118,7 @@ export default function NewsIndex() {
))} ))}
</div> </div>
{data?.pagination.total > 0 ? <div className="flex justify-center mt-10"> {data?.pagination && data?.pagination.total > 0 ? <div className="flex justify-center mt-10">
<Pagination <Pagination
current={params.pagination.page} current={params.pagination.page}
total={data?.pagination.total} total={data?.pagination.total}

View File

@ -85,7 +85,7 @@ export default function FormLogin() {
<Form.Item> <Form.Item>
<Button disabled={disabled || loading} loading={loading} type="primary" size={'large'} htmlType="submit" <Button disabled={disabled || loading} loading={loading} type="primary" size={'large'} htmlType="submit"
block shape={'round'}> block shape={'round'}>
{login ? '登录中' : '立即登录'} {loading ? '登录中...' : '立即登录'}
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>

View File

@ -2,7 +2,6 @@ import axios from 'axios';
import {stringify} from 'qs' import {stringify} from 'qs'
import {BizError} from './types'; import {BizError} from './types';
import {getAuthToken} from "@/hooks/useAuth.ts"; import {getAuthToken} from "@/hooks/useAuth.ts";
import {showToast} from "@/components/message.ts";
const JSON_FORMAT: string = 'application/json'; const JSON_FORMAT: string = 'application/json';
const REQUEST_TIMEOUT = 300000; // 超时时长5min const REQUEST_TIMEOUT = 300000; // 超时时长5min

4
src/types/api.d.ts vendored
View File

@ -82,6 +82,7 @@ declare interface ListCrawlerNewsItem extends BasicArticleInfo {
} }
declare interface VideoInfo { declare interface VideoInfo {
id: number; id: number;
video_title: string;
title: string; title: string;
cover: string; cover: string;
oss_video_url: string; oss_video_url: string;
@ -92,6 +93,7 @@ declare interface VideoInfo {
// room live // room live
declare interface LiveVideoInfo { declare interface LiveVideoInfo {
id: number; id: number;
title: string;
video_id: number; video_id: number;
video_title: string; video_title: string;
cover: string; cover: string;
@ -103,5 +105,5 @@ declare interface LiveVideoInfo {
declare interface LiveState{ declare interface LiveState{
id: number; id: number;
live_start_time?: number; live_start_time: number;
} }

View File

@ -21,7 +21,9 @@
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": false,
"noImplicitAny": false,
"noUnusedParameters": false,
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": ["src"], "include": ["src"],