由于各种原因重新初始化服务器(使用全新框架改善界面,重构逻辑代码)

This commit is contained in:
zzy 2024-04-11 11:49:22 +08:00
commit 534b397583
54 changed files with 11823 additions and 0 deletions

54
README.md Normal file
View File

@ -0,0 +1,54 @@
## 项目文件结构
root/
|- main/
| |- web.js 启动代码
|- routers/
| |- *.js 路由文件
| |- */ 各路由文件的子文件及使用的中间件
|- models/ 未来数据模型文件
|- views/ 视图文件(一般是不允许直接访问的文件,使用中间件访问)
|- static/ 静态文件(所有网页的静态资源存放处)
| |- css/ css文件
| |- js/ js文件
| |- pic/ 图片文件
| |- html/ html文件
| |- index.html 默认首页
|- .data/ 配置文件
|- .gitignore git忽略文件
|- package.json 项目依赖包
|- package-lock.json 项目依赖包(自动生成)
|- .env 配置文件(如需自己启用项目请根据default.env文件修改后启用项目)
|- server.js 主路由代码
|- utils.js 工具代码(包含路径,api返回格式)
- 默认首页简易解释
static/index.html 文件需要调用 static/js/base_script.js 动态加载在 static/html/base/*.html 的共享资源且需要动态引入.data内的json数据以渲染出一张张cards
## 前后端接口手册
- 请看utils.js的json内部格式
## 杂项
npm init
npm install [package names]
npm install
使用tmux
安装tmux如果尚未安装
bash
sudo apt-get install tmux # 对于Debian/Ubuntu系统
sudo yum install tmux # 对于CentOS/RedHat系统
启动一个新的tmux会话
bash
tmux
在tmux会话中启动你的Node.js应用
bash
node app.js
按下Ctrl + B然后按下D来“detach”分离tmux会话。
重新连接到tmux会话时运行
bash
tmux attach
https://zhuanlan.zhihu.com/p/667646001

5
default.env Normal file
View File

@ -0,0 +1,5 @@
LISTEN_PORT=3000
JWT_SECRET=YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG
DATABASE_NAME=sqlite3.db
USER_LOGIN_NAME=admin
USER_LOGIN_PASSWORD=MoAHzbzmZ9HZT5SGrhu9XyD7wUtxJRbW

87
main/web.js Normal file
View File

@ -0,0 +1,87 @@
/**
* Module dependencies.
*/
const app = require('../server.js');
const http = require('http');
require('dotenv').config();
env = process.env;
/**
* Get port from config and store in Express.
*/
const port = normalizePort(env.LISTEN_PORT || '13000');
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Web server listening on ' + bind);
}

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "zzy_web",
"version": "1.0.0",
"description": "None",
"main": "./main/web.js",
"scripts": {
"test": "nodemon ./main/web.js",
"start": "node ./main/web.js"
},
"author": "zzy",
"license": "ISC",
"dependencies": {
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"nodemon": "^3.1.0",
"sqlite3": "^5.1.7"
}
}

10
routes/api.js Normal file
View File

@ -0,0 +1,10 @@
const utils = require('../utils.js');
const express = require('express');
const router = express.Router();
router.use('/users', require('./api/users.js'));
router.use('/files', require('./api/files.js'));
router.use('/json', require('./api/json.js'));
module.exports = router;

55
routes/api/files.js Normal file
View File

@ -0,0 +1,55 @@
const express = require('express');
const utils = require('../../utils.js');
const router = express.Router();
router.get('/', (req, res, next) => {
// 读取文件夹列表
const files = fs.readdirSync(path.join(FILE_PATH));
let data = [];
files.forEach((fileName) => {
const filePath = path.join(FILE_PATH, fileName);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
// 如果是文件夹,递归遍历
// data.push({
// fileName: fileName,
// filePath: fileName,
// isFolder: true,
// fileSize: stats.size,
// modifyTime: stats.mtime.toLocaleString()
// })
} else {
// 如果是文件,执行操作
data.push({
fileName: fileName,
filePath: fileName,
isFolder: false,
fileSize: stats.size,
modifyTime: stats.mtime.toLocaleString()
})
}
})
fs.writeFileSync(path.join(FILE_DATA_PATH, 'files.json'), JSON.stringify(data));
res.json(data);
})
router.get('/:filename', function(req, res, next) {
const filename = req.params.filename;
console.log(filename);
data = JSON.parse(fs.readFileSync(path.join(FILE_DATA_PATH, 'files.json')));
data.getItems = (fileName) => {
return data.find((item) => {
return item.fileName === fileName;
})
}
var item = data.getItems(filename);
console.log(item);
if (item.isFolder == false) {
res.sendFile(path.join(FILE_PATH, filename));
} else {
res.location('/index.html');
}
});
module.exports = router;

9
routes/api/json.js Normal file
View File

@ -0,0 +1,9 @@
const utils = require('../../utils.js');
const router = require('express').Router();
router.get('/:filename', function(req, res, next) {
const filename = req.params.filename;
res.sendFile(utils.path.join(utils.DATA_JSON_PATH, filename));
});
module.exports = router;

36
routes/api/users.js Normal file
View File

@ -0,0 +1,36 @@
const utils = require('../../utils.js');
const express = require('express');
const auth = require('../utils/auth_utils');
const router = express.Router();
router.post('/login', function(req, res, next) {
const data = req.body;
if (data.username === (utils.env.USER_LOGIN_NAME || 'admin') &&
data.password === (utils.env.USER_LOGIN_PASSWORD || 'admin') ){
const token = auth.generateToken({
username: data.username,
id: -1
})
res.json({...utils.json.success,
data: token
})
} else {
res.json({...utils.json.user_is_valid})
}
});
//remote-data-processing-display
router.post('/_rdpd', function(req, res, next) {
express.response.send = function(data) {
res.json({...utils.json.success,
data: data
})
}
});
router.get('/admin', function(req, res, next) {
res.send('admin');
});
module.exports = router;

