整合初始化

main
expressgy 4 months ago
commit ec7af1413d
  1. 58
      .gitignore
  2. 25
      01===sys22/.gitignore
  3. BIN
      01===sys22/README.md
  4. 0
      01===sys22/baseSys/baseSys.dirname
  5. 3
      01===sys22/baseSys/userSys/README.md
  6. 1629
      01===sys22/baseSys/userSys/design/database/sys22_user.ndm2
  7. 66
      01===sys22/baseSys/userSys/design/database/sys22_user.sql
  8. 0
      01===sys22/baseSys/userSys/design/design.dirname
  9. BIN
      01===sys22/baseSys/userSys/design/接口概览.xmind
  10. BIN
      01===sys22/baseSys/userSys/design/模块设计.xmind
  11. BIN
      01===sys22/baseSys/userSys/design/用户系统表.xmind
  12. 12
      01===sys22/baseSys/userSys/design/需求分析.md
  13. 7
      01===sys22/baseSys/userSys/realization/READE.md
  14. 118
      01===sys22/baseSys/userSys/realization/app.js
  15. 86
      01===sys22/baseSys/userSys/realization/bin/www.js
  16. 66
      01===sys22/baseSys/userSys/realization/config/database/sys22_user.sql
  17. 52
      01===sys22/baseSys/userSys/realization/config/default.config.js
  18. 34
      01===sys22/baseSys/userSys/realization/package.json
  19. 1113
      01===sys22/baseSys/userSys/realization/pnpm-lock.yaml
  20. 0
      01===sys22/baseSys/userSys/realization/reallization.dirname
  21. 1220
      01===sys22/baseSys/userSys/realization/src/API/index.js
  22. 73
      01===sys22/baseSys/userSys/realization/src/Database/initDatabase.js
  23. 135
      01===sys22/baseSys/userSys/realization/src/Database/userBase.js
  24. 41
      01===sys22/baseSys/userSys/realization/src/Routes/index.js
  25. 18
      01===sys22/baseSys/userSys/realization/src/Routes/test/index.js
  26. 27
      01===sys22/baseSys/userSys/realization/src/Routes/user/authority.js
  27. 20
      01===sys22/baseSys/userSys/realization/src/Routes/user/index.js
  28. 25
      01===sys22/baseSys/userSys/realization/src/Routes/user/role.js
  29. 21
      01===sys22/baseSys/userSys/realization/src/Routes/user/signed.js
  30. 20
      01===sys22/baseSys/userSys/realization/src/Routes/user/will.js
  31. 31
      01===sys22/baseSys/userSys/realization/test/redisjson.js
  32. 22
      01===sys22/baseSys/userSys/realization/test/testDelToken.js
  33. 35
      01===sys22/baseSys/userSys/realization/test/testRedis.js
  34. 13
      01===sys22/baseSys/userSys/realization/test/testTime.js
  35. 11
      01===sys22/baseSys/userSys/realization/test/testToken.js
  36. 3
      01===sys22/baseSys/userSys/realization/test/trimr.js
  37. 21
      01===sys22/baseSys/userSys/realization/test/连表查询.js
  38. 10
      01===sys22/baseSys/userSys/realization/tools/RandomString.js
  39. 70
      01===sys22/baseSys/userSys/realization/tools/console/index.js
  40. 65
      01===sys22/baseSys/userSys/realization/tools/database/createConnection.js
  41. 14
      01===sys22/baseSys/userSys/realization/tools/database/formatSQL.js
  42. 17
      01===sys22/baseSys/userSys/realization/tools/getEnv.js
  43. 75
      01===sys22/baseSys/userSys/realization/tools/mail.js
  44. 308
      01===sys22/baseSys/userSys/realization/tools/redisJSON.js
  45. 136
      01===sys22/baseSys/userSys/realization/tools/user/encryptionString.js
  46. 3
      01===sys22/baseSys/userSys/realization/tools/user/sendEmail.js
  47. 34
      01===sys22/baseSys/userSys/realization/tools/user/token.js
  48. 11
      01===sys22/baseSys/userSys/realization/tools/uuid.js
  49. 0
      01===sys22/baseSys/userSys/userSys.dirname
  50. 24
      01===sys22/baseSys/userSys/vision/.gitignore
  51. 14
      01===sys22/baseSys/userSys/vision/index.html
  52. 25
      01===sys22/baseSys/userSys/vision/package.json
  53. 1174
      01===sys22/baseSys/userSys/vision/pnpm-lock.yaml
  54. 1
      01===sys22/baseSys/userSys/vision/public/vite.svg
  55. 14
      01===sys22/baseSys/userSys/vision/src/App.jsx
  56. 101
      01===sys22/baseSys/userSys/vision/src/api/index.js
  57. 111
      01===sys22/baseSys/userSys/vision/src/assets/index.css
  58. 1
      01===sys22/baseSys/userSys/vision/src/assets/react.svg
  59. 53
      01===sys22/baseSys/userSys/vision/src/components/Button/index.jsx
  60. 162
      01===sys22/baseSys/userSys/vision/src/components/Button/index.module.scss
  61. 1
      01===sys22/baseSys/userSys/vision/src/components/Modal/close.svg
  62. 68
      01===sys22/baseSys/userSys/vision/src/components/Modal/index.jsx
  63. 64
      01===sys22/baseSys/userSys/vision/src/components/Modal/index.module.scss
  64. 15
      01===sys22/baseSys/userSys/vision/src/components/RouterAuth/index.jsx
  65. 5
      01===sys22/baseSys/userSys/vision/src/config/sys22.js
  66. 38
      01===sys22/baseSys/userSys/vision/src/main.jsx
  67. 37
      01===sys22/baseSys/userSys/vision/src/router/index.jsx
  68. 5
      01===sys22/baseSys/userSys/vision/src/store/index.js
  69. 124
      01===sys22/baseSys/userSys/vision/src/store/netStore.js
  70. 123
      01===sys22/baseSys/userSys/vision/src/store/userStore.js
  71. 38
      01===sys22/baseSys/userSys/vision/src/view/Home/Setting/index.jsx
  72. 77
      01===sys22/baseSys/userSys/vision/src/view/Home/Setting/index.module.scss
  73. 1
      01===sys22/baseSys/userSys/vision/src/view/Home/Setting/start.svg
  74. BIN
      01===sys22/baseSys/userSys/vision/src/view/Home/bg0.jpg
  75. 79
      01===sys22/baseSys/userSys/vision/src/view/Home/index.jsx
  76. 245
      01===sys22/baseSys/userSys/vision/src/view/Home/index.module.scss
  77. 13
      01===sys22/baseSys/userSys/vision/src/view/Home/rocket.svg
  78. 9
      01===sys22/baseSys/userSys/vision/src/view/Home/setting.svg
  79. 12
      01===sys22/baseSys/userSys/vision/src/view/Home/start.svg
  80. BIN
      01===sys22/baseSys/userSys/vision/src/view/Index/background.jpg
  81. 23
      01===sys22/baseSys/userSys/vision/src/view/Index/index.jsx
  82. 28
      01===sys22/baseSys/userSys/vision/src/view/Index/index.module.scss
  83. 246
      01===sys22/baseSys/userSys/vision/src/view/SignIn/index.jsx
  84. 433
      01===sys22/baseSys/userSys/vision/src/view/SignIn/index.module.scss
  85. 1
      01===sys22/baseSys/userSys/vision/src/view/SignIn/next.svg
  86. 272
      01===sys22/baseSys/userSys/vision/src/view/SignUp/index.jsx
  87. 225
      01===sys22/baseSys/userSys/vision/src/view/SignUp/index.module.scss
  88. 1
      01===sys22/baseSys/userSys/vision/src/view/SignUp/next.svg
  89. 68
      01===sys22/baseSys/userSys/vision/test/backdrop.html
  90. 28
      01===sys22/baseSys/userSys/vision/vite.config.js
  91. 8
      01===sys22/list.md
  92. 12
      02=A.I.N.Y/README.md
  93. 24
      02=A.I.N.Y/Ying-Drosophila/.gitignore
  94. 13
      02=A.I.N.Y/Ying-Drosophila/index.html
  95. 24
      02=A.I.N.Y/Ying-Drosophila/package.json
  96. 1070
      02=A.I.N.Y/Ying-Drosophila/pnpm-lock.yaml
  97. 1
      02=A.I.N.Y/Ying-Drosophila/public/vite.svg
  98. 14
      02=A.I.N.Y/Ying-Drosophila/src/App.jsx
  99. 31
      02=A.I.N.Y/Ying-Drosophila/src/assets/default.css
  100. 15
      02=A.I.N.Y/Ying-Drosophila/src/assets/default.scss
  101. Some files were not shown because too many files have changed in this diff Show More

