Browse Source

完整视频查询

main
dark 4 months ago
parent
commit
bc849689a5
  1. 83
      src/components/tag-value/TagValue.tsx
  2. 1
      src/components/tag-value/index.ts
  3. 4
      src/components/user-picker/List.tsx
  4. 4
      src/components/user-picker/UserPicker.tsx
  5. 368
      src/pages/videos/list/index.tsx
  6. 23
      src/pages/videos/list/style.ts
  7. 8
      src/patches/x-columns.ts
  8. 16
      src/store/videos/video.ts
  9. 19
      src/utils/index.ts

83
src/components/tag-value/TagValue.tsx

@ -0,0 +1,83 @@
import { Space, Tag, TagProps } from 'antd'
import { memo, useEffect, useState } from 'react'
type ValueType = string | number | boolean | { label: any, value: string | number | boolean }
export interface TagValueProps extends Omit<TagProps, 'onChange'> {
tags?: ValueType[];
value?: ValueType | ValueType[];
onChange?: (value: ValueType | ValueType[]) => void;
checked?: boolean;
wrap?: boolean;
//单选
single?: boolean;
}
const parserValue = (value?: ValueType | ValueType[]) => {
if (value === undefined || value === null) {
return []
}
if (Array.isArray(value)) {
return value
}
return [ value ]
}
const TagValue = (
{
value, onChange, checked = true, tags = [], wrap, single,
}: TagValueProps) => {
const Com = checked ? Tag.CheckableTag : Tag
const [ innerValue, setValue ] = useState(() => {
return parserValue(value).filter(item => {
return tags?.some(t => {
return ((t as any).value ?? t) === item
})
})
})
useEffect(() => {
const val = parserValue(value).filter(item => {
return tags?.some(t => {
return ((t as any).value ?? t) === item
})
})
setValue(val)
}, [ value ])
return (
<Space size={0} wrap={wrap}>
{tags?.map(item => {
if (item == null || item === '' || item === undefined) return null
const val = (item as any).value ?? item
const selected = innerValue.includes(val)
return (
<Com key={String((item as any).value ?? item)}
onChange={checked ? () => {
const prevValue = parserValue(value)
const index = prevValue?.indexOf(val)
let newArr: ValueType[] = []
if (single) {
newArr = index === -1 ? [ val ] : []
} else {
if (index === -1) {
newArr = [ ...prevValue, val ]
} else {
const newArray = [ ...prevValue ]
newArray.splice(index, 1)
newArr = newArray
}
}
onChange?.(newArr)
setValue(newArr)
} : undefined}
checked={selected}>
{(item as any).label ?? item}
</Com>)
})}
</Space>
)
}
export default memo(TagValue)

1
src/components/tag-value/index.ts

@ -0,0 +1 @@
export * from './TagValue.tsx'

4
src/components/user-picker/List.tsx