7
routes/db/db.js Normal file
View File

@ -0,0 +1,7 @@
const utils = require('../../utils.js');
const sqlite3DB = require(utils.path.join(utils.RT_UTILS_PATH, 'db_utils'));
const db_path = utils.path.join(utils.DATA_DB_PATH, utils.env.DATABASE_NAME || 'sqlite3.db');
const db = new sqlite3DB(db_path);
module.exports = db;

19
routes/db/users.js Normal file
View File

@ -0,0 +1,19 @@
const db = require('./db.js');
db.createTable('users', [
{name :'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'},
{name :'name', type: 'VARCHAR(32) NOT NULL UNIQUE'},
{name :'password', type: `VARCHAR(255) NOT NULL DEFAULT '123456'`},
{name :'update_time', type: 'DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP'}
]);
// db.insertData('users', data);
// db.deleteData('users', `name='${data[0].name}'`);
// db.updateData('users', data[2], `name='${data[1].name}'`);
// db.selectData('users', ['name', 'email', 'password'], `name='${data[0].name}'`, (err, rows) => {
// console.log(rows);
// })
// db.selectData('users', ['name', 'email', 'password'], null, function(err, rows) {
// console.log(rows);
// });
// db.closeConnection();

11
routes/files.js Normal file
View File

@ -0,0 +1,11 @@
const utils = require('../utils.js');
const express = require('express');
const router = express.Router();
router.get('/', function(req, res, next) {
// res.sendFile(utils.path.join(utils.DATA_FILE_PATH, 'index.html'));
res.send('files');
});
module.exports = router;

7
routes/index.js Normal file
View File

@ -0,0 +1,7 @@
const utils = require('../utils.js');
const express = require('express');
const router = express.Router();
module.exports = router;

66
routes/users.js Normal file
View File

@ -0,0 +1,66 @@
const utils = require('../utils.js');
const express = require('express');
const db = require(utils.path.join(utils.RT_DB_PATH, 'users.js'));
const auth = require(utils.path.join(utils.RT_UTILS_PATH, 'auth_utils.js'));
const router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
token = req.cookies.token;
if (typeof token === 'string') {
return auth.verifyToken(token, (err, data) => {
if (err) {
res.redirect('/users/login');
return;
}
res.sendFile(utils.path.join(utils.VIEWS_PATH, 'users/users.html'));
})
}
res.redirect('/users/login');
});
/**
* GET /users/login
* @param {*} req
* @param {*} res
* @param {*} next
*/
router.get('/login', function(req, res, next) {
res.sendFile(utils.path.join(utils.VIEWS_PATH, 'users/login.html'));
})
router.use('/admin', function(req, res, next) {
res.send('admin');
});
// WILL BE FILLING
router.get('/register', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/logout', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit/password', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit/username', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit/email', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit/phone', function(req, res, next) {
res.send('respond with a resource');
})
router.get('/profile/edit/website', function(req, res, next) {
res.send('respond with a resource');
})
module.exports = router;

View File

@ -0,0 +1,40 @@
const utils = require('../../utils.js');
const jwt = require('jsonwebtoken');
const generateToken = (payload, options) => {
return jwt.sign(payload, utils.env.JWT_SECRET || 'YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG', {
expiresIn: 60,
...options
});
};
const verifyToken = (token, callback) => {
return jwt.verify(token, utils.env.JWT_SECRET || 'YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG', callback);
};
function checkToken(req, res, next) {
const token = req.get('token');
if (!token) {
return res.json({
code: '0001',
message: 'token is required',
data: null
});
}
verifyToken(token, (err, data) => {
if (err) {
return res.json({
code: '0002',
message: 'token is invalid',
data: null
});
}
req.tokenData = data;
next();
})
}
module.exports = {
generateToken,
verifyToken
};

138
routes/utils/db_utils.js Normal file
View File

@ -0,0 +1,138 @@
const sqlite3 = require('sqlite3').verbose();
class sqlite3DB {
constructor(databaseName, errorFunc = null) {
if (!databaseName) {
throw new Error('Database name is required.');
}
this.errorFunc = errorFunc || (err => {
console.error(`sql: ${err.SQL}\nerror: ${err.message}`);
});
this.db = new sqlite3.Database(databaseName, (err) => {
if (err) {
console.error(`Failed to open database: ${err.message}`);
}
});
}
runSql(sql, data, callback) {
if (data == null) {
data = undefined;
}
this.db.run(sql, data, (err) => {
if (err) {
err.SQL = sql;
this.errorFunc(err);
if (typeof callback === 'function') {
callback(err);
}
}
});
}
runSerialize(sql, data, callback) {
this.db.serialize(() => {
this.runSql(sql, data, callback);
});
}
prepareSql(sql, callback) {
return this.db.prepare(sql, (stmt, err) => {
if (err) {
err.SQL = sql;
this.errorFunc(err);
if (typeof callback === 'function') {
callback(err);
}
return;
}
return stmt;
});
}
createTable(tableName, columnDefinitions, callback) {
const columns = columnDefinitions.map(column => `${column.name} ${column.type}`).join(', ');
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`;
this.db.serialize(() => {
this.runSql(sql, null, callback);
});
}
insertData(tableName, data, callback) {
if (!Array.isArray(data)) {
data = [data];
}
const columns = Object.keys(data[0]).join(', ');
const values = Object.keys(data[0]).map(() => '?').join(', ');
const sql = `INSERT INTO ${tableName} (${columns}) VALUES (${values})`;
this.db.serialize(() => {
const stmt = this.prepareSql(sql, callback);
data.forEach((item) => {
stmt.run(Object.values(item), (err) => { if (err) {
err.SQL = sql;
this.errorFunc(err);
if (typeof callback === 'function') {
callback(err);
}
} });
});
stmt.finalize((err) => { if(err) {
err.SQL = sql;
this.errorFunc(err);
if (typeof callback === 'function') {
callback(err);
}
} });
});
}
selectData(tableName, columns, where, callback) {
const sql = `SELECT ${columns} FROM ${tableName}
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
return this.db.all(sql, [], (err, rows) => {
if (err) {
err.SQL = sql;
this.errorFunc(err);
if (typeof callback === 'function') {
callback(err);
}
}
if (typeof callback === 'function') {
callback(undefined, rows);
} else {
return rows;
}
});
}
updateData(tableName, _data, where, callback) {
var data = null;
if (Array.isArray(_data)) {
data = _data[0];
} else {
data = _data;
}
const columns = Object.keys(data).map((values) => {
return `${values}=?`;
}).join(', ');
const sql = `UPDATE ${tableName} SET ${columns}
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
const values = Object.values(data);
this.runSql(sql, values, callback);
}
deleteData(tableName, where, callback) {
const sql = `DELETE FROM ${tableName}
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
this.runSql(sql, callback);
}
closeConnection() {
this.db.close((err) => { if (err) {
err.SQL = 'ERROR close connection'
this.errorFunc('ERROR db close', err);
}});
}
}
module.exports = sqlite3DB;

