mirror of https://github.com/openspug/spug
A 微信token普通用户可以通过个人中心设置
parent
eae6f0b818
commit
02d9563367
|
@ -142,14 +142,20 @@ class RoleView(AdminView):
|
||||||
|
|
||||||
|
|
||||||
class SelfView(View):
|
class SelfView(View):
|
||||||
|
def get(self, request):
|
||||||
|
data = request.user.to_dict(selects=('nickname', 'wx_token'))
|
||||||
|
return json_response(data)
|
||||||
|
|
||||||
def patch(self, request):
|
def patch(self, request):
|
||||||
form, error = JsonParser(
|
form, error = JsonParser(
|
||||||
Argument('old_password', required=False),
|
Argument('old_password', required=False),
|
||||||
Argument('new_password', required=False),
|
Argument('new_password', required=False),
|
||||||
Argument('nickname', required=False),
|
Argument('nickname', required=False, help='请输入昵称'),
|
||||||
).parse(request.body, True)
|
Argument('wx_token', required=False),
|
||||||
|
).parse(request.body)
|
||||||
if error is None:
|
if error is None:
|
||||||
if form.get('old_password') and form.get('new_password'):
|
print(form)
|
||||||
|
if form.old_password and form.new_password:
|
||||||
if request.user.type == 'ldap':
|
if request.user.type == 'ldap':
|
||||||
return json_response(error='LDAP账户无法修改密码')
|
return json_response(error='LDAP账户无法修改密码')
|
||||||
if len(form.new_password) < 6:
|
if len(form.new_password) < 6:
|
||||||
|
@ -158,11 +164,14 @@ class SelfView(View):
|
||||||
request.user.password_hash = User.make_password(form.new_password)
|
request.user.password_hash = User.make_password(form.new_password)
|
||||||
request.user.token_expired = 0
|
request.user.token_expired = 0
|
||||||
request.user.save()
|
request.user.save()
|
||||||
|
return json_response()
|
||||||
else:
|
else:
|
||||||
return json_response(error='原密码错误,请重新输入')
|
return json_response(error='原密码错误,请重新输入')
|
||||||
if form.get('nickname'):
|
if form.nickname is not None:
|
||||||
request.user.nickname = form.nickname
|
request.user.nickname = form.nickname
|
||||||
request.user.save()
|
if form.wx_token is not None:
|
||||||
|
request.user.wx_token = form.wx_token
|
||||||
|
request.user.save()
|
||||||
return json_response(error=error)
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,11 @@ class Argument(object):
|
||||||
:param bool required: is required
|
:param bool required: is required
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, default=None, handler=None, required=True, type=str, filter=None, help=None,
|
def __init__(self, name, default=None, handler=None, required=True, type=str, filter=None, help=None):
|
||||||
nullable=False):
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default = default
|
self.default = default
|
||||||
self.type = type
|
self.type = type
|
||||||
self.required = required
|
self.required = required
|
||||||
self.nullable = nullable
|
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.help = help
|
self.help = help
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
|
@ -45,11 +43,12 @@ class Argument(object):
|
||||||
elif value in [u'', '', None]:
|
elif value in [u'', '', None]:
|
||||||
if self.default is not None:
|
if self.default is not None:
|
||||||
return self.default
|
return self.default
|
||||||
elif not self.nullable and self.required:
|
elif self.required:
|
||||||
raise ParseError(
|
raise ParseError(self.help or 'Value Error: %s must not be null' % self.name)
|
||||||
self.help or 'Value Error: %s must not be null' % self.name)
|
elif self.help:
|
||||||
|
raise ParseError(self.help)
|
||||||
else:
|
else:
|
||||||
return None
|
return value
|
||||||
try:
|
try:
|
||||||
if self.type:
|
if self.type:
|
||||||
if self.type in (list, dict) and isinstance(value, str):
|
if self.type in (list, dict) and isinstance(value, str):
|
||||||
|
|
|
@ -3,37 +3,55 @@
|
||||||
* Copyright (c) <spug.dev@gmail.com>
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
* Released under the AGPL-3.0 License.
|
* Released under the AGPL-3.0 License.
|
||||||
*/
|
*/
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Button, Form, Input, message } from 'antd';
|
import { observer } from 'mobx-react';
|
||||||
|
import { Button, Form, Input, Spin, message } from 'antd';
|
||||||
import styles from './index.module.css';
|
import styles from './index.module.css';
|
||||||
import { http } from 'libs';
|
import { http } from 'libs';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
|
||||||
export default function Basic(props) {
|
export default observer(function Basic(props) {
|
||||||
const [nickname, setNickname] = useState(localStorage.getItem('nickname'));
|
const [form] = Form.useForm()
|
||||||
const [loading, setLoading] = useState(false);
|
const [fetching, setFetching] = useState(false)
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!store.user.nickname) {
|
||||||
|
setFetching(true)
|
||||||
|
store.fetchUser()
|
||||||
|
.then(() => form.setFieldsValue(store.user))
|
||||||
|
.finally(() => setFetching(false))
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
http.patch('/api/account/self/', {nickname})
|
const formData = form.getFieldsValue();
|
||||||
|
http.patch('/api/account/self/', formData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
message.success('设置成功,重新登录或刷新页面后生效');
|
message.success('保存成功,昵称将在重新登录或刷新页面后生效');
|
||||||
localStorage.setItem('nickname', nickname)
|
localStorage.setItem('nickname', formData.nickname);
|
||||||
|
store.fetchUser()
|
||||||
})
|
})
|
||||||
.finally(() =>setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Spin spinning={fetching}>
|
||||||
<div className={styles.title}>基本设置</div>
|
<div className={styles.title}>基本设置</div>
|
||||||
<Form style={{maxWidth: 320}}>
|
<Form form={form} layout="vertical" style={{maxWidth: 320}} initialValues={store.user}>
|
||||||
<Form.Item colon={false} label="昵称">
|
<Form.Item required name="nickname" label="昵称">
|
||||||
<Input value={nickname} placeholder="请输入" onChange={e => setNickname(e.target.value)}/>
|
<Input placeholder="请输入"/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="wx_token" label="微信Token" extra={<a target="_blank" rel="noopener noreferrer" href="https://spug.cc/docs/wx-token/">什么是微信Token?</a>}>
|
||||||
|
<Input placeholder="请输入"/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" loading={loading} onClick={handleSubmit}>保存设置</Button>
|
<Button type="primary" loading={loading} onClick={handleSubmit}>保存设置</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</React.Fragment>
|
</Spin>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
|
@ -38,13 +38,13 @@ export default function Reset(props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={styles.title}>修改密码</div>
|
<div className={styles.title}>修改密码</div>
|
||||||
<Form style={{maxWidth: 320}} labelCol={{span: 6}} wrapperCol={{span: 18}}>
|
<Form style={{maxWidth: 320}} labelCol={{span: 6}} wrapperCol={{span: 18}}>
|
||||||
<Form.Item label="原密码">
|
<Form.Item required label="原密码">
|
||||||
<Input.Password value={old_password} placeholder="请输入" onChange={e => setOldPassword(e.target.value)}/>
|
<Input.Password value={old_password} placeholder="请输入" onChange={e => setOldPassword(e.target.value)}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="新密码">
|
<Form.Item required label="新密码">
|
||||||
<Input.Password value={new_password} placeholder="请输入" onChange={e => setNewPassword(e.target.value)}/>
|
<Input.Password value={new_password} placeholder="请输入" onChange={e => setNewPassword(e.target.value)}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="再次确认">
|
<Form.Item required label="再次确认">
|
||||||
<Input.Password value={new2_password} placeholder="请输入" onChange={e => setNew2Password(e.target.value)}/>
|
<Input.Password value={new2_password} placeholder="请输入" onChange={e => setNew2Password(e.target.value)}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
|
|
|
@ -3,48 +3,40 @@
|
||||||
* Copyright (c) <spug.dev@gmail.com>
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
* Released under the AGPL-3.0 License.
|
* Released under the AGPL-3.0 License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Menu } from 'antd';
|
import { Menu } from 'antd';
|
||||||
import { Breadcrumb } from 'components';
|
import { Breadcrumb } from 'components';
|
||||||
import Basic from './Basic';
|
import Basic from './Basic';
|
||||||
import Reset from './Reset';
|
import Reset from './Reset';
|
||||||
import styles from './index.module.css';
|
import styles from './index.module.css';
|
||||||
|
|
||||||
class Index extends React.Component {
|
function Index() {
|
||||||
constructor(props) {
|
const [selectedKeys, setSelectedKeys] = useState(['basic'])
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedKeys: ['basic']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const {selectedKeys} = this.state;
|
<div>
|
||||||
return (
|
<Breadcrumb>
|
||||||
<div>
|
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||||
<Breadcrumb>
|
<Breadcrumb.Item>个人中心</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
</Breadcrumb>
|
||||||
<Breadcrumb.Item>个人中心</Breadcrumb.Item>
|
<div className={styles.container}>
|
||||||
</Breadcrumb>
|
<div className={styles.left}>
|
||||||
<div className={styles.container}>
|
<Menu
|
||||||
<div className={styles.left}>
|
mode="inline"
|
||||||
<Menu
|
selectedKeys={selectedKeys}
|
||||||
mode="inline"
|
style={{border: 'none'}}
|
||||||
selectedKeys={selectedKeys}
|
onSelect={({selectedKeys}) => setSelectedKeys(selectedKeys)}>
|
||||||
style={{border: 'none'}}
|
<Menu.Item key="basic">基本设置</Menu.Item>
|
||||||
onSelect={({selectedKeys}) => this.setState({selectedKeys})}>
|
<Menu.Item key="reset">修改密码</Menu.Item>
|
||||||
<Menu.Item key="basic">基本设置</Menu.Item>
|
</Menu>
|
||||||
<Menu.Item key="reset">修改密码</Menu.Item>
|
</div>
|
||||||
</Menu>
|
<div className={styles.right}>
|
||||||
</div>
|
{selectedKeys[0] === 'basic' && <Basic/>}
|
||||||
<div className={styles.right}>
|
{selectedKeys[0] === 'reset' && <Reset/>}
|
||||||
{selectedKeys[0] === 'basic' && <Basic/>}
|
|
||||||
{selectedKeys[0] === 'reset' && <Reset/>}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Index
|
export default Index
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 24px;
|
||||||
color: rgba(0, 0, 0, .85);
|
color: rgba(0, 0, 0, .85);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||||
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
|
* Released under the AGPL-3.0 License.
|
||||||
|
*/
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import http from 'libs/http';
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
@observable user = {};
|
||||||
|
|
||||||
|
fetchUser = () => {
|
||||||
|
return http.get('/api/account/self/')
|
||||||
|
.then(res => this.user = res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Store()
|
Loading…
Reference in New Issue