From 89787a187dd73bd71ae1768ee4b1a18ec2bd11cf Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Fri, 7 Oct 2022 00:57:12 +0800 Subject: [PATCH] update --- admin-frontend/.eslintrc.json | 6 +- admin-frontend/src/App.tsx | 5 + admin-frontend/src/app/store.ts | 5 +- .../SubscribeManager.tsx | 52 +++- .../subsribeConfigManager/SubscribeModal.tsx | 241 ++++++++++++++++++ .../src/features/targetName/targetNameReq.ts | 15 ++ .../features/targetName/targetNameSlice.ts | 14 + .../features/weightConfig/WeightManager.tsx | 54 ++-- admin-frontend/src/pages/Home.css | 8 + admin-frontend/src/pages/Home.tsx | 42 ++- 10 files changed, 392 insertions(+), 50 deletions(-) create mode 100644 admin-frontend/src/features/subsribeConfigManager/SubscribeModal.tsx create mode 100644 admin-frontend/src/features/targetName/targetNameReq.ts create mode 100644 admin-frontend/src/features/targetName/targetNameSlice.ts diff --git a/admin-frontend/.eslintrc.json b/admin-frontend/.eslintrc.json index 4a244e9..ce2ac20 100644 --- a/admin-frontend/.eslintrc.json +++ b/admin-frontend/.eslintrc.json @@ -6,6 +6,8 @@ }, "extends": [ "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/eslint-recommended", "airbnb" ], "parser": "@typescript-eslint/parser", @@ -25,7 +27,9 @@ "no-use-before-define": "off", "@typescript-eslint/no-use-before-define": ["error"], "import/extensions": ["error", "ignorePackages", {"ts": "never", "tsx": "never"}], - "no-param-reassign": ["error", { "props": false }] + "no-param-reassign": ["error", { "props": false }], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"] }, "settings": { "import/resolver": { diff --git a/admin-frontend/src/App.tsx b/admin-frontend/src/App.tsx index a292b7f..10ccaf6 100644 --- a/admin-frontend/src/App.tsx +++ b/admin-frontend/src/App.tsx @@ -6,6 +6,7 @@ import Auth from './features/auth/Auth'; import { loadGlobalConf, selectGlobalConfLoaded } from './features/globalConf/globalConfSlice'; import GroupManager from './features/subsribeConfigManager/GroupManager'; import SubscribeManager from './features/subsribeConfigManager/SubscribeManager'; +import WeightConfig from './features/weightConfig/WeightManager'; import Home from './pages/Home'; import Unauthed from './pages/Unauthed'; @@ -41,6 +42,10 @@ function App() { path: 'groups/:groupNumber', element: , }, + { + path: 'weight', + element: , + }, ], }, ], { basename: '/bison' }); diff --git a/admin-frontend/src/app/store.ts b/admin-frontend/src/app/store.ts index 09e59fc..abfe3ff 100644 --- a/admin-frontend/src/app/store.ts +++ b/admin-frontend/src/app/store.ts @@ -15,6 +15,7 @@ import storage from 'redux-persist/lib/storage'; import authReducer from '../features/auth/authSlice'; import globalConfReducer from '../features/globalConf/globalConfSlice'; import { subscribeApi } from '../features/subsribeConfigManager/subscribeConfigSlice'; +import { targetNameApi } from '../features/targetName/targetNameSlice'; import { weightApi } from '../features/weightConfig/weightConfigSlice'; const rootReducer = combineReducers({ @@ -22,6 +23,7 @@ const rootReducer = combineReducers({ globalConf: globalConfReducer, [subscribeApi.reducerPath]: subscribeApi.reducer, [weightApi.reducerPath]: weightApi.reducer, + [targetNameApi.reducerPath]: targetNameApi.reducer, }); const persistConfig = { @@ -40,7 +42,8 @@ export const store = configureStore({ }, }) .concat(subscribeApi.middleware) - .concat(weightApi.middleware), + .concat(weightApi.middleware) + .concat(targetNameApi.middleware), }); export const persistor = persistStore(store); diff --git a/admin-frontend/src/features/subsribeConfigManager/SubscribeManager.tsx b/admin-frontend/src/features/subsribeConfigManager/SubscribeManager.tsx index 03950a4..fbb02d3 100644 --- a/admin-frontend/src/features/subsribeConfigManager/SubscribeManager.tsx +++ b/admin-frontend/src/features/subsribeConfigManager/SubscribeManager.tsx @@ -1,18 +1,34 @@ -import React from 'react'; +import React, { useState } from 'react'; import { - Button, Empty, Space, Table, Tag, + Button, Empty, Message, Popconfirm, Space, Table, Tag, Typography, } from '@arco-design/web-react'; import { useParams } from 'react-router-dom'; -import { useGetSubsQuery } from './subscribeConfigSlice'; +import { useDeleteSubMutation, useGetSubsQuery } from './subscribeConfigSlice'; import { useAppSelector } from '../../app/hooks'; import { selectPlatformConf } from '../globalConf/globalConfSlice'; import { SubscribeConfig } from '../../utils/type'; +import SubscribeModal from './SubscribeModal'; export default function SubscribeManager() { const { data: subs } = useGetSubsQuery(); + const [deleteSub, { isLoading: deleteIsLoading }] = useDeleteSubMutation(); const { groupNumber } = useParams(); const platformConf = useAppSelector(selectPlatformConf); + const isLoading = deleteIsLoading; + const [showModal, setShowModal] = useState(false); + const [formInitVal, setFormInitVal] = useState(null as SubscribeConfig | null); + + const handleNewSub = () => { + setFormInitVal(null); + setShowModal(true); + }; + + const handleEdit = (sub: SubscribeConfig) => () => { + setFormInitVal(sub); + setShowModal(true); + }; + const columns = [ { title: '平台名称', @@ -59,9 +75,20 @@ export default function SubscribeManager() { dataIndex: 'op', render: (_: any, record: SubscribeConfig) => ( - - - + + + { + deleteSub({ + groupNumber: parseInt(groupNumber!, 10), + target: record.target, + platformName: record.platformName, + }); + }} + > + + ), }, @@ -71,14 +98,21 @@ export default function SubscribeManager() { return ( <> - {subs[groupNumber].name} - {groupNumber} + {subs[groupNumber].name} + {groupNumber} - + `${record.platformName}-${record.target}`} + loading={isLoading} + /> + ); diff --git a/admin-frontend/src/features/subsribeConfigManager/SubscribeModal.tsx b/admin-frontend/src/features/subsribeConfigManager/SubscribeModal.tsx new file mode 100644 index 0000000..31e29d6 --- /dev/null +++ b/admin-frontend/src/features/subsribeConfigManager/SubscribeModal.tsx @@ -0,0 +1,241 @@ +import React, { useEffect, useState } from 'react'; +import { + Form, Input, InputTag, Modal, Select, Space, Tag, +} from '@arco-design/web-react'; +import useForm from '@arco-design/web-react/es/Form/useForm'; +import { IconInfoCircle } from '@arco-design/web-react/icon'; +import { useAppSelector } from '../../app/hooks'; +import { selectPlatformConf } from '../globalConf/globalConfSlice'; +import { CategoryConfig, SubscribeConfig } from '../../utils/type'; +import getTargetName from '../targetName/targetNameReq'; +import { useUpdateSubMutation, useNewSubMutation } from './subscribeConfigSlice'; + +function SubscribeTag({ + value, onChange, disabled, +}: { + value?: string[]; + onChange?: (arg0: string[]) => void; + disabled?: boolean; +}) { + const [valueState, setValueState] = useState(value || []); + const handleSetValue = (newVal: string[]) => { + setValueState(newVal); + if (onChange) { + onChange(newVal); + } + }; + useEffect(() => { + if (value) { + setValueState(value); + } + }, [value]); + + if (disabled) { + return 不支持标签; + } + return ( + + { valueState.length === 0 && 全部标签 } + + + ); +} + +SubscribeTag.defaultProps = { + value: [], + onChange: null, + disabled: false, +}; + +interface SubscribeModalProp { + visible: boolean; + setVisible: (arg0: boolean) => void; + groupNumber: string; + initval?: SubscribeConfig | null; +} + +function SubscribeModal({ + visible, setVisible, groupNumber, initval, +}: SubscribeModalProp) { + const [form] = useForm(); + const [confirmLoading, setConfirmLoading] = useState(false); + const platformConf = useAppSelector(selectPlatformConf); + const [updateSub] = useUpdateSubMutation(); + const [newSub] = useNewSubMutation(); + + const onSubmit = () => { + form.validate().then((value: SubscribeConfig) => { + const newVal = { ...value }; + if (typeof newVal.tags !== 'object') { + newVal.tags = []; + } + if (typeof newVal.cats !== 'object') { + newVal.cats = []; + } + if (newVal.target === '') { + newVal.target = 'default'; + } + let postPromise: Promise; + if (initval) { + postPromise = updateSub({ + groupNumber: parseInt(groupNumber, 10), + sub: newVal, + }); + } else { + postPromise = newSub({ + groupNumber: parseInt(groupNumber, 10), + sub: newVal, + }); + } + setConfirmLoading(true); + postPromise.then(() => { + setConfirmLoading(false); + setVisible(false); + form.clearFields(); + }); + }); + }; + + const [hasTarget, setHasTarget] = useState(false); + const [categories, setCategories] = useState({} as CategoryConfig); + const [enableTags, setEnableTags] = useState(false); + + const setPlatformStates = (platform: string) => { + setHasTarget(platformConf[platform].hasTarget); + setCategories(platformConf[platform].categories); + setEnableTags(platformConf[platform].enabledTag); + }; + + const handlePlatformSelected = (platform: string) => { + setPlatformStates(platform); + form.setFieldValue('cats', []); + if (!platformConf[platform].hasTarget) { + getTargetName(platform, 'default').then((res) => { + form.setFieldsValue({ + targetName: res, + target: '', + }); + }); + } else { + form.setFieldsValue({ + targetName: '', + target: '', + }); + } + }; + + useEffect(() => { + if (initval) { + const { platformName } = initval; + setPlatformStates(platformName); + form.setFieldsValue(initval); + } else { + form.clearFields(); + } + }, [initval, form, platformConf]); + + return ( + setVisible(false)} + confirmLoading={confirmLoading} + onOk={onSubmit} + > +
+ + + + new Promise((resolve) => { + getTargetName(form.getFieldValue('platformName'), value) + .then((res) => { + if (res) { + form.setFieldsValue({ + targetName: res, + }); + resolve(); + } else { + form.setFieldsValue({ + targetName: '', + }); + callback('账号不正确,请重新检查账号'); + resolve(); + } + }) + .catch(() => { + callback('服务器错误,请稍后再试'); + resolve(); + }); + }), + }, + ]} + > + } + placeholder={hasTarget ? '获取方式见文档' : '此平台不需要账号'} + /> + + + + + 0, + message: '请至少选择一个分类进行订阅', + }, + ]} + > + + + + + + + +
+ ); +} +SubscribeModal.defaultProps = { + initval: null, +}; +export default SubscribeModal; diff --git a/admin-frontend/src/features/targetName/targetNameReq.ts b/admin-frontend/src/features/targetName/targetNameReq.ts new file mode 100644 index 0000000..538b9a1 --- /dev/null +++ b/admin-frontend/src/features/targetName/targetNameReq.ts @@ -0,0 +1,15 @@ +import { RootState, store } from '../../app/store'; +import { baseUrl } from '../../utils/urls'; + +export default async function getTargetName(platformName: string, target: string) { + const url = `${baseUrl}target_name?platformName=${platformName}&target=${target}`; + const state = store.getState() as RootState; + const authToken = state.auth.token; + const res = await fetch(url, { + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + const resObj = await res.json(); + return resObj.targetName as string; +} diff --git a/admin-frontend/src/features/targetName/targetNameSlice.ts b/admin-frontend/src/features/targetName/targetNameSlice.ts new file mode 100644 index 0000000..cb5a9aa --- /dev/null +++ b/admin-frontend/src/features/targetName/targetNameSlice.ts @@ -0,0 +1,14 @@ +import { createApi } from '@reduxjs/toolkit/query/react'; +import baseQueryWithAuth from '../auth/authQuery'; + +export const targetNameApi = createApi({ + reducerPath: 'targetName', + baseQuery: baseQueryWithAuth, + endpoints: (builder) => ({ + getTargetName: builder.query<{targetName: string}, {target: string; platformName: string}>({ + query: () => '/target_name', + }), + }), +}); + +export const { useGetTargetNameQuery } = targetNameApi; diff --git a/admin-frontend/src/features/weightConfig/WeightManager.tsx b/admin-frontend/src/features/weightConfig/WeightManager.tsx index 9f4a8b3..fbf6f67 100644 --- a/admin-frontend/src/features/weightConfig/WeightManager.tsx +++ b/admin-frontend/src/features/weightConfig/WeightManager.tsx @@ -1,28 +1,32 @@ import React from 'react'; -import { WeightConfig } from '../../utils/type'; -import { useGetWeightQuery, useUpdateWeightMutation } from './weightConfigSlice'; +// import { WeightConfig } from '../../utils/type'; +// import { useGetWeightQuery, useUpdateWeightMutation } from './weightConfigSlice'; +// +// export default function WeightManager() { +// const { data: weight } = useGetWeightQuery(); +// const [updateWeight] = useUpdateWeightMutation(); +// +// const doUpdate = () => { +// const weightConfig: WeightConfig = { +// default: 20, +// time_config: [ +// { +// start_time: '01:00', +// end_time: '02:00', +// weight: 50, +// }, +// ], +// }; +// updateWeight({ weight: weightConfig, platform_name: 'weibo', target: '' }); +// }; +// return ( +// <> +//
{weight && JSON.stringify(weight)}
+// +// +// ); +// } -export default function WeightManager() { - const { data: weight } = useGetWeightQuery(); - const [updateWeight] = useUpdateWeightMutation(); - - const doUpdate = () => { - const weightConfig: WeightConfig = { - default: 20, - time_config: [ - { - start_time: '01:00', - end_time: '02:00', - weight: 50, - }, - ], - }; - updateWeight({ weight: weightConfig, platform_name: 'weibo', target: '' }); - }; - return ( - <> -
{weight && JSON.stringify(weight)}
- - - ); +export default function WeightConfig() { + return
下个版本再写
; } diff --git a/admin-frontend/src/pages/Home.css b/admin-frontend/src/pages/Home.css index 7f8cb90..501573b 100644 --- a/admin-frontend/src/pages/Home.css +++ b/admin-frontend/src/pages/Home.css @@ -7,9 +7,17 @@ .layout-collapse-demo .arco-layout-header .logo { height: 32px; margin: 12px 8px; + width: 150px; background: var(--color-fill-2); } +.layout-collapse-demo .arco-layout-header span { + height: 100%; + line-height: 100%; + font-size: 20px; + margin: 0 20px; +} + .layout-collapse-demo .arco-layout-content .arco-layout-footer, .layout-collapse-demo .arco-layout-content .arco-layout-content { color: var(--color-white); diff --git a/admin-frontend/src/pages/Home.tsx b/admin-frontend/src/pages/Home.tsx index b86073f..b17683d 100644 --- a/admin-frontend/src/pages/Home.tsx +++ b/admin-frontend/src/pages/Home.tsx @@ -4,15 +4,15 @@ import { IconRobot, IconDashboard } from '@arco-design/web-react/icon'; import './Home.css'; // import SubscribeManager from '../features/subsribeConfigManager/SubscribeManager'; import { - Link, Outlet, useLocation, useNavigate, + Link, Navigate, Outlet, useLocation, useNavigate, } from 'react-router-dom'; - -export function homeLoader() { -} +import { useAppSelector } from '../app/hooks'; +import { selectIsLogin } from '../features/auth/authSlice'; export default function Home() { const location = useLocation(); const navigate = useNavigate(); + const isLogin = useAppSelector(selectIsLogin); const path = location.pathname; useEffect(() => { @@ -20,13 +20,12 @@ export default function Home() { navigate('/home/groups'); } - if (path !== '/home/groups' && !path.startsWith('/home/groups/')) { - console.log(path); + if (path !== '/home/groups' && !path.startsWith('/home/groups/') && path !== '/home/weight') { navigate('/home/groups'); } }, [path]); - let currentKey: string = ''; + let currentKey = ''; if (path === '/home/groups') { currentKey = 'groups'; } else if (path.startsWith('/home/groups/')) { @@ -38,14 +37,18 @@ export default function Home() { const handleTabSelect = (tab: string) => { changeSelectTab(tab); if (tab === 'groups') { - navigate('/home/navigate'); + navigate('/home/groups'); } else if (tab === 'weight') { navigate('/home/weight'); } }; + if (!isLogin) { + return ; + } + let breadcrumbContent: ReactNode; - if (selectedTab === 'groups') { + if (path === '/home/groups') { breadcrumbContent = ( @@ -54,17 +57,26 @@ export default function Home() { ); - } else if (selectedTab === 'subs') { + } else if (path.startsWith('/home/groups/')) { breadcrumbContent = ( - - 订阅管理 + + 调度权重 - groupman + 群管理 + + + ); + } else if (path === '/home/weight') { + breadcrumbContent = ( + + + + 调度权重 ); @@ -72,7 +84,9 @@ export default function Home() { return ( -
+ + Nonebot Bison +