diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 3aaff5a..255e182 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -43,5 +43,12 @@ module.exports = {
'space-before-blocks': 2, // 要求语句块之前有空格
'@typescript-eslint/explicit-function-return-type': 0, // 禁止函数必须要定义返回类型
'react/display-name': 'off',
+ '@typescript-eslint/no-explicit-any': ['off'],
+ '@typescript-eslint/no-var-requires': ['off'],
+ '@typescript-eslint/no-use-before-define': ['off'],
+ '@typescript-eslint/no-empty-function': ['off'],
+ '@typescript-eslint/no-empty-interface': ['off'],
+ '@typescript-eslint/no-unused-vars': ['off'],
+ '@typescript-eslint/no-non-null-assertion': ['off'],
}
}
diff --git a/index.html b/index.html
index 600cb42..fe3c4aa 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
-
+
<%- title %>
diff --git a/package.json b/package.json
index 27da605..854c3e5 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,9 @@
"@ant-design/pro-components": "^2.7.0",
"antd": "^5.16.1",
"axios": "^1.6.8",
+ "mobx": "^6.12.3",
+ "mobx-persist-store": "^1.1.4",
+ "mobx-react": "^9.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4f10b44..a7588ed 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,6 +17,15 @@ dependencies:
axios:
specifier: ^1.6.8
version: 1.6.8
+ mobx:
+ specifier: ^6.12.3
+ version: 6.12.3
+ mobx-persist-store:
+ specifier: ^1.1.4
+ version: 1.1.4(mobx@6.12.3)
+ mobx-react:
+ specifier: ^9.1.1
+ version: 9.1.1(mobx@6.12.3)(react-dom@18.2.0)(react@18.2.0)
react:
specifier: ^18.2.0
version: 18.2.0
@@ -4506,6 +4515,56 @@ packages:
ufo: 1.5.3
dev: true
+ /mobx-persist-store@1.1.4(mobx@6.12.3):
+ resolution: {integrity: sha512-kGdTpnpfvTC61XiqlYnAN+gvkGiYfLgGpV1nj6k8hHom8V+vrPJyYMg+V1Rc5dNi7sa+qrHlbZfCvbaJoAtpSg==}
+ peerDependencies:
+ mobx: '*'
+ dependencies:
+ mobx: 6.12.3
+ dev: false
+
+ /mobx-react-lite@4.0.7(mobx@6.12.3)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==}
+ peerDependencies:
+ mobx: ^6.9.0
+ react: ^16.8.0 || ^17 || ^18
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ dependencies:
+ mobx: 6.12.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
+
+ /mobx-react@9.1.1(mobx@6.12.3)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-gVV7AdSrAAxqXOJ2bAbGa5TkPqvITSzaPiiEkzpW4rRsMhSec7C2NBCJYILADHKp2tzOAIETGRsIY0UaCV5aEw==}
+ peerDependencies:
+ mobx: ^6.9.0
+ react: ^16.8.0 || ^17 || ^18
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ dependencies:
+ mobx: 6.12.3
+ mobx-react-lite: 4.0.7(mobx@6.12.3)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /mobx@6.12.3:
+ resolution: {integrity: sha512-c8NKkO4R2lShkSXZ2Ongj1ycjugjzFFo/UswHBnS62y07DMcTc9Rvo03/3nRyszIvwPNljlkd4S828zIBv/piw==}
+ dev: false
+
/mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..089d7a9
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,77 @@
+
diff --git a/public/vite.svg b/public/vite.svg
deleted file mode 100644
index e7b8dfb..0000000
--- a/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/App.css b/src/App.css
index b9d355d..df674c0 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,42 +1,42 @@
#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
}
.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
}
.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
+ filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
+ filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
}
@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
}
.card {
- padding: 2em;
+ padding: 2em;
}
.read-the-docs {
- color: #888;
+ color: #888;
}
diff --git a/src/api/user/index.ts b/src/api/user/index.ts
new file mode 100644
index 0000000..ca6ddb8
--- /dev/null
+++ b/src/api/user/index.ts
@@ -0,0 +1,5 @@
+import service from '@/utils/axios/service.ts'
+
+export function getUserInfo(params: object) {
+ return service.get('/user/info', params)
+}
diff --git a/src/assets/images/background.png b/src/assets/images/background.png
new file mode 100644
index 0000000..32e90b6
Binary files /dev/null and b/src/assets/images/background.png differ
diff --git a/src/assets/styles/index.less b/src/assets/styles/index.less
index 6119ad9..2d00377 100644
--- a/src/assets/styles/index.less
+++ b/src/assets/styles/index.less
@@ -13,56 +13,32 @@
-moz-osx-font-smoothing: grayscale;
}
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
+/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
+// 滚动条整体部分
+&::-webkit-scrollbar {
+ width: 6px;
+ height: 8px;
}
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
+// 滚动条的轨道的两端按钮,允许通过点击微调小方块的位置。
+&::-webkit-scrollbar-button {
+ display: none;
}
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
+// 滚动条里面的小方块,能向上向下移动(或往左往右移动,取决于是垂直滚动条还是水平滚动条)
+&::-webkit-scrollbar-thumb {
+ background: rgba(144, 147, 153, 0.3);
+ cursor: pointer;
+ border-radius: 4px;
}
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
+// 边角,即两个滚动条的交汇处
+&::-webkit-scrollbar-corner {
+ display: none;
}
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
+// 两个滚动条的交汇处上用于通过拖动调整元素大小的小控件
+&::-webkit-resizer {
+ display: none;
}
+
diff --git a/src/assets/styles/variables.less b/src/assets/styles/variables.less
index f96954d..e69de29 100644
--- a/src/assets/styles/variables.less
+++ b/src/assets/styles/variables.less
@@ -1 +0,0 @@
-@primary-color: '#ff7875';
\ No newline at end of file
diff --git a/src/constants/index.ts b/src/constants/index.ts
new file mode 100644
index 0000000..dc1f11d
--- /dev/null
+++ b/src/constants/index.ts
@@ -0,0 +1 @@
+export const DEFAULT_NAME = 'admin'
diff --git a/src/main.tsx b/src/main.tsx
index 0e299fa..a352083 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -4,9 +4,13 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import './assets/styles/index.less'
import routeConfig from './router'
import 'virtual:svg-icons-register'
+import { Provider as MobxProvider } from 'mobx-react'
+import { RootStore } from '@/store'
const router = createBrowserRouter(routeConfig)
ReactDOM.createRoot(document.getElementById('root')!).render(
-
+
+
+
,
)
diff --git a/src/router/index.ts b/src/router/index.ts
index 5b2cb9e..e189b07 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,18 +1,12 @@
import type { RouteObject } from 'react-router-dom'
-import About from '@/views/about/About'
-// import Home from '@/views/home/Home'
import NoFound from '@/views/404/404'
-import Login from '@/views/Login/index'
+import Login from '@/views/Login'
const routes: RouteObject[] = [
{
path: '/',
Component: Login,
},
- {
- path: '/about',
- Component: About,
- },
{
path: '*',
Component: NoFound,
diff --git a/src/store/index.ts b/src/store/index.ts
index e69de29..39a7e7c 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -0,0 +1,6 @@
+import { useUserStore } from './modules/user.ts'
+
+/** 将每个Store实例化 */
+export const RootStore = {
+ user: new useUserStore(),
+}
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
new file mode 100644
index 0000000..b7d7849
--- /dev/null
+++ b/src/store/modules/user.ts
@@ -0,0 +1,40 @@
+import { action, makeAutoObservable } from 'mobx'
+import { makePersistable, isHydrated } from 'mobx-persist-store'
+
+export class useUserStore {
+ user: object = {}
+ constructor() {
+ makeAutoObservable(
+ this,
+ {
+ setUserInfo: action,
+ },
+ { autoBind: true },
+ )
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ makePersistable(this, {
+ // 在构造函数内使用 makePersistable
+ name: 'userStore', // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
+ properties: ['user'], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
+ storage: window.localStorage, // 保存的位置:看自己的业务情况选择,可以是localStorage,sessionstorage
+ // 。。还有一些其他配置参数,例如数据过期时间等等,可以康康文档,像storage这种字段可以配置在全局配置里,详见文档
+ }).then(
+ action((persistStore) => {
+ // persist 完成的回调,在这里可以执行一些拿到数据后需要执行的操作,如果在页面上要判断是否完成persist,使用 isHydrated
+ // console.log(persistStore)
+ }),
+ )
+ }
+ get getUserInfo() {
+ return this.user
+ }
+ get token() {
+ return this.user ? this.user.token : ''
+ }
+ get isHydrated() {
+ return isHydrated(this)
+ }
+ setUserInfo(user: object) {
+ this.user = user
+ }
+}
diff --git a/src/types/user/user.d.ts b/src/types/user/user.d.ts
new file mode 100644
index 0000000..1cd58e7
--- /dev/null
+++ b/src/types/user/user.d.ts
@@ -0,0 +1,13 @@
+declare namespace user {
+ interface userInfo {
+ name: string
+ avatar: string
+ userid: string
+ role: string
+ email: string
+ telephone: string
+ status: string
+ token: string
+ }
+}
+export { user }
diff --git a/src/utils/axios/service.ts b/src/utils/axios/service.ts
index 857a568..e57b863 100644
--- a/src/utils/axios/service.ts
+++ b/src/utils/axios/service.ts
@@ -1,29 +1,146 @@
-import axios from 'axios'
+import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
+import { message } from 'antd'
+// 数据返回的接口
+// 定义请求响应参数,不含data
+interface Result {
+ code: number
+ msg: string
+}
-const request = axios.create({
- baseURL: import.meta.env.VITE_BASE_API, // 域名配置,可添加变量配置文件定义
- headers: {
- Authorization: `Bearer`, // token从Cookie中获取
- 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
- },
- timeout: 5000, // 请求超时时间
-})
+// 请求响应参数,包含data
+interface ResultData extends Result {
+ data?: T
+}
+const URL = import.meta.env.VITE_API_BASE_URL
+enum RequestEnums {
+ TIMEOUT = 20000,
+ OVERDUE = 600, // 登录失效
+ FAIL = 999, // 请求失败
+ SUCCESS = 200, // 请求成功
+}
+const config = {
+ // 默认地址
+ baseURL: URL as string,
+ // 设置超时时间
+ timeout: RequestEnums.TIMEOUT as number,
+ // 跨域时候允许携带凭证
+ withCredentials: true,
+}
+class RequestHttp {
+ // 定义成员变量并指定类型
+ service: AxiosInstance
+ public constructor(config: AxiosRequestConfig) {
+ // 实例化axios
+ this.service = axios.create(config)
+ /**
+ * 请求拦截器
+ * 客户端发送请求 -> [请求拦截器] -> 服务器
+ */
+ this.service.interceptors.request.use(
+ (config: AxiosRequestConfig | any) => {
+ const token = localStorage.getItem('token') || ''
+ return {
+ ...config,
+ headers: {
+ 'x-access-token': token, // 请求头中携带token信息
+ },
+ }
+ },
+ (error: AxiosError) => {
+ // 请求报错
+ Promise.reject(error)
+ },
+ )
-//请求拦截
-request.interceptors.request.use(
- (config) => config,
- (err) => Promise.reject(err.response),
-)
-
-// 响应拦截
-request.interceptors.response.use(
- (response) => {
- // 有些情况下接口未必是RESTful风格,C相关的接口返回异常时状态码会小于0
- if (response.status !== 200) return Promise.reject(response.data)
- // 一般会和后端约定一些code,分别进行处理,这里直接返回了不做处理
- return response.data
- },
- (err) => Promise.reject(err.response),
-)
-
-export default request
+ /**
+ * 响应拦截器
+ * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
+ */
+ this.service.interceptors.response.use(
+ (response: AxiosResponse) => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ const { data, config } = response // 解构
+ if (data.code === RequestEnums.OVERDUE) {
+ // 登录信息失效,应跳转到登录页面,并清空本地的token
+ localStorage.setItem('token', '')
+ return Promise.reject(data)
+ }
+ // 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
+ if (data.code && data.code !== RequestEnums.SUCCESS) {
+ message.error(data) // 此处也可以使用组件提示报错信息
+ return Promise.reject(data)
+ }
+ return data
+ },
+ (error: AxiosError) => {
+ const { response } = error
+ if (response) {
+ this.handleCode(response.status)
+ }
+ if (!window.navigator.onLine) {
+ message.error('网络连接失败')
+ // 可以跳转到错误页面,也可以不做操作
+ // return router.replace({
+ // path: '/404'
+ // });
+ }
+ },
+ )
+ }
+ handleCode(code: number): void {
+ switch (code) {
+ case 400:
+ message.error('请求错误(400)')
+ break
+ case 401:
+ message.error('未授权,请重新登录(401)')
+ break
+ case 403:
+ message.error('拒绝访问(403)')
+ break
+ case 404:
+ message.error('请求出错(404)')
+ break
+ case 408:
+ message.error('请求超时(408)')
+ break
+ case 500:
+ message.error('服务器错误(500)')
+ break
+ case 501:
+ message.error('服务未实现(501)')
+ break
+ case 502:
+ message.error('网络错误(502)')
+ break
+ case 503:
+ message.error('服务不可用(503)')
+ break
+ case 504:
+ message.error('网络超时(504)')
+ break
+ case 505:
+ message.error('HTTP版本不受支持(505)')
+ break
+ default:
+ message.error(`连接出错(${code})!`)
+ break
+ }
+ }
+ // 常用方法封装
+ get(url: string, params?: object): Promise> {
+ return this.service.get(url, { params })
+ }
+ post(url: string, params?: object): Promise> {
+ return this.service.post(url, params)
+ }
+ put(url: string, params?: object): Promise> {
+ return this.service.put(url, params)
+ }
+ delete(url: string, params?: object): Promise> {
+ return this.service.delete(url, { params })
+ }
+}
+// 导出一个实例对象
+export default new RequestHttp(config)
diff --git a/src/utils/store/useStore.tsx b/src/utils/store/useStore.tsx
new file mode 100644
index 0000000..23859f6
--- /dev/null
+++ b/src/utils/store/useStore.tsx
@@ -0,0 +1,11 @@
+import { MobXProviderContext } from 'mobx-react'
+import { useContext } from 'react'
+import { RootStore } from '@/store'
+
+// 根据RootStore来实现参数的自动获取和返回值的自动推导
+function useStore(name: V): T[V] {
+ const store = useContext(MobXProviderContext) as T
+ return store[name]
+}
+
+export default useStore
diff --git a/src/views/404/404.tsx b/src/views/404/404.tsx
index 419d881..ef5e3fe 100644
--- a/src/views/404/404.tsx
+++ b/src/views/404/404.tsx
@@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom'
-
+import './index.less'
export default () => {
const navigate = useNavigate()
const goBack = () => {
@@ -7,8 +7,149 @@ export default () => {
}
return (
<>
- About Page
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Oops. Looks like you took a wrong turn.
+
+
+
+
>
)
}
diff --git a/src/views/404/index.less b/src/views/404/index.less
new file mode 100644
index 0000000..33629ec
--- /dev/null
+++ b/src/views/404/index.less
@@ -0,0 +1,1393 @@
+*,
+*:after,
+*:before {
+ box-sizing: border-box;
+}
+
+body {
+ padding: 0;
+ margin: 0;
+}
+
+.container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.container-star {
+ background-image: linear-gradient(to bottom, #292256 0%, #8446cf 70%, #a871d6 100%);
+}
+.container-star:after {
+ background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0) 40%, rgba(15, 10, 38, 0.2) 100%);
+ content: "";
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+}
+
+.star-1 {
+ position: absolute;
+ border-radius: 50%;
+ background-color: #ffffff;
+ -webkit-animation: twinkle 5s infinite ease-in-out;
+ animation: twinkle 5s infinite ease-in-out;
+}
+.star-1:after {
+ height: 100%;
+ width: 100%;
+ transform: rotate(90deg);
+ content: "";
+ position: absolute;
+ background-color: #fff;
+ border-radius: 50%;
+}
+.star-1:before {
+ background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 60%, rgba(255, 255, 255, 0) 100%);
+ position: absolute;
+ border-radius: 50%;
+ content: "";
+ top: -20%;
+ left: -50%;
+}
+
+.star-1:nth-of-type(1) {
+ top: 83vh;
+ left: 9vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(1):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(2) {
+ top: 100vh;
+ left: 98vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(2):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(3) {
+ top: 77vh;
+ left: 73vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(3):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(4) {
+ top: 78vh;
+ left: 86vw;
+ width: 5px;
+ height: 1.6666666667px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(4):before {
+ width: 10px;
+ height: 10px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(5) {
+ top: 87vh;
+ left: 79vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(5):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(6) {
+ top: 36vh;
+ left: 13vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-1:nth-of-type(6):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(7) {
+ top: 91vh;
+ left: 13vw;
+ width: 8px;
+ height: 2.6666666667px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(7):before {
+ width: 16px;
+ height: 16px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(8) {
+ top: 17vh;
+ left: 74vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(8):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(9) {
+ top: 84vh;
+ left: 30vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-1:nth-of-type(9):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(10) {
+ top: 86vh;
+ left: 69vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(10):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(11) {
+ top: 35vh;
+ left: 89vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-1:nth-of-type(11):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(12) {
+ top: 25vh;
+ left: 47vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-1:nth-of-type(12):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(13) {
+ top: 2vh;
+ left: 48vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(13):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(14) {
+ top: 18vh;
+ left: 92vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-1:nth-of-type(14):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(15) {
+ top: 8vh;
+ left: 12vw;
+ width: 8px;
+ height: 2.6666666667px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-1:nth-of-type(15):before {
+ width: 16px;
+ height: 16px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(16) {
+ top: 90vh;
+ left: 35vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(16):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(17) {
+ top: 1vh;
+ left: 11vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(17):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(18) {
+ top: 88vh;
+ left: 12vw;
+ width: 5px;
+ height: 1.6666666667px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(18):before {
+ width: 10px;
+ height: 10px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(19) {
+ top: 64vh;
+ left: 90vw;
+ width: 5px;
+ height: 1.6666666667px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(19):before {
+ width: 10px;
+ height: 10px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(20) {
+ top: 28vh;
+ left: 14vw;
+ width: 9px;
+ height: 3px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(20):before {
+ width: 18px;
+ height: 18px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(21) {
+ top: 94vh;
+ left: 41vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(21):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(22) {
+ top: 65vh;
+ left: 48vw;
+ width: 9px;
+ height: 3px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-1:nth-of-type(22):before {
+ width: 18px;
+ height: 18px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(23) {
+ top: 32vh;
+ left: 27vw;
+ width: 9px;
+ height: 3px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-1:nth-of-type(23):before {
+ width: 18px;
+ height: 18px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(24) {
+ top: 67vh;
+ left: 45vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-1:nth-of-type(24):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(25) {
+ top: 78vh;
+ left: 94vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(25):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(26) {
+ top: 47vh;
+ left: 26vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(26):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(27) {
+ top: 29vh;
+ left: 24vw;
+ width: 7px;
+ height: 2.3333333333px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-1:nth-of-type(27):before {
+ width: 14px;
+ height: 14px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(28) {
+ top: 11vh;
+ left: 44vw;
+ width: 8px;
+ height: 2.6666666667px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(28):before {
+ width: 16px;
+ height: 16px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(29) {
+ top: 82vh;
+ left: 73vw;
+ width: 6px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(29):before {
+ width: 12px;
+ height: 12px;
+ top: -250%;
+}
+
+.star-1:nth-of-type(30) {
+ top: 30vh;
+ left: 36vw;
+ width: 4px;
+ height: 1.3333333333px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-1:nth-of-type(30):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2 {
+ position: absolute;
+ border-radius: 50%;
+ background-color: #ffffff;
+ -webkit-animation: twinkle 5s infinite ease-in-out;
+ animation: twinkle 5s infinite ease-in-out;
+}
+
+.star-2:nth-of-type(31) {
+ top: 16vh;
+ left: 72vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(31):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(32) {
+ top: 61vh;
+ left: 13vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(32):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(33) {
+ top: 51vh;
+ left: 81vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(33):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(34) {
+ top: 73vh;
+ left: 7vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(34):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(35) {
+ top: 46vh;
+ left: 15vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(35):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(36) {
+ top: 32vh;
+ left: 17vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(36):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(37) {
+ top: 27vh;
+ left: 91vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(37):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(38) {
+ top: 62vh;
+ left: 96vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(38):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(39) {
+ top: 97vh;
+ left: 7vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(39):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(40) {
+ top: 39vh;
+ left: 75vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(40):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(41) {
+ top: 76vh;
+ left: 67vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(41):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(42) {
+ top: 71vh;
+ left: 34vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(42):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(43) {
+ top: 76vh;
+ left: 63vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(43):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(44) {
+ top: 1vh;
+ left: 87vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(44):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(45) {
+ top: 1vh;
+ left: 94vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-2:nth-of-type(45):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(46) {
+ top: 67vh;
+ left: 51vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(46):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(47) {
+ top: 48vh;
+ left: 79vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-2:nth-of-type(47):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(48) {
+ top: 18vh;
+ left: 70vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-2:nth-of-type(48):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(49) {
+ top: 84vh;
+ left: 53vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(49):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(50) {
+ top: 28vh;
+ left: 95vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(50):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(51) {
+ top: 76vh;
+ left: 33vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(51):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(52) {
+ top: 45vh;
+ left: 41vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 1s;
+ animation-delay: 1s;
+}
+.star-2:nth-of-type(52):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(53) {
+ top: 9vh;
+ left: 55vw;
+ width: 3px;
+ height: 3px;
+ -webkit-animation-delay: 4s;
+ animation-delay: 4s;
+}
+.star-2:nth-of-type(53):before {
+ width: 6px;
+ height: 6px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(54) {
+ top: 22vh;
+ left: 68vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(54):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(55) {
+ top: 15vh;
+ left: 12vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(55):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(56) {
+ top: 75vh;
+ left: 64vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(56):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(57) {
+ top: 30vh;
+ left: 21vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+}
+.star-2:nth-of-type(57):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(58) {
+ top: 20vh;
+ left: 40vw;
+ width: 4px;
+ height: 4px;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+.star-2:nth-of-type(58):before {
+ width: 8px;
+ height: 8px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(59) {
+ top: 15vh;
+ left: 32vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(59):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.star-2:nth-of-type(60) {
+ top: 4vh;
+ left: 4vw;
+ width: 2px;
+ height: 2px;
+ -webkit-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.star-2:nth-of-type(60):before {
+ width: 4px;
+ height: 4px;
+ top: -250%;
+}
+
+.container-title {
+ width: 600px;
+ height: 450px;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ position: absolute;
+ color: white;
+ line-height: 1;
+ font-weight: 700;
+ text-align: center;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ display: flex;
+}
+
+.title > * {
+ display: inline-block;
+ font-size: 200px;
+}
+
+.number {
+ text-shadow: 20px 20px 20px rgba(0, 0, 0, 0.2);
+ padding: 0 0.2em;
+ font-family: "Russo One", sans-serif;
+}
+
+.subtitle {
+ font-size: 25px;
+ margin-top: 1.5em;
+ font-family: "Lato", sans-serif;
+ text-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
+}
+
+button {
+ font-size: 22px;
+ //margin-top: 1.5em;
+ padding: 0.5em 1em;
+ letter-spacing: 1px;
+ font-family: "Lato", sans-serif;
+ color: white;
+ background-color: transparent;
+ border: 0;
+ cursor: pointer;
+ z-index: 999;
+ border: 2px solid white;
+ border-radius: 5px;
+ text-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
+ transition: opacity 0.2s ease;
+}
+button:hover {
+ opacity: 0.7;
+}
+button:focus {
+ outline: 0;
+}
+
+.moon {
+ position: relative;
+ border-radius: 50%;
+ width: 160px;
+ height: 160px;
+ z-index: 2;
+ background-color: #fff;
+ box-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #fff, 0 0 70px #fff, 0 0 80px #fff, 0 0 100px #ff1177;
+ -webkit-animation: rotate 5s ease-in-out infinite;
+ animation: rotate 5s ease-in-out infinite;
+}
+.moon .face {
+ top: 60%;
+ left: 47%;
+ position: absolute;
+}
+.moon .face .mouth {
+ border-top-left-radius: 50%;
+ border-bottom-right-radius: 50%;
+ border-top-right-radius: 50%;
+ background-color: #5c3191;
+ width: 25px;
+ height: 25px;
+ position: absolute;
+ -webkit-animation: snore 5s ease-in-out infinite;
+ animation: snore 5s ease-in-out infinite;
+ transform: rotate(45deg);
+ box-shadow: inset -4px -4px 4px rgba(0, 0, 0, 0.3);
+}
+.moon .face .eyes {
+ position: absolute;
+ top: -30px;
+ left: -30px;
+}
+.moon .face .eyes .eye-left,
+.moon .face .eyes .eye-right {
+ border: 4px solid #5c3191;
+ width: 30px;
+ height: 15px;
+ border-bottom-left-radius: 100px;
+ border-bottom-right-radius: 100px;
+ border-top: 0;
+ position: absolute;
+}
+.moon .face .eyes .eye-left:before, .moon .face .eyes .eye-left:after,
+.moon .face .eyes .eye-right:before,
+.moon .face .eyes .eye-right:after {
+ content: "";
+ position: absolute;
+ border-radius: 50%;
+ width: 4px;
+ height: 4px;
+ background-color: #5c3191;
+ top: -2px;
+ left: -4px;
+}
+.moon .face .eyes .eye-left:after,
+.moon .face .eyes .eye-right:after {
+ left: auto;
+ right: -4px;
+}
+.moon .face .eyes .eye-right {
+ left: 50px;
+}
+
+.container-bird {
+ perspective: 2000px;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+.bird {
+ position: absolute;
+ z-index: 1000;
+ left: 50%;
+ top: 50%;
+ height: 40px;
+ width: 50px;
+ transform: translate3d(-100vw, 0, 0) rotateY(90deg);
+ transform-style: preserve-3d;
+}
+
+.bird-container {
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ transform-style: preserve-3d;
+ transform: translate3d(50px, 30px, -300px);
+}
+
+.wing {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ border-radius: 3px;
+ transform-style: preserve-3d;
+ transform-origin: center bottom;
+ z-index: 300;
+}
+
+.wing-left {
+ background: linear-gradient(to bottom, #a58dc4 0%, #7979a8 100%);
+ transform: translate3d(0, 0, 0) rotateX(-30deg);
+ -webkit-animation: wingLeft 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+ animation: wingLeft 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+}
+
+.wing-right {
+ background: linear-gradient(to bottom, #d9d3e2 0%, #b8a5d1 100%);
+ transform: translate3d(0, 0, 0) rotateX(-30deg);
+ -webkit-animation: wingRight 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+ animation: wingRight 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+}
+
+.wing-right-top,
+.wing-left-top {
+ border-right: 25px solid transparent;
+ border-left: 25px solid transparent;
+ top: -20px;
+ width: 100%;
+ position: absolute;
+ transform-origin: 100% 100%;
+}
+
+.wing-right-top {
+ border-bottom: 20px solid #b8a5d1;
+ transform: translate3d(0, 0, 0) rotateX(60deg);
+ -webkit-animation: wingRightTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+ animation: wingRightTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+}
+
+.wing-left-top {
+ border-bottom: 20px solid #7979a8;
+ transform: translate3d(0, 0, 0) rotateX(-60deg);
+ -webkit-animation: wingLeftTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+ animation: wingLeftTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
+}
+
+.bird-anim:nth-child(1) {
+ -webkit-animation: bird1 30s linear infinite forwards;
+ animation: bird1 30s linear infinite forwards;
+}
+
+.bird-anim:nth-child(2) {
+ -webkit-animation: bird2 30s linear infinite forwards;
+ animation: bird2 30s linear infinite forwards;
+ -webkit-animation-delay: 3s;
+ animation-delay: 3s;
+ z-index: -1;
+}
+
+.bird-anim:nth-child(3) {
+ -webkit-animation: bird3 30s linear infinite forwards;
+ animation: bird3 30s linear infinite forwards;
+ -webkit-animation-delay: 5s;
+ animation-delay: 5s;
+}
+
+.bird-anim:nth-child(4) {
+ -webkit-animation: bird4 30s linear infinite forwards;
+ animation: bird4 30s linear infinite forwards;
+ -webkit-animation-delay: 7s;
+ animation-delay: 7s;
+}
+
+.bird-anim:nth-child(5) {
+ -webkit-animation: bird5 30s linear infinite forwards;
+ animation: bird5 30s linear infinite forwards;
+ -webkit-animation-delay: 14s;
+ animation-delay: 14s;
+}
+
+.bird-anim:nth-child(6) {
+ -webkit-animation: bird6 30s linear infinite forwards;
+ animation: bird6 30s linear infinite forwards;
+ -webkit-animation-delay: 10s;
+ animation-delay: 10s;
+ z-index: -1;
+}
+
+@-webkit-keyframes rotate {
+ 0%, 100% {
+ transform: rotate(-8deg);
+ }
+ 50% {
+ transform: rotate(0deg);
+ }
+}
+
+@keyframes rotate {
+ 0%, 100% {
+ transform: rotate(-8deg);
+ }
+ 50% {
+ transform: rotate(0deg);
+ }
+}
+@-webkit-keyframes snore {
+ 0%, 100% {
+ transform: scale(1) rotate(30deg);
+ }
+ 50% {
+ transform: scale(0.5) rotate(30deg);
+ border-bottom-left-radius: 50%;
+ }
+}
+@keyframes snore {
+ 0%, 100% {
+ transform: scale(1) rotate(30deg);
+ }
+ 50% {
+ transform: scale(0.5) rotate(30deg);
+ border-bottom-left-radius: 50%;
+ }
+}
+@-webkit-keyframes twinkle {
+ 0%, 100% {
+ opacity: 0.7;
+ }
+ 50% {
+ opacity: 0.3;
+ }
+}
+@keyframes twinkle {
+ 0%, 100% {
+ opacity: 0.7;
+ }
+ 50% {
+ opacity: 0.3;
+ }
+}
+@-webkit-keyframes wingLeft {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(-50deg);
+ }
+ 50% {
+ transform: translate3d(0, -20px, 0) rotateX(-130deg);
+ background: linear-gradient(to bottom, #d9d3e2 0%, #b8a5d1 100%);
+ }
+}
+@keyframes wingLeft {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(-50deg);
+ }
+ 50% {
+ transform: translate3d(0, -20px, 0) rotateX(-130deg);
+ background: linear-gradient(to bottom, #d9d3e2 0%, #b8a5d1 100%);
+ }
+}
+@-webkit-keyframes wingLeftTop {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(-10deg);
+ }
+ 50% {
+ transform: translate3d(0px, 0px, 0) rotateX(-40deg);
+ border-bottom: 20px solid #b8a5d1;
+ }
+}
+@keyframes wingLeftTop {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(-10deg);
+ }
+ 50% {
+ transform: translate3d(0px, 0px, 0) rotateX(-40deg);
+ border-bottom: 20px solid #b8a5d1;
+ }
+}
+@-webkit-keyframes wingRight {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(50deg);
+ }
+ 50% {
+ transform: translate3d(0, -20px, 0) rotateX(130deg);
+ background: linear-gradient(to bottom, #a58dc4 0%, #7979a8 100%);
+ }
+}
+@keyframes wingRight {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(50deg);
+ }
+ 50% {
+ transform: translate3d(0, -20px, 0) rotateX(130deg);
+ background: linear-gradient(to bottom, #a58dc4 0%, #7979a8 100%);
+ }
+}
+@-webkit-keyframes wingRightTop {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(10deg);
+ }
+ 50% {
+ transform: translate3d(0px, 0px, 0px) rotateX(40deg);
+ border-bottom: 20px solid #7979a8;
+ }
+}
+@keyframes wingRightTop {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0) rotateX(10deg);
+ }
+ 50% {
+ transform: translate3d(0px, 0px, 0px) rotateX(40deg);
+ border-bottom: 20px solid #7979a8;
+ }
+}
+@-webkit-keyframes bird1 {
+ 0% {
+ transform: translate3d(-120vw, -20px, -1000px) rotateY(-40deg) rotateX(0deg);
+ }
+ 100% {
+ transform: translate3d(100vw, -40vh, 1000px) rotateY(-40deg) rotateX(0deg);
+ }
+}
+@keyframes bird1 {
+ 0% {
+ transform: translate3d(-120vw, -20px, -1000px) rotateY(-40deg) rotateX(0deg);
+ }
+ 100% {
+ transform: translate3d(100vw, -40vh, 1000px) rotateY(-40deg) rotateX(0deg);
+ }
+}
+@-webkit-keyframes bird2 {
+ 0%, 15% {
+ transform: translate3d(100vw, -300px, -1000px) rotateY(10deg) rotateX(0deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -20px, -1000px) rotateY(10deg) rotateX(0deg);
+ }
+}
+@keyframes bird2 {
+ 0%, 15% {
+ transform: translate3d(100vw, -300px, -1000px) rotateY(10deg) rotateX(0deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -20px, -1000px) rotateY(10deg) rotateX(0deg);
+ }
+}
+@-webkit-keyframes bird3 {
+ 0% {
+ transform: translate3d(100vw, -50vh, 100px) rotateY(-5deg) rotateX(-20deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -10vh, 100px) rotateY(-5deg) rotateX(-20deg);
+ }
+}
+@keyframes bird3 {
+ 0% {
+ transform: translate3d(100vw, -50vh, 100px) rotateY(-5deg) rotateX(-20deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -10vh, 100px) rotateY(-5deg) rotateX(-20deg);
+ }
+}
+@-webkit-keyframes bird4 {
+ 0% {
+ transform: translate3d(100vw, 30vh, 200px) rotateY(-5deg) rotateX(10deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -30vh, 200px) rotateY(-5deg) rotateX(10deg);
+ }
+}
+@keyframes bird4 {
+ 0% {
+ transform: translate3d(100vw, 30vh, 200px) rotateY(-5deg) rotateX(10deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, -30vh, 200px) rotateY(-5deg) rotateX(10deg);
+ }
+}
+@-webkit-keyframes bird5 {
+ 0%, 5% {
+ transform: translate3d(100vw, 30vh, 400px) rotateY(-15deg) rotateX(-10deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, 10vh, 400px) rotateY(-15deg) rotateX(-10deg);
+ }
+}
+@keyframes bird5 {
+ 0%, 5% {
+ transform: translate3d(100vw, 30vh, 400px) rotateY(-15deg) rotateX(-10deg);
+ }
+ 100% {
+ transform: translate3d(-100vw, 10vh, 400px) rotateY(-15deg) rotateX(-10deg);
+ }
+}
+@-webkit-keyframes bird6 {
+ 0%, 10% {
+ transform: translate3d(-100vw, 20vh, -500px) rotateY(15deg) rotateX(10deg);
+ }
+ 100% {
+ transform: translate3d(100vw, 40vh, -800px) rotateY(5deg) rotateX(10deg);
+ }
+}
+@keyframes bird6 {
+ 0%, 10% {
+ transform: translate3d(-100vw, 20vh, -500px) rotateY(15deg) rotateX(10deg);
+ }
+ 100% {
+ transform: translate3d(100vw, 40vh, -800px) rotateY(5deg) rotateX(10deg);
+ }
+}
+@media screen and (max-width: 580px) {
+ .container-404 {
+ width: 100%;
+ }
+
+ .number {
+ font-size: 100px;
+ }
+
+ .subtitle {
+ font-size: 20px;
+ padding: 0 1em;
+ }
+
+ .moon {
+ width: 100px;
+ height: 100px;
+ }
+
+ .face {
+ transform: scale(0.7);
+ }
+}
diff --git a/src/views/Login/index.tsx b/src/views/Login/index.tsx
index b28b178..7b5c67b 100644
--- a/src/views/Login/index.tsx
+++ b/src/views/Login/index.tsx
@@ -1,10 +1,11 @@
import {
- AlipayOutlined,
+ GithubOutlined,
+ GitlabOutlined,
LockOutlined,
MobileOutlined,
- TaobaoOutlined,
+ QqOutlined,
UserOutlined,
- WeiboOutlined,
+ WechatOutlined,
} from '@ant-design/icons'
import {
LoginFormPage,
@@ -12,12 +13,14 @@ import {
ProFormCheckbox,
ProFormText,
} from '@ant-design/pro-components'
-import { Divider, Space, Tabs, message } from 'antd'
-import type { CSSProperties } from 'react'
+import { Divider, Space, Tabs, message, Button } from 'antd'
+import { CSSProperties } from 'react'
import { useState } from 'react'
-// import { history } from 'umi';
-
-type LoginType = 'phone' | 'account'
+import logo from '@/assets/icons/schisandra.svg'
+import background from '@/assets/images/background.png'
+import { observer } from 'mobx-react'
+// import useStore from '@/utils/store/useStore.tsx'
+type LoginType = 'account' | 'phone'
const iconStyles: CSSProperties = {
color: 'rgba(0, 0, 0, 0.2)',
@@ -26,16 +29,17 @@ const iconStyles: CSSProperties = {
cursor: 'pointer',
}
-export default () => {
+export default observer(() => {
+ // const store = useStore('user')
+
const items = [
{ label: '账户密码登录', key: 'account' },
{ label: '手机号登录', key: 'phone' },
]
- const [loginType, setLoginType] = useState('phone')
+ const [loginType, setLoginType] = useState('account')
- const onSubmit = async (formData: unknown) => {
+ const onSubmit = async (formData: object) => {
console.log(formData)
- // history.push('/');
}
return (
{
}}>
- // 去看看
- //
- // ),
- // }}
+ backgroundImageUrl={background}
+ logo={logo}
+ title='五味子云相册'
+ subTitle='随时随地分享你的美好瞬间'
+ activityConfig={{
+ style: {
+ boxShadow: '0px 0px 8px rgba(0, 0, 0, 0.2)',
+ color: '#fff',
+ borderRadius: 8,
+ backgroundColor: '#1677FF',
+ },
+ title: '活动标题,可配置图片',
+ subTitle: '活动介绍说明文字',
+ action: (
+
+ ),
+ }}
actions={
{
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
-
+
{
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
-
+
{
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
-
+
+
+
+
@@ -224,4 +240,4 @@ export default () => {
)
-}
+})
diff --git a/src/views/about/About.tsx b/src/views/about/About.tsx
deleted file mode 100644
index 419d881..0000000
--- a/src/views/about/About.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useNavigate } from 'react-router-dom'
-
-export default () => {
- const navigate = useNavigate()
- const goBack = () => {
- navigate(-1)
- }
- return (
- <>
- About Page
-
- >
- )
-}
diff --git a/src/views/home/Home.tsx b/src/views/home/Home.tsx
deleted file mode 100644
index fec678c..0000000
--- a/src/views/home/Home.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Link } from 'react-router-dom'
-import { Button, Card, Image } from 'antd'
-import SvgIcon from '@/components/SvgIcon/SvgIcon.tsx'
-import wallhaven from '@/assets/images/wallhaven.jpg'
-export default () => {
- return (
- <>
- Home Page
- 页面跳转
-
- 测试
-
-
- >
- )
-}