diff --git a/src/assets/core.scss b/src/assets/core.scss index 31359e4..b1acba5 100644 --- a/src/assets/core.scss +++ b/src/assets/core.scss @@ -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; + } + } + } } \ No newline at end of file diff --git a/src/components/form/tag-select.tsx b/src/components/form/tag-select.tsx new file mode 100644 index 0000000..bf2dc76 --- /dev/null +++ b/src/components/form/tag-select.tsx @@ -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([]) + const [checkedAll, _setCheckedAll] = React.useState(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 (
+
set(!visible)} + > + {checkedAll ? '全部来源' : '来源'} + +
+
+
    +
  • +
    + handleAllChanged(!checkedAll)}>全部来源 + handleAllChanged(e.target.checked)}/> +
    +
  • + {props.options.filter(s => s.value != 999999).map((option) => { + const checkStatus = level1Checked(option) + return (
  • + {(option.children && option.children.length > 0) ? + + {option.children.map((subOption) => { + const myCheckStatus = level2Checked(subOption, option) + return (
  • +
    + { + handleLevel2Change(!myCheckStatus, subOption, option) + }}>{subOption.label} + handleLevel2Change(e.target.checked, subOption, option)}/> +
    +
  • ) + })} +
}> +
+ {option.label} + { + handleLevel1Change(e.target.checked, option) + }}/> +
+ :
+ {option.label} +
+ } + + ) + })} + +
+
+ ) +} +export default TagSelect \ No newline at end of file diff --git a/src/pages/news/components/article-cascader.tsx b/src/pages/news/components/article-cascader.tsx index 0cffee6..18c4ba5 100644 --- a/src/pages/news/components/article-cascader.tsx +++ b/src/pages/news/components/article-cascader.tsx @@ -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)) } diff --git a/src/pages/news/components/edit-search-form.tsx b/src/pages/news/components/edit-search-form.tsx index d2e3921..094b77d 100644 --- a/src/pages/news/components/edit-search-form.tsx +++ b/src/pages/news/components/edit-search-form.tsx @@ -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 (
} - placeholder="请输入你先搜索的关键词" + type="text" className="rounded-3xl px-3 w-[270px]" + prefix={} + placeholder="请输入新闻标题关键词进行搜索" + onPressEnter={handleSubmit} /> 来源 - +
) } \ No newline at end of file