Compare commits

..

No commits in common. "a5b8e9cd874c11e64e1fa7b8f82ea71bb4aecd41" and "348d23f72cc433d68ea2e9f9d5fe02bfa4aac9c7" have entirely different histories.

10 changed files with 57 additions and 131 deletions

View File

@ -1,6 +1,6 @@
<svg width="37" height="34" viewBox="0 0 37 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.9241 2.04763C29.2793 1.00838 30.7152 0.976901 31.1363 1.95321L31.1719 2.04823L31.6512 3.44974C31.761 3.77115 31.9385 4.06528 32.1716 4.31227C32.4048 4.55927 32.6882 4.75339 33.0028 4.88154L33.1316 4.92964L34.5332 5.40829C35.5724 5.76342 35.6039 7.19937 34.6282 7.62042L34.5332 7.65605L33.1316 8.1353C32.8101 8.24506 32.5159 8.42251 32.2688 8.65567C32.0217 8.88884 31.8275 9.1723 31.6993 9.48693L31.6512 9.6152L31.1725 11.0173C30.8174 12.0566 29.3814 12.088 28.961 11.1123L28.9241 11.0173L28.4455 9.61579C28.3357 9.29427 28.1583 9.00003 27.9251 8.75292C27.6919 8.50582 27.4085 8.31161 27.0939 8.1834L26.9656 8.1353L25.5641 7.65665C24.5242 7.30152 24.4928 5.86556 25.4691 5.44511L25.5641 5.40829L26.9656 4.92964C27.287 4.81981 27.5811 4.64233 27.8281 4.40916C28.0751 4.176 28.2692 3.89257 28.3974 3.57801L28.4455 3.44974L28.9241 2.04763ZM34.7992 1.07303e-07C34.9103 -1.40135e-07 35.0192 0.0311648 35.1135 0.089953C35.2077 0.148741 35.2836 0.232795 35.3325 0.332562L35.361 0.402044L35.5689 1.01135L36.1787 1.2192C36.2901 1.25702 36.3877 1.32705 36.4592 1.42041C36.5307 1.51376 36.5729 1.62624 36.5804 1.74359C36.5879 1.86094 36.5604 1.97787 36.5013 2.07957C36.4423 2.18128 36.3544 2.26317 36.2488 2.31487L36.1787 2.34337L35.5694 2.55123L35.3616 3.16112C35.3237 3.27243 35.2536 3.36998 35.1602 3.44142C35.0668 3.51286 34.9543 3.55496 34.837 3.56241C34.7197 3.56985 34.6027 3.54229 34.5011 3.48322C34.3994 3.42415 34.3176 3.33623 34.2659 3.2306L34.2374 3.16112L34.0296 2.55182L33.4197 2.34397C33.3083 2.30614 33.2107 2.23611 33.1392 2.14276C33.0677 2.0494 33.0256 1.93693 33.0181 1.81958C33.0105 1.70223 33.038 1.58529 33.0971 1.48359C33.1561 1.38189 33.244 1.3 33.3496 1.2483L33.4197 1.21979L34.029 1.01194L34.2368 0.402044C34.2769 0.284712 34.3526 0.182855 34.4535 0.110754C34.5543 0.0386538 34.6752 -7.43997e-05 34.7992 1.07303e-07Z" fill="#9C34FE"/>
<path d="M18.481 20.1202C19.2177 19.3857 19.8018 18.5128 20.2 17.5517C20.5981 16.5906 20.8023 15.5604 20.8009 14.5201C20.8009 10.1463 17.2548 6.6001 12.8809 6.6001C8.50712 6.6001 4.96094 10.1463 4.96094 14.5201C4.96094 16.7073 5.84732 18.6873 7.28084 20.1202" stroke="black" stroke-width="2" stroke-linecap="round"/>
<path d="M1 32.9999L1.66 29.6999L8.92 24.4199L12.88 28.3799L16.84 24.4199L24.1 29.6999L24.76 32.9999" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.61523 17.8246C5.62403 15.112 6.06557 13.1305 6.93985 11.88C8.25193 10.0049 9.17527 10.1429 9.84715 10.4346C10.519 10.7263 10.915 12.635 12.0377 13.1855C13.1597 13.7353 16.0333 13.8032 17.0174 14.4652C18.0008 15.1265 20.2514 16.3574 19.6904 18.4589" stroke="black" stroke-width="2"/>
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<path d="M671.584 201.142c22.034 0 39.896 17.398 39.896 38.858v163.2l207.98-141.828a20.4 20.4 0 0 1 20.666-1.322c6.602 3.35 10.732 10.002 10.732 17.252V746.7c0 7.25-4.13 13.902-10.732 17.252a20.4 20.4 0 0 1-20.668-1.32L711.48 620.8V784c0 21.46-17.86 38.858-39.896 38.858H113.04c-22.034 0-39.896-17.398-39.896-38.858V240c0-21.46 17.862-38.858 39.896-38.858h558.546z"
fill="#9C34FE"></path>
<path d="M328.478 388.784c-7.584 0-14.122 5.196-15.64 12.434l-0.32 3.07v215.346c0 5.11 2.58 9.894 6.896 12.796a16.32 16.32 0 0 0 14.73 1.736l2.912-1.398 173.748-107.712c4.106-2.56 6.786-6.806 7.276-11.532 0.49-4.724-1.264-9.408-4.764-12.714l-2.512-1.944-173.748-107.712a16.28 16.28 0 0 0-8.578-2.332v-0.038z"
fill="#FFFFFF"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 859 B

