💄 update 新闻编辑样式

This commit is contained in:
LittleBoy 2024-12-22 14:51:15 +08:00
parent 3c57d2d1bc
commit c95fc4e79d
10 changed files with 252 additions and 129 deletions

View File

@ -29,12 +29,12 @@
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: #ccc; background: rgba(64, 150, 255, 0.5);
height: 10px; height: 10px;
border-radius: 5px; border-radius: 5px;
&:hover { &:hover {
background: #999; background: rgba(64, 150, 255, 1);
cursor: pointer; cursor: pointer;
} }
} }
@ -176,9 +176,22 @@
} }
} }
.page-action-to-top {
@apply text-right;
.btn-to-top {
@apply w-[44px] h-[44px] inline-block bg-blue-300 text-center p-0 transition hover:bg-blue-500;
min-width: 0;
border-radius: 50px;
font-size: 24px;
}
}
.data-list-container { .data-list-container {
height: calc(100vh - var(--app-header-header) - 200px); height: calc(100vh - var(--app-header-header) - 200px);
overflow: auto; overflow: auto;
margin-right: -20px;
padding-right: 16px;
scrollbar-gutter: stable;
.data-list-container-inner { .data-list-container-inner {
@ -186,6 +199,20 @@
} }
// override antd style // override antd style
.ant-checkbox {
border-radius: 2px;
.ant-checkbox-inner {
border-radius: 2px;
width: 18px;
height: 18px;
&:after {
//inset-inline-start: 28%;
}
}
}
.ant-message { .ant-message {
z-index: var(--message-z-index); z-index: var(--message-z-index);
} }
@ -259,6 +286,7 @@
.select-value { .select-value {
@apply text-blue-500 px-4 cursor-pointer h-[31px]; @apply text-blue-500 px-4 cursor-pointer h-[31px];
} }
.options-list-container { .options-list-container {
@apply overflow-auto py-1 drop-shadow absolute top-[30px]; @apply overflow-auto py-1 drop-shadow absolute top-[30px];
border-radius: 0 0 0.75rem 0.75rem; border-radius: 0 0 0.75rem 0.75rem;
@ -281,7 +309,8 @@
.tag-select-child-container { .tag-select-child-container {
.ant-popover-inner { .ant-popover-inner {
@apply p-0 overflow-hidden drop-shadow shadow-none; // @apply p-0 overflow-hidden drop-shadow shadow-none;
//
background: linear-gradient(180deg, rgb(235, 242, 253) 0%, rgb(244, 247, 252) 100%); background: linear-gradient(180deg, rgb(235, 242, 253) 0%, rgb(244, 247, 252) 100%);
border-radius: 0 0.75rem 0.75rem 0; border-radius: 0 0.75rem 0.75rem 0;
transform: translate(16px, -7px); transform: translate(16px, -7px);

View File

@ -69,13 +69,11 @@ export const IconPin = ({style, className}: IconProps) => (
) )
export const IconDelete = ({style, className}: IconProps) => ( export const IconDelete = ({style, className}: IconProps) => (
<svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg" <svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg"
width="1em" height="1em" viewBox="0 0 1024 1024" version="1.1"> width="0.86em" height="1em" viewBox="0 0 20 23" version="1.1">
<path <path
d="M765.505691 191.942567H639.627772c0-35.32453-28.636201-63.960731-63.96073-63.960731H447.74558c-35.32453 0-63.960731 28.636201-63.96073 63.960731H257.905908c-36.452213 0-66.00325 29.551036-66.00325 66.00325v59.875692c0 36.452213 29.551036 66.00325 66.00325 66.00325h-2.042519v445.681572c0 36.452213 29.551036 66.00325 66.003249 66.00325h379.679346c36.452213 0 66.00325-29.551036 66.00325-66.00325V383.823736h-2.04252c36.452213 0 66.00325-29.551036 66.00325-66.00325v-59.875693c-0.001023-36.452213-29.551036-66.002226-66.004273-66.002226z m-61.918211 611.470479c-0.101307 3.123131-1.743714 27.813462-27.961842 28.134781H347.905688c-27.988448-0.343831-27.969005-28.459169-27.969005-28.459169l-0.112564 0.031722V383.823736h383.763361v419.58931z m31.980365-483.550041H287.843754c-17.662265 0-31.980365-14.3181-31.980365-31.980365 0-17.662265 14.3181-31.980365 31.980365-31.980366H735.568868c17.662265 0 31.980365 14.3181 31.980366 31.980366-0.001023 17.662265-14.319124 31.980365-31.981389 31.980365z" d="M3.66667 22.3828C2.99444 22.3828 2.41918 22.1437 1.94089 21.6654C1.46259 21.1871 1.22304 20.6114 1.22222 19.9384V4.04948H0V1.60503H6.11111V0.382812H13.4444V1.60503H19.5556V4.04948H18.3333V19.9384C18.3333 20.6106 18.0942 21.1863 17.6159 21.6654C17.1376 22.1445 16.5619 22.3836 15.8889 22.3828H3.66667ZM15.8889 4.04948H3.66667V19.9384H15.8889V4.04948ZM6.11111 17.4939H8.55555V6.49392H6.11111V17.4939ZM11 17.4939H13.4444V6.49392H11V17.4939Z"
fill="currentColor"/> fill="currentColor" />
<path
d="M447.74558 767.588119c17.662265 0 31.980365-14.3181 31.980366-31.980365V479.764831c0-17.662265-14.3181-31.980365-31.980366-31.980365-17.662265 0-31.980365 14.3181-31.980365 31.980365v255.842923c0 17.662265 14.3181 31.980365 31.980365 31.980365zM575.667042 767.588119c17.662265 0 31.980365-14.3181 31.980365-31.980365V479.764831c0-17.662265-14.3181-31.980365-31.980365-31.980365-17.662265 0-31.980365 14.3181-31.980366 31.980365v255.842923c0 17.662265 14.3181 31.980365 31.980366 31.980365z"
fill="currentColor"/>
</svg> </svg>
) )
export const IconAddText = ({style, className}: IconProps) => ( export const IconAddText = ({style, className}: IconProps) => (
@ -84,6 +82,8 @@ export const IconAddText = ({style, className}: IconProps) => (
<path <path
d="M714.688 512a224 224 0 1 1 0 448 224 224 0 0 1 0-448z m96-448a64 64 0 0 1 64 64v284.992a36.032 36.032 0 0 1-71.424 5.76l-0.64-5.76V136h-624v752h212.032a35.968 35.968 0 1 1 0 72H170.688a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h640z m-96 512a160 160 0 1 0 0 320 160 160 0 0 0 0-320z m0 28.032c15.424 0 27.968 12.48 27.968 27.968v76.032h76.032a28.032 28.032 0 0 1 0 55.936h-76.032v76.032a28.032 28.032 0 0 1-56 0v-76.032H610.688a27.968 27.968 0 1 1 0-55.936h75.968V631.936c0-15.488 12.544-28.032 28.032-28.032zM401.664 672a32 32 0 1 1 0 64h-112a32 32 0 1 1 0-64h112z m57.984-192a32 32 0 0 1 0 64H289.664a32 32 0 1 1 0-64h169.984z m230.016-192a32 32 0 0 1 0 64h-400a32 32 0 1 1 0-64h400z" d="M714.688 512a224 224 0 1 1 0 448 224 224 0 0 1 0-448z m96-448a64 64 0 0 1 64 64v284.992a36.032 36.032 0 0 1-71.424 5.76l-0.64-5.76V136h-624v752h212.032a35.968 35.968 0 1 1 0 72H170.688a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h640z m-96 512a160 160 0 1 0 0 320 160 160 0 0 0 0-320z m0 28.032c15.424 0 27.968 12.48 27.968 27.968v76.032h76.032a28.032 28.032 0 0 1 0 55.936h-76.032v76.032a28.032 28.032 0 0 1-56 0v-76.032H610.688a27.968 27.968 0 1 1 0-55.936h75.968V631.936c0-15.488 12.544-28.032 28.032-28.032zM401.664 672a32 32 0 1 1 0 64h-112a32 32 0 1 1 0-64h112z m57.984-192a32 32 0 0 1 0 64H289.664a32 32 0 1 1 0-64h169.984z m230.016-192a32 32 0 0 1 0 64h-400a32 32 0 1 1 0-64h400z"
fill="currentColor"/> fill="currentColor"/>
<path d="M12.2222 0.382812C12.5337 0.383158 12.8334 0.502443 13.0599 0.716294C13.2864 0.930145 13.4227 1.22242 13.441 1.53341C13.4592 1.84439 13.3581 2.15061 13.1581 2.3895C12.9582 2.62838 12.6746 2.78191 12.3652 2.8187L12.2222 2.82726H2.44444V19.9384H19.5556V10.1606C19.5559 9.84907 19.6752 9.54944 19.889 9.32292C20.1029 9.0964 20.3952 8.96008 20.7061 8.94182C21.0171 8.92357 21.3234 9.02475 21.5622 9.22469C21.8011 9.42463 21.9547 9.70825 21.9914 10.0176L22 10.1606V19.9384C22.0002 20.5551 21.7673 21.1491 21.3479 21.6013C20.9286 22.0535 20.3539 22.3305 19.7389 22.3767L19.5556 22.3828H2.44444C1.82774 22.383 1.23375 22.1501 0.781553 21.7308C0.329353 21.3114 0.0523642 20.7367 0.00611137 20.1217L1.2255e-07 19.9384V2.82726C-0.000195041 2.21055 0.232719 1.61656 0.652052 1.16436C1.07138 0.712165 1.64614 0.435177 2.26111 0.388924L2.44444 0.382812H12.2222ZM19.8526 0.802035C20.0725 0.582832 20.3676 0.455567 20.678 0.446089C20.9884 0.436611 21.2908 0.545631 21.5237 0.751005C21.7566 0.95638 21.9026 1.24271 21.932 1.55184C21.9615 1.86096 21.8722 2.16971 21.6822 2.41537L21.5808 2.53148L9.48078 14.6303C9.26083 14.8495 8.96569 14.9767 8.65531 14.9862C8.34493 14.9957 8.04257 14.8867 7.80966 14.6813C7.57675 14.4759 7.43074 14.1896 7.40129 13.8805C7.37184 13.5713 7.46116 13.2626 7.65111 13.0169L7.75256 12.902L19.8526 0.802035Z"
fill="currentColor"/>
</svg> </svg>
) )
@ -130,9 +130,8 @@ export const IconLive = ({style, className}: IconProps) => (
export const IconEdit = ({style, className}: IconProps) => ( export const IconEdit = ({style, className}: IconProps) => (
<svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg" <svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg"
width="1em" height="1em" viewBox="0 0 1024 1024" version="1.1"> width="1em" height="1em" viewBox="0 0 23 24" version="1.1">
<path <path d="M12.2222 0.382812C12.5337 0.383158 12.8334 0.502443 13.0599 0.716294C13.2864 0.930145 13.4227 1.22242 13.441 1.53341C13.4592 1.84439 13.3581 2.15061 13.1581 2.3895C12.9582 2.62838 12.6746 2.78191 12.3652 2.8187L12.2222 2.82726H2.44444V19.9384H19.5556V10.1606C19.5559 9.84907 19.6752 9.54944 19.889 9.32292C20.1029 9.0964 20.3952 8.96008 20.7061 8.94182C21.0171 8.92357 21.3234 9.02475 21.5622 9.22469C21.8011 9.42463 21.9547 9.70825 21.9914 10.0176L22 10.1606V19.9384C22.0002 20.5551 21.7673 21.1491 21.3479 21.6013C20.9286 22.0535 20.3539 22.3305 19.7389 22.3767L19.5556 22.3828H2.44444C1.82774 22.383 1.23375 22.1501 0.781553 21.7308C0.329353 21.3114 0.0523642 20.7367 0.00611137 20.1217L1.2255e-07 19.9384V2.82726C-0.000195041 2.21055 0.232719 1.61656 0.652052 1.16436C1.07138 0.712165 1.64614 0.435177 2.26111 0.388924L2.44444 0.382812H12.2222ZM19.8526 0.802035C20.0725 0.582832 20.3676 0.455567 20.678 0.446089C20.9884 0.436611 21.2908 0.545631 21.5237 0.751005C21.7566 0.95638 21.9026 1.24271 21.932 1.55184C21.9615 1.86096 21.8722 2.16971 21.6822 2.41537L21.5808 2.53148L9.48078 14.6303C9.26083 14.8495 8.96569 14.9767 8.65531 14.9862C8.34493 14.9957 8.04257 14.8867 7.80966 14.6813C7.57675 14.4759 7.43074 14.1896 7.40129 13.8805C7.37184 13.5713 7.46116 13.2626 7.65111 13.0169L7.75256 12.902L19.8526 0.802035Z"
d="M804.6 689.8l64-64c10-10 27.4-3 27.4 11.4V928c0 53-43 96-96 96H96c-53 0-96-43-96-96V224c0-53 43-96 96-96h547c14.2 0 21.4 17.2 11.4 27.4l-64 64c-3 3-7 4.6-11.4 4.6H96v704h704V701c0-4.2 1.6-8.2 4.6-11.2z m313.2-403.6L592.6 811.4l-180.8 20c-52.4 5.8-97-38.4-91.2-91.2l20-180.8L865.8 34.2c45.8-45.8 119.8-45.8 165.4 0l86.4 86.4c45.8 45.8 45.8 120 0.2 165.6zM920.2 348L804 231.8 432.4 603.6l-14.6 130.6 130.6-14.6L920.2 348z m129.6-159.4l-86.4-86.4c-8.2-8.2-21.6-8.2-29.6 0L872 164l116.2 116.2 61.8-61.8c8-8.4 8-21.6-0.2-29.8z" fill="currentColor"/>
fill="currentColor"/>
</svg> </svg>
) )

View File

@ -0,0 +1,26 @@
import {Button} from "antd";
import {ArrowUpOutlined} from "@ant-design/icons";
type ButtonToTopProps = {
onClick?: () => void;
visible?: boolean;
container?: HTMLElement | string;
}
export default function ButtonToTop(props: ButtonToTopProps) {
return (
<div className={'page-action-to-top'}>
{props.visible && <button className="btn-to-top" onClick={()=>{
console.log(props)
if(props.onClick){
props.onClick()
}else if(props.container){
const container = typeof(props.container) == 'string'? document.querySelector(props.container):props.container
if(container){
container.scrollTop = 0
}
}
}}><ArrowUpOutlined/></button>}
</div>
)
}

View File

@ -1,12 +1,17 @@
import React, {useEffect} from "react"; import React, {CSSProperties, useCallback, useEffect, useImperativeHandle, useRef} from "react";
import {useInViewport} from "ahooks"; import {useInViewport, useScroll} from "ahooks";
export type InfiniteScrollerRef = {
scrollToPosition: (top: number) => void
};
export type InfiniteScrollerProps = { export type InfiniteScrollerProps = {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
rootClassName?: string; rootClassName?: string;
style?: CSSProperties;
loadingPlaceholder?: React.ReactNode; loadingPlaceholder?: React.ReactNode;
onCallback: (page: number, prevPage) => void; onCallback: (page: number, prevPage) => void;
onScroll?: (top: number) => void;
empty?: React.ReactNode; empty?: React.ReactNode;
loading?: boolean; loading?: boolean;
pagination?: { pagination?: {
@ -15,10 +20,32 @@ export type InfiniteScrollerProps = {
total: number; total: number;
}; };
} }
const InfiniteScroller = React.forwardRef<InfiniteScrollerRef, InfiniteScrollerProps>((props, ref) => {
export default function InfiniteScroller(props: InfiniteScrollerProps) {
const {pagination} = props; const {pagination} = props;
const [inView] = useInViewport(() => document.querySelector('.data-load-control-element')) const [inView] = useInViewport(() => document.querySelector('.data-load-control-element'))
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
const scrollPosition = useScroll(scrollContainerRef);
const scrollToPosition = useCallback((top: number) => {
if (scrollContainerRef.current) {
console.log('xaf');
scrollContainerRef.current.scrollTo({
top,
behavior: 'smooth'
})
}
}, [scrollContainerRef])
useImperativeHandle(ref, () => {
return {
scrollToPosition
}
})
useEffect(() => {
if (scrollPosition && props.onScroll) {
props.onScroll(scrollPosition.top)
}
}, [scrollPosition])
useEffect(() => { useEffect(() => {
if (!pagination) return; if (!pagination) return;
@ -30,16 +57,18 @@ export default function InfiniteScroller(props: InfiniteScrollerProps) {
} }
} }
}, [inView]) }, [inView])
return (<div className={`data-list-container ${props.rootClassName}`}>
return (<div ref={scrollContainerRef} className={`data-list-container ${props.rootClassName}`} style={props.style}>
<div className={`data-list-container-inner ${props.className}`}>{props.children}</div> <div className={`data-list-container-inner ${props.className}`}>{props.children}</div>
{props?.pagination && props.pagination.total > props.pagination.limit * props.pagination.page && (props.loadingPlaceholder || {props?.pagination && props.pagination.total > props.pagination.limit * props.pagination.page && (props.loadingPlaceholder ||
<div className="data-load-control-element py-10 text-center"> <div className="data-load-control-element py-10 text-center">
<div className="loading-text">...</div> <div className="loading-text">...</div>
</div>)} </div>)}
{props?.empty && props.pagination?.total == 0 && <div className="flex justify-center text-center pt-20"> {props?.empty && props.pagination?.total == 0 && <div className="flex justify-center text-center pt-20">
<div className=" rounded-lg px-4 py-10"> <div className=" rounded-lg px-4 py-10">
{props.empty} {props.empty}
</div> </div>
</div>} </div>}
</div>); </div>);
} }) //(props: InfiniteScrollerProps) =>{}
export default InfiniteScroller

View File

@ -49,11 +49,11 @@ export default function EditSearchForm(props: {
placeholder="请输入新闻标题关键词进行搜索" placeholder="请输入新闻标题关键词进行搜索"
onPressEnter={handleSubmit} onPressEnter={handleSubmit}
/> />
<span className="ml-5 text-sm"></span> {/*<span className="ml-5 text-sm">来源</span>*/}
<ArticleCascader {/*<ArticleCascader*/}
options={articleTags} {/* options={articleTags}*/}
onChange={setTags} {/* onChange={setTags}*/}
/> {/*/>*/}
<TagSelect onChange={setTags} options={articleTags}/> <TagSelect onChange={setTags} options={articleTags}/>
</div> </div>
) )

View File

@ -31,10 +31,51 @@
} }
.newListTable{ .newListTable{
:global{ :global{
.header{}
.body{}
.row{ .row{
@apply bg-white mt-2 p-4 rounded; @apply bg-white mt-2 py-2 px-4 rounded-xl gap-2 border;
border-width: 2px;
&.checked{
@apply border-primary-blue bg-primary-blue-bg;
}
}
.col{
@apply flex items-center relative pl-6;
height: 44px;
&:after{
@apply absolute;
border-right: solid 1px #e8e8e8;
content: ' ';
top:2px;
bottom: 2px;
left:0;
}
}
.title{
@apply flex-1 pl-0;
&:after{
display: none;
}
}
.source{
width: 150px;
}
.time{
width: 150px;
}
.operations{
@apply gap-4;
width: 100px;
}
.header{
@apply bg-primary-bg;
.operations{
}
}
.body{}
.icon-btn{
@apply text-gray-400 hover:text-blue-500 cursor-pointer;
font-size: 18px;
} }
} }
} }

View File

@ -1,142 +1,136 @@
import {Button, Checkbox, Pagination, Space, Table, TableColumnsType, TableProps, Typography} from "antd"; import {Checkbox, Space} from "antd";
import {Card} from "@/components/card"; import React, {useRef, 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";
import styles from './components/style.module.scss' import styles from './components/style.module.scss'
import InfiniteScroller from "@/components/scoller/infinite-scroller.tsx"; import InfiniteScroller, {InfiniteScrollerRef} from "@/components/scoller/infinite-scroller.tsx";
import {IconDelete, IconEdit} from "@/components/icons";
import {clsx} from "clsx";
import ButtonToTop from "@/components/scoller/button-to-top.tsx";
export default function NewEdit() { export default function NewEdit() {
const [state, setState] = useState<{
checkAll?: boolean;
showToTop?: boolean;
}>({})
const [editId, setEditId] = useState(-1) const [editId, setEditId] = useState(-1)
const [selectedRowKeys, setSelectedRowKeys] = useState<Id[]>([]) const [selectedRowKeys, setSelectedRowKeys] = useState<Id[]>([])
const [params, setParams] = useState<ApiArticleSearchParams>({ const [params, setParams] = useState<ApiArticleSearchParams>({
pagination: {page: 1, limit: 10} pagination: {page: 1, limit: 10}
}) })
const {data, refresh, loading} = useRequest(() => getList(params), {refreshDeps: [params]}) const [data, setData] = useState<DataList<ListArticleItem>>()
const {refresh, loading} = useRequest(() => getList(params), {
refreshDeps: [params],
onSuccess: (data) => {
setData(prev => {
// 判断页码是否是第1页
if (data.pagination.page == 1) return data;
return {
list: [...(prev?.list || []), ...(data?.list || [])],
pagination: data.pagination
}
})
}
})
// : TableColumnsType<ListArticleItem> const handleItemChecked = (checked: boolean, item: ListArticleItem) => {
const columns = [ if (checked) {
{ setSelectedRowKeys(prev => [...prev, item.id])
title: '标题', } else {
dataIndex: 'title', setSelectedRowKeys(prev => prev.filter(it => it != item.id))
render: (_, record) => (<div> }
<div className="title text-base">{_}</div> }
<div className="summary text-sm text-gray-400 line-clamp-1">{record.summary}</div> const handleCheckAll = (checked: boolean) => {
</div>) setState({checkAll: checked})
}, if (checked) {
{ setSelectedRowKeys(data?.list?.map(item => item.id) || [])
title: '来源', } else {
minWidth: 150, setSelectedRowKeys([])
dataIndex: 'media_name', }
}, }
{ const scrollerRef = useRef<InfiniteScrollerRef>(null)
title: '时间',
width: 150,
dataIndex: 'time',
render: (_, record) => {
return formatTime(record.publish_time, 'YYYY-MM-DD HH:mm')
}
},
{
title: '操作',
width: 80,
align: 'center',
render: (_, record) => (<Space>
<Button onClick={() => {
setEditId(record.id)
}}></Button>
<Button onClick={() => {
setEditId(record.id)
}}></Button>
<Checkbox type="link"/>
</Space>),
},
];
return (<div className="container pb-5 news-edit"> return (<div className="container pb-5 news-edit">
<div className="search-panel-container my-5"> <div className="search-panel-container my-5">
<div className="search-form flex gap-5 justify-between"> <div className="search-form flex gap-5 justify-between">
<EditSearchForm onSubmit={setParams}/> <EditSearchForm onSubmit={setParams}/>
<Button type="primary" onClick={() => setEditId(0)}></Button> {/*<Button type="primary" onClick={() => setEditId(0)}>手动新增</Button>*/}
</div> </div>
<div className="news-list-container mt-5">
<div className={styles.newListTable}> <div className="news-list-container mt-2">
<div className="header flex gap-2"> <div className="controls flex justify-end mb-3 gap-2">
<div className="col flex-1"></div> <Space>
<div className="col source w-[120px] min-w-[120px]"></div> <span> {data?.list.length} </span>
<div className="col time w-[120px]"></div> <span className={'text-blue-500'}> {selectedRowKeys.length} </span>
<div className="col operations min-w-[150px]"></div> </Space>
<div>
<span className={'inline-block cursor-pointer mr-2'} onClick={() => {
handleCheckAll(!state.checkAll)
}}></span>
<Checkbox checked={state.checkAll} onChange={e => {
handleCheckAll(e.target.checked)
}}
indeterminate={selectedRowKeys.length > 0 && selectedRowKeys.length < data?.list.length}></Checkbox>
</div> </div>
<InfiniteScroller onCallback={(page)=>{ </div>
<div className={styles.newListTable}>
<div className="header row flex">
<div className="col title"></div>
<div className="col source source"></div>
<div className="col time"></div>
<div className="col operations"></div>
</div>
<InfiniteScroller ref={scrollerRef} onCallback={(page) => {
setParams(prev => ({ setParams(prev => ({
...prev, ...prev,
pagination: {page, limit: 10} pagination: {page, limit: 10}
})) }))
}} loading={loading} pagination={data?.pagination}> }} onScroll={(top)=> setState({showToTop: top > 30})} loading={loading} pagination={data?.pagination}>
<div className="body"> <div className="body">
{data?.list?.map((item, i) => { {data?.list?.map((item, i) => {
return <div key={i} className="row flex gap-3"> const checked = selectedRowKeys.includes(item.id)
<div className="col flex-1"> return <div key={i} className={clsx("row flex", {checked})}>
<div className="title text-base">{item.title}</div> <div className="col title">
<div className="summary text-xs text-gray-400 line-clamp-1">{item.summary}</div> <div>
<div className="text-base">{item.title}</div>
<div
className="summary text-xs text-gray-400 line-clamp-1">{item.summary}</div>
</div>
</div> </div>
<div className="col source w-[120px] min-w-[120px]"> <div className="col source">
<div className="text-sm text-gray-400">{item.media_name}</div> <div className="text-sm">{item.media_name}</div>
</div> </div>
<div className="col time w-[120px]"> <div className="col time">
<div <div
className="text-sm text-gray-400">{formatTime(item.publish_time, 'YYYY-MM-DD HH:mm')}</div> className="text-sm">{formatTime(item.publish_time, 'YYYY-MM-DD HH:mm')}</div>
</div> </div>
<div className="col operations w-[150px]"> <div className="col operations">
<Space> <span className="icon-btn" onClick={() => setEditId(item.id)}><IconEdit/></span>
<Button onClick={() => { <span className="icon-btn"><IconDelete/></span>
setEditId(item.id) <Checkbox checked={checked}
}}></Button> onChange={e => handleItemChecked(e.target.checked, item)}/>
<Button onClick={() => {
setEditId(item.id)
}}></Button>
<Checkbox type="link"/>
</Space>
</div> </div>
</div> </div>
})} })}
</div> </div>
</InfiniteScroller> </InfiniteScroller>
</div> </div>
{/*<Table<ListArticleItem>*/}
{/* columns={columns}*/} <div className="page-action">
{/* dataSource={data?.list || []}*/} <ButtonToTop visible={state.showToTop} onClick={()=>scrollerRef.current?.scrollToPosition(0)} />
{/* rowKey={'id'}*/} <div>
{/* bordered={false}*/} <ButtonPush2Video ids={selectedRowKeys} onSuccess={refresh}/>
{/* pagination={false}*/} </div>
{/* rowHoverable={false}*/} </div>
{/* className={styles.newListTable}*/}
{/*/>*/}
{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}
pageSize={10}
showSizeChanger={false}
simple={true}
rootClassName={'simple-pagination'}
onChange={(page) => setParams(prev => ({
...prev,
pagination: {page, limit: 10}
}))}
/>
<ButtonPush2Video ids={selectedRowKeys} onSuccess={refresh}/>
</div>}
</div> </div>
<ArticleEditModal <ArticleEditModal
type="news" id={editId} type="news" id={editId}

View File

@ -1,5 +1,5 @@
import {useState} from "react"; import React, {useState} from "react";
import {Checkbox, Divider, Empty, Modal, Pagination} from "antd"; import {Checkbox, Divider, Empty, Modal, Pagination, Space} from "antd";
import {useRequest} from "ahooks"; import {useRequest} from "ahooks";
import SearchPanel from "@/pages/news/components/search-panel.tsx"; import SearchPanel from "@/pages/news/components/search-panel.tsx";
@ -78,7 +78,10 @@ export default function NewsIndex() {
</Modal>} </Modal>}
<div className="news-list-container"> <div className="news-list-container">
<div className="controls flex justify-end mb-3 gap-2"> <div className="controls flex justify-end mb-3 gap-2">
<div className={'text-blue-500'}> {checkedId.length} </div> <Space>
<span> {data?.list.length} </span>
<span className={'text-blue-500'}> {checkedId.length} </span>
</Space>
<div> <div>
<span className={'inline-block cursor-pointer mr-2'} onClick={() => { <span className={'inline-block cursor-pointer mr-2'} onClick={() => {
handleCheckAll(!state.checkAll) handleCheckAll(!state.checkAll)

View File

@ -31,6 +31,7 @@ const AppRouter = () => {
theme={{ theme={{
token: { token: {
borderRadius: 4, borderRadius: 4,
}, },
}} }}

View File

@ -3,6 +3,7 @@ const themeConfig = {
colors:{ colors:{
'primary':'#7356f6', 'primary':'#7356f6',
'primary-blue':'rgb(64, 150, 255)', 'primary-blue':'rgb(64, 150, 255)',
'primary-blue-bg':'#d9eaff',
'primary-bg': 'rgb(244, 247, 252)', 'primary-bg': 'rgb(244, 247, 252)',
'info':'rgba(238, 245, 255, 1)', 'info':'rgba(238, 245, 255, 1)',
'news-to-edit':'#ececec', 'news-to-edit':'#ececec',