36
server.js Normal file
View File

@ -0,0 +1,36 @@
const express = require('express');
const path = require('path');
require('dotenv').config();
env = process.env;
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(require('cookie-parser')());
app.use(express.static(path.join(__dirname, 'static')));
app.use('/', require('./routes/index.js'));
app.use('/users', require('./routes/users.js'));
app.use('/files', require('./routes/files.js'));
app.use('/api', require('./routes/api.js'));
// catch 404 and forward to error handler
app.use(function(req, res, next) {
res.status(404);
res.end('<h1>404 Not Found</h1>');
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
console.log(err);
// render the error page
res.status(err.status || 500);
res.end('<h1>error</h1>');
});
module.exports = app;

File diff suppressed because one or more lines are too long

9894
static/css/anchor/main.css Normal file

File diff suppressed because it is too large Load Diff

1
static/css/anchor/vendor/aos.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
/*!
* Bootstrap Table of Contents v1.0.0 (http://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */nav[data-toggle=toc] .nav>li>a{display:block;padding:4px 20px;font-size:15px;font-weight:500;color:#767676}nav[data-toggle=toc] .nav>li>a:focus,nav[data-toggle=toc] .nav>li>a:hover{padding-left:19px;color:#563d7c;text-decoration:none;background-color:transparent;border-left:1px solid #563d7c}nav[data-toggle=toc] .nav-link.active,nav[data-toggle=toc] .nav-link.active:focus,nav[data-toggle=toc] .nav-link.active:hover{padding-left:18px;font-weight:700;color:#563d7c;background-color:transparent;border-left:2px solid #563d7c}nav[data-toggle=toc] .nav-link+ul{display:none;padding-bottom:10px}nav[data-toggle=toc] .nav .nav>li>a{padding-top:1px;padding-bottom:1px;padding-left:30px;font-size:14px;font-weight:400}nav[data-toggle=toc] .nav .nav>li>a:focus,nav[data-toggle=toc] .nav .nav>li>a:hover{padding-left:29px}nav[data-toggle=toc] .nav .nav>li>.active,nav[data-toggle=toc] .nav .nav>li>.active:focus,nav[data-toggle=toc] .nav .nav>li>.active:hover{padding-left:28px;font-weight:500}nav[data-toggle=toc] .nav-link.active+ul{display:block}

273
static/css/anchor/vendor/prism.css vendored Normal file
View File

@ -0,0 +1,273 @@
/*
Name: Duotone Light
Author: Simurai, adapted from DuoTone themes for Atom (http://simurai.com/projects/2016/01/01/duotone-themes)
Conversion: Bram de Haan (http://atelierbram.github.io/Base2Tone-prism/output/prism/prism-base2tone-morning-light.css)
Generated with Base16 Builder (https://github.com/base16-builder/base16-builder)
*/
code[class*="language-"],
pre[class*="language-"] {
font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace;
font-size: 13px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
background: #faf8f5;
color: #728fcb;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #333;
color:#ccc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #333;
color:#ccc;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #b6ad9a;
}
.token.punctuation {
color: #b6ad9a;
}
.token.namespace {
opacity: .7;
}
.token.tag,
.token.operator,
.token.number {
color: #063289;
}
.token.property,
.token.function {
color: #b29762;
}
.token.tag-id,
.token.selector,
.token.atrule-id {
color: #2d2006;
}
code.language-javascript,
.token.attr-name {
color: #896724;
}
code.language-css,
code.language-scss,
.token.boolean,
.token.string,
.token.entity,
.token.url,
.language-css .token.string,
.language-scss .token.string,
.style .token.string,
.token.attr-value,
.token.keyword,
.token.control,
.token.directive,
.token.unit,
.token.statement,
.token.regex,
.token.atrule {
color: #728fcb;
}
.token.placeholder,
.token.variable {
color: #93abdc;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #2d2006;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #896724;
}
.token.entity {
cursor: help;
}
pre > code.highlight {
outline: .4em solid #896724;
outline-offset: .4em;
}
/* overrides color-values for the Line Numbers plugin
* http://prismjs.com/plugins/line-numbers/
*/
.line-numbers .line-numbers-rows {
border-right-color: #ece8de;
}
.line-numbers-rows > span:before {
color: #cdc4b1;
}
/* overrides color-values for the Line Highlight plugin
* http://prismjs.com/plugins/line-highlight/
*/
.line-highlight {
background: rgba(45, 32, 6, 0.2);
background: -webkit-linear-gradient(left, rgba(45, 32, 6, 0.2) 70%, rgba(45, 32, 6, 0));
background: linear-gradient(to right, rgba(45, 32, 6, 0.2) 70%, rgba(45, 32, 6, 0));
}
/* JUST SOME STYLES FOR PRE CODES */
code { font-size: 14px;
display: inline-block;
line-height: 1.5;}
.code-toolbar {position: relative;}
.code-toolbar .toolbar-item a {
position: absolute;
top: -10px;
right: -10px;
color: #ffffff;
background:#333;
padding: 0 8px;
border-radius: 30px;
font-size: 13px;
cursor: pointer;
}
.code-toolbar .toolbar-item a:hover, .code-toolbar .toolbar-item a:focus { background: #ff5684;color:#fff;}
#docsarea .alert-orange { color:rgba(255,255,255,0.83);}
#docsarea .alert-orange code {color:#b73b3b;}
#docsarea .alert-orange a {color:inherit; border-bottom:1px dashed;}
ul.ascii, ul.ascii ul { margin-left: 0; padding-left: 0; list-style: none; }
ul.ascii li { margin: 0; padding: 0; }
/* level 1 */
ul.ascii > li::before { content: ""; }
/* level 2 */
ul.ascii > li > ul > li::before { content: "├──\00a0"; }
ul.ascii > li > ul > li:last-child::before { content: "└──\00a0"; }
/* level 3 */
ul.ascii > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0└──\00a0"; }
/* level 4 */
ul.ascii > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
/* level 5 */
ul.ascii > li > ul > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
/* level 6 */
ul.ascii > li > ul > li > ul > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }

0
static/css/main.css Normal file
View File

154
static/fetch-wrapper.js Normal file
View File

@ -0,0 +1,154 @@
class FetchWrapper {
constructor(baseURL, options = {}, contentTypeMappings = {}, onError = console.error) {
this.baseURL = baseURL;
this.contentTypeMappings = contentTypeMappings;
this.defaultContentType = 'application/json';
this.onError = onError;
this.defaultOptions = {
...{
method: 'GET',
headers: new Headers({
'Content-Type': this.defaultContentType
}),
mode: 'cors', // 指定是否允许跨域请求默认为cors
cache: 'default', // 请求的缓存模式
credentials: 'same-origin', // 请求的凭据模式默认同源请求时发送cookies
parseResponseHandler: {
'application/json': response => response.json(),
'text/*': response => response.text(),
'image/*': response => response.blob(),
'application/octet-stream': response => response.blob(),
'*/*': response => response.arrayBuffer(), // 默认处理程序
},
...options
}
};
}
async fetch(url, options = {}) {
// 合并默认选项与传入的options
const mergedOptions = { ...this.defaultOptions, ...options };
// 根据URL自动设置Content-Type如果有匹配的映射
const matchedMapping = Object.entries(this.contentTypeMappings).find(([pattern, type]) => url.endsWith(pattern));
if (matchedMapping && !mergedOptions.headers.has('Content-Type')) {
mergedOptions.headers['Content-Type'] = matchedMapping[1];
}
// 根据请求方法处理数据
if (['POST', 'PUT', 'PATCH'].includes(mergedOptions.method)) {
if (typeof mergedOptions.body === 'object') {
mergedOptions.body = JSON.stringify(mergedOptions.body);
// 如果Content-Type未设置或默认为application/json则需要在这里设置
if (!mergedOptions.headers.has('Content-Type')) {
mergedOptions.headers.set('Content-Type', 'application/json');
}
} else if (mergedOptions.body !== null && typeof mergedOptions.body !== 'string') {
throw new Error('非GET请求时, body必须是一个对象或字符串');
}
} else if (mergedOptions.method === 'GET' && mergedOptions.body) {
console.warn('GET请求不支持发送请求体, 已忽略提供的body参数');
delete mergedOptions.body;
}
url = new URL(url, this.baseURL).href;
return fetch(url, mergedOptions)
.then(async response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
// console.warn(`HTTP warning! status: ${response.status}`);
}
const contentType = response.headers.get('content-type').split(';')[0].trim();
const parseResponseHandler = this.getParseResponseHandler(contentType);
const responseData = await parseResponseHandler(response);
return formatResponse(response, responseData);
})
.catch(error => {
error.options = mergedOptions;
error.url = url;
error.timestamp = Date.now();
error.onFunction = 'fetch';
this.onError(error.message);
throw error;
});
}
getParseResponseHandler(contentType) {
if (contentType.startsWith('text/')) {
return this.defaultOptions.parseResponseHandler['text/*'] || this.defaultOptions.parseResponseHandler['*/*'];
}
if (contentType.startsWith('image/')) {
return this.defaultOptions.parseResponseHandler['image/*'] || this.defaultOptions.parseResponseHandler['*/*'];
}
return this.defaultOptions.parseResponseHandler[contentType] || this.defaultOptions.parseResponseHandler['*/*'];
}
async fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000) {
let remainingAttempts = maxRetries;
const delays = [];
for (let i = 0; i < maxRetries; i++) {
delays.push(retryDelayBaseMs * Math.pow(2, i));
}
const attemptFetch = async (retryIndex = 0) => {
try {
return await this.fetch(url, options);
} catch (error) {
if (remainingAttempts > 1) {
console.log(`请求失败,剩余重试次数:${remainingAttempts - 1}`);
setTimeout(() => attemptFetch(retryIndex + 1), delays[retryIndex]);
remainingAttempts--;
return;
} else {
this.onError(error.message);
throw error;
}
}
};
return attemptFetch();
}
// 可以为不同的HTTP方法提供便捷的方法
async post(url, body, options = {}) {
return this.fetch(url, { ...options, method: 'POST', body });
}
async get(url, options = {}) {
return this.fetch(url, { ...options, method: 'GET' });
}
async put(url, body, options = {}) {
return this.fetch(url, { ...options, method: 'PUT', body });
}
async patch(url, body, options = {}) {
return this.fetch(url, { ...options, method: 'PATCH', body });
}
async delete(url, options = {}) {
return this.fetch(url, { ...options, method: 'DELETE' });
}
};
function formatResponse(response, data) {
return {
status: response.status,
message: response.statusText,
data,
};
}
// 检查是否在Node.js环境中
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
// Node.js环境使用CommonJS
module.exports = FetchWrapper;
} else if (typeof window !== 'undefined') {
// 浏览器环境,直接暴露到全局作用域(不推荐,但简单示例)
// window.FetchWrapper = FetchWrapper;
}

