diff --git a/.gitignore b/.gitignore
index fcd0041..e2838ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ node_modules
public
.idea
.git
+.yarn.lock
diff --git a/configs/webpack.config.js b/configs/webpack.config.js
index 368e725..2265d64 100644
--- a/configs/webpack.config.js
+++ b/configs/webpack.config.js
@@ -63,16 +63,14 @@ module.exports = argv => ({
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
- {
- loader: 'less-loader',
+ { loader: 'less-loader',
options: {
+ modules:false,
modifyVars: theme,
- javascriptEnabled: true,
- },
- },
+ javascriptEnabled: true
+ }},
],
exclude: /\.module\.less$/,
- include: /node_modules/,
},
{
@@ -129,11 +127,10 @@ module.exports = argv => ({
devServer: {
contentBase: SRC,
hot: true,
+ host:"0.0.0.0",
inline: true,
disableHostCheck: true,
- historyApiFallback: {
- disableDotRule: true,
- },
+ historyApiFallback:true,
stats: 'minimal',
clientLogLevel: 'warning',
},
diff --git a/index.html b/index.html
index 5f33cb5..2f2f093 100644
--- a/index.html
+++ b/index.html
@@ -34,6 +34,9 @@
transform: translateX(-50%);
}
+
+
河码
+
diff --git a/package.json b/package.json
index e84a6d3..e589a5f 100644
--- a/package.json
+++ b/package.json
@@ -61,8 +61,8 @@
"@types/react-dom": "^16.0.11",
"antd-mobile": "^2.2.8",
"dva": "^2.4.1",
- "react": "^16.6.3",
- "react-dom": "^16.6.3",
+ "react": "^16.8.0-alpha.1",
+ "react-dom": "^16.8.0-alpha.1",
"tslib": "^1.9.3"
},
"husky": {
diff --git a/scripts/start.js b/scripts/start.js
index 0136277..3fd17e0 100644
--- a/scripts/start.js
+++ b/scripts/start.js
@@ -21,6 +21,6 @@ const devServerOptions = Object.assign({}, config.devServer, {
const server = new webpackDevServer(compiler, devServerOptions);
-server.listen(8080, '127.0.0.1', () => {
+server.listen(8080, '0.0.0.0', () => {
console.log('Starting server on http://localhost:8080');
});
diff --git "a/src/assets/Icons/\345\244\264\345\203\217.png" "b/src/assets/Icons/\345\244\264\345\203\217.png"
new file mode 100644
index 0000000..ada87a3
Binary files /dev/null and "b/src/assets/Icons/\345\244\264\345\203\217.png" differ
diff --git "a/src/assets/Icons/\346\267\273\345\212\240.png" "b/src/assets/Icons/\346\267\273\345\212\240.png"
new file mode 100644
index 0000000..cbab15b
Binary files /dev/null and "b/src/assets/Icons/\346\267\273\345\212\240.png" differ
diff --git "a/src/assets/images/logo\345\233\276\346\241\210.png" "b/src/assets/images/logo\345\233\276\346\241\210.png"
new file mode 100644
index 0000000..7400ee1
Binary files /dev/null and "b/src/assets/images/logo\345\233\276\346\241\210.png" differ
diff --git a/src/assets/images/react.png b/src/assets/images/react.png
deleted file mode 100644
index e648293..0000000
Binary files a/src/assets/images/react.png and /dev/null differ
diff --git a/src/index.tsx b/src/index.tsx
index c0cbfd8..f94b562 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,5 +1,6 @@
import dva from 'dva';
import createHistory from 'history/createBrowserHistory';
+import './style.less';
const app = dva({
history: createHistory(),
@@ -11,11 +12,11 @@ const app = dva({
});
import router from './router';
-import count from './models/count';
+import appModel from './models/app/app.model';
// 4. 注册程序路由
app.router(router);
-app.model(count);
+app.model(appModel);
// 5. 启动项目
app.start('#root');
diff --git a/src/models/app/app.model.ts b/src/models/app/app.model.ts
new file mode 100644
index 0000000..dc5fa81
--- /dev/null
+++ b/src/models/app/app.model.ts
@@ -0,0 +1,7 @@
+export default {
+ namespace: 'app',
+ state: {
+ isLogin: true,
+ },
+ reducers: {},
+};
diff --git a/src/pages/Common/Avater.tsx b/src/pages/Common/Avater.tsx
new file mode 100644
index 0000000..8d4289b
--- /dev/null
+++ b/src/pages/Common/Avater.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+
+interface IAvater {
+ url: string;
+ style?: React.CSSProperties;
+ className?: string;
+}
+
+export const Avater = (props: IAvater) => {
+ return (
+
+

+
+ );
+};
diff --git a/src/pages/Common/Input.tsx b/src/pages/Common/Input.tsx
new file mode 100644
index 0000000..a31e431
--- /dev/null
+++ b/src/pages/Common/Input.tsx
@@ -0,0 +1,45 @@
+import * as React from 'react';
+import { Icon } from 'antd-mobile';
+import styles from './index.module.less';
+
+export type InputType = 'text' | 'password' | 'phone' | 'file';
+type RuleType = RegExp | boolean | (RegExp | boolean)[];
+interface IInputProps {
+ type?: InputType;
+ onchange?: (e: React.ChangeEvent
) => void;
+ rule: RuleType;
+ isALlMatch?: boolean; // 是否需要完全匹配规则
+ value?: string | number;
+ refValue?: string; // 与value值比较是否相等
+}
+
+export const Input = (props: IInputProps) => {
+ const [isOk, setOk] = React.useState(false);
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const val = e.target.value;
+ const isOk = props.refValue ? val === props.refValue : true;
+ const regs: (RegExp | boolean)[] = [];
+ //校验规则
+ if (Array.isArray(props.rule)) regs.push(...props.rule);
+ else regs.push(props.rule);
+ if (props.isALlMatch)
+ setOk(isOk && regs.every(r => (typeof r === 'boolean' ? r : r.test(e.target.value))));
+ else setOk(isOk && regs.some(r => (typeof r === 'boolean' ? r : r.test(e.target.value))));
+
+ if (props.onchange) props.onchange(e);
+ };
+
+ return (
+
+
+ {isOk ? (
+
+
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/src/pages/Common/PickerComponent.tsx b/src/pages/Common/PickerComponent.tsx
new file mode 100644
index 0000000..15b5f0f
--- /dev/null
+++ b/src/pages/Common/PickerComponent.tsx
@@ -0,0 +1,26 @@
+import { Picker } from 'antd-mobile';
+import { PickerData } from 'antd-mobile/lib/picker/PropsType';
+import * as React from 'react';
+import { useState } from 'react';
+import styles from '../Register/index.module.less';
+import { SignItem } from './SignItem';
+
+interface IPickerProps {
+ data: PickerData[];
+ title: string;
+ head: string;
+}
+
+export const PickerComponent = (props: IPickerProps) => {
+ const [val, setVal] = useState(props.data[0].label);
+
+ return (
+
+ Coderiver会基于您的角色向您推荐相关项目和团队
+ {props.head}
+ setVal(props.data[val].label as string)}>
+ {val}
+
+
+ );
+};
diff --git a/src/pages/Common/SignItem.tsx b/src/pages/Common/SignItem.tsx
new file mode 100644
index 0000000..6d25c89
--- /dev/null
+++ b/src/pages/Common/SignItem.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+
+interface IRegisterItem {
+ title: string;
+ className?: string;
+}
+
+export class SignItem extends React.Component {
+ render() {
+ const { title } = this.props;
+ return (
+ <>
+ {title}
+ {this.props.children}
+ >
+ );
+ }
+}
diff --git a/src/pages/Common/index.module.less b/src/pages/Common/index.module.less
new file mode 100644
index 0000000..867415e
--- /dev/null
+++ b/src/pages/Common/index.module.less
@@ -0,0 +1,32 @@
+:global{
+ .am-input-error-extra{
+ position: absolute;
+ right: 0;
+ top: calc(~"50% - 12.5px");
+ height: 42px;
+ width: 42px;
+ margin-left: 6px;
+ background-image: url("data:image/svg+xml;charset=utf-8,");
+ background-size: 42px auto;
+ }
+ .flex{
+ display: flex;
+ }
+ .flex-btw{
+ .flex();
+ justify-content: space-between;
+ }
+}
+img{
+ width: 100%;
+ height: 100%;
+}
+
+.commonInput{
+ position: relative;
+ &>span{
+ position: absolute;
+ right: 0;
+ top: calc(~"50% - 12.5px");
+ }
+}
diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx
new file mode 100644
index 0000000..6cdfada
--- /dev/null
+++ b/src/pages/Login/index.tsx
@@ -0,0 +1,62 @@
+import { Button, Icon } from 'antd-mobile';
+import { Link, Redirect, Route, Switch } from 'dva/router';
+import { History } from 'history';
+import * as React from 'react';
+import { ESignMethod } from 'utils/enum';
+import styles from '../Register/index.module.less';
+import { SignItem } from '@pages/Common/SignItem';
+
+export interface ILoginProps {
+ history: History;
+}
+
+export class Login extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ method: ESignMethod.Phone,
+ };
+ }
+ private handleMethod = () => {
+ let nextUrl = this.state.method === ESignMethod.Email ? 'phone' : 'email';
+ this.setState({ method: nextUrl as ESignMethod });
+ this.props.history.replace('/login/' + nextUrl);
+ };
+ public render() {
+ return (
+
+
+ this.props.history.goBack()} />
+
+
+
+
+
+
+
+ 输入密码
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const InputEmail = () => (
+ <>
+ 输入邮箱
+
+ >
+);
+
+const InputPhone = () => (
+ <>
+ 手机号码
+
+ >
+);
diff --git a/src/pages/Main/index.module.less b/src/pages/Main/index.module.less
new file mode 100644
index 0000000..42bcd57
--- /dev/null
+++ b/src/pages/Main/index.module.less
@@ -0,0 +1,35 @@
+.main{
+ padding: 40px 0;
+
+ h1,h5{
+ padding: 0 40px;
+ margin: 0;
+ }
+ .content{
+ padding: 40px;
+ }
+ .title{
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+ .itemIcon,.itemIconLg{
+ width: 22px;
+ height: 22px;
+ background-position: center;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ }
+ .itemIconLg{
+ width: 36px;
+ height: 36px
+ }
+}
+
+:global{
+ .am-tabs-tab-bar-wrap{
+ position: fixed;
+ bottom: 0;
+ width: 100%;
+ }
+}
diff --git a/src/pages/Main/index.tsx b/src/pages/Main/index.tsx
new file mode 100644
index 0000000..2a94956
--- /dev/null
+++ b/src/pages/Main/index.tsx
@@ -0,0 +1,167 @@
+import * as React from 'react';
+import { TabBar } from 'antd-mobile';
+import styles from './index.module.less';
+import avater from '@assets/Icons/头像.png';
+import { Avater } from '@pages/Common/Avater';
+
+export interface IMainProps {}
+
+export class MainComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedTab: 'redTab',
+ hidden: false,
+ };
+ }
+
+ renderContent(pageText) {
+ return haha
;
+ }
+
+ render() {
+ return (
+
+
Hi,Mike
+
+ 为您推荐
+
+
+
+
+ }
+ selectedIcon={
+
+ }
+ selected={this.state.selectedTab === 'blueTab'}
+ onPress={() => {
+ this.setState({
+ selectedTab: 'blueTab',
+ });
+ }}
+ data-seed="logId"
+ >
+ {this.renderContent('Life')}
+
+
+
+ }
+ selectedIcon={
+
+ }
+ title="项目中心"
+ key="Koubei"
+ selected={this.state.selectedTab === 'redTab'}
+ onPress={() => {
+ this.setState({
+ selectedTab: 'redTab',
+ });
+ }}
+ data-seed="logId1"
+ >
+ {this.renderContent('Koubei')}
+
+
+ }
+ />
+
+ }
+ selectedIcon={
+
+ }
+ title="英雄广场"
+ key="Friend"
+ selected={this.state.selectedTab === 'greenTab'}
+ onPress={() => {
+ this.setState({
+ selectedTab: 'greenTab',
+ });
+ }}
+ >
+ {this.renderContent('Friend')}
+
+ {
+ this.setState({
+ selectedTab: 'yellowTab',
+ });
+ }}
+ >
+ {this.renderContent('My')}
+
+
+
+ );
+ }
+}
diff --git a/src/pages/Register/index.module.less b/src/pages/Register/index.module.less
new file mode 100644
index 0000000..981c91e
--- /dev/null
+++ b/src/pages/Register/index.module.less
@@ -0,0 +1,52 @@
+.register,.login{
+ position: relative;
+ h1{
+ font-weight: lighter;
+ color: #fff;
+ margin-bottom: 112px;
+ font-size: 62px;
+ }
+ .roleTitle{
+ margin-bottom: 48px;
+ }
+ .info{
+ color:#fff;
+ margin-bottom: 112px;
+ font-weight: lighter;
+ }
+ h6{
+ font-size: 30px;
+ margin: 0;
+ }
+ input,:global(.val){
+ display: block;
+ width: 100%;
+ height: 125px;
+ line-height: 125px;
+ font-size: 48px;
+ background: transparent;
+ outline: none;
+ border: 0;
+ border-bottom: 1px solid #a1d0df;
+ text-decoration: none;
+ color: #fff;
+ margin-bottom: 50px;
+ }
+
+ &>div:last-child{
+ width: calc(~"100% - 100px");
+ position: absolute;
+ bottom: 33px;
+ text-align: right;
+ .right{
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ background: #fff;
+ width: 100px;
+ height: 100px;
+ border-radius: 50% 50%;
+ float: right;
+ }
+ }
+}
diff --git a/src/pages/Register/index.tsx b/src/pages/Register/index.tsx
new file mode 100644
index 0000000..dd52b8e
--- /dev/null
+++ b/src/pages/Register/index.tsx
@@ -0,0 +1,181 @@
+import { PickerComponent } from '@pages/Common/PickerComponent';
+import { Button, Icon } from 'antd-mobile';
+import { PickerData } from 'antd-mobile/lib/picker/PropsType';
+import { Route, Switch } from 'dva/router';
+import { History } from 'history';
+import * as React from 'react';
+import { arrayLast } from 'utils/utils';
+import styles from './index.module.less';
+import { ESignMethod } from 'utils/enum';
+import { SignItem } from '@pages/Common/SignItem';
+import { Input } from '@pages/Common/Input';
+import { USER_NAME_REGEXP, PHONE_REGEXP, EMAIL_REGEXP, PWD_REGEXP } from 'utils/regExps';
+
+export interface IRegisterProps {
+ history: History;
+}
+interface IRegisterState {
+ currentIndex: number;
+ method: ESignMethod;
+}
+
+export class Register extends React.Component {
+ private routeUrls = ['acount', 'pwd', 'role', 'exp'];
+ constructor(props) {
+ super(props);
+
+ //当前进入的路由url
+ let currentRoute = arrayLast(this.props.history.location.pathname.split('/'));
+ let currentIndex = this.routeUrls.indexOf(currentRoute);
+
+ this.state = {
+ currentIndex: currentIndex + 1,
+ method: ESignMethod.Email,
+ };
+ }
+ private go = () => {
+ let currentUrl: string;
+ if (this.state.currentIndex === this.routeUrls.length) {
+ currentUrl = '/login';
+ } else {
+ currentUrl = '/register/' + this.routeUrls[this.state.currentIndex];
+ this.setState({ currentIndex: this.state.currentIndex + 1 });
+ }
+ this.props.history.push(currentUrl);
+ };
+ private goBack = () => {
+ this.props.history.go(-1);
+ this.setState({ currentIndex: this.state.currentIndex - 1 });
+ };
+ public render() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const InputName = () => (
+
+ 昵称
+
+
+);
+
+const InputAccount = () => (
+
+ 手机号码/邮箱
+
+
+);
+
+const InputPassWord = () => {
+ const [pwd, setPwd] = React.useState('');
+ const [confirmPwd, setConfirmPwd] = React.useState('');
+ const [showPwd, setShowPwd] = React.useState(false);
+
+ const totalShowPwd = () => {
+ setShowPwd(!showPwd);
+ };
+
+ return (
+
+
+ 密码
+ {showPwd ? '隐藏' : '显示'}
+
+ {
+ setPwd(e.target.value);
+ }}
+ />
+
+ 确认密码
+ {showPwd ? '隐藏' : '显示'}
+
+ {
+ setConfirmPwd(e.target.value);
+ }}
+ />
+
+ );
+};
+
+const InputRole = () => {
+ const data: PickerData[] = [
+ {
+ label: '产品经理',
+ value: 0,
+ },
+ {
+ label: '前端工程师',
+ value: 1,
+ },
+ {
+ label: 'Java工程师',
+ value: 2,
+ },
+ {
+ label: '运营',
+ value: 3,
+ },
+ {
+ label: '设计师',
+ value: 4,
+ },
+ {
+ label: '移动开发',
+ value: 5,
+ },
+ ];
+
+ return ;
+};
+
+const InputExperience = () => {
+ const data: PickerData[] = [
+ {
+ label: '无经验',
+ value: 0,
+ },
+ {
+ label: '0-3年',
+ value: 1,
+ },
+ {
+ label: '3-5年',
+ value: 2,
+ },
+ {
+ label: '5-10年',
+ value: 3,
+ },
+ {
+ label: '10年以上',
+ value: 4,
+ },
+ ];
+ return ;
+};
diff --git a/src/pages/Welcome/index.module.less b/src/pages/Welcome/index.module.less
index 1bb8132..248c131 100644
--- a/src/pages/Welcome/index.module.less
+++ b/src/pages/Welcome/index.module.less
@@ -1,3 +1,68 @@
-.container {
- font-size: 50px;
+:global{
+ .bg{
+ height:100%;
+ background: linear-gradient(to bottom,#40AEA8,#4589EE);
+ padding: 33px 50px;
+ color: #a1d0df;
+ font-family: HiraginoSansGB-W3;
+ }
+}
+
+.logo{
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
+ width: min-content;
+ height: min-content;
+}
+.co{
+ position: fixed;
+ bottom: 13px;
+}
+.sign{
+ &>div:first-child{
+ text-align: right;
+ a{
+ text-decoration: none;
+ color: #fff;
+ }
+ }
+ &>div:nth-child(2){
+ margin-top: 60px;
+ }
+ h1{
+ margin: 54px 0;
+ padding: 0;
+ font-family:"HiraginoSansGB-W3";
+ font-weight: lighter;
+ }
+ .btns{
+ &>a{
+ margin-bottom: 10px;
+ border-radius: 50px;
+ display: block;
+ height: 100px;
+ text-align: center;
+ font-size: 40px;
+ line-height: 100px;
+ }
+ &>a:first-child{
+ color: #40AEA8;
+ background: #fff;
+ }
+ &>a:last-child{
+ background: transparent;
+ border: 1px solid #fff;
+ color: #fff;
+ }
+ }
+ &>div:last-child{
+ a{
+ margin-right: 10px;
+ font-size: 16px;
+ }
+ }
}
diff --git a/src/pages/Welcome/index.tsx b/src/pages/Welcome/index.tsx
index 435cff3..b123a12 100644
--- a/src/pages/Welcome/index.tsx
+++ b/src/pages/Welcome/index.tsx
@@ -1,6 +1,36 @@
-import React from 'react';
+import * as React from 'react';
+import logoUrl from '../../assets/images/logo图案.png';
import styles from './index.module.less';
+import { Button } from 'antd-mobile';
+import { History } from 'history';
+import { Link } from 'dva/router';
-const Welcome = () => welcome login
;
+export interface IWelcomeProps {
+ history: History;
+}
-export default Welcome;
+export default class Welcome extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ public render() {
+ return (
+
+
+ 登录
+
+
{/*

*/}
+
欢迎来到CodeRiver
+
+ 使用Github账号登录
+ 创建账号
+
+
更多登陆方式
+
+
+ );
+ }
+}
diff --git a/src/router.tsx b/src/router.tsx
index d354116..743768e 100644
--- a/src/router.tsx
+++ b/src/router.tsx
@@ -1,12 +1,17 @@
import * as React from 'react';
import { Route, Router, Switch } from 'dva/router';
import Welcome from '@pages/Welcome';
+import { History } from 'history';
+import { Register } from '@pages/Register';
+import { Login } from '@pages/Login';
-export default ({ history }: { history: any }) => {
+export default ({ history }: { history: History }) => {
return (
-
+
+
+
);
diff --git a/src/style.less b/src/style.less
new file mode 100644
index 0000000..4b19fe7
--- /dev/null
+++ b/src/style.less
@@ -0,0 +1,12 @@
+*{
+ box-sizing: border-box;
+}
+body{
+ font-size: 14px;
+}
+html,body,#root{
+ width: 100%;
+ height: 100%;
+ margin: 0;
+}
+
diff --git a/src/utils/enum.ts b/src/utils/enum.ts
new file mode 100644
index 0000000..6d7baf5
--- /dev/null
+++ b/src/utils/enum.ts
@@ -0,0 +1,4 @@
+export enum ESignMethod {
+ Email = 'email',
+ Phone = 'phone',
+}
diff --git a/src/utils/regExps.ts b/src/utils/regExps.ts
new file mode 100644
index 0000000..0bb931d
--- /dev/null
+++ b/src/utils/regExps.ts
@@ -0,0 +1,5 @@
+export const USER_NAME_REGEXP = /^[A-Za-z0-9_\-\u4e00-\u9fa5]+$/;
+export const PHONE_REGEXP = /^0?(13|14|15|18|17)[0-9]{9}$/;
+export const EMAIL_REGEXP = /^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}$/;
+// 可以包含数字、字母、下划线,并且要同时含有数字和字母,且长度要在8-16位之间
+export const PWD_REGEXP = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z_]{8,16}$/;
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
new file mode 100644
index 0000000..1de0b85
--- /dev/null
+++ b/src/utils/utils.ts
@@ -0,0 +1,3 @@
+export function arrayLast(arr: T[]): T {
+ return arr[arr.length - 1];
+}