6 changed files with 409 additions and 20 deletions
			
			
		- 
					15src/layout/ListPageLayout.tsx
 - 
					36src/locales/lang/pages/cms/collect/zh-CN.ts
 - 
					4src/locales/lang/zh-CN.ts
 - 
					315src/pages/cms/collect/index.tsx
 - 
					13src/pages/cms/collect/style.ts
 - 
					26src/store/cms/collect.ts
 
@ -0,0 +1,36 @@ | 
			
		|||||
 | 
				export default { | 
			
		||||
 | 
				  title: '采集站点管理', | 
			
		||||
 | 
				  description: '采集站点管理', | 
			
		||||
 | 
				  list: '采集列表', | 
			
		||||
 | 
				  add: '新增采集', | 
			
		||||
 | 
				  edit: '编辑采集', | 
			
		||||
 | 
				  delete: '删除采集', | 
			
		||||
 | 
				  detail: '采集详情', | 
			
		||||
 | 
				  type_id: [ | 
			
		||||
 | 
				    '在线观看', '磁链', '网盘' | 
			
		||||
 | 
				  ], | 
			
		||||
 | 
				  sync_pic: [ | 
			
		||||
 | 
				    '关闭', '开启', '全局' | 
			
		||||
 | 
				  ], | 
			
		||||
 | 
				  columns: { | 
			
		||||
 | 
				    name: '站点名称', | 
			
		||||
 | 
				    url: '采集地址', | 
			
		||||
 | 
				    param: '参数', | 
			
		||||
 | 
				    type_id: '类型', | 
			
		||||
 | 
				    opt: '操作', | 
			
		||||
 | 
				    filter: '过滤器', | 
			
		||||
 | 
				    filter_form: '过滤表单', | 
			
		||||
 | 
				    sync_pic: '同步图片', | 
			
		||||
 | 
				    icon_cdn: '图标CDN', | 
			
		||||
 | 
				    class: '分类', | 
			
		||||
 | 
				    weights: '权重', | 
			
		||||
 | 
				    status: '状态', | 
			
		||||
 | 
				    open_replace: '开启替换', | 
			
		||||
 | 
				    replace_str: '替换字符串', | 
			
		||||
 | 
				    site_auth: '搜片认证站点', | 
			
		||||
 | 
				    site_cooperation: '搜片合作优质站点', | 
			
		||||
 | 
				    categories_rules: '采集规则', | 
			
		||||
 | 
				    create_time: '创建时间', | 
			
		||||
 | 
				    update_time: '更新时间' | 
			
		||||
 | 
				  } | 
			
		||||
 | 
				} | 
			
		||||
