登陆相关

This commit is contained in:
tangping 2023-05-17 00:58:25 +08:00
commit c032eacf07
42 changed files with 15479 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
dist/
deploy_versions/
.temp/
.rn_temp/
node_modules/
.DS_Store
.swc

View File

@ -0,0 +1,17 @@
module.exports = {
content: ["./src/**/*.{js,tx,tsx,jsx,vue}"],
prefixer: false,
compile: false,
globalUtility: false,
darkMode: "media",
corePlugins: {
preflight: false,
divideColor: false,
divideOpacity: false,
divideStyle: false,
divideWidth: false,
space: false,
placeholderColor: false,
placeholderOpacity: false,
},
};

View File

@ -0,0 +1,47 @@
const range = (size) =>
Object.fromEntries(
[...Array(size).keys()]
.slice(1)
.map((i) => [`${i}_${size}`, `${(i / size) * 100}%`])
);
module.exports = {
content: ["./src/**/*.{js,tx,tsx,jsx,vue}"],
prefixer: false,
separator: "_",
compile: false,
globalUtility: false,
darkMode: "media",
corePlugins: {
preflight: false,
divideColor: false,
divideOpacity: false,
divideStyle: false,
divideWidth: false,
space: false,
placeholderColor: false,
placeholderOpacity: false,
transitionProperty: false,
},
exclude: [/([0-9]{1,}[.][0-9]*)$/],
theme: {
width: (theme) => ({
auto: "auto",
full: "100%",
screen: "100vw",
...Object.assign(...[2, 3, 4, 5, 6, 12].map(range)),
...theme("spacing"),
}),
height: (theme) => ({
auto: "auto",
full: "100%",
screen: "100vh",
...Object.assign(...[2, 3, 4, 5, 6, 12].map(range)),
...theme("spacing"),
}),
maxHeight: {
full: "100%",
screen: "100vh",
},
},
};

22
babel.config.js Normal file
View File

@ -0,0 +1,22 @@
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
presets: [
['taro', {
framework: 'react',
ts: false
}]
],
plugins: [
[
"import",
{
"libraryName": "@nutui/nutui-react-taro",
"libraryDirectory": "dist/esm",
"style": true,
"camel2DashComponentName": false
},
'nutui-react-taro'
]
]
}

9
config/dev.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = {
env: {
NODE_ENV: '"development"'
},
defineConstants: {
},
mini: {},
h5: {}
}

92
config/index.js Normal file
View File

