web端支持管理员用户和普通用户区分,支持删除用户
This commit is contained in:
parent
d858723b2d
commit
ba767ad611
|
@ -46,7 +46,7 @@ type Config struct {
|
|||
Password string `json:"password" yaml:"password" mapstructure:"password"`
|
||||
Host string `json:"host" yaml:"host" mapstructure:"host"`
|
||||
Port int `json:"port" yaml:"port" mapstructure:"port"`
|
||||
Announcement string `json:"announcement" yaml:"announcement" mapstructure:"announcement"`
|
||||
CommonUser map[string]string `json:"common_user" mapstructure:"common_user"`
|
||||
} `json:"web" mapstructure:"web"`
|
||||
Cron string `json:"cron" yaml:"cron" mapstructure:"cron"`
|
||||
CronRandomWait int `json:"cron_random_wait" yaml:"cron_random_wait" mapstructure:"cron_random_wait"`
|
||||
|
@ -89,12 +89,9 @@ type Config struct {
|
|||
AppKey string `json:"app_key" yaml:"app_key" mapstructure:"app_key"`
|
||||
} `json:"ji_guang_push" yaml:"ji_guang_push" mapstructure:"ji_guang_push"`
|
||||
|
||||
SuperUser string `json:"super_user" yaml:"super_user" mapstructure:"super_user"`
|
||||
SuperPassword string `json:"super_password" yaml:"super_password" mapstructure:"super_password"`
|
||||
|
||||
// github的代理地址,用于检查更新或者其他的
|
||||
GithubProxy string `json:"github_proxy" yaml:"github_proxy" mapstructure:"github_proxy"`
|
||||
|
||||
// 热重载
|
||||
HotReload bool `json:"hot_reload" yaml:"hot_reload" mapstructure:"hot_reload"`
|
||||
|
||||
version string `mapstructure:"version"`
|
||||
|
|
|
@ -55,6 +55,10 @@ web:
|
|||
account: admin
|
||||
# 网页端登录密码
|
||||
password: admin
|
||||
# web端登录普通用户的账号密码,支持多个用户,普通用户只能看到自己的信息
|
||||
common_user:
|
||||
# 代表账号为user,密码为123的普通用户,可添加多个,继续在下面写就好了
|
||||
user: 123
|
||||
|
||||
# 微信公众号测试号配置
|
||||
wechat:
|
||||
|
|
|
@ -6,6 +6,16 @@
|
|||
然后查看报错内容截图并在[github](https://github.com/johlanse/study_xxqg/issues) 提交issue
|
||||
```
|
||||
|
||||
|
||||
+ ### 关于cookie的时间问题
|
||||
```yaml
|
||||
原理是是通过带上当前cookie访问一个api即可,在1.0.35版本之后我通过cron定时执行保活,默认的cron是 0 */1 * * *
|
||||
|
||||
目前暂不知道能够续期的次数
|
||||
|
||||
如果你想让访问间隔时间更短或者更长,可以通过添加环境变量 CHECK_ENV 为cron值
|
||||
```
|
||||
|
||||
+ ### 浏览器中登录不上怎么办?显示一个白条没反应
|
||||
|
||||
```yaml
|
||||
|
|
14
docs/push.md
14
docs/push.md
|
@ -81,17 +81,25 @@ web:
|
|||
host: 0.0.0.0
|
||||
# 监听的端口号 0-65535可选
|
||||
port: 8081
|
||||
# web端登录得账号
|
||||
# web端登录管理员的账号
|
||||
account:admin
|
||||
# web端登录的密码
|
||||
# web端登录管理员的密码
|
||||
password: admin
|
||||
# web端登录普通用户的账号密码,支持多个用户,普通用户只能看到自己的信息
|
||||
common_user:
|
||||
# 代表账号为user,密码为123的普通用户,可添加多个,继续在下面写就好了
|
||||
user: 123
|
||||
|
||||
# user1: 123
|
||||
# user2: 123
|
||||
|
||||
```
|
||||
|
||||
+ 开启后通过浏览器访问 *http://ip:port*或者*http://ip:port/new*即可打开网址 ,若为docker运行,则ip为宿主机公网ip,端口为docker映射到宿主机的端口
|
||||
+ 若无法访问,首先检查程序运行日志,查看有无报错,其次查看docker的运行情况,端口是否映射正常,然后可以通过curl命令检测在宿主机中能否访问,然后检查防火墙之类的
|
||||
+ 若点击登录之后出现一个小框然后无反应,则说明账户密码错误,请重新配置程序账户密码并重启程序
|
||||
|
||||
> 登录的账号密码是在配置文件中配置,不是学习强国的登录账号
|
||||
> 登录的账号密码是在配置文件中配置,不是学习强国的登录账号,管理员登录支持删除用户,同时能看到所有人的用户信息,普通用户就是```common_user```下面配置的用户,支持多个用户,键是账号,值是密码
|
||||
|
||||
### 钉钉推送
|
||||
配置config.yml的如下部分,具体使用教程详情参考[钉钉](https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f875e5903iVpC#topic-2026027)
|
||||
|
|
|
@ -4,10 +4,12 @@ package model
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/playwright-community/playwright-go"
|
||||
"github.com/robfig/cron/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/johlanse/study_xxqg/utils"
|
||||
|
@ -66,7 +68,8 @@ func Query() ([]*User, error) {
|
|||
return nil, err
|
||||
}
|
||||
if u.Status != 0 {
|
||||
if utils.CheckUserCookie(u.ToCookies()) {
|
||||
|
||||
if ok, _ := utils.CheckUserCookie(u.ToCookies()); ok {
|
||||
users = append(users, u)
|
||||
} else {
|
||||
log.Warningln(u.Nick + "的cookie已失效")
|
||||
|
@ -140,7 +143,7 @@ func QueryByPushID(pushID string) ([]*User, error) {
|
|||
return users, err
|
||||
}
|
||||
if u.Status != 0 {
|
||||
if utils.CheckUserCookie(u.ToCookies()) {
|
||||
if ok, _ := utils.CheckUserCookie(u.ToCookies()); ok {
|
||||
users = append(users, u)
|
||||
} else {
|
||||
log.Warningln(u.Nick + "的cookie已失效")
|
||||
|
@ -304,7 +307,14 @@ func check() {
|
|||
log.Errorf("%v 出现错误,%v", "auth check", err)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
c := cron.New()
|
||||
cr := "0 */1 * * *"
|
||||
if crEnv, ok := os.LookupEnv("CHECK_ENV"); ok {
|
||||
cr = crEnv
|
||||
log.Infoln("已成功自定义保活cron : " + cr)
|
||||
}
|
||||
_, err := c.AddFunc(cr, func() {
|
||||
log.Infoln("开始执行保活任务")
|
||||
users, _ := Query()
|
||||
for _, user := range users {
|
||||
response, _ := utils.GetClient().R().SetCookies(user.ToCookies()...).Get("https://pc-api.xuexi.cn/open/api/auth/check")
|
||||
|
@ -314,12 +324,16 @@ func check() {
|
|||
token = cookie.Value
|
||||
}
|
||||
}
|
||||
if token != "" {
|
||||
if token != "" && user.Token != token {
|
||||
user.Token = token
|
||||
_ = UpdateUser(user)
|
||||
log.Infoln("用户" + user.Nick + "的ck已成功保活cookie")
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Hour * time.Duration(2))
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorln("添加保活任务失败" + err.Error())
|
||||
return
|
||||
}
|
||||
c.Start()
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/guonaihong/gout"
|
||||
|
@ -20,10 +21,15 @@ func (d *Ding) Send() func(id string, kind string, message string) {
|
|||
s := TypeSecret{Secret: d.Secret, Webhook: d.Token}
|
||||
return func(id string, kind string, message string) {
|
||||
if kind == "flush" {
|
||||
|
||||
if strings.Contains(message, "login.xuexi.cn") {
|
||||
message = fmt.Sprintf("[点我登录](%v)", message)
|
||||
}
|
||||
|
||||
err := s.SendMessage(map[string]interface{}{
|
||||
"msgtype": "markdown",
|
||||
"markdown": map[string]string{
|
||||
"title": "学习强国登录",
|
||||
"title": "study_xxqg信息推送",
|
||||
"text": message,
|
||||
},
|
||||
})
|
||||
|
|
16
push/wx.go
16
push/wx.go
|
@ -211,6 +211,22 @@ func handleRestart(id string) {
|
|||
// @param message
|
||||
//
|
||||
func sendMsg(id, message string) {
|
||||
// 登录消息单独采用模板发送
|
||||
if strings.Contains(message, "login.xuexi.cn") {
|
||||
_, err := wx.SendTemplateMessage(&mp.TemplateMessage{
|
||||
ToUser: id,
|
||||
TemplateId: conf.GetConfig().Wechat.LoginTempID,
|
||||
URL: message,
|
||||
TopColor: "",
|
||||
RawJSONData: nil,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorln(err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m := map[string]interface{}{
|
||||
"data": map[string]string{
|
||||
"value": message,
|
||||
|
|
|
@ -48,17 +48,17 @@ func GetAbout() string {
|
|||
* @param user
|
||||
* @return bool
|
||||
*/
|
||||
func CheckUserCookie(cookies []*http.Cookie) bool {
|
||||
func CheckUserCookie(cookies []*http.Cookie) (bool, error) {
|
||||
client := req.C().DevMode()
|
||||
response, err := client.R().SetCookies(cookies...).Get("https://pc-api.xuexi.cn/open/api/score/get")
|
||||
if err != nil {
|
||||
log.Errorln("获取用户总分错误" + err.Error())
|
||||
return false
|
||||
return true, err
|
||||
}
|
||||
if !gjson.GetBytes(response.Bytes(), "ok").Bool() {
|
||||
return false
|
||||
return false, err
|
||||
}
|
||||
return true
|
||||
return true, err
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -37,7 +37,15 @@ func checkToken() gin.HandlerFunc {
|
|||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "",
|
||||
Data: nil,
|
||||
Data: 1,
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
} else if checkCommonUser(token) {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "",
|
||||
Data: 2,
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
|
@ -63,6 +71,14 @@ func userLogin() gin.HandlerFunc {
|
|||
_ = ctx.BindJSON(u)
|
||||
config := conf.GetConfig()
|
||||
if u.Account == config.Web.Account && u.Password == config.Web.Password {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "登录成功,尊贵的管理员用户",
|
||||
Data: utils.StrMd5(u.Account + u.Password),
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
} else if checkCommonUser(utils.StrMd5(u.Account + u.Password)) {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "登录成功",
|
||||
|
@ -163,6 +179,23 @@ func getUsers() gin.HandlerFunc {
|
|||
})
|
||||
return
|
||||
}
|
||||
level := ctx.GetInt("level")
|
||||
if level != 1 {
|
||||
users, err = model.QueryByPushID(ctx.GetString("token"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if users == nil {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 200,
|
||||
Message: "查询成功",
|
||||
Data: []interface{}{},
|
||||
Success: true,
|
||||
Error: "",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var datas []map[string]interface{}
|
||||
for _, user := range users {
|
||||
|
@ -326,6 +359,17 @@ func generate() gin.HandlerFunc {
|
|||
func deleteUser() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
uid := ctx.Query("uid")
|
||||
level := ctx.GetInt("level")
|
||||
if level != 1 {
|
||||
ctx.JSON(200, Resp{
|
||||
Code: 401,
|
||||
Message: "你没有权限删除用户!",
|
||||
Data: "",
|
||||
Success: false,
|
||||
Error: "你没有权限删除用户!",
|
||||
})
|
||||
return
|
||||
}
|
||||
err := model.DeleteUser(uid)
|
||||
if err != nil {
|
||||
ctx.JSON(200, Resp{
|
||||
|
|
|
@ -87,7 +87,7 @@ func check() gin.HandlerFunc {
|
|||
return func(ctx *gin.Context) {
|
||||
token := ctx.GetHeader("Authorization")
|
||||
token = strings.Split(token, " ")[1]
|
||||
if token == "" || (utils.StrMd5(config.Web.Account+config.Web.Password) != token) {
|
||||
if token == "" {
|
||||
ctx.JSON(401, Resp{
|
||||
Code: 401,
|
||||
Message: "the auth fail",
|
||||
|
@ -96,8 +96,33 @@ func check() gin.HandlerFunc {
|
|||
Error: "",
|
||||
})
|
||||
ctx.Abort()
|
||||
} else {
|
||||
} else if utils.StrMd5(config.Web.Account+config.Web.Password) == token {
|
||||
ctx.Set("level", 1)
|
||||
ctx.Set("token", token)
|
||||
ctx.Next()
|
||||
} else if checkCommonUser(token) {
|
||||
ctx.Set("level", 2)
|
||||
ctx.Set("token", token)
|
||||
ctx.Next()
|
||||
} else {
|
||||
ctx.JSON(401, Resp{
|
||||
Code: 401,
|
||||
Message: "the auth fail",
|
||||
Data: nil,
|
||||
Success: false,
|
||||
Error: "",
|
||||
})
|
||||
ctx.Abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkCommonUser(token string) bool {
|
||||
config := conf.GetConfig()
|
||||
for key, value := range config.Web.CommonUser {
|
||||
if token == utils.StrMd5(key+value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "/static/xxqg/build/static/css/main.6f1e3389.css",
|
||||
"main.js": "/static/xxqg/build/static/js/main.8720e9a4.js",
|
||||
"main.js": "/static/xxqg/build/static/js/main.b72f3ffd.js",
|
||||
"static/js/787.273d6ce9.chunk.js": "/static/xxqg/build/static/js/787.273d6ce9.chunk.js",
|
||||
"index.html": "/static/xxqg/build/index.html",
|
||||
"main.6f1e3389.css.map": "/static/xxqg/build/static/css/main.6f1e3389.css.map",
|
||||
"main.8720e9a4.js.map": "/static/xxqg/build/static/js/main.8720e9a4.js.map",
|
||||
"main.b72f3ffd.js.map": "/static/xxqg/build/static/js/main.b72f3ffd.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.8720e9a4.js"
|
||||
"static/js/main.b72f3ffd.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.8720e9a4.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.b72f3ffd.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>
|
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
|
@ -3,7 +3,7 @@ import './App.css';
|
|||
import {Button, Dialog, Divider, Form, Input, List, Modal, NavBar, Popup, TextArea, Toast,} 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} from "./utils/api";
|
||||
import {checkQrCode, getLog, getScore, getToken, getUsers, getLink, stopStudy, study, login, checkToken,getAbout,deleteUser} from "./utils/api";
|
||||
import QrCode from 'qrcode.react';
|
||||
import * as util from "util";
|
||||
|
||||
|
@ -14,24 +14,46 @@ class App extends React.Component<any, any> {
|
|||
this.state = {
|
||||
popup_visible: false,
|
||||
index: "login",
|
||||
is_login: false
|
||||
is_login: false,
|
||||
// 用户等级,1是管理员,2是普通用户
|
||||
level: 2
|
||||
};
|
||||
}
|
||||
|
||||
set_level = (level:number)=>{
|
||||
this.setState({
|
||||
level: level
|
||||
})
|
||||
}
|
||||
set_login = ()=>{
|
||||
this.setState({
|
||||
is_login: true
|
||||
})
|
||||
this.check_token()
|
||||
}
|
||||
|
||||
check_token = ()=>{
|
||||
checkToken().then((t) =>{
|
||||
console.log(t)
|
||||
if (!t){
|
||||
console.log("未登录")
|
||||
}else {
|
||||
if (t.data === 1){
|
||||
console.log("管理员登录")
|
||||
this.set_level(1)
|
||||
}else {
|
||||
console.log("不是管理员登录")
|
||||
this.set_level(2)
|
||||
}
|
||||
this.setState({
|
||||
is_login: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
checkToken().then((t) =>{
|
||||
console.log(t)
|
||||
if (t){
|
||||
this.set_login()
|
||||
}
|
||||
})
|
||||
|
||||
this.check_token()
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,7 +65,7 @@ class App extends React.Component<any, any> {
|
|||
left={<UnorderedListOutline fontSize={36} onClick={this.back}/>}>
|
||||
{"study_xxqg"}
|
||||
</NavBar>
|
||||
<Router data={this.state.index}/>
|
||||
<Router data={this.state.index} level={this.state.level} set_level = {this.set_level}/>
|
||||
<Popup
|
||||
bodyStyle={{width: '50vw'}}
|
||||
visible={this.state.popup_visible}
|
||||
|
@ -97,11 +119,10 @@ class Login extends Component<any, any>{
|
|||
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()
|
||||
}else {
|
||||
Dialog.alert({content: resp.message,closeOnMaskClick:false})
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -154,7 +175,7 @@ class Router extends Component<any, any>{
|
|||
<QrCode style={{margin:10}} fgColor={"#000000"} size={200} value={this.state.img} />
|
||||
</>;
|
||||
|
||||
let userList = <Users data={"12"}/>;
|
||||
let userList = <Users data={"12"} level={this.props.level}/>;
|
||||
let config = <h1>配置管理</h1>
|
||||
let help = <Help />
|
||||
let log = <Log />
|
||||
|
@ -364,10 +385,29 @@ class Users extends Component<any, any>{
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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"}}>
|
||||
|
@ -379,6 +419,8 @@ class Users extends Component<any, any>{
|
|||
</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 />
|
||||
</>
|
||||
|
|
|
@ -24,12 +24,13 @@ export async function checkToken() {
|
|||
return false
|
||||
}
|
||||
let responseData = await http.post(base + "/auth/check/"+token);
|
||||
return responseData.data.success;
|
||||
return responseData.data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export async function login(data) {
|
||||
let responseData = await http.post(base+"/auth/login",data);
|
||||
return responseData.data;
|
||||
|
@ -54,13 +55,19 @@ export async function getAbout(){
|
|||
}
|
||||
|
||||
export async function getToken(code,sign){
|
||||
let resp = await http.post(base+"/user/",{
|
||||
let token = window.localStorage.getItem("xxqg_token")
|
||||
let resp = await http.post(base+"/user?register_id="+token,{
|
||||
"code":code,
|
||||
"state":sign
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function deleteUser(uid){
|
||||
let resp = await http.delete(base+"/user?uid="+uid);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function getUsers(){
|
||||
let resp = await http.get(base+"/user");
|
||||
return resp.data
|
||||
|
|
Loading…
Reference in New Issue