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: "请至少选择一个分类进行订阅"}
- ]}>
-
+
+ {
+ 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: "请至少选择一个分类进行订阅",
+ },
+ ]}
+ >
+ 0
+ ? "请选择要订阅的分类"
+ : "本平台不支持分类"
+ }
+ >
+ {Object.keys(categories).length > 0 &&
+ Object.keys(categories).map((indexStr) => (
+
+ {categories[parseInt(indexStr)]}
+
+ ))}
+
+
+
+
+
+
+ );
}
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}>
- ) => setSelectGroups(value)}
- style={{width: '80%'}}>
- {
- Object.keys(groups).filter(groupNumber => groupNumber !== currentGroupNumber)
- .map((groupNumber) =>
-
- {`${groupNumber} - ${groups[groupNumber].name}`}
- )
- }
-
+ };
+ return (
+ setShowModal(false)}
+ onOk={handleOk}
+ >
+ ) => setSelectGroups(value)}
+ style={{ width: "80%" }}
+ >
+ {Object.keys(groups)
+ .filter((groupNumber) => groupNumber !== currentGroupNumber)
+ .map((groupNumber) => (
+
+ {`${groupNumber} - ${groups[groupNumber].name}`}
+
+ ))}
+
+ );
}
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 (
-
-
-
-
-
-
-
-
- {
- tab === 'manage' ?
-
- : null
- }
-
-
-
- )
+
+
+
+
+
+
+
+ {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;
}