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

- + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
4
+
+
+
+
+
+
+
+
+
+
4
+
+
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

- 页面跳转 - - 测试 - - - - ) -}