View File

@ -0,0 +1,136 @@
# FetchWrapper 类使用文档
**概述:**
`FetchWrapper` 是一个基于 `fetch API` 的封装类用于简化网络请求操作并提供默认选项、Content-Type 自动映射、错误处理和重试机制等功能。
## 构造函数
````javascript
new FetchWrapper(baseURL, contentTypeMappings = {}, defaultOptions = {}, onError = console.error)
````
- **baseURL**:基础 URL 字符串,所有相对路径的请求都会基于此 URL 进行拼接。
- **contentTypeMappings**:对象,键为文件扩展名或通配符模式,值为对应的 Content-Type当请求 URL 匹配时自动设置 Content-Type 头部。
- **defaultOptions**:对象,定义了发起请求时的默认选项,例如:
````javascript
{
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
// 其他默认配置项...
}
````
- **onError**:错误回调函数,当发生网络请求错误时触发,默认为 `console.error`
## 主要方法
### formatResponse(response, data)
````javascript
formatResponse(response, data)
````
- 格式化响应对象和数据,返回一个统一格式的对象。
### fetch(url, options = {})
````javascript
async fetch(url, options = {})
````
- 发起 HTTP 请求,参数说明:
- **url**:请求 URL。
- **options**:覆盖默认选项的对象,可自定义请求方法、头部、请求体等信息。
### getParseResponseHandler(contentType)
````javascript
getParseResponseHandler(contentType)
````
- 根据响应内容类型Content-Type获取相应的解析器函数。
### fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000)
````javascript
async fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000)
````
- 发起带有重试机制的 HTTP 请求,在请求失败时会按照指数退避策略进行重试。
- **maxRetries**最大重试次数默认为3次。
- **retryDelayBaseMs**首次重试延迟时间的基础单位默认为1000毫秒。
### post(url, body, options = {})
### get(url, options = {})
### put(url, body, options = {})
### patch(url, body, options = {})
### delete(url, options = {})
这些是针对不同HTTP方法的便捷调用方法。
## 示例使用:
````javascript
const wrapper = new FetchWrapper('https://api.example.com', {
'.json': 'application/json',
}, {
credentials: 'include',
});
async function fetchData() {
try {
const response = await wrapper.get('/data.json');
console.log(response.data);
} catch (error) {
wrapper.onError(error);
}
}
//使用带重试机制的 POST 请求
async function postDataWithRetry() {
const body = { key: 'value' };
const options = { headers: { 'X-Custom-Header': 'value' } };
try {
const response = await wrapper.fetchWithRetry('/post-data', { body }, 5, 2000);
console.log(response.data);
} catch (error) {
wrapper.onError(error);
}
}
````
以上JavaScript代码已用Markdown代码块包裹可以直接复制并粘贴到您的项目中。通过创建一个 `FetchWrapper` 实例,您可以利用其便捷的方法来发起网络请求,并根据需要灵活定制请求选项以及实现重试功能。
当您在fetch-wrapper.js文件中使用export default class FetchWrapper {...}导出FetchWrapper类时您可以在其他JavaScript模块中这样导入
````javascript
// 导入 FetchWrapper 类
import FetchWrapper from './fetch-wrapper';
// 创建一个实例
const wrapper = new FetchWrapper('https://api.example.com', {}, { credentials: 'include' });
// 使用这个实例发起请求
async function fetchData() {
try {
const response = await wrapper.get('/data.json');
console.log(response.data);
} catch (error) {
wrapper.onError(error);
}
}
fetchData();
````
这里,./fetch-wrapper是相对路径表示FetchWrapper类所在的模块位置。根据实际项目结构您需要调整这个路径以便正确指向fetch-wrapper.js文件。
formatResponse(response, data) {
return {
status: response.status,
message: response.statusText,
data,
};
}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/ico/qq.png">
<title>author-qq</title>
</head>
<body>
<div>
<img src="/pic/author-link/qq-link.png" alt="QQ: 24502666535">
</div>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/ico/wechat.png">
<title>author-wechat</title>
</head>
<body>
<div>
<img src="/pic/author-link/wechat-link.png" alt="微信: ZZYXYZ2450266535">
</div>
</body>
</html>

