modify password (#176)
Co-authored-by: wenyu.dai <“wenyu.dai@qq.com”>
diff --git a/package.json b/package.json
index e92c0fe..2c08861 100755
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
},
"dependencies": {
"@babel/polyfill": "^7.0.0-beta.36",
- "antd": "^3.8.2",
+ "antd": "^3.26.19",
"classnames": "^2.2.5",
"dayjs": "^1.8.17",
"dva": "^2.2.3",
diff --git a/src/components/GlobalHeader/index.js b/src/components/GlobalHeader/index.js
index cbf7f87..5288c2b 100644
--- a/src/components/GlobalHeader/index.js
+++ b/src/components/GlobalHeader/index.js
@@ -15,59 +15,100 @@
* limitations under the License.
*/
-import React, { PureComponent } from 'react';
-import { Dropdown, Icon, Menu } from 'antd';
-import styles from './index.less';
-import { getIntlContent, getCurrentLocale } from '../../utils/IntlUtils'
-import { emit } from '../../utils/emit';
+import React, { PureComponent } from "react";
+import { Dropdown, Form, Icon, Input, Menu, Modal } from "antd";
+import { connect } from "dva";
+import styles from "./index.less";
+import { getIntlContent, getCurrentLocale } from "../../utils/IntlUtils";
+import { emit } from "../../utils/emit";
-const TranslationOutlinedSvg = () => <svg viewBox="64 64 896 896" focusable="false" data-icon="translation" width="1em" height="1em" fill="currentColor" aria-hidden="true"><defs><style /></defs><path d="M140 188h584v164h76V144c0-17.7-14.3-32-32-32H96c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h544v-76H140V188z" /><path d="M414.3 256h-60.6c-3.4 0-6.4 2.2-7.6 5.4L219 629.4c-.3.8-.4 1.7-.4 2.6 0 4.4 3.6 8 8 8h55.1c3.4 0 6.4-2.2 7.6-5.4L322 540h196.2L422 261.4a8.42 8.42 0 00-7.7-5.4zm12.4 228h-85.5L384 360.2 426.7 484zM936 528H800v-93c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v93H592c-13.3 0-24 10.7-24 24v176c0 13.3 10.7 24 24 24h136v152c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V752h136c13.3 0 24-10.7 24-24V552c0-13.3-10.7-24-24-24zM728 680h-88v-80h88v80zm160 0h-88v-80h88v80z" /></svg>
-const TranslationOutlined = props => <Icon component={TranslationOutlinedSvg} {...props} />;
+const TranslationOutlinedSvg = () => (
+ <svg
+ viewBox="64 64 896 896"
+ focusable="false"
+ data-icon="translation"
+ width="1em"
+ height="1em"
+ fill="currentColor"
+ aria-hidden="true"
+ >
+ <defs>
+ <style />
+ </defs>
+ <path d="M140 188h584v164h76V144c0-17.7-14.3-32-32-32H96c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h544v-76H140V188z" />
+ <path d="M414.3 256h-60.6c-3.4 0-6.4 2.2-7.6 5.4L219 629.4c-.3.8-.4 1.7-.4 2.6 0 4.4 3.6 8 8 8h55.1c3.4 0 6.4-2.2 7.6-5.4L322 540h196.2L422 261.4a8.42 8.42 0 00-7.7-5.4zm12.4 228h-85.5L384 360.2 426.7 484zM936 528H800v-93c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v93H592c-13.3 0-24 10.7-24 24v176c0 13.3 10.7 24 24 24h136v152c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V752h136c13.3 0 24-10.7 24-24V552c0-13.3-10.7-24-24-24zM728 680h-88v-80h88v80zm160 0h-88v-80h88v80z" />
+ </svg>
+);
+const TranslationOutlined = props => (
+ <Icon component={TranslationOutlinedSvg} {...props} />
+);
-export default class GlobalHeader extends PureComponent {
+@connect(({ manage, loading }) => ({
+ manage,
+ loading: loading.effects["manage/update"]
+}))
+@Form.create({})
+class GlobalHeader extends PureComponent {
constructor(props) {
super(props);
this.state = {
menu: (
<Menu onClick={this.handleLocalesValueChange}>
- <Menu.Item key='0'>
+ <Menu.Item key="0">
<span>English</span>
</Menu.Item>
- <Menu.Item key='1'>
+ <Menu.Item key="1">
<span>中文</span>
</Menu.Item>
</Menu>
),
- localeName: window.sessionStorage.getItem('locale') ? window.sessionStorage.getItem('locale') : 'en-US',
- userName: window.sessionStorage.getItem('userName')
- }
+ localeName: window.sessionStorage.getItem("locale")
+ ? window.sessionStorage.getItem("locale")
+ : "en-US",
+ userName: window.sessionStorage.getItem("userName"),
+ visible: false
+ };
}
handleLocalesValueChange = value => {
const { changeLocalName } = this.props;
- if (value.key === '0') {
- emit.emit('change_language', 'en-US');
- window.sessionStorage.setItem('locale', 'en-US');
+ if (value.key === "0") {
+ emit.emit("change_language", "en-US");
+ window.sessionStorage.setItem("locale", "en-US");
this.setState({
- localeName: 'en-Us'
+ localeName: "en-Us"
});
- changeLocalName('en-Us');
+ changeLocalName("en-Us");
} else {
- emit.emit('change_language', 'zh-CN');
- window.sessionStorage.setItem('locale', 'zh-CN');
+ emit.emit("change_language", "zh-CN");
+ window.sessionStorage.setItem("locale", "zh-CN");
this.setState({
- localeName: 'zh-CN'
+ localeName: "zh-CN"
});
- changeLocalName('zh-CN');
+ changeLocalName("zh-CN");
}
getCurrentLocale(this.state.localeName);
- }
+ };
render() {
- const { onLogout } = this.props;
- const { userName } = this.state;
+ const {
+ onLogout,
+ form: { getFieldDecorator, resetFields, validateFields, getFieldValue },
+ dispatch,
+ loading
+ } = this.props;
+ const { userName, visible } = this.state;
const menu = (
<Menu>
+ <Menu.Item
+ key="1"
+ onClick={() => {
+ this.setState({ visible: true });
+ }}
+ >
+ <Icon type="form" />{" "}
+ {getIntlContent("SHENYU.GLOBALHEADER.CHANGE.PASSWORD")}
+ </Menu.Item>
<Menu.Item key="0" onClick={onLogout}>
<Icon type="logout" /> {getIntlContent("SHENYU.GLOBALHEADER.LOGOUT")}
</Menu.Item>
@@ -75,17 +116,132 @@
);
return (
<div className={styles.header}>
- <Dropdown placement="bottomCenter" overlay={this.state.menu} trigger={['click']}>
+ <Dropdown
+ placement="bottomCenter"
+ overlay={this.state.menu}
+ trigger={["click"]}
+ >
<TranslationOutlined />
</Dropdown>
<div className={styles.right}>
<Dropdown overlay={menu}>
<span>
- <Icon type="user" />{userName}<Icon type="down" />
+ <Icon type="user" />
+ {userName}
+ <Icon type="down" />
</span>
</Dropdown>
</div>
+ <Modal
+ title={getIntlContent("SHENYU.GLOBALHEADER.CHANGE.PASSWORD")}
+ visible={visible}
+ forceRender
+ okButtonProps={{
+ loading
+ }}
+ onCancel={() => {
+ this.setState({ visible: false });
+ resetFields();
+ }}
+ onOk={() => {
+ validateFields((errors, values) => {
+ if (!errors) {
+ dispatch({
+ type: "manage/updatePassword",
+ payload: {
+ id: window.sessionStorage.getItem("userId"),
+ userName: window.sessionStorage.getItem("userName"),
+ password: values.password
+ },
+ callback: () => {
+ this.setState({ visible: false });
+ resetFields();
+ }
+ });
+ }
+ });
+ }}
+ >
+ <Form labelCol={{ span: 8 }} wrapperCol={{ span: 14 }}>
+ <Form.Item
+ required
+ label={getIntlContent("SHENYU.GLOBALHEADER.NEW.PASSWORD")}
+ extra={getIntlContent("SHENYU.GLOBALHEADER.PASSWORD.EXTRA")}
+ >
+ {getFieldDecorator("password", {
+ rules: [
+ {
+ validator(rule, value, callback) {
+ const confirmPassword = getFieldValue("confirmPassword");
+ if (!value) {
+ callback(
+ getIntlContent(
+ "SHENYU.GLOBALHEADER.PASSWORD.REQUIRED"
+ )
+ );
+ return;
+ }
+ if (value.length < 8 || value.length > 16) {
+ callback(
+ getIntlContent("SHENYU.GLOBALHEADER.PASSWORD.LENGTH")
+ );
+ return;
+ }
+ if (
+ !/(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^a-zA-Z0-9])/.test(
+ value
+ )
+ ) {
+ callback(
+ getIntlContent("SHENYU.GLOBALHEADER.PASSWORD.RULE")
+ );
+ return;
+ }
+ if (confirmPassword) {
+ validateFields(["confirmPassword"], { force: true });
+ }
+ callback();
+ }
+ }
+ ]
+ })(<Input.Password />)}
+ </Form.Item>
+ <Form.Item
+ label={getIntlContent("SHENYU.GLOBALHEADER.CONFIRM.PASSWORD")}
+ required
+ >
+ {getFieldDecorator("confirmPassword", {
+ rules: [
+ {
+ validator(rule, value, callback) {
+ const password = getFieldValue("password");
+ if (!value) {
+ callback(
+ getIntlContent(
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.REQUIRED"
+ )
+ );
+ return;
+ }
+ if (password !== value) {
+ callback(
+ getIntlContent(
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.RULE"
+ )
+ );
+ return;
+ }
+ callback();
+ }
+ }
+ ]
+ })(<Input.Password />)}
+ </Form.Item>
+ </Form>
+ </Modal>
</div>
);
}
}
+
+export default GlobalHeader;
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index e76a722..297e5f9 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -1,5 +1,19 @@
{
"SHENYU.GLOBALHEADER.LOGOUT": "Logout",
+ "SHENYU.GLOBALHEADER.CHANGE.PASSWORD": "Change Password",
+ "SHENYU.GLOBALHEADER.NEW.PASSWORD": "New Password",
+ "SHENYU.GLOBALHEADER.PASSWORD.EXTRA":
+ "The password is 8 ~ 16 characters long and must contain uppercase letters, lowercase letters, numbers and special characters",
+ "SHENYU.GLOBALHEADER.PASSWORD.REQUIRED": "Please enter a new password",
+ "SHENYU.GLOBALHEADER.PASSWORD.LENGTH":
+ "The password length is 8 ~ 16 characters",
+ "SHENYU.GLOBALHEADER.PASSWORD.RULE":
+ "The password must contain uppercase letters, lowercase letters, numbers and special characters",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD": "Confirm Password",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.REQUIRED":
+ "Please enter the confirm password",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.RULE":
+ "The two passwords are inconsistent",
"SHENYU.HOME.WELCOME":
"Welcome to the Apache ShenYu Gateway Management System",
"SHENYU.SIDERMENU.LOGO": "Apache ShenYu Gateway Managment",
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index 5d27336..b60ff7c 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -1,5 +1,16 @@
{
"SHENYU.GLOBALHEADER.LOGOUT": "退出登录",
+ "SHENYU.GLOBALHEADER.CHANGE.PASSWORD": "修改密码",
+ "SHENYU.GLOBALHEADER.NEW.PASSWORD": "新密码",
+ "SHENYU.GLOBALHEADER.PASSWORD.EXTRA":
+ "密码长度为8~16个字符,必须包含大写字母、小写字母、数字、特殊字符",
+ "SHENYU.GLOBALHEADER.PASSWORD.REQUIRED": "请输入新密码",
+ "SHENYU.GLOBALHEADER.PASSWORD.LENGTH": "密码长度为8~16个字符",
+ "SHENYU.GLOBALHEADER.PASSWORD.RULE":
+ "密码必须包含大写字母、小写字母、数字、特殊字符",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD": "确认密码",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.REQUIRED": "请输入确认密码",
+ "SHENYU.GLOBALHEADER.CONFIRM.PASSWORD.RULE": "两次密码不一致",
"SHENYU.HOME.WELCOME": "欢迎登录Apache ShenYu网关管理系统",
"SHENYU.COMMON.OPEN": "开启",
"SHENYU.COMMON.CLOSE": "关闭",
diff --git a/src/models/login.js b/src/models/login.js
index a98bc3d..e6a0f16 100644
--- a/src/models/login.js
+++ b/src/models/login.js
@@ -33,7 +33,6 @@
const response = yield call(queryLogin, payload);
// Login successfully
-
if (response.data) {
yield put({
type: "changeLoginStatus",
@@ -44,6 +43,7 @@
});
window.sessionStorage.setItem("token", response.data.token);
window.sessionStorage.setItem("userName", response.data.userName);
+ window.sessionStorage.setItem("userId", response.data.id);
/* const urlParams = new URL(window.location.href);
const params = getPageQuery();
let { redirect } = params;
@@ -80,6 +80,7 @@
});
window.sessionStorage.removeItem("token");
window.sessionStorage.removeItem("userName");
+ window.sessionStorage.removeItem("userId");
yield put(
routerRedux.push({
pathname: "/user/login"
diff --git a/src/models/manage.js b/src/models/manage.js
index 9e2f0d2..41e514b 100644
--- a/src/models/manage.js
+++ b/src/models/manage.js
@@ -21,7 +21,8 @@
findUser,
updateUser,
deleteUser,
- addUser
+ addUser,
+ updatePassword
} from "../services/api";
import { getIntlContent } from "../utils/IntlUtils";
@@ -101,12 +102,25 @@
getIntlContent("SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS")
);
callback();
- yield put({ type: "reload", fetchValue });
+ if (fetchValue) {
+ yield put({ type: "reload", fetchValue });
+ }
} else {
message.warn(json.message);
}
},
-
+ *updatePassword(params, { call }) {
+ const { payload, callback } = params;
+ const json = yield call(updatePassword, payload);
+ if (json.code === 200) {
+ message.success(
+ getIntlContent("SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS")
+ );
+ callback();
+ } else {
+ message.warn(json.message);
+ }
+ },
*reload(params, { put }) {
const { fetchValue } = params;
const { userName, currentPage, pageSize } = fetchValue;
diff --git a/src/services/api.js b/src/services/api.js
index 2e10a66..6779a6d 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -49,6 +49,18 @@
}
});
}
+
+/* update password */
+export async function updatePassword(params) {
+ return request(`${baseUrl}/dashboardUser/modify-password/${params.id}`, {
+ method: `PUT`,
+ body: {
+ userName: params.userName,
+ password: params.password
+ }
+ });
+}
+
/* get all metadata */
export async function getAllMetadata(params) {
const { appName, currentPage, pageSize } = params;