@ -0,0 +1,58 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
|
||||
# Runtime data |
||||
pids |
||||
*.pid |
||||
*.seed |
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
lib-cov |
||||
|
||||
# Coverage directory used by tools like istanbul |
||||
coverage |
||||
|
||||
# nyc test coverage |
||||
.nyc_output |
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
||||
.grunt |
||||
|
||||
# node-waf configuration |
||||
.lock-wscript |
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
||||
build/Release |
||||
|
||||
# Dependency directories |
||||
node_modules |
||||
jspm_packages |
||||
|
||||
# Optional npm cache directory |
||||
.npm |
||||
|
||||
# Optional REPL history |
||||
.node_repl_history |
||||
|
||||
# 0x |
||||
profile-* |
||||
|
||||
# mac files |
||||
.DS_Store |
||||
|
||||
# vim swap files |
||||
*.swp |
||||
|
||||
# webstorm |
||||
.idea |
||||
|
||||
# vscode |
||||
.vscode |
||||
*code-workspace |
||||
|
||||
# clinic |
||||
profile* |
||||
*clinic* |
||||
*flamegraph* |
@ -0,0 +1,25 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist.* |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,3 @@ |
||||
# userSys |
||||
|
||||
用户系统 |
@ -0,0 +1,66 @@ |
||||
CREATE TABLE `sys22_user`.`user_authority` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', |
||||
`authority_name` varchar(32) NOT NULL COMMENT '权限名称', |
||||
`type` int(2) NOT NULL COMMENT '权限类型', |
||||
`grade` int(2) NOT NULL COMMENT '权限等级', |
||||
`sequence` int(3) NOT NULL COMMENT '序列', |
||||
`authority_remarks` varchar(255) NOT NULL COMMENT '权限备注', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_info` ( |
||||
`uuid` char(32) NOT NULL COMMENT '唯一id', |
||||
`username` varchar(255) NULL COMMENT '用户名', |
||||
`email` varchar(255) NULL COMMENT '电子邮箱', |
||||
`phone` int(11) NULL COMMENT '手机号码', |
||||
`person` varchar(18) NULL COMMENT '身份证', |
||||
`status` int(1) NOT NULL DEFAULT 0 COMMENT '使用状态', |
||||
`createtime` bigint(13) NOT NULL COMMENT '创建时间', |
||||
PRIMARY KEY (`uuid`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_info_other` ( |
||||
`uuid` char(32) NOT NULL COMMENT '用户唯一ID', |
||||
`address` varchar(255) NULL COMMENT '住址', |
||||
`country` varchar(255) NULL COMMENT '国家地区', |
||||
`sex` int(1) NULL COMMENT '性别', |
||||
`birthady` int(8) NULL COMMENT '生日', |
||||
`nickname` varchar(64) NULL COMMENT '昵称', |
||||
`personal` varchar(255) NULL COMMENT '个人简介', |
||||
`slogn` varchar(64) NULL COMMENT '标语', |
||||
`avatar` varchar(64) NULL COMMENT '头像', |
||||
`background` varchar(64) NULL COMMENT '背景', |
||||
`updateTime` bigint(13) NOT NULL COMMENT '修改时间', |
||||
PRIMARY KEY (`uuid`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_login` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增id', |
||||
`uuid` char(32) NOT NULL COMMENT '用户唯一ID', |
||||
`password` varchar(255) NOT NULL COMMENT '加密密码', |
||||
`createtime` bigint(13) NOT NULL COMMENT '密码创建时间', |
||||
`status` int(1) NOT NULL DEFAULT 0 COMMENT '密码使用状态', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_relation_authority` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', |
||||
`role_id` int NOT NULL COMMENT '角色ID', |
||||
`authority_id` int NOT NULL COMMENT '权限ID', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_relation_role` ( |
||||
`id` int NOT NULL COMMENT '自增ID', |
||||
`uuid` char(32) NOT NULL COMMENT '用户标识', |
||||
`role_id` int NOT NULL DEFAULT 0 COMMENT '角色ID', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE `sys22_user`.`user_role` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '唯一ID', |
||||
`role_name` varchar(32) NOT NULL COMMENT '角色名称', |
||||
`role_remarks` varchar(255) NOT NULL COMMENT '角色备注', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
@ -0,0 +1,12 @@ |
||||
# userSys设计-需求分析 |
||||
|
||||
# 一、提供的服务 |
||||
|
||||
1. 记录用户信息 |
||||
2. 设置用户角色 |
||||
3. 控制角色权限 |
||||
4. 对外提供接口 |
||||
5. 记录系统权限 |
||||
6. 稳定、高可用、可拓展、可迭代 |
||||
7. 安全与保密性 |
||||
8. 高性能 |
@ -0,0 +1,7 @@ |
||||
# userSys |
||||
|
||||
## run |
||||
|
||||
```bash |
||||
npm start |
||||
``` |
@ -0,0 +1,118 @@ |
||||
// koa-router:提供全面的路由功能,比如类似Express的app.get/post/put的写法,URL命名参数、路由命名、嵌套路由、支持加载多个中间件
|
||||
// koa-bodyparser:post提交数据中间件,解析请求体时需要加载的中间件,支持x-www-form-urlencoded, application/json等格式的请求体,不支持form-data的请求体
|
||||
// koa-views:对进行视图模板渲染,支持ejs, nunjucks等模板引擎
|
||||
// koa-static:静态资源中间件,用作类似Nginx的静态文件服务,在本地开发时可用于加载前端文件或后端Fake数据
|
||||
// koa-session:session验证,支持将会话信息存储在本地Cookie或Redis, MongoDB
|
||||
// koa-jwt:token验证,路由权限控制功能,Session Base转为用Token Base
|
||||
// koa-helmet:网络安全,增加Strict-Transport-Security, X-Frame-Options, X-Frame-Options等HTTP头,提高应用程序的安全性
|
||||
// koa-compress:当响应体较大时,启用类似Gzip的压缩技术减少传输内容
|
||||
// koa-logger:输出请求日志的功能,包括请求的url、状态码、响应时间、响应体大小等信息
|
||||
// koa-convert:基于Promise的中间件和基于Generate的中间件相互转换
|
||||
// koa-nunjucks-2:轻量级 Nunjucks 中间件,可以用作模板引擎,为koa应用提供页面渲染功能
|
||||
// koa-favicon:页面logo加载
|
||||
// koa-json:get提交数据的中间件
|
||||
// koa-onerror:在服务器产生错误(throw 抛出等)后自动重定义到指定路径
|
||||
// koa-respond:在Koa上下文中添加了常用的方法
|
||||
|
||||
/** |
||||
* 添加依赖 |
||||
* */ |
||||
const Koa = require('koa')// 主依赖 Koa2
|
||||
, logger = require('koa-logger')// 日志
|
||||
// , bodyparser = require('koa-bodyparser')// 获取body的参数,post,支持x-www-form-urlencoded, application/json等格式的请求体,不支持form-data的请求体
|
||||
, koaBody = require('koa-body')// 支持form-data,支持文件,不支持x-www-form-urlencoded,不可同时使用,
|
||||
|
||||
|
||||
global.path = __dirname |
||||
|
||||
/** |
||||
* 路由 |
||||
* */ |
||||
const router = require('./src/Routes/index') |
||||
/** |
||||
* 创建应用程序 |
||||
* */ |
||||
const app = new Koa(); |
||||
// 数据库操作
|
||||
// databaseOperation()
|
||||
|
||||
// 请求日志
|
||||
app.use(logger()); |
||||
// 时间
|
||||
app.use(printMethod()); |
||||
// ctx.request.body body参数
|
||||
// 已过时,被koa-body替代
|
||||
// app.use(bodyparser({
|
||||
// enableTypes: ['json', 'form', 'text']
|
||||
// }))
|
||||
app.use(koaBody({ |
||||
multipart:true, // 支持文件上传
|
||||
encoding:'gzip', |
||||
strict:false,// 参数:如果启用,则不解析GET,HEAD,DELETE请求,默认为true
|
||||
formidable:{ |
||||
// uploadDir:path.join(__dirname,'public/upload/'), // 设置文件上传目录
|
||||
keepExtensions: true, // 保持文件的后缀
|
||||
maxFieldsSize:2 * 1024 * 1024, // 文件上传大小
|
||||
// onFileBegin:(name,file) => { // 文件上传前的设置
|
||||
// // console.log(`name: ${name}`);
|
||||
// // console.log(file);
|
||||
// },
|
||||
} |
||||
})); |
||||
|
||||
// token
|
||||
app.use(verToken()) |
||||
|
||||
|
||||
// 路由
|
||||
app.use(router.routes(), router.allowedMethods({ |
||||
// throw: true, // 抛出错误,代替设置响应头状态
|
||||
// notImplemented: () => '不支持当前请求所需要的功能',
|
||||
// methodNotAllowed: () => '不支持的请求方式'
|
||||
})); |
||||
|
||||
// veriToken
|
||||
function verToken(){ |
||||
return async function(ctx, next){ |
||||
// 请求地址判断
|
||||
if(ctx.req.url.indexOf('/api/user/will') == 0){ |
||||
await next() |
||||
}else{ |
||||
const token = ctx.request.header.authorization |
||||
const result2 = global.token.decrypt(token) |
||||
// 判断此Token有没有过期
|
||||
if(result2.token){ |
||||
const result = await global.Redis.getToken(token) |
||||
// 判断Redis是否有此token
|
||||
if(result){ |
||||
ctx.uuid = result2.id.uuid |
||||
ctx.token = token |
||||
await next() |
||||
}else{ |
||||
ctx.body = global.msg.failed({}, '账户令牌失效!', true) |
||||
} |
||||
|
||||
}else{ |
||||
ctx.body = global.msg.failed({}, '账户令牌失效。', true) |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
// 打印时间
|
||||
function printMethod() { |
||||
return async function (ctx, next) { |
||||
const start = new Date() |
||||
await next() |
||||
const ms = new Date() - start |
||||
console.info(`Method ${ctx.method} ${ctx.url} - ${ms}ms`) |
||||
} |
||||
} |
||||
|
||||
setTimeout(other) |
||||
function other(){ |
||||
console.w('other test') |
||||
console.w(global.path) |
||||
} |
||||
module.exports = app; |
@ -0,0 +1,86 @@ |
||||
/** |
||||
* 引入依赖 |
||||
* */ |
||||
|
||||
const initConsole = require('../tools/console/index') |
||||
, {getSystem} = require('../tools/getEnv') |
||||
, app = require('../app') |
||||
, http = require('http') |
||||
, config = require('../config/default.config') |
||||
, {createDatabase, createTables} = require('../src/Database/initDatabase') |
||||
, initRedisJSON = require('../tools/redisJSON') |
||||
, getStr = require('../tools/RandomString') |
||||
, makeUUID = require('../tools/uuid') |
||||
, mail = require('../tools/mail') |
||||
, {HASH} = require('../tools/user/encryptionString') |
||||
, token = require('../tools/user/token') |
||||
|
||||
async function run() { |
||||
// 初始化console
|
||||
console.time('> Web Start Use'); |
||||
initConsole(); |
||||
// console.clear();
|
||||
/** |
||||
* 环境探测 |
||||
* */ |
||||
const sysEnv = getSystem(); |
||||
console.dev(sysEnv); |
||||
/** |
||||
* 初始化数据库 |
||||
* */ |
||||
try { |
||||
let result = await createDatabase(); |
||||
console.s('> ' + result.message); |
||||
result = await createTables(); |
||||
console.s('> ' + result.message); |
||||
} catch (e) { |
||||
console.dev(e); |
||||
throw new Error(e) |
||||
} |
||||
/** |
||||
* 初始化Redis |
||||
* */ |
||||
try { |
||||
global.Redis = await initRedisJSON() |
||||
} catch (e) { |
||||
throw new Error(e) |
||||
} |
||||
/** |
||||
* 加载工具 |
||||
* */ |
||||
console.dev('> 加载工具类。') |
||||
global.cfg = config |
||||
global.getStr = getStr |
||||
global.makeUUID = makeUUID |
||||
global.mail = mail |
||||
global.HASH = HASH |
||||
global.token = token |
||||
// 统一消息回复
|
||||
global.msg = { |
||||
success: (data, message = "success") => ({data, message, status:true}), |
||||
failed: (data, message = "failed", reStart = false) => ({data, message, reStart, status:false}) |
||||
} |
||||
|
||||
/** |
||||
* 启动服务 |
||||
* */ |
||||
const server = http.createServer(app.callback()); |
||||
server.listen(config.PORT) |
||||
// 启动
|
||||
server.on("listening", function onListening(port) { |
||||
console.info(`> Web System Name: ${config.PROJECT_NAME}`) |
||||
console.info(`> httpServer listening in http://localhost:${config.PORT}`); |
||||
console.timeEnd('> Web Start Use') |
||||
}) |
||||
// 出错
|
||||
server.on("error", function onError(error) { |
||||
console.error('> httpServer Error!'); |
||||
console.error(error); |
||||
process.exit(1); |
||||
}) |
||||
} |
||||
|
||||
run() |
||||
|
||||
|
||||
|
@ -0,0 +1,66 @@ |
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_authority` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', |
||||
`authority_name` varchar(32) NOT NULL COMMENT '权限名称', |
||||
`type` int(2) NOT NULL COMMENT '权限类型', |
||||
`grade` int(2) NOT NULL COMMENT '权限等级', |
||||
`sequence` int(3) NOT NULL COMMENT '序列', |
||||
`authority_remarks` varchar(255) NOT NULL COMMENT '权限备注', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_info` ( |
||||
`uuid` char(32) NOT NULL COMMENT '唯一id', |
||||
`username` varchar(255) NULL COMMENT '用户名', |
||||
`email` varchar(255) NULL COMMENT '电子邮箱', |
||||
`phone` bigint(11) NULL COMMENT '手机号码', |
||||
`person` varchar(18) NULL COMMENT '身份证', |
||||
`status` int(1) NOT NULL DEFAULT 1 COMMENT '使用状态', |
||||
`createtime` bigint(13) NOT NULL COMMENT '创建时间', |
||||
PRIMARY KEY (`uuid`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_info_other` ( |
||||
`uuid` char(32) NOT NULL COMMENT '用户唯一ID', |
||||
`address` varchar(255) NULL COMMENT '住址', |
||||
`country` varchar(255) NULL COMMENT '国家地区', |
||||
`sex` int(1) NULL COMMENT '性别', |
||||
`birthday` int(8) NULL COMMENT '生日', |
||||
`nickname` varchar(64) NULL COMMENT '昵称', |
||||
`personal` varchar(255) NULL COMMENT '个人简介', |
||||
`slogan` varchar(64) NULL COMMENT '标语', |
||||
`avatar` varchar(64) NULL COMMENT '头像', |
||||
`background` varchar(64) NULL COMMENT '背景', |
||||
`updateTime` bigint(13) NOT NULL COMMENT '修改时间', |
||||
PRIMARY KEY (`uuid`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_login` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增id', |
||||
`uuid` char(32) NOT NULL COMMENT '用户唯一ID', |
||||
`password` char(128) NOT NULL COMMENT '加密密码', |
||||
`createtime` bigint(13) NOT NULL COMMENT '密码创建时间', |
||||
`status` int(1) NOT NULL DEFAULT 0 COMMENT '密码使用状态', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_relation_authority` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', |
||||
`role_id` int NOT NULL COMMENT '角色ID', |
||||
`authority_id` int NOT NULL COMMENT '权限ID', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_relation_role` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', |
||||
`uuid` char(32) NOT NULL COMMENT '用户标识', |
||||
`role_id` int NOT NULL DEFAULT 0 COMMENT '角色ID', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys22_user`.`user_role` ( |
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '唯一ID', |
||||
`role_name` varchar(32) NOT NULL COMMENT '角色名称', |
||||
`role_remarks` varchar(255) NOT NULL COMMENT '角色备注', |
||||
PRIMARY KEY (`id`) |
||||
); |
||||
|
@ -0,0 +1,52 @@ |
||||
module.exports = { |
||||
// 配置文件路径
|
||||
CONFIG_PATH:__dirname, |
||||
SYSTEM_NAME:'ANTO-G', |
||||
PROJECT_NAME:'sys22 userSys', |
||||
PORT:'3000', |
||||
KEY:'expressgy', |
||||
DATABASE_INIT:{ |
||||
host: 'localhost', |
||||
user: 'root', |
||||
password:'Hxl1314521', |
||||
database: "sys22_user", |
||||
port: 3306, |
||||
sqlFile:__dirname + "/database/sys22_user.sql" |
||||
}, |
||||
// 加密
|
||||
encryption:{ |
||||
salt:'время,вперёд!',// 盐
|
||||
secretKey:"быть всегда готовым!",// 密钥
|
||||
}, |
||||
// 用户系统
|
||||
user:{ |
||||
// 注册
|
||||
sign:{ |
||||
mailVerf: false,// 邮箱验证注册
|
||||
phoneVerf:false,// 手机验证注册
|
||||
length:4,// 验证码长度
|
||||
}, |
||||
// 登录
|
||||
login:{ |
||||
timeLimit:1000 * 60 * 60 * 24 * 14 ,// token时常
|
||||
} |
||||
}, |
||||
// 邮件服务
|
||||
EMAILCONFIG : { |
||||
HOST:'smtp.qq.com', |
||||
USER:'togy.gc@qq.com', |
||||
PASS:'qnpjbbeyunysdhac' |
||||
}, |
||||
// redis
|
||||
RedisJSON:{ |
||||
host:'localhost', |
||||
port:6379, |
||||
timeout:5 * 60 * 1000, |
||||
// timeout:5 * 1000,
|
||||
startClear: false, |
||||
pool:0, |
||||
clearTime:1000 * 60 * 60 * 6 |
||||
}, |
||||
// 一个用户的同时在线终端数量
|
||||
maxClientOnline:6 |
||||
} |
@ -0,0 +1,34 @@ |
||||
{ |
||||
"name": "realization", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1", |
||||
"start": "node bin/www.js", |
||||
"dev": "node_modules\\.bin\\nodemon bin\\www.js", |
||||
"prd": "pm2 start bin/www" |
||||
}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"dependencies": { |
||||
"crypto": "^1.0.1", |
||||
"jsonwebtoken": "^8.5.1", |
||||
"koa": "^2.13.4", |
||||
"koa-body": "^5.0.0", |
||||
"koa-bodyparser": "^4.3.0", |
||||
"koa-logger": "^3.2.1", |
||||
"koa-router": "^12.0.0", |
||||
"mysql": "^2.18.1", |
||||
"mysqls": "^1.2.2", |
||||
"nodemailer": "^6.8.0", |
||||
"redis": "^4.3.1", |
||||
"uuid": "^8.3.2" |
||||
}, |
||||
"devDependencies": { |
||||
"axios": "^0.27.2", |
||||
"mysql-import": "^5.0.21", |
||||
"nodemon": "^2.0.19" |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
const { createConnectionNull, createConnectionDatabase } = require('../../tools/database/createConnection') |
||||
, config = require('../../config/default.config') |
||||
, Importer = require('mysql-import') |
||||
|
||||
// 创建数据库
|
||||
function createDatabase(){ |
||||
return new Promise(async (rec, rej) => { |
||||
try { |
||||
const result = await createConnectionNull() |
||||
try{ |
||||
const SQL = `Create Database If Not Exists ?? Character Set UTF8;` |
||||
const params = config.DATABASE_INIT.database |
||||
result.connect.query(SQL, params,async (err,data) => { |
||||
if(err){ |
||||
rej({ |
||||
status:false, |
||||
message:'数据库创建失败。', |
||||
code:err.code, |
||||
errno:err.errno, |
||||
sqlMessage:err.sqlMessage |
||||
}) |
||||
}else{ |
||||
result.connect.destroy() |
||||
rec({ |
||||
status:true, |
||||
message:'数据库创建成功。', |
||||
// data
|
||||
}) |
||||
} |
||||
}) |
||||
}catch (e){ |
||||
rej(e) |
||||
} |
||||
}catch (e){ |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
// 创建表,这里使用的是SQL文件导入
|
||||
function createTables(){ |
||||
return new Promise(async (rec, rej) => { |
||||
const importer = new Importer({ |
||||
host: config.DATABASE_INIT.host, |
||||
user: config.DATABASE_INIT.user, |
||||
password: config.DATABASE_INIT.password, |
||||
database: config.DATABASE_INIT.database |
||||
}); |
||||
importer.import(config.DATABASE_INIT.sqlFile).then(()=>{ |
||||
const files_imported = importer.getImported(); |
||||
rec({ |
||||
status: true, |
||||
message: '数据表创建成功。', |
||||
SQLfile:files_imported |
||||
}) |
||||
}).catch(err=>{ |
||||
console.error(err); |
||||
rej({ |
||||
status: false, |
||||
message: '数据表创建失败。', |
||||
code: err.code, |
||||
errno: err.errno, |
||||
sqlMessage: err.sqlMessage |
||||
}) |
||||
}); |
||||
}) |
||||
} |
||||
|
||||
|
||||
module.exports = { |
||||
createDatabase, |
||||
createTables |
||||
} |
@ -0,0 +1,135 @@ |
||||
/** |
||||
* 数据操作的方法封装 |
||||
* */ |
||||
const {init, exec, sql, transaction} = require('mysqls') |
||||
const config = require("../../config/default.config"); |
||||
|
||||
init({ |
||||
host: config.DATABASE_INIT.host, |
||||
user: config.DATABASE_INIT.user, |
||||
password: config.DATABASE_INIT.password, |
||||
database: config.DATABASE_INIT.database, |
||||
port: config.DATABASE_INIT.port, |
||||
}) |
||||
|
||||
|
||||
function INSERT(table, data) { |
||||
return sql |
||||
.table(table) |
||||
.data(data) |
||||
.insert() |
||||
} |
||||
|
||||
function DELETE(table, data) { |
||||
return sql |
||||
.table(table) |
||||
.where(data) |
||||
.delet(); |
||||
} |
||||
|
||||
function UPDATE(table, data, where) { |
||||
return sql |
||||
.table(table) |
||||
.data(data) |
||||
.where(where) |
||||
.update() |
||||
} |
||||
|
||||
function SELECT(table, field, where) { |
||||
return sql |
||||
.table(table) |
||||
.field(field) |
||||
.where(where) |
||||
.select() |
||||
} |
||||
|
||||
function ORDER(table, field, where, order){ |
||||
return sql |
||||
.table(table) |
||||
.field(field) |
||||
.where(where) |
||||
.order(order) |
||||
.select() |
||||
} |
||||
|
||||
function LIMIT(table, field, limit, where){ |
||||
return sql |
||||
.table(table) |
||||
.field(field) |
||||
.limit(limit) |
||||
.where(where) |
||||
.select() |
||||
} |
||||
|
||||
function COUNT(table,count, where){ |
||||
return sql |
||||
.count(count) |
||||
.table(table) |
||||
.where(where) |
||||
.select() |
||||
} |
||||
|
||||
const table = { |
||||
userInfo:'user_info', |
||||
userInfoOther:'user_info_other', |
||||
userLogin:'user_login', |
||||
userRole:'user_role', |
||||
userAuthority:'user_authority', |
||||
userRelationRole:'user_relation_role', |
||||
userRelationAuthority:'user_relation_authority' |
||||
} |
||||
|
||||
const DB = { |
||||
SQL:{ |
||||
exec, sql |
||||
} |
||||
} |
||||
for(let i in table){ |
||||
DB[i] = (() => { |
||||
return { |
||||
INSERT(data){ |
||||
const SQL = INSERT(table[i],data) |
||||
console.e(SQL); |
||||
return exec(SQL) |
||||
}, |
||||
DELETE(data){ |
||||
const SQL = DELETE(table[i],data) |
||||
console.e(SQL); |
||||
return exec(SQL) |
||||
}, |
||||
// 批量删除ID关键段
|
||||
DELETEINID(data){ |
||||
const SQL = `DELETE FROM ${table[i]} WHERE id in (${data})` |
||||
console.e(SQL); |
||||
return exec(SQL) |
||||
}, |
||||
UPDATE(data, where){ |
||||
const SQL = UPDATE(table[i], data, where) |
||||
console.e(SQL); |
||||
return exec(SQL) |
||||
}, |
||||
SELECT(field, where){ |
||||
const SQL = SELECT(table[i], field, where) |
||||
console.e(SQL); |
||||
return exec(SQL) |
||||
}, |
||||
ORDER(field, where, order){ |
||||
const SQL = ORDER(table[i], field, where, order) |
||||
console.e(SQL) |
||||
return exec(SQL) |
||||
}, |
||||
LIMIT(field, limit, where){ |
||||
const SQL = LIMIT(table[i], field, limit, where) |
||||
console.e(SQL) |
||||
return exec(SQL) |
||||
}, |
||||
COUNT(count, where){ |
||||
const SQL = COUNT(table[i], count, where) |
||||
console.e(SQL) |
||||
return exec(SQL) |
||||
} |
||||
} |
||||
})() |
||||
} |
||||
|
||||
module.exports = DB |
@ -0,0 +1,41 @@ |
||||
// 用户登陆表
|
||||
const router = require('koa-router')(); |
||||
|
||||
// 全局路由
|
||||
router.prefix('/api') |
||||
|
||||
router.use( |
||||
// 用户系统
|
||||
require('./user/index'), |
||||
// 测试系统
|
||||
require('./test/index'), |
||||
) |
||||
router.get('/', async ctx => { |
||||
ctx.body = 'sys22_userSys API!' |
||||
}) |
||||
|
||||
module.exports = router |
||||
|
||||
/** |
||||
* |
||||
router.get('/', async ctx => { |
||||
console.log('query'.green, ctx.query) |
||||
// console.log('body'.green, ctx.request.body)
|
||||
// Get请求只能传Query参数,POST可以传Query和Body(常用)两种形式的参数。
|
||||
ctx.body = 'Hello World'; |
||||
}) |
||||
router.post('/', async ctx => { |
||||
console.log('query'.green, ctx.query) |
||||
console.log('body'.green, ctx.request.body) |
||||
ctx.body = 'Hello World'; |
||||
}) |
||||
|
||||
// 调用router.routes()来组装匹配好的路由,返回一个合并好的中间件
|
||||
// 调用router.allowedMethods()获得一个中间件,当发送了不符合的请求时,会返回 `405 Method Not Allowed` 或 `501 Not Implemented`
|
||||
app.use(router.routes()); |
||||
app.use(router.allowedMethods({ |
||||
// throw: true, // 抛出错误,代替设置响应头状态
|
||||
// notImplemented: () => '不支持当前请求所需要的功能',
|
||||
// methodNotAllowed: () => '不支持的请求方式'
|
||||
})); |
||||
* */ |
@ -0,0 +1,18 @@ |
||||
const router = require('koa-router')(); |
||||
|
||||
router.prefix('/test') |
||||
|
||||
router.get('/', function (ctx, next) { |
||||
ctx.body = 'this is a testes response!' |
||||
}) |
||||
router.post('/', function (ctx, next) { |
||||
console.log(ctx.request.body) |
||||
ctx.body = 'this is a testes response!' |
||||
}) |
||||
|
||||
router.get('/bar', function (ctx, next) { |
||||
ctx.body = 'this is a testes/bar response' |
||||
}) |
||||
|
||||
// 这里必须要使用router.routes(),多层嵌套
|
||||
module.exports = router.routes() |
@ -0,0 +1,27 @@ |
||||
/** |
||||
* 登陆后操作 |
||||
* */ |
||||
|
||||
const router = require('koa-router')(); |
||||
const api = require('../../API/index') |
||||
|
||||
router.prefix('/authority') |
||||
|
||||
// 创建权限
|
||||
router.post('/createAuthority', api.createAuthority) |
||||
// 删除权限
|
||||
router.del('/romveAuthority', api.romveAuthority) |
||||
// 修改权限信息
|
||||
router.put('/editAuthority', api.editAuthority) |
||||
// 获取所有权限
|
||||
router.get('/getAllAuthority', api.getAllAuthority) |
||||
// 添加角色权限关系
|
||||
router.post('/relation/addAuthorityAndRoleRelation', api.addAuthorityAndRoleRelation) |
||||
// 删除角色权限关联
|
||||
router.post('/relation/removeAuthorityAndRoleRelation', api.removeAuthorityAndRoleRelation) |
||||
// 清空当前权限的所有使用角色
|
||||
router.post('/relation/clearAuthorityRole', api.clearAuthorityRole) |
||||
// 获取角色权限
|
||||
router.get('/getRoleAuthority', api.getRoleAuthority) |
||||
|
||||
module.exports = router.routes() |
@ -0,0 +1,20 @@ |
||||
const router = require('koa-router')(); |
||||
|
||||
router.prefix('/user') |
||||
|
||||
router.get('/', async ctx => { |
||||
ctx.body = '/API/user' |
||||
}) |
||||
router.use( |
||||
// 登陆前操作
|
||||
require('./will'), |
||||
// // 账户操作
|
||||
require('./signed'), |
||||
// // 角色
|
||||
require('./role'), |
||||
// // 权限
|
||||
require('./authority') |
||||
) |
||||
|
||||
// 这里必须要使用router.routes(),多层嵌套
|
||||
module.exports = router.routes() |
@ -0,0 +1,25 @@ |
||||
/** |
||||
* 登陆后操作 |
||||
* */ |
||||
|
||||
const router = require('koa-router')(); |
||||
const api = require('../../API/index') |
||||
|
||||
router.prefix('/role') |
||||
|
||||
// 创建角色
|
||||
router.post('/createRole', api.createRole) |
||||
// 删除角色
|
||||
router.del('/deleteRoleList', api.deleteRoleList) |
||||
// 修改角色信息
|
||||
router.put('/editRole', api.editRole) |
||||
// 获取全部角色列表
|
||||
router.get('/getAllRoleList', api.getAllRoleList) |
||||
// 获取个人角色列表
|
||||
router.get('/getPersonalRoleIdList', api.getPersonalRoleIdList) |
||||
// 添加用户角色关联
|
||||
router.post('/relation/addRoleAndUserRelation', api.addRoleAndUserRelation) |
||||
// 删除用户角色关联
|
||||
router.post('/realtion/romoveRoleAndUserRelation', api.romoveRoleAndUserRelation) |
||||
|
||||
module.exports = router.routes() |
@ -0,0 +1,21 @@ |
||||
/** |
||||
* 登陆后操作 |
||||
* */ |
||||
|
||||
const router = require('koa-router')(); |
||||
const api = require('../../API/index') |
||||
|
||||
router.prefix('/signed') |
||||
|
||||
// 退出登录
|
||||
router.get('/signOut', api.signOut) |
||||
// 修改用户信息
|
||||
router.put('/editUserinfo', api.editUserinfo) |
||||
// 修改密码
|
||||
router.put('/editPassword', api.editPassword) |
||||
// 注销用户
|
||||
router.post('/writeoff', api.writeoff) |
||||
// 获取用户信息
|
||||
router.get('/getuserInfo', api.getuserInfo) |
||||
|
||||
module.exports = router.routes() |
@ -0,0 +1,20 @@ |
||||
/** |
||||
* 登录前操作 |
||||
* */ |
||||
const router = require('koa-router')(); |
||||
const api = require('../../API/index') |
||||
|
||||
router.prefix('/will') |
||||
|
||||
// 注册
|
||||
router.post('/signUp', api.signUp) |
||||
// 登录
|
||||
router.post('/signIn', api.signIn) |
||||
// 找回密码
|
||||
router.post('/reset', api.reset) |
||||
// 用户名查重
|
||||
router.get('/checkOnly', api.checkOnly) |
||||
// 发送验证码
|
||||
router.get('/sendCode', api.sendCode) |
||||
|
||||
module.exports = router.routes() |
@ -0,0 +1,31 @@ |
||||
const { createClient } = require('redis'); |
||||
|
||||
async function redisJSONDemo() { |
||||
try { |
||||
const TEST_KEY = 'test_node'; |
||||
|
||||
const client = createClient({ |
||||
url: 'redis://uair.cc:6379' |
||||
}); |
||||
await client.connect(); |
||||
|
||||
await client.json.set('signUpCode','$',{}) |
||||
await client.json.set('signUpCode','expressgy',{code:'9527',time:new Date().getTime()}) |
||||
await client.json.set('signUpCode','expressnie',{code:'9527',time:new Date().getTime()}) |
||||
await client.json.set('signUpCode','admin',{code:'9527',time:new Date().getTime(),name:"何夕"}) |
||||
await client.json.set('signUpCode','张良',{code:'9527',time:new Date().getTime(),name:"何夕"}) |
||||
const value = await client.json.get('signUpCode'); |
||||
// , {
|
||||
// // JSON Path: .node = the element called 'node' at root level.
|
||||
// path: 'pets[0].name',
|
||||
// }
|
||||
|
||||
console.log(value); |
||||
|
||||
await client.quit(); |
||||
} catch (e) { |
||||
console.error(e); |
||||
} |
||||
} |
||||
|
||||
redisJSONDemo(); |
@ -0,0 +1,22 @@ |
||||
const { createClient } = require('redis'); |
||||
|
||||
async function redisJSONDemo() { |
||||
try { |
||||
const TEST_KEY = 'test_node'; |
||||
|
||||
const client = createClient({ |
||||
url: 'redis://uair.cc:6379' |
||||
}); |
||||
await client.connect(); |
||||
|
||||
await client.set('4afcef91d5b45a965bcd4ffbd15b3af3','$') |
||||
console.log(await client.get('4afcef91d5b45a965bcd4ffbd15b3af3')) |
||||
console.log(await client.del('a76cb34121b5d327ae28ec56469ef6df9b2c16392bee476a4ea69314d57d36403b568654f53e89b473f631b547c2dd4ddb7d721a2f1192d999010a1fe214422dc67f4d1d5248dd86b3cc0acdeba15a89f8a60063d9e20e8d1f2f5b3777cebda3969e1d5f0cc7d7d920b1ac58617f4d832bf58311d9fd077aa16648f4b17f86bb50a37eefe369bf07d1f8cd624bd1ed6fa8dfb6c732a1487b1dbcd98541e8ce0922ace0050a485a3194d5fa823f3e43a2214ccac8c34362006ef90eae0afa89b9efba3e6902e22bd9a9071d79d46429e5be0fae3c381ff14728665c6435cf3b0d')) |
||||
|
||||
await client.quit(); |
||||
} catch (e) { |
||||
console.error(e); |
||||
} |
||||
} |
||||
|
||||
redisJSONDemo(); |
@ -0,0 +1,35 @@ |
||||
// ===============================================UUID && Token========================================================
|
||||
//
|
||||
// async function test() {
|
||||
// const R = await Redis()
|
||||
// await R.setLogin('uuid1', 'token11')
|
||||
// await R.setLogin('uuid1', 'token12')
|
||||
// await R.setLogin('uuid1', 'token13')
|
||||
// await R.setLogin('uuid1', 'token14')
|
||||
// await R.setLogin('uuid1', 'token15')
|
||||
// console.log(await R.getLogin('uuid1'))
|
||||
// await R.delLogin('uuid1', 'token15')
|
||||
// console.log(await R.getLogin('uuid1'))
|
||||
// // await R.setLogin('uuid1', 'token16')
|
||||
// // await R.setLogin('uuid1', 'token17')
|
||||
// // await R.setLogin('uuid1', 'token18')
|
||||
// // await R.setLogin('uuid1', 'token19')
|
||||
// // await R.setLogin('uuid1', 'token10')
|
||||
// // await R.setLogin('uuid2', 'token21')
|
||||
// // await R.setLogin('uuid2', 'token22')
|
||||
// // await R.setLogin('uuid2', 'token273')
|
||||
// // await R.setLogin('uuid2', 'token23')
|
||||
// // await R.setLogin('uuid2', 'token24')
|
||||
// // await R.setLogin('uuid2', 'token25')
|
||||
// // await R.setLogin('uuid2', 'token26')
|
||||
// // await R.setLogin('uuid2', 'token27')
|
||||
// // await R.setLogin('uuid2', 'token28')
|
||||
// // await R.clearLoginArr()
|
||||
// // console.log(await R.getToken('token21'));
|
||||
// // console.log(await R.getLogin('uuid1'));
|
||||
// // console.log(await R.getLogin('uuid2'));
|
||||
// // console.log('end')
|
||||
// }
|
||||
|
||||
|
||||
// test()
|
@ -0,0 +1,13 @@ |
||||
|
||||
console.time(0) |
||||
var timestamp = timestamp=new Date() |
||||
console.timeEnd(0) |
||||
|
||||
|
||||
console.time(1) |
||||
var timestamp1 = timestamp.valueOf(); |
||||
console.timeEnd(1) |
||||
|
||||
console.time(2) |
||||
var timestamp2 = timestamp.getTime(); |
||||
console.timeEnd(2) |
@ -0,0 +1,11 @@ |
||||
const token = require('../tools/user/token') |
||||
|
||||
const a = token.encrypt({user:'xsxsxs'}, 3) |
||||
|
||||
console.log(a) |
||||
|
||||
setTimeout(() => { |
||||
const b = token.decrypt(a) |
||||
|
||||
console.log(b) |
||||
},2000) |
@ -0,0 +1,3 @@ |
||||
const a = " asxasxas " |
||||
console.log(a); |
||||
console.log(a.trim()); |
@ -0,0 +1,21 @@ |
||||
/** |
||||
* 数据操作的方法封装 |
||||
* */ |
||||
const {init, exec, sql, transaction} = require('mysqls') |
||||
const config = require("../config/default.config"); |
||||
|
||||
init({ |
||||
host: config.DATABASE_INIT.host, |
||||
user: config.DATABASE_INIT.user, |
||||
password: config.DATABASE_INIT.password, |
||||
database: config.DATABASE_INIT.database, |
||||
port: config.DATABASE_INIT.port, |
||||
}) |
||||
|
||||
|
||||
let sqlstring = sql.field('authority_id').table('user_relation_authority').where({role_id:4}).select() |
||||
let SQQ = sql.query(`SELECT * from user_authority where id in (${sqlstring})`) |
||||
console.log(SQQ) |
||||
exec(SQQ).then(res => { |
||||
console.log(res) |
||||
}) |
@ -0,0 +1,10 @@ |
||||
function getStr(len, size='big'){ |
||||
if(size == 'big'){ |
||||
return Math.random().toString(36).slice(-len).toString().toUpperCase(); |
||||
}else{ |
||||
return Math.random().toString(36).slice(-len).toString().toLowerCase(); |
||||
} |
||||
|
||||
} |
||||
|
||||
module.exports = getStr |
@ -0,0 +1,70 @@ |
||||
const styles = { |
||||
// style: [ style code, reset code ]
|
||||
'bold' : ['\x1B[1m', '\x1B[22m'], |
||||
'italic' : ['\x1B[3m', '\x1B[23m'], |
||||
'underline' : ['\x1B[4m', '\x1B[24m'], |
||||
'inverse' : ['\x1B[7m', '\x1B[27m'], |
||||
'strikethrough' : ['\x1B[9m', '\x1B[29m'], |
||||
'white' : ['\x1B[37m', '\x1B[39m'], |
||||
'grey' : ['\x1B[90m', '\x1B[39m'], |
||||
'black' : ['\x1B[30m', '\x1B[39m'], |
||||
'blue' : ['\x1B[34m', '\x1B[39m'], |
||||
'cyan' : ['\x1B[36m', '\x1B[39m'], |
||||
'green' : ['\x1B[32m', '\x1B[39m'], |
||||
'magenta' : ['\x1B[35m', '\x1B[39m'], |
||||
'red' : ['\x1B[31m', '\x1B[39m'], |
||||
'yellow' : ['\x1B[33m', '\x1B[39m'], |
||||
'whiteBG' : ['\x1B[47m', '\x1B[49m'], |
||||
'greyBG' : ['\x1B[49;5;8m', '\x1B[49m'], |
||||
'blackBG' : ['\x1B[40m', '\x1B[49m'], |
||||
'blueBG' : ['\x1B[44m', '\x1B[49m'], |
||||
'cyanBG' : ['\x1B[46m', '\x1B[49m'], |
||||
'greenBG' : ['\x1B[42m', '\x1B[49m'], |
||||
'magentaBG' : ['\x1B[45m', '\x1B[49m'], |
||||
'redBG' : ['\x1B[41m', '\x1B[49m'], |
||||
'yellowBG' : ['\x1B[43m', '\x1B[49m'] |
||||
}; |
||||
function initConsole() { |
||||
// 斜体
|
||||
console.__proto__.i = function () { |
||||
console.log(`\x1B[3m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 粗体
|
||||
console.__proto__.b = function () { |
||||
console.log(`\x1B[1m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 下划线
|
||||
console.__proto__.u = function () { |
||||
console.log(`\x1B[4m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 错误
|
||||
console.__proto__.e = console.__proto__.err = function () { |
||||
console.error(`\x1B[31m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 成功
|
||||
console.__proto__.s = console.__proto__.success = function () { |
||||
console.log(`\x1B[32m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 警告
|
||||
console.__proto__.w = console.__proto__.warning = function () { |
||||
console.warn(`\x1B[33m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 一般提示
|
||||
console.__proto__.i = console.__proto__.info = function () { |
||||
console.log(`\x1B[34m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
// 青色
|
||||
console.__proto__.a = function () { |
||||
console.log(`\x1B[36m${Array.from(arguments).join(',')}\x1B[0m`) |
||||
} |
||||
|
||||
// dev
|
||||
console.__proto__.dev = console.log |
||||
|
||||
for(let i in styles){ |
||||
console.__proto__[i] = function (){ |
||||
console.log(`${styles[i][0]}${Array.from(arguments).join(',')}${styles[i][1]}`) |
||||
} |
||||
} |
||||
} |
||||
module.exports = initConsole |
@ -0,0 +1,65 @@ |
||||
/** |
||||
* 创建数据库连接 |
||||
* */ |
||||
const mysql = require("mysql"); |
||||
const config = require("../../config/default.config"); |
||||
|
||||
function createConnectionNull() { |
||||
return new Promise((rec, rej) => { |
||||
const db = mysql.createConnection({ |
||||
host: config.DATABASE_INIT.host, |
||||
user: config.DATABASE_INIT.user, |
||||
password: config.DATABASE_INIT.password, |
||||
}); |
||||
db.connect(err => { |
||||
if (err) { |
||||
rej({ |
||||
status: false, |
||||
message: '数据库连接失败。', |
||||
code: err.code, |
||||
errno: err.errno, |
||||
sqlMessage: err.sqlMessage |
||||
}) |
||||
} else { |
||||
rec({ |
||||
status: true, |
||||
message: '数据库连接成功。', |
||||
connect: db |
||||
}) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
function createConnectionDatabase() { |
||||
return new Promise((rec, rej) => { |
||||
const db = mysql.createConnection({ |
||||
host: config.DATABASE_INIT.host, |
||||
user: config.DATABASE_INIT.user, |
||||
password: config.DATABASE_INIT.password, |
||||
database: config.DATABASE_INIT.database |
||||
}); |
||||
db.connect(err => { |
||||
if (err) { |
||||
rej({ |
||||
status: false, |
||||
message: '数据库连接失败。', |
||||
code: err.code, |
||||
errno: err.errno, |
||||
sqlMessage: err.sqlMessage |
||||
}) |
||||
} else { |
||||
rec({ |
||||
status: true, |
||||
message: '数据库连接成功。', |
||||
connect: db |
||||
}) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
createConnectionNull, |
||||
createConnectionDatabase |
||||
} |
@ -0,0 +1,14 @@ |
||||
/** |
||||
* 格式化SQL语句 |
||||
* */ |
||||
|
||||
function formatSQL(tableStruct){ |
||||
let field = '' |
||||
for(let i of tableStruct.field){ |
||||
field += `${i.name} ${i.type} ${i.attribute.join(' ')} comment "${i.comment}",` |
||||
} |
||||
field = field.slice(0, field.length - 1) |
||||
return `Create Table If Not Exists ${tableStruct.tableName} (${field})comment = "${tableStruct.comment}";` |
||||
} |
||||
|
||||
module.exports = formatSQL |
@ -0,0 +1,17 @@ |
||||
/** |
||||
* 环境探测 |
||||
* */ |
||||
|
||||
const os = require('os') |
||||
|
||||
// 获取系统信息
|
||||
function getSystem(){ |
||||
return { |
||||
hostname:os.hostname(), |
||||
os:os.type() + os.arch() |
||||
} |
||||
} |
||||
|
||||
module.exports = { |
||||
getSystem |
||||
} |
@ -0,0 +1,75 @@ |
||||
const nodemailer = require('nodemailer'); //引入模块
|
||||
|
||||
|
||||
const reg = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/ |
||||
|
||||
const { EMAILCONFIG,SYSTEM_NAME } = require('../config/default.config') |
||||
|
||||
const transporter = nodemailer.createTransport({ |
||||
//node_modules/nodemailer/lib/well-known/services.json 查看相关的配置,如果使用qq邮箱,就查看qq邮箱的相关配置
|
||||
host:EMAILCONFIG.HOST, |
||||
// secureConnection:true,
|
||||
service: 'qq', //类型qq邮箱
|
||||
// port: 465,
|
||||
secure: true, // true for 465, false for other ports
|
||||
auth: { |
||||
user: EMAILCONFIG.USER, // 发送方的邮箱
|
||||
pass:EMAILCONFIG.PASS// smtp 的授权码
|
||||
} |
||||
//pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码)
|
||||
//邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码
|
||||
}); |
||||
const typeList = { |
||||
signUp:'账户注册', |
||||
signIn:'登录', |
||||
reset:'重置密码', |
||||
writeoff:'注销账户' |
||||
} |
||||
|
||||
function sendMail(mail, code, type) { |
||||
console.log(mail, code) |
||||
return new Promise((rec,rej) => { |
||||
if(!checkEmail(mail)){ |
||||
rej('邮箱格式错误!') |
||||
} |
||||
// 发送的配置项
|
||||
const mailOptions = { |
||||
from: '"TOGY.GC" <togy.gc@qq.com>', // 发送方
|
||||
to: mail, //接收者邮箱,多个邮箱用逗号间隔
|
||||
subject: `${SYSTEM_NAME}!`, // 标题
|
||||
text: 'Hello world?', // 文本内容
|
||||
html: `<div style="position: relative;height: 300px;width: 100%">
|
||||
<div><h1 style="text-align: center;line-height: 70px">欢迎使用 ${SYSTEM_NAME} </h1><p style="text-align: center">您在某些地方请求了邮箱的验证码,如果不是自己操作请修改账户的密码。</p></div> |
||||
<div style="width: 100%;position:relative;height: 200px;display: flex;align-items: center;justify-content: center"><div style="line-height: 80px;padding: 1em;font-size: 24px;color:red;font-weight: bold;text-align: centerline-height: 1em">此验证码用于 ${typeList[type]}。 </div><p style="text-align: center"></p></div> |
||||
<div style="width: 100%;position:relative;height: 200px;display: flex;align-items: center;justify-content: center"> |
||||
<div style="background: #333333;height: 80px;line-height: 80px;padding: 1em;font-size: 32px;color:#FEFEFE;font-weight: bold;text-align: center">${code}</div> |
||||
</div> |
||||
</div>`, //页面内容 |
||||
// attachments: [{//发送文件
|
||||
// filename: 'index.html', //文件名字
|
||||
// path: './index.html' //文件路径
|
||||
// },
|
||||
// {
|
||||
// filename: 'sendEmail.js', //文件名字
|
||||
// content: 'sendEmail.js' //文件路径
|
||||
// }
|
||||
// ]
|
||||
}; |
||||
//发送函数
|
||||
transporter.sendMail(mailOptions, (error, info) => { |
||||
if (error) { |
||||
rej(error,info) |
||||
} else { |
||||
rec(info) //因为是异步 所有需要回调函数通知成功结果
|
||||
} |
||||
}); |
||||
}) |
||||
} |
||||
|
||||
function checkEmail(email){ |
||||
return reg.test(email) |
||||
} |
||||
module.exports = { |
||||
sendMail, |
||||
checkEmail |
||||
} |
@ -0,0 +1,308 @@ |
||||
const {createClient} = require('redis') |
||||
, {RedisJSON, maxClientOnline, user} = require('../config/default.config') |
||||
,{HASH} = require('../tools/user/encryptionString') |
||||
|
||||
function initRedisJSON() { |
||||
return new Promise(async (res, rej) => { |
||||
const client = createClient({ |
||||
// https://github.com/redis/node-redis
|
||||
url: `redis://default:Hxl1314521@${RedisJSON.host}:${RedisJSON.port}` |
||||
}); |
||||
try { |
||||
await client.connect() |
||||
RedisJSON.startClear ? client.flushAll() : '' |
||||
client.select(RedisJSON.pool) |
||||
res(client) |
||||
} catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const codeList = ['signUp', 'signIn', 'reset', 'writeoff'] |
||||
|
||||
function Redis() { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
const client = await initRedisJSON() |
||||
|
||||
// 初始化RedisJSON池
|
||||
// 同类的
|
||||
for(let codeType of codeList){ |
||||
await client.json.set(codeType, '$', {}, {NX: true}); |
||||
} |
||||
// 特殊的
|
||||
await client.json.set('login', '$', {}, {NX: true}); |
||||
// 执行清理程序
|
||||
setInterval(async () => { |
||||
clearAllCode(client) |
||||
}, RedisJSON.clearTime) |
||||
|
||||
// 生成对外程序
|
||||
const codeMethods = {} |
||||
for(let codeType of codeList){ |
||||
const nowCodeType = codeType.replace(codeType[0],codeType.split("")[0].toUpperCase()) |
||||
codeMethods['set' + nowCodeType + 'Code'] = (emailOrUsername, code) => setCode(client, codeType, emailOrUsername, code) |
||||
codeMethods['get' + nowCodeType + 'Code'] = (emailOrUsername) => getCode(client, codeType, emailOrUsername) |
||||
codeMethods['del' + nowCodeType + 'Code'] = (emailOrUsername) => delCode(client, codeType, emailOrUsername) |
||||
// codeMethods['clear' + nowCodeType + 'Code'] = () => clearCode(client, codeType)
|
||||
} |
||||
const RJ = { |
||||
client, |
||||
// uuid和Token
|
||||
setLogin: (uuid, token) => setLogin(client, uuid, token), |
||||
getLogin: (uuid) => getLogin(client, uuid), |
||||
delLogin: (uuid, token) => delLogin(client, uuid, token), |
||||
getToken: (token) => getToken(client, token), |
||||
// 'signUp', 'signIn', 'reset' Code
|
||||
...codeMethods |
||||
} |
||||
res(RJ) |
||||
} catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$统一清理程序$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
function clearAllCode(client){ |
||||
return new Promise(async (res, rej) => { |
||||
// 清理signUpCOde
|
||||
try{ |
||||
await clearCode(client, 'signUp') |
||||
}catch (e) { |
||||
console.e('清理注册Code发生异常', e) |
||||
} |
||||
// 清理登录Code
|
||||
try{ |
||||
await clearCode(client, 'signIn') |
||||
}catch (e) { |
||||
console.e('清理登录Code发生异常', e) |
||||
} |
||||
|
||||
// 清理找回密码Code
|
||||
try{ |
||||
await clearCode(client, 'reset') |
||||
}catch (e) { |
||||
console.e('清理找回密码Code发生异常', e) |
||||
} |
||||
res() |
||||
|
||||
// 清理UUID
|
||||
try { |
||||
await clearLoginArr(client) |
||||
console.dev('清理UUID成功') |
||||
} catch (e) { |
||||
console.dev('清理UUID发生异常', e) |
||||
} |
||||
|
||||
}) |
||||
} |
||||
|
||||
// ===============================================UUID && Token========================================================
|
||||
// login,用于同账号的登陆数量,获取UUID下的TokenList
|
||||
function getLogin(client, uuid) { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
const result = await client.json.get('login', {path: uuid}) |
||||
res(result) |
||||
} catch (e) { |
||||
if (e.toString().indexOf('not exist') > -1) { |
||||
res([]) |
||||
} else { |
||||
rej(e) |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
// 添加UUID下的Token
|
||||
function setLogin(client, uuid, token) { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
const nowTime = new Date().getTime() |
||||
const result = await getLogin(client, uuid) |
||||
if(result == null){ |
||||
console.e('需要重启服务,Redis被清空,没有根节点。') |
||||
throw new Error('需要重启服务,Redis被清空,没有根节点。') |
||||
} |
||||
if (result.length == 0) { |
||||
await client.json.set('login', uuid, [{token, time: nowTime}]) |
||||
await setToken(client, token, uuid) |
||||
res() |
||||
} else if (result.length < maxClientOnline) { |
||||
await client.json.ARRAPPEND('login', uuid, {token, time: nowTime}) |
||||
await setToken(client, token, uuid) |
||||
res() |
||||
} else { |
||||
// 已经达到最大值了
|
||||
for (let i = result.length - 1; i >= 0; i--) { |
||||
if (nowTime - result[i].time > user.login.timeLimit) { |
||||
await delLoginArr(client, uuid, i, result[i].token) |
||||
} |
||||
} |
||||
const result2 = await getLogin(client, uuid) |
||||
if(result2.length == maxClientOnline){ |
||||
await delLoginArr(client, uuid, 0, result[0].token) |
||||
} |
||||
await client.json.ARRAPPEND('login', uuid, {token, time: nowTime}) |
||||
await setToken(client, token, uuid) |
||||
res() |
||||
} |
||||
} catch (e) { |
||||
console.log(e); |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
// 删除UUID List中的占用的Token
|
||||
function delLoginArr(client, uuid, index, token) { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
await client.json.ARRPOP('login', uuid, index) |
||||
await delToken(client, token) |
||||
res() |
||||
} catch (e) { |
||||
rej(e) |
||||
} |
||||
|
||||
}) |
||||
} |
||||
// 退出登录时删除UUID下的Token
|
||||
function delLogin(client, uuid, token){ |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
const result = await getLogin(client, uuid) |
||||
for(let i = result.length - 1; i >= 0 ; i--){ |
||||
if(result[i].token == token){ |
||||
await delLoginArr(client, uuid, i, token) |
||||
} |
||||
} |
||||
res() |
||||
}catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
// 清理过期的UUID下的Token
|
||||
function clearLoginArr(client) { |
||||
return new Promise(async (res, rej) => { |
||||
const result = await client.json.get('login') |
||||
const nowTime = new Date().getTime() |
||||
try { |
||||
for (let i in result) { |
||||
const list = result[i]; |
||||
for (let j = list.length - 1; j >= 0; j--) { |
||||
if (nowTime - list[j].time > user.login.timeLimit) { |
||||
await delLoginArr(client, i, j, list[j].token) |
||||
} |
||||
} |
||||
} |
||||
res() |
||||
} catch (e) { |
||||
rej(e) |
||||
} |
||||
|
||||
}) |
||||
} |
||||
// 获取Token的UUID
|
||||
function getToken(client, token) { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
const result = await client.get(token) |
||||
res(result) |
||||
} catch (e) { |
||||
if (e.toString().indexOf('not exist') > -1) { |
||||
res([]) |
||||
} else { |
||||
rej(e) |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
// 添加Token键值对,在添加UUID时,同步添加
|
||||
function setToken(client, token, uuid) { |
||||
return new Promise(async (res, rej) => { |
||||
try { |
||||
await client.set(token, uuid) |
||||
res() |
||||
}catch (e) { |
||||
rej() |
||||
} |
||||
}) |
||||
} |
||||
// 删除Token键值对,在删除UUID时,同步删除
|
||||
function delToken(client, token) { |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
const a = await client.del(token) |
||||
if(a != 1){ |
||||
rej('删除token指定的UUID失败') |
||||
console.e('删除token指定的UUID失败') |
||||
}else{ |
||||
console.dev('删除成功', a) |
||||
} |
||||
res() |
||||
}catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>UUID && Token>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
// 通用设置Code
|
||||
function setCode(client, type, emailOrUsername, code){ |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
console.log('设置登陆吗') |
||||
// 这里不能设置特殊符号
|
||||
emailOrUsername = HASH(emailOrUsername,'md5') |
||||
const time = new Date().getTime() |
||||
const a = await client.json.set(type, emailOrUsername, {code, time}, {NX:true}) |
||||
console.log(a) |
||||
res() |
||||
}catch (e) { |
||||
console.log(e) |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
function getCode(client, type, emailOrUsername){ |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
emailOrUsername = HASH(emailOrUsername,'md5') |
||||
const result = await client.json.get(type, {path: emailOrUsername}) |
||||
res(result) |
||||
}catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
function delCode(client, type, emailOrUsername){ |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
emailOrUsername = HASH(emailOrUsername,'md5') |
||||
const result = await client.json.del(type, emailOrUsername) |
||||
res(result) |
||||
}catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
function clearCode(client, type){ |
||||
return new Promise(async (res, rej) => { |
||||
try{ |
||||
const nowTime = new Date().getTime() |
||||
const result = await client.json.get(type) |
||||
for(let i in result){ |
||||
if(nowTime - result[i].time > RedisJSON.timeout){ |
||||
await delResetCode(client, type, i) |
||||
} |
||||
} |
||||
res() |
||||
}catch (e) { |
||||
rej(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
|
||||
module.exports = Redis |
@ -0,0 +1,136 @@ |
||||
/** |
||||
* 加密 |
||||
* */ |
||||
const crypto = require('crypto'); |
||||
|
||||
/** |
||||
* 配置文件 |
||||
* */ |
||||
const CFG = global.cfg ? global.cfg.encryption : require('../../config/default.config') |
||||
/** |
||||
* 加密算法 |
||||
* */ |
||||
|
||||
// 不可逆加密
|
||||
const MD5 = "md5" // 32位
|
||||
, SHA256 = 'sha256' // 64位
|
||||
, SHA512 = 'sha512' // 128位
|
||||
// 可逆加密
|
||||
const AES128 = 'aes-128-cbc' |
||||
, AES256 = 'aes-256-gcm' |
||||
|
||||
// vi
|
||||
// ase-128-cbc 加密算法要求key和iv长度都为16
|
||||
// const key = Buffer.from('9vApxLk5G3PAsJrM', 'utf8');
|
||||
// const iv = Buffer.from('FnJL7EDzjqWjcaY9', 'utf8');
|
||||
|
||||
// const key = crypto.randomBytes(32); // 256 位的共享密钥
|
||||
// const iv = crypto.randomBytes(16); // 初始向量,16 字节
|
||||
const key = Buffer.from(HASH(CFG.encryption.secretKey, MD5).slice(0, 16), 'utf8'); |
||||
const iv = Buffer.from(HASH(CFG.encryption.salt, MD5).slice(0, 16), 'utf8'); |
||||
|
||||
/** |
||||
* 不可逆加密 |
||||
* */ |
||||
function HASH(plaintext, algorithm = SHA512) { |
||||
const sha512 = crypto.createHash(algorithm) |
||||
const sha512Sum = sha512.update(plaintext + CFG.encryption.salt) |
||||
const ciphertext = sha512Sum.digest('hex') |
||||
return ciphertext |
||||
} |
||||
|
||||
/** |
||||
* 可逆加密 |
||||
* */ |
||||
// 加密
|
||||
|
||||
// 加密
|
||||
function encrypt(plaintext, algorithm = AES128) { |
||||
const cipher = crypto.createCipheriv(algorithm, key, iv); // 初始化加密算法
|
||||
let ciphertext = cipher.update(plaintext, 'utf8', 'hex'); |
||||
ciphertext += cipher.final('hex'); |
||||
// return {
|
||||
// ciphertext,
|
||||
// tag : cipher.getAuthTag()
|
||||
// };
|
||||
return ciphertext |
||||
} |
||||
|
||||
// 解密
|
||||
function decrypt(ciphertext, algorithm = AES128) { |
||||
let plaintext = ''; |
||||
const cipher = crypto.createDecipheriv(algorithm, key, iv); |
||||
plaintext += cipher.update(ciphertext, 'hex', 'utf8'); |
||||
plaintext += cipher.final('utf8'); |
||||
return plaintext; |
||||
} |
||||
|
||||
// const a = 'i love u!'
|
||||
// const b = encrypt(a)
|
||||
// const c = decrypt(b)
|
||||
// console.log(a, b, c);
|
||||
// aes128()
|
||||
// aes256()
|
||||
|
||||
function aes256(){ |
||||
'use strict'; |
||||
|
||||
const crypto = require('crypto'); |
||||
|
||||
// 初始化参数
|
||||
const text = 'Encryption Testing AES GCM mode'; // 要加密和解密的数据
|
||||
const key = crypto.randomBytes(32); // 256 位的共享密钥
|
||||
const iv = crypto.randomBytes(16); // 初始向量,16 字节
|
||||
const algorithm = 'aes-256-gcm'; // 加密算法和操作模式
|
||||
|
||||
// 加密
|
||||
const cipher = crypto.createCipheriv(algorithm, key, iv); // 初始化加密算法
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex'); |
||||
encrypted += cipher.final('hex'); |
||||
const tag = cipher.getAuthTag(); // 生成标签,用于验证密文的来源
|
||||
|
||||
// 解密
|
||||
const decipher = crypto.createDecipheriv(algorithm, key, iv); // 初始化解密算法
|
||||
decipher.setAuthTag(tag); // 传入验证标签,验证密文的来源
|
||||
let decrypted = decipher.update(encrypted, 'hex', 'utf8'); |
||||
decrypted += decipher.final('utf8'); |
||||
|
||||
console.log(decrypted); // Encryption Testing AES GCM mode
|
||||
|
||||
} |
||||
function aes128(){ |
||||
// 加密
|
||||
function genSign(src, key, iv) { |
||||
let sign = ''; |
||||
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); |
||||
sign += cipher.update(src, 'utf8', 'hex'); |
||||
sign += cipher.final('hex'); |
||||
return sign; |
||||
} |
||||
|
||||
// 解密
|
||||
function deSign(sign, key, iv) { |
||||
let src = ''; |
||||
const cipher = crypto.createDecipheriv('aes-128-cbc', key, iv); |
||||
src += cipher.update(sign, 'hex', 'utf8'); |
||||
src += cipher.final('utf8'); |
||||
return src; |
||||
} |
||||
|
||||
// ase-128-cbc 加密算法要求key和iv长度都为16
|
||||
const key = Buffer.from('9vApxLk5G3PAsJrM', 'utf8'); |
||||
const iv = Buffer.from('FnJL7EDzjqWjcaY9', 'utf8'); |
||||
const sign = genSign('hello world', key, iv); |
||||
console.log(sign); // 764a669609b0c9b041faeec0d572fd7a
|
||||
|
||||
|
||||
// 解密
|
||||
const src=deSign('764a669609b0c9b041faeec0d572fd7a', key, iv); |
||||
console.log(src); // hello world
|
||||
} |
||||
|
||||
module.exports = { |
||||
HASH, |
||||
encrypt, |
||||
decrypt |
||||
} |
@ -0,0 +1,3 @@ |
||||
/** |
||||
* 发送验证码 |
||||
* */ |
@ -0,0 +1,34 @@ |
||||
/** |
||||
* 生成和验证jwt |
||||
* */ |
||||
const jwt = require('jsonwebtoken'); |
||||
const { encrypt, decrypt } = require('./encryptionString') |
||||
|
||||
// authorization
|
||||
// Token.decrypt(ctx.header.authorization); // 获取其中的令牌
|
||||
|
||||
const CFG = global.cfg ? global.cfg.encryption : require('../../config/default.config') |
||||
|
||||
module.exports = { |
||||
// 加密
|
||||
encrypt: (token, time = CFG.user.login.timeLimit) => { |
||||
// 需要设置超时时间
|
||||
return encrypt(jwt.sign(token, CFG.encryption.secretKey, {expiresIn: time / 1000})) |
||||
}, |
||||
// 解密
|
||||
decrypt: (token) => { |
||||
try { |
||||
token = decrypt(token) |
||||
let data = jwt.verify(token, CFG.encryption.secretKey); |
||||
return { |
||||
token:true, |
||||
id:data |
||||
}; |
||||
} catch (e) { |
||||
return { |
||||
token:false, |
||||
data:e |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
/** |
||||
* 生成uuid |
||||
* */ |
||||
const { v4: uuidv4 } = require('uuid'); |
||||
// 需要去重- 然后转换成字符串
|
||||
|
||||
function makeUUID(){ |
||||
return uuidv4().split('-').join("") |
||||
} |
||||
|
||||
module.exports = makeUUID |
@ -0,0 +1,24 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,14 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="zh"> |
||||
<head> |
||||
<meta charset="UTF-8"/> |
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg"/> |
||||
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>--> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
||||
<title>sys22</title> |
||||
</head> |
||||
<body> |
||||
<div id="root"></div> |
||||
<script type="module" src="/src/main.jsx"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,25 @@ |
||||
{ |
||||
"name": "vision", |
||||
"private": true, |
||||
"version": "0.0.0", |
||||
"type": "module", |
||||
"scripts": { |
||||
"start": "vite", |
||||
"build": "vite build", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"animate.css": "^4.1.1", |
||||
"mobx": "^6.6.2", |
||||
"react": "^18.2.0", |
||||
"react-dom": "^18.2.0", |
||||
"react-router-dom": "^6.4.2" |
||||
}, |
||||
"devDependencies": { |
||||
"@types/react": "^18.0.17", |
||||
"@types/react-dom": "^18.0.6", |
||||
"@vitejs/plugin-react": "^2.1.0", |
||||
"sass": "^1.55.0", |
||||
"vite": "^3.1.0" |
||||
} |
||||
} |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,14 @@ |
||||
import {useState} from 'react' |
||||
import { useRoutes } from 'react-router-dom' |
||||
import indexRouterList from "@/router" |
||||
|
||||
|
||||
export default function App() { |
||||
const indexRoute = useRoutes(indexRouterList) |
||||
return ( |
||||
<div className="App"> |
||||
{indexRoute} |
||||
</div> |
||||
) |
||||
} |
||||
|
@ -0,0 +1,101 @@ |
||||
class Net { |
||||
constructor(option = {}) { |
||||
this.baseUrl = option.baseUrl ? option.baseUrl.slice(-1) != '/' ? option.baseUrl + '/' : option.baseUrl : '' |
||||
} |
||||
|
||||
get(url, data = null, option = null) { |
||||
return new Promise((res, rej) => { |
||||
let URL |
||||
if (url[0] != '/') { |
||||
URL = this.baseUrl + url |
||||
} else { |
||||
URL = this.baseUrl + url.slice(1) |
||||
} |
||||
data = data?.replace(/:/g, '=').replace(/,/g, '&').replace(/{/g, '?').replace(/}/g, '').replace(/"/g, '') |
||||
if(data){ |
||||
URL = URL + data |
||||
} |
||||
const headers = new Headers({ |
||||
'Content-Type': 'application/json' |
||||
}) |
||||
const token = window.localStorage.getItem('token') |
||||
if(token){ |
||||
headers.append('Authorization', token) |
||||
} |
||||
fetch(URL, { |
||||
method: 'GET', |
||||
headers, |
||||
}).then( |
||||
response => { |
||||
if (response.status >= 200 && response.status < 300) { |
||||
response.json().then( |
||||
json => { |
||||
res(json) |
||||
} |
||||
).catch( |
||||
e => rej(e) |
||||
) |
||||
} else { |
||||
rej(response.statusText) |
||||
} |
||||
} |
||||
).catch( |
||||
e => { |
||||
rej(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
post(url, data = null, option = null) { |
||||
return new Promise((res, rej) => { |
||||
let URL |
||||
if (url[0] != '/') { |
||||
URL = this.baseUrl + url |
||||
} else { |
||||
URL = this.baseUrl + url.slice(1) |
||||
} |
||||
const form = new FormData(); |
||||
for (let i in data) { |
||||
form.append(i, data[i]); |
||||
} |
||||
const headers = new Headers() |
||||
const token = window.localStorage.getItem('token') |
||||
if(token){ |
||||
headers.append('Authorization', token) |
||||
} |
||||
|
||||
fetch(URL, { |
||||
method: 'POST', |
||||
body: form, |
||||
headers |
||||
}).then( |
||||
response => { |
||||
if (response.status >= 200 && response.status < 300) { |
||||
response.json().then( |
||||
json => { |
||||
res(json) |
||||
} |
||||
).catch( |
||||
e => rej(e) |
||||
) |
||||
} else { |
||||
rej(response.statusText) |
||||
} |
||||
} |
||||
).catch( |
||||
e => { |
||||
rej(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
request(option) { |
||||
|
||||
} |
||||
} |
||||
|
||||
export const net = new Net({ |
||||
baseUrl: 'api/user' |
||||
}) |
@ -0,0 +1,111 @@ |
||||
:root { |
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; |
||||
font-size: 16px; |
||||
line-height: 24px; |
||||
font-weight: 400; |
||||
|
||||
color-scheme: light dark; |
||||
color: rgba(255, 255, 255, 0.87); |
||||
background-color: #242424; |
||||
|
||||
font-synthesis: none; |
||||
text-rendering: optimizeLegibility; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
-webkit-text-size-adjust: 100%; |
||||
|
||||
--animate-duration: 900ms; |
||||
--animate-delay: 0.9s; |
||||
} |
||||
|
||||
a { |
||||
font-weight: 500; |
||||
color: #646cff; |
||||
text-decoration: inherit; |
||||
} |
||||
a:hover { |
||||
color: #535bf2; |
||||
} |
||||
|
||||
html,body{ |
||||
height: 100% !important; |
||||
width: 100% !important; |
||||
overflow: hidden; |
||||
} |
||||
#root,.App{ |
||||
height: 100%; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 3.2em; |
||||
line-height: 1.1; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
color: #213547; |
||||
background-color: #ffffff; |
||||
} |
||||
a:hover { |
||||
color: #747bff; |
||||
} |
||||
button { |
||||
background-color: #f9f9f9; |
||||
} |
||||
} |
||||
#modalRoot{ |
||||
position: absolute; |
||||
height: 100%; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
top:0; |
||||
left: 0; |
||||
align-items: center; |
||||
justify-content: center; |
||||
display: none; |
||||
backdrop-filter: blur(50px); |
||||
animation: modalRootShow ease-in-out 500ms forwards; |
||||
} |
||||
@keyframes modalRootShow { |
||||
from{ |
||||
background: #33333300; |
||||
} |
||||
to{ |
||||
background: #66666633; |
||||
} |
||||
} |
||||
@keyframes modalRootHide { |
||||
from{ |
||||
background: #33333333; |
||||
} |
||||
to{ |
||||
background: #66666600; |
||||
} |
||||
} |
||||
|
||||
fieldset, img,input,button { border:none; padding:0;margin:0;outline-style:none; } |
After Width: | Height: | Size: 4.0 KiB |
@ -0,0 +1,53 @@ |
||||
import BPEMR from './index.module.scss' |
||||
import {useState, useEffect} from "react"; |
||||
|
||||
export default function Button(props) { |
||||
// console.log(props) |
||||
|
||||
const [size, setSize] = useState(props.size) |
||||
const [type, setType] = useState(props.type) |
||||
const [loading, setLoading] = useState(props.loading) |
||||
const [disable, setDisable] = useState(props.disable) |
||||
if (props.onClick && typeof props.onClick === 'function') { |
||||
|
||||
} else { |
||||
if (props.onClick) { |
||||
throw new Error('onClick必须位函数') |
||||
} |
||||
} |
||||
|
||||
useEffect(() => { |
||||
setSize(props.size ? props.size : 'default') |
||||
setType(props.type ? props.type : 'default') |
||||
setLoading(props.loading ? BPEMR.loading : '') |
||||
setDisable(props.disable ? BPEMR.disable : '') |
||||
}, [props.size, props.type, props.disable, props.loading, props.onClick]) |
||||
|
||||
const sizeOp = { |
||||
default: BPEMR.defaultsize, |
||||
undersize: BPEMR.undersize, |
||||
oversize: BPEMR.oversize, |
||||
} |
||||
const typeOp = { |
||||
default: BPEMR.defaultColor, |
||||
|
||||
error: BPEMR.eColor, |
||||
error2: BPEMR.e2Color, |
||||
error3: BPEMR.e3Color, |
||||
|
||||
success: BPEMR.sColor, |
||||
success2: BPEMR.s3Color, |
||||
success3: BPEMR.s2Color, |
||||
|
||||
warning: BPEMR.wColor, |
||||
warning1: BPEMR.w2Color, |
||||
warning3: BPEMR.w3Color, |
||||
} |
||||
const classname = [BPEMR.BPEMR_button, loading, disable, sizeOp[size], typeOp[type]].join(' ') |
||||
return ( |
||||
<div className={BPEMR.BPEMR_button_box}> |
||||
<div className={classname} style={props.style} title={props.disable ? '当前不可用!' : ''} onClick={!props.disable ? props.onClick : () => { |
||||
}}>{props.children}</div> |
||||
</div> |
||||
) |
||||
} |
@ -0,0 +1,162 @@ |
||||
.BPEMR_button_box{ |
||||
position: relative; |
||||
width: fit-content; |
||||
width: -webkit-fit-content; |
||||
width: -moz-fit-content; |
||||
padding: 5px 0; |
||||
} |
||||
.BPEMR_button { |
||||
position: relative; |
||||
width: fit-content; |
||||
width: -webkit-fit-content; |
||||
width: -moz-fit-content; |
||||
line-height: 2em; |
||||
padding: 0 1em; |
||||
margin: 0.5em 0; |
||||
user-select: none; |
||||
cursor: pointer; |
||||
border-radius: 5px; |
||||
transition: all ease-in-out 200ms; |
||||
color: #fefefe; |
||||
} |
||||
.BPEMR_button.loading{ |
||||
$jump : jump; |
||||
animation: jump .4s ease-in-out infinite alternate; |
||||
@keyframes jump { |
||||
0%{top:0px} |
||||
33%{top:-2px} |
||||
66%{top:-8px} |
||||
100%{top:-10px} |
||||
} |
||||
&{ |
||||
box-shadow: 0 5px 20px -10px #33333399; |
||||
} |
||||
} |
||||
.BPEMR_button.disable{ |
||||
cursor: no-drop; |
||||
&:hover{ |
||||
box-shadow: none !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.defaultsize { |
||||
font-size: 1rem; |
||||
|
||||
&:hover { |
||||
box-shadow: 0px 0 5px 0px #33333399; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.oversize { |
||||
font-size: 1.2rem; |
||||
|
||||
&:hover { |
||||
box-shadow: 0px 0 7px 0px #33333399; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.undersize { |
||||
font-size: 0.8rem; |
||||
|
||||
&:hover { |
||||
box-shadow: 0px 0 3px 1px #99999999; |
||||
} |
||||
} |
||||
|
||||
|
||||
.BPEMR_button.defaultColor { |
||||
color: #222831; |
||||
background: #fefefe; |
||||
border: 1px solid #bcbcbc; |
||||
|
||||
&:active { |
||||
background: #fff !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.eColor { |
||||
background: #E84545; |
||||
background: #ff4d4f; |
||||
border: 1px solid #ff4d4f; |
||||
|
||||
&:active { |
||||
background: #ff4d4fcc !important; |
||||
border: 1px solid #ff4d4fcc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.e2Color { |
||||
background: #F6416C; |
||||
border: 1px solid #F6416C; |
||||
&:active { |
||||
background: #F6416Ccc !important; |
||||
border: 1px solid #F6416Ccc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.e3Color { |
||||
background: #FF165D; |
||||
border: 1px solid #FF165D; |
||||
&:active { |
||||
background: #E84545ee !important; |
||||
border: 1px solid #E84545cc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.sColor { |
||||
background: #17B978; |
||||
border: 1px solid #17B978; |
||||
&:active { |
||||
background: #17B978cc !important; |
||||
border: 1px solid #17B978cc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.s2Color { |
||||
background: #3EC1D3; |
||||
border: 1px solid #3EC1D3; |
||||
&:active { |
||||
background: #3EC1D3cc !important; |
||||
border: 1px solid #3EC1D3cc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.s3Color { |
||||
background: #00ADB5; |
||||
border: 1px solid #00ADB5; |
||||
&:active { |
||||
background: #00ADB5cc !important; |
||||
border: 1px solid #00ADB5cc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.wColor { |
||||
background: #FF9A00; |
||||
border: 1px solid #FF9A00; |
||||
&:active { |
||||
background: #FF9A00cc !important; |
||||
border: 1px solid #FF9A00cc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.w2Color { |
||||
background: #FF6F3C; |
||||
border: 1px solid #FF6F3C; |
||||
&:active { |
||||
background: #FF6F3Ccc !important; |
||||
border: 1px solid #FF6F3Ccc !important; |
||||
} |
||||
} |
||||
|
||||
.BPEMR_button.w3Color { |
||||
background: #FFC93C; |
||||
border: 1px solid #FFC93C; |
||||
&:active { |
||||
background: #FFC93Ccc !important; |
||||
border: 1px solid #FFC93Ccc !important; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 817 B |
@ -0,0 +1,68 @@ |
||||
import React, { useEffect, useState } from "react"; |
||||
import { createPortal } from "react-dom"; |
||||
import BPEMR from './index.module.scss'; |
||||
import closeSvg from './close.svg' |
||||
|
||||
export default function Dialog(props){ |
||||
const modalRoot = document.body.querySelector('#modalRoot') |
||||
let node = window.document.createElement("div"); |
||||
if(modalRoot){ |
||||
node = modalRoot; |
||||
}else{ |
||||
node.id='modalRoot' |
||||
window.document.body.appendChild(node) |
||||
} |
||||
|
||||
const [state, setState] = useState(props.state) |
||||
if(typeof props.width === 'number' || Number(props.width) != 'NaN'){ |
||||
|
||||
}else{ |
||||
throw new Error('width必须为数字') |
||||
} |
||||
const [width, setWidth] = useState(Number(props.width)) |
||||
|
||||
const [temCloseAnimation, settemCloseAnimation] = useState(BPEMR.temModalShow) |
||||
|
||||
useEffect(() => { |
||||
setState(props.state) |
||||
setWidth(props.width) |
||||
if(state){ |
||||
modalRoot.style.animation = '' |
||||
modalRoot.style.display = 'flex' |
||||
}else{ |
||||
modalRoot.style.display = 'none' |
||||
settemCloseAnimation(BPEMR.temModalShow) |
||||
} |
||||
}) |
||||
|
||||
useEffect(() => { |
||||
modalRoot.style.animation = 'modalRootHide ease-in-out 500ms forwards' |
||||
},[temCloseAnimation]) |
||||
|
||||
|
||||
|
||||
if(!props.close){ |
||||
throw new Error('缺少弹窗关闭参数。') |
||||
}else if(typeof props.close != 'function'){ |
||||
throw new Error('弹窗必须返回关闭程序。') |
||||
} |
||||
|
||||
|
||||
|
||||
function close(){ |
||||
settemCloseAnimation(BPEMR.temModalHide) |
||||
setTimeout(() => { |
||||
props.close(false) |
||||
}, 500) |
||||
} |
||||
|
||||
const className = [BPEMR.BPEMR_Dialog_Tem, temCloseAnimation].join(' ') |
||||
|
||||
return createPortal( |
||||
<div className={className} style={{width:width + 'px', height:width * 0.618 + 'px'}}> |
||||
<div className={BPEMR.close} onClick={close}><img src={closeSvg} alt=""/></div> |
||||
<div>{props.children}</div> |
||||
</div>, |
||||
node |
||||
); |
||||
} |
@ -0,0 +1,64 @@ |
||||
|
||||
$modalShow:modalShow; |
||||
$modalHide:modalHide; |
||||
|
||||
.BPEMR_Dialog_Tem{ |
||||
position:relative; |
||||
background: #fefefe; |
||||
min-width: 10em !important; |
||||
max-width: 90vw !important; |
||||
border-radius: 5px; |
||||
padding: 1.5em; |
||||
box-sizing: border-box; |
||||
overflow: overlay; |
||||
& > .close{ |
||||
position: absolute; |
||||
top:0.5em; |
||||
right: 0.5em; |
||||
width: 1.5em; |
||||
height: 1.5em; |
||||
border-radius: 1.5em; |
||||
cursor: pointer; |
||||
box-shadow: 0px 1px 10px -1px #33333333; |
||||
transition: box-shadow ease-in-out 300ms; |
||||
& > img { |
||||
height: 1.5em; |
||||
} |
||||
&:hover{ |
||||
box-shadow: 1px 1px 10px -3px #333; |
||||
} |
||||
} |
||||
} |
||||
.temModalShow{ |
||||
animation: modalShow ease-in-out 300ms forwards; |
||||
} |
||||
.temModalHide{ |
||||
animation: modalHide ease-in-out 300ms forwards; |
||||
} |
||||
|
||||
@keyframes modalShow { |
||||
0%{ |
||||
transform: scale(0.3); |
||||
box-shadow: 5px 10px 30px -10px #33333300; |
||||
} |
||||
80%{ |
||||
transform: scale(1.1); |
||||
} |
||||
100%{ |
||||
transform: scale(1); |
||||
box-shadow: 5px 10px 30px -10px #333333aa; |
||||
} |
||||
} |
||||
@keyframes modalHide { |
||||
0%{ |
||||
transform: scale(1); |
||||
box-shadow: 5px 10px 30px -10px #333333aa; |
||||
} |
||||
20%{ |
||||
transform: scale(1.1); |
||||
} |
||||
100%{ |
||||
transform: scale(0); |
||||
box-shadow: 5px 10px 30px -10px #33333300; |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
import {useNavigate, useLocation} from "react-router-dom"; |
||||
import {useEffect} from "react"; |
||||
|
||||
export default function RouterAuth(props){ |
||||
const navigate = useNavigate(); |
||||
const location = useLocation(); |
||||
useEffect(() => { |
||||
const path = window.location.pathname |
||||
// console.log(location) |
||||
if(path == '/homes'){ |
||||
navigate('/home') |
||||
} |
||||
}) |
||||
return props.children |
||||
} |
@ -0,0 +1,5 @@ |
||||
import icon from '../assets/react.svg' |
||||
export const config = { |
||||
projectName:'sys_22', |
||||
icon, |
||||
} |
@ -0,0 +1,38 @@ |
||||
import React from 'react' |
||||
import ReactDOM from 'react-dom/client' |
||||
import {BrowserRouter} from "react-router-dom"; |
||||
import App from './App' |
||||
import '@/assets/index.css' |
||||
import RouterAuth from "./components/RouterAuth"; |
||||
|
||||
import {config} from "./config/sys22"; |
||||
|
||||
// console.log(config) |
||||
|
||||
document.title = config.projectName |
||||
changeFavicon(config.icon) |
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render( |
||||
<React.StrictMode> |
||||
<BrowserRouter> |
||||
<RouterAuth> |
||||
<App/> |
||||
</RouterAuth> |
||||
</BrowserRouter> |
||||
</React.StrictMode> |
||||
) |
||||
|
||||
function changeFavicon(link){ |
||||
let $favicon = document.querySelector('link[rel="icon"]'); |
||||
// If a <link rel="icon"> element already exists, |
||||
// change its href to the given link. |
||||
if ($favicon !== null) { |
||||
$favicon.href = link; |
||||
// Otherwise, create a new element and append it to <head>. |
||||
} else { |
||||
$favicon = document.createElement("link"); |
||||
$favicon.rel = "icon"; |
||||
$favicon.href = link; |
||||
document.head.appendChild($favicon); |
||||
} |
||||
}; |
@ -0,0 +1,37 @@ |
||||
import { Navigate } from 'react-router-dom' |
||||
|
||||
import Index from '@/view/Index' |
||||
import SignIn from '@/view/SignIn' |
||||
import SignUp from '@/view/SignUp' |
||||
import Home from '@/view/Home' |
||||
import Setting from '@/view/Home/Setting' |
||||
|
||||
export default [ |
||||
// 路由表 |
||||
{ |
||||
path:'/', |
||||
element: <Index /> |
||||
}, |
||||
{ |
||||
path:'/signIn', |
||||
element: <SignIn /> |
||||
}, |
||||
{ |
||||
path:'/signUp', |
||||
element: <SignUp /> |
||||
}, |
||||
{ |
||||
path: '/home', |
||||
element: <Home />, |
||||
children:[ |
||||
{ |
||||
path:'setting', |
||||
element: <Setting /> |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
path: "", |
||||
element: <Navigate to="home" replace /> |
||||
} |
||||
] |
@ -0,0 +1,5 @@ |
||||
import UserStore from './userStore' |
||||
import NetStore from "./netStore"; |
||||
|
||||
export const userStore = new UserStore() |
||||
export const netStore = new NetStore() |
@ -0,0 +1,124 @@ |
||||
import {observable, action, computed, makeObservable} from "mobx"; |
||||
import {config} from "@/config/sys22"; |
||||
import {userStore} from "./index"; |
||||
|
||||
import {net} from "@/api"; |
||||
|
||||
|
||||
class NetStore { |
||||
// 系统配置
|
||||
// config = config;
|
||||
|
||||
constructor() { |
||||
// mobx6 和以前版本这是最大的区别
|
||||
makeObservable(this, { |
||||
// config: observable,
|
||||
// name: observable,
|
||||
// sex: observable,
|
||||
// userObj: observable,
|
||||
// setName: action,
|
||||
// titleName: computed
|
||||
}); |
||||
|
||||
} |
||||
|
||||
// 用户名查重
|
||||
checkOnly(data) { |
||||
return new Promise(res => { |
||||
net.get('/will/checkOnly', data).then( |
||||
response => { |
||||
console.log(response) |
||||
res(response.status) |
||||
}, |
||||
e => { |
||||
console.log(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
// 登录
|
||||
signIn(data) { |
||||
return new Promise(res => { |
||||
net.post('/will/signIn', data).then( |
||||
response => { |
||||
console.log(response) |
||||
res(response.status) |
||||
if (response.status) { |
||||
userStore.setToken(response.data.token) |
||||
} |
||||
}, |
||||
e => { |
||||
console.log(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
// 发送验证码
|
||||
sendCode(data, type = 'signIn') { |
||||
return new Promise(res => { |
||||
net.get('/will/sendCode', { |
||||
...data, |
||||
type |
||||
}).then( |
||||
response => { |
||||
console.log(response) |
||||
res(response.status) |
||||
}, |
||||
e => { |
||||
console.log(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
// 注册
|
||||
signUp(data){ |
||||
return new Promise(res => { |
||||
net.post('/will/signUp', data).then( |
||||
response => { |
||||
console.log(response) |
||||
res(response.status) |
||||
if (response.status) { |
||||
userStore.setToken(response.data.token) |
||||
} |
||||
}, |
||||
e => { |
||||
console.log(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
// 获取用户信息
|
||||
getUserInfo(){ |
||||
return new Promise(res => { |
||||
net.get('/signed/getuserInfo').then( |
||||
response => { |
||||
res(response.data) |
||||
}, |
||||
e => { |
||||
console.log(e) |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
// setName(v) {
|
||||
// console.log('触发action');
|
||||
// this.name = v;
|
||||
// }
|
||||
// setUserObj(obj) {
|
||||
// this.userObj = obj;
|
||||
// }
|
||||
//
|
||||
// get titleName(){
|
||||
// return this.name+'___111';
|
||||
// }
|
||||
// get userObject() {
|
||||
// return this.userObj;
|
||||
// }
|
||||
} |
||||
|
||||
export default NetStore |
@ -0,0 +1,123 @@ |
||||
import { observable, action, computed, makeObservable} from "mobx"; |
||||
import {config} from "@/config/sys22"; |
||||
import {netStore} from "./index"; |
||||
|
||||
|
||||
class UserStore { |
||||
// 系统配置
|
||||
config = config; |
||||
// Token
|
||||
token = null; |
||||
// 登陆时的注册名称
|
||||
signInUsername = null; |
||||
// home页跳转状态
|
||||
homePageGotoStatus = true; |
||||
// 用户信息
|
||||
userInfo = null; |
||||
|
||||
|
||||
|
||||
name = 'kangkang000'; |
||||
sex = '男'; |
||||
userObj = { |
||||
name: 'kangkang000', |
||||
age: 23, |
||||
token: '12345689' |
||||
} |
||||
|
||||
constructor() { |
||||
// mobx6 和以前版本这是最大的区别
|
||||
makeObservable(this, { |
||||
config: observable, |
||||
token:observable, |
||||
signInUsername:observable, |
||||
homePageGotoStatus:observable, |
||||
userInfo:observable, |
||||
|
||||
name: observable, |
||||
sex: observable, |
||||
userObj: observable, |
||||
setName: action, |
||||
titleName: computed |
||||
}); |
||||
|
||||
} |
||||
|
||||
// 设置Token
|
||||
setToken(token){ |
||||
window.localStorage.setItem('token',token) |
||||
this.token = token |
||||
} |
||||
// 获取Token
|
||||
get token(){ |
||||
if(!this.token){ |
||||
this.token = window.localStorage.getItem('token') |
||||
return this.token |
||||
}else{ |
||||
return this.token |
||||
} |
||||
} |
||||
|
||||
// 设置SignInUsername
|
||||
setSignInUsername(username){ |
||||
this.signInUsername = username |
||||
} |
||||
get signInUsername(){ |
||||
return this.signInUsername |
||||
} |
||||
|
||||
// 设置home页跳转状态
|
||||
setHomePageGotoStatus(state){ |
||||
this.homePageGotoStatus = state |
||||
} |
||||
get homePageGotoStatus(){ |
||||
return this.homePageGotoStatus |
||||
} |
||||
|
||||
// 获取用户信息
|
||||
getUserInfo(){ |
||||
return new Promise(async res => { |
||||
this.userInfo = window.localStorage.getItem('userInfo') |
||||
if(!this.userInfo){ |
||||
res(await this.setUserInfo()) |
||||
}else{ |
||||
res(JSON.parse(this.userInfo)) |
||||
} |
||||
|
||||
}) |
||||
} |
||||
// 强制刷新UserInfo
|
||||
setUserInfo(){ |
||||
return new Promise(async res => { |
||||
netStore.getUserInfo().then( |
||||
resp => { |
||||
window.localStorage.setItem('userInfo',JSON.stringify(resp)) |
||||
console.log(resp) |
||||
res(resp) |
||||
}, |
||||
e => { |
||||
console.log(e); |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
|
||||
|
||||
|
||||
setName(v) { |
||||
console.log('触发action'); |
||||
this.name = v; |
||||
} |
||||
setUserObj(obj) { |
||||
this.userObj = obj; |
||||
} |
||||
|
||||
get titleName(){ |
||||
return this.name+'___111'; |
||||
} |
||||
get userObject() { |
||||
return this.userObj; |
||||
} |
||||
} |
||||
|
||||
export default UserStore |
@ -0,0 +1,38 @@ |
||||
import {userStore, netStore} from "@/store"; |
||||
import {useEffect, useState} from "react"; |
||||
import css from './index.module.scss'; |
||||
import star from './start.svg' |
||||
|
||||
|
||||
|
||||
export default function Setting(){ |
||||
const [userInfo, setUserInfo] = useState(null) |
||||
useEffect(() => { |
||||
async function fetchData() { |
||||
setUserInfo(await userStore.getUserInfo()) |
||||
} |
||||
fetchData() |
||||
},[0]) |
||||
|
||||
return <div className={css.Setting}> |
||||
{/*头部*/} |
||||
{/*<div className={css.header}></div>*/} |
||||
{/*身子*/} |
||||
<div className={css.body}> |
||||
{/*左侧菜单*/} |
||||
<div className={css.menu}> |
||||
<div className={css.myself}> |
||||
<div className={css.avatar}> |
||||
<img src={userInfo?.userInfoOther.avatar.includes('img') ? userInfo?.userInfoOther.avatar : star} alt=""/> |
||||
</div> |
||||
<div className={css.nickname}>{userInfo?.userInfoOther.nickname}</div> |
||||
<div className={css.slogan}>{userInfo?.userInfoOther.slogan}</div> |
||||
</div> |
||||
<div></div> |
||||
<div></div> |
||||
</div> |
||||
{/*右侧主体*/} |
||||
<div className={css.main}>body</div> |
||||
</div> |
||||
</div> |
||||
} |
@ -0,0 +1,77 @@ |
||||
.Setting{ |
||||
position: relative; |
||||
width: 100%; |
||||
height: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
.header{ |
||||
position: relative; |
||||
flex-shrink: 0; |
||||
height: 40px; |
||||
background: #1a1a1a; |
||||
margin-bottom: 1rem; |
||||
border-radius: 2rem; |
||||
} |
||||
.body{ |
||||
position: relative; |
||||
flex: 1; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
//background: #1a1a1a; |
||||
display: flex; |
||||
.menu{ |
||||
position: relative; |
||||
width: 20rem; |
||||
background: #fefefe; |
||||
border-radius: 0.5rem; |
||||
box-sizing: border-box; |
||||
padding: 1rem; |
||||
& > div{ |
||||
position: relative; |
||||
padding: 1rem; |
||||
background: #fefefeee; |
||||
margin: 1rem 0; |
||||
border-radius: 0.5rem; |
||||
} |
||||
& > div.myself{ |
||||
margin-top: 0px; |
||||
& > div.avatar{ |
||||
position: relative; |
||||
margin-bottom: 1rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 1rem 0; |
||||
& > img{ |
||||
width: 5rem; |
||||
} |
||||
} |
||||
& > div.nickname{ |
||||
position: relative; |
||||
line-height: 3rem; |
||||
font-size: 1.1rem; |
||||
text-align: center; |
||||
} |
||||
& > div.slogan{ |
||||
position: relative; |
||||
max-width: 100%; |
||||
width: 100%; |
||||
|
||||
overflow:hidden; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
-o-text-overflow:ellipsis; |
||||
text-align: center; |
||||
} |
||||
} |
||||
} |
||||
.main{ |
||||
position: relative; |
||||
border-radius: 0.5rem; |
||||
background: #1a1a1a; |
||||
margin-left: 1rem; |
||||
flex: 1; |
||||
padding: 1rem; |
||||
} |
||||
} |
||||
} |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 5.0 MiB |
@ -0,0 +1,79 @@ |
||||
import {Outlet, useNavigate} from 'react-router-dom' |
||||
import {userStore, netStore} from "@/store"; |
||||
import css from './index.module.scss' |
||||
import {useEffect, useState} from "react"; |
||||
import start from './start.svg'; |
||||
import setting from './setting.svg'; |
||||
import rocket from './rocket.svg' |
||||
import bg0 from './bg0.jpg' |
||||
export default function Home(){ |
||||
const navigate = useNavigate() |
||||
useEffect(() => { |
||||
// home页动画状态 |
||||
userStore.setHomePageGotoStatus(false) |
||||
setTimeout(() => { |
||||
userStore.setHomePageGotoStatus(true) |
||||
setMainStyle([css.main].join(' ')) |
||||
}, 900) |
||||
// 获取userInfo |
||||
},[0]) |
||||
|
||||
// 跳转 |
||||
const [oldPath, setOldPath] = useState('') |
||||
function goto(path){ |
||||
if(oldPath == path)return; |
||||
// 记录上次跳转位置,防止重复 |
||||
setOldPath(path) |
||||
// 修改可用状态 |
||||
userStore.setHomePageGotoStatus(false) |
||||
// 展开动画 |
||||
setMainStyle([css.temPageshow, css.main].join(' ')) |
||||
// 跳转 |
||||
navigate(path) |
||||
setTimeout(() => { |
||||
userStore.setHomePageGotoStatus(true) |
||||
setMainStyle(css.main) |
||||
}, 900) |
||||
} |
||||
// 全页 |
||||
const [homeStyle,setHomeStyle] = useState(['animate__fadeIn', 'animate__animated', 'animate__faster'].join(' ')) |
||||
// 主体 |
||||
const [mainStyle, setMainStyle] = useState([css.temPageshow, css.main].join(' ')) |
||||
|
||||
|
||||
// 菜单 |
||||
const [menuStyle, setMenuStyle] = useState([css.temUpShow, css.menu].join(' ')) |
||||
return( |
||||
<div className={css.Home}> |
||||
<div className={homeStyle}> |
||||
{/*主体*/} |
||||
<div className={mainStyle}><div><Outlet/></div></div> |
||||
{/*菜单*/} |
||||
<div className={menuStyle}> |
||||
<div className={css.menuContainer}> |
||||
<div className={css.start}> |
||||
<div onClick={() =>userStore.homePageGotoStatus ? goto('/home') : ''} className={css.startIco}><img src={start} alt=""/></div> |
||||
</div> |
||||
<div className={css.kong}></div> |
||||
<div className={css.touchBar}> |
||||
{/* 个人设置 */} |
||||
<div className={css.cmdBox}> |
||||
<div>设置</div> |
||||
<div onClick={() => userStore.homePageGotoStatus ? goto('setting') : ''}><img src={setting} alt=""/></div> |
||||
<div></div> |
||||
</div> |
||||
{/* 用户系统 */} |
||||
<div className={css.cmdBox}> |
||||
<div>系统</div> |
||||
<div><img style={{transform: 'scale(1.1)'}} src={rocket} alt=""/></div> |
||||
<div></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{/*路由堆放点*/} |
||||
{/*<Outlet/>*/} |
||||
</div> |
||||
) |
||||
} |
@ -0,0 +1,245 @@ |
||||
.Home { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
//background-image: linear-gradient(to bottom, transparent 75%, #ffffff 100%), radial-gradient(at 0% 0%, hsla(154, 4%, 50%, 0.25) 0, transparent 50%), radial-gradient(at 0% 50%, hsla(120, 5%, 50%, 0.23) 0, transparent 50%), radial-gradient(at 40% 40%, hsla(120, 4%, 50%, 0.21) 0, transparent 50%), radial-gradient(at 80% 0%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), linear-gradient(to bottom, transparent 75%, #ffffff 100%); |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
user-select: none; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
|
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
overflow: hidden; |
||||
|
||||
background-image: url("bg0.jpg"); |
||||
background-position: center; |
||||
background-size: cover; |
||||
background-repeat: no-repeat; |
||||
|
||||
& > div.main { |
||||
position: relative; |
||||
flex: 1; |
||||
display: flex; |
||||
flex-direction: column; |
||||
padding: 1rem; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
border-radius: 1rem; |
||||
padding: 1rem; |
||||
box-sizing: border-box; |
||||
background-color: #cccccc11; |
||||
backdrop-filter: blur(20px); |
||||
box-shadow: 0 0 10px 3px #33333366; |
||||
|
||||
& > div { |
||||
overflow: hidden; |
||||
} |
||||
} |
||||
} |
||||
|
||||
& > div.menu { |
||||
position: relative; |
||||
flex-shrink: 0; |
||||
height: 5rem; |
||||
width: 100%; |
||||
|
||||
& > div.menuContainer { |
||||
position: relative; |
||||
margin: 0 auto; |
||||
width: fit-content; |
||||
width: -webkit-fit-content; |
||||
width: -moz-fit-content; |
||||
height: 100%; |
||||
border-radius: 1rem 1rem 0 0; |
||||
box-shadow: -1px -1px 10px 1px #66666699; |
||||
background: #ddddddaa; |
||||
backdrop-filter: blur(20px); |
||||
padding: 0 2rem; |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
& > div.start { |
||||
position: relative; |
||||
height: 100%; |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
& > div.startIco { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 5rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
& > img { |
||||
height: 4.5rem; |
||||
cursor: pointer; |
||||
transition: height ease-in-out 300ms; |
||||
background: #00000000; |
||||
|
||||
&:hover { |
||||
height: 5rem; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
& > div.kong { |
||||
position: relative; |
||||
width: 3px; |
||||
height: 3rem; |
||||
margin: 0 1rem; |
||||
border-radius: 1rem; |
||||
background: #fff; |
||||
} |
||||
|
||||
& > div.touchBar { |
||||
position: relative; |
||||
height: 100%; |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
& > div.cmdBox { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 4rem; |
||||
|
||||
&:hover div:nth-child(1) { |
||||
//display: block; |
||||
opacity: 1; |
||||
top: -2.6rem; |
||||
} |
||||
|
||||
&:hover div:nth-child(3) { |
||||
width: 1rem; |
||||
opacity: 1; |
||||
box-shadow: 0px 3px 10px 2px #000; |
||||
} |
||||
|
||||
& > div:nth-child(1) { |
||||
position: absolute; |
||||
//display: none; |
||||
width: 100%; |
||||
top: -10px; |
||||
left: 0; |
||||
opacity: 0; |
||||
border-radius: 5px; |
||||
background: #FF9A00aa; |
||||
width: 4rem; |
||||
line-height: 1.6rem; |
||||
text-align: center; |
||||
margin: 0 auto; |
||||
right: 0; |
||||
font-size: 0.9rem; |
||||
transition: all ease-in-out 300ms; |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 4rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
cursor: pointer; |
||||
|
||||
&:hover img { |
||||
width: 2.5rem; |
||||
} |
||||
|
||||
|
||||
& > img { |
||||
width: 2rem; |
||||
transition: width ease-in-out 300ms; |
||||
} |
||||
|
||||
} |
||||
|
||||
& > div:nth-child(3) { |
||||
position: absolute; |
||||
//background: #666; |
||||
opacity: 1; |
||||
width: 0; |
||||
height: 0px; |
||||
margin: 0 auto; |
||||
border-radius: 1rem; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 14px; |
||||
transition: all ease-in-out 300ms; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
$upShow: upShow; |
||||
$downHide: downHide; |
||||
@keyframes upShow { |
||||
from { |
||||
top: 100%; |
||||
} |
||||
to { |
||||
top: 0; |
||||
} |
||||
} |
||||
.temUpShow{ |
||||
animation: upShow ease-in-out 400ms forwards; |
||||
} |
||||
.temDownHide{ |
||||
|
||||
} |
||||
$pageshow: pageshow; |
||||
$pagehide: pagehide; |
||||
@keyframes pageshow { |
||||
0% { |
||||
transform: scale(0); |
||||
} |
||||
60% { |
||||
transform: scale(1.01); |
||||
} |
||||
75% { |
||||
transform: scale(0.98); |
||||
} |
||||
90% { |
||||
transform: scale(1.005); |
||||
} |
||||
100% { |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
@keyframes pagehide { |
||||
0% { |
||||
transform: scale(1); |
||||
} |
||||
30% { |
||||
transform: scale(1.1); |
||||
} |
||||
100% { |
||||
transform: scale(0); |
||||
display: none !important; |
||||
} |
||||
} |
||||
.temPageshow { |
||||
display: flex !important; |
||||
animation: pageshow ease-in-out 600ms forwards; |
||||
} |
||||
|
||||
.temPagehide { |
||||
animation: pagehide ease-in-out 600ms forwards; |
||||
} |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 233 KiB |
@ -0,0 +1,23 @@ |
||||
import css from './index.module.scss' |
||||
import background from './background.jpg' |
||||
import { useNavigate } from "react-router-dom"; |
||||
import 'animate.css' |
||||
import {useState} from "react"; |
||||
|
||||
|
||||
|
||||
export default function Index(){ |
||||
const navigate = useNavigate(); |
||||
const [goOutAnimate, setGoOutAnimate] = useState('animate__fadeInDown') |
||||
function go(){ |
||||
setGoOutAnimate('animate__fadeOutUp'); |
||||
setTimeout(() => { |
||||
navigate('/signIn') |
||||
},900) |
||||
} |
||||
return (<div className={[css.Index, 'animate__animated', goOutAnimate].join(' ')} onClick={go} > |
||||
<div> |
||||
<div className={css.title}>幽意</div> |
||||
</div> |
||||
</div>) |
||||
} |
@ -0,0 +1,28 @@ |
||||
.Index{ |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
background-image: url("./background.jpg"); |
||||
background-position: center 100%; |
||||
background-repeat: no-repeat; |
||||
background-size:cover; |
||||
-moz-user-select:none; |
||||
-webkit-user-select:none; |
||||
user-select:none; |
||||
cursor: pointer; |
||||
& > div{ |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
backdrop-filter: blur(15px); |
||||
background: #33333333; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
& >div.title{ |
||||
font-size: 2.5rem; |
||||
color:#fefefe; |
||||
letter-spacing: 20px; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,246 @@ |
||||
import css from './index.module.scss' |
||||
import {userStore, netStore} from "@/store"; |
||||
import {useEffect, useRef, useState} from "react"; |
||||
import next from './next.svg'; |
||||
import Button from "@/components/Button"; |
||||
import {useNavigate} from "react-router-dom"; |
||||
|
||||
|
||||
export default function Login(){ |
||||
// 用户信息 |
||||
const [userInfo, setUserInfo] = useState('') |
||||
// 用户输入长度确认 |
||||
const [userInfoState, setUserInfoState] = useState('') |
||||
// 登录容器样式 |
||||
const [loginStyle, setLoginStyle] = useState(['animate__zoomIn', 'animate__animated', 'animate__faster'].join(' ')) |
||||
// 当前页 |
||||
const [pageNum, setPageNum] = useState(1) |
||||
// 第一屏样式 |
||||
const [page1Style, setPage1Style] = useState(css.page1) |
||||
// 第二屏样式 |
||||
const [page2Style, setPage2Style] = useState(css.page2) |
||||
// 登录或者注册 |
||||
const [signInOrOut, setSignInOrOut] = useState(true) |
||||
// 请求用户信息存在的状态 |
||||
const [checkUserInfoState, setCheckUserInfoState] = useState(true) |
||||
// 登陆方式 |
||||
const [loginMethod, setLoginMethod] = useState('') |
||||
// 密码 |
||||
const [password, setPassword] = useState('') |
||||
// 验证码 |
||||
const [verfCode, setVerfCode] = useState('') |
||||
// 发送验证码按钮状态 |
||||
const [sendBut, setSendBut] = useState(false) |
||||
// 路由跳转 |
||||
const navigate = useNavigate(); |
||||
|
||||
|
||||
// input |
||||
const input1 = useRef() |
||||
const input2 = useRef() |
||||
const input3 = useRef() |
||||
|
||||
|
||||
useEffect(() => { |
||||
if(pageNum == 1){ |
||||
input1.current.focus() |
||||
} |
||||
if(!signInOrOut){ |
||||
setTimeout(() => { |
||||
if(loginMethod == ''){ |
||||
input2.current.focus() |
||||
}else{ |
||||
input3.current.focus() |
||||
} |
||||
},800) |
||||
} |
||||
}, [pageNum,signInOrOut]) |
||||
|
||||
// 用户信息输入 |
||||
const userInfoInput = event => { |
||||
if(event.target.value.length >= 8){ |
||||
setUserInfoState(css.temNextShow) |
||||
}else if(event.target.value.length < 8){ |
||||
if(userInfo.length >= 8){ |
||||
setUserInfoState(css.temNextHide) |
||||
}else{ |
||||
setUserInfoState('') |
||||
} |
||||
} |
||||
setUserInfo(event.target.value) |
||||
} |
||||
const handleUserInfoKeyDown = event => { |
||||
// console.log(event) |
||||
switch (event.code){ |
||||
case 'Enter': |
||||
checkUserInfo() |
||||
break; |
||||
case 'Tab': |
||||
checkUserInfo() |
||||
event.preventDefault() |
||||
break; |
||||
} |
||||
} |
||||
// 密码输入 |
||||
const passwordInput = event => { |
||||
setPassword(event.target.value) |
||||
} |
||||
const handlePasswordKeyDown = event => { |
||||
if(event.code == 'Enter'){ |
||||
signIn() |
||||
} |
||||
} |
||||
// 验证码输入 |
||||
const verfCodeInput = event => { |
||||
setVerfCode(event.target.value) |
||||
} |
||||
|
||||
// 点击下一步,检验用户信息是否存在 |
||||
async function checkUserInfo(){ |
||||
if(!userInfoState && checkUserInfoState){ |
||||
return |
||||
} |
||||
setCheckUserInfoState(false) |
||||
const state = await netStore.checkOnly({username: userInfo}) |
||||
setPage1Style([css.page1, css.temSlideOutLeft].join(' ')) |
||||
setPage2Style([css.page2, css.temSlideOutLeft].join(' ')) |
||||
setSignInOrOut(state) |
||||
|
||||
|
||||
} |
||||
// 返回按钮的状态控制,300ms内不允许点击 |
||||
let goBackPage1State = true |
||||
// 返回第一页 |
||||
function goBackPage1(){ |
||||
if(goBackPage1State){ |
||||
goBackPage1State = false |
||||
setTimeout(() => { |
||||
goBackPage1State = true |
||||
},300) |
||||
}else{ |
||||
return |
||||
} |
||||
setSignInOrOut(true) |
||||
setCheckUserInfoState(true) |
||||
setPage1Style([css.page1, css.temSlideInLeft].join(' ')) |
||||
setPage2Style([css.page2, css.temSlideInLeft].join(' ')) |
||||
} |
||||
// 切换登陆方式 |
||||
function changeLoginMethod(){ |
||||
loginMethod == '' ? setLoginMethod(css.left) : setLoginMethod('') |
||||
loginMethod != '' ? input2.current.focus() : input3.current.focus() |
||||
} |
||||
// 发送验证码 |
||||
async function sendEmail(){ |
||||
setSendBut(true) |
||||
let status = 60 |
||||
const sendEmailInterval = setInterval(() => { |
||||
status-- |
||||
if(status == 0){ |
||||
setSendBut(false) |
||||
clearInterval(sendEmailInterval) |
||||
} |
||||
},1000) |
||||
const state = await netStore.sendCode({username:userInfo}) |
||||
console.log(state) |
||||
} |
||||
// 登录 |
||||
let signInState = true |
||||
async function signIn(){ |
||||
console.log('点击登录') |
||||
if(!signInState){ |
||||
return |
||||
} |
||||
setTimeout(() => { |
||||
signInState = true |
||||
},3000) |
||||
const data = { |
||||
username:userInfo |
||||
} |
||||
if(loginMethod == ''){ |
||||
// 密码登录 |
||||
data.password = password; |
||||
if(password.length < 8){ |
||||
alert('密码长度不正确') |
||||
return |
||||
} |
||||
}else{ |
||||
// 验证码登录 |
||||
if(verfCode.length != 4){ |
||||
alert('验证码格式错误') |
||||
return |
||||
} |
||||
data.code = verfCode; |
||||
} |
||||
const state = await netStore.signIn(data) |
||||
signInState = true |
||||
if(state){ |
||||
setLoginStyle(['animate__zoomOut', 'animate__animated', 'animate__faster'].join(' ')) |
||||
setTimeout(() => { |
||||
navigate('/home') |
||||
},500) |
||||
}else{ |
||||
alert('登陆失败') |
||||
} |
||||
} |
||||
|
||||
// 注册 |
||||
function signUp(){ |
||||
userStore.setSignInUsername(userInfo) |
||||
setLoginStyle(['animate__zoomOut', 'animate__animated', 'animate__faster'].join(' ')) |
||||
setTimeout(() => { |
||||
navigate('/signUp') |
||||
},500) |
||||
} |
||||
|
||||
return( |
||||
<div className={css.login}> |
||||
<div className={loginStyle}> |
||||
<div className={css.title}>{userStore.config.projectName}</div> |
||||
<div className={css.icon}><img src={userStore.config.icon} alt=""/></div> |
||||
{/*密码登录和验证码登陆切换*/} |
||||
<div> |
||||
{!signInOrOut && <div onClick={changeLoginMethod}> |
||||
<div className={loginMethod}></div> |
||||
<div>密<span style={{width:'1rem',display:'inline-block'}}></span>码</div> |
||||
<div>验证码</div> |
||||
</div>} |
||||
</div> |
||||
{/*登录页第一屏,输入用户信息*/} |
||||
<div className={css.loginBox}> |
||||
<div className={page1Style}> |
||||
<div><input ref={input1} type="text" onChange={userInfoInput} onKeyDown={handleUserInfoKeyDown} value={userInfo} spellCheck ="false" placeholder={'请输入账户信息'}/></div> |
||||
<div className={userInfoState}> |
||||
<div onClick={checkUserInfo}> |
||||
<img src={next} alt=""/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{/*登录页第二屏,输入验证信息或前往注册*/} |
||||
<div className={page2Style}> |
||||
{!signInOrOut && |
||||
<div className={css.input}> |
||||
<div className={loginMethod == '' ? css.temTdShow : css.temTdHide}> |
||||
<div><input ref={input2} type="password" onChange={passwordInput} onKeyDown={handlePasswordKeyDown} value={password} spellCheck ="false" placeholder={'请输入密码'}/></div> |
||||
</div> |
||||
<div className={loginMethod != '' ? css.temTdShow2 : css.temTdHide2}> |
||||
<div> |
||||
<div><input ref={input3} type="text" onChange={verfCodeInput} onKeyDown={handlePasswordKeyDown} value={verfCode} spellCheck ="false" placeholder={'请输入验证码'}/></div> |
||||
<div><Button size='undersize' type='warning1' onClick={() => { |
||||
sendEmail()}} disable={sendBut != 0}>发送验证码</Button></div> |
||||
</div> |
||||
</div> |
||||
</div>} |
||||
<div className={css.button}> |
||||
<div onClick={goBackPage1}>返回</div> |
||||
<div onClick={() => signInOrOut ? signUp() : signIn() }> |
||||
{!signInOrOut && <div>登录</div>} |
||||
{signInOrOut && <div>注册</div>} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
@ -0,0 +1,433 @@ |
||||
.login { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
|
||||
background-image: linear-gradient(to bottom, transparent 75%, #ffffff 100%), radial-gradient(at 0% 0%, hsla(154, 4%, 50%, 0.25) 0, transparent 50%), radial-gradient(at 0% 50%, hsla(120, 5%, 50%, 0.23) 0, transparent 50%), radial-gradient(at 40% 40%, hsla(120, 4%, 50%, 0.21) 0, transparent 50%), radial-gradient(at 80% 0%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), linear-gradient(to bottom, transparent 75%, #ffffff 100%); |
||||
background-position: center; |
||||
background-size: cover; |
||||
background-repeat: no-repeat; |
||||
|
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
user-select: none; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
width: 18rem; |
||||
height: 25rem; |
||||
backdrop-filter: blur(20px); |
||||
|
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
overflow: hidden; |
||||
|
||||
background: #00ADB5; |
||||
border-radius: 1rem; |
||||
padding: 1rem; |
||||
box-sizing: border-box; |
||||
|
||||
& > div.title { |
||||
position: relative; |
||||
font-size: 1.6rem; |
||||
line-height: 4rem; |
||||
text-align: center; |
||||
flex-shrink: 0; |
||||
} |
||||
|
||||
& > div.icon { |
||||
position: relative; |
||||
text-align: center; |
||||
height: 4rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-shrink: 0; |
||||
|
||||
& > img { |
||||
width: 2rem; |
||||
} |
||||
} |
||||
|
||||
& > div:nth-child(3) { |
||||
position: relative; |
||||
flex-shrink: 0; |
||||
height: 1.5rem; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
width: 7rem; |
||||
border-radius: 2rem; |
||||
border: 1px solid #fefefe; |
||||
display: flex; |
||||
font-size: 0.8rem; |
||||
line-height: 1.3rem; |
||||
margin: 0 auto; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
flex: 1; |
||||
text-align: center; |
||||
cursor: pointer; |
||||
color: #fefefe; |
||||
} |
||||
|
||||
& > div:nth-child(1) { |
||||
border-radius: 2rem; |
||||
position: absolute; |
||||
width: 50%; |
||||
left: 0; |
||||
background: #535bf2; |
||||
height: 100%; |
||||
transition: left ease-in-out 300ms; |
||||
} |
||||
|
||||
& > div:nth-child(1).left { |
||||
left: 50%; |
||||
} |
||||
} |
||||
} |
||||
|
||||
& > div.loginBox { |
||||
position: relative; |
||||
flex: 1; |
||||
height: 100%; |
||||
width: 100%; |
||||
padding: 1rem; |
||||
box-sizing: border-box; |
||||
overflow: hidden; |
||||
display: flex; |
||||
//flex-wrap: wrap; |
||||
|
||||
& > div.page1 { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
display: flex; |
||||
flex-shrink: 0; |
||||
flex-direction: column; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
|
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
& > div:nth-child(1) { |
||||
flex: 1; |
||||
|
||||
& > input { |
||||
width: 100%; |
||||
border-radius: 5px; |
||||
font-size: 1rem; |
||||
line-height: 1.5rem; |
||||
padding: 0.5rem; |
||||
box-sizing: border-box; |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
height: 0; |
||||
overflow: hidden; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
background: #535bf2; |
||||
height: 3.5rem; |
||||
width: 3.5rem; |
||||
margin: 0 auto; |
||||
cursor: pointer; |
||||
border-radius: 1.8rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
transition: box-shadow ease-in-out 300ms; |
||||
|
||||
&:hover { |
||||
box-shadow: 0px 0px 10px -1px #33333399; |
||||
} |
||||
|
||||
& > img { |
||||
width: 2rem; |
||||
padding: 0.8rem; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
& > div.page2 { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
display: flex; |
||||
flex-shrink: 0; |
||||
flex-direction: column; |
||||
margin-left: 2rem; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
flex: 1; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
& > div.input { |
||||
position: relative; |
||||
display: block; |
||||
|
||||
& > div { |
||||
position: absolute; |
||||
left: 0; |
||||
right: 0; |
||||
height: 100%; |
||||
width: 100%; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
& input { |
||||
width: 100%; |
||||
border-radius: 5px; |
||||
font-size: 1rem; |
||||
line-height: 1.5rem; |
||||
padding: 0.5rem; |
||||
box-sizing: border-box; |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
& > div:nth-child(1) { |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
position: relative; |
||||
width: 100%; |
||||
|
||||
& > div { |
||||
display: flex; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
& > input { |
||||
display: block; |
||||
flex: 1; |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
position: relative; |
||||
flex-shrink: 0; |
||||
width: 6rem; |
||||
//margin-left: 0.5rem; |
||||
font-size: 0.8px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
& > div{ |
||||
padding: 0; |
||||
margin: 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 登陆方式切换动画 |
||||
$tdShow: tdShow; |
||||
$tdHide: tdHide; |
||||
@keyframes tdHide { |
||||
0% { |
||||
transform: rotateX(0deg); |
||||
opacity: 1; |
||||
} |
||||
100% { |
||||
transform: rotateX(90deg); |
||||
opacity: 0; |
||||
display: none; |
||||
overflow: hidden; |
||||
} |
||||
} |
||||
@keyframes tdShow { |
||||
0% { |
||||
transform: rotateX(90deg); |
||||
opacity: 0; |
||||
display: none; |
||||
overflow: hidden; |
||||
} |
||||
100% { |
||||
transform: rotateX(0deg); |
||||
opacity: 1; |
||||
} |
||||
} |
||||
|
||||
.temTdHide { |
||||
animation: tdHide ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
.temTdShow { |
||||
animation: tdShow ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
$tdShow2: tdShow2; |
||||
$tdHide2: tdHide2; |
||||
@keyframes tdHide2 { |
||||
0% { |
||||
transform: rotateX(0deg); |
||||
opacity: 1; |
||||
} |
||||
100% { |
||||
transform: rotateX(90deg); |
||||
opacity: 0; |
||||
display: none; |
||||
overflow: hidden; |
||||
} |
||||
} |
||||
@keyframes tdShow2 { |
||||
0% { |
||||
transform: rotateX(-90deg); |
||||
opacity: 0; |
||||
display: none; |
||||
overflow: hidden; |
||||
} |
||||
100% { |
||||
transform: rotateX(0deg); |
||||
opacity: 1; |
||||
} |
||||
} |
||||
|
||||
.temTdHide2 { |
||||
animation: tdHide2 ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
.temTdShow2 { |
||||
animation: tdShow2 ease-in-out 500ms forwards; |
||||
} |
||||
} |
||||
|
||||
& > div.button { |
||||
position: relative; |
||||
overflow: hidden; |
||||
display: flex; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
height: 2.5rem; |
||||
background: #535bf2; |
||||
border-radius: 5px; |
||||
color: #fefefe; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
& > div:nth-child(1) { |
||||
width: 4.5rem; |
||||
transition: width ease-in-out 300ms; |
||||
|
||||
&:hover { |
||||
width: 8rem; |
||||
} |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
flex: 1; |
||||
margin-left: 1rem; |
||||
text-align: center; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
$nextShow: nextShow; |
||||
$nextHide: nextHide; |
||||
@keyframes nextShow { |
||||
0% { |
||||
height: 0; |
||||
} |
||||
100% { |
||||
height: 50%; |
||||
} |
||||
} |
||||
|
||||
@keyframes nextHide { |
||||
0% { |
||||
height: 50%; |
||||
} |
||||
100% { |
||||
height: 0; |
||||
} |
||||
} |
||||
|
||||
.temNextShow { |
||||
animation: nextShow ease-in-out 300ms forwards; |
||||
} |
||||
|
||||
.temNextHide { |
||||
animation: nextHide ease-in-out 300ms forwards; |
||||
} |
||||
|
||||
$slideOutLeft: slideOutLeft; |
||||
$slideOutRight: slideOutRight; |
||||
$slideInLeft: slideInLeft; |
||||
$slideInRight: slideInRight; |
||||
|
||||
@keyframes slideOutLeft { |
||||
from { |
||||
left: 0; |
||||
} |
||||
to { |
||||
left: calc(-100% - 2rem); |
||||
} |
||||
} |
||||
|
||||
@keyframes slideOutRight { |
||||
from { |
||||
left: 0; |
||||
} |
||||
to { |
||||
left: calc(100% - 2rem); |
||||
} |
||||
} |
||||
|
||||
@keyframes slideInLeft { |
||||
from { |
||||
left: -100% |
||||
} |
||||
to { |
||||
left: 0; |
||||
} |
||||
} |
||||
|
||||
@keyframes slideInRight { |
||||
from { |
||||
left: 100% |
||||
} |
||||
to { |
||||
left: 0; |
||||
} |
||||
} |
||||
|
||||
.temSlideOutLeft { |
||||
animation: slideOutLeft ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
.temSlideOutRight { |
||||
animation: slideOutRight ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
.temSlideInLeft { |
||||
animation: slideInLeft ease-in-out 500ms forwards; |
||||
} |
||||
|
||||
.temSlideInRight { |
||||
animation: slideInRight ease-in-out 500ms forwards; |
||||
} |
After Width: | Height: | Size: 573 B |
@ -0,0 +1,272 @@ |
||||
import css from './index.module.scss' |
||||
import {userStore, netStore} from "@/store"; |
||||
import {useState} from "react"; |
||||
import next from './next.svg'; |
||||
import Button from "@/components/Button"; |
||||
import {useNavigate} from "react-router-dom"; |
||||
|
||||
export default function SignUp() { |
||||
// 用户名 |
||||
const [username, setUsername] = useState(userStore.signInUsername ? userStore.signInUsername : '') |
||||
// 密码 |
||||
const [password, setPassword] = useState('') |
||||
// 电子邮件 |
||||
const [email, setEmail] = useState('') |
||||
// 手机号 |
||||
const [phone, setPhone] = useState('') |
||||
// 性别 |
||||
const [sex, setSex] = useState('') |
||||
// 昵称 |
||||
const [nickname, setNickname] = useState('') |
||||
// 身份证 |
||||
const [personal, setPersonal] = useState('') |
||||
// 邮箱验证码 |
||||
const [code, setCode] = useState('') |
||||
// 路由跳转 |
||||
const navigate = useNavigate(); |
||||
|
||||
// 设置值 |
||||
const method = { |
||||
username: setUsername, |
||||
password: setPassword, |
||||
email: setEmail, |
||||
phone: setPhone, |
||||
sex: setSex, |
||||
nickname: setNickname, |
||||
personal: setPersonal, |
||||
code: setCode |
||||
} |
||||
// 用户名可使用状态 |
||||
const [userCanuse, setUserCanuse] = useState('') |
||||
// 邮箱可使用状态 |
||||
const [emailCanuse, setEmailCanuse] = useState('') |
||||
// 昵称不可为空 |
||||
const [nicknameCanuse, setNicknameCanuse] = useState('') |
||||
// 密码验证 |
||||
const [passwordCanuse, setPasswordCanuse] = useState('') |
||||
// 验证码验证 |
||||
const [codeCanuse, setCodeCanuse] = useState('') |
||||
|
||||
// 当前页 |
||||
const [nowPage, setNowPage] = useState(1) |
||||
|
||||
// 输入 |
||||
function handleChangeInput(event) { |
||||
const inputType = event.target.getAttribute('iname') |
||||
if (inputType == 'code') { |
||||
if (event.target.value.length >= 0 && event.target.value.length <= 4) { |
||||
method[inputType](event.target.value) |
||||
} |
||||
} else { |
||||
method[inputType](event.target.value) |
||||
} |
||||
|
||||
} |
||||
|
||||
// 用户名验证 |
||||
const [checkUserStatus, setCheckUserStatus] = useState(true) |
||||
function handleUsernameKeyUp() { |
||||
if (!checkUserStatus) { |
||||
setCheckUserStatus(true) |
||||
} |
||||
} |
||||
async function handleUsernameBlur() { |
||||
if(username.length < 8){ |
||||
setUserCanuse('账户名称异常!') |
||||
return |
||||
} |
||||
if (checkUserStatus) { |
||||
setCheckUserStatus(false) |
||||
const status = await netStore.checkOnly({ |
||||
username: username.trim().toLowerCase() |
||||
}) |
||||
if (status) { |
||||
setUserCanuse('') |
||||
} else { |
||||
setUserCanuse('此用户名称已存在!') |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 邮箱验证 |
||||
const [checkEmailStatus, setCheckEmailStatus] = useState(true) |
||||
function handleEmailKeyUp(event) { |
||||
if (!checkEmailStatus) { |
||||
setCheckEmailStatus(true) |
||||
} |
||||
} |
||||
async function handleEmailBlur() { |
||||
if (checkEmailStatus) { |
||||
if (/^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(email)) { |
||||
|
||||
} else { |
||||
setEmailCanuse('邮箱格式错误!') |
||||
return |
||||
} |
||||
setCheckEmailStatus(false); |
||||
// 验证邮箱 |
||||
const status = await netStore.checkOnly({ |
||||
email: email.trim().toLowerCase() |
||||
}) |
||||
if (status) { |
||||
setEmailCanuse('') |
||||
} else { |
||||
setEmailCanuse('此邮箱已存在!') |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
// 验证昵称 |
||||
function handleCheckNickname() { |
||||
if (nickname.length < 1 || nickname.length > 16) { |
||||
setNicknameCanuse('昵称不规范!') |
||||
}else{ |
||||
setNicknameCanuse('') |
||||
} |
||||
} |
||||
function handleNicknameKeyUp(event){ |
||||
console.log(event.target.value) |
||||
switch (event.code){ |
||||
case 'Enter': |
||||
nextPage() |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// 下一页,发送邮件验证 |
||||
async function nextPage() { |
||||
if(nickname.length < 2 || username.length < 8 || email.length < 6){ |
||||
alert('请验证输入') |
||||
}else if (nicknameCanuse.length != 0 || userCanuse.length != 0 || emailCanuse.length != 0) { |
||||
alert('请验证输入') |
||||
} else { |
||||
// 下一页 |
||||
setPage1Style([css.page1, css.temPagehide].join(' ')) |
||||
setTimeout(() => { |
||||
setPage2Style([css.page2, css.temPageshow].join(' ')) |
||||
}, 450) |
||||
// 发送验证码 |
||||
const res = await netStore.sendCode({username,email}, 'signUp') |
||||
console.log(res) |
||||
setNowPage(2) |
||||
} |
||||
} |
||||
|
||||
// 验证密码 |
||||
function handleCheckPassword() { |
||||
if (password.length < 8 || password.length > 128) { |
||||
setPasswordCanuse('密码格式异常!') |
||||
}else{ |
||||
setPasswordCanuse('') |
||||
} |
||||
} |
||||
// 验证验证码 |
||||
function handleCheckCode() { |
||||
if (code.length != 4) { |
||||
setCodeCanuse('验证码格式异常!') |
||||
}else{ |
||||
setCodeCanuse('') |
||||
} |
||||
} |
||||
// 注册 |
||||
async function SIGNUP(){ |
||||
if(password.length < 8 || code.length != 4){ |
||||
alert('请验证输入!') |
||||
}else if(codeCanuse.length !=0 || passwordCanuse.length != 0){ |
||||
alert('请验证输入!') |
||||
}else{ |
||||
const res = await netStore.signUp({ |
||||
username, |
||||
password, |
||||
code, |
||||
email, |
||||
nickname |
||||
}) |
||||
if(res){ |
||||
alert('注册成功') |
||||
} |
||||
setSignUpStyle(['animate__slideOutDown', 'animate__animated', 'animate__fast'].join(' ')) |
||||
setTimeout(() => { |
||||
navigate('/home') |
||||
}, 500) |
||||
} |
||||
} |
||||
|
||||
const [signUpStyle, setSignUpStyle] = useState(['animate__slideInUp', 'animate__animated', 'animate__fast'].join(' ')) |
||||
const [page1Style, setPage1Style] = useState([css.page1, css.temPageshow].join(' ')) |
||||
const [page2Style, setPage2Style] = useState([css.page2].join(' ')) |
||||
|
||||
return <div className={css.SignUp}> |
||||
<div className={signUpStyle}> |
||||
<div className={css.title}>注册</div> |
||||
{/*<div className={css.icon}><img src={userStore.config.icon} alt=""/></div>*/} |
||||
<div className={css.signUpBox}> |
||||
<div className={css.username}> |
||||
<div className={css.inputBox}> |
||||
<div className={css.title}> |
||||
<div className={css.label}>账户</div> |
||||
<div className={css.message}>{userCanuse}</div> |
||||
</div> |
||||
<div className={css.input}> |
||||
<input type="text" iname={'username'} value={username} onKeyUp={handleUsernameKeyUp} |
||||
onChange={handleChangeInput} disabled={nowPage == 1 ? false : true} onBlur={handleUsernameBlur}/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div> |
||||
<div className={page1Style}> |
||||
<div className={css.inputBox}> |
||||
<div className={css.title}> |
||||
<div className={css.label}>邮箱</div> |
||||
<div className={css.message}>{emailCanuse}</div> |
||||
</div> |
||||
<div className={css.input}> |
||||
<input type="text" iname={'email'} value={email} onKeyUp={handleEmailKeyUp} |
||||
onChange={handleChangeInput} onBlur={handleEmailBlur}/> |
||||
</div> |
||||
</div> |
||||
<div className={css.inputBox}> |
||||
<div className={css.title}> |
||||
<div className={css.label}>昵称</div> |
||||
<div className={css.message}>{nicknameCanuse}</div> |
||||
</div> |
||||
<div className={css.input}> |
||||
<input type="text" iname={'nickname'} value={nickname} onChange={handleChangeInput} |
||||
onBlur={handleCheckNickname} onKeyUp={handleNicknameKeyUp}/> |
||||
</div> |
||||
</div> |
||||
<div onClick={nextPage}> |
||||
<div><img src={next} alt=""/></div> |
||||
</div> |
||||
</div> |
||||
<div className={page2Style}> |
||||
<div className={css.inputBox}> |
||||
<div className={css.title}> |
||||
<div className={css.label}>密码</div> |
||||
<div className={css.message}>{passwordCanuse}</div> |
||||
</div> |
||||
<div className={css.input}> |
||||
<input type="text" iname={'password'} value={password} onChange={handleChangeInput} |
||||
onBlur={handleCheckPassword}/> |
||||
</div> |
||||
</div> |
||||
<div className={css.inputBox}> |
||||
<div className={css.title}> |
||||
<div className={css.label}>验证码</div> |
||||
<div className={css.message}>{codeCanuse}</div> |
||||
</div> |
||||
<div className={css.input}> |
||||
<input type="text" iname={'code'} value={code} onChange={handleChangeInput} |
||||
onBlur={handleCheckCode}/> |
||||
</div> |
||||
</div> |
||||
<div onClick={nextPage}> |
||||
<div><Button type='warning' onClick={SIGNUP}>注册</Button></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
} |
@ -0,0 +1,225 @@ |
||||
.SignUp { |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
|
||||
background-image: linear-gradient(to bottom, transparent 75%, #ffffff 100%), radial-gradient(at 0% 0%, hsla(154, 4%, 50%, 0.25) 0, transparent 50%), radial-gradient(at 0% 50%, hsla(120, 5%, 50%, 0.23) 0, transparent 50%), radial-gradient(at 40% 40%, hsla(120, 4%, 50%, 0.21) 0, transparent 50%), radial-gradient(at 80% 0%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), radial-gradient(at 80% 50%, #ffffff 0, transparent 50%), linear-gradient(to bottom, transparent 75%, #ffffff 100%); |
||||
background-position: center; |
||||
background-size: cover; |
||||
background-repeat: no-repeat; |
||||
|
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
user-select: none; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
width: 300px; |
||||
height: 450px; |
||||
backdrop-filter: blur(20px); |
||||
|
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
overflow: hidden; |
||||
|
||||
background: #00ADB5; |
||||
border-radius: 1rem; |
||||
padding: 1rem; |
||||
box-sizing: border-box; |
||||
|
||||
& > div.title { |
||||
position: relative; |
||||
font-size: 1.6rem; |
||||
line-height: 4rem; |
||||
text-align: center; |
||||
flex-shrink: 0; |
||||
} |
||||
|
||||
& > div.icon { |
||||
position: relative; |
||||
text-align: center; |
||||
height: 4rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-shrink: 0; |
||||
|
||||
& > img { |
||||
width: 2rem; |
||||
} |
||||
} |
||||
|
||||
& > div.signUpBox { |
||||
position: relative; |
||||
flex: 1; |
||||
box-sizing: border-box; |
||||
padding: 1rem; |
||||
display: flex; |
||||
overflow: hidden; |
||||
flex-direction: column; |
||||
//background: red; |
||||
& > div.username { |
||||
position: relative; |
||||
margin-bottom: 1rem; |
||||
flex-shrink: 0; |
||||
} |
||||
|
||||
& > div:nth-child(2) { |
||||
position: relative; |
||||
flex: 1; |
||||
|
||||
& > div{ |
||||
width: 100%; |
||||
} |
||||
|
||||
& > div.page1 { |
||||
position: absolute; |
||||
display: flex; |
||||
flex-direction: column; |
||||
height: 100%; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
flex: 1; |
||||
} |
||||
|
||||
& > div:nth-child(3) { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
& > div { |
||||
background: #535bf2; |
||||
height: 3.5rem; |
||||
width: 3.5rem; |
||||
margin: 0 auto; |
||||
cursor: pointer; |
||||
border-radius: 1.8rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
transition: box-shadow ease-in-out 300ms; |
||||
|
||||
&:hover { |
||||
box-shadow: 0px 0px 10px -1px #33333399; |
||||
} |
||||
|
||||
& > img { |
||||
width: 2rem; |
||||
padding: 0.8rem; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
& > div.page2 { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
height: 100%; |
||||
display: none; |
||||
flex-direction: column; |
||||
& > div{ |
||||
position: relative; |
||||
flex: 1; |
||||
} |
||||
& > div:nth-child(3){ |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.inputBox { |
||||
& > div.title { |
||||
position: relative; |
||||
margin: 0.5rem 0; |
||||
padding: 0; |
||||
display: flex; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
} |
||||
|
||||
& > div.label { |
||||
flex-shrink: 0; |
||||
width: 5rem; |
||||
font-size: 1rem; |
||||
} |
||||
|
||||
& > div.message { |
||||
position: relative; |
||||
flex: 1; |
||||
overflow: hidden; |
||||
color: #E84545ee; |
||||
font-size: 0.9rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
} |
||||
|
||||
& > div.input { |
||||
position: relative; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
display: flex; |
||||
|
||||
& > input { |
||||
flex: 1; |
||||
border-radius: 5px; |
||||
line-height: 2rem; |
||||
box-sizing: border-box; |
||||
padding: 0.1rem 0.3rem; |
||||
text-align: center; |
||||
} |
||||
} |
||||
} |
||||
|
||||
$pageshow: pageshow; |
||||
$pagehide: pagehide; |
||||
@keyframes pageshow { |
||||
0% { |
||||
transform: scale(0); |
||||
} |
||||
60% { |
||||
transform: scale(1.05); |
||||
} |
||||
75% { |
||||
transform: scale(0.95); |
||||
} |
||||
90% { |
||||
transform: scale(1.03); |
||||
} |
||||
100% { |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
@keyframes pagehide { |
||||
0% { |
||||
transform: scale(1); |
||||
} |
||||
30% { |
||||
transform: scale(1.1); |
||||
} |
||||
100% { |
||||
transform: scale(0); |
||||
display: none !important; |
||||
} |
||||
} |
||||
.temPageshow { |
||||
display: flex !important; |
||||
animation: pageshow ease-in-out 600ms forwards; |
||||
} |
||||
|
||||
.temPagehide { |
||||
animation: pagehide ease-in-out 600ms forwards; |
||||
} |
After Width: | Height: | Size: 573 B |
@ -0,0 +1,68 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<title>Title</title> |
||||
<style> |
||||
/*.container {*/ |
||||
/* text-align: center;*/ |
||||
/* display: flex;*/ |
||||
/* align-items: center;*/ |
||||
/* justify-content: space-around;*/ |
||||
/* padding-bottom: 20px;*/ |
||||
/* color: white;*/ |
||||
/* font-size: 50px;*/ |
||||
/* position: relative;*/ |
||||
/*}*/ |
||||
.bur { |
||||
width: 400px; |
||||
height: 400px; |
||||
background: url('../src/view/Home/bg0.jpg')center / cover no-repeat; |
||||
|
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.method3-bur2-1 { |
||||
/* 因为设置了父元素设置了 display:flex 属性,这里必须设置长宽来刚好遮罩住 */ |
||||
width: 400px; |
||||
height: 400px; |
||||
/* 关键属性 */ |
||||
backdrop-filter: blur(5px); |
||||
} |
||||
.main{ |
||||
width: 500px; |
||||
height: 500px; |
||||
overflow: hidden; |
||||
} |
||||
.son{ |
||||
position: relative; |
||||
height: 100%; |
||||
width: 100%; |
||||
background: url('../src/view/Home/bg0.jpg') center / cover no-repeat; |
||||
filter: blur(5px); |
||||
transform: scale(1.5); |
||||
} |
||||
|
||||
|
||||
</style> |
||||
</head> |
||||
<body> |
||||
|
||||
<div class='container'> |
||||
<div class='bur method3-bur2'> |
||||
<div class='method3-bur2-1'> |
||||
<span>这是文字</span> |
||||
</div> |
||||
|
||||
</div> |
||||
<div class="main"> |
||||
<div class="son"> |
||||
</div> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,28 @@ |
||||
import {defineConfig} from 'vite' |
||||
import react from '@vitejs/plugin-react' |
||||
import {resolve} from "path"; |
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({ |
||||
plugins: [react()], |
||||
resolve: { |
||||
alias: [ |
||||
{ |
||||
find: '@', |
||||
replacement: resolve(__dirname, 'src') |
||||
}, |
||||
] |
||||
}, |
||||
server: { |
||||
proxy: { |
||||
'/api': { // 匹配请求路径,localhost:3000/snow
|
||||
target: 'http://localhost:3000', // 代理的目标地址
|
||||
changeOrigin: true, // 开发模式,默认的origin是真实的 origin:localhost:3000 代理服务会把origin修改为目标地址
|
||||
// secure: true, // 是否https接口
|
||||
// ws: true, // 是否代理websockets
|
||||
// rewrite target目标地址 + '/abc',如果接口是这样的,那么不用重写
|
||||
// rewrite: (path) => path.replace(/^\/snow/, '') // 路径重写,本项目不需要重写
|
||||
} |
||||
} |
||||
}, |
||||
}) |
@ -0,0 +1,8 @@ |
||||
# 文档结构说明 |
||||
|
||||
```bash |
||||
|------baseSys # 基础系统 |
||||
|------.gitignore # git排除文件 |
||||
|------list.md # 根目录结构说明 |
||||
|------README.md # 系统说明 |
||||
``` |
@ -0,0 +1,12 @@ |
||||
# A.I.N.Y |
||||
|
||||
希望可以坚持下去 |
||||
|
||||
# HotoKiss 2023-01-04 |
||||
|
||||
> 参考素锦 背景图 `/src/assets/index.png` |
||||
|
||||
前端: |
||||
|
||||
1. 参考HELLO WORLD的开场动画,调用素锦的北京 |
||||
2. 线框风格 |
@ -0,0 +1,24 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,13 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="zh"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Vite + React</title> |
||||
</head> |
||||
<body> |
||||
<div id="root"></div> |
||||
<script type="module" src="/src/main.jsx"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,24 @@ |
||||
{ |
||||
"name": "ying-drosophila", |
||||
"private": true, |
||||
"version": "0.0.0", |
||||
"type": "module", |
||||
"scripts": { |
||||
"start": "vite", |
||||
"build": "vite build", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"mobx": "^6.7.0", |
||||
"react": "^18.2.0", |
||||
"react-dom": "^18.2.0", |
||||
"react-router-dom": "^6.6.1", |
||||
"sass": "^1.57.1" |
||||
}, |
||||
"devDependencies": { |
||||
"@types/react": "^18.0.26", |
||||
"@types/react-dom": "^18.0.9", |
||||
"@vitejs/plugin-react": "^3.0.0", |
||||
"vite": "^4.0.0" |
||||
} |
||||
} |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,14 @@ |
||||
import {useState} from 'react' |
||||
import { useRoutes } from 'react-router-dom' |
||||
import indexRouterList from "./router" |
||||
|
||||
|
||||
export default function App() { |
||||
const indexRoute = useRoutes(indexRouterList) |
||||
return ( |
||||
<div className="App"> |
||||
{indexRoute} |
||||
</div> |
||||
) |
||||
} |
||||
|
@ -0,0 +1,31 @@ |
||||
html{ |
||||
font-size: 62.5%; |
||||
} |
||||
body { |
||||
margin: 0; |
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', |
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', |
||||
sans-serif; |
||||
/* 'Cantarell', */ |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
color:#333; |
||||
} |
||||
p, h1, h2, h3, h4, h5, h6{ |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
fieldset, img,input,button { border:none; padding:0;margin:0;outline-style:none; } |
||||
ul, ol { list-style:none; } |
||||
textarea { resize:none; } |
||||
img {border:0; vertical-align:middle;} |
||||
|
||||
a{color:#333; text-decoration:none; } |
||||
a:hover{color:#C81623;} |
||||
h1,h2,h3,h4,h5,h6{text-decoration:none;font-weight:normal;} |
||||
s,i,em{font-style:normal;text-decoration:none;} |
||||
.col-red{color: #C81623!important;} |
||||
code { |
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', |
||||
monospace; |
||||
} |
@ -0,0 +1,15 @@ |
||||
@mixin widthAuto{ |
||||
/*div宽度适应文字*/ |
||||
width:fit-content; |
||||
width:-webkit-fit-content; |
||||
width:-moz-fit-content; |
||||
} |
||||
@mixin noSelect{ |
||||
/*无法选中*/ |
||||
-webkit-touch-callout:none; |
||||
-webkit-user-select:none; |
||||
-khtml-user-select:none; |
||||
-moz-user-select:none; |
||||
-ms-user-select:none; |
||||
user-select:none; |
||||
} |