View File

@ -0,0 +1,36 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1440 126" style="enable-background:new 0 0 1440 126;" xml:space="preserve">
<path class="bg-light" d="M685.6,38.8C418.7-11.1,170.2,9.9,0,30v96h1440V30C1252.7,52.2,1010,99.4,685.6,38.8z"></path>
</svg>
<footer class="bg-light pb-5 pt-5">
<div class="container">
<div class="col">
<div class="row">
<a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
法律声明 </a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
隐私政策</a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
使用条款</a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
关于</a>
</div>
<div class="row">
<div>
联系作者:
</div>
<a href="./html/autor-link/author-linking-wechat.html" target="_blank" class="">
<img class="rounded-circle shadow" src="/pic/ico/wechat.png" alt="wechat-link" width="25">
</a>
<a href="./html/autor-link/author-linking-qq.html" target="_blank" class="">
<img class="rounded-circle shadow" src="/pic/ico/qq.png" alt="qq-link" width="25">
</a>
</div>
<div class="row">
<a href="https://beian.miit.gov.cn" target="_blank" class="">
<img class="rounded-circle shadow" src="/pic/ico/beian.png" alt="beian-pic" width="25">
赣ICP备2023009103号
</a>
</div>
<div class="row">
<div class="d-block mt-3 mb-3 text-muted">Copyright <i class="fas fa-copyright text-black"></i> 2024 zzy. All rights reserved.</div>
</div>
</div>
</div>
</footer>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,22 @@
<!-- The modal itself -->
<div class="modal fade" id="_zzy_base_modal_" tabindex="-1" role="dialog" aria-labelledby="base_modal_center_title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header border-0">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="pb-4 pr-4 pl-4 text-center">
<h3 class="heading mt-4 text-black">服务条款</h3>
<p class="text-black">该网页仅用于学习,不用于其他用途,点击确认则默认同意。若不同意,请点击网站下方[法律声明]阅读详细内容。</p>
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-info btn-round confirm-action">Accept</button>
<button type="button" class="btn btn-info btn-round" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>zzyxyz</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<div class="include" src="/html/base/base_modal.html"></div>
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<!-- <div id="linking-cards" class="row"></div> -->
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>

