From 649c1cf8f2c8ac804912a7c25a59aced38b81aa3 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Sat, 12 Feb 2022 10:35:35 +0800 Subject: [PATCH] format frontend code --- .pre-commit-config.yaml | 2 +- admin-frontend/src/App.test.tsx | 8 +- admin-frontend/src/App.tsx | 49 ++- admin-frontend/src/api/config.ts | 47 ++- admin-frontend/src/api/utils.ts | 92 +++--- admin-frontend/src/component/addSubsModal.tsx | 301 ++++++++++-------- admin-frontend/src/component/inputTag.tsx | 157 +++++---- .../src/component/subscribeCard.tsx | 260 +++++++++------ admin-frontend/src/index.tsx | 18 +- .../src/pages/admin/configPage/index.tsx | 89 +++--- admin-frontend/src/pages/admin/index.tsx | 68 ++-- admin-frontend/src/pages/auth.tsx | 41 +-- admin-frontend/src/reportWebVitals.ts | 4 +- admin-frontend/src/setupTests.ts | 2 +- admin-frontend/src/store/globalConfSlice.ts | 41 ++- admin-frontend/src/store/groupConfigSlice.ts | 37 ++- admin-frontend/src/store/hooks.ts | 8 +- admin-frontend/src/store/index.ts | 8 +- admin-frontend/src/store/loginSlice.ts | 119 ++++--- admin-frontend/src/utils/type.ts | 66 ++-- 20 files changed, 825 insertions(+), 592 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77e9bc6..baf2d35 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,4 +20,4 @@ repos: rev: v2.5.1 hooks: - id: prettier - types_or: [markdown] + types_or: [markdown, ts, tsx] diff --git a/admin-frontend/src/App.test.tsx b/admin-frontend/src/App.test.tsx index 2a68616..d76787e 100644 --- a/admin-frontend/src/App.test.tsx +++ b/admin-frontend/src/App.test.tsx @@ -1,8 +1,8 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; -test('renders learn react link', () => { +test("renders learn react link", () => { render(); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); diff --git a/admin-frontend/src/App.tsx b/admin-frontend/src/App.tsx index aacb1e7..6ab4c01 100644 --- a/admin-frontend/src/App.tsx +++ b/admin-frontend/src/App.tsx @@ -1,49 +1,46 @@ -import 'antd/dist/antd.css'; -import React, {useEffect} from 'react'; -import {useDispatch, useSelector} from 'react-redux'; -import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; -import './App.css'; -import {Admin} from './pages/admin'; -import {Auth} from './pages/auth'; -import {getGlobalConf} from './store/globalConfSlice'; -import {useAppSelector} from './store/hooks'; -import {loadLoginState, loginSelector} from './store/loginSlice'; - +import "antd/dist/antd.css"; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; +import "./App.css"; +import { Admin } from "./pages/admin"; +import { Auth } from "./pages/auth"; +import { getGlobalConf } from "./store/globalConfSlice"; +import { useAppSelector } from "./store/hooks"; +import { loadLoginState, loginSelector } from "./store/loginSlice"; function LoginSwitch() { - const login = useSelector(loginSelector) + const login = useSelector(loginSelector); if (login.login) { return ; } else { - return ( -
- not login -
- ) + return
not login
; } } function App() { - const dispatch = useDispatch() - const globalConf = useAppSelector(state => state.globalConf) + const dispatch = useDispatch(); + const globalConf = useAppSelector((state) => state.globalConf); useEffect(() => { dispatch(getGlobalConf()); - dispatch(loadLoginState()) + dispatch(loadLoginState()); }, [dispatch]); - return <> - { globalConf.loaded && + return ( + <> + {globalConf.loaded && ( - + - + - } - ; + )} + + ); } export default App; diff --git a/admin-frontend/src/api/config.ts b/admin-frontend/src/api/config.ts index 3286f74..d66f325 100644 --- a/admin-frontend/src/api/config.ts +++ b/admin-frontend/src/api/config.ts @@ -1,6 +1,12 @@ import axios from "axios"; -import { GlobalConf, TokenResp, SubscribeResp, TargetNameResp, SubscribeConfig } from "../utils/type"; -import { baseUrl } from './utils'; +import { + GlobalConf, + TokenResp, + SubscribeResp, + TargetNameResp, + SubscribeConfig, +} from "../utils/type"; +import { baseUrl } from "./utils"; export async function getGlobalConf(): Promise { const res = await axios.get(`${baseUrl}global_conf`); @@ -8,7 +14,9 @@ export async function getGlobalConf(): Promise { } export async function auth(token: string): Promise { - const res = await axios.get(`${baseUrl}auth`, {params: {token}}); + const res = await axios.get(`${baseUrl}auth`, { + params: { token }, + }); return res.data; } @@ -17,22 +25,39 @@ export async function getSubscribe(): Promise { return res.data; } -export async function getTargetName(platformName: string, target: string): Promise { - const res = await axios.get(`${baseUrl}target_name`, {params: {platformName, target}}); +export async function getTargetName( + platformName: string, + target: string +): Promise { + const res = await axios.get(`${baseUrl}target_name`, { + params: { platformName, target }, + }); return res.data; } export async function addSubscribe(groupNumber: string, req: SubscribeConfig) { - const res = await axios.post(`${baseUrl}subs`, req, {params: {groupNumber}}) + const res = await axios.post(`${baseUrl}subs`, req, { + params: { groupNumber }, + }); return res.data; } -export async function delSubscribe(groupNumber: string, platformName: string, target: string) { - const res = await axios.delete(`${baseUrl}subs`, {params: {groupNumber, platformName, target}}); +export async function delSubscribe( + groupNumber: string, + platformName: string, + target: string +) { + const res = await axios.delete(`${baseUrl}subs`, { + params: { groupNumber, platformName, target }, + }); return res.data; } -export async function updateSubscribe(groupNumber: string, req: SubscribeConfig) { - return axios.patch(`${baseUrl}subs`, req, {params: {groupNumber}}) - .then(res => res.data); +export async function updateSubscribe( + groupNumber: string, + req: SubscribeConfig +) { + return axios + .patch(`${baseUrl}subs`, req, { params: { groupNumber } }) + .then((res) => res.data); } diff --git a/admin-frontend/src/api/utils.ts b/admin-frontend/src/api/utils.ts index ba8a7c9..13ee025 100644 --- a/admin-frontend/src/api/utils.ts +++ b/admin-frontend/src/api/utils.ts @@ -1,51 +1,61 @@ -import axios, {AxiosError} from "axios"; -import {Store} from "src/store"; -import { clearLoginStatus } from 'src/store/loginSlice'; +import axios, { AxiosError } from "axios"; +import { Store } from "src/store"; +import { clearLoginStatus } from "src/store/loginSlice"; // import { useContext } from 'react'; // import { LoginContext } from "../utils/context"; -export const baseUrl = '/bison/api/' -let store: Store +export const baseUrl = "/bison/api/"; +let store: Store; export const injectStore = (_store: Store) => { - store = _store -} + store = _store; +}; // const loginStatus = useContext(LoginContext); -axios.interceptors.request.use(function (config) { - if (config.url && config.url.startsWith(baseUrl) && config.url !== `${baseUrl}auth` - && config.url !== `${baseUrl}global_conf`) { - const token = localStorage.getItem('token'); - if (token) { - config.headers['Authorization'] = `Bearer ${token}`; - } else { - throw new axios.Cancel('User not login'); +axios.interceptors.request.use( + function (config) { + if ( + config.url && + config.url.startsWith(baseUrl) && + config.url !== `${baseUrl}auth` && + config.url !== `${baseUrl}global_conf` + ) { + const token = localStorage.getItem("token"); + if (token) { + config.headers["Authorization"] = `Bearer ${token}`; + } else { + throw new axios.Cancel("User not login"); + } } + return config; + }, + function (error) { + return Promise.reject(error); } - return config; -}, function (error) { - return Promise.reject(error); -}); +); -axios.interceptors.response.use(function (response) { - // const data = response.data; - // const parseToMap = (item: any): any => { - // if (item instanceof Array) { - // return item.map(parseToMap); - // } else if (item instanceof Object) { - // let res = new Map(); - // for (const key of Object.keys(item)) { - // res.set(key, parseToMap(item[key])); - // } - // return res; - // } else { - // return item; - // } - // } - // response.data = parseToMap(data); - return response; -}, function(error: AxiosError) { - if(error.response && error.response.status === 401) { - store.dispatch(clearLoginStatus()); +axios.interceptors.response.use( + function (response) { + // const data = response.data; + // const parseToMap = (item: any): any => { + // if (item instanceof Array) { + // return item.map(parseToMap); + // } else if (item instanceof Object) { + // let res = new Map(); + // for (const key of Object.keys(item)) { + // res.set(key, parseToMap(item[key])); + // } + // return res; + // } else { + // return item; + // } + // } + // response.data = parseToMap(data); + return response; + }, + function (error: AxiosError) { + if (error.response && error.response.status === 401) { + store.dispatch(clearLoginStatus()); + } + return Promise.reject(error); } - return Promise.reject(error); -}); +); diff --git a/admin-frontend/src/component/addSubsModal.tsx b/admin-frontend/src/component/addSubsModal.tsx index b51113e..9b68a8f 100644 --- a/admin-frontend/src/component/addSubsModal.tsx +++ b/admin-frontend/src/component/addSubsModal.tsx @@ -1,15 +1,15 @@ -import {Form, Input, Modal, Select, Tag} from 'antd'; -import React, {useEffect, useState} from "react"; -import {useSelector} from 'react-redux'; -import {addSubscribe, getTargetName, updateSubscribe} from 'src/api/config'; -import {InputTag} from 'src/component/inputTag'; -import {platformConfSelector} from 'src/store/globalConfSlice'; -import {CategoryConfig, SubscribeConfig} from 'src/utils/type'; +import { Form, Input, Modal, Select, Tag } from "antd"; +import React, { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { addSubscribe, getTargetName, updateSubscribe } from "src/api/config"; +import { InputTag } from "src/component/inputTag"; +import { platformConfSelector } from "src/store/globalConfSlice"; +import { CategoryConfig, SubscribeConfig } from "src/utils/type"; interface InputTagCustomProp { - value?: Array, - onChange?: (value: Array) => void, - disabled?: boolean + value?: Array; + onChange?: (value: Array) => void; + disabled?: boolean; } function InputTagCustom(prop: InputTagCustomProp) { const [value, setValue] = useState(prop.value || []); @@ -18,165 +18,202 @@ function InputTagCustom(prop: InputTagCustomProp) { if (prop.onChange) { prop.onChange(newVal); } - } + }; useEffect(() => { if (prop.value) { setValue(prop.value); } - }, [prop.value]) + }, [prop.value]); return ( <> - { - prop.disabled ? 不支持标签: + {prop.disabled ? ( + 不支持标签 + ) : ( <> - {value.length === 0 && - 全部标签 - } - - - } + {value.length === 0 && 全部标签} + + + )} - ) + ); } interface AddModalProp { - showModal: boolean, - groupNumber: string, - setShowModal: (s: boolean) => void, - refresh: () => void - initVal?: SubscribeConfig + showModal: boolean; + groupNumber: string; + setShowModal: (s: boolean) => void; + refresh: () => void; + initVal?: SubscribeConfig; } export function AddModal({ - showModal, groupNumber, setShowModal, refresh, initVal + showModal, + groupNumber, + setShowModal, + refresh, + initVal, }: AddModalProp) { - const [ confirmLoading, setConfirmLoading ] = useState(false); - const platformConf = useSelector(platformConfSelector) - const [ hasTarget, setHasTarget ] = useState(false); - const [ categories, setCategories ] = useState({} as CategoryConfig); - const [ enabledTag, setEnableTag ] = useState(false); - const [ form ] = Form.useForm(); - const [ inited, setInited ] = useState(false); + const [confirmLoading, setConfirmLoading] = useState(false); + const platformConf = useSelector(platformConfSelector); + const [hasTarget, setHasTarget] = useState(false); + const [categories, setCategories] = useState({} as CategoryConfig); + const [enabledTag, setEnableTag] = useState(false); + const [form] = Form.useForm(); + const [inited, setInited] = useState(false); const changePlatformSelect = (platform: string) => { - setHasTarget(_ => platformConf[platform].hasTarget); - setCategories(_ => platformConf[platform].categories); - setEnableTag(platformConf[platform].enabledTag) - if (! platformConf[platform].hasTarget) { - getTargetName(platform, 'default') - .then(res => { - console.log(res) - form.setFieldsValue({ - targetName: res.targetName, - target: '' - }) - }) + setHasTarget((_) => platformConf[platform].hasTarget); + setCategories((_) => platformConf[platform].categories); + setEnableTag(platformConf[platform].enabledTag); + if (!platformConf[platform].hasTarget) { + getTargetName(platform, "default").then((res) => { + console.log(res); + form.setFieldsValue({ + targetName: res.targetName, + target: "", + }); + }); } else { form.setFieldsValue({ - targetName: '', - target: '' - }) + targetName: "", + target: "", + }); } - } + }; const handleSubmit = (value: any) => { - let newVal = Object.assign({}, value) - if (typeof newVal.tags !== 'object') { - newVal.tags = [] + let newVal = Object.assign({}, value); + if (typeof newVal.tags !== "object") { + newVal.tags = []; } - if (typeof newVal.cats !== 'object') { - newVal.cats = [] + if (typeof newVal.cats !== "object") { + newVal.cats = []; } - if (newVal.target === '') { - newVal.target = 'default' + if (newVal.target === "") { + newVal.target = "default"; } - if (initVal) { // patch - updateSubscribe(groupNumber, newVal) - .then(() => { - setConfirmLoading(false); - setShowModal(false); - form.resetFields(); - refresh(); - }); - } else { - addSubscribe(groupNumber, newVal) - .then(() => { + if (initVal) { + // patch + updateSubscribe(groupNumber, newVal).then(() => { setConfirmLoading(false); setShowModal(false); form.resetFields(); refresh(); - }); + }); + } else { + addSubscribe(groupNumber, newVal).then(() => { + setConfirmLoading(false); + setShowModal(false); + form.resetFields(); + refresh(); + }); } - } + }; const handleModleFinish = () => { form.submit(); setConfirmLoading(() => true); - } + }; useEffect(() => { if (initVal && !inited) { const platformName = initVal.platformName; setHasTarget(platformConf[platformName].hasTarget); setCategories(platformConf[platformName].categories); setEnableTag(platformConf[platformName].enabledTag); - setInited(true) - form.setFieldsValue(initVal) + setInited(true); + form.setFieldsValue(initVal); } - }, [initVal, form, platformConf, inited]) - return setShowModal(false)} - onOk={handleModleFinish}> -
- - - - { - try { - const res = await getTargetName(form.getFieldValue('platformName'), value); - if (res.targetName) { - form.setFieldsValue({ - targetName: res.targetName - }) - return Promise.resolve() - } else { - form.setFieldsValue({ - targetName: '' - }) - return Promise.reject("账号不正确,请重新检查账号") - } - } catch { - return Promise.reject('服务器错误,请稍后再试') - } - } - } - ]}> - - - - - - 0, message: "请至少选择一个分类进行订阅"} - ]}> - + {Object.keys(platformConf).map((platformName) => ( + + {platformConf[platformName].name} - ) - } - - - - - -
+ ))} + + + { + try { + const res = await getTargetName( + form.getFieldValue("platformName"), + value + ); + if (res.targetName) { + form.setFieldsValue({ + targetName: res.targetName, + }); + return Promise.resolve(); + } else { + form.setFieldsValue({ + targetName: "", + }); + return Promise.reject("账号不正确,请重新检查账号"); + } + } catch { + return Promise.reject("服务器错误,请稍后再试"); + } + }, + }, + ]} + > + + + + + + 0, + message: "请至少选择一个分类进行订阅", + }, + ]} + > + + + + + +
+ ); } diff --git a/admin-frontend/src/component/inputTag.tsx b/admin-frontend/src/component/inputTag.tsx index 9408a61..e633e91 100644 --- a/admin-frontend/src/component/inputTag.tsx +++ b/admin-frontend/src/component/inputTag.tsx @@ -1,122 +1,151 @@ -import {Input, Tag, Tooltip} from "antd"; -import {PresetColorType, PresetStatusColorType} from 'antd/lib/_util/colors' -import {LiteralUnion} from 'antd/lib/_util/type' -import React, {useRef, useState, useEffect} from "react"; -import { PlusOutlined } from '@ant-design/icons'; +import { Input, Tag, Tooltip } from "antd"; +import { PresetColorType, PresetStatusColorType } from "antd/lib/_util/colors"; +import { LiteralUnion } from "antd/lib/_util/type"; +import React, { useRef, useState, useEffect } from "react"; +import { PlusOutlined } from "@ant-design/icons"; interface InputTagProp { - value?: Array, - onChange?: (value: Array) => void + value?: Array; + onChange?: (value: Array) => void; color?: LiteralUnion; - addText?: string + addText?: string; } export function InputTag(prop: InputTagProp) { - const [ value, setValue ] = useState>(prop.value || []); - const [ inputVisible, setInputVisible ] = useState(false); - const [ inputValue, setInputValue ] = useState(''); - const [ editInputIndex, setEditInputIndex ] = useState(-1); - const [ editInputValue, setEditInputValue ] = useState(''); + const [value, setValue] = useState>(prop.value || []); + const [inputVisible, setInputVisible] = useState(false); + const [inputValue, setInputValue] = useState(""); + const [editInputIndex, setEditInputIndex] = useState(-1); + const [editInputValue, setEditInputValue] = useState(""); const inputRef = useRef(null as any); const editInputRef = useRef(null as any); useEffect(() => { if (prop.value) { setValue(prop.value); } - }, [prop.value]) + }, [prop.value]); useEffect(() => { if (inputVisible) { - inputRef.current.focus() + inputRef.current.focus(); } }, [inputVisible]); useEffect(() => { if (editInputIndex !== -1) { - editInputRef.current.focus(); + editInputRef.current.focus(); } }, [editInputIndex]); const handleClose = (removedTag: string) => { - const tags = value.filter(tag => tag !== removedTag); - setValue(_ => tags); + const tags = value.filter((tag) => tag !== removedTag); + setValue((_) => tags); if (prop.onChange) { prop.onChange(tags); } - } - + }; + const showInput = () => { - setInputVisible(_ => true); - } + setInputVisible((_) => true); + }; const handleInputConfirm = () => { if (inputValue && value.indexOf(inputValue) === -1) { const newVal = [...value, inputValue]; - setValue(_ => newVal); + setValue((_) => newVal); if (prop.onChange) { prop.onChange(newVal); } } - setInputVisible(_ => false); - setInputValue(_ => ''); - } + setInputVisible((_) => false); + setInputValue((_) => ""); + }; const handleEditInputChange = (e: any) => { - setEditInputValue(_ => e.target.value); - } + setEditInputValue((_) => e.target.value); + }; const handleEditInputConfirm = () => { const newTags = value.slice(); newTags[editInputIndex] = editInputValue; - setValue(_ => newTags); - if (prop.onChange) { - prop.onChange(newTags); - } - setEditInputIndex(_ => -1); - setEditInputValue(_ => ''); - } + setValue((_) => newTags); + if (prop.onChange) { + prop.onChange(newTags); + } + setEditInputIndex((_) => -1); + setEditInputValue((_) => ""); + }; const handleInputChange = (e: any) => { setInputValue(e.target.value); - } + }; return ( <> - { value.map((tag, index) => { + {value.map((tag, index) => { if (editInputIndex === index) { return ( - + + ); + } + const isLongTag = tag.length > 20; + const tagElem = ( + handleClose(tag)} + > + { + setEditInputIndex((_) => index); + setEditInputValue((_) => tag); + e.preventDefault(); + }} + > + {isLongTag ? `${tag.slice(0, 20)}...` : tag} + + ); - } - const isLongTag = tag.length > 20; - const tagElem = ( - handleClose(tag)}> - { - setEditInputIndex(_ => index); - setEditInputValue(_ => tag); - e.preventDefault(); - }}> - {isLongTag ? `${tag.slice(0, 20)}...` : tag} - - + return isLongTag ? ( + + {tagElem} + + ) : ( + tagElem ); - return isLongTag ? ( - - {tagElem} - - ) : ( tagElem ); })} {inputVisible && ( - + )} {!inputVisible && ( - - {prop.addText || "Add Tag"} + + {prop.addText || "Add Tag"} )} ); - } diff --git a/admin-frontend/src/component/subscribeCard.tsx b/admin-frontend/src/component/subscribeCard.tsx index 66d38dc..37c3ef0 100644 --- a/admin-frontend/src/component/subscribeCard.tsx +++ b/admin-frontend/src/component/subscribeCard.tsx @@ -1,108 +1,190 @@ -import {CopyOutlined, DeleteOutlined, EditOutlined} from '@ant-design/icons'; -import {Card, Col, Form, message, Popconfirm, Select, Tag, Tooltip} from 'antd'; -import Modal from 'antd/lib/modal/Modal'; -import React, {useState} from "react"; -import {useDispatch, useSelector} from 'react-redux'; -import {addSubscribe, delSubscribe} from 'src/api/config'; -import {platformConfSelector} from 'src/store/globalConfSlice'; -import {groupConfigSelector, updateGroupSubs} from 'src/store/groupConfigSlice'; -import {PlatformConfig, SubscribeConfig, SubscribeResp} from 'src/utils/type'; -import {AddModal} from './addSubsModal'; +import { CopyOutlined, DeleteOutlined, EditOutlined } from "@ant-design/icons"; +import { + Card, + Col, + Form, + message, + Popconfirm, + Select, + Tag, + Tooltip, +} from "antd"; +import Modal from "antd/lib/modal/Modal"; +import React, { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { addSubscribe, delSubscribe } from "src/api/config"; +import { platformConfSelector } from "src/store/globalConfSlice"; +import { + groupConfigSelector, + updateGroupSubs, +} from "src/store/groupConfigSlice"; +import { PlatformConfig, SubscribeConfig, SubscribeResp } from "src/utils/type"; +import { AddModal } from "./addSubsModal"; interface CopyModalProp { - setShowModal: (modalShow: boolean) => void - showModal: boolean - config: SubscribeConfig, - groups: SubscribeResp - currentGroupNumber: string - reload: () => void + setShowModal: (modalShow: boolean) => void; + showModal: boolean; + config: SubscribeConfig; + groups: SubscribeResp; + currentGroupNumber: string; + reload: () => void; } -function CopyModal({setShowModal,config, - currentGroupNumber,groups,showModal,reload}: CopyModalProp) { - const [confirmLoading, setConfirmLoading] = useState(false) - const [ selectedGroups, setSelectGroups ] = useState>([]); - const postReqs = async (selectedGroups: Array, config: SubscribeConfig) => { - for(let selectedGroup of selectedGroups) { +function CopyModal({ + setShowModal, + config, + currentGroupNumber, + groups, + showModal, + reload, +}: CopyModalProp) { + const [confirmLoading, setConfirmLoading] = useState(false); + const [selectedGroups, setSelectGroups] = useState>([]); + const postReqs = async ( + selectedGroups: Array, + config: SubscribeConfig + ) => { + for (let selectedGroup of selectedGroups) { await addSubscribe(selectedGroup, config); } - } + }; const handleOk = () => { if (selectedGroups.length === 0) { message.error("请至少选择一个目标群"); - } else{ - setConfirmLoading(true) + } else { + setConfirmLoading(true); postReqs(selectedGroups, config).then(() => { - setConfirmLoading(false) - setShowModal(false) - return reload() - }) + setConfirmLoading(false); + setShowModal(false); + return reload(); + }); } - } - return setShowModal(false)} onOk={handleOk}> - + }; + return ( + setShowModal(false)} + onOk={handleOk} + > + + ); } interface SubscribeCardProp { - groupNumber: string - config: SubscribeConfig + groupNumber: string; + config: SubscribeConfig; } -export function SubscribeCard({groupNumber, config}: SubscribeCardProp) { - const platformConfs = useSelector(platformConfSelector) - const [showModal, setShowModal] = useState(false) - const [showEditModal, setShowEditModal] = useState(false) +export function SubscribeCard({ groupNumber, config }: SubscribeCardProp) { + const platformConfs = useSelector(platformConfSelector); + const [showModal, setShowModal] = useState(false); + const [showEditModal, setShowEditModal] = useState(false); const platformConf = platformConfs[config.platformName] as PlatformConfig; const dispatcher = useDispatch(); const groupSubscribes = useSelector(groupConfigSelector); - const reload = () => dispatcher(updateGroupSubs()) - const handleDelete = (groupNumber: string, platformName: string, target: string) => () => { - delSubscribe(groupNumber, platformName, target).then(() => { - reload() - }) - } + const reload = () => dispatcher(updateGroupSubs()); + const handleDelete = + (groupNumber: string, platformName: string, target: string) => () => { + delSubscribe(groupNumber, platformName, target).then(() => { + reload(); + }); + }; return ( - - - {setShowEditModal(state => !state)}}/> - , - - {setShowModal(state => !state)}}/> - , - - - , - ]}> -
- - { platformConf.hasTarget ? config.target : 无帐号 } - - - {Object.keys(platformConf.categories).length > 0 ? - config.cats.map((catKey: number) => ({platformConf.categories[catKey]})) : - 不支持类型} - - - {platformConf.enabledTag ? config.tags.length > 0 ? config.tags.map(tag => ({tag})) : (全部标签) : - 不支持Tag} - -
-
- - - - ) + + + { + setShowEditModal((state) => !state); + }} + /> + , + + { + setShowModal((state) => !state); + }} + /> + , + + + + + , + ]} + > +
+ + {platformConf.hasTarget ? ( + config.target + ) : ( + 无帐号 + )} + + + {Object.keys(platformConf.categories).length > 0 ? ( + config.cats.map((catKey: number) => ( + + {platformConf.categories[catKey]} + + )) + ) : ( + 不支持类型 + )} + + + {platformConf.enabledTag ? ( + config.tags.length > 0 ? ( + config.tags.map((tag) => ( + + {tag} + + )) + ) : ( + 全部标签 + ) + ) : ( + 不支持Tag + )} + +
+
+ + + + ); } diff --git a/admin-frontend/src/index.tsx b/admin-frontend/src/index.tsx index 5621bf9..874357b 100644 --- a/admin-frontend/src/index.tsx +++ b/admin-frontend/src/index.tsx @@ -1,11 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {Provider} from 'react-redux'; -import App from './App'; -import './index.css'; -import reportWebVitals from './reportWebVitals'; -import store from './store'; -import {injectStore} from 'src/api/utils'; +import React from "react"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import App from "./App"; +import "./index.css"; +import reportWebVitals from "./reportWebVitals"; +import store from "./store"; +import { injectStore } from "src/api/utils"; injectStore(store); ReactDOM.render( @@ -14,7 +14,7 @@ ReactDOM.render( , - document.getElementById('root') + document.getElementById("root") ); // If you want to start measuring performance in your app, pass a function diff --git a/admin-frontend/src/pages/admin/configPage/index.tsx b/admin-frontend/src/pages/admin/configPage/index.tsx index 06d676a..9552fe1 100644 --- a/admin-frontend/src/pages/admin/configPage/index.tsx +++ b/admin-frontend/src/pages/admin/configPage/index.tsx @@ -1,56 +1,73 @@ -import {Button, Collapse, Empty, Row} from 'antd'; -import React, {ReactElement, useEffect, useState} from "react"; -import {useDispatch, useSelector} from 'react-redux'; -import {AddModal} from 'src/component/addSubsModal'; -import {SubscribeCard} from 'src/component/subscribeCard'; -import {groupConfigSelector, updateGroupSubs} from 'src/store/groupConfigSlice'; +import { Button, Collapse, Empty, Row } from "antd"; +import React, { ReactElement, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { AddModal } from "src/component/addSubsModal"; +import { SubscribeCard } from "src/component/subscribeCard"; +import { + groupConfigSelector, + updateGroupSubs, +} from "src/store/groupConfigSlice"; interface ConfigPageProp { - tab: string + tab: string; } export function ConfigPage(prop: ConfigPageProp) { - const [ showModal, setShowModal ] = useState(false); - const [ currentAddingGroupNumber, setCurrentAddingGroupNumber ] = useState(''); + const [showModal, setShowModal] = useState(false); + const [currentAddingGroupNumber, setCurrentAddingGroupNumber] = useState(""); const configData = useSelector(groupConfigSelector); const dispatcher = useDispatch(); useEffect(() => { - dispatcher(updateGroupSubs()) + dispatcher(updateGroupSubs()); }, [prop.tab, dispatcher]); - const clickNew = (groupNumber: string) => (e: React.MouseEvent) => { - setShowModal(_ => true); - setCurrentAddingGroupNumber(groupNumber); - e.stopPropagation(); - } - + const clickNew = + (groupNumber: string) => (e: React.MouseEvent) => { + setShowModal((_) => true); + setCurrentAddingGroupNumber(groupNumber); + e.stopPropagation(); + }; + if (Object.keys(configData).length === 0) { - return + return ; } else { let groups: Array = []; for (let key of Object.keys(configData)) { let value = configData[key]; groups.push( - {`${key} - ${value.name}`} - }> - - {value.subscribes.map((subs, idx) => )} + + {`${key} - ${value.name}`} + + + } + > + + {value.subscribes.map((subs, idx) => ( + + ))} - ) + ); } return ( -
- - {groups} - - dispatcher(updateGroupSubs())} - setShowModal={(s: boolean) => setShowModal(_ => s)} /> -
- ) +
+ {groups} + dispatcher(updateGroupSubs())} + setShowModal={(s: boolean) => setShowModal((_) => s)} + /> +
+ ); } } - - diff --git a/admin-frontend/src/pages/admin/index.tsx b/admin-frontend/src/pages/admin/index.tsx index 4d860be..8fe24aa 100644 --- a/admin-frontend/src/pages/admin/index.tsx +++ b/admin-frontend/src/pages/admin/index.tsx @@ -1,37 +1,39 @@ -import {BugOutlined, SettingOutlined} from '@ant-design/icons'; -import {Layout, Menu} from 'antd'; -import React, {useState} from "react"; -import {useSelector} from 'react-redux'; -import {loginSelector} from 'src/store/loginSlice'; -import './admin.css'; -import {ConfigPage} from './configPage'; +import { BugOutlined, SettingOutlined } from "@ant-design/icons"; +import { Layout, Menu } from "antd"; +import React, { useState } from "react"; +import { useSelector } from "react-redux"; +import { loginSelector } from "src/store/loginSlice"; +import "./admin.css"; +import { ConfigPage } from "./configPage"; export function Admin() { - const login = useSelector(loginSelector) - const [ tab, changeTab ] = useState("manage"); + const login = useSelector(loginSelector); + const [tab, changeTab] = useState("manage"); return ( - - -
-
- changeTab(key)}> - }>订阅管理 - { login.type === 'admin' && - }>查看日志 - } - -
- -
- { - tab === 'manage' ? - - : null - } -
-
-
- ) + + +
+ changeTab(key)} + > + }> + 订阅管理 + + {login.type === "admin" && ( + }> + 查看日志 + + )} + +
+ +
+ {tab === "manage" ? : null} +
+
+
+ ); } - diff --git a/admin-frontend/src/pages/auth.tsx b/admin-frontend/src/pages/auth.tsx index 299c58d..c27046d 100644 --- a/admin-frontend/src/pages/auth.tsx +++ b/admin-frontend/src/pages/auth.tsx @@ -1,28 +1,31 @@ -import React, {useEffect} from "react"; -import {useDispatch, useSelector} from "react-redux"; -import {useParams} from "react-router"; -import {Redirect} from 'react-router-dom'; -import {login, loginSelector} from 'src/store/loginSlice'; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useParams } from "react-router"; +import { Redirect } from "react-router-dom"; +import { login, loginSelector } from "src/store/loginSlice"; -interface AuthParam { - code: string +interface AuthParam { + code: string; } export function Auth() { - const { code } = useParams(); + const { code } = useParams(); const dispatch = useDispatch(); - const loginState = useSelector(loginSelector) + const loginState = useSelector(loginSelector); useEffect(() => { const loginFun = async () => { dispatch(login(code)); - } + }; loginFun(); - }, [code, dispatch]) - return <> - { loginState.login ? - : - loginState.failed ? -
登录失败,请重新获取连接
: -
Logining...
- } -; + }, [code, dispatch]); + return ( + <> + {loginState.login ? ( + + ) : loginState.failed ? ( +
登录失败,请重新获取连接
+ ) : ( +
Logining...
+ )} + + ); } diff --git a/admin-frontend/src/reportWebVitals.ts b/admin-frontend/src/reportWebVitals.ts index 49a2a16..5fa3583 100644 --- a/admin-frontend/src/reportWebVitals.ts +++ b/admin-frontend/src/reportWebVitals.ts @@ -1,8 +1,8 @@ -import { ReportHandler } from 'web-vitals'; +import { ReportHandler } from "web-vitals"; const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); diff --git a/admin-frontend/src/setupTests.ts b/admin-frontend/src/setupTests.ts index 8f2609b..1dd407a 100644 --- a/admin-frontend/src/setupTests.ts +++ b/admin-frontend/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; diff --git a/admin-frontend/src/store/globalConfSlice.ts b/admin-frontend/src/store/globalConfSlice.ts index 914066a..d11d52b 100644 --- a/admin-frontend/src/store/globalConfSlice.ts +++ b/admin-frontend/src/store/globalConfSlice.ts @@ -1,23 +1,31 @@ -import {CaseReducer, createAsyncThunk, createSlice, PayloadAction} from "@reduxjs/toolkit"; -import {getGlobalConf as getGlobalConfApi} from "src/api/config"; -import {GlobalConf} from "src/utils/type"; -import {RootState} from "."; - +import { + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, +} from "@reduxjs/toolkit"; +import { getGlobalConf as getGlobalConfApi } from "src/api/config"; +import { GlobalConf } from "src/utils/type"; +import { RootState } from "."; const initialState: GlobalConf = { platformConf: {}, - loaded: false -} + loaded: false, +}; -const setGlobalConf: CaseReducer> = (_, action) => { - return {...action.payload, loaded: true} -} +const setGlobalConf: CaseReducer> = ( + _, + action +) => { + return { ...action.payload, loaded: true }; +}; export const getGlobalConf = createAsyncThunk( "globalConf/set", getGlobalConfApi, { - condition: (_, { getState }) => !(getState() as RootState).globalConf.loaded + condition: (_, { getState }) => + !(getState() as RootState).globalConf.loaded, } ); @@ -26,10 +34,11 @@ export const globalConfSlice = createSlice({ initialState, reducers: {}, extraReducers: (builder) => { - builder.addCase(getGlobalConf.fulfilled, setGlobalConf) - } -}) + builder.addCase(getGlobalConf.fulfilled, setGlobalConf); + }, +}); -export const platformConfSelector = (state: RootState) => state.globalConf.platformConf +export const platformConfSelector = (state: RootState) => + state.globalConf.platformConf; -export default globalConfSlice.reducer +export default globalConfSlice.reducer; diff --git a/admin-frontend/src/store/groupConfigSlice.ts b/admin-frontend/src/store/groupConfigSlice.ts index 3f42130..98089c8 100644 --- a/admin-frontend/src/store/groupConfigSlice.ts +++ b/admin-frontend/src/store/groupConfigSlice.ts @@ -1,27 +1,36 @@ -import {CaseReducer, createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'; -import {SubscribeResp} from 'src/utils/type'; -import {getSubscribe} from 'src/api/config'; -import {RootState} from '.'; -const initialState: SubscribeResp = {} +import { + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, +} from "@reduxjs/toolkit"; +import { SubscribeResp } from "src/utils/type"; +import { getSubscribe } from "src/api/config"; +import { RootState } from "."; +const initialState: SubscribeResp = {}; -const setSubs: CaseReducer> = (_, action) => { - return action.payload -} +const setSubs: CaseReducer> = ( + _, + action +) => { + return action.payload; +}; export const updateGroupSubs = createAsyncThunk( - "groupConfig/update", getSubscribe -) + "groupConfig/update", + getSubscribe +); export const groupConfigSlice = createSlice({ name: "groupConfig", initialState, reducers: { - setSubs + setSubs, }, extraReducers: (reducer) => { - reducer.addCase(updateGroupSubs.fulfilled, setSubs) - } -}) + reducer.addCase(updateGroupSubs.fulfilled, setSubs); + }, +}); export const groupConfigSelector = (state: RootState) => state.groupConfig; export default groupConfigSlice.reducer; diff --git a/admin-frontend/src/store/hooks.ts b/admin-frontend/src/store/hooks.ts index 99221df..5e6629e 100644 --- a/admin-frontend/src/store/hooks.ts +++ b/admin-frontend/src/store/hooks.ts @@ -1,5 +1,5 @@ -import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux"; -import {AppDispatch, RootState} from "."; +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "."; -export const useAppDispacher = () => useDispatch() -export const useAppSelector: TypedUseSelectorHook = useSelector +export const useAppDispacher = () => useDispatch(); +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/admin-frontend/src/store/index.ts b/admin-frontend/src/store/index.ts index 36f6821..18530e7 100644 --- a/admin-frontend/src/store/index.ts +++ b/admin-frontend/src/store/index.ts @@ -1,15 +1,15 @@ -import {configureStore} from "@reduxjs/toolkit"; +import { configureStore } from "@reduxjs/toolkit"; import loginSlice from "./loginSlice"; import globalConfSlice from "./globalConfSlice"; -import groupConfigSlice from './groupConfigSlice'; +import groupConfigSlice from "./groupConfigSlice"; const store = configureStore({ reducer: { login: loginSlice, globalConf: globalConfSlice, groupConfig: groupConfigSlice, - } -}) + }, +}); export default store; diff --git a/admin-frontend/src/store/loginSlice.ts b/admin-frontend/src/store/loginSlice.ts index 6db2da1..1b6ab40 100644 --- a/admin-frontend/src/store/loginSlice.ts +++ b/admin-frontend/src/store/loginSlice.ts @@ -1,108 +1,121 @@ -import { AnyAction, CaseReducer, createAsyncThunk, createSlice, PayloadAction, ThunkAction } from "@reduxjs/toolkit"; -import jwt_decode from 'jwt-decode'; +import { + AnyAction, + CaseReducer, + createAsyncThunk, + createSlice, + PayloadAction, + ThunkAction, +} from "@reduxjs/toolkit"; +import jwt_decode from "jwt-decode"; import { LoginStatus, TokenResp } from "src/utils/type"; import { auth } from "src/api/config"; -import {RootState} from "."; +import { RootState } from "."; const initialState: LoginStatus = { login: false, - type: '', - name: '', - id: '123', + type: "", + name: "", + id: "123", // groups: [], - token: '', - failed: false -} + token: "", + failed: false, +}; interface storedInfo { - type: string - name: string - id: string + type: string; + name: string; + id: string; } -const loginAction: CaseReducer> = (_, action) => { +const loginAction: CaseReducer> = ( + _, + action +) => { return { login: true, failed: false, type: action.payload.type, name: action.payload.name, id: action.payload.id, - token: action.payload.token - } -} + token: action.payload.token, + }; +}; export const login = createAsyncThunk( "auth/login", async (code: string) => { let res = await auth(code); if (res.status !== 200) { - throw Error("Login Error") + throw Error("Login Error"); } else { - localStorage.setItem('loginInfo', JSON.stringify({ - 'type': res.type, - 'name': res.name, - id: res.id, - })) - localStorage.setItem('token', res.token) + localStorage.setItem( + "loginInfo", + JSON.stringify({ + type: res.type, + name: res.name, + id: res.id, + }) + ); + localStorage.setItem("token", res.token); } - return res + return res; }, { condition: (_: string, { getState }) => { - const { login } = getState() as { login: LoginStatus } + const { login } = getState() as { login: LoginStatus }; return !login.login; - } + }, } -) - +); export const loginSlice = createSlice({ - name: 'auth', + name: "auth", initialState, reducers: { doLogin: loginAction, doClearLogin: (state) => { - state.login = false - } + state.login = false; + }, }, extraReducers: (builder) => { builder.addCase(login.fulfilled, loginAction); builder.addCase(login.rejected, (stat) => { - stat.failed = true - }) - } -}) + stat.failed = true; + }); + }, +}); -export const { doLogin, doClearLogin } = loginSlice.actions +export const { doLogin, doClearLogin } = loginSlice.actions; -export const loadLoginState = (): ThunkAction => +export const loadLoginState = + (): ThunkAction => (dispatch, getState) => { if (getState().login.login) { - return + return; } - const infoJson = localStorage.getItem('loginInfo') - const jwtToken = localStorage.getItem('token'); + const infoJson = localStorage.getItem("loginInfo"); + const jwtToken = localStorage.getItem("token"); if (infoJson && jwtToken) { const decodedJwt = jwt_decode(jwtToken) as { exp: number }; if (decodedJwt.exp < Date.now() / 1000) { - return + return; } - const info = JSON.parse(infoJson) as storedInfo + const info = JSON.parse(infoJson) as storedInfo; const payload: TokenResp = { ...info, status: 200, token: jwtToken, - } - dispatch(doLogin(payload)) + }; + dispatch(doLogin(payload)); } - } + }; -export const clearLoginStatus = (): ThunkAction => - (dispatch) => { - localStorage.removeItem('loginInfo') - localStorage.removeItem('token') - dispatch(doClearLogin()) - } -export const loginSelector = (state: RootState) => state.login +export const clearLoginStatus = + (): ThunkAction => (dispatch) => { + localStorage.removeItem("loginInfo"); + localStorage.removeItem("token"); + dispatch(doClearLogin()); + }; +export const loginSelector = (state: RootState) => state.login; -export default loginSlice.reducer +export default loginSlice.reducer; diff --git a/admin-frontend/src/utils/type.ts b/admin-frontend/src/utils/type.ts index e514ed0..6f99657 100644 --- a/admin-frontend/src/utils/type.ts +++ b/admin-frontend/src/utils/type.ts @@ -1,34 +1,34 @@ interface QQGroup { - id: string, - name: string, + id: string; + name: string; } export interface LoginStatus { - login: boolean - type: string - name: string - id: string + login: boolean; + type: string; + name: string; + id: string; // groups: Array - token: string, - failed: boolean, + token: string; + failed: boolean; } export type LoginContextType = { - login: LoginStatus - save: (status: LoginStatus) => void -} + login: LoginStatus; + save: (status: LoginStatus) => void; +}; export interface SubscribeConfig { - platformName: string - target: string - targetName: string - cats: Array - tags: Array + platformName: string; + target: string; + targetName: string; + cats: Array; + tags: Array; } export interface GlobalConf { - platformConf: AllPlatformConf, - loaded: boolean + platformConf: AllPlatformConf; + loaded: boolean; } export interface AllPlatformConf { @@ -36,34 +36,34 @@ export interface AllPlatformConf { } export interface CategoryConfig { - [idx: number]: string + [idx: number]: string; } export interface PlatformConfig { - name: string - categories: CategoryConfig - enabledTag: boolean, - platformName: string, - hasTarget: boolean + name: string; + categories: CategoryConfig; + enabledTag: boolean; + platformName: string; + hasTarget: boolean; } export interface TokenResp { - status: number, - token: string, - type: string, - id: string - name: string + status: number; + token: string; + type: string; + id: string; + name: string; } export interface SubscribeGroupDetail { - name: string, - subscribes: Array + name: string; + subscribes: Array; } export interface SubscribeResp { - [idx: string]: SubscribeGroupDetail + [idx: string]: SubscribeGroupDetail; } export interface TargetNameResp { - targetName: string + targetName: string; }