58
.gitignore vendored

@ -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?

Binary file not shown.

@ -0,0 +1,3 @@
# userSys
用户系统

File diff suppressed because it is too large Load Diff

@ -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"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -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,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"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

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; }

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

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;
}
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1666169304891" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3611" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2000"><path d="M1024 512c0 282.7776-229.2224 512-512 512S0 794.7776 0 512 229.2224 0 512 0s512 229.2224 512 512z m-372.736-194.9184L512 456.3456l-139.264-139.264A39.424 39.424 0 0 0 317.0816 372.736L456.3456 512l-139.264 139.264a39.424 39.424 0 1 0 55.6544 55.6544l139.264-139.264 139.264 139.264a39.424 39.424 0 1 0 55.6544-55.6544L567.6544 512l139.264-139.264a39.424 39.424 0 0 0-55.6544-55.6544z" p-id="3612" data-spm-anchor-id="a313x.7781069.0.i4" class="selected" fill="#E84545"></path></svg>

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;
}
}
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1669121438513" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9223" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512.03456 512m-400 0a400 400 0 1 0 800 0 400 400 0 1 0-800 0Z" fill="#FFDA64" p-id="9224"></path><path d="M512.03456 160a400 400 0 0 1 400 376V512a400 400 0 0 0-800 0v24A400 400 0 0 1 512.03456 160z" fill="#FFFFFF" p-id="9225"></path><path d="M512.03456 848A400 400 0 0 1 113.47456 480C112.03456 490.56 112.03456 501.28 112.03456 512a400 400 0 0 0 800 0c0-10.72 0-21.44-1.44-32A400 400 0 0 1 512.03456 848z" fill="#FFB666" p-id="9226"></path><path d="M1017.95456 220c-20.48-35.36-85.12-36.96-192-4.64A432 432 0 0 0 98.11456 635.52C16.03456 712-14.20544 768 6.11456 804 16.03456 822.56 39.55456 832 74.27456 832a455.36 455.36 0 0 0 124-23.2 432 432 0 0 0 727.68-420.32C1008.03456 312 1038.27456 256 1017.95456 220zM512.03456 128a384 384 0 0 1 364.16 262.24 2023.36 2023.36 0 0 1-310.08 215.52A2022.56 2022.56 0 0 1 224.03456 766.4 384 384 0 0 1 512.03456 128zM33.79456 788c-8-13.92 7.84-53.28 75.68-119.2a432 432 0 0 0 65.44 113.28c-97.12 27.36-134.56 17.92-141.12 5.92zM512.03456 896a382.56 382.56 0 0 1-262.56-104.16 2105.28 2105.28 0 0 0 332.8-158.24 2104.64 2104.64 0 0 0 304-209.12A384 384 0 0 1 512.03456 896z m336.96-654.08c97.12-27.36 134.56-17.28 141.12-5.92s-3.52 48-75.68 119.2a432 432 0 0 0-65.44-113.28z" fill="#3A394B" p-id="9227"></path><path d="M624.03456 304m-48 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0Z" fill="#3A394B" p-id="9228"></path><path d="M471.07456 270.08m-24 0a24 24 0 1 0 48 0 24 24 0 1 0-48 0Z" fill="#3A394B" p-id="9229"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

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;
}

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1668444427192" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="23952" xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240">
<path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="23953"></path>
<path d="M263.645867 496.7424A34.133333 34.133333 0 0 1 294.161067 477.866667h435.677866a34.133333 34.133333 0 0 1 30.5152 18.875733l111.854934 223.675733A68.266667 68.266667 0 0 1 811.1104 819.2H212.8896a68.266667 68.266667 0 0 1-61.064533-98.781867l111.854933-223.675733zM204.8 887.466667a34.133333 34.133333 0 1 1 68.266667 0v34.133333a34.133333 34.133333 0 1 1-68.266667 0v-34.133333z m580.266667-34.133334a34.133333 34.133333 0 0 0-34.133334 34.133334v34.133333a34.133333 34.133333 0 1 0 68.266667 0v-34.133333a34.133333 34.133333 0 0 0-34.133333-34.133334z"
fill="#FFAA44" p-id="23954"></path>
<path d="M273.066667 430.08a341.333333 341.333333 0 0 1 119.1936-259.1744l97.518933-83.626667a34.133333 34.133333 0 0 1 44.4416 0l97.518933 83.626667A341.333333 341.333333 0 0 1 750.933333 430.045867V716.8a238.933333 238.933333 0 1 1-477.866666 0v-286.72z"
fill="#FF7744" p-id="23955"></path>
<path d="M477.866667 819.2a34.133333 34.133333 0 1 1 68.266666 0v136.533333a34.133333 34.133333 0 1 1-68.266666 0v-136.533333z"
fill="#FFAA44" p-id="23956"></path>
<path d="M512 273.066667a102.4 102.4 0 1 1 0 204.8 102.4 102.4 0 0 1 0-204.8z" fill="#FFFFFF" p-id="23957"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1668442910768" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9248"
xmlns:xlink="http://www.w3.org/1999/xlink" width="175" height="175">
<path d="M627.2 972.8c19.456 19.456 51.2 25.6 76.8 12.8 45.056-19.456 83.456-38.4 121.856-70.656 19.456-19.456 32.256-44.544 25.6-76.8-6.656-38.4 0-83.456 19.456-121.856s57.344-64 96.256-76.8c25.6-6.656 44.544-32.256 51.2-57.344 6.656-45.056 6.656-96.256 0-140.8-6.656-25.6-25.6-51.2-51.2-57.856-38.4-12.8-76.8-38.4-96.256-76.8s-25.6-83.456-19.456-121.856c6.656-25.6-6.656-57.856-25.6-76.8-38.4-31.744-76.8-51.2-121.856-70.656-25.6-12.8-57.344-6.656-76.8 12.8C563.2 109.056 460.8 109.056 396.8 51.2c-19.456-19.456-51.2-25.6-76.8-12.8-44.544 18.944-83.456 38.4-121.856 70.656-25.6 19.456-32.256 45.056-25.6 76.8C192 268.8 140.8 358.4 57.856 384c-32.256 6.656-51.2 32.256-51.2 57.856C0 467.456 0 486.4 0 512s0 45.056 6.656 70.656c0 25.6 19.456 51.2 51.2 57.344 38.4 12.8 76.8 38.4 96.256 76.8s25.6 83.456 18.944 121.856c-6.656 25.6 6.656 57.344 25.6 76.8 38.4 32.256 76.8 51.2 121.856 70.656 25.6 12.8 57.856 6.656 76.8-12.8 63.488-64.512 165.888-64.512 229.888-0.512z"
fill="#919BF2" p-id="9249" data-spm-anchor-id="a313x.7781069.0.i2" class="selected"></path>
<path d="M512 512m-147.456 0a147.456 147.456 0 1 0 294.912 0 147.456 147.456 0 1 0-294.912 0Z" fill="#FFFFFF"
p-id="9250"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1668437044959" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7380"
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<path d="M510.9 379m-83.9 0a83.9 83.9 0 1 0 167.8 0 83.9 83.9 0 1 0-167.8 0Z" fill="#FAFAF2" p-id="7381"></path>
<path d="M398 574.2m-73.7 0a73.7 73.7 0 1 0 147.4 0 73.7 73.7 0 1 0-147.4 0Z" fill="#FAFAF2" p-id="7382"></path>
<path d="M515.1 708.6m-73.7 0a73.7 73.7 0 1 0 147.4 0 73.7 73.7 0 1 0-147.4 0Z" fill="#FAFAF2" p-id="7383"></path>
<path d="M870.5 338.2c-3.8-26.1-18-45.2-41-57.8-19.9-10.9-41.7-14.6-64-15.6-22.3-1.1-44.4 1.1-66.3 4.6-2.8 0.5-4.6-0.1-6.8-1.6-53.8-39.1-114-58.8-180.4-59.3-23.7-0.2-47 2.3-69.9 8.2-102.4 26.5-173.9 88.7-213.6 186.9-10.6 26.3-16.5 53.8-18.9 82.1-1.7 20.5-1.7 40.8 0.9 61.2 0.3 2.4-0.5 3.9-1.9 5.6-5.4 7-11.1 13.9-16.2 21.2-19 26.9-35.2 55.1-39 88.6-3.5 31 6.1 56.6 31.8 75.3 19.2 13.9 41.5 18.8 64.5 20.9 23.1 2.1 46.1 0.2 69.1-3.2 4.7-0.7 9.2-2.2 14.2 1.5 45.8 33.7 97.4 52.5 154 57.6 30 2.7 59.8 0.8 89.1-6 90.6-20.9 158.6-72 203.5-153.4 20-36.3 31.1-75.5 34.9-116.7 1.8-19.2 1.9-38.5-0.6-57.7-0.6-4.6-0.1-8.6 3.6-12 2.5-2.3 4.3-5.3 6.4-8 16.5-21.7 31.3-44.5 40.2-70.6 5.6-16.7 9-34 6.4-51.8zM397.7 623.1c-28.8 0-52.3-23.4-52.5-52.3-0.2-28.9 23.6-52.6 52.6-52.6 28.9 0.1 52.2 23.5 52.2 52.4 0 29-23.4 52.5-52.3 52.5z m111.6 124.4c-21.8-0.1-39.2-17.6-39.2-39.4 0-21.7 17.6-39.4 39.3-39.3 21.6 0 39.3 17.8 39.3 39.4-0.1 21.6-17.9 39.4-39.4 39.3z m2.3-292.2c-43.4-0.2-78.2-35.2-78.1-78.5 0.1-43.5 35.3-78.5 78.8-78.3 43.1 0.1 78.2 35.4 78.2 78.6-0.1 43.4-35.4 78.4-78.9 78.2z m0 0"
fill="#FFC152" p-id="7384"></path>
<path d="M511.6 410.8c-18.7 0-33.8-15.3-33.6-34 0.1-18.7 15.4-33.9 34-33.8 18.9 0.1 34 15.5 33.9 34.4-0.1 18.5-15.5 33.5-34.3 33.4z m0 0M414.4 570.8c0 9.1-7.5 16.6-16.6 16.7-9.2 0.1-17-7.7-16.9-16.9 0.1-9.2 7.5-16.6 16.7-16.7 9.3-0.1 16.8 7.4 16.8 16.9z m0 0M496.8 708.1c0-6.8 5.5-12.5 12.3-12.6 6.9-0.1 12.8 5.7 12.8 12.7 0 6.8-5.6 12.4-12.4 12.6-7 0.1-12.7-5.6-12.7-12.7z m0 0"
fill="#FF6994" p-id="7385"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

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;
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1667652583766" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2521" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M729.6 448H128v85.333333h601.6L597.333333 665.6l59.733334 59.733333 234.666666-234.666666L661.333333 256l-59.733333 59.733333 128 132.266667z" fill="#fefefe" p-id="2522" data-spm-anchor-id="a313x.7781069.0.i0" class="selected"></path></svg>

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;
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1667652583766" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2521" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M729.6 448H128v85.333333h601.6L597.333333 665.6l59.733334 59.733333 234.666666-234.666666L661.333333 256l-59.733333 59.733333 128 132.266667z" fill="#fefefe" p-id="2522" data-spm-anchor-id="a313x.7781069.0.i0" class="selected"></path></svg>

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"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

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;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save