@ -0,0 +1,315 @@ | 
			
		|||||
 | 
				import { useEffect, useMemo, useState } from 'react' | 
			
		||||
 | 
				import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' | 
			
		||||
 | 
				import { useTranslation } from '@/i18n.ts' | 
			
		||||
 | 
				import { useAtom, useAtomValue } from 'jotai' | 
			
		||||
 | 
				import { | 
			
		||||
 | 
				  collectsAtom, | 
			
		||||
 | 
				  deleteCollectAtom, | 
			
		||||
 | 
				  saveOrUpdateCollectAtom, | 
			
		||||
 | 
				  collectSearchAtom, | 
			
		||||
 | 
				  syncPicTypes, | 
			
		||||
 | 
				  types | 
			
		||||
 | 
				} from '@/store/cms/collect.ts' | 
			
		||||
 | 
				import { Button, Form, Popconfirm } from 'antd' | 
			
		||||
 | 
				import ListPageLayout from '@/layout/ListPageLayout.tsx' | 
			
		||||
 | 
				import Switch from '@/components/switch' | 
			
		||||
 | 
				import Action from '@/components/action/Action.tsx' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const i18nPrefix = 'cms.collect' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const Collect = () => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  // const { styles } = useStyle()
 | 
			
		||||
 | 
				  const { t } = useTranslation() | 
			
		||||
 | 
				  const [ form ] = Form.useForm() | 
			
		||||
 | 
				  const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCollectAtom) | 
			
		||||
 | 
				  const [ search, setSearch ] = useAtom(collectSearchAtom) | 
			
		||||
 | 
				  const { data, isFetching, isLoading, refetch } = useAtomValue(collectsAtom) | 
			
		||||
 | 
				  const { mutate: deleteCollect, isPending: isDeleting } = useAtomValue(deleteCollectAtom) | 
			
		||||
 | 
				  const [ open, setOpen ] = useState(false) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const columns = useMemo(() => { | 
			
		||||
 | 
				    return [ | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        title: 'ID', | 
			
		||||
 | 
				        dataIndex: 'id', | 
			
		||||
 | 
				        hideInTable: true, | 
			
		||||
 | 
				        hideInSearch: true, | 
			
		||||
 | 
				        formItemProps: { hidden: true } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.name`, '站点名称'), | 
			
		||||
 | 
				        'dataIndex': 'name', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 200, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        ellipsis: true, | 
			
		||||
 | 
				        formItemProps: { | 
			
		||||
 | 
				          width: undefined, | 
			
		||||
 | 
				          rules: [ | 
			
		||||
 | 
				            { required: true, message: t(`${i18nPrefix}.columns.name`, '站点名称') } | 
			
		||||
 | 
				          ] | 
			
		||||
 | 
				        }, | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.icon_cdn`, '站点图标'), | 
			
		||||
 | 
				        'dataIndex': 'icon_cdn', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 80, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_text, record) => { | 
			
		||||
 | 
				          return <img src={record.icon_cdn} style={{ width: 20, height: 20 }}/> | 
			
		||||
 | 
				        }, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.url`, '站点URL'), | 
			
		||||
 | 
				        'dataIndex': 'url', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 150, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        ellipsis: true, | 
			
		||||
 | 
				        copyable: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.param`, '参数'), | 
			
		||||
 | 
				        'dataIndex': 'param', | 
			
		||||
 | 
				        hideInTable: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.type_id`, '类型'), | 
			
		||||
 | 
				        'dataIndex': 'type_id', | 
			
		||||
 | 
				        valueType: 'select', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 100, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_text, record) => { | 
			
		||||
 | 
				          return t(`${i18nPrefix}.type_id.${record.type_id}`, '') | 
			
		||||
 | 
				        }, | 
			
		||||
 | 
				        fieldProps: { | 
			
		||||
 | 
				          options: types | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.opt`, '操作方式'), | 
			
		||||
 | 
				        'dataIndex': 'opt', | 
			
		||||
 | 
				        hideInTable: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.filter`, '过滤模式'), | 
			
		||||
 | 
				        'dataIndex': 'filter', | 
			
		||||
 | 
				        hideInTable: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.filter_form`, '过滤表单'), | 
			
		||||
 | 
				        'dataIndex': 'filter_form', | 
			
		||||
 | 
				        hideInTable: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.sync_pic`, '同步图片'), | 
			
		||||
 | 
				        'dataIndex': 'sync_pic', | 
			
		||||
 | 
				        valueType: 'select', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 100, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_text, record) => { | 
			
		||||
 | 
				          return t(`${i18nPrefix}.sync_pic.${record.sync_pic}`, '') | 
			
		||||
 | 
				        }, | 
			
		||||
 | 
				        fieldProps: { | 
			
		||||
 | 
				          options: syncPicTypes | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.class`, '扩展分类'), | 
			
		||||
 | 
				        'dataIndex': 'class', | 
			
		||||
 | 
				        valueType: 'textarea', | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.weights`, '权重'), | 
			
		||||
 | 
				        'dataIndex': 'weights', | 
			
		||||
 | 
				        valueType: 'digit', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 80, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.status`, '启用'), | 
			
		||||
 | 
				        'dataIndex': 'status', | 
			
		||||
 | 
				        valueType: 'switch', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 80, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_dom, record) => { | 
			
		||||
 | 
				          return <Switch value={record.status} size={'small'}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.open_replace`, '开启替换'), | 
			
		||||
 | 
				        'dataIndex': 'open_replace', | 
			
		||||
 | 
				        valueType: 'switch', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 80, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_dom, record) => { | 
			
		||||
 | 
				          return <Switch value={record.open_replace} size={'small'}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.replace_str`, '替换内容'), | 
			
		||||
 | 
				        'dataIndex': 'replace_str', | 
			
		||||
 | 
				        valueType: 'textarea', | 
			
		||||
 | 
				        ellipsis: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.site_auth`, '搜片认证站点'), | 
			
		||||
 | 
				        'dataIndex': 'site_auth', | 
			
		||||
 | 
				        valueType: 'switch', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 100, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_dom, record) => { | 
			
		||||
 | 
				          return <Switch value={record.site_auth} size={'small'}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.site_cooperation`, '搜片合作优质站点'), | 
			
		||||
 | 
				        'dataIndex': 'site_cooperation', | 
			
		||||
 | 
				        valueType: 'switch', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 130, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        render: (_dom, record) => { | 
			
		||||
 | 
				          return <Switch value={record.site_cooperation} size={'small'}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        'title': t(`${i18nPrefix}.columns.categories_rules`, '站点采集规则'), | 
			
		||||
 | 
				        'dataIndex': 'categories_rules', | 
			
		||||
 | 
				        valueType: 'textarea', | 
			
		||||
 | 
				        onHeaderCell: () => ({ | 
			
		||||
 | 
				          width: 200, | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        ellipsis: true, | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        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={() => { | 
			
		||||
 | 
				                    deleteCollect([ record.id ]) | 
			
		||||
 | 
				                  }} | 
			
		||||
 | 
				                  title={t('message.deleteConfirm')}> | 
			
		||||
 | 
				            <a key="del"> | 
			
		||||
 | 
				              {t('actions.delete', '删除')} | 
			
		||||
 | 
				            </a> | 
			
		||||
 | 
				          </Popconfirm> | 
			
		||||
 | 
				        ] | 
			
		||||
 | 
				      } | 
			
		||||
 | 
				    ] as ProColumns[] | 
			
		||||
 | 
				  }, [ isDeleting ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    if (isSuccess) { | 
			
		||||
 | 
				      setOpen(false) | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  }, [ isSuccess ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  return ( | 
			
		||||
 | 
				          <ListPageLayout> | 
			
		||||
 | 
				            <ProTable | 
			
		||||
 | 
				                    rowKey="id" | 
			
		||||
 | 
				                    headerTitle={t(`${i18nPrefix}.title`, '站点管理')} | 
			
		||||
 | 
				                    toolbar={{ | 
			
		||||
 | 
				                      search: { | 
			
		||||
 | 
				                        loading: isFetching && !!search.key, | 
			
		||||
 | 
				                        onSearch: (value: string) => { | 
			
		||||
 | 
				                          setSearch(prev => ({ | 
			
		||||
 | 
				                            ...prev, | 
			
		||||
 | 
				                            key: value | 
			
		||||
 | 
				                          })) | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                        allowClear: true, | 
			
		||||
 | 
				                        placeholder: t(`${i18nPrefix}.placeholder`, '输入站点名称') | 
			
		||||
 | 
				                      }, | 
			
		||||
 | 
				                      actions: [ | 
			
		||||
 | 
				                        <Button | 
			
		||||
 | 
				                                onClick={() => { | 
			
		||||
 | 
				                                  form.resetFields() | 
			
		||||
 | 
				                                  form.setFieldsValue({ | 
			
		||||
 | 
				                                    id: 0, | 
			
		||||
 | 
				                                  }) | 
			
		||||
 | 
				                                  setOpen(true) | 
			
		||||
 | 
				                                }} | 
			
		||||
 | 
				                                type={'primary'}>{t(`${i18nPrefix}.add`, '添加')}</Button> | 
			
		||||
 | 
				                      ] | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    scroll={{ | 
			
		||||
 | 
				                      x: 2000, | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    loading={isLoading || isFetching} | 
			
		||||
 | 
				                    dataSource={data?.rows ?? []} | 
			
		||||
 | 
				                    columns={columns} | 
			
		||||
 | 
				                    search={false} | 
			
		||||
 | 
				                    options={{ | 
			
		||||
 | 
				                      reload: () => { | 
			
		||||
 | 
				                        refetch() | 
			
		||||
 | 
				                      }, | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    pagination={{ | 
			
		||||
 | 
				                      total: data?.total, | 
			
		||||
 | 
				                      pageSize: search.pageSize, | 
			
		||||
 | 
				                      current: search.page, | 
			
		||||
 | 
				                      onChange: (current, pageSize) => { | 
			
		||||
 | 
				                        setSearch(prev => { | 
			
		||||
 | 
				                          return { | 
			
		||||
 | 
				                            ...prev, | 
			
		||||
 | 
				                            page: current, | 
			
		||||
 | 
				                            pageSize: pageSize, | 
			
		||||
 | 
				                          } | 
			
		||||
 | 
				                        }) | 
			
		||||
 | 
				                      }, | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				            /> | 
			
		||||
 | 
				            <BetaSchemaForm | 
			
		||||
 | 
				                    shouldUpdate={false} | 
			
		||||
 | 
				                    width={600} | 
			
		||||
 | 
				                    form={form} | 
			
		||||
 | 
				                    layout={'vertical'} | 
			
		||||
 | 
				                    scrollToFirstError={true} | 
			
		||||
 | 
				                    title={t(`${i18nPrefix}.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '站点编辑' : '站点添加')} | 
			
		||||
 | 
				                    // colProps={{ span: 24 }}
 | 
			
		||||
 | 
				                    labelCol={{ span: 6 }} | 
			
		||||
 | 
				                    // wrapperCol={{ span: 14 }}
 | 
			
		||||
 | 
				                    layoutType={'DrawerForm'} | 
			
		||||
 | 
				                    open={open} | 
			
		||||
 | 
				                    drawerProps={{ | 
			
		||||
 | 
				                      maskClosable: false, | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    onOpenChange={(open) => { | 
			
		||||
 | 
				                      setOpen(open) | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    loading={isSubmitting} | 
			
		||||
 | 
				                    onFinish={async (values) => { | 
			
		||||
 | 
				                      // console.log('values', values)
 | 
			
		||||
 | 
				                      saveOrUpdate(values) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                    }} | 
			
		||||
 | 
				                    columns={columns as ProFormColumnsType[]}/> | 
			
		||||
 | 
				          </ListPageLayout> | 
			
		||||
 | 
				  ) | 
			
		||||
 | 
				} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export default Collect | 
			
		||||
@ -0,0 +1,13 @@ | 
			
		|||||
 | 
				import { createStyles } from '@/theme' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { | 
			
		||||
 | 
				    const prefix = `${prefixCls}-${token?.proPrefix}-cms-collect-page` | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const container = css`
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    `
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    return { | 
			
		||||
 | 
				        container: cx(prefix, props?.className, container), | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				}) | 
			
		||||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue