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 clsx from "clsx";
import {Popconfirm, Space} from "antd";
import {Popconfirm} from "antd";
import {IconAdd, IconDelete} from "@/components/icons";
import ImageList from "@/components/article/list.tsx";
@ -10,7 +10,7 @@ import styles from './article.module.scss'
type Props = {
children?: React.ReactNode;
index?: number;
index: number;
className?: string;
blocks: BlockContent[];
editable?: boolean;
@ -51,22 +51,6 @@ export default function ArticleBlock(
errorMessage
}: Props) {
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 _blocks = [...blocks]

View File

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

View File

@ -14,7 +14,7 @@ type Props = {
function pushBlocksToGroup(blocks: BlockContent[],groups: BlockContent[][]){
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中没有文本则直接合并
lastGroup.push(...blocks)
} else {

View File

@ -1,6 +1,6 @@
import React, {useState} from "react";
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 styles from './article.module.scss'

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import {useSortable} from "@dnd-kit/sortable";
import {useSetState} from "ahooks";
import React, {useEffect} from "react";
import {clsx} from "clsx";
import {Image, Popconfirm} from "antd";
import {Popconfirm} from "antd";
import {CheckCircleFilled, MenuOutlined, MinusCircleFilled} from "@ant-design/icons";
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' : ''}`}>
<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'}>
<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 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 {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';
function getCurrentRole() {
return (localStorage.getItem(UserRoleStorageKey)) as UserRole
}
export function setCurrentRole(role: UserRole) {
localStorage.setItem(UserRoleStorageKey, role)
}
function removeRoleStorage() {
localStorage.removeItem(UserRoleStorageKey)
}
@ -87,7 +78,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
}
// 登出
const logout = async () => {
setAuthToken(null)
setAuthToken(null,null,-1)
removeRoleStorage()
dispatch({
action: 'logout',

View File

@ -18,7 +18,7 @@ const clearAuth = () => {
localStorage.removeItem(AppConfig.AUTH_TOKEN_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) {
clearAuth();
return;

View File

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

View File

@ -2,7 +2,6 @@ import {Button, Modal} from "antd";
import {Player} from "@/components/video/player.tsx";
import { ArticleGroupList } from "@/_local/mock-data";
import ArticleGroup from "@/components/article/group";
type Props = {
@ -31,7 +30,7 @@ export default function VideoDetail({video, onClose}: Props) {
<span>标题: xxxxxxxx</span>
</div>
<div className="content-container max-h-[500px] overflow-auto pr-1 mt-4">
<ArticleGroup groups={ArticleGroupList}/>
<ArticleGroup groups={[]}/>
</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;
}, [])
const processDeleteVideo = async (ids: number[]) => {
const processDeleteVideo = async (ids: Id[]) => {
deleteByIds(ids).then(() => {
showToast('删除成功!', 'success')
loadList()

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,6 @@ import axios from 'axios';
import {stringify} from 'qs'
import {BizError} from './types';
import {getAuthToken} from "@/hooks/useAuth.ts";
import {showToast} from "@/components/message.ts";
const JSON_FORMAT: string = 'application/json';
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 {
id: number;
video_title: string;
title: string;
cover: string;
oss_video_url: string;
@ -92,6 +93,7 @@ declare interface VideoInfo {
// room live
declare interface LiveVideoInfo {
id: number;
title: string;
video_id: number;
video_title: string;
cover: string;
@ -103,5 +105,5 @@ declare interface LiveVideoInfo {
declare interface LiveState{
id: number;
live_start_time?: number;
live_start_time: number;
}

View File

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