💄 麻烦的新闻来源过滤

This commit is contained in:
LittleBoy 2024-12-22 03:20:10 +08:00
parent b1efffc9fe
commit 9e274a8e46
4 changed files with 206 additions and 37 deletions

View File

@ -244,14 +244,59 @@
.timer-select-options {
@apply rounded-xl py-1 overflow-hidden drop-shadow absolute inset-x-0 top-[30px];
background: linear-gradient(180deg,rgb(244, 247, 252) 0%, rgb(217, 232, 255) 100%);
background: linear-gradient(180deg, rgb(244, 247, 252) 0%, rgb(217, 232, 255) 100%);
}
.timer-select-option-item {
@apply py-1.5 px-4 cursor-pointer text-gray-800 hover:text-blue-500;
&.selected{
&.selected {
@apply text-blue-500;
}
}
}
.tag-select-container {
.select-value {
@apply text-blue-500 px-4 cursor-pointer h-[31px];
}
.options-list-container {
@apply overflow-auto py-1 drop-shadow absolute top-[30px];
border-radius: 0 0 0.75rem 0.75rem;
background: linear-gradient(180deg, rgb(244, 247, 252) 0%, rgb(217, 232, 255) 100%);
max-height: calc(100vh - var(--app-header-header) - 300px);
}
.options-list {
overflow-y: auto;
overflow-x: hidden;
}
.select-option-item {
@apply py-1.5 px-4 cursor-pointer text-gray-800 hover:text-blue-500 hover:bg-[#d9eaff];
&.selected {
@apply text-blue-500;
}
}
}
.tag-select-child-container {
.ant-popover-inner {
@apply p-0 overflow-hidden drop-shadow shadow-none; //
background: linear-gradient(180deg, rgb(235, 242, 253) 0%, rgb(244, 247, 252) 100%);
border-radius: 0 0.75rem 0.75rem 0;
transform: translate(16px, -7px);
//backdrop-filter: 0 3px 1px 5px rgb(0, 0, 0,0.1);
}
.sub-options-list {
@apply py-1.5 top-0;
max-height: 150px;
.select-option-item {
@apply py-1.5 px-4 cursor-pointer text-gray-800 hover:text-blue-500 hover:bg-[#d9eaff];
&.selected {
@apply text-blue-500;
}
}
}
}

View File

@ -0,0 +1,151 @@
import React, {useMemo, useState} from "react";
import {Checkbox, Popover} from "antd";
import {useBoolean} from "ahooks";
import {CaretUpOutlined} from "@ant-design/icons";
const TagSelect = (props: {
options: OptionItem[];
onChange: (values: Id[][]) => void;
className?: string;
}) => {
const [selectValues, _setSelectValues] = React.useState<Id[][]>([])
const [checkedAll, _setCheckedAll] = React.useState<boolean>(false)
const [visible, {set}] = useBoolean(false);
const allValues = useMemo(()=>{
const values:Id[][] = []
props.options.forEach(item=>{
if(item && item.children?.length > 0){
// eslint-disable-next-line no-unsafe-optional-chaining
values.push(...(item.children?.map(c => [item.value, c.value])))
}
})
return values
},[props.options])
const handleAllChanged = (checked: boolean) => {
_setCheckedAll(checked)
const values:Id[][] = []
if (checked){
// set(false)
values.push(...allValues)
}
_setSelectValues(values)
}
const handleLevel1Change = (checked: boolean, item: OptionItem) => {
console.log('handleLevel1Change', checked, item)
if (checked) {
const values = selectValues.filter(s => s[0] != item.value)
const checkedIds: Id[][] = [];
item.children?.forEach(c => {
checkedIds.push([item.value, c.value])
})
const _values = [...values, ...checkedIds];
_setCheckedAll(_values.length == allValues.length)
_setSelectValues(_values)
} else {
_setCheckedAll(false)
const values = selectValues.filter(s => s[0] != item.value)
_setSelectValues([...values])
}
}
const level1Checked = (item: OptionItem) => {
// 完全没有选中
if (selectValues.findIndex(s => s[0] == item.value) == -1) return -1
const myList = selectValues.filter(s => s[0] == item.value)
// 只有1个但是全选或者选中元素和子元素个数相等
if (myList.length == item.children?.length) return 1;
return 0;
}
const handleLevel2Change = (checked: boolean, item: OptionItem, parent: OptionItem) => {
// 获取一级选项的选中状态
const parentList = selectValues.filter(s => s[0] == parent.value)
_setSelectValues((prev) => {
let values = [...prev]
if (checked) {
values = [...prev,[parent.value, item.value]]
} else {
_setCheckedAll(false)
if(parentList.length == 0){ // && parentList[0].length == 1
return prev;
}
values = values.filter(s => s.length == 1 || s[1] != item.value)
}
_setCheckedAll(values.length == allValues.length)
return values
})
}
const level2Checked = (item: OptionItem, parent: OptionItem) => {
// 获取一级选项的选中状态
const parentList = selectValues.filter(s => s[0] == parent.value)
// 没有找到父级
if (parentList.length == 0) return false;
if (parentList.length == 1) {
// 只有一个 且长度为0 说明是全选
if (parentList[0].length == 1) return true;
return parentList[0][1] == item.value;
}
return parentList.findIndex(s => s[1] == item.value) != -1;
}
return (<div className={`tag-select-container z-10 select-none relative group ${props.className}`}>
<div className="select-value w-[120px] flex justify-center items-center"
onMouseEnter={() => set(!visible)}
>
<span>{checkedAll ? '全部来源' : '来源'}</span>
<CaretUpOutlined className={`ml-2 arrow-icon ${visible ? 'rotate-0' : 'rotate-180'}`}/>
</div>
<div className={`options-list-container absolute ${visible ? 'block' : 'hidden'}`}>
<ul className="options-list">
<li className="select-option-item relative">
<div className="option-value whitespace-nowrap flex justify-between">
<span className="text-center flex-1"
onClick={() => handleAllChanged(!checkedAll)}></span>
<Checkbox className="ml-6" checked={checkedAll}
onChange={e => handleAllChanged(e.target.checked)}/>
</div>
</li>
{props.options.filter(s => s.value != 999999).map((option) => {
const checkStatus = level1Checked(option)
return (<li key={option.value} className="select-option-item relative">
{(option.children && option.children.length > 0) ?
<Popover placement="rightTop" trigger={['hover']}
rootClassName="tag-select-child-container" arrow={false}
content={option.children && <ul className="sub-options-list">
{option.children.map((subOption) => {
const myCheckStatus = level2Checked(subOption, option)
return (<li key={subOption.value}
className="sub-option-item select-option-item whitespace-nowrap">
<div
className="option-value whitespace-nowrap flex justify-between">
<span
onClick={() => {
handleLevel2Change(!myCheckStatus, subOption, option)
}}>{subOption.label}</span>
<Checkbox className="ml-6" checked={myCheckStatus}
onChange={e => handleLevel2Change(e.target.checked, subOption, option)}/>
</div>
</li>)
})}
</ul>}>
<div className="option-value whitespace-nowrap flex justify-between">
<span className="text-center flex-1">{option.label}</span>
<Checkbox className="ml-6"
checked={checkStatus == 1}
indeterminate={checkStatus == 0}
onChange={e => {
handleLevel1Change(e.target.checked, option)
}}/>
</div>
</Popover> : <div className="option-value whitespace-nowrap flex justify-between">
<span>{option.label}</span>
</div>
}
</li>)
})}
</ul>
</div>
</div>
)
}
export default TagSelect

View File

@ -1,5 +1,5 @@
import {Cascader} from "antd";
import React, {useEffect, useMemo} from "react";
import React, {useEffect} from "react";
const prevSelectValues: Id[][] = [];
@ -71,9 +71,6 @@ export default function ArticleCascader(props: {
// 清除上一次的选中值
prevSelectValues.length = 0;
}, [])
// const allOptionValue = useMemo(() => {
// return getAllValue(props.options)
// }, [props.options])
const setSelectValues = (value: Id[][]) => {
_setSelectValues(value)
@ -81,33 +78,6 @@ export default function ArticleCascader(props: {
props.onChange?.(value)
}
const handleChange = (values: Id[][]) => {
// const fullValues = buildValues(props.options, values)
// const diffValue = getValuesDiff(fullValues, prevSelectValues);
// const isIncrease = fullValues.length > prevSelectValues.length;
// prevSelectValues.length = 0;
//
// if(values.length == 0){
// setSelectValues([])
// return;
// }
// // 判断操作的是否是全部
// if(diffValue?.length == 1 && diffValue[0] == -1){
// if(isIncrease) prevSelectValues.push(...allOptionValue);
// setSelectValues(isIncrease ? [...allOptionValue] : [])
// return;
// }
// // if(fullValues.length != allOptionValue.length){
// // setSelectValues(fullValues.filter(s=>s.length == 1 && s[0] != -1))
// // }else{
// //
// // }
//
// if(fullValues.filter(s=>s.length > 1 || s[0] != -1).length == allOptionValue.length - 1){
// prevSelectValues.push(...allOptionValue);
// setSelectValues( [...allOptionValue])
// return;
// }
// prevSelectValues.push(...fullValues);
setSelectValues(values.filter(s=>s.length > 1 || s[0] != -1))
}

View File

@ -4,6 +4,7 @@ import ArticleCascader from "@/pages/news/components/article-cascader.tsx";
import React, {useState} from "react";
import {useSetState} from "ahooks";
import useArticleTags from "@/hooks/useArticleTags.ts";
import TagSelect from "@/components/form/tag-select.tsx";
export default function EditSearchForm(props: {
onSubmit: (values: ApiArticleSearchParams) => void;
@ -35,6 +36,7 @@ export default function EditSearchForm(props: {
}
})
}
return (
<div className="search-form-input flex gap-2 items-center">
<Input
@ -42,16 +44,17 @@ export default function EditSearchForm(props: {
setParams({title: e.target.value})
}}
allowClear
type="text" className="rounded px-3 w-[250px]"
suffix={<SearchOutlined/>}
placeholder="请输入你先搜索的关键词"
type="text" className="rounded-3xl px-3 w-[270px]"
prefix={<SearchOutlined/>}
placeholder="请输入新闻标题关键词进行搜索"
onPressEnter={handleSubmit}
/>
<span className="ml-5 text-sm"></span>
<ArticleCascader
options={articleTags}
onChange={setTags}
/>
<Button type="primary" onClick={handleSubmit}></Button>
<TagSelect onChange={setTags} options={articleTags}/>
</div>
)
}