chenyang 5 months ago
parent
commit
eca8c7eb77
  1. 318
      src/pages/websites/ssl/record/index.tsx
  2. 26
      src/pages/websites/ssl/record/style.ts
  3. 3
      src/service/websites.ts
  4. 7
      src/store/websites/domain.ts
  5. 90
      src/store/websites/record.ts
  6. 4
      src/types/website/domain.d.ts
  7. 15
      src/types/website/record.d.ts

318
src/pages/websites/ssl/record/index.tsx

@ -0,0 +1,318 @@
import { useTranslation } from '@/i18n.ts'
import { Button, Form, Popconfirm, Divider, Space, Tooltip, Badge } from 'antd'
import { useAtom, useAtomValue } from 'jotai'
import {
deleteWebsiteDnsRecordsAtom,
saveOrUpdateWebsiteDnsRecordsAtom, websiteDnsRecordsAtom, websiteDnsRecordssAtom, websiteDnsRecordsSearchAtom,
} from '@/store/websites/record'
import { useEffect, useMemo, useState } from 'react'
import Action from '@/components/action/Action.tsx'
import {
BetaSchemaForm,
ProColumns,
ProFormColumnsType,
} from '@ant-design/pro-components'
import ListPageLayout from '@/layout/ListPageLayout.tsx'
import { useStyle } from './style'
import { FilterOutlined } from '@ant-design/icons'
import { getValueCount } from '@/utils'
import { Table as ProTable } from '@/components/table'
const i18nPrefix = 'websiteDnsRecordss.list'
const WebsiteDnsRecords = () => {
const { styles, cx } = useStyle()
const { t } = useTranslation()
const [ form ] = Form.useForm()
const [ filterForm ] = Form.useForm()
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateWebsiteDnsRecordsAtom)
const [ search, setSearch ] = useAtom(websiteDnsRecordsSearchAtom)
const [ currentWebsiteDnsRecords, setWebsiteDnsRecords ] = useAtom(websiteDnsRecordsAtom)
const { data, isFetching, isLoading, refetch } = useAtomValue(websiteDnsRecordssAtom)
const { mutate: deleteWebsiteDnsRecords, isPending: isDeleting } = useAtomValue(deleteWebsiteDnsRecordsAtom)
const [ open, setOpen ] = useState(false)
const [ openFilter, setFilterOpen ] = useState(false)
const [ searchKey, setSearchKey ] = useState(search?.title)
const columns = useMemo(() => {
return [
{
title: 'ID',
dataIndex: 'id',
hideInTable: true,
hideInSearch: true,
formItemProps: { hidden: true }
},
{
title: t(`${i18nPrefix}.columns.record_id`, 'record_id'),
dataIndex: 'record_id',
},
{
title: t(`${i18nPrefix}.columns.domain_id`, 'domain_id'),
dataIndex: 'domain_id',
},
{
title: t(`${i18nPrefix}.columns.name`, 'name'),
dataIndex: 'name',
},
{
title: t(`${i18nPrefix}.columns.content`, 'content'),
dataIndex: 'content',
},
{
title: t(`${i18nPrefix}.columns.poxy`, 'poxy'),
dataIndex: 'poxy',
},
{
title: t(`${i18nPrefix}.columns.ttl`, 'ttl'),
dataIndex: 'ttl',
},
{
title: t(`${i18nPrefix}.columns.type`, 'type'),
dataIndex: 'type',
},
{
title: t(`${i18nPrefix}.columns.status`, 'status'),
dataIndex: 'status',
},
{
title: t(`${i18nPrefix}.columns.tag`, 'tag'),
dataIndex: 'tag',
},
{
title: t(`${i18nPrefix}.columns.remark`, 'remark'),
dataIndex: 'remark',
},
{
title: t(`${i18nPrefix}.columns.created`, 'created'),
dataIndex: 'created',
},
{
title: t(`${i18nPrefix}.columns.modified`, 'modified'),
dataIndex: 'modified',
},
{
title: t(`${i18nPrefix}.columns.option`, '操作'),
key: 'option',
valueType: 'option',
fixed: 'right',
render: (_, record) => [
<Action key="edit"
as={'a'}
onClick={() => {
form.setFieldsValue(record)
setOpen(true)
}}>{t('actions.edit')}</Action>,
<Popconfirm
key={'del_confirm'}
disabled={isDeleting}
onConfirm={() => {
deleteWebsiteDnsRecords([ record.id ])
}}
title={t('message.deleteConfirm')}>
<a key="del">
{t('actions.delete', '删除')}
</a>
</Popconfirm>
]
}
] as ProColumns[]
}, [ isDeleting, currentWebsiteDnsRecords, search ])
useEffect(() => {
setSearchKey(search?.title)
filterForm.setFieldsValue(search)
}, [ search ])
useEffect(() => {
if (isSuccess) {
setOpen(false)
}
}, [ isSuccess ])
return (
<ListPageLayout className={styles.container}>
<ProTable
rowKey="id"
headerTitle={t(`${i18nPrefix}.title`, '记录管理管理')}
toolbar={{
search: {
loading: isFetching && !!search?.title,
onSearch: (value: string) => {
setSearch(prev => ({
...prev,
title: value
}))
},
allowClear: true,
onChange: (e) => {
setSearchKey(e.target?.value)
},
value: searchKey,
placeholder: t(`${i18nPrefix}.placeholder`, '输入记录管理名称')
},
actions: [
<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={() => {
form.resetFields()
form.setFieldsValue({
id: 0,
})
setOpen(true)
}}
type={'primary'}>{t(`${i18nPrefix}.add`, '添加')}</Button>
]
}}
scroll={{
x: 2500, y: 'calc(100vh - 290px)'
}}
search={false}
onRow={(record) => {
return {
className: cx({
'ant-table-row-selected': currentWebsiteDnsRecords?.id === record.id
}),
onClick: () => {
setWebsiteDnsRecords(record)
}
}
}}
dateFormatter="string"
loading={isLoading || isFetching}
dataSource={data?.rows ?? []}
columns={columns}
options={{
reload: () => {
refetch()
},
}}
pagination={{
total: data?.total,
pageSize: search.pageSize,
current: search.page,
onShowSizeChange: (current: number, size: number) => {
setSearch({
...search,
pageSize: size,
page: current
})
},
onChange: (current, pageSize) => {
setSearch(prev => {
return {
...prev,
page: current,
pageSize: pageSize,
}
})
},
}}
/>
<BetaSchemaForm
grid={true}
shouldUpdate={false}
width={1000}
form={form}
layout={'vertical'}
scrollToFirstError={true}
title={t(`${i18nPrefix}.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '记录管理编辑' : '记录管理添加')}
layoutType={'DrawerForm'}
open={open}
drawerProps={{
maskClosable: false,
}}
onOpenChange={(open) => {
setOpen(open)
}}
loading={isSubmitting}
onFinish={async (values) => {
saveOrUpdate(values)
}}
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}
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>
)
},
}}
onFinish={async (values) => {
//处理,变成数组
Object.keys(values).forEach(key => {
if (typeof values[key] === 'string' && values[key].includes(',')) {
values[key] = values[key].split(',')
}
})
setSearch(values)
}}
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/>
</ListPageLayout>
)
}
export default WebsiteDnsRecords

26
src/pages/websites/ssl/record/style.ts

@ -0,0 +1,26 @@
import { createStyles } from '@/theme'
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => {
const prefix = `${prefixCls}-${token?.proPrefix}-websiteDnsRecords-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)
}
}
.ant-pro-table-highlight{
}
`
return {
container: cx(prefix, props?.className, container),
}
})

3
src/service/websites.ts

@ -30,6 +30,9 @@ const websitesServ = {
},
domain: {
...createCURD<any, Websites.IWebsiteDomain>('/website/domain'),
},
record: {
...createCURD<any, Websites.IWebsiteDomain>('/website/record'),
}
}

7
src/store/websites/domain.ts

@ -4,8 +4,7 @@ import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack
import { message } from 'antd'
import { t } from 'i18next'
import websitesServ from '@/service/websites.ts'
import { Websites } from '@/types/website/domain'
import { IWebsiteDomain } from '@/types/website/domain'
type SearchParams = IPage & {
key?: string
@ -17,7 +16,7 @@ export const websiteDomainIdAtom = atom(0)
export const websiteDomainIdsAtom = atom<number[]>([])
export const websiteDomainAtom = atom<Websites.IWebsiteDomain>(undefined as unknown as Websites.IWebsiteDomain )
export const websiteDomainAtom = atom<IWebsiteDomain>(undefined as unknown as IWebsiteDomain )
export const websiteDomainSearchAtom = atom<SearchParams>({
key: '',
@ -51,7 +50,7 @@ export const websiteDomainsAtom = atomWithQuery((get) => {
})
//saveOrUpdateAtom
export const saveOrUpdateWebsiteDomainAtom = atomWithMutation<IApiResult, Websites.IWebsiteDomain>((get) => {
export const saveOrUpdateWebsiteDomainAtom = atomWithMutation<IApiResult, IWebsiteDomain>((get) => {
return {
mutationKey: [ 'updateWebsiteDomain' ],

90
src/store/websites/record.ts

@ -0,0 +1,90 @@
import { atom } from 'jotai'
import { IApiResult, IPage } from '@/global'
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'
import { message } from 'antd'
import { t } from 'i18next'
import websitesServ from '@/service/websites'
import { IWebsiteDnsRecords } from '@/types/website/record'
type SearchParams = IPage & {
key?: string
[key: string]: any
}
export const websiteDnsRecordsIdAtom = atom(0)
export const websiteDnsRecordsIdsAtom = atom<number[]>([])
export const websiteDnsRecordsAtom = atom<IWebsiteDnsRecords>(undefined as unknown as IWebsiteDnsRecords )
export const websiteDnsRecordsSearchAtom = atom<SearchParams>({
key: '',
pageSize: 10,
page: 1,
} as SearchParams)
export const websiteDnsRecordsPageAtom = atom<IPage>({
pageSize: 10,
page: 1,
})
export const websiteDnsRecordssAtom = atomWithQuery((get) => {
return {
queryKey: [ 'websiteDnsRecordss', get(websiteDnsRecordsSearchAtom) ],
queryFn: async ({ queryKey: [ , params ] }) => {
return await websitesServ.record.list(params as SearchParams)
},
select: res => {
const data = res.data
data.rows = data.rows?.map(row => {
return {
...row,
//status: convertToBool(row.status)
}
})
return data
}
}
})
//saveOrUpdateAtom
export const saveOrUpdateWebsiteDnsRecordsAtom = atomWithMutation<IApiResult, IWebsiteDnsRecords>((get) => {
return {
mutationKey: [ 'updateWebsiteDnsRecords' ],
mutationFn: async (data) => {
//data.status = data.status ? '1' : '0'
if (data.id === 0) {
return await websitesServ.record.add(data)
}
return await websitesServ.record.update(data)
},
onSuccess: (res) => {
const isAdd = !!res.data?.id
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
//更新列表
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore fix
get(queryClientAtom).invalidateQueries({ queryKey: [ 'websiteDnsRecordss', get(websiteDnsRecordsSearchAtom) ] })
return res
}
}
})
export const deleteWebsiteDnsRecordsAtom = atomWithMutation((get) => {
return {
mutationKey: [ 'deleteWebsiteDnsRecords' ],
mutationFn: async (ids: number[]) => {
return await websitesServ.record.batchDelete(ids ?? get(websiteDnsRecordsIdsAtom))
},
onSuccess: (res) => {
message.success('message.deleteSuccess')
//更新列表
get(queryClientAtom).invalidateQueries({ queryKey: [ 'websiteDnsRecordss', get(websiteDnsRecordsSearchAtom) ] })
return res
}
}
})

4
src/types/website/domain.d.ts

@ -1,5 +1,4 @@
export namespace Websites {
export interface IWebsiteDomain {
export interface IWebsiteDomain {
id: number;
name: string;
dns_account_id: number;
@ -10,5 +9,4 @@ export namespace Websites {
nameservers: string;
tag: string;
remark: string;
}
}

15
src/types/website/record.d.ts

@ -0,0 +1,15 @@
export interface IWebsiteDnsRecords {
id: number;
record_id: string;
domain_id: string;
name: string;
content: string;
poxy: number;
ttl: number;
type: string;
status: number;
tag: string;
remark: string;
created: string;
modified: string;
}
Loading…
Cancel
Save