@ -0,0 +1,92 @@
const path = require('path')
const config = {
projectName: 'mall',
date: '2023-5-15',
designWidth: 375,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2,
375: 2 / 1
},
sourceRoot: 'src',
outputRoot: 'dist',
plugins: ['@tarojs/plugin-html'],
defineConstants: {
},
copy: {
patterns: [
],
options: {
}
},
framework: 'react',
compiler: 'webpack5',
cache: {
enable: false // Webpack 持久化缓存配置建议开启。默认配置请参考https://docs.taro.zone/docs/config-detail#cache
},
sass: {
data: `@import "@nutui/nutui-react-taro/dist/styles/variables.scss";`
},
alias: {
'@/components': path.resolve(__dirname, '..', 'src/components'),
'@/assets': path.resolve(__dirname, '..', 'src/assets'),
'@/images': path.resolve(__dirname, '..', 'src/assets/images')
},
mini: {
postcss: {
pxtransform: {
enable: true,
config: {
selectorBlackList: ['nut-']
}
},
url: {
enable: true,
config: {
limit: 1024 // 设定转换尺寸上限
}
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
},
h5: {
publicPath: '/',
staticDirectory: 'static',
// esnextModules: ['nutui-react'],
postcss: {
pxtransform: {
enable: true,
config: {
selectorBlackList: ['nut-']
}
},
autoprefixer: {
enable: true,
config: {
}
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
}
}
module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
}

37
config/prod.js Normal file
View File

@ -0,0 +1,37 @@
module.exports = {
env: {
NODE_ENV: '"production"'
},
defineConstants: {
},
mini: {},
h5: {
/**
* WebpackChain 插件配置
* @docs https://github.com/neutrinojs/webpack-chain
*/
// webpackChain (chain) {
// /**
// * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
// * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
// */
// chain.plugin('analyzer')
// .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
// /**
// * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
// * @docs https://github.com/chrisvfritz/prerender-spa-plugin
// */
// const path = require('path')
// const Prerender = require('prerender-spa-plugin')
// const staticDir = path.join(__dirname, '..', 'dist')
// chain
// .plugin('prerender')
// .use(new Prerender({
// staticDir,
// routes: [ '/pages/index/index' ],
// postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
// }))
// }
}
}

91
package.json Normal file
View File

@ -0,0 +1,91 @@
{
"name": "mall",
"version": "1.0.0",
"private": true,
"description": "商城",
"templateInfo": {
"name": "react-NutUI",
"typescript": false,
"css": "sass"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.7.7",
"@nutui/nutui-react-taro": "^1.4.7",
"@tarojs/components": "3.6.5",
"@tarojs/helper": "3.6.5",
"@tarojs/plugin-framework-react": "3.6.5",
"@tarojs/plugin-html": "3.6.5",
"@tarojs/plugin-platform-alipay": "3.6.5",
"@tarojs/plugin-platform-h5": "3.6.5",
"@tarojs/plugin-platform-jd": "3.6.5",
"@tarojs/plugin-platform-qq": "3.6.5",
"@tarojs/plugin-platform-swan": "3.6.5",
"@tarojs/plugin-platform-tt": "3.6.5",
"@tarojs/plugin-platform-weapp": "3.6.5",
"@tarojs/react": "3.6.5",
"@tarojs/runtime": "3.6.5",
"@tarojs/shared": "3.6.5",
"@tarojs/taro": "3.6.5",
"autoprefixer": "^10.4.14",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-refresh": "^0.11.0",
"tailwindcss": "^3.3.2"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
"@tarojs/cli": "3.6.5",
"@tarojs/webpack5-runner": "3.6.5",
"@types/node": "^18.15.11",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react-router-dom": "^5.1.7",
"@types/react-syntax-highlighter": "^13.5.2",
"@types/react-test-renderer": "^18.0.0",
"@types/react-transition-group": "^4.4.4",
"@types/webpack-env": "^1.13.6",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"babel-plugin-import": "^1.13.3",
"babel-preset-taro": "3.6.5",
"eslint": "^8.12.0",
"eslint-config-taro": "3.6.5",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-react-hooks": "^4.2.0",
"postcss": "^8.4.18",
"style-loader": "1.3.0",
"stylelint": "^14.4.0",
"ts-node": "^10.9.1",
"typescript": "^4.1.0",
"webpack": "^5.78.0"
}
}

12380
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

32
project.config.json Normal file
View File

@ -0,0 +1,32 @@
{
"miniprogramRoot": "dist/",
"projectname": "mall",
"description": "商城",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"enhance": false,
"compileHotReLoad": false,
"postcss": false,
"minified": false,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"condition": false
},
"compileType": "miniprogram",
"libVersion": "2.25.3",
"srcMiniprogramRoot": "dist/",
"packOptions": {
"ignore": [],
"include": []
},
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}

View File

@ -0,0 +1,7 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "mall",
"setting": {
"compileHotReLoad": true
}
}

13
project.tt.json Normal file
View File

@ -0,0 +1,13 @@
{
"miniprogramRoot": "./",
"projectname": "mall",
"description": "商城",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
}

15
src/app.config.js Normal file
View File

@ -0,0 +1,15 @@
export default defineAppConfig({
pages: [
'pages/index/index',
'pages/login/index',
'pages/register/index',
'pages/forgot/index',
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black',
navigationStyle: 'custom'
}
})

21
src/app.js Normal file
View File