View File

@ -0,0 +1,24 @@
<nav class="navbar navbar-dark bg-secondary navbar-expand-md">
<!-- Navbar content -->
<a class="navbar-brand" href="/">
<img src="/pic/ico/Home.png" width="30" height="30" class="d-inline-block align-top" alt="">
zzyxyz
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<div class="navbar-nav mr-auto">
<a class="nav-item nav-link active" href="/">Home <span class="sr-only">(current)</span></a>
<a class="nav-item nav-link disabled" href="#">Features</a>
<a class="nav-item nav-link disabled" href="#">Pricing</a>
<a class="nav-item nav-link disabled" href="#">Disabled</a>
</div>
<div class="navbar-nav ml-auto">
<form class="form-inline">
<input class="form-control" type="search" placeholder="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>zzyxyz</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<div class="include" src="/html/base/base_modal.html"></div>
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<div id="linking-cards" class="row"></div>
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
<script>
$(function() {
$.getJSON("/api/json/cs-study-link-content.json", function(data) {
const default_item = data.default;
data.datas.forEach(i => {
item = {...default_item, ...i}
const card = `
<div class="card shadow-sm border-0 col-md-4">
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
<div class="card-body">
<h5 class="card-title">${item.title}</h5>
<p class="card-text text-muted">${item.intro}</p>
<p class="card-text text-muted">${item.msg}</p>
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
</div>
</div>`;
$("#linking-cards").append(card);
});
});
});
</script>
</body>
</html>

38
static/html/legal.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>zzyxyz</title>
<link rel="icon" href="/ico/Home.png">
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<h1>网站法律声明</h1>
<p>欢迎使用本网站!以下是本平台的法律声明,请仔细阅读。通过访问和使用本网站及其提供的各项服务,即表示您已阅读、理解并同意接受以下全部条款和条件。</p>
<h1>该网页仅用于学习,不用于其他用途</h1>
</body>
</html>
<!--
---
title: 法律声明
---
# # 网站法律声明
欢迎使用本网站!以下是本平台的法律声明,请仔细阅读。通过访问和使用本网站及其提供的各项服务,即表示您已阅读、理解并同意接受以下全部条款和条件。
## 一、版权与知识产权声明
...
## 二、服务条款
...
## 三、隐私政策
...
## 四、免责声明
...
... -->

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>zzyxyz</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<div class="include" src="/html/base/base_modal.html"></div>
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<div id="linking-cards" class="row"></div>
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
<script>
$(document).ready( function() {
$.getJSON("/api/json/office-link.json", function(data) {
const default_item = data.default;
data.datas.forEach(i => {
item = {...default_item, ...i}
const card = `
<div class="card shadow-sm border-0 col-md-4">
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
<div class="card-body">
<h5 class="card-title">${item.title}</h5>
<p class="card-text text-muted">${item.intro}</p>
<p class="card-text text-muted">${item.msg}</p>
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
</div>
</div>`;
$("#linking-cards").append(card);
});
})
});
</script>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>zzyxyz</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<div class="include" src="/html/base/base_modal.html"></div>
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<div id="linking-cards" class="row"></div>
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
<script>
$(document).ready( function() {
$.getJSON("/api/json/self-study-link.json", function(data) {
const default_item = data.default;
data.datas.forEach(i => {
item = {...default_item, ...i}
item.ex_url = ''
item.other_urls.forEach(j => {
item.ex_url += `<a href="${j}" class="btn btn-info btn-round" target="${false ? '_blank' : ''}">${j}</a>`
})
const card = `
<div class="card shadow-sm border-0 col-md-4">
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
<div class="card-body">
<h5 class="card-title">${item.title}</h5>
<p class="card-text text-muted">${item.intro}</p>
<p class="card-text text-muted">${item.msg}</p>
${item.ex_url}
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
</div>
</div>`;
$("#linking-cards").append(card);
});
})
});
</script>
</body>
</html>

