Commit 87f61707 authored by 周宏民's avatar 周宏民

feat: 乐山登录页

parent 35d37a0e
Pipeline #94278 waiting for manual action with stages
......@@ -50,6 +50,9 @@
},
{
"title": "项目 - 大悟"
},
{
"title": "项目 - 乐山"
}
]
}
......@@ -8,7 +8,7 @@ import omit from 'omit.js';
import { useIntl } from '@wisdom-utils/components';
import classNames from 'classnames';
import services from '../../../../../api/service/base';
import { appService } from '@/api';
import styles from './index.less';
import LoginContext from './LoginContext';
import ItemMap from './map';
......@@ -99,7 +99,7 @@ const LoginItem = props => {
const onGetCaptcha = useCallback(mobile => {
// eslint-disable-next-line no-undef
services
appService
.sendMessVerificationCode({
phoneNumber: mobile,
ignoreSite: true,
......@@ -151,19 +151,17 @@ const LoginItem = props => {
return Promise.reject(intl.formatMessage({ id: 'pages.login.phoneLogin.errorMessage' }));
};
// eslint-disable-next-line no-shadow
const rules = [
...options.rules,
options.rules.push({
validator: equalsValidate,
}),
];
const rulesArr = [...rules];
rulesArr.push({
validator: equalsValidate,
});
delete options.rules;
return (
<FormItem shouldUpdate style={{ marginBottom: '14px' }}>
{({ getFieldValue, validateFields }) => (
<>
<FormItem name={name} {...options} rules={rules}>
<FormItem name={name} {...options} rules={rulesArr}>
<Input {...customProps} {...inputProps} />
</FormItem>
<Button
......
......@@ -2,7 +2,7 @@
* @Author: 634665781 634665781@qq.com
* @Date: 2022-07-08 14:28:01
* @LastEditors: Please set LastEditors
* @LastEditTime: 2024-10-30 17:39:32
* @LastEditTime: 2024-11-29 09:12:26
* @FilePath: \CivWeb\src\pages\user\login\index.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -60,6 +60,7 @@ import TaihuLogin from './template/project/taihu';
import DeqingLogin from './template/project/deqing';
import RuiyunLogin from './template/project/ruiyun';
import DawuLogin from './template/project/dawu';
import LeshanLogin from './template/project/leshan';
import { AppInitState } from '../../../render';
const LoginTemplate = {
'新春.html': baseLoginNewYear,
......@@ -117,6 +118,7 @@ const LoginTemplate = {
'项目 - 德庆.html': DeqingLogin,
'项目 - 瑞云.html': RuiyunLogin,
'项目 - 大悟.html': DawuLogin,
'项目 - 乐山.html': LeshanLogin,
default: BaseLogin,
};
/* eslint-disable */
......
/*
* @Title:
* @Author: hongmye
* @Date: 2024-11-28 14:47:37
*/
import { Form, Checkbox } from 'antd';
import React from 'react';
import { useIntl } from '@wisdom-utils/components';
import LoginForm from '../../../components/Login';
import LoginMessage from '../../../js/loginMessage';
import passIcon from './images/ls-pwd.png';
import userIcon from './images/ls-id.png';
import styles from './index.less';
/* eslint-disable */
const { UserName, Password, Validate, Submit } = LoginForm;
const useAccount = props => (
<LoginForm onSubmit={props.onSubmit} welcome={props.welcome}>
{props.status === 'error' && props.type === 'account' && !props.submitting && (
<LoginMessage
content={useIntl().formatMessage({
id: 'pages.login.accountLogin.errorMessage',
})}
/>
)}
<UserName
name="userName"
placeholder={useIntl().formatMessage({
id: 'pages.login.username.placeholder',
})}
prefix={<img src={userIcon} width={15} />}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.username.required',
}),
},
]}
/>
<Password
name="password"
placeholder={useIntl().formatMessage({
id: 'pages.login.password.placeholder',
})}
prefix={<img src={passIcon} width={15} />}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.password.required',
}),
},
]}
/>
{props?.isValidate ? <Validate name="validate" /> : null}
<div>
<Checkbox checked={props.autoLogin} onChange={e => props.setAutoLogin(e.target.checked)}>
{useIntl().formatMessage({ id: 'pages.login.rememberMe' })}
</Checkbox>
</div>
<Submit className={styles.submitBtn} loading={props.submitting}>
{useIntl().formatMessage({ id: 'pages.login.submit' })}
</Submit>
</LoginForm>
);
export default useAccount;
/*
* @Title:
* @Author: hongmye
* @Date: 2024-11-29 13:34:32
*/
import { Form } from 'antd';
import React from 'react';
import LoginForm from '../../../components/Login';
import LoginMessage from '../../../js/loginMessage';
import phoneIcon from './images/ls_phone.png';
import passIcon from './images/ls-pwd.png';
import styles from './index.less';
const { Mobile, Captcha, Submit } = LoginForm;
const usePhone = props => {
const [form] = Form.useForm();
return (
<LoginForm form={form} onSubmit={props.onSubmit} welcome={props.welcome}>
{props.status === 'error' && props.type === 'account' && !props.submitting && (
<LoginMessage content="手机号码未注册" />
)}
<Mobile name="mobile" placeholder="请输入手机号码" prefix={<img src={phoneIcon} width={15} alt="" />} />
<Captcha
name="captcha"
placeholder="请输入短信验证码"
countDown={60}
getCaptchaButtonText=""
getCaptchaSecondText="秒"
autoComplete="off"
prefix={<img src={passIcon} width={15} alt="" />}
rules={[
{
required: true,
message: '请输入短信验证码!',
},
]}
/>
<Submit className={styles.submitBtn} loading={props.submitting} style={{ marginTop: '0px' }}>
登录
</Submit>
</LoginForm>
);
};
export default usePhone;
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
/* eslint-disable */
/*
* @Author: ;
* @Date: 2024-11-29
* @Description:乐山登录页;
*/
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import styles from './index.less';
import classNames from 'classnames';
import { Button, Modal } from 'antd';
import moment from 'moment';
import { connect } from 'react-redux';
import { withRouter } from '@wisdom-utils/runtime';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import defaultSetting from '../../../../../../../config/defaultSetting';
import LogoQ from './images/切换.png';
import LogoF from './images/电脑切换.png';
import qrcodeimg from './images/下载二维码.png';
import qrcodeS from './images/扫.png';
import Logo from './images/登录图标.png';
import Logoimg from './images/登录logo.png';
import LoginAction from '../../../login';
import { actionCreators } from '@/containers/App/store';
import QRCode from 'qrcode.react';
import axios from 'axios';
import Account from './Account';
import Phone from './Phone';
import { defaultApp } from '../../../../../../micro';
// 、登录页
const LeshanLogin = forwardRef((props, _ref) => {
const sliVerify = useRef();
const formRef = useRef(null);
const [isQrcode, setIsQrcode] = useState(false);
const [weather, setWeather] = useState({
province: '四川',
city: '乐山市',
adcode: '511100',
weather: '雾',
temperature: '10',
winddirection: '东南',
windpower: '≤3',
humidity: '84',
reporttime: '2024-11-29 10:06:41',
temperature_float: '10.0',
humidity_float: '84.0',
weatherImg: 0,
});
const [status, setStatus] = useState('normal');
const [autoLogin, setAutoLogin] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [dateObj, setDateObj] = useState({});
const [type, setType] = useState('Account');
const [visible, setVisible] = useState(false);
const [action, setAction] = useState(() => new LoginAction(props, setVisible, true));
const getWeather = () => {
axios({
url: 'https://restapi.amap.com/v3/weather/weatherInfo',
method: 'get',
dataType: 'json',
params: {
city: '乐山市',
key: '4d3847e60090e1c4d602a2077136c906',
},
}).then(res => {
if (res.data?.count === 0) {
console.log('获取当前区域天气失败');
this.setState({ weather: null });
return false;
}
const obj = res.data?.lives?.[0];
let weatherImg = 0;
if (obj.weather == '晴') {
weatherImg = 5;
} else if (obj.weather == '阴' || obj.weather == '霾') {
weatherImg = 1;
} else if (obj.weather == '多云') {
weatherImg = 2;
} else if (/雨/.test(obj.weather)) {
weatherImg = 0;
} else if (/雪/.test(obj.weather)) {
weatherImg = 3;
}
obj.weatherImg = weatherImg;
setWeather(obj);
});
};
const renderPlatform = () => {
const params = {
fromRef: formRef,
type,
setType,
status,
submitting,
autoLogin,
setAutoLogin,
action,
onSubmit: handleSubmit,
loginMode: props.loginMode,
updateLoginMode: props.updateLoginMode,
welcome: null,
};
return type === 'Mobile' ? <Phone {...params} /> : <Account {...params} />;
};
const checkLogType = () => {
setIsQrcode(!isQrcode);
};
const handleWeek = () => {
const weekMap = {
1: '周一',
2: '周二',
3: '周三',
4: '周四',
5: '周五',
6: '周六',
7: '周日',
};
return weekMap[Number(moment().format('E'))] || '';
};
const PopOvercontent = () => {
const qrcodes = window.globalConfig && window.globalConfig.qrcode;
if (qrcodes) {
return <QRCode value={qrcodes} />;
}
return <span>手持APP下载未配置</span>;
};
useEffect(() => {
action.globalConfig = props.global;
}, [props.global]);
const handleSubmit = values => {
/* eslint-disable */
action &&
(type === 'Account'
? action.loginHandler(values.userName, values.password, null, autoLogin, sliVerify)
: type === 'Mobile'
? action.phoneLoginFormHandler(values.mobile, values.captcha)
: null);
setSubmitting(true);
props.updateCurrentIndex && props.updateCurrentIndex(-1);
};
useEffect(() => {
action &&
action.events.on('loginSuccess', event => {
setSubmitting(false);
props.updateCurrentIndex && props.updateCurrentIndex(0);
props.history.push(`/?client=${props.global.client}`);
defaultApp();
});
action &&
action.events.on('loginError', event => {
setVisible(false);
setSubmitting(false);
});
action &&
action.events.on('loginVisible', status => {
setVisible(status);
});
action.events.on('loginHomePage', () => {
props.history.push(`/homePage`);
});
action.events.on('loginIndustry', () => {
props.history.push(`/industry`);
});
return () => {
action && action.events && action.events.removeAllListeners('loginSuccess');
action && action.events && action.events.removeAllListeners('loginError');
action && action.events && action.events.removeAllListeners('loginVisible');
action && action.events && action.events.removeAllListeners('loginHomePage');
action && action.events && action.events.removeAllListeners('loginIndustry');
};
}, [props.loginMode]);
useEffect(() => {
// getWeather();
const timer = setInterval(() => {
setDateObj({
curTime: moment().format('HH:mm:ss'),
week: handleWeek(),
date: moment().format('YYYY/MM/DD'),
});
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return (
<HelmetProvider>
<Helmet>
<title>{props.global.title || defaultSetting.title}</title>
<meta name="description" content={props.global.title || defaultSetting.title} />
</Helmet>
<div className={classNames(styles.leshanInfoBg, 'leshanInfoBg')}>
<div className={styles.IBtimeBox}>
<div className={styles.TitleCont}>
<img src={Logoimg} alt="" />
</div>
{weather ? (
<div className={styles.weather}>
<img src={require(`./images/ls_weather_${weather.weatherImg}.png`)} />
<div className={styles['weather-desc']}>
<span className={styles['weather-text']}>{weather.weather}</span>
<span>
{weather.temperature}
<span></span>
</span>
</div>
<div className={styles.weather_bar} />
</div>
) : null}
<span className={styles.IBTtime}>{dateObj.curTime}</span>
<span className={styles.IBTweek}>{dateObj.week}</span>
<span className={styles.IBTdate}>{dateObj.date}</span>
</div>
<div className={styles.loginBg}>
<div className={styles.IBcenter}>
<div className={styles.IBCleft} />
<div className={styles.IBCright}>
<div className={styles.IBlogoc}>
<img src={isQrcode ? LogoF : LogoQ} alt="" onClick={() => checkLogType()} />
</div>
{isQrcode ? (
<div className={styles.IBLogoForm} style={{ display: isQrcode ? '' : 'none' }}>
<div className={styles.QrcodeCont}>{PopOvercontent()}</div>
<div className={styles.qrcodedis}>
<img src={qrcodeS} alt="" />
<div className={styles.textCont}>
<p>打开手机扫一扫</p>
<p>
<span>下载</span>
<span className={styles.lastText}>熊猫掌天下</span>
</p>
</div>
</div>
</div>
) : (
<div className={styles.IBLogoForm} style={{ display: isQrcode ? 'none' : '' }}>
<div className={styles.IBlogo}>
<img src={Logo} alt="" />
</div>
{/* 登录主题 */}
<div className={styles.loginForm}>
{renderPlatform()}
<div>
{type === 'Mobile' ? (
<Button
size="large"
style={{ width: '100%' }}
type="primary"
ghost
onClick={() => setType('Account')}
>
账号密码登录
</Button>
) : (
<Button
onClick={() => setType('Mobile')}
size="large"
style={{ width: '100%' }}
type="primary"
ghost
>
手机验证码登录
</Button>
)}
</div>
</div>
</div>
)}
</div>
</div>
</div>
<Modal centered visible={visible} width={340} footer={null} closable={false} bodyStyle={{ padding: '15px' }}>
<div ref={sliVerify} />
</Modal>
</div>
</HelmetProvider>
);
});
const mapStateToProps = state => ({
global: state.getIn(['global', 'globalConfig']),
loginMode: state.getIn(['global', 'loginMode']),
});
const mapDispatchToProps = dispatch => ({
updateConfig(config) {
dispatch(actionCreators.getConfig(config));
},
createContext(data) {
dispatch(actionCreators.createContext(data));
},
updateLoginMode(mode) {
dispatch(actionCreators.changeLoginMode(mode));
},
updateCurrentIndex(index) {
dispatch(actionCreators.updateCurrentIndex(index));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withRouter(LeshanLogin));
@import '~antd/es/style/themes/default.less';
.leshanInfoBg {
width: 100%;
height: 100%;
background-image: url('./images/背景.png');
background-size: cover;
background-repeat: no-repeat;
position: relative;
.weather {
color: #fff;
position: absolute;
top: 25px;
right: 230px;
display: flex;
align-items: center;
img {
height: auto;
}
.weather-desc {
margin-top: 5px;
display: flex;
flex-direction: column;
margin-left: 10px;
}
.weather_bar {
width: 1px;
height: 35px;
background-color: #fff;
margin: 0 30px;
}
}
.loginBg {
position: absolute;
top: 40%;
left: 40%;
margin-left: -385px;
margin-top: -226px;
width: 1101px;
height: 646px;
background-image: url('./images/标题.png');
background-size: 100% 100%;
background-repeat: no-repeat;
.IBcenter {
width: 100%;
height: 596px;
margin-top: 50px;
.IBCleft {
width: 682px;
height: 100%;
background-image: url('./images/左图.gif');
background-size: 100% 100%;
background-repeat: no-repeat;
float: left;
}
.IBCright {
width: 419px;
height: 100%;
float: left;
background: rgba(255, 255, 255, 0.8);
.IBlogoc {
height: 74px;
width: 100%;
text-align: end;
img {
height: 74px;
width: 74px;
cursor: pointer;
}
}
.IBLogoForm {
.IBlogo {
height: 100px;
width: 100%;
text-align: center;
img {
height: 100px;
width: 100px;
}
}
.QrcodeCont {
text-align: center;
height: 360px;
width: 100%;
padding-top: 80px;
img {
width: 211px;
height: 211px;
}
}
.qrcodedis {
display: flex;
padding: 0px 40px 0px 105px;
img {
width: 48px;
height: 40px;
margin-top: 10px;
}
.textCont {
margin-left: 25px;
font-size: 16px;
p {
margin-bottom: 5px;
color: #333333;
.lastText {
margin-left: 5px;
color: #00bda0;
}
}
}
}
}
}
}
.IBtitle {
width: 100%;
height: 10.5%;
text-align: center;
img {
width: 70%;
height: 100%;
}
}
.title {
font-family: SourceHanSansCN-Medium;
font-weight: bold;
color: #43484D;
position: absolute;
top: 21px;
right: 50%;
margin-right: -285px;
line-height: 1;
width: 570px;
text-align: center;
overflow-wrap: break-word;
font-size: 24px;
img {
margin-right: 14px;
}
}
.ddCode {
position: absolute;
top: 50px;
right: -9px;
cursor: pointer;
}
.titleImg {
position: absolute;
top: 132px;
right: 133px;
}
.loginForm {
width: 250px;
margin: 9% auto 25%;
}
}
.IBtimeBox {
.TitleCont {
padding: 24px 0px 0px 32px;
img {
width: 496px;
height: 56px;
}
}
}
.IBTtime {
position: absolute;
width: 140px;
height: 26px;
font-size: 32px;
font-family: Microsoft YaHei UI;
font-weight: 400;
color: #FFFFFF;
line-height: 34px;
top: 34px;
right: 100px;
}
.IBTweek {
position: absolute;
top: 25px;
right: 30px;
height: 15px;
font-size: 16px;
font-family: Source Han Sans CN;
color: #FFFFFF;
line-height: 30px;
}
.IBTdate {
position: absolute;
top: 46px;
right: 20px;
width: 80px;
height: 13px;
font-size: 14px;
font-family: Microsoft YaHei UI;
color: #FFFFFF;
line-height: 30px;
}
}
.submitBtn {
background: #00BDA0 !important;
font-weight: bold;
font-size: 18px;
color: #FFFFFF;
text-align: center;
cursor: pointer;
border-radius: 20px;
border: none;
}
.submitBtn:active,
.submitBtn:hover,
.submitBtn:focus {
background: #00BDA0 !important;
background-size: 100% 100%;
opacity: 0.8 !important;
border: none !important;
box-shadow: none !important;
}
:global {
.leshanInfoBg {
.@{ant-prefix}-btn-background-ghost {
border-color: #00BDA0 !important;
color: #00BDA0 !important;
border-radius: 20px;
}
.@{ant-prefix}-btn-background-ghost:active,
.@{ant-prefix}-btn-background-ghost:hover,
.@{ant-prefix}-btn-background-ghost:focus {
border-color: #00BDA0 !important;
color: #00BDA0 !important;
}
.@{ant-prefix}-btn-default {
color: #00BDA0;
}
.@{ant-prefix}-btn-default:active,
.@{ant-prefix}-btn-default:hover,
.@{ant-prefix}-btn-default:focus {
color: #00BDA0;
}
.@{ant-prefix}-input-affix-wrapper:not(.@{ant-prefix}-input-affix-wrapper-disabled):hover {
border-color: #00BDA0;
box-shadow: 0 0 0 2px rgba(0, 189, 160, 0.2);
}
.@{ant-prefix}-input-affix-wrapper:focus {
border-color: #00BDA0;
box-shadow: 0 0 0 2px rgba(0, 189, 160, 0.2);
}
.@{ant-prefix}-checkbox-checked .@{ant-prefix}-checkbox-inner {
background-color: #00BDA0;
border-color: #00BDA0;
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment