Merge branch 'master' into features/UserManger

This commit is contained in:
2021-07-01 15:48:20 +08:00
37 changed files with 1565 additions and 1331 deletions

View File

@@ -1,6 +1,7 @@
using Ewide.Core; using Ewide.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -11,4 +12,10 @@ namespace Ewide.Application
{ {
} }
public class HouseQueryDetailInput
{
[Required(ErrorMessage = "Id不能为空")]
public string Id { get; set; }
}
} }

View File

@@ -3,7 +3,9 @@ using Ewide.Core.Extension;
using Furion.DatabaseAccessor; using Furion.DatabaseAccessor;
using Furion.DependencyInjection; using Furion.DependencyInjection;
using Furion.DynamicApiController; using Furion.DynamicApiController;
using Mapster;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -77,5 +79,32 @@ WHERE 1=1";
"TotalFloor" "TotalFloor"
}); });
} }
[HttpGet("/houseQuery/detail")]
public async Task<dynamic> Detail([FromQuery] HouseQueryDetailInput input)
{
var houseCodeOutputAsync = await _dapperRepository.QueryAsync<HouseCodeOutput>(
@"SELECT HC.Id,HC.Address,HC.ProjectId,HC.ZoneId,HC.Type,AA.Name AreaName,RA.Name RoadName,CA.Name CommName,O.Name ZoneName,Proj.Note ProjectNote,CONCAT(Proj.Name,'(',Proj.Note,')') ProjectFullName,HC.HouseCode,HC.Lng,HC.Lat,HC.No FROM bs_house_code HC
LEFT JOIN bs_house_projectinfo Proj ON Proj.Id=HC.ProjectId
LEFT JOIN sys_org O ON HC.ZoneId = O.Id
LEFT JOIN sys_area_code CA ON CA.Code = Proj.AreaCode
LEFT JOIN sys_area_code RA ON RA.AdCode = SUBSTR(CA.AdCode,1,9)
LEFT JOIN sys_area_code AA ON AA.AdCode = SUBSTR(CA.AdCode,1,6)
WHERE HC.Id=@Id", new { input.Id }
);
var houseCodeOutput = houseCodeOutputAsync.SingleOrDefault();
var houseInfoOutputForDetailPage = new HouseInfoOutputForDetailPage
{
HouseCode = houseCodeOutput
};
var houseInfoOutput = (await _houseInfoRep.DetachedEntities.FirstOrDefaultAsync(p => p.HouseCodeId == houseCodeOutput.Id)).Adapt<HouseInfoOutput>();
houseInfoOutputForDetailPage.HouseInfo = houseInfoOutput;
return houseInfoOutputForDetailPage;
}
} }
} }

View File

@@ -41,6 +41,20 @@ namespace Ewide.Core
// 解析异常信息 // 解析异常信息
var (StatusCode, ErrorCode, Errors) = UnifyContext.GetExceptionMetadata(context); var (StatusCode, ErrorCode, Errors) = UnifyContext.GetExceptionMetadata(context);
// 如果是代码自行抛出的异常,视为接口调用成功,返回结果失败
if (context.Exception.GetType() == typeof(Furion.FriendlyException.AppFriendlyException))
{
return DisplayJson(new RestfulResult<object>
{
Code = StatusCodes.Status200OK,
Success = false,
Data = null,
Message = Errors,
Extras = UnifyContext.Take(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
});
}
return DisplayJson(new RestfulResult<object> return DisplayJson(new RestfulResult<object>
{ {
Code = StatusCode, Code = StatusCode,

View File

@@ -431,7 +431,7 @@ namespace Ewide.Core.Service
///<summary> ///<summary>
///发送验证码 ///发送验证码
/// </summary> /// </summary>
[HttpPost("/sysUser/SendCode")] [HttpPost("/sysUser/sendCode")]
public async Task<dynamic> SendCode(Usermailphone input) public async Task<dynamic> SendCode(Usermailphone input)
{ {
var Orgcode_Key = "ewide_Orgcode"; var Orgcode_Key = "ewide_Orgcode";
@@ -505,7 +505,7 @@ namespace Ewide.Core.Service
///<summary> ///<summary>
///检验验证码并且绑定 ///检验验证码并且绑定
/// </summary> /// </summary>
[HttpPost("/sysUser/CheckBindcode")] [HttpPost("/sysUser/checkBindcode")]
public async Task<dynamic> CheckBindcode(Usermailphone input) public async Task<dynamic> CheckBindcode(Usermailphone input)
{ {
var Orgcode_Key = "ewide_Orgcode"; var Orgcode_Key = "ewide_Orgcode";

View File

@@ -168,6 +168,7 @@ namespace Ewide.EntityFramework.Core
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
if (App.HostEnvironment.EnvironmentName == "Testing") return; if (App.HostEnvironment.EnvironmentName == "Testing") return;
//集成测试下面代码会报错 //集成测试下面代码会报错
#if DEBUG
XmlSerializerUtil xmlHandler = new XmlSerializerUtil(); XmlSerializerUtil xmlHandler = new XmlSerializerUtil();
Dictionary<Type, object> dic = xmlHandler.ReaderALL(); Dictionary<Type, object> dic = xmlHandler.ReaderALL();
foreach (KeyValuePair<Type, object> item in dic) foreach (KeyValuePair<Type, object> item in dic)
@@ -183,6 +184,7 @@ namespace Ewide.EntityFramework.Core
} }
modelBuilder.Entity(item.Key).HasData(data); modelBuilder.Entity(item.Key).HasData(data);
} }
#endif
} }
} }
} }

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>False</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net5.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1 @@
REACT_APP_BASE_URL=http://localhost:5566/

View File

@@ -0,0 +1 @@
REACT_APP_BASE_URL=http://118.178.224.202:90/

View File