75
static/index.html Normal file
View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="./css/anchor/main.css" rel="stylesheet"/>
<link href="./css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>zzyxyz</title>
<link rel="icon" href="./pic/ico/Home.png">
</head>
<body>
<div class="include" src="./html/base/base_modal.html"></div>
<div class="include" src="./html/base/base_nav.html"></div>
<!-- wavy header -->
<div class="jumbotron jumbotron-lg jumbotron-fluid mb-0 pb-3 bg-secondary position-relative">
<div class="container-fluid text-white h-50">
<div class="d-lg-flex align-items-center justify-content-between text-center pl-lg-5">
<div class="col pt-4 pb-4">
<h1 class="display-3">Home</h1>
<h5 class="font-weight-light mb-4">This is Home Page</h5>
<a href="#" class="btn btn-lg btn-outline-white btn-round">Learn more</a>
</div>
</div>
</div>
</div>
<svg style="-webkit-transform:rotate(-180deg); -moz-transform:rotate(-180deg); -o-transform:rotate(-180deg); transform:rotate(-180deg); margin-top: -1px;" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewbox="0 0 1440 126" style="enable-background:new 0 0 1440 126;" xml:space="preserve">
<path class="bg-secondary" d="M685.6,38.8C418.7-11.1,170.2,9.9,0,30v96h1440V30C1252.7,52.2,1010,99.4,685.6,38.8z"/>
</svg>
<main class="container pb-5 pt-5">
<a href="/users/login" class="btn btn-outline-primary btn-round">Test Login</a>
<div id="linking-cards" class="row">
</div>
</main>
<div class="include" src="./html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="./js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="./js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="./js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="./js/anchor/functions.js" type="text/javascript"></script>
<script src="./js/base_script.js" type="text/javascript"></script>
<script>
$(function() {
$.getJSON("/api/json/index-content.json", function(data) {
const default_item = data.default;
data.datas.forEach(i => {
item = {...default_item, ...i}
const card = `
<div class="card shadow-sm border-0 col-md-4">
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
<div class="card-body">
<h5 class="card-title">${item.title}</h5>
<p class="card-text text-muted">${item.intro}</p>
<p class="card-text text-muted">${item.msg}</p>
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
</div>
</div>`;
$("#linking-cards").append(card);
});
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,11 @@
jQuery( document ).ready(function() {
$(window).scroll(function(){
$('.topnav').toggleClass('bg-white navbar-light shadow-sm scrollednav py-0', $(this).scrollTop() > 50);
});
$('#modal_newsletter').on('show.bs.modal', function () {
$('.downloadzip')[0].click();
});
});

1
static/js/anchor/vendor/aos.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
/*!
* Bootstrap Table of Contents v1.0.0 (http://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
!function(e){"use strict";window.Toc={helpers:{findOrFilter:function(e,t){var n=e.find(t);return e.filter(t).add(n).filter(":not([data-toc-skip])")},generateUniqueIdBase:function(t){var n=e(t).text(),r=n.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g,"-");return r||t.tagName.toLowerCase()},generateUniqueId:function(e){for(var t=this.generateUniqueIdBase(e),n=0;;n++){var r=t;if(n>0&&(r+="-"+n),!document.getElementById(r))return r}},generateAnchor:function(e){if(e.id)return e.id;var t=this.generateUniqueId(e);return e.id=t,t},createNavList:function(){return e('<ul class="nav navbar-nav"></ul>')},createChildNavList:function(e){var t=this.createNavList();return e.append(t),t},generateNavEl:function(t,n){var r=e('<a class="nav-link"></a>');r.attr("href","#"+t),r.text(n);var a=e("<li></li>");return a.append(r),a},generateNavItem:function(t){var n=this.generateAnchor(t),r=e(t),a=r.data("toc-text")||r.text();return this.generateNavEl(n,a)},getTopLevel:function(e){for(var t=1;t<=6;t++){var n=this.findOrFilter(e,"h"+t);if(n.length>1)return t}return 1},getHeadings:function(e,t){var n="h"+t,r=t+1,a="h"+r;return this.findOrFilter(e,n+","+a)},getNavLevel:function(e){return parseInt(e.tagName.charAt(1),10)},populateNav:function(e,t,n){var r,a=e,i=this;n.each(function(n,o){var s=i.generateNavItem(o),u=i.getNavLevel(o);u===t?a=e:r&&a===e&&(a=i.createChildNavList(r)),a.append(s),r=s})},parseOps:function(t){var n;return n=t.jquery?{$nav:t}:t,n.$scope=n.$scope||e(document.body),n}},init:function(e){e=this.helpers.parseOps(e),e.$nav.attr("data-toggle","toc");var t=this.helpers.createChildNavList(e.$nav),n=this.helpers.getTopLevel(e.$scope),r=this.helpers.getHeadings(e.$scope,n);this.helpers.populateNav(t,n,r)}},e(function(){e('nav[data-toggle="toc"]').each(function(t,n){var r=e(n);Toc.init(r)})})}(jQuery);

File diff suppressed because one or more lines are too long

2
static/js/anchor/vendor/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
static/js/anchor/vendor/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

41
static/js/anchor/vendor/prism.js vendored Normal file

File diff suppressed because one or more lines are too long

1
static/js/anchor/vendor/share.js vendored Normal file

File diff suppressed because one or more lines are too long

20
static/js/base_script.js Normal file
View File

@ -0,0 +1,20 @@
$(".include").each(function() {
if (!!$(this).attr("src")) {
var $includeObj = $(this);
$(this).load($(this).attr("src"), function(html) {
$includeObj.after(html).remove(); //加载的文件内容写入到当前标签后面并移除当前标签
})
}
});
// FIX ME
setTimeout(function() {
const $_zzy_base_modal_ = $('#_zzy_base_modal_');
$_zzy_base_modal_.modal('show');
$_zzy_base_modal_.on('click', '.confirm-action', function() {
$_zzy_base_modal_.modal('hide');
console.log('用户已同意条款,页面开始加载...');
// 在这里执行其他初始化操作
});
}, 1000);

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
static/pic/ico/Home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
static/pic/ico/beian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/pic/ico/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/pic/ico/wechat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

51
utils.js Normal file
View File

@ -0,0 +1,51 @@
const path = require('path');
const fs = require('fs');
require('dotenv').config();
utils = {};
utils.ROOT_PATH = path.resolve(__dirname);
utils.VIEWS_PATH = path.resolve(utils.ROOT_PATH, 'views');
utils.DATA_PATH = path.resolve(utils.ROOT_PATH, '.data');
utils.DATA_JSON_PATH = path.resolve(utils.DATA_PATH, 'json');
utils.DATA_DB_PATH = path.resolve(utils.DATA_PATH, 'db');
utils.DATA_CONFIG_PATH = path.resolve(utils.DATA_PATH, 'config');
utils.DATA_FILE_PATH = path.resolve(utils.DATA_PATH, 'file');
utils.ROUTES_PATH = path.resolve(utils.ROOT_PATH, 'routes');
utils.RT_DB_PATH = path.resolve(utils.ROUTES_PATH, 'db');
utils.RT_API_PATH = path.resolve(utils.ROUTES_PATH, 'api');
utils.RT_UTILS_PATH = path.resolve(utils.ROUTES_PATH, 'utils');
// 检查并创建路径
[utils.DATA_PATH, utils.DATA_JSON_PATH, utils.DATA_DB_PATH, utils.DATA_CONFIG_PATH, utils.DATA_FILE_PATH].forEach((p) => {
if (!fs.existsSync(p)) {
fs.mkdirSync(p, { recursive: true });
}
});
utils.json = {
success: {
code: '0000',
msg: 'success',
data: null
},
error: {
code: '0001',
msg: 'unknown error',
data: null
},
token_is_invalid: {
code: '0002',
msg: 'token invalid',
data: null
},
user_is_valid: {
code: '0100',
msg: 'login error',
data: null
},
}
utils.path = path;
utils.fs = fs;
utils.env = process.env;
module.exports = utils;

95
views/users/login.html Normal file
View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>Login</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<!-- <div class="include" src="/html/base/base_modal.html"></div> -->
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<!-- <div id="linking-cards" class="row"></div> -->
<div class="container col-md-5">
<form id="login-form">
<div class="form-group">
<input type="text" class="form-control input-round" id="username" placeholder="Enter Username" required="">
<small id="usernameHelp" class="form-text text-muted">We'll never share your username and password with anyone else.</small>
</div>
<div class="form-group">
<input type="password" class="form-control input-round" id="password" placeholder="Password" required="">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-success btn-round">Login</button>
<button type="button" class="btn btn-success btn-round disabled">Register</button>
<p class="message"></p>
</form>
</div>
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
<script src="/fetch-wrapper.js" type="text/javascript" charset="utf-8"></script>
<script>
document.getElementById('login-form').addEventListener('submit', function(event) {
event.preventDefault();
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
// 这里仅做简单的输入非空验证,实际应用中需要后端配合进行账号密码验证
if (!username || !password) {
document.querySelector('.message').textContent = 'Both username and password are required.';
} else {
document.querySelector('.message').textContent = '';
// 假设这里发起一个异步登录请求
login(username, password);
}
});
async function login(username, password) {
try {
// 这里应该调用后端API进行登录操作以下仅为模拟示例
const wrapper = new FetchWrapper(window.location.origin);
const response = await wrapper.post('/api/users/login', {
username: username,
password: password
})
if (response.data.code === '0000') {
// 登录成功,跳转到主页
document.cookie = `token=${response.data.data};`;
window.location.href = '/users';
} else {
// 登录失败,给出错误提示
document.querySelector('.message').textContent =
'Login failed.登录失败...' + response.data.code +
'请检查用户名和密码是否正确, 或联系管理员添加新账号';
}
} catch (error) {
console.error('Error fetching:', error);
}
}
</script>
</body>
</html>

View File

86
views/users/users.html Normal file
View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
<!-- Fonts -->
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
<!-- CSS -->
<link href="/css/anchor/main.css" rel="stylesheet"/>
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
<title>Users Page</title>
<link rel="icon" href="/pic/ico/Home.png">
</head>
<body>
<!-- <div class="include" src="/html/base/base_modal.html"></div> -->
<div class="include" src="/html/base/base_nav.html"></div>
<main class="container pb-5 pt-5">
<!-- <div id="linking-cards" class="row"></div> -->
<h1>Users</h1>
<form id="login-form">
<!-- remote-data-processing-display -->
<h2>Remote Data Processing Display</h2>
<div class="form-group">
<input type="input" class="form-control input-round" id="input0" placeholder="input0" required="">
</div>
<div class="form-group">
<input type="input" class="form-control input-round" id="input1" placeholder="input1" required="">
</div>
<div class="form-group">
<input type="input" class="form-control input-round" id="input2" placeholder="input2" required="">
</div>
<button type="submit" class="btn btn-success btn-round">submit</button>
<div class="form-group">
<input type="input" class="form-control input-round" id="output" placeholder="output" required="">
</div>
</form>
</main>
<div class="include" src="/html/base/base_footer.html"></div>
<!-- Javascript -->
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/anchor/functions.js" type="text/javascript"></script>
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
<script src="/fetch-wrapper.js"></script>
<script>
document.getElementById('login-form').addEventListener('submit', function(event) {
event.preventDefault();
var input = [];
input[0] = document.getElementById('input0').value;
input[1] = document.getElementById('input1').value;
input[2] = document.getElementById('input2').value;
submit(input);
});
async function submit(input) {
try {
// 这里应该调用后端API进行登录操作以下仅为模拟示例
const wrapper = new FetchWrapper(window.location.origin);
const response = await wrapper.post('/api/users/_rdpd', {
input: input
})
if (response.data.code === '0000') {
document.querySelector('.message').textContent = `${response.data.data}`;
} else {
document.querySelector('.message').textContent =
'Login failed.登录失败...' + response.data.code;
window.location.href = '/users/login';
}
} catch (error) {
console.error('Error fetching:', error);
}
}
</script>
</body>
</html>