View File

@ -40,7 +40,7 @@ body {
}
.app-header {
@apply w-full navigation-container flex justify-between items-center p-basic fixed top-0 inset-x-0 bg-white/90;
@apply w-full navigation-container flex justify-between items-center p-basic fixed top-0 inset-x-0;
z-index: var(--header-z-index);
height: var(--app-header-header);
}
@ -66,16 +66,6 @@ body {
.article-action-icon {
@apply cursor-pointer text-gray-500 hover:text-blue-500 block;
}
.news-detail-modal{
.ant-modal-content{
padding-right: 0;
padding-left: 0;
}
}
.news-detail-content-container{
margin-right: -30px;
padding-right: 30px;
}
img {
max-width: 100%;

View File

@ -1,13 +1,13 @@
import {Input} from "antd";
import {useBoolean, useLocalStorageState, useSetState,useClickAway} from "ahooks";
import {useCallback, useMemo, useRef, useState} from "react";
import {clsx} from "clsx";
import {useBoolean, useLocalStorageState, useSetState} from "ahooks";
import {useMemo, useState} from "react";
import useArticleTags from "@/hooks/useArticleTags.ts";
import {UpOutlined, MenuOutlined, SearchOutlined} from "@ant-design/icons";
import {CloseOutlined, MenuOutlined, SearchOutlined} from "@ant-design/icons";
import TimeSelect from "@/components/form/time-select.tsx";
import styles from './style.module.scss'
import {clsx} from "clsx";
import {IconPin} from "@/components/icons";
type SearchPanelProps = {
@ -23,10 +23,9 @@ const DEFAULT_STATE = {
}
export default function SearchPanel({onSearch}: SearchPanelProps) {
const tags = useArticleTags();
const [panelVisible, {set}] = useBoolean(false)
const [panelVisible, {setTrue, setFalse}] = useBoolean(false)
const [params, setParams] = useSetState<ApiArticleSearchParams>({
pagination,
time_flag:1
});
const [prevSearchName, setPrevSearchName] = useState<string>()
@ -89,29 +88,6 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
}
return [] as OptionItem[];
}, [pinnedTag, tags])
const pinnedManagePanel = useRef<HTMLDivElement|null>(null)
const togglePinnedManagePanel = useCallback((visible: boolean) => {
if(!pinnedManagePanel.current){
return;
}
const _target = pinnedManagePanel.current!;
if(visible){
_target.style.height = 'auto'
const {height} = _target.getBoundingClientRect()
_target.style.height = '38px'
requestAnimationFrame(()=>{
_target.style.height = `${height}px`
})
}else{
requestAnimationFrame(()=>{
_target.style.height = '0'
})
}
},[pinnedManagePanel])
const setTrue = ()=> togglePinnedManagePanel(true)
const setFalse = ()=>togglePinnedManagePanel(false)
useClickAway(() => setFalse(), pinnedManagePanel)
return (<div className={`${styles.searchPanel} pt-8 pb-2`}>
<div className="flex justify-between items-center">
@ -127,22 +103,23 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
/>
<TimeSelect
className="w-[120px] ml-1"
value={params.time_flag || 1}
value={params.time_flag || 0}
onChange={handleTimeFilter}
/>
</div>
</div>
<div className="filter-container mt-5">
<div className="filter-container mt-5 h-[40px]">
<div className="list-container relative">
<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={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_1_id == -1 ? 'selected' : ''}`}
onClick={() => {
handleFilter({tag_level_1_id: -1, tag_level_2_id: -1})
setSubOptions([])
}}>
</div>
<div className="justify-between flex items-center border-b pb-2 overflow-hidden">
<div
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_1_id == -1 ? 'selected' : ''}`}
onClick={() => {
handleFilter({tag_level_1_id: -1, tag_level_2_id: -1})
setSubOptions([])
}}>
</div>
<div className="pinned-tag-list flex flex-wrap overflow-hidden flex-1 min-w-0 h-[32px]">
{pinnedList.filter(s => (Number(s.value) !== 999999)).map(it => (
<span
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_1_id == it.value ? 'selected' : ''}`}
@ -153,25 +130,18 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
}}>{it.label}</span>)
)}
</div>
<div className="pinned-menu mt-2">
<span className={'cursor-pointer block hover:text-blue-500'} onClick={e=>{
e.stopPropagation();
e.preventDefault();
setTrue();
}}>
<div className="pinned-menu ">
<span className={'cursor-pointer block hover:text-blue-500'} onClick={setTrue}>
<MenuOutlined style={{fontSize: 20}}/>
</span>
</div>
</div>
<div ref={pinnedManagePanel} className={clsx(styles.pinnedManagePanelContainer)}>
{/* 固定新闻来源 */}
<div className={clsx(styles.pinnedManagePanel)}>
<div className={clsx(styles.pinnedManagePanel, panelVisible ? 'block' : 'hidden')}>
<div className="header flex justify-between">
<div className="title font-bold"></div>
<div className={'cursor-pointer block hover:text-blue-500'} onClick={setFalse}>
<UpOutlined style={{fontSize: 20}}/>
</div>
<span className={'cursor-pointer block hover:text-blue-500'} onClick={setFalse}><CloseOutlined
style={{fontSize: 20}}/></span>
</div>
<div className="tags-list-container">
{
@ -195,7 +165,6 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
}
</div>
</div>
</div>
{/* 二级目录 */}
{state.tag_level_1_id != -1 && subOptions.length > 0 &&
<div

View File

@ -14,15 +14,10 @@
}
}
}
.pinnedManagePanelContainer {
@apply absolute bg-white top-0 rounded shadow-md z-10;
height: 0;
overflow: hidden;
transition: height 0.2s ease-in-out;
inset-inline: -20px;
}
.pinnedManagePanel {
@apply px-4 pt-2 pb-4 grid;
@apply absolute bg-white top-0 px-4 pt-2 pb-4 rounded shadow-md z-10;
inset-inline: -20px;
:global {
.btn-panel {

View File

@ -1,7 +1,6 @@
import React, {useRef, useState} from "react";
import {Checkbox, Divider, Empty, Modal, Space} from "antd";
import {Checkbox, Divider, Empty, Modal, Pagination, Space} from "antd";
import {useRequest} from "ahooks";
import {CloseOutlined} from "@ant-design/icons"
import SearchPanel from "@/pages/news/components/search-panel.tsx";
import {getById, getList} from "@/service/api/news.ts";
@ -16,7 +15,7 @@ import ButtonToTop from "@/components/scoller/button-to-top.tsx";
export default function NewsIndex() {
const [params, setParams] = useState<ApiArticleSearchParams>({
pagination: {page: 1, limit: 12},time_flag:1
pagination: {page: 1, limit: 12}
})
const [checkedId, setCheckedId] = useState<Id[]>([])
const [activeNews, setActiveNews] = useState<NewsInfo>()
@ -47,7 +46,7 @@ export default function NewsIndex() {
const {update, close} = showLoading('获取新闻详情...')
getById(id).then(res => {
close()
setActiveNews({...res, id})
setActiveNews(res)
}).catch(() => {
update('获取新闻详情失败', 'info')
})
@ -61,46 +60,23 @@ export default function NewsIndex() {
setCheckedId([])
}
}
const scrollerRef = useRef<InfiniteScrollerRef | null>(null)
const handleCheckChange = (id: number) => {
if (checkedId.includes(id)) {
setCheckedId(checkedId.filter(id => id != id))
} else {
setCheckedId([...checkedId, id])
}
}
const scrollerRef = useRef<InfiniteScrollerRef|null>(null)
return (<div className={'container pb-5'}>
<SearchPanel onSearch={setParams}/>
{activeNews && <Modal
rootClassName={'news-detail-modal'}
closeIcon={null} open={true} width={1000}
maskClosable={false}
footer={null} onCancel={() => setActiveNews(undefined)}
>
<div className="news-detail pl-16 pr-1 flex pb-5">
{activeNews && <Modal open={true} centered width={1000} footer={null} onCancel={() => setActiveNews(undefined)}>
<div className="news-detail px-3 pb-5">
<div className="px-4 py-6 bg-white">
<div className="new-title text-2xl">{activeNews?.title}</div>
<div className="info mt-2 mb-2 text-sm flex gap-3">
<Divider className={'my-4'}/>
<div className="info mt-2 mb-5 text-sm flex gap-3">
<span className="source text-blue-400">{activeNews?.media_name}</span>
<span className="create-time text-gray-400">{formatTime(activeNews?.publish_time)}</span>
</div>
<Divider className={'my-2'}/>
<div className="overflow-auto leading-7 text-base news-detail-content-container"
<div className="overflow-auto leading-7 text-base"
style={{maxHeight: 500}} dangerouslySetInnerHTML={{__html: activeNews?.content || ''}}></div>
</div>
<div className="actions ml-2">
<div className="close">
<CloseOutlined className="text-xl text-gray-400 hover:text-gray-800"
onClick={() => setActiveNews(undefined)}/>
</div>
<div className="whitespace-nowrap text-sm mt-2">
<Checkbox
checked={checkedId.includes(activeNews!.id)}
onChange={() => handleCheckChange(activeNews!.id)}
><span></span></Checkbox>
</div>
</div>
</div>
</Modal>}
<div className="news-list-container">
@ -123,7 +99,7 @@ export default function NewsIndex() {
pagination={data?.pagination}
loading={loading}
ref={scrollerRef}
onScroll={(top) => setState({showToTop: top > 30})}
onScroll={(top)=> setState({showToTop: top > 30})}
onCallback={(page) => {
setParams({...params, pagination: {...params.pagination, page}})
}}
@ -154,15 +130,19 @@ export default function NewsIndex() {
</div>}
</div>
<div className="info text-gray-400 mt-4 text-sm">
<div className="line-clamp-1">: <span>{item.data_source_name}</span></div>
<div>: <span>{item.media_name}</span></div>
<div className="extras flex items-center justify-between gap-3">
<div><span>{formatTime(item.publish_time,'min')}</span></div>
<div><span>{formatTime(item.publish_time)}</span></div>
<div
className={`checkbox mt-1`}>
className={`checkbox mt-[2px]`}>
{item.internal_article_id > 0 ?
<span className={"inline-block text-gray-600"}></span> :
<span className={"inline-block w-[16px] "}></span> :
<Checkbox checked={checkedId.includes(item.id)} onChange={() => {
handleCheckChange(item.id)
if (checkedId.includes(item.id)) {
setCheckedId(checkedId.filter(id => id != item.id))
} else {
setCheckedId([...checkedId, item.id])
}
}}/>}
</div>
</div>
@ -175,7 +155,7 @@ export default function NewsIndex() {
</div>
<div className="page-action">
<ButtonToTop visible={state.showToTop} onClick={() => scrollerRef.current?.scrollToPosition(0)}/>
<ButtonToTop visible={state.showToTop} onClick={()=>scrollerRef.current?.scrollToPosition(0)} />
<div>
<ButtonNewsDownload ids={checkedId}/>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,14 +1,12 @@
.main {
@apply py-10;
background: url(./components/main-bg.jpg) no-repeat center;
background-size: cover;
background-color: rgb(244, 247, 252);
height: calc(100vh - var(--app-header-header));
min-height: 500px;
align-items: center;
display: flex;
overflow: hidden;
justify-content: right;
}
.boxLogin {

1
src/types/core.d.ts vendored
View File

@ -35,7 +35,6 @@ declare interface ArticleDetail {
}
declare interface NewsInfo {
id: number;
title: string;
content: string;
media_name: string;

View File

@ -55,13 +55,8 @@ function getDayjs(time:any){
return dayjs(time);
}
export function formatTime(time: any, template: 'min' | 'date' | string = 'YYYY-MM-DD HH:mm:ss') {
if (!time) return '-';
if (template == 'min') {
template = 'YYYY-MM-DD HH:mm'
} else if (template == 'date') {
template = 'YYYY-MM-DD'
}
export function formatTime(time:any,template = 'YYYY-MM-DD HH:mm:ss') {
if(!time) return '-';
return getDayjs(time).format(template)
}

View File

@ -6,7 +6,7 @@ import dayjs from "dayjs";
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
const devServerHost = mode == 'test' ? 'https://fm-admin.starbitech.com' : 'http://192.168.0.231:9999'
const devServerHost = mode == 'test' ? '124.220.14.192' : '192.168.0.231:9999'
const AUTH_TOKEN_KEY = mode == 'production' ? 'digital-person-token' : `digital-person-token_${mode}`
if (mode !== 'production') {
@ -41,7 +41,7 @@ export default defineConfig(({mode}) => {
port: 10021,
proxy: {
'/mgmt': {
target: `${devServerHost}`, // http://124.220.14.192/ 192.168.0.231:9999
target: `http://${devServerHost}`, // http://124.220.14.192/ 192.168.0.231:9999
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '')
},