@@ -20,6 +20,7 @@
"photoswipe": "^4.1.3", "photoswipe": "^4.1.3",
"react": "^17.0.2", "react": "^17.0.2",
"react-color": "^2.19.3", "react-color": "^2.19.3",
"react-cropper": "^2.1.8",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-monaco-editor": "^0.43.0", "react-monaco-editor": "^0.43.0",
@@ -44,6 +45,7 @@
"rules": { "rules": {
"eqeqeq": "off", "eqeqeq": "off",
"no-unused-vars": "off", "no-unused-vars": "off",
"no-sparse-arrays": "off",
"array-callback-return": "off", "array-callback-return": "off",
"jsx-a11y/anchor-is-valid": "off" "jsx-a11y/anchor-is-valid": "off"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>宽易科技</title>
<script src="https://webapi.amap.com/maps?v=2.0&key=c6a4832b8afbde0361b36630a3fc5bdc&plugin=Map3D,AMap.DistrictSearch,AMap.Geocoder,AMap.AutoComplete,AMap.PlaceSearch"></script> <script src="https://webapi.amap.com/maps?v=2.0&key=c6a4832b8afbde0361b36630a3fc5bdc&plugin=Map3D,AMap.DistrictSearch,AMap.Geocoder,AMap.AutoComplete,AMap.PlaceSearch"></script>
</head> </head>
<body> <body>

View File

@@ -9,6 +9,3 @@
border-radius: 0; border-radius: 0;
background-color: @primary-color; background-color: @primary-color;
} }
.ant-anchor-link-active {
background: linear-gradient(90deg, #fff, transparent);
}

View File

@@ -220,6 +220,23 @@
color: fade(@black, 50%); color: fade(@black, 50%);
} }
} }
&.yo-form--no-border {
.ant-form-item {
padding: @padding-md 0;
border-right: 0;
border-left: 0;
&:first-child {
border-top: 0;
}
&:last-child {
border-bottom: 0;
}
}
.yo-form-group {
margin-bottom: 0;
}
}
} }
.yo-modal-form { .yo-modal-form {
.ant-modal-body { .ant-modal-body {

View File

@@ -13,11 +13,11 @@ import status from './status'
* api.getItemGroupType(parmas).then(...) * api.getItemGroupType(parmas).then(...)
*/ */
import urls from './requests' import urls from './requests'
import { notification } from 'antd' import { message as Message, notification } from 'antd'
const STATUS = status const STATUS = status
axios.defaults.baseURL = '/api' axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? '/api' : process.env.REACT_APP_BASE_URL
const initInstance = (options) => { const initInstance = (options) => {
const instance = axios const instance = axios
@@ -64,6 +64,10 @@ const errorNotification = ({ code, message }) => {
} }
} }
const errorMessage = (message) => {
Message.error(message)
}
const handlerUnauthorized = () => { const handlerUnauthorized = () => {
token.value = '' token.value = ''
window.location.replace('/login') window.location.replace('/login')
@@ -125,13 +129,28 @@ for (let key in urls) {
api[`${key}Await`](params) api[`${key}Await`](params)
.then((res) => { .then((res) => {
const { data } = res const { data } = res
const isFile = [ArrayBuffer, Blob].includes(data.constructor)
const result = isFile ? res : data
// 错误的返回码,以通知的形式弹出
if (errerCodes.indexOf(data.code) >= 0) { if (errerCodes.indexOf(data.code) >= 0) {
errorNotification(data) errorNotification(data)
reject([ArrayBuffer, Blob].indexOf(data.constructor) > -1 ? res : data) reject(result)
} else if (data.code === STATUS.Unauthorized) { }
// 非文件,返回码正确,但是结果失败,以消息的形式弹出
else if (!isFile && !data.success) {
errorMessage(data.message)
reject(result)
}
// 未登录
else if (data.code === STATUS.Unauthorized) {
handlerUnauthorized() handlerUnauthorized()
} else { }
reslove([ArrayBuffer, Blob].indexOf(data.constructor) > -1 ? res : data)
else {
reslove(result)
} }
}) })
.catch(({ response }) => { .catch(({ response }) => {

View File

@@ -1,5 +1,6 @@
const urls = { const urls = {
houseQueryPage: ['/houseQuery/page', 'post'], houseQueryPage: ['/houseQuery/page', 'post'],
houseQueryDetail: ['/houseQuery/detail', 'get'],
} }
export default urls export default urls

View File

@@ -78,12 +78,12 @@ const urls = {
/** /**
* 发送验证码 * 发送验证码
*/ */
SendCode: ['/sysUser/SendCode', 'post'], sysUserSendCode: ['/sysUser/sendCode', 'post'],
/** /**
* 绑定/验证 * 绑定/验证
*/ */
CheckBindcode: ['/sysUser/CheckBindcode', 'post'], sysUserCheckBindcode: ['/sysUser/checkBindcode', 'post'],
} }

View File

@@ -78,17 +78,9 @@ function renderItem(data) {
} }
function renderCheckbox(data) { function renderCheckbox(data) {
return ( const grid = (
<label className="ant-card-grid ant-card-grid-hoverable"> <label className="ant-card-grid ant-card-grid-hoverable">
<Popover <Checkbox value={data.id} checked={data.checked} onChange={e => this.onChange(e, data)}>
placement="topLeft"
content={data.remark || <span className="text-normal">没有说明</span>}
>
<Checkbox
value={data.id}
checked={data.checked}
onChange={e => this.onChange(e, data)}
>
{data.title} {data.title}
</Checkbox> </Checkbox>
{data.visibleParent && data.type == 2 && ( {data.visibleParent && data.type == 2 && (
@@ -97,9 +89,15 @@ function renderCheckbox(data) {
</Tooltip> </Tooltip>
)} )}
<div className="text-gray">{data.permission}</div> <div className="text-gray">{data.permission}</div>
</Popover>
</label> </label>
) )
return data.remark ? (
<Popover placement="topLeft" content={data.remark}>
{grid}
</Popover>
) : (
grid
)
} }
export default class AuthorityView extends Component { export default class AuthorityView extends Component {

View File

@@ -1,29 +1,23 @@
import React, { Component } from 'react' import React, { Component } from 'react'
export default class Container extends Component { export default class Container extends Component {
getMode(mode) { getMode(mode) {
const c = 'container' const c = 'container'
switch (mode) { const modes = ['xxs', 'xs', 'sm', 'md', 'fluid']
case 'sm': if (modes.includes(mode)) {
return c + '-sm' return `${c}-${mode}`
case 'md':
return c + '-md'
case 'fluid':
return c + '-fluid'
default:
return c
} }
return c
} }
render() { render() {
let className = this.getMode(this.props.mode) const { mode, className, children } = this.props
if (this.props.className) {
className += ` ${this.props.className}` let containerName = this.getMode(mode)
if (className) {
containerName = [containerName, className].join(' ')
} }
return ( return <section className={containerName}>{children}</section>
<section className={className}>{this.props.children}</section>
)
} }
} }

View File

@@ -3,7 +3,7 @@ import { Avatar } from 'antd'
import { isEqual } from 'lodash' import { isEqual } from 'lodash'
import { PreviewFileBase64 } from 'util/file' import { PreviewFileBase64 } from 'util/file'
const getSrc = async (id) => { const getSrc = async id => {
if (id) { if (id) {
const base64 = await PreviewFileBase64(id) const base64 = await PreviewFileBase64(id)
return base64 return base64
@@ -12,9 +12,8 @@ const getSrc = async (id) => {
} }
export default class Image extends Component { export default class Image extends Component {
state = { state = {
src: '' src: '',
} }
id = '' id = ''
@@ -25,9 +24,9 @@ export default class Image extends Component {
this.setSrc() this.setSrc()
} }
shouldComponentUpdate(props) { shouldComponentUpdate(props, state) {
// props变更或state.src变更时进行渲染 // props变更或state.src变更时进行渲染
return !isEqual(this.props, props) || !this.state.src return !isEqual(this.props, props) || this.state.src !== state.src
} }
componentDidUpdate() { componentDidUpdate() {
@@ -46,13 +45,9 @@ export default class Image extends Component {
render() { render() {
if (this.props.type === 'avatar') { if (this.props.type === 'avatar') {
return ( return <Avatar src={this.state.src} {...this.props} />
<Avatar src={this.state.src} {...this.props} />
)
} else { } else {
return ( return <img src={this.state.src} {...this.props} alt="" />
<img src={this.state.src} {...this.props} alt="" />
)
} }
} }
} }

View File

@@ -1,4 +1,4 @@
export { default as AntIcon } from 'components/ant-icon' export { default as AntIcon } from './ant-icon'
export { default as AuthorityView } from './authority-view' export { default as AuthorityView } from './authority-view'
export { default as Auth } from './authorized' export { default as Auth } from './authorized'
export { default as ColorSelector } from './form/color-selector' export { default as ColorSelector } from './form/color-selector'

View File

@@ -222,6 +222,7 @@ export default class QueryTable extends Component {
this.query = this.props.queryInitialValues this.query = this.props.queryInitialValues
} }
try {
const res = await this.loadData( const res = await this.loadData(
{ {
pageIndex: this.pagination.current, pageIndex: this.pagination.current,
@@ -245,9 +246,10 @@ export default class QueryTable extends Component {
this.pagination = false this.pagination = false
} }
} finally {
this.onLoaded() this.onLoaded()
} }
}
/** /**
* 数据开始加载 * 数据开始加载

View File

@@ -0,0 +1,35 @@
import React, { Component } from 'react'
import { Card } from 'antd'
import Container from 'components/container'
import { api } from 'common/api'
import ReactJson from 'react-json-view'
export default class detail extends Component {
state = {
loading: false,
record: null,
}
componentDidMount() {
// 获取详细数据
const { id } = this.props.param
if (id) {
api.houseQueryDetail({ id }).then(({ data }) => {
this.setState({
record: data,
loading: false,
})
})
}
}
render() {
return (
<Container>
<Card>
<ReactJson src={this.state.record} />
</Card>
</Container>
)
}
}

View File

@@ -12,7 +12,14 @@ import {
Row, Row,
Tag, Tag,
} from 'antd' } from 'antd'
import { AntIcon, Auth, Container, InputNumberRange, QueryTable } from 'components' import {
AntIcon,
Auth,
Container,
InputNumberRange,
QueryTable,
QueryTableActions,
} from 'components'
import { api } from 'common/api' import { api } from 'common/api'
import auth from 'components/authorized/handler' import auth from 'components/authorized/handler'
import { first, isEqual, last } from 'lodash' import { first, isEqual, last } from 'lodash'
@@ -46,6 +53,9 @@ const authName = 'houseQuery'
export default class index extends Component { export default class index extends Component {
state = { state = {
codes: { codes: {
houseStatus: [],
houseType: [],
houseIndustry: [],
houseUsedStatus: [], houseUsedStatus: [],
housePropertyRights: [], housePropertyRights: [],
landAttribute: [], landAttribute: [],
@@ -95,10 +105,11 @@ export default class index extends Component {
sorter: true, sorter: true,
}, },
{ {
title: '任务截止时间', title: '建档状态',
dataIndex: 'endTime', dataIndex: 'state',
sorter: true, sorter: true,
width: 150, width: 100,
render: text => this.bindCodeValue(text, 'house_status'),
}, },
] ]
@@ -109,7 +120,22 @@ export default class index extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
const flag = auth({ [authName]: [['edit'], ['delete']] }) const flag = auth({ [authName]: 'detail' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth={{ [authName]: 'detail' }}>
<a onClick={() => this.onOpen(record.id)}>查看</a>
</Auth>
</QueryTableActions>
),
})
}
} }
/** /**
@@ -132,6 +158,9 @@ export default class index extends Component {
const { onLoading, onLoadData } = this.table.current const { onLoading, onLoadData } = this.table.current
onLoading() onLoading()
getDictData( getDictData(
'house_status',
'house_type',
'house_industry',
'house_used_status', 'house_used_status',
'house_property_rights', 'house_property_rights',
'land_attribute', 'land_attribute',
@@ -198,8 +227,12 @@ export default class index extends Component {
* @param {*} modal * @param {*} modal
* @param {*} id * @param {*} id
*/ */
onOpen(modal, id) { onOpen(id) {
modal.current.open({ id }) window.openContentWindow({
title: '房屋详情',
path: 'business/house/query/detail',
param: { id },
})
} }
/** /**

View File

@@ -28,14 +28,7 @@ const authName = 'houseTask'
export default class index extends Component { export default class index extends Component {
state = { state = {
codes: { codes: {
status: [ houseStatus: [],
{ code: -1, value: '审核退回' },
{ code: 0, value: '待处理' },
{ code: 1, value: '暂存' },
{ code: 2, value: '待提交' },
{ code: 3, value: '审核中' },
{ code: 6, value: '审核通过' },
],
houseType: [], houseType: [],
houseIndustry: [], houseIndustry: [],
}, },
@@ -92,7 +85,7 @@ export default class index extends Component {
dataIndex: 'state', dataIndex: 'state',
sorter: true, sorter: true,
width: 100, width: 100,
render: text => this.bindCodeValue(text, 'status'), render: text => this.bindCodeValue(text, 'house_status'),
}, },
] ]
@@ -146,7 +139,7 @@ export default class index extends Component {
componentDidMount() { componentDidMount() {
const { onLoading, onLoadData } = this.table.current const { onLoading, onLoadData } = this.table.current
onLoading() onLoading()
getDictData('house_type', 'house_industry').then(codes => { getDictData('house_status', 'house_type', 'house_industry').then(codes => {
this.setState({ codes: { ...this.state.codes, ...codes } }, () => { this.setState({ codes: { ...this.state.codes, ...codes } }, () => {
onLoadData() onLoadData()
}) })
@@ -295,8 +288,8 @@ export default class index extends Component {
<Form.Item label="建档状态" name="state"> <Form.Item label="建档状态" name="state">
<Select allowClear className="w-150" placeholder="建档状态"> <Select allowClear className="w-150" placeholder="建档状态">
<Select.Option value="">全部</Select.Option> <Select.Option value="">全部</Select.Option>
{codes.status.map(item => ( {codes.houseStatus.map(item => (
<Select.Option key={item.code} value={item.code}> <Select.Option key={item.code} value={+item.code}>
{item.value} {item.value}
</Select.Option> </Select.Option>
))} ))}

View File

@@ -0,0 +1,186 @@
import React, { Component } from 'react'
import { Button, Card, Col, Descriptions, Modal, Row, Spin, Tooltip, Upload } from 'antd'
import { AntIcon, Image } from 'components'
import { Cropper } from 'react-cropper'
import 'cropperjs/dist/cropper.css'
import { BlobToFile } from 'util/file'
import './base.less'
import { api } from 'common/api'
export default class base extends Component {
state = {
img: true,
cropperVisible: false,
loadingAvatar: false,
}
cropper = React.createRef()
avatarFile = null
async onOpenAvatarCropper() {
this.setState({ cropperVisible: true })
}
onCloseAvatarCropper() {
this.setState({ cropperVisible: false }, () => {
setTimeout(() => {
const cropper = this.cropper.current && this.cropper.current.cropper
if (cropper) {
cropper.destroy()
}
this.avatarFile = null
this.setState({ img: true })
}, 300)
})
}
onUploadAvatar() {
this.setState({ loadingAvatar: true })
const canvas = this.cropper.current.cropper.getCroppedCanvas()
canvas.toBlob(async data => {
try {
const file = BlobToFile(data, this.avatarFile.name, this.avatarFile.type)
const fd = new FormData()
fd.append('file', file)
const { data: avatar } = await api.sysFileInfoUpload(fd)
await api.sysUserUpdateInfo({ avatar })
this.onCloseAvatarCropper()
this.props.loadData()
} finally {
this.setState({ loadingAvatar: false })
}
})
}
render() {
const { user } = this.props
const { img, cropperVisible, loadingAvatar } = this.state
const cropper = this.cropper.current && this.cropper.current.cropper
return (
<>
<Card title="我的信息">
<div className="yo-avatar-info">
<Image id={user.avatar} type="avatar" size={128} />
<div
onClick={() => this.onOpenAvatarCropper()}
className="yo-avatar-info--cover"
>
<AntIcon type="sync" />
</div>
</div>
<br />
<Descriptions column={1} labelStyle={{ width: '100px' }}>
<Descriptions.Item label="用户名">{user.name}</Descriptions.Item>
<Descriptions.Item label="昵称">{user.nickName}</Descriptions.Item>
<Descriptions.Item label="帐号">{user.account}</Descriptions.Item>
<Descriptions.Item label="性别">{user.sex}</Descriptions.Item>
<Descriptions.Item label="生日">
{user.birthday &&
(typeof user.birthday === 'string'
? user.birthday
: user.birthday.format('YYYY-MM-DD'))}
</Descriptions.Item>
</Descriptions>
</Card>
<Modal
visible={cropperVisible}
width={800}
footer={false}
destroyOnClose
onCancel={() => this.onCloseAvatarCropper()}
>
<Spin spinning={loadingAvatar} indicator={<AntIcon type="loading" />}>
<Row gutter={16} align="middle">
<Col span={12}>
<div className="yo-avatar-cropper">
<Cropper
ref={this.cropper}
src={img}
style={{ width: '100%', height: 240 }}
cropBoxResizable={false}
aspectRatio={1}
preview=".yo-avatar-preview"
/>
</div>
</Col>
<Col span={12}>
<div className="yo-avatar-preview" />
</Col>
<Col span={12} className="mt-md text-center">
<Upload
beforeUpload={file => {
this.avatarFile = file
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
this.setState({ img: reader.result })
}
return false
}}
showUploadList={false}
className="mr-xs"
>
<Button type="primary" icon={<AntIcon type="picture" />}>
选择图片
</Button>
</Upload>
<Button.Group>
<Tooltip placement="bottom" title="放大">
<Button
disabled={!cropper}
type="text"
onClick={() => cropper.zoom(0.1)}
icon={<AntIcon type="zoom-in" />}
/>
</Tooltip>
<Tooltip placement="bottom" title="缩小">
<Button
disabled={!cropper}
type="text"
onClick={() => cropper.zoom(-0.1)}
icon={<AntIcon type="zoom-out" />}
/>
</Tooltip>
<Tooltip placement="bottom" title="左旋">
<Button
disabled={!cropper}
type="text"
onClick={() => cropper.rotate(-90)}
icon={<AntIcon type="undo" />}
/>
</Tooltip>
<Tooltip placement="bottom" title="右旋">
<Button
disabled={!cropper}
type="text"
onClick={() => cropper.rotate(90)}
icon={<AntIcon type="redo" />}
/>
</Tooltip>
</Button.Group>
</Col>
<Col span={12} className="mt-md text-center">
<Button
disabled={!cropper}
type="primary"
className="mr-xs"
onClick={() => this.onUploadAvatar()}
>
确认
</Button>
<Button onClick={() => this.onCloseAvatarCropper()}>取消</Button>
</Col>
</Row>
</Spin>
</Modal>
</>
)
}
}

View File

@@ -0,0 +1,51 @@
@import (reference) '~assets/style/app.less';
.yo-avatar-info {
position: relative;
overflow: hidden;
width: 128px;
margin: 0 auto;
border-radius: 50%;
&--cover {
font-size: @font-size-lg * 2;
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
cursor: pointer;
transition: @animation-duration-slow;
opacity: 0;
color: @white;
background-color: fade(@black, 50%);
&:hover {
opacity: 1;
}
}
}
.yo-avatar-cropper {
overflow: hidden;
border-radius: @border-radius-base;
background-color: #ccc;
}
.yo-avatar-preview {
overflow: hidden;
width: 200px;
height: 200px;
margin: 0 auto;
border-radius: 50%;
background: #ccc;
}

View File

@@ -1,41 +1,113 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Anchor, Form, Input, InputNumber, Spin } from 'antd' import ReactDOM from 'react-dom'
import { AntIcon, Container, IconSelector } from 'components' import { Anchor, Card, Col, Row, Spin } from 'antd'
import { cloneDeep } from 'lodash' import { AntIcon, Container } from 'components'
import Safety from './setting/satety/index' import moment from 'moment'
import store from 'store'
import { api } from 'common/api'
import Base from './base'
import Info from './setting/info' import Info from './setting/info'
import nav from 'store/reducer/nav' import Safety from './setting/satety'
const { getState, dispatch, subscribe } = store
const navs = [
{ title: '基本信息', component: Info },
{ title: '安全设置', component: Safety },
]
export default class index extends Component { export default class index extends Component {
state = {} state = {
loading: false,
user: getState('user'),
}
container = window
constructor(props) {
super(props)
this.unsubscribe = subscribe('user', user => {
this.setState({ user })
})
}
componentDidMount() {
this.loadData()
}
componentWillUnmount() {
this.unsubscribe()
}
loadData = async () => {
this.setState({ loading: true })
try {
const { data } = await api.getLoginUser()
if (data.birthday) {
data.birthday = moment(data.birthday)
}
dispatch({
type: 'SET_USER_ACCOUNT',
user: data,
})
} finally {
this.setState({ loading: false })
}
}
setContainer = container => {
this.container = (ReactDOM.findDOMNode(container) || {}).parentNode
}
render() { render() {
// let navs = [ const { loadData } = this
// {
// title: '我的信息', const { loading, user } = this.state
// component: require('./setting/info'),
// },
// {
// title: '安全设置',
// component: require('./setting/satety'),
// },
// ]
// return (
// <Container>
// <Anchor offsetTop={16} className="yo-account--anchor">
// {navs.map(item => {
// return <Anchor.Link key={item.title} title={nav.title}></Anchor.Link>
// })}
// </Anchor>
// <br />
// </Container>
// )
return ( return (
<div> <Spin spinning={loading} indicator={<AntIcon type="loading" />} ref={this.setContainer}>
<Info></Info> <Container mode="fluid">
<Safety></Safety> <Row gutter={16}>
</div> <Col flex="200px">
<Anchor
getContainer={() => this.container}
offsetTop={24}
targetOffset={100}
wrapperStyle={{ backgroundColor: 'transparent' }}
onClick={e => e.preventDefault()}
>
{navs.map((item, i) => (
<Anchor.Link
key={i}
href={`#account-setting-${i}`}
title={item.title}
/>
))}
</Anchor>
</Col>
<Col flex="1">
<Row gutter={16}>
<Col xl={{ span: 10, order: 2 }}>
<br />
<Base user={user} loadData={loadData} />
</Col>
<Col xl={{ span: 14, order: 1 }}>
{navs.map((item, i) => (
<section key={i} id={`account-setting-${i}`}>
<br />
<Card title={item.title}>
<item.component user={user} loadData={loadData} />
</Card>
</section>
))}
</Col>
</Row>
</Col>
</Row>
</Container>
</Spin>
) )
} }
} }

View File

@@ -1,96 +1,53 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Button, DatePicker, Form, Input, message, Radio, Spin } from 'antd' import { Button, DatePicker, Form, Input, message as Message, Radio } from 'antd'
import { AntIcon } from 'components'
import { api } from 'common/api' import { api } from 'common/api'
import { cloneDeep } from 'lodash'
import { AntIcon, Container, IconSelector, Image } from 'components'
import store from 'store'
import moment from 'moment' import moment from 'moment'
const { getState } = store
export default class index extends Component { export default class index extends Component {
state = { state = {
info: getState('user'),
saving: false, saving: false,
loading: false,
} }
form = React.createRef() form = React.createRef()
componentDidMount() { componentDidMount() {
this.setState({ const { user } = this.props
loading: true, if (user.birthday) {
}) user.birthday = moment(user.birthday)
api.getLoginUser()
.then(({ data }) => {
delete data.apps
delete data.menus
this.setState({
info: data,
})
let birthday = data.birthday
birthday = moment(birthday)
data = {
...data,
birthday: birthday,
} }
this.form.current.setFieldsValue(data) this.form.current.setFieldsValue(user)
}) }
.finally(() => {
this.setState({ async onSvaeInfo() {
loading: false, this.setState({ saving: true })
}) try {
}) await api.sysUserUpdateInfo(this.form.current.getFieldsValue())
await this.props.loadData()
Message.success('更新个人信息成功')
} finally {
this.setState({ saving: false })
} }
onSvaeInfo(data) {
this.setState({
saving: true,
})
let { birthday } = data.current.getFieldsValue()
let { nickName } = data.current.getFieldsValue()
let { sex } = data.current.getFieldsValue()
let { tel } = data.current.getFieldsValue()
api.sysUserUpdateInfo({
nickName: nickName,
birthday: birthday,
sex: sex,
tel: tel,
}).then(() => {
message.success('更新个人信息成功')
this.setState({
saving: false,
})
})
} }
onAvatarStart() {} onAvatarStart() {}
render() { render() {
const { info } = this.state const { user } = this.props
const { saving } = this.state
return ( return (
<Container mode="xxs"> <>
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}> <Form className="yo-form yo-form--no-border" ref={this.form}>
<Form className="yo-form" ref={this.form}>
<h4 className="h4">我的信息</h4>
<div className="yo-avatar-info">
<Image
id={info.avatar}
size={128}
icon={<AntIcon type="user" />}
type="avatar"
/>
</div>
<br />
<div className="yo-form-group yo-form--short"> <div className="yo-form-group yo-form--short">
<Form.Item label="昵称" name="nickName"> <Form.Item label="昵称" name="nickName">
<Input placeholder="请输入昵称"></Input> <Input autoComplete="off" placeholder="请输入昵称" />
</Form.Item> </Form.Item>
<Form.Item label="用户名"> <Form.Item label="用户名">
<span>{info.name}</span> <span>{user.name}</span>
</Form.Item> </Form.Item>
<Form.Item label="生日" name="birthday"> <Form.Item label="生日" name="birthday">
<DatePicker <DatePicker className="w-100-p" placeholder="请选择生日" />
onChange={this.onChange}
className="w-100-p"
placeholder="请选择生日"
/>
</Form.Item> </Form.Item>
<Form.Item label="性别" name="sex"> <Form.Item label="性别" name="sex">
<Radio.Group> <Radio.Group>
@@ -99,29 +56,32 @@ export default class index extends Component {
<span>保密</span> <span>保密</span>
</Radio.Button> </Radio.Button>
<Radio.Button value={1}> <Radio.Button value={1}>
<AntIcon color="#1890ff" className="mr-xxs" type="man" /> <AntIcon
style={{ color: '#1890ff' }}
className="mr-xxs"
type="man"
/>
<span></span> <span></span>
</Radio.Button> </Radio.Button>
<Radio.Button value={2}> <Radio.Button value={2}>
<AntIcon color="#eb2f96" className="mr-xxs" type="woman" /> <AntIcon
style={{ color: '#eb2f96' }}
className="mr-xxs"
type="woman"
/>
<span></span> <span></span>
</Radio.Button> </Radio.Button>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Form.Item label="电话" name="tel"> <Form.Item label="电话" name="tel">
<Input placeholder="请输入电话" /> <Input autoComplete="off" placeholder="请输入电话" />
</Form.Item> </Form.Item>
</div> </div>
</Form> </Form>
</Spin> <Button loading={saving} onClick={() => this.onSvaeInfo()} block type="text">
<Button
loading={this.state.saving}
onClick={() => this.onSvaeInfo(this.form)}
block
>
更新个人信息 更新个人信息
</Button> </Button>
</Container> </>
) )
} }
} }

View File

@@ -1,179 +1,126 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Button, DatePicker, Form, Input, List, message as Message, Spin } from 'antd' import { List } from 'antd'
import { api } from 'common/api' import { api } from 'common/api'
import { cloneDeep } from 'lodash' import { AntIcon, ModalForm, QueryTableActions } from 'components'
import { AntIcon, Container, IconSelector, Image, ModalForm } from 'components'
import store from 'store'
import moment from 'moment'
import Item from 'antd/lib/list/Item'
import PasswordForm from './password' import PasswordForm from './password'
import Mail from './mail' import Mail from './mail'
import Phone from './phone' import Phone from './phone'
const { getState } = store
const apiAction = { const apiAction = {
update: api.sysUserUpdatePwd, updatePwd: api.sysUserUpdatePwd,
} }
export default class form extends Component { export default class form extends Component {
state = { updatePwdForm = React.createRef()
saving: false, mailForm = React.createRef()
info: [], mhoneForm = React.createRef()
loading: true,
} onOpen(modal) {
form = React.createRef() modal.current.open()
// 新增窗口实例
updateForm = React.createRef()
MailForm = React.createRef()
PhoneForm = React.createRef()
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
} }
/** render() {
* 打开新增/编辑弹窗 const { user, loadData } = this.props
* @param {*} modal
* @param {*} record
*/
onOpen(modal, record) {
modal.current.open({
record,
})
}
componentDidMount() { const index = []
api.getLoginUser().then(({ data }) => {
this.setState({
loading: true,
})
let index = []
//密码 //密码
index.push({ index.push({
title: '登录密码', title: '登录密码',
description: description:
'安全性高的密码可以使帐号更安全。建议您定期更换密码设置一个包含字母符号或数字中至少两项且长度超过6位的密码。', '安全性高的密码可以使帐号更安全。建议您定期更换密码设置一个包含字母符号或数字中至少两项且长度超过6位的密码。',
extra: ( // extra: (
<div> // <div>
当前密码强度为: // 当前密码强度为:
{ // {
[ // [
<span class="text-error"></span>, // <span class="text-error">弱</span>,
<span class="text-warning"></span>, // <span class="text-warning">中</span>,
<span class="text-success"></span>, // <span class="text-success">强</span>,
][data.securityLevel - 1] // ][user.securityLevel - 1]
} // }
</div> // </div>
), // ),
done: true, done: true,
action: () => { action: () => {
this.onOpen(this.updateForm) this.onOpen(this.updatePwdForm)
}, },
}) })
//手机 //手机
index.push({ index.push({
title: '手机绑定(发送验证码到手机,未实现)', title: '手机绑定',
description: ( description: (
<div> <div>
手机号可以直接用于登录找回密码等 手机号可以直接用于登录找回密码等
{data.phone && ( {user.phone && (
<span> <>
您已绑定了手机<b>{data.phone}</b> 您已绑定了手机<b>{user.phone}</b>
</span> </>
)} )}
</div> </div>
), ),
done: !!data.phone, done: !!user.phone,
action: () => { action: () => {
this.onOpen(this.PhoneForm) this.onOpen(this.mhoneForm)
}, },
}) })
//邮箱 //邮箱
index.push({ index.push({
title: '邮箱绑定(发送验证码到邮箱,未实现)', title: '邮箱绑定',
description: ( description: (
<div> <div>
安全邮箱可以直接用于登录找回密码等 安全邮箱可以直接用于登录找回密码等
{data.email && ( {user.email && (
<span> <>
您已绑定了邮箱<b>{data.email}</b> 您已绑定了邮箱<b>{user.email}</b>
</span> </>
)} )}
</div> </div>
), ),
done: !!data.email, done: !!user.email,
action: () => { action: () => {
this.onOpen(this.MailForm) this.onOpen(this.mailForm)
}, },
}) })
this.setState({
info: index,
loading: false,
})
})
}
render() {
return ( return (
<Container mode="xxs">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form">
<h4 className="h4">安全设置</h4>
</div>
<List
dataSource={this.state.info}
bordered
item-layout="vertical"
renderItem={item => (
<List.Item extra={item.extra} slot="renderItem" slot-scope="item">
{item.done == true ? (
<> <>
<span className="text-success" slot="actions"> <List
dataSource={index}
itemLayout="vertical"
renderItem={item => (
<List.Item
extra={item.extra}
actions={[
item.done == true ? (
<QueryTableActions>
<span className="text-success">
<AntIcon className="mr-xxs" type="check-circle" /> <AntIcon className="mr-xxs" type="check-circle" />
已设置 已设置
</span> </span>
<a onClick={item.action} slot="actions"> <a onClick={item.action}>修改</a>
修改 </QueryTableActions>
</a>
</>
) : ( ) : (
<> <QueryTableActions>
<span className="text-warning" slot="actions"> <span className="text-warning">
<AntIcon className="mr-xxs" type="exclamation-circle" /> <AntIcon className="mr-xxs" type="exclamation-circle" />
未设置 未设置
</span> </span>
<a onClick={item.action} slot="actions"> <a onClick={item.action}>设置</a>
设置 </QueryTableActions>
</a> ),
</> ]}
)} >
<List.Item.Meta description={item.description} title={item.title} /> <List.Item.Meta description={item.description} title={item.title} />
</List.Item> </List.Item>
)} )}
/> />
<br /> <ModalForm title={`更新密码`} action={apiAction.updatePwd} ref={this.updatePwdForm}>
<ModalForm title={`更新密码`} action={apiAction.update} ref={this.updateForm}> <PasswordForm loadData={loadData} />
<PasswordForm />
</ModalForm> </ModalForm>
<Mail ref={this.MailForm} /> <Phone ref={this.mhoneForm} loadData={loadData} />
<Phone ref={this.PhoneForm} /> <Mail ref={this.mailForm} loadData={loadData} />
</Spin> </>
</Container>
) )
} }
} }

View File

@@ -1,380 +1,347 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { import { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd'
Form, import { AntIcon } from 'components'
Input,
InputNumber,
Modal,
Spin,
Steps,
Button,
Row,
Col,
message,
Select,
} from 'antd'
import { AntIcon, Container, IconSelector } from 'components'
import { cloneDeep, indexOf } from 'lodash'
import { api } from 'common/api' import { api } from 'common/api'
import { COUNT_DWON_KEY } from 'common/storage' import { COUNT_DWON_KEY } from 'common/storage'
import { Option } from 'antd/lib/mentions' import store from 'store'
import { set } from 'nprogress' import { cloneDeep } from 'lodash'
import { getKeyThenIncreaseKey } from 'antd/lib/message'
const initialValues = { const { getState } = store
orgcode: '',
target: '', const steps = [
code: '',
type: null,
}
var tempcode = ''
const { Step } = Steps
export default class form extends Component {
state = {
buttondisabled: true,
visible: false,
loading: false,
codeLoading: false,
current: 0,
countdown: 0,
sendOrNo: true,
type: [],
}
form = React.createRef()
//发送验证码
sendcode(data) {
this.setState({
codeLoading: true,
})
var reg = /^([a-zA-Z]|[0-9])(\w|)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/
let { target } = data.current.getFieldsValue()
let { type } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = data.current.getFieldsValue()
if (!reg.test(target) && type != '1' && type != '2') {
message.warn('请输入正确的邮箱')
this.setState({
codeLoading: true,
})
return
}
api.SendCode({
target: target,
type: type,
code: code,
orgcode: orgcode,
})
.then(res => {
if (res.success) {
this.addTime()
this.showcountdown()
}
})
.finally(() => {
this.setState({
codeLoading: false,
})
})
}
//进入下一步
next(data) {
this.setState({
loading: true,
})
let { target } = data.current.getFieldsValue()
let { type } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = data.current.getFieldsValue()
tempcode = data.current.getFieldsValue()
let form = {
target: target,
type: type,
code: code,
orgcode: orgcode,
}
api.CheckBindcode(form)
.then(res => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY)
let current = this.state.current + 1
this.setState({
form: {
...form,
type: null,
},
buttondisabled: true,
current: current,
})
}
})
.finally(() => {
this.setState({
loading: false,
})
})
}
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now()
var date = now + 60 * 1000 + 500
window.localStorage.setItem(COUNT_DWON_KEY, date)
}
/**
* 显示倒计时
*/
showcountdown() {
let _this = this
var Furdate = window.localStorage.getItem(COUNT_DWON_KEY)
var nowdate = new Date().getTime()
if (Furdate >= nowdate) {
this.setState({
sendOrNo: false,
countdown: parseInt((Furdate - nowdate) / 1000),
})
setTimeout(() => {
_this.showcountdown()
}, 1000)
} else {
this.setState({
sendOrNo: true,
})
}
}
//打开窗口
open = (data = {}) => {
this.setState({ visible: true, loading: true })
api.getLoginUser().then(({ data }) => {
let index = []
data.phone &&
index.push({
Title: '使用手机号' + data.phone + '进行验证 ',
Value: 1,
})
data.email &&
index.push({
Title: '使用邮箱' + data.email + '进行验证',
Value: 2,
})
this.setState({
type: index,
})
if (index.length > 0) {
this.form.current.setFieldsValue({
type: index[0].Value,
})
}
this.setState({ loading: false })
})
}
// 前一步
prev() {
window.localStorage.removeItem(COUNT_DWON_KEY)
let current = this.state.current - 1
this.setState({
current: current,
})
}
//完成
complete(data) {
let { target } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = tempcode
api.CheckBindcode({
target: target,
type: null,
code: code,
orgcode: orgcode,
}).then(res => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY)
message.success('改绑完成')
this.onResetFields()
}
})
}
onResetFields() {
setTimeout(() => {
this.setState({ visible: false })
this.setState({ current: 0 })
//window.localStorage.removeItem(COUNT_DWON_KEY)
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300)
}
render() {
let steps = [
{ {
title: '验证', title: '验证',
}, },
{ {
title: '绑定', title: '绑定',
}, },
] ]
const close = () => {
this.setState({ const reg = /^([a-zA-Z]|[0-9])(\w|)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/
const initState = {
visible: false, visible: false,
current: 0, loading: false,
// 可用于验证的类型
types: [],
// 当前步骤
currentStep: 0,
nextDisabled: true,
// 正在发送验证码
sendingCode: false,
// 冷却时间
countDown: 0,
}
export default class form extends Component {
state = cloneDeep(initState)
form = React.createRef()
orgCode = ''
//打开窗口
open = () => {
this.setState({ visible: true })
this.showCountDown()
const data = getState('user')
const types = []
data.phone &&
types.push({
title: `使用手机号${data.phone}进行验证`,
value: 1,
})
data.email &&
types.push({
title: `使用邮箱${data.email}进行验证`,
value: 2,
})
this.setState({ types })
}
close() {
this.setState(cloneDeep(initState))
}
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now() + 60 * 1000
window.localStorage.setItem(COUNT_DWON_KEY, now)
}
/**
* 显示倒计时
*/
showCountDown() {
const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY)
const nowTime = Date.now()
if (surplusTime >= nowTime) {
this.setState({
countDown: parseInt((surplusTime - nowTime) / 1000),
})
setTimeout(() => {
this.showCountDown()
}, 1000)
} else {
this.setState({ countDown: 0 })
}
}
//发送验证码
async onSendCode() {
const form = this.form.current
const valid = await form.validateFields()
if (!valid) {
return
}
this.setState({ sendingCode: true })
const data = form.getFieldsValue()
try {
await api.sysUserSendCode(data)
const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机'
Message.success(`已发送验证码到${typeName},请注意查收`)
this.addTime()
this.showCountDown()
} finally {
this.setState({ sendingCode: false })
}
}
// 下一步
async onNext() {
this.setState({ loading: true })
const data = this.form.current.getFieldsValue()
this.orgCode = data.orgCode
try {
await api.sysUserCheckBindcode(data)
window.localStorage.removeItem(COUNT_DWON_KEY)
this.setState({
nextDisabled: true,
currentStep: this.state.currentStep + 1,
})
} finally {
this.setState({ loading: false })
}
}
// 上一步
onPrev() {
window.localStorage.removeItem(COUNT_DWON_KEY)
this.setState({
currentStep: this.state.currentStep - 1,
}) })
} }
//完成
async onComplete() {
this.setState({ loading: true })
try {
await api.sysUserCheckBindcode({
...this.form.current.getFieldsValue(),
orgCode: this.orgCode,
})
await this.props.loadData()
window.localStorage.removeItem(COUNT_DWON_KEY)
Message.success('绑定邮箱成功')
this.close()
} finally {
this.setState({ loading: false })
}
}
renderForm() {
const { nextDisabled, sendingCode, countDown } = this.state
return (
<div className="yo-form-group">
<Form
ref={this.form}
onValuesChange={(changedValues, allValues) => {
this.setState({
nextDisabled: !(allValues.target && allValues.code),
})
}}
>
<Form.Item
label="邮箱"
name="target"
required={false}
rules={[
{ required: true, message: '请输入邮箱' },
{ pattern: reg, message: '邮箱格式错误' },
]}
>
<Input autoComplete="off" placeholder="请输入邮箱" />
</Form.Item>
<Form.Item label="验证码">
<Row gutter={16} align="middle">
<Col flex="1">
<Form.Item name="code" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col>
<Col>
{countDown ? (
<Button disabled>{countDown}秒后重新发送</Button>
) : (
<Button onClick={() => this.onSendCode()} loading={sendingCode}>
发送验证码
</Button>
)}
</Col>
</Row>
</Form.Item>
</Form>
<div className="text-center pt-md pb-md">
<Button
disabled={nextDisabled}
onClick={() => this.onComplete()}
type="primary"
>
绑定
</Button>
</div>
</div>
)
}
renderStepForm() {
const { types, currentStep, sendingCode, countDown } = this.state
return ( return (
<Container mode="xxs">
<Modal
footer={false}
onCancel={close}
destroyOnClose
visible={this.state.visible}
className="yo-modal-form"
title="绑定邮箱"
>
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form">
{this.state.type.length !== 0 ? (
<div className="yo-form-group"> <div className="yo-form-group">
<br />
<Row> <Row>
<Col flex="1" /> <Col span={12} offset={6} className="pt-md pb-md">
<Col flex="3"> <Steps current={currentStep}>
<Steps current={this.state.current}>
{steps.map(item => ( {steps.map(item => (
<Steps.Step <Steps.Step key={item.title} title={item.title} />
key={item.title}
title={item.title}
/>
))} ))}
</Steps> </Steps>
</Col> </Col>
<Col flex="1" />
</Row> </Row>
<br />
<Form <Form
ref={this.form} ref={this.form}
initialValues={initialValues}
onValuesChange={(changedValues, allValues) => { onValuesChange={(changedValues, allValues) => {
this.setState({ this.setState({
buttondisabled: !( nextDisabled: !(
allValues.orgcode || allValues.orgCode ||
(allValues.target && allValues.code) (allValues.target && allValues.code)
), ),
}) })
}} }}
> >
<div> {currentStep === 0 && (
{this.state.current == 0 && ( <>
<div> <Form.Item
<Form.Item label="选择验证方式" name="type"> label="选择验证方式"
<Select placeholder="请选择验证方式"> name="type"
{this.state.type.map(item => ( required={false}
<Select.Option rules={[{ required: true, message: '请选择一种验证方式' }]}
key={item.Title}
value={item.Value}
> >
{item.Title} <Select placeholder="请选择验证方式">
{types.map(item => (
<Select.Option key={item.value} value={item.value}>
{item.title}
</Select.Option> </Select.Option>
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item label="验证码" name="orgcode"> <Form.Item label="验证码">
<Row gutter={16}> <Row gutter={16}>
<Col flex="1"> <Col flex="1">
<Input placeholder="请输入验证码" /> <Form.Item name="orgCode" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col> </Col>
<Col> <Col>
{this.state.sendOrNo ? ( {countDown ? (
<Button <Button disabled>
onClick={() => { {countDown}
this.sendcode(this.form) 秒后重新发送
}}
>
<Spin
spinning={
this.state
.codeLoading
}
indicator={
<AntIcon type="loading" />
}
/>
发送验证码
</Button> </Button>
) : ( ) : (
<Button disabled> <Button
{this.state.countdown} onClick={() => this.onSendCode()}
秒后重新发送 loading={sendingCode}
>
发送验证码
</Button> </Button>
)} )}
</Col> </Col>
</Row> </Row>
</Form.Item> </Form.Item>
</div>
)}
{this.state.current == 1 && (
<div>
<Form.Item label="新邮箱号码" name="target">
<Input placeholder="请输入邮箱账号" />
</Form.Item>
<Form.Item label="验证码" name="code">
<Row gutter={16}>
<Col flex="1">
<Input placeholder="请输入六位验证码" />
</Col>
<Col>
{this.state.sendOrNo ? (
<Button
onClick={() => {
this.sendcode(this.form)
}}
>
<Spin
spinning={
this.state
.codeLoading
}
indicator={
<AntIcon type="loading" />
}
/>
发送验证码
</Button>
) : (
<Button disabled>
{this.state.countdown}
秒后重新发送
</Button>
)}
</Col>
</Row>
</Form.Item>
</div>
)}
</div>
</Form>
<br />
<div className="text-center">
{this.state.current == 0 && (
<>
<div>
<Button
onClick={() => this.next(this.form)}
type="primary"
disabled={this.state.buttondisabled}
>
下一步
</Button>
</div>
<br />
</> </>
)} )}
{this.state.current == 1 && ( {currentStep === 1 && (
<> <>
{this.state.current > 0 && ( <Form.Item
<Button onClick={() => this.prev()}> label="新邮箱"
前一步 name="target"
required={false}
rules={[
{ required: true, message: '请输入邮箱' },
{ pattern: reg, message: '邮箱格式错误' },
]}
>
<Input autoComplete="off" placeholder="请输入邮箱" />
</Form.Item>
<Form.Item label="验证码">
<Row gutter={16}>
<Col flex="1">
<Form.Item name="code" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col>
<Col>
{countDown ? (
<Button disabled>
{countDown}
秒后重新发送
</Button>
) : (
<Button
onClick={() => this.onSendCode()}
loading={sendingCode}
>
发送验证码
</Button> </Button>
)} )}
</Col>
</Row>
</Form.Item>
</>
)}
</Form>
<div className="text-center pt-md pb-md">
{currentStep === 0 && (
<>
<Button <Button
disabled={this.state.buttondisabled} onClick={() => this.onNext(this.form)}
onClick={() => this.complete(this.form)} type="primary"
disabled={this.state.nextDisabled}
>
下一步绑定
</Button>
</>
)}
{currentStep === 1 && (
<>
<Button onClick={() => this.onPrev()} className="mr-sm">
上一步验证
</Button>
<Button
disabled={this.state.nextDisabled}
onClick={() => this.onComplete()}
type="primary" type="primary"
> >
完成 完成
@@ -383,72 +350,27 @@ export default class form extends Component {
)} )}
</div> </div>
</div> </div>
) : ( )
<div className="yo-form-group">
<Form
initialValues={initialValues}
ref={this.form}
onValuesChange={(changedValues, allValues) => {
this.setState({
buttondisabled: !(
allValues.target && allValues.code
),
})
}}
>
<Form.Item label="请输入邮箱" name="target">
<Input placeholder="请输入邮箱号码" />
</Form.Item>
<Form.Item label="验证码" name="code">
<Row gutter={16} align="middle">
<Col flex="1">
<Input placeholder="请输入验证码" />
</Col>
<Col>
{this.state.sendOrNo ? (
<Button
onClick={() => {
this.sendcode(this.form)
}}
>
<Spin
spinning={this.state.codeLoading}
indicator={
<AntIcon type="loading" />
} }
/>
发送验证码 render() {
</Button> const { visible, loading, types } = this.state
) : (
<Button disabled> return (
{this.state.countdown}秒后重新发送 <Modal
</Button> destroyOnClose
)} footer={false}
</Col> onCancel={() => this.close()}
</Row> visible={visible}
</Form.Item> className="yo-modal-form"
</Form> title="绑定邮箱"
<br />
<Row>
<Col flex="1" />
<Col flex="1" align="middle">
<Button
disabled={this.state.buttondisabled}
onClick={() => this.complete(this.form)}
type="primary"
> >
绑定 <Spin spinning={loading} indicator={<AntIcon type="loading" />}>
</Button> <div className="yo-form">
</Col> {types.length ? this.renderStepForm() : this.renderForm()}
<Col flex="1" />
</Row>
<br />
</div>
)}
</div> </div>
</Spin> </Spin>
</Modal> </Modal>
</Container>
) )
} }
} }

View File

@@ -1,15 +1,9 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Form, Input, InputNumber, Spin } from 'antd' import { Form, Input } from 'antd'
import { AntIcon, IconSelector } from 'components'
import { cloneDeep } from 'lodash'
const initialValues = {
sort: 100,
}
export default class form extends Component { export default class form extends Component {
state = { state = {
// 加载状态 // 加载状态
loading: true,
exist: false, exist: false,
} }
// 表单实例 // 表单实例
@@ -31,25 +25,7 @@ export default class form extends Component {
* [异步,必要] * [异步,必要]
* @param {*} params * @param {*} params
*/ */
async fillData(params) { async fillData() {}
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const exist = !!params.record
this.setState({
exist,
})
this.record = {
...this.record,
}
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({
loading: false,
})
}
/** /**
* 获取数据 * 获取数据
@@ -63,9 +39,6 @@ export default class form extends Component {
const valid = await form.validateFields() const valid = await form.validateFields()
if (valid) { if (valid) {
const postData = form.getFieldsValue() const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
}
//#region 从前段转换后端所需格式 //#region 从前段转换后端所需格式
//#endregion //#endregion
return postData return postData
@@ -75,32 +48,39 @@ export default class form extends Component {
render() { render() {
return ( return (
<Form className="yo-form" ref={this.form}> <Form className="yo-form" ref={this.form}>
<Spin spinning={this.state.loading}>
{/* <AntIcon slot="indicator" spin type="loading" /> */}
<div className="yo-form-group"> <div className="yo-form-group">
<Form.Item <Form.Item
label="旧密码" label="旧密码"
rules={[{ required: true, message: '请输入旧密码' }]} rules={[{ required: true, message: '请输入旧密码' }]}
name="password" name="password"
> >
<Input placeholder="请输入旧密码" /> <Input.Password autoComplete="off" placeholder="请输入旧密码" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="新密码" label="新密码"
rules={[{ required: true, message: '请输入新密码' }]} rules={[{ required: true, message: '请输入新密码' }]}
name="newPassword" name="newPassword"
> >
<Input placeholder="请输入新密码" /> <Input.Password autoComplete="off" placeholder="请输入新密码" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="确认新密码" label="确认新密码"
rules={[{ required: true, message: '确认新密码' }]} rules={[
{ required: true, message: '请确认新密码' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('newPassword') === value) {
return Promise.resolve()
}
return Promise.reject(new Error('确认新密码不匹配'))
},
}),
]}
name="confirm" name="confirm"
> >
<Input placeholder="请确认新密码" /> <Input.Password autoComplete="off" placeholder="请确认新密码" />
</Form.Item> </Form.Item>
</div> </div>
</Spin>
</Form> </Form>
) )
} }

View File

@@ -1,381 +1,347 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { import { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd'
Form, import { AntIcon } from 'components'
Input,
InputNumber,
Modal,
Spin,
Steps,
Button,
Row,
Col,
message,
Select,
} from 'antd'
import { AntIcon, Container, IconSelector } from 'components'
import { cloneDeep, indexOf } from 'lodash'
import { api } from 'common/api' import { api } from 'common/api'
import { COUNT_DWON_KEY } from 'common/storage' import { COUNT_DWON_KEY } from 'common/storage'
import { Option } from 'antd/lib/mentions' import store from 'store'
import { set } from 'nprogress' import { cloneDeep } from 'lodash'
import { getKeyThenIncreaseKey } from 'antd/lib/message'
const initialValues = { const { getState } = store
orgcode: '',
target: '', const steps = [
code: '',
type: null,
}
var tempcode = ''
const { Step } = Steps
export default class form extends Component {
state = {
buttondisabled: true,
visible: false,
loading: false,
codeLoading: false,
current: 0,
countdown: 0,
sendOrNo: true,
type: [],
}
form = React.createRef()
//发送验证码
sendcode(data) {
this.setState({
codeLoading: true,
})
var reg =
/^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/
let { target } = data.current.getFieldsValue()
let { type } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = data.current.getFieldsValue()
if (!reg.test(target) && type != '1' && type != '2') {
message.warn('请输入正确的手机号码')
this.setState({
codeLoading: true,
})
return
}
api.SendCode({
target: target,
type: type,
code: code,
orgcode: orgcode,
})
.then(res => {
if (res.success) {
this.addTime()
this.showcountdown()
}
})
.finally(() => {
this.setState({
codeLoading: false,
})
})
}
//进入下一步
next(data) {
this.setState({
loading: true,
})
let { target } = data.current.getFieldsValue()
let { type } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = data.current.getFieldsValue()
tempcode = data.current.getFieldsValue()
let form = {
target: target,
type: type,
code: code,
orgcode: orgcode,
}
api.CheckBindcode(form)
.then(res => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY)
let current = this.state.current + 1
this.setState({
form: {
...form,
type: null,
},
buttondisabled: true,
current: current,
})
}
})
.finally(() => {
this.setState({
loading: false,
})
})
}
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now()
var date = now + 60 * 1000 + 500
window.localStorage.setItem(COUNT_DWON_KEY, date)
}
/**
* 显示倒计时
*/
showcountdown() {
let _this = this
var Furdate = window.localStorage.getItem(COUNT_DWON_KEY)
var nowdate = new Date().getTime()
if (Furdate >= nowdate) {
this.setState({
sendOrNo: false,
countdown: parseInt((Furdate - nowdate) / 1000),
})
setTimeout(() => {
_this.showcountdown()
}, 1000)
} else {
this.setState({
sendOrNo: true,
})
}
}
//打开窗口
open = (data = {}) => {
this.setState({ visible: true, loading: true })
api.getLoginUser().then(({ data }) => {
let index = []
data.phone &&
index.push({
Title: '使用手机号' + data.phone + '进行验证 ',
Value: 1,
})
data.email &&
index.push({
Title: '使用邮箱' + data.email + '进行验证',
Value: 2,
})
this.setState({
type: index,
})
if (index.length > 0) {
this.form.current.setFieldsValue({
type: index[0].Value,
})
}
this.setState({ loading: false })
})
}
// 前一步
prev() {
window.localStorage.removeItem(COUNT_DWON_KEY)
let current = this.state.current - 1
this.setState({
current: current,
})
}
//完成
complete(data) {
let { target } = data.current.getFieldsValue()
let { code } = data.current.getFieldsValue()
let { orgcode } = tempcode
api.CheckBindcode({
target: target,
type: null,
code: code,
orgcode: orgcode,
}).then(res => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY)
message.success('改绑完成')
this.onResetFields()
}
})
}
onResetFields() {
setTimeout(() => {
this.setState({ visible: false })
this.setState({ current: 0 })
//window.localStorage.removeItem(COUNT_DWON_KEY)
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300)
}
render() {
let steps = [
{ {
title: '验证', title: '验证',
}, },
{ {
title: '绑定', title: '绑定',
}, },
] ]
const close = () => {
this.setState({ const reg = /^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/
const initState = {
visible: false, visible: false,
current: 0, loading: false,
// 可用于验证的类型
types: [],
// 当前步骤
currentStep: 0,
nextDisabled: true,
// 正在发送验证码
sendingCode: false,
// 冷却时间
countDown: 0,
}
export default class form extends Component {
state = cloneDeep(initState)
form = React.createRef()
orgCode = ''
//打开窗口
open = () => {
this.setState({ visible: true })
this.showCountDown()
const data = getState('user')
const types = []
data.phone &&
types.push({
title: `使用手机号${data.phone}进行验证`,
value: 1,
})
data.email &&
types.push({
title: `使用邮箱${data.email}进行验证`,
value: 2,
})
this.setState({ types })
}
close() {
this.setState(cloneDeep(initState))
}
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now() + 60 * 1000
window.localStorage.setItem(COUNT_DWON_KEY, now)
}
/**
* 显示倒计时
*/
showCountDown() {
const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY)
const nowTime = Date.now()
if (surplusTime >= nowTime) {
this.setState({
countDown: parseInt((surplusTime - nowTime) / 1000),
})
setTimeout(() => {
this.showCountDown()
}, 1000)
} else {
this.setState({ countDown: 0 })
}
}
//发送验证码
async onSendCode() {
const form = this.form.current
const valid = await form.validateFields()
if (!valid) {
return
}
this.setState({ sendingCode: true })
const data = form.getFieldsValue()
try {
await api.sysUserSendCode(data)
const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机'
Message.success(`已发送验证码到${typeName},请注意查收`)
this.addTime()
this.showCountDown()
} finally {
this.setState({ sendingCode: false })
}
}
// 下一步
async onNext() {
this.setState({ loading: true })
const data = this.form.current.getFieldsValue()
this.orgCode = data.orgCode
try {
await api.sysUserCheckBindcode(data)
window.localStorage.removeItem(COUNT_DWON_KEY)
this.setState({
nextDisabled: true,
currentStep: this.state.currentStep + 1,
})
} finally {
this.setState({ loading: false })
}
}
// 上一步
onPrev() {
window.localStorage.removeItem(COUNT_DWON_KEY)
this.setState({
currentStep: this.state.currentStep - 1,
}) })
} }
//完成
async onComplete() {
this.setState({ loading: true })
try {
await api.sysUserCheckBindcode({
...this.form.current.getFieldsValue(),
orgCode: this.orgCode,
})
await this.props.loadData()
window.localStorage.removeItem(COUNT_DWON_KEY)
Message.success('绑定手机号成功')
this.close()
} finally {
this.setState({ loading: false })
}
}
renderForm() {
const { nextDisabled, sendingCode, countDown } = this.state
return (
<div className="yo-form-group">
<Form
ref={this.form}
onValuesChange={(changedValues, allValues) => {
this.setState({
nextDisabled: !(allValues.target && allValues.code),
})
}}
>
<Form.Item
label="手机号码"
name="target"
required={false}
rules={[
{ required: true, message: '请输入手机号码' },
{ pattern: reg, message: '手机号码格式错误' },
]}
>
<Input autoComplete="off" placeholder="请输入手机号码" />
</Form.Item>
<Form.Item label="验证码">
<Row gutter={16} align="middle">
<Col flex="1">
<Form.Item name="code" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col>
<Col>
{countDown ? (
<Button disabled>{countDown}秒后重新发送</Button>
) : (
<Button onClick={() => this.onSendCode()} loading={sendingCode}>
发送验证码
</Button>
)}
</Col>
</Row>
</Form.Item>
</Form>
<div className="text-center pt-md pb-md">
<Button
disabled={nextDisabled}
onClick={() => this.onComplete()}
type="primary"
>
绑定
</Button>
</div>
</div>
)
}
renderStepForm() {
const { types, currentStep, sendingCode, countDown } = this.state
return ( return (
<Container mode="xxs">
<Modal
footer={false}
onCancel={close}
destroyOnClose
visible={this.state.visible}
className="yo-modal-form"
title="绑定手机"
>
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form">
{this.state.type.length !== 0 ? (
<div className="yo-form-group"> <div className="yo-form-group">
<br />
<Row> <Row>
<Col flex="1" /> <Col span={12} offset={6} className="pt-md pb-md">
<Col flex="3"> <Steps current={currentStep}>
<Steps current={this.state.current}>
{steps.map(item => ( {steps.map(item => (
<Steps.Step <Steps.Step key={item.title} title={item.title} />
key={item.title}
title={item.title}
/>
))} ))}
</Steps> </Steps>
</Col> </Col>
<Col flex="1" />
</Row> </Row>
<br />
<Form <Form
ref={this.form} ref={this.form}
initialValues={initialValues}
onValuesChange={(changedValues, allValues) => { onValuesChange={(changedValues, allValues) => {
this.setState({ this.setState({
buttondisabled: !( nextDisabled: !(
allValues.orgcode || allValues.orgCode ||
(allValues.target && allValues.code) (allValues.target && allValues.code)
), ),
}) })
}} }}
> >
<div> {currentStep === 0 && (
{this.state.current == 0 && ( <>
<div> <Form.Item
<Form.Item label="选择验证方式" name="type"> label="选择验证方式"
<Select placeholder="请选择验证方式"> name="type"
{this.state.type.map(item => ( required={false}
<Select.Option rules={[{ required: true, message: '请选择一种验证方式' }]}
key={item.Title}
value={item.Value}
> >
{item.Title} <Select placeholder="请选择验证方式">
{types.map(item => (
<Select.Option key={item.value} value={item.value}>
{item.title}
</Select.Option> </Select.Option>
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item label="验证码" name="orgcode"> <Form.Item label="验证码">
<Row gutter={16}> <Row gutter={16}>
<Col flex="1"> <Col flex="1">
<Input placeholder="请输入验证码" /> <Form.Item name="orgCode" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col> </Col>
<Col> <Col>
{this.state.sendOrNo ? ( {countDown ? (
<Button <Button disabled>
onClick={() => { {countDown}
this.sendcode(this.form) 秒后重新发送
}}
>
<Spin
spinning={
this.state
.codeLoading
}
indicator={
<AntIcon type="loading" />
}
/>
发送验证码
</Button> </Button>
) : ( ) : (
<Button disabled> <Button
{this.state.countdown} onClick={() => this.onSendCode()}
秒后重新发送 loading={sendingCode}
>
发送验证码
</Button> </Button>
)} )}
</Col> </Col>
</Row> </Row>
</Form.Item> </Form.Item>
</div>
)}
{this.state.current == 1 && (
<div>
<Form.Item label="新手机号码" name="target">
<Input placeholder="请输入手机账号" />
</Form.Item>
<Form.Item label="验证码" name="code">
<Row gutter={16}>
<Col flex="1">
<Input placeholder="请输入六位验证码" />
</Col>
<Col>
{this.state.sendOrNo ? (
<Button
onClick={() => {
this.sendcode(this.form)
}}
>
<Spin
spinning={
this.state
.codeLoading
}
indicator={
<AntIcon type="loading" />
}
/>
发送验证码
</Button>
) : (
<Button disabled>
{this.state.countdown}
秒后重新发送
</Button>
)}
</Col>
</Row>
</Form.Item>
</div>
)}
</div>
</Form>
<br />
<div className="text-center">
{this.state.current == 0 && (
<>
<div>
<Button
onClick={() => this.next(this.form)}
type="primary"
disabled={this.state.buttondisabled}
>
下一步
</Button>
</div>
<br />
</> </>
)} )}
{this.state.current == 1 && ( {currentStep === 1 && (
<> <>
{this.state.current > 0 && ( <Form.Item
<Button onClick={() => this.prev()}> label="新手机号码"
前一步 name="target"
required={false}
rules={[
{ required: true, message: '请输入手机号码' },
{ pattern: reg, message: '手机号码格式错误' },
]}
>
<Input autoComplete="off" placeholder="请输入手机号码" />
</Form.Item>
<Form.Item label="验证码">
<Row gutter={16}>
<Col flex="1">
<Form.Item name="code" noStyle>
<Input autoComplete="off" placeholder="请输入验证码" />
</Form.Item>
</Col>
<Col>
{countDown ? (
<Button disabled>
{countDown}
秒后重新发送
</Button>
) : (
<Button
onClick={() => this.onSendCode()}
loading={sendingCode}
>
发送验证码
</Button> </Button>
)} )}
</Col>
</Row>
</Form.Item>
</>
)}
</Form>
<div className="text-center pt-md pb-md">
{currentStep === 0 && (
<>
<Button <Button
disabled={this.state.buttondisabled} onClick={() => this.onNext(this.form)}
onClick={() => this.complete(this.form)} type="primary"
disabled={this.state.nextDisabled}
>
下一步绑定
</Button>
</>
)}
{currentStep === 1 && (
<>
<Button onClick={() => this.onPrev()} className="mr-sm">
上一步验证
</Button>
<Button
disabled={this.state.nextDisabled}
onClick={() => this.onComplete()}
type="primary" type="primary"
> >
完成 完成
@@ -384,72 +350,27 @@ export default class form extends Component {
)} )}
</div> </div>
</div> </div>
) : ( )
<div className="yo-form-group">
<Form
initialValues={initialValues}
ref={this.form}
onValuesChange={(changedValues, allValues) => {
this.setState({
buttondisabled: !(
allValues.target && allValues.code
),
})
}}
>
<Form.Item label="请输入手机" name="target">
<Input placeholder="请输入手机号码" />
</Form.Item>
<Form.Item label="验证码" name="code">
<Row gutter={16} align="middle">
<Col flex="1">
<Input placeholder="请输入验证码" />
</Col>
<Col>
{this.state.sendOrNo ? (
<Button
onClick={() => {
this.sendcode(this.form)
}}
>
<Spin
spinning={this.state.codeLoading}
indicator={
<AntIcon type="loading" />
} }
/>
发送验证码 render() {
</Button> const { visible, loading, types } = this.state
) : (
<Button disabled> return (
{this.state.countdown}秒后重新发送 <Modal
</Button> destroyOnClose
)} footer={false}
</Col> onCancel={() => this.close()}
</Row> visible={visible}
</Form.Item> className="yo-modal-form"
</Form> title="绑定手机"
<br />
<Row>
<Col flex="1" />
<Col flex="1" align="middle">
<Button
disabled={this.state.buttondisabled}
onClick={() => this.complete(this.form)}
type="primary"
> >
绑定 <Spin spinning={loading} indicator={<AntIcon type="loading" />}>
</Button> <div className="yo-form">
</Col> {types.length ? this.renderStepForm() : this.renderForm()}
<Col flex="1" />
</Row>
<br />
</div>
)}
</div> </div>
</Spin> </Spin>
</Modal> </Modal>
</Container>
) )
} }
} }

View File

@@ -5,7 +5,7 @@ import { cloneDeep } from 'lodash'
import getDictData from 'util/dic' import getDictData from 'util/dic'
const initialValues = { const initialValues = {
sort: 100 sort: 100,
} }
export default class form extends Component { export default class form extends Component {
state = { state = {
@@ -13,8 +13,8 @@ export default class form extends Component {
loading: true, loading: true,
exist: false, exist: false,
codes: { codes: {
dicAreacodeType: [] areacodeType: [],
} },
} }
// 表单实例 // 表单实例
form = React.createRef() form = React.createRef()
@@ -36,26 +36,25 @@ export default class form extends Component {
* @param {*} params * @param {*} params
*/ */
async fillData(params) { async fillData(params) {
this.record = cloneDeep(params.record) this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式 //#region 从后端转换成前段所需格式
const codes = await getDictData('dic_areacode_type') const codes = await getDictData('areacode_type')
const exist = !!params.record; const exist = !!params.record
this.setState({ this.setState({
codes, codes,
exist exist,
}) })
this.record = { this.record = {
...this.record ...this.record,
} }
//#endregion //#endregion
this.form.current.setFieldsValue(this.record) this.form.current.setFieldsValue(this.record)
this.setState({ this.setState({
loading: false loading: false,
}) })
} }
@@ -82,43 +81,55 @@ export default class form extends Component {
render() { render() {
return ( return (
<Form <Form initialValues={initialValues} ref={this.form} className="yo-form">
initialValues={initialValues}
ref={this.form}
className="yo-form"
>
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}> <Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group"> <div className="yo-form-group">
{/* 表单控件 */} {/* 表单控件 */}
<Form.Item label="区域类型" name="levelType" rules={[{ required: true, message: '请选择区域类型' }]}> <Form.Item
<Select placeholder="请选择区域类型" suffixIcon={<AntIcon type="lock" v-if={this.state.exist} />}> label="区域类型"
{ name="levelType"
this.state.codes.dicAreacodeType.map(item => { rules={[{ required: true, message: '请选择区域类型' }]}
return <Select.Option >
key={item.code} <Select
value={+item.code} placeholder="请选择区域类型"
>{item.value}</Select.Option> suffixIcon={<AntIcon type="lock" v-if={this.state.exist} />}
}) >
} {this.state.codes.areacodeType.map(item => {
return (
<Select.Option key={item.code} value={+item.code}>
{item.value}
</Select.Option>
)
})}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item label="区域名称" name="name" rules={[{ required: true, message: '请输入区域名称' }]}> <Form.Item
label="区域名称"
name="name"
rules={[{ required: true, message: '请输入区域名称' }]}
>
<Input autoComplete="off" placeholder="请输入区域名称" /> <Input autoComplete="off" placeholder="请输入区域名称" />
</Form.Item> </Form.Item>
<Form.Item label="区域编码" name="code" tooltip="用于系统内部使用,添加后不可更改"> <Form.Item
<Input disabled={this.state.exist} placeholder="请输入区域编码" suffix={this.state.exist && <AntIcon type="lock" />}> label="区域编码"
</Input> name="code"
tooltip="用于系统内部使用,添加后不可更改"
>
<Input
disabled={this.state.exist}
placeholder="请输入区域编码"
suffix={this.state.exist && <AntIcon type="lock" />}
></Input>
</Form.Item> </Form.Item>
<Form.Item label="行政编码" name="adCode" tooltip="国家规定的区划代码,可随实际情况更改而更改"> <Form.Item
<Input placeholder="请输入区域编码" > label="行政编码"
</Input> name="adCode"
tooltip="国家规定的区划代码,可随实际情况更改而更改"
>
<Input placeholder="请输入区域编码"></Input>
</Form.Item> </Form.Item>
<Form.Item label="排序" name="sort"> <Form.Item label="排序" name="sort">
<InputNumber <InputNumber min={0} placeholder="请输入排序" className="w-100-p" />
min={0}
placeholder="请输入排序"
className="w-100-p"
/>
</Form.Item> </Form.Item>
<Form.Item label="备注" name="note"> <Form.Item label="备注" name="note">
<Input.TextArea placeholder="请输入备注" /> <Input.TextArea placeholder="请输入备注" />

View File

@@ -64,6 +64,7 @@ export default class index extends Component {
{ {
title: '机构类型', title: '机构类型',
dataIndex: 'type', dataIndex: 'type',
width: 120,
sorter: true, sorter: true,
render: text => <>{this.bindCodeValue(text, 'org_type')}</>, render: text => <>{this.bindCodeValue(text, 'org_type')}</>,
}, },

View File

@@ -13,6 +13,8 @@ export default class index extends Component {
focusUser: false, focusUser: false,
focusPassword: false, focusPassword: false,
btnDisabled: true,
} }
backgroundImage = require(`assets/image/login-bg-0${Math.floor(Math.random() * 4)}.jpg`) backgroundImage = require(`assets/image/login-bg-0${Math.floor(Math.random() * 4)}.jpg`)
@@ -36,6 +38,7 @@ export default class index extends Component {
Message.success('登录成功') Message.success('登录成功')
this.props.history.replace('/') this.props.history.replace('/')
} else { } else {
this.setState({ loading: false })
Message.error(message) Message.error(message)
} }
}) })
@@ -48,15 +51,26 @@ export default class index extends Component {
} }
render() { render() {
const { loading, focusUser, focusPassword, btnDisabled } = this.state
return ( return (
<div className="yo-login"> <div className="yo-login">
<img src={this.backgroundImage.default} alt="" /> <img src={this.backgroundImage.default} alt="" />
<div className="yo-login--placeholder"> <div className="yo-login--placeholder">
<Container mode="sm"> <Container mode="sm">
<Form ref={this.form} layout="vertical" onFinish={this.onLogin}> <Form
ref={this.form}
layout="vertical"
onFinish={this.onLogin}
onValuesChange={(changedValues, values) => {
this.setState({
btnDisabled: !values.account || !values.password,
})
}}
>
<Form.Item <Form.Item
name="account" name="account"
className={!this.state.focusUser && 'yo-login--label'} className={!focusUser && 'yo-login--label'}
colon={false} colon={false}
label="用户名" label="用户名"
> >
@@ -75,7 +89,7 @@ export default class index extends Component {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="password" name="password"
className={!this.state.focusPassword && 'yo-login--label'} className={!focusPassword && 'yo-login--label'}
colon={false} colon={false}
label="密码" label="密码"
> >
@@ -95,8 +109,8 @@ export default class index extends Component {
</Form.Item> </Form.Item>
<Form.Item className="mt-lg"> <Form.Item className="mt-lg">
<Button <Button
disabled={this.form.user === '' || this.form.password === ''} disabled={btnDisabled}
loading={this.state.loading} loading={loading}
block block
htmlType="submit" htmlType="submit"
size="large" size="large"

View File

@@ -94,9 +94,10 @@ class User extends Component {
<AntIcon type="user" className="mr-sm" /> <AntIcon type="user" className="mr-sm" />
个人中心 个人中心
</Menu.Item> </Menu.Item>
<Menu.Item key="2">其他菜单</Menu.Item> <Menu.Item key="2">
<Menu.Item key="3">其他菜单</Menu.Item> <AntIcon type="file-text" className="mr-sm" />
<Menu.Item key="4">其他菜单</Menu.Item> 操作手册
</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="-1" onClick={() => this.onLogout()}> <Menu.Item key="-1" onClick={() => this.onLogout()}>
<AntIcon type="logout" className="mr-sm" /> <AntIcon type="logout" className="mr-sm" />

View File

@@ -3762,6 +3762,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
sha.js "^2.4.8" sha.js "^2.4.8"
cropperjs@^1.5.12:
version "1.5.12"
resolved "https://registry.nlark.com/cropperjs/download/cropperjs-1.5.12.tgz?cache=0&sync_timestamp=1623485718941&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcropperjs%2Fdownload%2Fcropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50"
integrity sha1-2cDbK/uMDXadUXOej5FrvEThD1A=
cross-fetch@^3.0.4: cross-fetch@^3.0.4:
version "3.1.4" version "3.1.4"
resolved "https://registry.npm.taobao.org/cross-fetch/download/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" resolved "https://registry.npm.taobao.org/cross-fetch/download/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
@@ -9686,6 +9691,13 @@ react-color@^2.19.3:
reactcss "^1.2.0" reactcss "^1.2.0"
tinycolor2 "^1.4.1" tinycolor2 "^1.4.1"
react-cropper@^2.1.8:
version "2.1.8"
resolved "https://registry.nlark.com/react-cropper/download/react-cropper-2.1.8.tgz?cache=0&sync_timestamp=1624865139834&other_urls=https%3A%2F%2Fregistry.nlark.com%2Freact-cropper%2Fdownload%2Freact-cropper-2.1.8.tgz#bf35a7de65769f8ad357e8ae884e791fe3fe9212"
integrity sha1-vzWn3mV2n4rTV+iuiE55H+P+khI=
dependencies:
cropperjs "^1.5.12"
react-dev-utils@^11.0.3: react-dev-utils@^11.0.3:
version "11.0.4" version "11.0.4"
resolved "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-11.0.4.tgz?cache=0&sync_timestamp=1615231838520&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" resolved "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-11.0.4.tgz?cache=0&sync_timestamp=1615231838520&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a"