@ -0,0 +1,21 @@
import React, { useEffect } from 'react'
import { useDidShow, useDidHide } from '@tarojs/taro'
// 全局样式
import './app.scss'
function App(props) {
// 可以使用所有的 React Hooks
useEffect(() => { })
// 对应 onShow
useDidShow(() => {
})
// 对应 onHide
useDidHide(() => { })
return props.children
}
export default App

101
src/app.scss Normal file
View File

@ -0,0 +1,101 @@
@import './tailwind.scss';
.text-blue {
color: #2196F3;
}
.text-base {
font-size: 16px;
line-height: 24px;
}
.text-2xl {
font-size: 24px;
line-height: 32px;
}
.text-3xl {
font-size: 28px;
line-height: 36px;
}
.text-right {
text-align: right;
}
.grid {
display: grid;
}
.bg-slate-50 {
background: #F6F7FD;
}
.h-screen {
height: 100vh;
}
.h-35 {
height: 35px;
}
.h-6 {
height: 24px
}
.w-35 {
width: 35px;
}
.w-6 {
width: 24px
}
.square-35 {
height: 35px;
width: 35px;
}
.flex {
display: flex;
}
.justify-center {
justify-content: center;
}
.items-center {
align-items: center;
}
.ml-7 {
margin-left: 30px;
}
.mt-8 {
margin-top: 32px;
}
.mt-22 {
margin-top: 22px;
}
.mt-58 {
margin-top: 58px;
}
.absolute {
position: absolute
}
.relative {
position: relative
}
.bottom-16 {
bottom: 16px;
}

BIN
src/assets/images/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
src/assets/images/cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/assets/images/cart1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/assets/images/home1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/images/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

52
src/components/action.jsx Normal file
View File

@ -0,0 +1,52 @@
import { Tabbar } from '@nutui/nutui-react-taro';
import { TabbarItem } from '@nutui/nutui-react-taro';
import { View } from '@tarojs/components';
import cart from '@/images/cart.png'
import cart1 from '@/images/cart1.png'
import home1 from '@/images/home1.png'
import user from '@/images/user.png'
import './action.scss'
import { useState } from 'react';
import Taro from '@tarojs/taro'
const home = home1
const TabbarAction = (props = {}) => {
const { index = 0 } = props
const [activeIndex, setActiveIndex] = useState(index)
const onSwitch = (_, id) => {
setActiveIndex(id)
if (id === 0) {
nav('/pages/index/index')
} else if (id === 1) {
nav('/pages/goods/index')
} else if (id === 2) {
nav('/pages/home/index')
}
}
const nav = (url = '') => {
Taro.navigateTo({
url
})
}
return <View>
<Tabbar bottom size={24} className='tabbar-container' onSwitch={onSwitch} activeVisible={activeIndex}
unactiveColor='#A9A9AA'
activeColor='#F67952'
>
<TabbarItem tabTitle="首页" icon={activeIndex === 0 ? home1 : home} />
<TabbarItem tabTitle="商城" icon={activeIndex === 1 ? cart1 : cart} />
<TabbarItem tabTitle="我的" icon={activeIndex === 2 ? user1 : user} />
</Tabbar>
</View>
}
export default TabbarAction

View File

@ -0,0 +1,12 @@
.tabbar-container {
height: 66px;
background: #FFFFFF;
box-shadow: 0px 8px 53px 0px rgba(233, 234, 234, 0.8);
border-radius: 13px 13px 0px 0px;
border: none;
width: 100vw;
}
.nut-tabbar-item__icon-box--nav-word {
font-size: 14px !important;
}

16
src/components/nav.jsx Normal file
View File

@ -0,0 +1,16 @@
import { NavBar } from '@nutui/nutui-react-taro'
import './nav.scss'
import { View } from '@tarojs/components'
const Nav = (props = {}) => {
const {
title = ''
} = props
return <View className="nav-container">
<NavBar title={title} />
</View>
}
export default Nav

8
src/components/nav.scss Normal file
View File

@ -0,0 +1,8 @@
.nav-container {
width: 375px;
height: 60px;
background: #FFFFFF;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.12);
border-radius: 0px 0px 0px 0px;
opacity: 1;
}

