web端更新功能
This commit is contained in:
parent
90efd5ae46
commit
5f93bcf721
|
@ -106,6 +106,8 @@ var (
|
|||
config = Config{
|
||||
Model: 1,
|
||||
}
|
||||
|
||||
configPath = "./config/config.yml"
|
||||
)
|
||||
|
||||
//go:embed config_default.yml
|
||||
|
@ -154,7 +156,7 @@ func InitConfig(path string, restart func()) {
|
|||
if path == "" {
|
||||
path = "./config/config.yml"
|
||||
}
|
||||
|
||||
configPath = path
|
||||
pathDir := strings.TrimSuffix(path, "config.yml")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(pathDir)
|
||||
|
@ -177,6 +179,7 @@ func InitConfig(path string, restart func()) {
|
|||
viper.SetDefault("scheme", "https://johlanse.github.io/study_xxqg/scheme.html?")
|
||||
viper.SetDefault("special_min_score", 10)
|
||||
viper.SetDefault("tg.custom_api", "https://api.telegram.org")
|
||||
viper.AutomaticEnv()
|
||||
err := viper.Unmarshal(&config, func(decoderConfig *mapstructure.DecoderConfig) {
|
||||
|
||||
})
|
||||
|
@ -228,3 +231,23 @@ func InitConfig(path string, restart func()) {
|
|||
func GetConfig() Config {
|
||||
return config
|
||||
}
|
||||
|
||||
// GetConfigFile
|
||||
/* @Description:
|
||||
* @return string
|
||||
*/
|
||||
func GetConfigFile() string {
|
||||
file, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(file)
|
||||
}
|
||||
|
||||
func SaveConfigFile(data string) error {
|
||||
err := os.WriteFile(configPath, []byte(data), 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
2
go.sum
2
go.sum
|
@ -176,6 +176,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALr
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
|
||||
github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
|
|
@ -6,6 +6,68 @@ import (
|
|||
"github.com/johlanse/study_xxqg/conf"
|
||||
)
|
||||
|
||||
func configFileGet() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
level := ctx.GetInt("level")
|
||||
if level != 1 {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 403,
|
||||
Message: "",
|
||||
Data: nil,
|
||||
Success: false,
|
||||
Error: "",
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "获取成功",
|
||||
Data: conf.GetConfigFile(),
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func configFileSet() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
level := ctx.GetInt("level")
|
||||
if level != 1 {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 403,
|
||||
Message: "",
|
||||
Data: nil,
|
||||
Success: false,
|
||||
Error: "",
|
||||
})
|
||||
return
|
||||
}
|
||||
var body map[string]string
|
||||
_ = ctx.ShouldBindJSON(&body)
|
||||
|
||||
err := conf.SaveConfigFile(body["data"])
|
||||
if err != nil {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 503,
|
||||
Message: "",
|
||||
Data: nil,
|
||||
Success: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "获取成功",
|
||||
Data: conf.GetConfigFile(),
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func configGet() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
level := ctx.GetInt("level")
|
||||
|
|
|
@ -105,6 +105,8 @@ func RouterInit() *gin.Engine {
|
|||
|
||||
config.GET("", configGet())
|
||||
config.POST("", configSet())
|
||||
config.GET("/file", configFileGet())
|
||||
config.POST("/file", configFileSet())
|
||||
|
||||
// 对用户管理的组
|
||||
user := router.Group("/user", check())
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "/static/xxqg/build/static/css/main.6f1e3389.css",
|
||||
"main.js": "/static/xxqg/build/static/js/main.dc0d1572.js",
|
||||
"main.css": "/static/xxqg/build/static/css/main.00055dce.css",
|
||||
"main.js": "/static/xxqg/build/static/js/main.e7a49c3d.js",
|
||||
"static/js/787.273d6ce9.chunk.js": "/static/xxqg/build/static/js/787.273d6ce9.chunk.js",
|
||||
"static/media/codicon.ttf": "/static/xxqg/build/static/media/codicon.b797181c93b3755f4fa1.ttf",
|
||||
"index.html": "/static/xxqg/build/index.html",
|
||||
"main.6f1e3389.css.map": "/static/xxqg/build/static/css/main.6f1e3389.css.map",
|
||||
"main.dc0d1572.js.map": "/static/xxqg/build/static/js/main.dc0d1572.js.map",
|
||||
"main.00055dce.css.map": "/static/xxqg/build/static/css/main.00055dce.css.map",
|
||||
"main.e7a49c3d.js.map": "/static/xxqg/build/static/js/main.e7a49c3d.js.map",
|
||||
"787.273d6ce9.chunk.js.map": "/static/xxqg/build/static/js/787.273d6ce9.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.6f1e3389.css",
|
||||
"static/js/main.dc0d1572.js"
|
||||
"static/css/main.00055dce.css",
|
||||
"static/js/main.e7a49c3d.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/static/xxqg/build/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/static/xxqg/build/logo192.png"/><link rel="manifest" href="/static/xxqg/build/manifest.json"/><title>Study XXQG</title><script defer="defer" src="/static/xxqg/build/static/js/main.dc0d1572.js"></script><link href="/static/xxqg/build/static/css/main.6f1e3389.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/static/xxqg/build/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/static/xxqg/build/logo192.png"/><link rel="manifest" href="/static/xxqg/build/manifest.json"/><title>Study XXQG</title><script defer="defer" src="/static/xxqg/build/static/js/main.e7a49c3d.js"></script><link href="/static/xxqg/build/static/css/main.00055dce.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -10,6 +10,8 @@ object-assign
|
|||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */
|
||||
|
||||
/**
|
||||
* @license qrcode.react
|
||||
* Copyright (c) Paul O'Shannessy
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
|
||||
module.exports = function override(config, env) {
|
||||
config.plugins.push(new MonacoWebpackPlugin({
|
||||
languages: ['json']
|
||||
}));
|
||||
return config;
|
||||
}
|
|
@ -13,9 +13,11 @@
|
|||
"antd-mobile": "^5.10.2",
|
||||
"axios": "^0.26.1",
|
||||
"http-proxy-middleware": "^2.0.5",
|
||||
"monaco-editor": "^0.34.0",
|
||||
"qrcode.react": "^3.0.1",
|
||||
"react": "17.x",
|
||||
"react-dom": "17.x",
|
||||
"react-monaco-editor": "^0.50.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-uuid": "^1.0.2",
|
||||
"typescript": "^4.6.3",
|
||||
|
@ -46,7 +48,4 @@
|
|||
]
|
||||
},
|
||||
"homepage": "/static/xxqg/build/"
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React, {Component} from 'react';
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import {Button, Dialog, Divider, Form, Input, List, Modal, NavBar, Popup, TextArea, Toast,} from "antd-mobile";
|
||||
import {List, NavBar, Popup,} from "antd-mobile";
|
||||
import {UnorderedListOutline} from "antd-mobile-icons";
|
||||
import {ListItem} from "antd-mobile/es/components/list/list-item";
|
||||
import {checkQrCode, getLog, getScore, getToken, getUsers, getLink, stopStudy, study, login, checkToken,getAbout,deleteUser, getExpiredUsers} from "./utils/api";
|
||||
import QrCode from 'qrcode.react';
|
||||
import {checkToken} from "./utils/api";
|
||||
import Login from './compents/Login';
|
||||
import Router from './compents/Router';
|
||||
|
||||
|
||||
class App extends React.Component<any, any> {
|
||||
|
@ -23,12 +24,54 @@ class App extends React.Component<any, any> {
|
|||
this.setState({
|
||||
level: level
|
||||
})
|
||||
|
||||
this.items.push(
|
||||
{
|
||||
"key":"config",
|
||||
"text":"配置管理"
|
||||
}
|
||||
)
|
||||
this.items.push(
|
||||
{
|
||||
"key":"log",
|
||||
"text":"日志查看"
|
||||
}
|
||||
)
|
||||
this.items.push(
|
||||
{
|
||||
"key":"other",
|
||||
"text":"其他功能"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
this.items.map((value, index, array)=>{
|
||||
if (value.key === "config" || value.key === "log"){
|
||||
this.elements.push(
|
||||
|
||||
<ListItem disabled={this.state.level === 2} onClick={() => {
|
||||
this.setState({"index": value.key})
|
||||
}}>{value.text}</ListItem>
|
||||
);
|
||||
}else {
|
||||
this.elements.push(
|
||||
|
||||
<ListItem onClick={() => {
|
||||
this.setState({"index": value.key})
|
||||
}}>{value.text}</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
}
|
||||
set_login = () => {
|
||||
this.setState({
|
||||
is_login: true
|
||||
})
|
||||
this.check_token()
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
check_token = () => {
|
||||
|
@ -53,8 +96,22 @@ class App extends React.Component<any, any> {
|
|||
|
||||
componentDidMount() {
|
||||
this.check_token()
|
||||
|
||||
|
||||
}
|
||||
|
||||
elements:any = []
|
||||
items = [
|
||||
{
|
||||
"key":"login",
|
||||
"text":"添加用户"
|
||||
},
|
||||
{
|
||||
"key":"user_list",
|
||||
"text":"用户管理"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
|
@ -74,11 +131,11 @@ class App extends React.Component<any, any> {
|
|||
})}>
|
||||
<h1 style={{textAlign: "center"}}>XXQG</h1>
|
||||
<List>
|
||||
<ListItem onClick={()=>{this.setState({"index":"login"})}}>添加用户</ListItem>
|
||||
<ListItem onClick={()=>{this.setState({"index":"user_list"})}}>用户管理</ListItem>
|
||||
<ListItem onClick={()=>{this.setState({"index":"config"})}}>配置管理</ListItem>
|
||||
<ListItem onClick={()=>{this.setState({"index":"log"})}}>日志查看</ListItem>
|
||||
<ListItem onClick={()=>{this.setState({"index":"help"})}}>帮助</ListItem>
|
||||
{this.elements}
|
||||
<ListItem onClick={() => {
|
||||
window.localStorage.removeItem("xxqg_token")
|
||||
this.setState({"index": "help"})
|
||||
}}>帮助</ListItem>
|
||||
<ListItem onClick={() => {
|
||||
window.localStorage.removeItem("xxqg_token")
|
||||
this.setState({
|
||||
|
@ -97,7 +154,6 @@ class App extends React.Component<any, any> {
|
|||
}
|
||||
|
||||
|
||||
|
||||
back = () => {
|
||||
this.setState({
|
||||
popup_visible: true,
|
||||
|
@ -107,362 +163,4 @@ class App extends React.Component<any, any> {
|
|||
}
|
||||
|
||||
|
||||
class Login extends Component<any, any>{
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
img : "你还未获取登录链接"
|
||||
};
|
||||
}
|
||||
|
||||
onFinish = (value:string)=>{
|
||||
login(JSON.stringify(value)).then(resp => {
|
||||
console.log(resp.message)
|
||||
Dialog.alert({content: resp.message,closeOnMaskClick:false})
|
||||
if (resp.success){
|
||||
window.localStorage.setItem("xxqg_token",resp.data)
|
||||
this.props.parent.set_login()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<Form
|
||||
onFinish = {this.onFinish}
|
||||
footer={
|
||||
<Button block type='submit' color='primary' size='large'>
|
||||
登录
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form.Header><h1>XXQG 登录页</h1></Form.Header>
|
||||
<Form.Item name='account' label='账号' rules={[{ required: true }]}>
|
||||
<Input placeholder='请输入账号' />
|
||||
</Form.Item>
|
||||
<Form.Item name='password' label='密码' rules={[{ required: true }]}>
|
||||
<Input placeholder='请输入密码' type={"password"}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Router extends Component<any, any>{
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
img : "你还未获取登录链接"
|
||||
};
|
||||
}
|
||||
|
||||
isWechat = ()=> {
|
||||
if (/MicroMessenger/i.test(window.navigator.userAgent)){
|
||||
return "inline"
|
||||
}else {
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let login = <>
|
||||
<h2 style={{margin:10,color:"red",display:this.isWechat()}}>当前环境为微信环境,请点击右上角在浏览器中打开</h2>
|
||||
<Button onClick={this.click} color={"primary"} style={{margin:10,marginRight:10}} block>生成链接</Button>
|
||||
<QrCode style={{margin:10}} fgColor={"#000000"} size={200} value={this.state.img} />
|
||||
</>;
|
||||
|
||||
let userList = <Users data={"12"} level={this.props.level}/>;
|
||||
let config = <h1>配置管理</h1>
|
||||
let help = <Help />
|
||||
let log = <Log />
|
||||
if (this.props.data === "login"){
|
||||
return login;
|
||||
}else if (this.props.data === "user_list"){
|
||||
return userList;
|
||||
}else if (this.props.data === "help"){
|
||||
return help;
|
||||
} else if (this.props.data === "log"){
|
||||
return log;
|
||||
} else {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.check !== undefined){
|
||||
clearInterval(this.state.check)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
click = async () => {
|
||||
let data = await getLink()
|
||||
|
||||
this.setState({
|
||||
img: data.url
|
||||
})
|
||||
let check = setInterval(async ()=>{
|
||||
let resp = await checkQrCode(data.code);
|
||||
if (resp.success){
|
||||
clearInterval(check)
|
||||
console.log("登录成功")
|
||||
console.log(resp.data)
|
||||
|
||||
let token = await getToken(resp.data.split("=")[1],data.sign)
|
||||
console.log(token)
|
||||
if (token.success){
|
||||
Toast.show("登录成功")
|
||||
}
|
||||
|
||||
}
|
||||
},5000)
|
||||
this.setState({
|
||||
check: check
|
||||
})
|
||||
setTimeout(()=>{
|
||||
clearInterval(check)
|
||||
},1000*300)
|
||||
|
||||
let element = document.createElement("a");
|
||||
element.href = "dtxuexi://appclient/page/study_feeds?url="+escape(data.url)
|
||||
element.click()
|
||||
}
|
||||
}
|
||||
|
||||
class Log extends Component<any, any>{
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data : ""
|
||||
}
|
||||
}
|
||||
|
||||
reverse = ( str:string ):string=>{
|
||||
return str.split("\n").reverse().join("\n").trim()
|
||||
};
|
||||
|
||||
timer: any
|
||||
|
||||
componentDidMount() {
|
||||
getLog().then(data=>{
|
||||
this.setState({
|
||||
data:this.reverse(data)
|
||||
})
|
||||
})
|
||||
this.timer = setInterval(()=>{
|
||||
getLog().then((data:string)=>{
|
||||
this.setState({
|
||||
data:this.reverse(data)
|
||||
})
|
||||
})
|
||||
},30000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<TextArea style={{margin:10}} autoSize disabled={true} value={this.state.data}/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
class Help extends Component<any, any> {
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
about: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getAbout().then((value)=>{
|
||||
this.setState({
|
||||
about:value.data
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
render() {
|
||||
return <>
|
||||
<h2 style={{margin:10}}>项目地址:<a href="https://github.com/johlanse/study_xxqg">https://github.com/johlanse/study_xxqg</a></h2>
|
||||
<br/><h2 style={{margin:10}}>{this.state.about}</h2>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
class Users extends Component<any, any>{
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
users:[],
|
||||
expired_users:[]
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
|
||||
getExpiredUsers().then(users => {
|
||||
console.log(users)
|
||||
if (users.data !== null){
|
||||
this.setState({
|
||||
expired_users: users.data
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
format = (value:any)=> {
|
||||
const date = new Date(value*1000);
|
||||
let y = date.getFullYear(),
|
||||
m = date.getMonth() + 1,
|
||||
d = date.getDate(),
|
||||
h = date.getHours(),
|
||||
i = date.getMinutes(),
|
||||
s = date.getSeconds();
|
||||
if (m < 10) { m = parseInt('0') + m; }
|
||||
if (d < 10) { d = parseInt('0') + d; }
|
||||
if (h < 10) { h = parseInt('0') + h; }
|
||||
if (i < 10) { i = parseInt('0') + i; }
|
||||
if (s < 10) { s = parseInt('0') + s; }
|
||||
return y + '-' + m + '-' + d + ' ' + h + ':' + i + ':' + s;
|
||||
}
|
||||
|
||||
getScore = (token:string,nick:string)=>{
|
||||
getScore(token).then((data)=>{
|
||||
console.log(data)
|
||||
Modal.alert({
|
||||
title: nick,
|
||||
content: data.data,
|
||||
closeOnMaskClick: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
checkStudy = (is_study:boolean)=>{
|
||||
if (is_study){
|
||||
return "停止学习"
|
||||
}else {
|
||||
return "开始学习"
|
||||
}
|
||||
}
|
||||
|
||||
checkStudyColor = (is_study:boolean)=>{
|
||||
if (is_study){
|
||||
return "danger"
|
||||
}else {
|
||||
return "primary"
|
||||
}
|
||||
}
|
||||
|
||||
study = (uid:string,is_study:boolean) =>{
|
||||
if (!is_study){
|
||||
study(uid).then(()=>{
|
||||
Toast.show("开始学习成功")
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
})
|
||||
}else {
|
||||
stopStudy(uid).then(()=>{
|
||||
Toast.show("已停止学习")
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete_user = (uid:string,nick:string)=>{
|
||||
Dialog.confirm({content:"你确定要删除用户"+nick+"吗?"}).then((confirm) => {
|
||||
if (confirm){
|
||||
deleteUser(uid).then((data) => {
|
||||
if (data.success){
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
if (users.data != null){
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}else {
|
||||
Dialog.show({content:data.error})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
let elements = []
|
||||
for (let i = 0; i < this.state.users.length; i++) {
|
||||
console.log(this.props.level)
|
||||
elements.push(
|
||||
<>
|
||||
<ListItem key={this.state.users[i].uid} style={{border:"blue soild 1px"}}>
|
||||
<h3>姓名:{this.state.users[i].nick}</h3>
|
||||
<h3>UID: {this.state.users[i].uid}</h3>
|
||||
<h3>登录时间:{this.format(this.state.users[i].login_time)}</h3>
|
||||
<Button onClick={this.study.bind(this,this.state.users[i].uid,this.state.users[i].is_study)} color={this.checkStudyColor(this.state.users[i].is_study)} block={true}>
|
||||
{this.checkStudy(this.state.users[i].is_study)}
|
||||
</Button>
|
||||
<br />
|
||||
<Button onClick={this.getScore.bind(this,this.state.users[i].token,this.state.users[i].nick)} color={"success"} block={true}>积分查询</Button>
|
||||
<br />
|
||||
<Button style={{display: this.props.level !== 1 ? "none" : "inline"}} onClick={this.delete_user.bind(this,this.state.users[i].uid,this.state.users[i].nick)} color={"danger"} block={true}>删除用户</Button>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.state.expired_users.length; i++) {
|
||||
console.log(this.state.expired_users[i].uid)
|
||||
elements.push(
|
||||
<>
|
||||
<ListItem key={this.state.expired_users[i].uid} style={{border:"blue soild 1px",backgroundColor:"#cdced0"}}>
|
||||
<h3>姓名:{this.state.expired_users[i].nick}</h3>
|
||||
<h3>UID: {this.state.expired_users[i].uid}</h3>
|
||||
<h3>登录时间:{this.format(this.state.expired_users[i].login_time)}</h3>
|
||||
<Button style={{display: this.props.level !== 1 ? "none" : "inline"}} onClick={this.delete_user.bind(this,this.state.expired_users[i].uid,this.state.expired_users[i].nick)} color={"danger"} block={true}>删除用户</Button>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.state.users.length === 0){
|
||||
elements.push(<>
|
||||
<span style={{color:"red"}}>未获取到有效用户</span>
|
||||
</>)
|
||||
}
|
||||
|
||||
return <List>{elements}</List>;
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import React, {Component} from "react";
|
||||
import {getConfig, saveConfig} from "../utils/api";
|
||||
import MonacoEditor from 'react-monaco-editor';
|
||||
import {Button, Dialog, Toast} from "antd-mobile";
|
||||
class Config extends Component<any, any>{
|
||||
private monaco: React.RefObject<MonacoEditor>;
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.monaco = React.createRef<MonacoEditor>()
|
||||
this.state = {
|
||||
config: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getConfig().then((value)=>{
|
||||
this.setState({
|
||||
config:value.data
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
editorDidMount = (editor:any, monaco:any) => {
|
||||
console.log('editorDidMount', editor);
|
||||
editor.focus();
|
||||
}
|
||||
onChange = (newValue:any, e:any)=> {
|
||||
|
||||
}
|
||||
|
||||
onSave = ()=> {
|
||||
// @ts-ignore
|
||||
let data = this.monaco.current.editor?.getModel().getValue()
|
||||
saveConfig(data).then(resp => {
|
||||
if (resp.code === 200){
|
||||
Toast.show("保存成功")
|
||||
}else {
|
||||
Dialog.show({content:resp.err})
|
||||
}
|
||||
})
|
||||
}
|
||||
render() {
|
||||
const options = {
|
||||
selectOnLineNumbers: true
|
||||
};
|
||||
return <>
|
||||
<Button style={{margin:10,marginRight:30}} onClick={this.onSave} color={"primary"} block={true}>保存配置</Button><br/>
|
||||
<MonacoEditor
|
||||
ref={this.monaco}
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
language="yaml"
|
||||
theme="vs"
|
||||
value={this.state.config}
|
||||
options={options}
|
||||
onChange={this.onChange}
|
||||
editorDidMount={this.editorDidMount}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Config
|
|
@ -0,0 +1,29 @@
|
|||
import React, {Component} from "react";
|
||||
import {getAbout} from "../utils/api";
|
||||
|
||||
class Help extends Component<any, any> {
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
about: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getAbout().then((value)=>{
|
||||
this.setState({
|
||||
about:value.data
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
render() {
|
||||
return <>
|
||||
<h2 style={{margin:10}}>项目地址:<a href="https://github.com/johlanse/study_xxqg">https://github.com/johlanse/study_xxqg</a></h2>
|
||||
<br/><h2 style={{margin:10}}>{this.state.about}</h2>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
export default Help
|
|
@ -0,0 +1,46 @@
|
|||
import React, {Component} from "react";
|
||||
import {getLog} from "../utils/api";
|
||||
import {TextArea} from "antd-mobile";
|
||||
|
||||
class Log extends Component<any, any>{
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data : ""
|
||||
}
|
||||
}
|
||||
|
||||
reverse = ( str:string ):string=>{
|
||||
return str.split("\n").reverse().join("\n").trim()
|
||||
};
|
||||
|
||||
timer: any
|
||||
|
||||
componentDidMount() {
|
||||
getLog().then(data=>{
|
||||
this.setState({
|
||||
data:this.reverse(data)
|
||||
})
|
||||
})
|
||||
this.timer = setInterval(()=>{
|
||||
getLog().then((data:string)=>{
|
||||
this.setState({
|
||||
data:this.reverse(data)
|
||||
})
|
||||
})
|
||||
},30000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<TextArea style={{margin:10}} autoSize disabled={true} value={this.state.data}/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
export default Log
|
|
@ -0,0 +1,47 @@
|
|||
import React, {Component} from "react";
|
||||
import {login} from "../utils/api";
|
||||
import {Button, Dialog, Form, Input} from "antd-mobile";
|
||||
|
||||
class Login extends Component<any, any>{
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
img : "你还未获取登录链接"
|
||||
};
|
||||
}
|
||||
|
||||
onFinish = (value:string)=>{
|
||||
login(JSON.stringify(value)).then(resp => {
|
||||
console.log(resp.message)
|
||||
Dialog.alert({content: resp.message,closeOnMaskClick:false})
|
||||
if (resp.success){
|
||||
window.localStorage.setItem("xxqg_token",resp.data)
|
||||
this.props.parent.set_login()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<Form
|
||||
onFinish = {this.onFinish}
|
||||
footer={
|
||||
<Button block type='submit' color='primary' size='large'>
|
||||
登录
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form.Header><h1>XXQG 登录页</h1></Form.Header>
|
||||
<Form.Item name='account' label='账号' rules={[{ required: true }]}>
|
||||
<Input placeholder='请输入账号' />
|
||||
</Form.Item>
|
||||
<Form.Item name='password' label='密码' rules={[{ required: true }]}>
|
||||
<Input placeholder='请输入密码' type={"password"}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Login
|
|
@ -0,0 +1,20 @@
|
|||
import React, {Component} from "react";
|
||||
import {Button, Toast} from "antd-mobile";
|
||||
import {restart} from "../utils/api";
|
||||
|
||||
class Other extends Component<any, any>{
|
||||
|
||||
onrestart = ()=>{
|
||||
restart().then(r => {
|
||||
|
||||
});
|
||||
Toast.show("重启完成")
|
||||
}
|
||||
render() {
|
||||
return <>
|
||||
<Button style={{margin:10,marginRight:30}} onClick={this.onrestart} color={"primary"} block={true}>重启程序</Button><br/>
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Other
|
|
@ -0,0 +1,96 @@
|
|||
import React, {Component} from "react";
|
||||
import {Button, Toast} from "antd-mobile";
|
||||
import QrCode from "qrcode.react";
|
||||
import Users from "./User";
|
||||
import Help from "./Help";
|
||||
import Log from "./Log";
|
||||
import {checkQrCode, getLink, getToken} from "../utils/api";
|
||||
import Config from "./Config";
|
||||
import Other from "./Other";
|
||||
|
||||
class Router extends Component<any, any>{
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
img : "你还未获取登录链接"
|
||||
};
|
||||
}
|
||||
|
||||
isWechat = ()=> {
|
||||
if (/MicroMessenger/i.test(window.navigator.userAgent)){
|
||||
return "inline"
|
||||
}else {
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let login = <>
|
||||
<h2 style={{margin:10,color:"red",display:this.isWechat()}}>当前环境为微信环境,请点击右上角在浏览器中打开</h2>
|
||||
<Button onClick={this.click} color={"primary"} style={{margin:10,marginRight:10}} block>生成链接</Button>
|
||||
<QrCode style={{margin:10}} fgColor={"#000000"} size={200} value={this.state.img} />
|
||||
</>;
|
||||
|
||||
let userList = <Users data={"12"} level={this.props.level}/>;
|
||||
let config = <Config />
|
||||
let help = <Help />
|
||||
let log = <Log />
|
||||
if (this.props.data === "login"){
|
||||
return login;
|
||||
}else if (this.props.data === "user_list"){
|
||||
return userList;
|
||||
}else if (this.props.data === "help"){
|
||||
return help;
|
||||
} else if (this.props.data === "log"){
|
||||
return log;
|
||||
}else if (this.props.data === "other") {
|
||||
return <Other />
|
||||
}
|
||||
else {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.check !== undefined){
|
||||
clearInterval(this.state.check)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
click = async () => {
|
||||
let data = await getLink()
|
||||
|
||||
this.setState({
|
||||
img: data.url
|
||||
})
|
||||
let check = setInterval(async ()=>{
|
||||
let resp = await checkQrCode(data.code);
|
||||
if (resp.success){
|
||||
clearInterval(check)
|
||||
console.log("登录成功")
|
||||
console.log(resp.data)
|
||||
|
||||
let token = await getToken(resp.data.split("=")[1],data.sign)
|
||||
console.log(token)
|
||||
if (token.success){
|
||||
Toast.show("登录成功")
|
||||
}
|
||||
|
||||
}
|
||||
},5000)
|
||||
this.setState({
|
||||
check: check
|
||||
})
|
||||
setTimeout(()=>{
|
||||
clearInterval(check)
|
||||
},1000*300)
|
||||
|
||||
let element = document.createElement("a");
|
||||
element.href = "dtxuexi://appclient/page/study_feeds?url="+escape(data.url)
|
||||
element.click()
|
||||
}
|
||||
}
|
||||
|
||||
export default Router
|
|
@ -0,0 +1,173 @@
|
|||
import React, {Component} from "react";
|
||||
import {deleteUser, getExpiredUsers, getScore, getUsers, stopStudy, study} from "../utils/api";
|
||||
import {Button, Dialog, Divider, List, Modal, Toast} from "antd-mobile";
|
||||
import {ListItem} from "antd-mobile/es/components/list/list-item";
|
||||
|
||||
class Users extends Component<any, any>{
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
users:[],
|
||||
expired_users:[]
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
|
||||
getExpiredUsers().then(users => {
|
||||
console.log(users)
|
||||
if (users.data !== null){
|
||||
this.setState({
|
||||
expired_users: users.data
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
format = (value:any)=> {
|
||||
const date = new Date(value*1000);
|
||||
let y = date.getFullYear(),
|
||||
m = date.getMonth() + 1,
|
||||
d = date.getDate(),
|
||||
h = date.getHours(),
|
||||
i = date.getMinutes(),
|
||||
s = date.getSeconds();
|
||||
if (m < 10) { m = parseInt('0') + m; }
|
||||
if (d < 10) { d = parseInt('0') + d; }
|
||||
if (h < 10) { h = parseInt('0') + h; }
|
||||
if (i < 10) { i = parseInt('0') + i; }
|
||||
if (s < 10) { s = parseInt('0') + s; }
|
||||
return y + '-' + m + '-' + d + ' ' + h + ':' + i + ':' + s;
|
||||
}
|
||||
|
||||
getScore = (token:string,nick:string)=>{
|
||||
getScore(token).then((data)=>{
|
||||
console.log(data)
|
||||
Modal.alert({
|
||||
title: nick,
|
||||
content: data.data,
|
||||
closeOnMaskClick: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
checkStudy = (is_study:boolean)=>{
|
||||
if (is_study){
|
||||
return "停止学习"
|
||||
}else {
|
||||
return "开始学习"
|
||||
}
|
||||
}
|
||||
|
||||
checkStudyColor = (is_study:boolean)=>{
|
||||
if (is_study){
|
||||
return "danger"
|
||||
}else {
|
||||
return "primary"
|
||||
}
|
||||
}
|
||||
|
||||
study = (uid:string,is_study:boolean) =>{
|
||||
if (!is_study){
|
||||
study(uid).then(()=>{
|
||||
Toast.show("开始学习成功")
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
})
|
||||
}else {
|
||||
stopStudy(uid).then(()=>{
|
||||
Toast.show("已停止学习")
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete_user = (uid:string,nick:string)=>{
|
||||
Dialog.confirm({content:"你确定要删除用户"+nick+"吗?"}).then((confirm) => {
|
||||
if (confirm){
|
||||
deleteUser(uid).then((data) => {
|
||||
if (data.success){
|
||||
getUsers().then(users =>{
|
||||
console.log(users)
|
||||
if (users.data != null){
|
||||
this.setState({
|
||||
users: users.data
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}else {
|
||||
Dialog.show({content:data.error})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
let elements = []
|
||||
for (let i = 0; i < this.state.users.length; i++) {
|
||||
console.log(this.props.level)
|
||||
elements.push(
|
||||
<>
|
||||
<ListItem key={this.state.users[i].uid} style={{border:"blue soild 1px"}}>
|
||||
<h3>姓名:{this.state.users[i].nick}</h3>
|
||||
<h3>UID: {this.state.users[i].uid}</h3>
|
||||
<h3>登录时间:{this.format(this.state.users[i].login_time)}</h3>
|
||||
<Button onClick={this.study.bind(this,this.state.users[i].uid,this.state.users[i].is_study)} color={this.checkStudyColor(this.state.users[i].is_study)} block={true}>
|
||||
{this.checkStudy(this.state.users[i].is_study)}
|
||||
</Button>
|
||||
<br />
|
||||
<Button onClick={this.getScore.bind(this,this.state.users[i].token,this.state.users[i].nick)} color={"success"} block={true}>积分查询</Button>
|
||||
<br />
|
||||
<Button style={{display: this.props.level !== 1 ? "none" : "inline"}} onClick={this.delete_user.bind(this,this.state.users[i].uid,this.state.users[i].nick)} color={"danger"} block={true}>删除用户</Button>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.state.expired_users.length; i++) {
|
||||
console.log(this.state.expired_users[i].uid)
|
||||
elements.push(
|
||||
<>
|
||||
<ListItem key={this.state.expired_users[i].uid} style={{border:"blue soild 1px",backgroundColor:"#cdced0"}}>
|
||||
<h3>姓名:{this.state.expired_users[i].nick}</h3>
|
||||
<h3>UID: {this.state.expired_users[i].uid}</h3>
|
||||
<h3>登录时间:{this.format(this.state.expired_users[i].login_time)}</h3>
|
||||
<Button style={{display: this.props.level !== 1 ? "none" : "inline"}} onClick={this.delete_user.bind(this,this.state.expired_users[i].uid,this.state.expired_users[i].nick)} color={"danger"} block={true}>删除用户</Button>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
if (this.state.users.length === 0){
|
||||
elements.push(<>
|
||||
<span style={{color:"red"}}>未获取到有效用户</span>
|
||||
</>)
|
||||
}
|
||||
|
||||
return <List>{elements}</List>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Users
|
|
@ -73,6 +73,22 @@ export async function getUsers(){
|
|||
return resp.data
|
||||
}
|
||||
|
||||
export async function getConfig() {
|
||||
let resp = await http.get(base+"/config/file");
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function restart() {
|
||||
let resp = await http.post(base+"/restart");
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function saveConfig(data) {
|
||||
let resp = await http.post(base+"/config/file",{
|
||||
"data":data
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function getExpiredUsers(){
|
||||
let resp = await http.get(base+"/user/expired");
|
||||
|
|
|
@ -5999,6 +5999,11 @@ mkdirp@^0.5.5, mkdirp@~0.5.1:
|
|||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
monaco-editor@^0.34.0:
|
||||
version "0.34.0"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.34.0.tgz#b1749870a1f795dbfc4dc03d8e9b646ddcbeefa7"
|
||||
integrity sha512-VF+S5zG8wxfinLKLrWcl4WUizMx+LeJrG4PM/M78OhcwocpV0jiyhX/pG6Q9jIOhrb/ckYi6nHnaR5OojlOZCQ==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
@ -7185,6 +7190,13 @@ react-is@^17.0.1, react-is@^17.0.2:
|
|||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-monaco-editor@^0.50.1:
|
||||
version "0.50.1"
|
||||
resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.50.1.tgz#3b68ce03e4b316f435b9bcd8ec13ffd4c050c605"
|
||||
integrity sha512-qvYdJhQdkPIrPDMxCrEl0T2x9TfBB+aUbrpFv69FwoTybeyfAjxgJ219MYSsgn3c/g06BgyLX4ENrXM4niZ9ag==
|
||||
dependencies:
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-refresh@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
||||
|
|
Loading…
Reference in New Issue