@ -1,7 +1,7 @@
import { useStyle } from './style.ts' import { useStyle } from './style.ts'
import { Avatar, Button, Checkbox, CheckboxProps, Radio } from 'antd' import { Avatar, Button, Checkbox, CheckboxProps, Radio } from 'antd'
import { createContext, useContext } from 'react' import { createContext, useContext } from 'react'
import { IUser } from '@/types'
import { System } from '@/types'
import { DeleteOutlined } from '@ant-design/icons' import { DeleteOutlined } from '@ant-design/icons'
export const ListItem = (props: CheckboxProps) => { export const ListItem = (props: CheckboxProps) => {
@ -24,7 +24,7 @@ export const ListItem = (props: CheckboxProps) => {
) )
} }
export const ListViewItem = ({ user, onDel }: { user: IUser, onDel: (user: IUser) => void }) => {
export const ListViewItem = ({ user, onDel }: { user: System.IUser, onDel: (user: System.IUser) => void }) => {
const { styles, cx, theme } = useStyle() const { styles, cx, theme } = useStyle()
if (!user) { if (!user) {
return null return null

4
src/components/user-picker/UserPicker.tsx

@ -8,7 +8,7 @@ import DepartmentTree from '@/components/department-tree/DepartmentTree.tsx'
import { DraggablePanel } from '@/components/draggable-panel' import { DraggablePanel } from '@/components/draggable-panel'
import { useAtom, useAtomValue } from 'jotai' import { useAtom, useAtomValue } from 'jotai'
import { userListAtom, userSearchAtom, } from '@/store/system/user.ts' import { userListAtom, userSearchAtom, } from '@/store/system/user.ts'
import { IUser } from '@/types'
import { System } from '@/types'
import EmptyWrap from '@/components/empty/EmptyWrap.tsx' import EmptyWrap from '@/components/empty/EmptyWrap.tsx'
export interface UserSelectProps extends SelectProps { export interface UserSelectProps extends SelectProps {
@ -62,7 +62,7 @@ const UserModel = memo(({ multiple, children, value, onChange, ...props }: UserM
const [ , setSearch ] = useAtom(userSearchAtom) const [ , setSearch ] = useAtom(userSearchAtom)
const { data: users, isPending } = useAtomValue(userListAtom) const { data: users, isPending } = useAtomValue(userListAtom)
const [ open, setOpen ] = useState(false) const [ open, setOpen ] = useState(false)
const selectUserRef = useRef<IUser[]>([])
const selectUserRef = useRef<System.IUser[]>([])
const [ , update ] = useState({}) const [ , update ] = useState({})
useEffect(() => { useEffect(() => {

368
src/pages/videos/list/index.tsx

@ -1,11 +1,11 @@
import { useTranslation } from '@/i18n.ts' import { useTranslation } from '@/i18n.ts'
import { getToken } from '@/store/system.ts' import { getToken } from '@/store/system.ts'
import { Button, DatePicker, Form, Image, Popconfirm } from 'antd'
import { Button, DatePicker, Form, Image, Popconfirm, Divider, Space, Tooltip, Badge } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { import {
deleteVideoAtom, getTypeName, deleteVideoAtom, getTypeName,
saveOrUpdateVideoAtom, videosAtom, videoSearchAtom, videoTypes
saveOrUpdateVideoAtom, videoAtom, videosAtom, videoSearchAtom, videoTypes
} from '@/store/videos/video.ts' } from '@/store/videos/video.ts'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import Action from '@/components/action/Action.tsx' import Action from '@/components/action/Action.tsx'
@ -19,21 +19,28 @@ import {
import ListPageLayout from '@/layout/ListPageLayout.tsx' import ListPageLayout from '@/layout/ListPageLayout.tsx'
import { categoryByIdAtom, categoryIdAtom } from '@/store/videos/category.ts' import { categoryByIdAtom, categoryIdAtom } from '@/store/videos/category.ts'
import TagPro from '@/components/tag-pro/TagPro.tsx' import TagPro from '@/components/tag-pro/TagPro.tsx'
import TagValue from '@/components/tag-value/TagValue.tsx'
import { useStyle } from './style'
import { FilterOutlined } from '@ant-design/icons'
import { getValueCount } from '@/utils'
const i18nPrefix = 'videos.list' const i18nPrefix = 'videos.list'
const Video = () => { const Video = () => {
// const { styles } = useStyle()
const { styles, cx } = useStyle()
const { t } = useTranslation() const { t } = useTranslation()
const [ form ] = Form.useForm() const [ form ] = Form.useForm()
const [ filterForm ] = Form.useForm()
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateVideoAtom) const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateVideoAtom)
const [ search, setSearch ] = useAtom(videoSearchAtom) const [ search, setSearch ] = useAtom(videoSearchAtom)
const [ currentVideo, setVideo ] = useAtom(videoAtom)
const { data, isFetching, isLoading, refetch } = useAtomValue(videosAtom) const { data, isFetching, isLoading, refetch } = useAtomValue(videosAtom)
const { mutate: deleteVideo, isPending: isDeleting } = useAtomValue(deleteVideoAtom) const { mutate: deleteVideo, isPending: isDeleting } = useAtomValue(deleteVideoAtom)
const setCategoryId = useSetAtom(categoryIdAtom) const setCategoryId = useSetAtom(categoryIdAtom)
const { data: category, isLoading: isCategoryFetching } = useAtomValue(categoryByIdAtom) const { data: category, isLoading: isCategoryFetching } = useAtomValue(categoryByIdAtom)
const [ open, setOpen ] = useState(false) const [ open, setOpen ] = useState(false)
const [ openFilter, setFilterOpen ] = useState(false)
const columns = useMemo(() => { const columns = useMemo(() => {
return [ return [
@ -84,7 +91,25 @@ const Video = () => {
style: { width: '100%' } style: { width: '100%' }
}, },
render: (_dom, record) => { render: (_dom, record) => {
return t(`${i18nPrefix}.type_id.${record.type_id}`)
return <TagValue
tags={[ { label: t(`${i18nPrefix}.type_id.${record.type_id}`), value: record.type_id } ]}
wrap={currentVideo?.id === record.id}
value={search?.type_id}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
type_id: values[0],
}
})
setCategoryId(values[0])
const typeName = getTypeName(values[0])
form.setFieldsValue({
class_name: typeName,
})
setFilterOpen(true)
}}
/>
}, },
colProps: { colProps: {
span: 8 span: 8
@ -110,16 +135,38 @@ const Video = () => {
}, },
valueType: 'dateYear', valueType: 'dateYear',
colProps: { colProps: {
span: 4
span: 6
}, },
render: (_dom, record) => { render: (_dom, record) => {
return record.year
return <TagValue
tags={[ record.year ]}
wrap={currentVideo?.id === record.id}
value={search?.year}
single={true}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
year: values[0],
}
})
setFilterOpen(true)
}}
/>
}, },
renderFormItem: (_schema, config) => { renderFormItem: (_schema, config) => {
const props = { ...config } as any
delete props.mode
const isForm = config.type === 'form'
let value = isForm && config.value ? dayjs().set('year', config.value) : undefined
if (config.value?.$isDayjsObject) {
value = config.value as dayjs.Dayjs
}
return <DatePicker return <DatePicker
{..._schema.formItemProps}
{...props}
picker={'year'} picker={'year'}
{...config}
value={dayjs().set('year', config.value || new Date().getFullYear())}
value={value}
/> />
} }
@ -131,9 +178,24 @@ const Video = () => {
fieldProps: { fieldProps: {
style: { width: '100%' } style: { width: '100%' }
}, },
colProps: { colProps: {
span: 12
span: 10
},
render: (_dom, record) => {
return <TagValue
tags={record.class_name?.split(',')}
wrap={currentVideo?.id === record.id}
value={search?.class_name}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
class_name: values,
}
})
setFilterOpen(true)
}}
/>
}, },
}, },
{ {
@ -142,6 +204,21 @@ const Video = () => {
hideInTable: true, hideInTable: true,
colProps: { colProps: {
span: 6 span: 6
}, render: (_dom, record) => {
return <TagValue
tags={[ record.douban_id ]}
wrap={currentVideo?.id === record.id}
value={search?.douban_id}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
douban_id: values[0],
}
})
setFilterOpen(true)
}}
/>
}, },
}, },
{ {
@ -150,6 +227,21 @@ const Video = () => {
hideInTable: true, hideInTable: true,
colProps: { colProps: {
span: 6 span: 6
}, render: (_dom, record) => {
return <TagValue
tags={[ record.imdb_id ]}
wrap={currentVideo?.id === record.id}
value={search?.imdb_id}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
imdb_id: values[0],
}
})
setFilterOpen(true)
}}
/>
}, },
}, },
{ {
@ -158,6 +250,21 @@ const Video = () => {
hideInTable: true, hideInTable: true,
colProps: { colProps: {
span: 6 span: 6
}, render: (_dom, record) => {
return <TagValue
tags={[ record.rt_id ]}
wrap={currentVideo?.id === record.id}
value={search?.rt_id}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
rt_id: values[0],
}
})
setFilterOpen(true)
}}
/>
}, },
}, },
{ {
@ -166,13 +273,28 @@ const Video = () => {
hideInTable: true, hideInTable: true,
colProps: { colProps: {
span: 6 span: 6
}, render: (_dom, record) => {
return <TagValue
tags={[ record.mal_id ]}
wrap={currentVideo?.id === record.id}
value={search?.mal_id}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
mal_id: values[0],
}
})
setFilterOpen(true)
}}
/>
}, },
}, },
{ {
'title': t(`${i18nPrefix}.columns.img`, '封面'), 'title': t(`${i18nPrefix}.columns.img`, '封面'),
'dataIndex': 'pic', 'dataIndex': 'pic',
hideInSearch: true, hideInSearch: true,
hideInTable: true,
width: 80,
colProps: { colProps: {
span: 24 span: 24
}, },
@ -189,10 +311,14 @@ const Video = () => {
fileList={[]} fileList={[]}
action="/api/v1/videos/image/upload" action="/api/v1/videos/image/upload"
/> />
}
},
render: (_dom, record) => {
const url = `/api/v1/videos/image/${record.video_id}`
return <Image src={`${url}?width=60&height=80`} alt="cover" preview={{
src: url,
}} style={{ width: 80 }}/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.actor`, 'Actor'), 'title': t(`${i18nPrefix}.columns.actor`, 'Actor'),
'dataIndex': 'actor', 'dataIndex': 'actor',
@ -201,7 +327,22 @@ const Video = () => {
fieldProps: { fieldProps: {
style: { width: '100%' } style: { width: '100%' }
}, },
render: (_dom, record) => {
return <TagValue
tags={record.actor?.split(',') ?? []}
wrap={currentVideo?.id === record.id}
value={search?.actor ?? []}
onChange={(values) => {
setSearch((prev: any) => {
return {
...prev,
actor: values,
}
})
setFilterOpen(true)
}}
/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.director`, 'Director'), 'title': t(`${i18nPrefix}.columns.director`, 'Director'),
@ -210,6 +351,22 @@ const Video = () => {
fieldProps: { fieldProps: {
style: { width: '100%' } style: { width: '100%' }
}, },
render: (_dom, record) => {
return <TagValue
tags={record.director?.split(',') ?? []}
wrap={!currentVideo?.id === record.id}
value={search?.director ?? []}
onChange={(values) => {
setSearch(prev => {
return {
...prev,
director: (values as Array<any>)
}
})
setFilterOpen(true)
}}
/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.writer`, 'Writer'), 'title': t(`${i18nPrefix}.columns.writer`, 'Writer'),
@ -218,6 +375,21 @@ const Video = () => {
fieldProps: { fieldProps: {
style: { width: '100%' } style: { width: '100%' }
}, },
render: (_dom, record) => {
return <TagValue
tags={record.writer?.split(',') ?? []}
wrap={!currentVideo?.id === record.id}
value={search?.writer ?? []}
onChange={(values) => {
setSearch(prev => {
return {
...prev,
writer: (values as Array<any>)
}
})
setFilterOpen(true)
}}/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.content`, 'Content'), 'title': t(`${i18nPrefix}.columns.content`, 'Content'),
@ -247,7 +419,22 @@ const Video = () => {
renderFormItem: (schema, config) => { renderFormItem: (schema, config) => {
return <TagPro loading={isCategoryFetching} return <TagPro loading={isCategoryFetching}
tags={category?.extend?.class?.split(',') ?? []} {...config} {...schema.fieldProps} /> tags={category?.extend?.class?.split(',') ?? []} {...config} {...schema.fieldProps} />
}
},
render: (_dom, record) => {
return <TagValue
tags={record.tag?.split(',') ?? []}
wrap={!currentVideo?.id === record.id}
value={search?.tag ?? []}
onChange={(values) => {
setSearch(prev => {
return {
...prev,
tag: (values as Array<any>)
}
})
setFilterOpen(true)
}}/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.area`, 'Area'), 'title': t(`${i18nPrefix}.columns.area`, 'Area'),
@ -260,7 +447,23 @@ const Video = () => {
renderFormItem: (schema, config) => { renderFormItem: (schema, config) => {
return <TagPro loading={isCategoryFetching} return <TagPro loading={isCategoryFetching}
tags={category?.extend?.area?.split(',') ?? []} {...config} {...schema.fieldProps} /> tags={category?.extend?.area?.split(',') ?? []} {...config} {...schema.fieldProps} />
}
},
render: (_dom, record) => {
return <TagValue
tags={record.area?.split(',') ?? []}
wrap={!currentVideo?.id === record.id}
value={search?.area ?? []}
onChange={(values) => {
setSearch(prev => {
return {
...prev,
area: (values as Array<any>)
}
})
setFilterOpen(true)
}}
/>
},
}, },
{ {
'title': t(`${i18nPrefix}.columns.lang`, 'Lang'), 'title': t(`${i18nPrefix}.columns.lang`, 'Lang'),
@ -273,7 +476,22 @@ const Video = () => {
renderFormItem: (schema, config) => { renderFormItem: (schema, config) => {
return <TagPro loading={isCategoryFetching} return <TagPro loading={isCategoryFetching}
tags={category?.extend?.lang?.split(',') ?? []} {...config} {...schema.fieldProps} /> tags={category?.extend?.lang?.split(',') ?? []} {...config} {...schema.fieldProps} />
}
},
render: (_dom, record) => {
return <TagValue
tags={record.lang?.split(',') ?? []}
wrap={!currentVideo?.id === record.id}
value={search?.lang ?? []}
onChange={(values) => {
setSearch(prev => {
return {
...prev,
lang: (values as Array<any>)
}
})
setFilterOpen(true)
}}/>
},
}, },
/*{ /*{
'title': t(`${i18nPrefix}.columns.version`, 'Version'), 'title': t(`${i18nPrefix}.columns.version`, 'Version'),
@ -309,8 +527,6 @@ const Video = () => {
formItemProps: { hidden: true }, formItemProps: { hidden: true },
hideInTable: true, hideInTable: true,
}, },
{ {
title: t(`${i18nPrefix}.columns.option`, '操作'), title: t(`${i18nPrefix}.columns.option`, '操作'),
key: 'option', key: 'option',
@ -338,7 +554,13 @@ const Video = () => {
] ]
} }
] as ProColumns[] ] as ProColumns[]
}, [ isDeleting, category, isCategoryFetching ])
}, [ isDeleting, category, isCategoryFetching, currentVideo, search ])
useEffect(() => {
filterForm.setFieldsValue(search)
}, [ search ])
useEffect(() => { useEffect(() => {
if (isSuccess) { if (isSuccess) {
@ -347,7 +569,7 @@ const Video = () => {
}, [ isSuccess ]) }, [ isSuccess ])
return ( return (
<ListPageLayout>
<ListPageLayout className={styles.container}>
<ProTable <ProTable
rowKey="id" rowKey="id"
headerTitle={t(`${i18nPrefix}.title`, '视频管理')} headerTitle={t(`${i18nPrefix}.title`, '视频管理')}
@ -364,7 +586,17 @@ const Video = () => {
placeholder: t(`${i18nPrefix}.placeholder`, '输入视频名称') placeholder: t(`${i18nPrefix}.placeholder`, '输入视频名称')
}, },
actions: [ actions: [
<Button
<Tooltip key={'filter'} title={t(`${i18nPrefix}.filter.tooltip`, '高级查询')}>
<Badge count={ getValueCount(search) }>
<Button
onClick={() => {
setFilterOpen(true)
}}
icon={<FilterOutlined/>} shape={'circle'} size={'small'}/>
</Badge>
</Tooltip>,
<Divider type={'vertical'} key={'divider'}/>,
<Button key={'add'}
onClick={() => { onClick={() => {
form.resetFields() form.resetFields()
form.setFieldsValue({ form.setFieldsValue({
@ -378,10 +610,21 @@ const Video = () => {
scroll={{ scroll={{
x: 2500, y: 'calc(100vh - 290px)' x: 2500, y: 'calc(100vh - 290px)'
}} }}
search={false}
onRow={(record) => {
return {
className: cx({
'ant-table-row-selected': currentVideo?.id === record.id
}),
onClick: () => {
setVideo(record)
}
}
}}
dateFormatter="string"
loading={isLoading || isFetching} loading={isLoading || isFetching}
dataSource={data?.rows ?? []} dataSource={data?.rows ?? []}
columns={columns} columns={columns}
search={false}
options={{ options={{
reload: () => { reload: () => {
refetch() refetch()
@ -437,6 +680,83 @@ const Video = () => {
}} }}
columns={columns as ProFormColumnsType[]}/> columns={columns as ProFormColumnsType[]}/>
<BetaSchemaForm
title={t(`${i18nPrefix}.filter.title`, '视频高级查询')}
grid={true}
shouldUpdate={false}
width={500}
form={filterForm}
open={openFilter}
onOpenChange={open => {
setFilterOpen(open)
}}
layout={'vertical'}
scrollToFirstError={true}
// colProps={{ span: 24 }}
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 14 }}
layoutType={'DrawerForm'}
drawerProps={{
maskClosable: false,
mask: false,
}}
submitter={{
searchConfig: {
resetText: t(`${i18nPrefix}.filter.reset`, '清空'),
submitText: t(`${i18nPrefix}.filter.submit`, '查询'),
},
onReset: () => {
filterForm.resetFields()
},
render: (props,) => {
return (
<div style={{ textAlign: 'right' }}>
<Space>
<Button onClick={() => {
props.reset()
}}>{props.searchConfig?.resetText}</Button>
<Button type="primary"
onClick={() => {
props.submit()
}}
>{props.searchConfig?.submitText}</Button>
</Space>
</div>
)
},
}}
onValuesChange={(values) => {
if (values.type_id) {
setCategoryId(values.type_id)
const typeName = getTypeName(values.type_id)
filterForm.setFieldsValue({
class_name: typeName,
})
}
}}
onFinish={async (values) => {
// console.log('values', values)
//处理,变成数组
Object.keys(values).forEach(key => {
if (typeof values[key] === 'string' && values[key].includes(',')) {
values[key] = values[key].split(',')
}
})
if (Object.keys(values).length === 0) {
setSearch({})
} else {
setSearch(prev => {
return {
...prev,
...values
}
})
}
}}
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/>
</ListPageLayout> </ListPageLayout>
) )
} }

23
src/pages/videos/list/style.ts

@ -0,0 +1,23 @@
import { createStyles } from '@/theme'
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => {
const prefix = `${prefixCls}-${token?.proPrefix}-video-list-page`
const container = css`
.ant-table-cell{
.ant-tag{
padding-inline: 3px;
margin-inline-end: 3px;
}
}
.ant-table-empty {
.ant-table-body{
height: calc(100vh - 350px)
}
}
`
return {
container: cx(prefix, props?.className, container),
}
})

8
src/patches/x-columns.ts

@ -0,0 +1,8 @@
export const useColumnsWidth = (width: number, formWith?: number | string) => ({
width,
fieldProps: {
style: {
width: `${formWith ?? '100%'}`
},
}
})

16
src/store/videos/video.ts

@ -17,7 +17,7 @@ export const getTypeName = (typeId: number) => {
return videoTypes.find(item => item.value === typeId)?.label return videoTypes.find(item => item.value === typeId)?.label
} }
type SearchParams = IPage & {
type SearchParams = IPage & Partial<Videos.IVideo> & {
key?: string key?: string
} }
@ -25,6 +25,7 @@ export const videoIdAtom = atom(0)
export const videoIdsAtom = atom<number[]>([]) export const videoIdsAtom = atom<number[]>([])
//选中的行
export const videoAtom = atom<Videos.IVideo>(undefined as unknown as Videos.IVideo) export const videoAtom = atom<Videos.IVideo>(undefined as unknown as Videos.IVideo)
export const videoSearchAtom = atom<SearchParams>({ export const videoSearchAtom = atom<SearchParams>({
@ -40,7 +41,18 @@ export const videosAtom = atomWithQuery((get) => {
return { return {
queryKey: [ 'videos', get(videoSearchAtom) ], queryKey: [ 'videos', get(videoSearchAtom) ],
queryFn: async ({ queryKey: [ , params ] }) => { queryFn: async ({ queryKey: [ , params ] }) => {
return await videoServ.video.list(params as SearchParams)
//处理数组,转成,分隔的字符串
const p = {} as SearchParams
Object.keys(params as any).forEach(key=>{
const value =(params as any)[key]
if (Array.isArray(value)) {
p[key] = value.join(',')
} else {
p[key] = value
}
})
return await videoServ.video.list(p)
}, },
select: res => { select: res => {
const data = res.data const data = res.data

19
src/utils/index.ts

@ -112,7 +112,7 @@ export const convertToBool = (value: any): boolean => {
// 处理常见 falsy 值 // 处理常见 falsy 值
if (value === undefined || value === null || if (value === undefined || value === null ||
value === false || value === 0 || value === '' || Number.isNaN(value)) {
value === false || value === 0 || value === '' || Number.isNaN(value)) {
return false return false
} }
@ -124,3 +124,20 @@ export const convertToBool = (value: any): boolean => {
// 其他情况,包括数字(非零)、字符串(已经被上述逻辑处理)和其他 truthy 值 // 其他情况,包括数字(非零)、字符串(已经被上述逻辑处理)和其他 truthy 值
return Boolean(value) return Boolean(value)
} }
//数组去重
export const unique = (arr: any[]): any[] => {
return Array.from(new Set(arr))
}
export const getValueCount = (obj: any, filterObj: any = {}) => {
// 获取对象中所有值的数量
let count = 0
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key]) {
if (!filterObj?.[key])
count++
}
}
return count
}
Loading…
Cancel
Save