24
src/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-touch-fullscreen" content="yes">
<meta name="format-detection" content="telephone=no,address=no">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>mall</title>
<script><%= htmlWebpackPlugin.options.script %></script>
</head>
<body>
<div id="app"></div>
</body>
<script type="text/javascript">
</script>
</html>

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '找回密码'
})

133
src/pages/forgot/index.jsx Normal file
View File

@ -0,0 +1,133 @@
import { Image, Input, Text, View } from "@tarojs/components"
import back from '@/images/back.png'
import checked from '@/images/checked.png'
import eyeClose from '@/images/eyeClose.png'
import './index.scss'
import { Button } from "@nutui/nutui-react-taro"
import { useState } from "react"
import Taro from "@tarojs/taro"
const unChecked = checked
const activeEye = eyeClose
const Login = () => {
const [account, setAccount] = useState('')
const [pwd, setPwd] = useState()
const [showPwd, setShowPwd] = useState(false)
const [loginMode, setLoginMode] = useState('account')
const [mobile, setMobile] = useState('')
const [smsCode, setSmsCode] = useState('')
const [interval, setIntervalTime] = useState(0)
//
const backFn = () => {
Taro.getCurrentPages().length > 0 && Taro.navigateBack()
}
//
const loginFn = () => {
Taro.redirectTo({
url: '/pages/login/index'
})
}
//
const cleanFn = () => {
if (loginMode === 'account') {
setAccount('')
setPwd('')
return
}
setMobile('')
setSmsCode('')
}
//
const countDown = () => {
if (!mobile) {
return
}
setIntervalTime(60)
if (interval > 0) {
return
}
let start = 60
const timer = setInterval(() => {
if (start > 0) {
start--
if (start <= 0) {
clearInterval(timer)
}
setIntervalTime(start)
}
}, 1000)
}
return <View className="login-frame bg-slate-50 h-screen text-base">
<View className="login-header flex justify-center items-center text-lg font-bold">
<Image src={back} className="square-35 absolute left-7" onClick={backFn} />
<View>新起点</View>
</View>
<View className="forgot-container relative">
<View className="relative font-bold text-lg block h-6 ">忘记密码</View>
<View>
<View className="form-item mt-58">
<View className="form-label">账号</View>
<View className="form-control relative">
<Input className="form-input" placeholder="请输入手机号" onInput={(v) => {
setAccount(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={account ? checked : unChecked} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">验证码</View>
<View className="form-control relative">
<Input className="form-input" maxlength={6} placeholder="请输入验证码" onInput={(v) => {
setSmsCode(v.detail.value)
}} />
<Button className="code-btn" disabled={interval > 0} onClick={countDown}>{interval ? interval + 's' : '获取验证码'}</Button>
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">密码</View>
<View className="form-control relative">
<Input className="form-input" type={showPwd ? 'text' : 'password'} placeholder="设置新密码" onInput={(v) => {
setPwd(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showPwd ? activeEye : eyeClose} onClick={() => {
setShowPwd(v => !v)
}} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">密码</View>
<View className="form-control relative">
<Input className="form-input" type={showPwd ? 'text' : 'password'} placeholder="再次确认新密码" onInput={(v) => {
setPwd(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showPwd ? activeEye : eyeClose} onClick={() => {
setShowPwd(v => !v)
}} />
</View>
</View>
<View className="forgot-form-helper">
<View className="forgot-password" onClick={loginFn}>已有账号去登陆</View>
</View>
<View className="login-footer flex flex-col justify-center">
<Button className="login-btn">找回密码</Button>
</View>
</View>
</View>
</View>
}
export default Login

123
src/pages/forgot/index.scss Normal file
View File

@ -0,0 +1,123 @@
.login-header {
height: 86px;
}
.login-frame {
height: auto;
font-size: 14px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
}
.forgot-container {
width: 375px;
height: calc(100vh - 86px);
background: #FFFFFF;
border-radius: 20px 20px 0 0;
box-shadow: 0px 8px 51px 0px rgba(230, 234, 238, 0.8);
box-sizing: border-box;
padding: 20px 30px;
}
.form-item {
height: 68px;
opacity: 1;
}
.form-label {
font-size: 12px;
font-weight: bold;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
color: #2a2b2d;
line-height: 16px;
}
.form-input>.weui-input {
border: none;
height: 52px;
line-height: 52px;
border-bottom: 1px solid #262A34;
position: relative;
font-size: 16px;
color: #000;
}
.form-input>.weui-input:focus {
border-bottom: 2px solid #F67952;
}
.form-input-placeholder,
.form-input>.weui-input::placeholder {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: bold;
color: #aeafb4;
}
.form-control {
height: 52px;
position: relative;
}
.forgot-password {
height: 24px;
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #666;
line-height: 24px;
text-align: right;
}
.forgot-form-helper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.login-btn {
width: 253px;
height: 55px;
background: #F67952;
border-radius: 68px 68px 68px 68px;
opacity: 1;
border: none;
color: white;
font-size: 16px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
}
.login-footer {
margin-top: 50px;
display: flex;
flex-direction: column;
align-items: center;
}
.quick-login {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #000000;
line-height: 24px;
margin-top: 30px;
}
.code-btn {
width: 83px;
height: 30px;
background: #F67952;
border-radius: 4px 4px 4px 4px;
opacity: 1;
font-size: 12px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
color: #FFFFFF;
border: none;
padding: 0;
position: absolute;
right: 0;
bottom: 12px;
}

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '首页'
})

69
src/pages/index/index.jsx Normal file
View File

@ -0,0 +1,69 @@
import React from 'react'
import { View } from '@tarojs/components'
import {
Button
} from "@nutui/nutui-react-taro";
import './index.scss'
import TabbarAction from '@/components/action';
import { Infiniteloading } from '@nutui/nutui-react-taro';
import { useEffect } from 'react';
import { useState } from 'react';
function Index() {
const [list, setList] = useState([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
useEffect(() => {
if (list.length > 50) {
setHasMore(false)
return
}
let l = []
for (let i = list.length; i < 10 + list.length; i++) {
l.push(i)
}
setList([...list, ...l])
}, [page])
return (
<View className='home-container'>
<View className='home-title'>首页</View>
<TabbarAction />
<View className='home-body' id="customScroll">
<Infiniteloading
containerId="customScroll"
useWindow={false}
loadTxt="loading"
loadMoreTxt="没有数据啦~"
loadIcon='loading'
hasMore={hasMore}
onLoadMore={(x) => {
setPage(p => p + 1)
x()
console.log(x)
}}
>
<View className='goods-container'>
{
list.map(item => {
return <View className='goods-item' key={item}>
{item}
</View>
})
}
</View>
</Infiniteloading>
</View>
</View>
)
}
export default Index

View File

@ -0,0 +1,46 @@
.home-container {
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-size: 14px;
background: #FDFDFD;
}
.home-title {
width: 100vw;
height: 60px;
background: #FFFFFF;
box-shadow: 0px 4px 13px 0px rgba(0, 0, 0, 0.12);
border-radius: 0px 0px 0px 0px;
opacity: 1;
font-size: 18px;
font-weight: bold;
color: #181A20;
line-height: 60px;
text-align: center;
}
.home-body {
height: calc(100vh - 60px - 66px);
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
}
.goods-container {
display: flex;
width: 100vw;
height: auto;
box-sizing: border-box;
flex-wrap: wrap;
flex-direction: row;
// background: red;
padding: 0 8px;
justify-content: space-between;
margin-top: 24px;
}
.goods-item {
height: 142px;
width: 174px;
background: red;
margin-top: 8px;
}

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '登录'
})

158
src/pages/login/index.jsx Normal file
View File

@ -0,0 +1,158 @@
import { Image, Input, Text, View } from "@tarojs/components"
import back from '@/images/back.png'
import checked from '@/images/checked.png'
import eyeClose from '@/images/eyeClose.png'
import './index.scss'
import { Button } from "@nutui/nutui-react-taro"
import { useState } from "react"
import Taro from "@tarojs/taro"
import { useDidShow } from "@tarojs/taro"
const unChecked = checked
const activeEye = eyeClose
const Login = () => {
const [account, setAccount] = useState('')
const [pwd, setPwd] = useState()
const [showPwd, setShowPwd] = useState(false)
const [loginMode, setLoginMode] = useState('account')
const [mobile, setMobile] = useState('')
const [smsCode, setSmsCode] = useState('')
const [interval, setIntervalTime] = useState(0)
useDidShow(() => {
})
//
const backFn = () => {
Taro.getCurrentPages().length > 0 && Taro.navigateBack()
}
//
const registerFn = () => {
Taro.navigateTo({
url: '/pages/register/index'
})
}
//
const forgotPasswordFn = () => {
Taro.navigateTo({
url: '/pages/forgot/index'
})
}
//
const cleanFn = () => {
if (loginMode === 'account') {
setAccount('')
setPwd('')
return
}
setMobile('')
setSmsCode('')
}
//
const countDown = () => {
if (!mobile) {
return
}
setIntervalTime(60)
if (interval > 0) {
return
}
let start = 60
const timer = setInterval(() => {
if (start > 0) {
start--
if (start <= 0) {
clearInterval(timer)
}
setIntervalTime(start)
}
}, 1000)
}
return <View className="login-frame bg-slate-50 h-screen text-base">
<View className="login-header flex justify-center items-center text-lg font-bold">
<Image src={back} className="square-35 absolute left-7" onClick={backFn} />
<View>新起点</View>
</View>
<View className="login-container relative">
<View className="relative font-bold text-lg block h-6 ">登录</View>
{
loginMode === 'account' ? <View>
<View className="form-item mt-58">
<View className="form-label">账号</View>
<View className="form-control relative">
<Input className="form-input" placeholder="请输入账号" onInput={(v) => {
setAccount(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={account ? checked : unChecked} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">密码</View>
<View className="form-control relative">
<Input className="form-input" type={showPwd ? 'text' : 'password'} placeholder="请输入密码" onInput={(v) => {
setPwd(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showPwd ? activeEye : eyeClose} onClick={() => {
setShowPwd(v => !v)
}} />
</View>
</View>
<View className="form-helper">
<View className="forgot-password" onClick={registerFn}>注册账号</View>
<View className="forgot-password" onClick={forgotPasswordFn}>忘记密码</View>
</View>
<View className="login-footer flex flex-col justify-center">
<Button className="login-btn">登录</Button>
<View className="quick-login" onClick={() => {
setLoginMode('mobile')
cleanFn()
}}>快捷登录</View>
</View>
</View> : <View>
<View className="form-item mt-58">
<View className="form-label">手机号</View>
<View className="form-control relative">
<Input className="form-input" placeholder="请输手机号" placeholderClass="form-input-placeholder" onInput={(v) => {
setMobile(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={mobile ? checked : unChecked} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">验证码</View>
<View className="form-control relative">
<Input className="form-input" maxlength={6} placeholder="请输入验证码" onInput={(v) => {
setSmsCode(v.detail.value)
}} />
<Button className="code-btn" disabled={interval > 0} onClick={countDown}>{interval ? interval + 's' : '获取验证码'}</Button>
</View>
</View>
<View className="login-footer flex flex-col justify-center">
<Button className="login-btn">登录</Button>
<View className="quick-login" onClick={() => {
setLoginMode('account')
cleanFn()
}}>密码登录</View>
</View>
</View>
}
</View>
</View>
}
export default Login

123
src/pages/login/index.scss Normal file
View File

@ -0,0 +1,123 @@
.login-header {
height: 86px;
}
.login-frame {
height: auto;
font-size: 14px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
}
.login-container {
width: 375px;
height: calc(100vh - 86px);
background: #FFFFFF;
border-radius: 20px 20px 0 0;
box-shadow: 0px 8px 51px 0px rgba(230, 234, 238, 0.8);
box-sizing: border-box;
padding: 38px 30px;
}
.form-item {
height: 68px;
opacity: 1;
}
.form-label {
font-size: 12px;
font-weight: bold;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
color: #2a2b2d;
line-height: 16px;
}
.form-input>.weui-input {
border: none;
height: 52px;
line-height: 52px;
border-bottom: 1px solid #262A34;
position: relative;
font-size: 16px;
color: #000;
}
.form-input>.weui-input:focus {
border-bottom: 2px solid #F67952;
}
.form-input-placeholder,
.form-input>.weui-input::placeholder {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: bold;
color: #aeafb4;
}
.form-control {
height: 52px;
position: relative;
}
.forgot-password {
height: 24px;
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #666;
line-height: 24px;
text-align: right;
}
.form-helper {
margin-top: 16px;
display: flex;
justify-content: space-between;
}
.login-btn {
width: 253px;
height: 55px;
background: #F67952;
border-radius: 68px 68px 68px 68px;
opacity: 1;
border: none;
color: white;
font-size: 16px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
}
.login-footer {
margin-top: 50px;
display: flex;
flex-direction: column;
align-items: center;
}
.quick-login {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #000000;
line-height: 24px;
margin-top: 30px;
}
.code-btn {
width: 83px;
height: 30px;
background: #F67952;
border-radius: 4px 4px 4px 4px;
opacity: 1;
font-size: 12px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
color: #FFFFFF;
border: none;
padding: 0;
position: absolute;
right: 0;
bottom: 12px;
}

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '注册账号'
})

View File

@ -0,0 +1,132 @@
import { Image, Input, Text, View } from "@tarojs/components"
import back from '@/images/back.png'
import checked from '@/images/checked.png'
import eyeClose from '@/images/eyeClose.png'
import './index.scss'
import { Button } from "@nutui/nutui-react-taro"
import { useState } from "react"
import Taro from "@tarojs/taro"
const unChecked = checked
const activeEye = eyeClose
const Login = () => {
const [pwd, setPwd] = useState()
const [confirmPassword, setConfirmPassword] = useState()
const [payPassword, setPayPassword] = useState()
const [confirmPayPassword, setConfirmPayPassword] = useState()
const [showPwd, setShowPwd] = useState(false)
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
const [showPayPassword, setShowPayPassword] = useState(false)
const [showConfirmPayPassword, setShowConfirmPayPassword] = useState(false)
const [mobile, setMobile] = useState('')
const [smsCode, setSmsCode] = useState('')
const [interval, setIntervalTime] = useState(0)
//
const backFn = () => {
Taro.getCurrentPages().length > 0 && Taro.navigateBack()
}
//
const countDown = () => {
if (!mobile) {
return
}
setIntervalTime(60)
if (interval > 0) {
return
}
let start = 60
const timer = setInterval(() => {
if (start > 0) {
start--
if (start <= 0) {
clearInterval(timer)
}
setIntervalTime(start)
}
}, 1000)
}
return <View className="login-frame bg-slate-50 h-screen text-base">
<View className="login-header flex justify-center items-center text-lg font-bold">
<Image src={back} className="square-35 absolute left-7" onClick={backFn} />
<View>新起点</View>
</View>
<View className="register-container relative">
<View className="form-item ">
<View className="form-label">手机号</View>
<View className="form-control relative">
<Input className="form-input" placeholder="请输手机号" placeholderClass="form-input-placeholder" onInput={(v) => {
setMobile(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={mobile ? checked : unChecked} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">验证码</View>
<View className="form-control relative">
<Input className="form-input" maxlength={6} placeholder="请输入验证码" onInput={(v) => {
setSmsCode(v.detail.value)
}} />
<Button className="code-btn" disabled={interval > 0} onClick={countDown}>{interval ? interval + 's' : '获取验证码'}</Button>
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">登录密码</View>
<View className="form-control relative">
<Input className="form-input" type={showPwd ? 'text' : 'password'} placeholder="请设置登录密码" onInput={(v) => {
setPwd(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showPwd ? activeEye : eyeClose} onClick={() => {
setShowPwd(v => !v)
}} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">确认登录密码</View>
<View className="form-control relative">
<Input className="form-input" type={showConfirmPassword ? 'text' : 'password'} placeholder="请确认登录密码" onInput={(v) => {
setConfirmPassword(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showConfirmPassword ? activeEye : eyeClose} onClick={() => {
setShowConfirmPassword(v => !v)
}} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">支付密码</View>
<View className="form-control relative">
<Input className="form-input" type={showPayPassword ? 'text' : 'password'} placeholder="请设置支付密码" onInput={(v) => {
setPayPassword(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showPayPassword ? activeEye : eyeClose} onClick={() => {
setShowPayPassword(v => !v)
}} />
</View>
</View>
<View className="form-item mt-22">
<View className="form-label">确认支付密码</View>
<View className="form-control relative">
<Input className="form-input" type={showConfirmPassword ? 'text' : 'password'} placeholder="请确认支付密码" onInput={(v) => {
setConfirmPayPassword(v.detail.value)
}} />
<Image className="w-6 h-6 absolute right-0 bottom-16" src={showConfirmPayPassword ? activeEye : eyeClose} onClick={() => {
setShowConfirmPayPassword(v => !v)
}} />
</View>
</View>
<View className="login-footer flex flex-col justify-center">
<Button className="login-btn">注册</Button>
</View>
</View>
</View>
}
export default Login

View File

@ -0,0 +1,123 @@
.login-header {
height: 86px;
}
.login-frame {
height: auto;
font-size: 14px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
}
.register-container {
width: 375px;
height: calc(100vh - 86px);
background: #FFFFFF;
border-radius: 20px 20px 0 0;
box-shadow: 0px 8px 51px 0px rgba(230, 234, 238, 0.8);
box-sizing: border-box;
padding: 22px 30px;
}
.form-item {
height: 68px;
opacity: 1;
}
.form-label {
font-size: 12px;
font-weight: bold;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
color: #2a2b2d;
line-height: 16px;
}
.form-input>.weui-input {
border: none;
height: 52px;
line-height: 52px;
border-bottom: 1px solid #262A34;
position: relative;
font-size: 16px;
color: #000;
}
.form-input>.weui-input:focus {
border-bottom: 2px solid #F67952;
}
.form-input-placeholder,
.form-input>.weui-input::placeholder {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: bold;
color: #aeafb4;
}
.form-control {
height: 52px;
position: relative;
}
.forgot-password {
height: 24px;
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #666;
line-height: 24px;
text-align: right;
}
.form-helper {
margin-top: 16px;
display: flex;
justify-content: space-between;
}
.login-btn {
width: 253px;
height: 55px;
background: #F67952;
border-radius: 68px 68px 68px 68px;
opacity: 1;
border: none;
color: white;
font-size: 16px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
}
.login-footer {
margin-top: 50px;
display: flex;
flex-direction: column;
align-items: center;
}
.quick-login {
font-size: 16px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #000000;
line-height: 24px;
margin-top: 30px;
}
.code-btn {
width: 83px;
height: 30px;
background: #F67952;
border-radius: 4px 4px 4px 4px;
opacity: 1;
font-size: 12px;
font-family: Source Han Sans CN-Bold, Source Han Sans CN;
font-weight: bold;
color: #FFFFFF;
border: none;
padding: 0;
position: absolute;
right: 0;
bottom: 12px;
}

1540
src/tailwind.scss Normal file

File diff suppressed because it is too large Load Diff

17
tailwind.config.js Normal file
View File

@ -0,0 +1,17 @@
module.exports = {
content: ["./src/**/*.{js,tx,tsx,jsx,vue}"],
prefixer: false,
compile: false,
globalUtility: false,
darkMode: "media",
corePlugins: {
preflight: false,
divideColor: false,
divideOpacity: false,
divideStyle: false,
divideWidth: false,
space: false,
placeholderColor: false,
placeholderOpacity: false,
},
};