first commit

This commit is contained in:
编码猿
2024-09-27 02:06:13 +08:00
commit 852d94fbb9
36760 changed files with 3274413 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
@charset "UTF-8";
body,ul,ol,li {
margin: 0;
padding: 0;
list-style: none;
}
.main {
width: 520px;
height: 280px;
border: 1px solid #000;
margin: 30px auto;
position: relative;
overflow: hidden;
}
.main ul li,
.circular-list li {
float: left;
}
.main ul {
width: 2100px;
height: 280px;
position: absolute;
transition: all 0.6s;
}
.main .page-action {
position: absolute;
width: 100%;
top: 50%;
margin-top: -20px;
}
.action-left,.action-right {
width: 40px;
height: 40px;
background: #00000087;
color: #fff;
text-align: center;
line-height: 35px;
font-size: 26px;
border-radius: 100%;
}
.action-left{
float: left;
margin-left: 10px;
}
.action-right {
float: right;
margin-right: 10px;
}
.circular-list {
position: absolute;
bottom: 10px;
left: 50%;
margin-left: -36px;
}
.circular-list li {
width: 13px;
height: 13px;
background: #fff;
border-radius: 100%;
margin-right: 5px;
}
.active {
background: #d0c300 !important;
}

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body, ul {
margin: 0;
padding: 0;
}
.main {
width: 520px;
height: 280px;
overflow: hidden;
}
li {
list-style: none;
float: left;
}
.listimg {
width: 2000px;
perspective: 1200px;
transform-style: preserve-3d;
backface-visibility: hidden;
}
li:nth-child(1) {
transform:rotateY(-23deg);
}
li:nth-child(2) {
transform:rotateY(43deg);
}
</style>
</head>
<body>
<div class="main">
<ul class="listimg">
<li><img src="./img/1.jpg" alt=""></li>
<li><img src="./img/3.jpg" alt=""></li>
</ul>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>js 轮播</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div class="main">
<!-- 轮播图片部分 -->
<ul style="left: 0" class="broadcast-wrapper">
<li><img src="./img/1.jpg" alt=""></li>
<li><img src="./img/2.jpg" alt=""></li>
<li><img src="./img/3.jpg" alt=""></li>
<li><img src="./img/4.jpg" alt=""></li>
</ul>
<div class="page-action">
<!-- 上一页 -->
<div class="action-left"> < </div>
<!-- 下一页 -->
<div class="action-right"> > </div>
</div>
<ol class="circular-list"></ol>
</div>
</body>
<script src="./js/MyBroadcast.js"></script>
<script>
new Broadcast({
time: 3000,
pageaction: {
// 上一页按钮class
prev: 'action-left',
// 下一页按钮class
next: 'action-right'
}
})
</script>
</html>

View File

@@ -0,0 +1,161 @@
(function(bom, dom){
// 插件的使用配置
let configDefaults = {
time: 3000,
pageaction: {
prev: 'action-left', // 上一页按钮class
next: 'action-right' // 下一页按钮class
}
}
// 插件程序的配置
let config = {
timer: null,
lengths: document.getElementsByClassName('broadcast-wrapper')[0].getElementsByTagName("li").length,
fatherWrapper: 'broadcast-wrapper',
circular: 'circular-list',
width() {
return document.getElementsByClassName(this.fatherWrapper)[0].getElementsByTagName("li")[0].getElementsByTagName("img")[0].width
},
returnFatherWrapper() {
return document.getElementsByClassName(this.fatherWrapper)[0]
},
totalWidth() {
return parseInt(`-${(this.lengths - 1) * this.width()}`)
},
returnCircular() {
return document.getElementsByClassName(this.circular)[0]
},
createdCircular() {
var html = ""
for (let i = 0; i < this.lengths; i++) {
html+= `<li class="${i == 0 ? 'active' : ''}"></li>`
}
this.returnCircular().innerHTML = html
}
}
var index = 1;
function Broadcast(options) {
if(arguments.length == 0) {
console.error('请传入默认配置')
}else {
configDefaults.time = options.time || configDefaults.time
configDefaults.pageaction.prev = options.pageaction.prev || configDefaults.pageaction.prev
configDefaults.pageaction.next = options.pageaction.next || configDefaults.pageaction.next
this.init()
}
}
Broadcast.prototype = {
// 函数的入口
init(){
config.createdCircular()
this.prevClick()
this.nextClick()
this.autoPlay()
this.autoChangeCircular()
document.getElementsByClassName('main')[0].onmouseover = () => this.stopPlay()
document.getElementsByClassName('main')[0].onmouseout = () => this.autoPlay()
},
// 上一页按钮点击事件
prevClick() {
this.$(configDefaults.pageaction.prev)[0].onclick = () => {
index--
if(index <= 0) {
index = config.lengths
}
this.clickChange()
this.transition(config.width())
}
},
// 下一页按钮点击事件
nextClick() {
this.$(configDefaults.pageaction.next)[0].onclick = () => {
index++
if(index > config.lengths) {
index = 1
}
this.clickChange()
this.transition(parseInt(`-${config.width()}`))
}
},
// 过渡动画的设置
transition(px) {
var offsetLeft = parseInt(config.returnFatherWrapper().style.left),
ow = offsetLeft + px;
if(ow >= config.totalWidth()) {
config.returnFatherWrapper().style.left = `${ow}px`
}else {
config.returnFatherWrapper().style.left = `0px`
}
if(ow > 0) {
config.returnFatherWrapper().style.left = `${config.totalWidth()}px`
}
},
clickChange() {
var Circularli = this.$(config.circular)[0].getElementsByTagName("li")
var Wrapperli = this.$(config.fatherWrapper)[0].getElementsByTagName("li")
for(let i = 0;i<Circularli.length;i++){
if(Circularli[i].className == "active"){
Circularli[i].className = ""
}
}
Circularli[index-1].className="active"
},
autoChangeCircular() {
// 小圆点
var Circularli = this.$(config.circular)[0].getElementsByTagName("li")
var Wrapperli = this.$(config.fatherWrapper)[0].getElementsByTagName("li")
var self = this
for(let i = 0;i<Circularli.length;i++){
Circularli[i].onmouseover = function () {
for(var j = 0;j<Circularli.length;j++){
if(Circularli[j].className == "active"){
Circularli[j].className = ""
}
}
Circularli[i].className="active"
config.returnFatherWrapper().style.left = `${parseInt(`-${i*config.width()}`)}px`
}
}
},
// 自动轮播
autoPlay() {
config.timer = setInterval(() => {
this.$(configDefaults.pageaction.next)[0].onclick()
}, configDefaults.time)
},
// 停止播放
stopPlay() {
clearInterval(config.timer)
},
// 获取元素
$(className){
return document.getElementsByClassName(className)
}
}
bom.Broadcast = Broadcast
})(window, document)

View File

@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}

View File

@@ -0,0 +1,29 @@
var async = require("async");
var request = require('superagent');
var urls = [
"https://www.npmjs.com/package/async",
"https://caolan.github.io/async/",
"https://www.baidu.com/",
"https://www.tslang.cn/",
"http://www.jtthink.com/"
]
async.mapLimit(urls, 2, async function(value, key) {
const response = await fetchs(value)
return response
}, (err, results) => {
if (err) throw err
// results is now an array of the response bodies
console.log(results)
})
async function fetchs(url){
return new Promise(function(resolve,reject){
request
.get(url)
.end((err, res) => {
resolve(res.text)
});
})
}

View File

@@ -0,0 +1,22 @@
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
// sudo siege -c 50 http://localhost:3000
if (cluster.isMaster) {
console.log('[master] ' + "start master...");
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('listening', function (worker, address) {
console.log('[master] ' + 'listening: worker' + worker.id + ',pid:' + worker.process.pid + ', Address:' + address.address + ":" + address.port);
});
} else if (cluster.isWorker) {
console.log('[worker] ' + "start worker ..." + cluster.worker.id);
http.createServer(function (req, res) {
console.log('worker'+cluster.worker.id);
res.end('worker'+cluster.worker.id+', PID: '+process.pid);
}).listen(3000);
}

View File

@@ -0,0 +1,11 @@
var gulp = require("gulp"),
babel = require("gulp-babel"),
watch = require('gulp-watch'),
uglify = require('gulp-uglify');
gulp.task('default', function () {
return watch('src/es6.js')
.pipe(babel())
.pipe(uglify())
.pipe(gulp.dest('dist'));
});

View File

@@ -0,0 +1,48 @@
<container>
<row>
<columns small="12">
<h1>Hi, Elijah Baily</h1>
<p class="lead">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi impedit sapiente delectus molestias quia.</p>
<img src="http://placehold.it/548x300" alt="">
<callout class="primary">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam assumenda, praesentium qui vitae voluptate dolores. <a href="#">Click it!</a></p>
</callout>
<h2>Title Ipsum <small>This is a note.</small></h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi repellat, harum. Quas nobis id aut, aspernatur, sequi tempora laborum corporis cum debitis, ullam, dolorem dolore quisquam aperiam! Accusantium, ullam, nesciunt. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ducimus consequuntur commodi, aut sed, quas quam optio accusantium recusandae nesciunt, architecto veritatis. Voluptatibus sunt esse dolor ipsum voluptates, assumenda quisquam.</p>
<button class="large secondary" href="#">Click Me!</button>
</columns>
</row>
<wrapper class="secondary">
<spacer size="16"></spacer>
<row>
<columns small="12" large="6">
<h5>Connect With Us:</h5>
<menu class="vertical" align="left">
<item style="text-align: left;" href="#">Twitter</item>
<item style="text-align: left;" href="#">Facebook</item>
<item style="text-align: left;" href="#">Google +</item>
</menu>
</columns>
<columns small="12" large="6">
<h5>Contact Info:</h5>
<p>Phone: 408-341-0600</p>
<p>Email: <a href="mailto:foundation@zurb.com">foundation@zurb.com</a></p>
</columns>
</row>
</wrapper>
<center>
<menu>
<item href="#">Terms</item>
<item href="#">Privacy</item>
<item href="#">Unsubscribe</item>
</menu>
</center>
</container>

View File

@@ -0,0 +1,44 @@
const nodemailer = require('nodemailer');
const fs = require('fs');
const path = require('path');
// https://segmentfault.com/a/1190000012251328
// 163 邮箱授权码 lb714500
// qq 邮箱授权码 ogmqticmpbnoeaje
let transporter = nodemailer.createTransport({
// 服务器地址
host: 'smtp.163.com',
// 使用了内置传输发送邮件 查看支持列表https://nodemailer.com/smtp/well-known/
service: '163',
// SMTP 端口
port: 465,
// 使用SSL
secureConnection: true,
auth: {
user: 'lb2271608011@163.com',
// 这里密码不是qq密码是smtp授权码
pass: 'lb714500',
}
});
let mailOptions = {
// "发件人名称" <发件人邮箱>
from: '"163邮箱给qq发邮件" <lb2271608011@163.com>',
// 接收人邮箱
to: '2271608011@qq.com',
// 邮件标题
subject: 'Hello',
// 发送text或者html格式
// text: 'Hello world?',
html: fs.createReadStream(path.resolve(__dirname, 'mail.html')) // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log(info);
});

View File

@@ -0,0 +1,30 @@
{
"name": "11-16",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"s": "node-dev src/http.js && gulp watch",
"fz": "node-dev cluster.js > server.log"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"async": "^2.6.1",
"gulp": "^3.9.1",
"gulp-htmlmin": "^5.0.1",
"mysql": "^2.16.0",
"nodemailer": "^4.7.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"gulp-babel": "^7.0.1",
"gulp-minify": "^3.1.0",
"gulp-uglify": "^3.0.1",
"gulp-watch": "^5.0.1",
"superagent": "^4.0.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>关于我</h1>
</body>
</html>

View File

@@ -0,0 +1 @@
var zhangsan = () => 5+21

View File

@@ -0,0 +1,14 @@
const http = require('http');
const { render } = require('./utils');
const url = require('url');
const util = require('util');
var index = function(req, res){
//render('test',res)
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
res.end("你查询的参数是:"+(url.parse(req.url,true)).query.id)
}
// 熟悉 url 模块还有其他什么方法
http.createServer(index).listen(8080)

View File

@@ -0,0 +1,4 @@
var { demo,demo2 } = require('./utils')
demo()
demo2("nihazo")

View File

@@ -0,0 +1,11 @@
class utils {
private name: "";
constructor(name){
this.name = name;
}
public getNames(params?:string) : string {
return params+this.name;
}
}
(new utils("zhangsan")).getNames("你好")

View File

@@ -0,0 +1,28 @@
const mysql = require('mysql');
// 第一步 配置数据库连接
var pool = mysql.createPool({
host : 'localhost',
user : 'root',
password : 'lb714500',
database : 'shop'
});
// 连接数据库
// connection.connect();
// 第二部 配置查询数据库
var sql = "INSERT INTO shop_commodity(commodity_name) VALUES('大米科技')"
var params = ['4']
pool.query(sql,params, function (error, results, fields) {
if (error) throw error;
console.log(results);
});
// 目标实现一个小说爬虫
// 1: gulp 进行文件监听使用babel转换es6代码为es5js代码压缩
// 2: mysql数据库存储爬到的数据
// 3: 使用nodejs的web框架 express(koa2) 并改造
// 4: 使用nodejs nodemailer 向我们自己发送邮件,提示小说有没有更新,到底更新几章

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h2>我是本地的html文件</h2>
</body>
</html>

View File

@@ -0,0 +1,33 @@
const fs = require('fs');
// exports.demo = function () {
// console.log("我是demo方法")
// }
// module.exports = function () {
// console.log("我是demo方法")
// }
// module.exports.demo = function () {
// console.log("我是demo方法")
// }
function render(filename,res) {
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
res.end((fs.readFileSync(`${filename}.html`)).toString())
}
function demo2() {
console.log("我是demo2方法")
}
// module.exports = {
// demo2(ddd) {
// console.log(ddd)
// },
// demo() {
// console.log("我是demo方法")
// }
// }
module.exports = { render , demo2}

View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
var gulp = require('gulp');
var babel = require("gulp-babel");
var watch = require('gulp-watch');
gulp.task('babel', function () {
return watch('src/**/*.js', function () {
gulp
.src('src/**/*.js')
.pipe(babel())
.pipe(gulp.dest('dist/'));
});
});

View File

@@ -0,0 +1,32 @@
{
"name": "18-11-23",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": " concurrently \"gulp babel\" \"pm2 start -i max ./src/bin/www.js\" ",
"stop": "pm2 stop www",
"start": "pm2 start -i max ./src/bin/www.js",
"dep": " concurrently \"gulp babel\" \"node-dev ./src/bin/www.js\" \" node-dev ./src/timer.js \" "
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"express-load-router": "^2.1.4",
"http-errors": "~1.6.2",
"mysql": "^2.16.0",
"node-schedule": "^1.3.0",
"nodemailer": "^4.7.0",
"pug": "^2.0.3"
},
"devDependencies": {
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"cheerio": "^1.0.0-rc.2",
"concurrently": "^4.1.0",
"gulp": "^3.9.1",
"gulp-babel": "^8.0.0-beta.2",
"gulp-watch": "^5.0.1",
"request": "^2.88.0"
}
}

View File

@@ -0,0 +1,41 @@
var createError = require('http-errors');
var express = require('express');
var loadRouter = require('express-load-router');
var path = require('path');
var cookieParser = require('cookie-parser');
var app = express();
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
loadRouter(app, path.join(__dirname, 'controllers'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// catch 404 and forward to error handler
// app.use(function(req, res, next) {
// next(createError(404));
// });
// 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 : {};
// // render the error page
// res.status(err.status || 500);
// });
module.exports = app;

View File

@@ -0,0 +1,59 @@
var app = require('../app');
var debug = require('debug')('18-11-23:server');
var http = require('http');
var server;
/**
* Get port from environment and store in Express.
*/
var port = 3000;
app.set('port', port);
server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
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('Listening on ' + bind);
}

View File

@@ -0,0 +1,55 @@
module.exports = {
BaseUrl: 'http://www.xbiquge.la',
BookUrl: '/15/15409/index.html',
ApiStatusCode: {
success: {
status: 200,
data: null
},
errors: {
status: 404,
data: []
}
},
MysqlConfig: {
connectionLimit : 10,
host : 'localhost',
user : 'root',
password : 'lb714500',
database : 'chongzi'
},
Sql: {
index: {
insert: "INSERT chongzi_text (text_title, text_url) VALUES (?,?)",
select: "SELECT * FROM chongzi_text"
}
},
mailOption: {
send: {
// 服务器地址
host: 'smtp.163.com',
// 使用了内置传输发送邮件 查看支持列表https://nodemailer.com/smtp/well-known/
service: '163',
// SMTP 端口
port: 465,
// 使用SSL
secureConnection: true,
auth: {
user: 'lb2271608011@163.com',
// 这里密码不是qq密码是smtp授权码
pass: 'lb714500',
}
},
accept: {
// "发件人名称" <发件人邮箱>
from: '"小说更新程序" <lb2271608011@163.com>',
// 接收人邮箱
to: '2271608011@qq.com',
// 邮件标题
subject: null,
// 发送text或者html格式
// text: 'Hello world?',
html: null // html body
}
}
}

View File

@@ -0,0 +1,40 @@
const db = require("./../../model");
const request = require('request');
const config = require('./../../config');
const cheerio = require('cheerio')
const utils = require('./../../utils')
exports.index = {
method: 'GET',
params: [':id'],
async handler(req, res) {
// 爬取网页并解析dom
const $ = utils.Dom(await utils.http(utils.ReturnUrl()))
var data = [], webSitelengths = $('#list dl dd').length; // 从网页上爬取到的数据长度(这个数据长度会大于数据库的数据[小说更新])
// 数据库中已存在的小说数据长度
var MysqlLength = (await db.query({ sql: config.Sql.index.select, par: [] })).data.length
// 对比爬到的数据和数据库中的数据,如果爬到的数据有更新,
// 那么将新章节存到数据库,没有就从数据库读数据显示到页面上
if(webSitelengths > MysqlLength){
for(var i = MysqlLength;i<webSitelengths;i++) {
// 暂存数据到数组
data.push({ title: $('#list dl dd').eq(i).find('a').text(), url: config.BaseUrl+$('#list dl dd').eq(i).find('a').attr("href") })
// 数据库存数据
await db.query({
sql: config.Sql.index.insert,
par: [ $('#list dl dd').eq(i).find('a').text(), config.BaseUrl+$('#list dl dd').eq(i).find('a').attr("href")]
})
}
console.log(utils.SendMail(data))
}
res.json(await db.query({ sql: config.Sql.index.select, par: [] }))
}
};

View File

@@ -0,0 +1,5 @@
exports.info = (req, res) => {
res.json({
title: "user"
});
};

View File

@@ -0,0 +1,4 @@
ul
each obj in data
li
a(href='' +obj.url+ '')= obj.title

View File

@@ -0,0 +1,24 @@
const mysql = require('mysql');
const config = require('../config');
class Mysql {
constructor() {
this.pool = mysql.createPool(config.MysqlConfig)
}
query (params) {
return new Promise((resolve,reject) => {
this.pool.query(params.sql, params.par ? params.par : [], function (error, results, fields) {
if (error) {
throw error;
reject(error)
} else {
config.ApiStatusCode.success.data = results
results.length == 0 ? resolve(config.ApiStatusCode.errors) : resolve(config.ApiStatusCode.success);
}
})
})
}
}
module.exports = new Mysql()

View File

@@ -0,0 +1,11 @@
var schedule = require("node-schedule");
const request = require('request');
var rule = new schedule.RecurrenceRule();
rule.minute = 42;
var j = schedule.scheduleJob(rule, function(){
request('http://localhost:3000/home/api?id=2', function (error, response, body) {
console.log(body);
});
});

View File

@@ -0,0 +1,52 @@
const config = require("../config")
const request = require('request');
const cheerio = require('cheerio');
const nodemailer = require('nodemailer');
const fs = require('fs');
const path = require('path');
const pug = require('pug');
module.exports = {
ReturnUrl() {
return config.BaseUrl+config.BookUrl
},
http (url) {
return new Promise((resolve,reject) => {
request(url, function (error, response, body) {
if(error) throw error;
resolve(body)
})
})
},
Dom(html) {
return cheerio.load(html)
},
SendMail(data) {
const compiledFunction = pug.compileFile(path.join(__dirname, '../email.pug'));
config.mailOption.accept.subject = "您关注的小说 <牧神记> 有新更新啦?"
config.mailOption.accept.html = compiledFunction({ data: data })
let transporter = nodemailer.createTransport(config.mailOption.send);
transporter.sendMail(config.mailOption.accept, (error, info) => {
if (error) {
return console.log(error);
}
return info;
});
}
}

View File

@@ -0,0 +1,21 @@
第一天:
1使用nodejs的web开发框架express 生成了一个基本的nodejs web项目
2使用了 express的自动路由加载模块express-load-router实现对路由的自动加载无需手动进行繁琐的配置
3使用 mysql 模块,负责与数据库进行链接,并封装数据库链接 使用es6 语法进行。
第二天:
1建立mysql数据库(用来存放爬虫爬到的小说数据)继续完善mysql类库文件的封装
2request 发送 http请求获取网页源码
3: 使用 cheerio 模块解析网页源码拿到我们想要的数据
4: 返回数据给前端
第三天:
1配置好babel + gulp + watch 对代码进行转换
2完成数据库的Promise封装实现小说数据入库
3node-schedule实现定时爬取小说网站对比新旧章节后将最新章节存入数据库然后将最新章节邮件通知给用户
4通过两种方法pm2cluster 实现接口负载均衡
5手工配置pug渲染邮件模板可自己美化pug代码达到精美邮件模板
6concurrently 实现并发执行多个任务

View File

@@ -0,0 +1,12 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}

View File

@@ -0,0 +1,9 @@
# root = true
# [*]
# charset = utf-8
# indent_style = space
# indent_size = 2
# end_of_line = lf
# insert_final_newline = true
# trim_trailing_whitespace = true

View File

@@ -0,0 +1,4 @@
/build/
/config/
/dist/
/*.js

View File

@@ -0,0 +1,29 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

14
历届学生/王凯/18-12-06/.gitignore vendored Executable file
View File

@@ -0,0 +1,14 @@
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

View File

@@ -0,0 +1,10 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

View File

@@ -0,0 +1,21 @@
# demo
> A Vue.js project
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

View File

@@ -0,0 +1,41 @@
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@@ -0,0 +1,54 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,101 @@
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

View File

@@ -0,0 +1,22 @@
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

View File

@@ -0,0 +1,92 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'~': resolve('src'),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

View File

@@ -0,0 +1,95 @@
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

View File

@@ -0,0 +1,145 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@@ -0,0 +1,7 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})

View File

@@ -0,0 +1,76 @@
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: false,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

View File

@@ -0,0 +1,4 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport"/>
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="black" name="apple-mobile-web-app-status-bar-style"/>
<meta content="telephone=no" name="format-detection"/>
<title>demo</title>
<!-- 引入自适应类库不建议在main.js里引入 -->
<script src="//unpkg.com/vue-ydui/dist/ydui.flexible.js"></script>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1,75 @@
{
"name": "demo",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "bmy <2271608011@qq.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vue-ydui": "^1.2.6"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

View File

@@ -0,0 +1,46 @@
<template>
<div id="app">
<yd-navbar fixed title="首页"></yd-navbar>
<div class="content">
<img src="./assets/logo.png">
<ul>
<li><a href="#/index">首页</a></li>
<li><a href="#/about">关于我</a></li>
</ul>
<router-view/>
</div>
<yd-tabbar fixed>
<yd-tabbar-item title="首页" link="/index">
<yd-icon name="home" slot="icon" size="0.54rem"></yd-icon>
</yd-tabbar-item>
<yd-tabbar-item title="购物车" link="#">
<yd-icon name="shopcart-outline" slot="icon" size="0.54rem"></yd-icon>
</yd-tabbar-item>
<yd-tabbar-item title="个人中心" link="/about">
<yd-icon name="ucenter-outline" slot="icon" size="0.54rem"></yd-icon>
</yd-tabbar-item>
<yd-tabbar-item title="图片" link="#">
<img slot="icon" style="height: 25px;" src="http://static.ydcss.com/ydui/img/logo.png">
</yd-tabbar-item>
</yd-tabbar>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
.content {
margin-top:50px;
margin-bottom: 54px;
}
</style>

View File

@@ -0,0 +1,67 @@
import Vue from 'vue'
import axios from 'axios'
import { Loading, Toast } from 'vue-ydui/dist/lib.rem/dialog';
axios.defaults.baseURL = 'http://localhost:3000';
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
Loading.open('很快加载好了')
return config;
}, function (error) {
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
switch (response.data.status){
case 200:
Loading.close()
return response;
break;
default:
Toast({
mes: '网络请求失败',
timeout: 1500,
icon: 'error'
})
break;
}
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default {
get (params) {
return new Promise(function(resolve,reject){
axios.get(params.url, {
params: params.parameter
})
.then((success) => {
resolve(success.data)
})
.catch((err) => {
reject(err)
})
})
},
post () {
return new Promise(function(resolve,reject){
axios.post(params.url,params.parameter)
.then((success) => {
resolve(success.data)
})
.catch((err) => {
reject(err)
})
})
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,13 @@
<template>
<div class="about">
<h2>关于我</h2>
</div>
</template>
<script>
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,33 @@
<template>
<div class="hello">
<ul>
<li v-for="(item,index) in BookList" :key="index">
<a :href="item.text_url">{{item.text_title}}</a>
</li>
</ul>
</div>
</template>
<script>
export default {
created () {
this.getIndexData()
},
data () {
return {
BookList: []
}
},
methods: {
async getIndexData() {
this.BookList = (await this.$http.get({ url: this.$config.api.home.index, parameter: {}})).data
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,7 @@
export default {
api: {
home: {
index: '/home/index'
}
}
}

View File

@@ -0,0 +1,25 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import http from './api/index'
import YDUI from 'vue-ydui'
import 'vue-ydui/dist/ydui.rem.css'
import config from './config'
Vue.use(YDUI);
Vue.prototype.$config = config
Vue.prototype.$http = http
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})

View File

@@ -0,0 +1,21 @@
import Vue from 'vue'
import Router from 'vue-router'
import IndexComponents from '~/components/index/index'
import AboutComponents from '~/components/about/index'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/index',
name: 'index',
component: IndexComponents
},
{
path: '/about',
name: 'about',
component: AboutComponents
}
]
})

View File

View File

@@ -0,0 +1,73 @@
/*
Navicat Premium Data Transfer
Source Server : 本地数据库
Source Server Type : MySQL
Source Server Version : 80013
Source Host : localhost:3306
Source Schema : Novel
Target Server Type : MySQL
Target Server Version : 80013
File Encoding : 65001
Date: 20/01/2019 23:47:49
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for novel_collection
-- ----------------------------
DROP TABLE IF EXISTS `novel_collection`;
CREATE TABLE `novel_collection` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`collection_user` varchar(255) NOT NULL COMMENT '用户的id',
`collection_bookid` varchar(255) DEFAULT NULL COMMENT '用户收藏的小说id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of novel_collection
-- ----------------------------
BEGIN;
INSERT INTO `novel_collection` VALUES (1, '1', '25/25991/');
INSERT INTO `novel_collection` VALUES (2, '1', '29/29000/');
INSERT INTO `novel_collection` VALUES (3, '1', '27/27613/');
INSERT INTO `novel_collection` VALUES (4, '1', '25/25952/');
INSERT INTO `novel_collection` VALUES (5, '1', '26/26966/');
INSERT INTO `novel_collection` VALUES (6, '1', '25/25155/');
INSERT INTO `novel_collection` VALUES (7, '1', '26/26472/');
INSERT INTO `novel_collection` VALUES (8, '2', '27/27613/');
COMMIT;
-- ----------------------------
-- Table structure for novel_user
-- ----------------------------
DROP TABLE IF EXISTS `novel_user`;
CREATE TABLE `novel_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_phone` varchar(11) DEFAULT NULL,
`user_pwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`user_ip` varchar(15) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of novel_user
-- ----------------------------
BEGIN;
INSERT INTO `novel_user` VALUES (1, '15155145354', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (2, '18305520337', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (3, '18305520334', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (4, '15155145352', '25f9e794323b453885f5181f1b624d0b', '112.32.95.213');
INSERT INTO `novel_user` VALUES (5, '18305520338', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (6, '18305520339', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (7, '15155145350', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (8, '13003085278', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (9, '18305520336', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
INSERT INTO `novel_user` VALUES (10, '15155145359', 'e10adc3949ba59abbe56e057f20f883e', '112.32.95.213');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,12 @@
const express = require('express')
const router = express.Router()
const http = require('./http')
const utils = require('./utils')
const config = require('./config')
const mysql = require('yn-mysql-utils')
const md5 = require('md5')
let db = new mysql(config.mysql)
module.exports = { express , router, http , utils, config, db, md5}

View File

@@ -0,0 +1,73 @@
var createError = require('http-errors');
var express = require('express');
var loadRouter = require('express-load-router');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var config = require('./config');
var session = require('express-session')
var app = express();
var classRouter = require('./controller/class');
var IndexRouter = require('./controller/index');
var InfoRouter = require('./controller/info');
var likeActionRouter = require('./controller/user/likeAction');
var loginActionRouter = require('./controller/user/loginAction');
// 后端允许前端跨域
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// 使用 session 中间件
app.use(session({
secret : 'secret', // 对session id 相关的cookie 进行签名
resave : false,
saveUninitialized: true, // 是否保存未初始化的会话
cookie : {
maxAge : 1000 * 60 * 10, // 设置 session 的有效时间,单位毫秒
},
}));
app.use(`${config.ApiBaseUrl}`, classRouter)
app.use(`${config.ApiBaseUrl}`, IndexRouter)
app.use(`${config.ApiBaseUrl}`, InfoRouter)
app.use(`${config.ApiBaseUrl}`, loginActionRouter)
app.use(`${config.ApiBaseUrl}`, likeActionRouter)
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// 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 : {};
res.json({
status: 404,
message: err.message
})
});
module.exports = app;

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('xiaoshuo-backend:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var 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() {
console.log("后端api接口运行在http://127.0.0.1:3000/")
}

View File

@@ -0,0 +1,66 @@
module.exports = {
ApiBaseUrl: '/api/json/1.0/',
getIpAddres: 'http://2019.ip138.com/ic.asp',
mysql: {
host : '127.0.0.1',
user : 'root',
password : 'lb714500',
database : 'Novel',
connectionLimit : 10
},
TargetUrl: {
baseUrl: 'http://www.xbiquge.la/',
class: {
title: 'fenlei/',
fantasy: {
id: '1_',
url: 'xuanhuanxiaoshuo/'
},
though: {
id: '2_',
url: 'xiuzhenxiaoshuo/'
},
urban: {
id: '3_',
url: 'dushixiaoshuo/'
},
passThrough: {
id: '4_',
url: 'chuanyuexiaoshuo/'
},
game: {
id: '5_',
url: 'wangyouxiaoshuo/'
},
scienceFiction: {
id: '6_',
url: 'kehuanxiaoshuo/'
},
hotlist: {
url: 'paihangbang/'
}
}
},
sql: {
index: {
selectData: 'SELECT * FROM novel_book WHERE book_key = ? AND book_page = ?',
selectSave: 'SELECT * FROM novel_book WHERE book_key = ? AND book_page = ? AND book_hot = ?',
inrset: 'INSERT INTO novel_book(book_title,book_introduce,book_img,book_author,book_time,book_url,book_hot,book_page,book_key) VALUES (?,?,?,?,?,?,?,?,?)',
delete: 'DELETE FROM novel_book WHERE book_key = ? AND book_hot = 1'
},
user: {
getUserInfo: 'SELECT * FROM novel_user WHERE id = ?',
checkUser: 'SELECT * FROM novel_user WHERE user_phone = ?',
login: 'SELECT id,user_phone,user_ip FROM novel_user WHERE user_phone = ? AND user_pwd = ?',
userReg: 'INSERT INTO novel_user(user_phone,user_pwd,user_ip) VALUES (?,?,?)',
checkBook: 'SELECT * FROM novel_collection WHERE collection_user = ? AND collection_bookid = ?',
insertBook: 'INSERT INTO novel_collection(collection_user,collection_bookid) VALUES (?,?)',
selectBook: 'SELECT * FROM novel_collection WHERE collection_user = ?',
deleteBook: 'DELETE FROM novel_collection WHERE collection_user = ? AND collection_bookid = ?'
}
},
apiStatus: {
status: 200,
data: null
}
}

View File

@@ -0,0 +1,70 @@
const { http, utils, db, config,router } = require("../../_");
// http://127.0.0.1:3000/api/json/1.0/class?class=1&page=1
router.get('/class', async function(req,res,next){
// 请求数据 and 返回dom数据
let $ = await utils.AnalyticalData({ class:req.query.class, page: req.query.page } ),
left = [],
father = null;
/**
* 存在 action 爬取每个分类下面的更多小说
*/
if(req.query.hasOwnProperty("action")){
// http://127.0.0.1:3000/api/json/1.0/class?class=2&page=1&action=more
father = $("#newscontent .l ul li")
father.each(function(){
left.push({
title: $(this).find(".s2 a").text(),
url: $(this).find(".s2 a").attr("href")
})
})
let newList = []
left.forEach(async (item) => {
let $ = await utils.AnalyticalData({ siteUrl: item.url } )
newList.push({
title: $('#maininfo #info').find('h1').text(),
imgURL: $('#sidebar #fmimg').find('img').attr('src'),
introduce: $('#maininfo #intro').children().last().text(),
author: ($('#maininfo #info').find('h1+p').text()).substring(7),
url: item.url
})
if(newList.length == 30) {
return res.json(newList)
}
})
/**
* 不存在 action 爬取每个分类小面的推荐小说
*/
} else {
// http://127.0.0.1:3000/api/json/1.0/class?class=2
father = $("#hotcontent .ll .item")
father.each(async function() {
left.push({
title: $(this).find("dl dt a").text(),
imgURL: $(this).find(".image img").attr("src"),
author: $(this).find("dl dt span").text(),
introduce: $(this).find("dl dd").text(),
url: $(this).find(".image a").attr("href")
})
})
return res.json(left)
}
})
module.exports = router

View File

@@ -0,0 +1,8 @@
const { http, utils, db, config,router } = require("../../_");
router.get('/index', async function(req, res, next) {
console.log('index');
})
module.exports = router

View File

@@ -0,0 +1,66 @@
const { http, utils, db, config,router } = require("../../_");
/**
* 小说详情页面
* 地址http://127.0.0.1:3000/api/json/1.0/info?id=28/28039/
*/
router.get('/info', async (req, res, next) => {
let id = req.query.id,
action = req.query.action == undefined ? null: req.query.action,
InfoData = {};
try {
let $ = utils.returnDOM(await http.get({ url: `${config.TargetUrl.baseUrl}${id}` }));
InfoData.title = $("#info").find("h1").text()
InfoData.author = $("#info p").eq(0).text()
InfoData.updateTime = $("#info p").eq(2).text()
InfoData.imgUrl = $("#fmimg img").attr("src")
InfoData.introduce = $("#intro p").eq(1).text()
InfoData.oldChapter = []
let father = $("#list dl dd a");
father.each(function (item) {
InfoData.oldChapter.push({
chapterTitle: $(this).text(),
chapterUrl: $(this).attr("href"),
})
})
if(action == null) {
InfoData.newChapter = {}
InfoData.newChapter.newTitle = $("#info p").eq(3).find("a").text()
InfoData.newChapter.newUrl = $("#info p").eq(3).find("a").attr("href")
utils.returnJson(200,InfoData,res)
} else {
res.json(InfoData)
}
}catch (e) {
console.log(e)
}
})
/**
* 阅读小说接口
* 地址http://127.0.0.1:3000/api/json/1.0/read?id=/28/28039/13633573.html
*/
router.get('/read', async (req, res, next) => {
let id = req.query.id;
try {
let $ = utils.returnDOM(await http.get({ url: `${config.TargetUrl.baseUrl}${id}` }));
utils.returnJson(200,{
title: $(".bookname h1").text(),
content: (unescape($("#content").html().replace(/&#x/g,'%u').replace(/;/g,''))).replace(/%uA0%uA0%uA0%uA0|\n/gi,'&nbsp;&nbsp;')
},res)
}catch (e) {
console.log(e)
}
})
module.exports = router

View File

@@ -0,0 +1,83 @@
const { http, utils, db, config,router,md5 } = require("../../_");
/**
* 检查用户是否已经添加过某本小说
*/
router.post('/user/checkbook', async function (req, res, next) {
let userid = req.body.userid,
bookid = req.body.bookid;
let Check = await db.Query({ sql: config.sql.user.checkBook, par: [ userid, bookid ] });
if(Check.length) {
utils.returnJson(200,"已收藏",res);
}else {
utils.returnJson(200,"未收藏",res);
}
});
/**
* 用户添加小说到书架
*/
router.post('/user/addbok', async function (req, res, next) {
let userid = req.body.userid,
bookid = req.body.bookid;
let add = await db.Query({ sql: config.sql.user.insertBook, par: [ userid, bookid ] });
if(add.message.length == 0) {
utils.returnJson(200,"添加成功",res);
}else {
utils.returnJson(404,"添加失败",res);
}
})
/**
* 获取用户书架中收藏的书
* http://127.0.0.1:3000/api/json/1.0/user/getbook?userid=1
*/
router.get('/user/getbook', async function (req, res, next) {
let userid = req.query.userid;
let getBook = await db.Query({ sql: config.sql.user.selectBook, par: [ userid ] }),
infoData = [],
keys = 0;
if(getBook.length ==0) {
utils.returnJson(404,"数据没有数据",res);
}else {
getBook.forEach(async (value , index) => {
infoData.push(await http.get({ url: `http://127.0.0.1:3000/api/json/1.0/info?action=back&id=${value.collection_bookid}` }));
infoData[keys].bookid = value.collection_bookid
keys++;
if(keys == getBook.length) {
utils.returnJson(200,infoData,res);
}
})
}
})
/**
* 获取用户书架中收藏的书
* http://127.0.0.1:3000/api/json/1.0/user/deletebook?uid=1&bid=26/26966/
*/
router.get('/user/deletebook', async function (req, res, next) {
let userid = req.query.uid,
bookid = req.query.bid;
let delbook = await db.Query({ sql: config.sql.user.deleteBook, par: [ userid, bookid ] });
if(delbook.message.length == 0) {
utils.returnJson(200,"删除成功",res);
}else {
utils.returnJson(404,"删除失败",res);
}
})
module.exports = router

View File

@@ -0,0 +1,65 @@
const { http, utils, db, config,router,md5 } = require("../../_");
/**
* 用户登录
*/
router.post('/user/login', async function (req, res, next) {
let UserName = req.body.name,
UserPwd = req.body.pwd,
IP = req.body.ip;
let UserInfo = await db.Query({ sql: config.sql.user.login, par: [ UserName, md5(UserPwd) ] });
if(UserInfo.length) {
req.session.userinfo = UserInfo;
utils.returnJson(200,UserInfo[0],res);
}else {
utils.returnJson(404,'账号和密码不匹配',res)
}
});
/**
* 注册账户
*/
router.post('/user/reg', async (req, res, next) => {
let userName = req.body.name,
UserPwd = req.body.pwd;
if(/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(userName)) {
if( (await db.Query({ sql: config.sql.user.checkUser, par: [userName] })).length ) {
utils.returnJson(404,'手机号已存在',res)
}else {
let userReg = await db.Query({ sql: config.sql.user.userReg, par: [userName, md5(UserPwd), await utils.GetIp()] })
req.session.userinfo = await db.Query({ sql: config.sql.user.getUserInfo, par: [userReg.insertId] })
utils.returnJson(200,'regSuccess',res)
}
}else {
utils.returnJson(404,'手机号格式不正确',res)
}
});
/**
* 退出登录
*/
router.post('/user/outLogin', async (req, res, next) => {
});
/**
* 检查用户是否登录 前端检查 + 后端session检查
*/
router.get('/user/checkLogin', async (req, res, next) => {
console.log(req.session.userinfo)
// if(req.session.userinfo.length) {
// utils.returnJson(200,'Logined',res)
// }else {
// utils.returnJson(404,'noLogin',res)
// }
});
/**
* 重置密码
*/
router.post('/user/restPwd', async (req, res, next) => {
});
module.exports = router

View File

@@ -0,0 +1,15 @@
# POST http://127.0.0.1:3000/api/json/1.0/user/reg HTTP/1.1
# content-type: application/json
# {
# "name": "15155145354",
# "pwd": "123456"
# }
POST http://127.0.0.1:3000/api/json/1.0/user/login HTTP/1.1
content-type: application/json
{
"name": "15155145354",
"pwd": "123456"
}

View File

@@ -0,0 +1,19 @@
const axios = require('axios');
const config = require('./../config');
// 配置爬虫的基地址
axios.defaults.baseURL = config.TargetUrl.baseUrl;
module.exports = {
get(params) {
return new Promise(function(resolve,reject){
axios.get(params.url)
.then( (success) => {
resolve(success.data);
})
.catch( (error) => {
reject(error);
});
})
}
}

View File

@@ -0,0 +1,21 @@
{
"name": "xiaoshuo-backend",
"version": "1.2.1",
"private": true,
"scripts": {
"dev": "node-dev ./bin/www"
},
"dependencies": {
"md5": "^2.2.1",
"axios": "^0.18.0",
"cheerio": "^1.0.0-rc.2",
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"express-load-router": "^2.1.4",
"express-session": "^1.15.6",
"http-errors": "~1.6.2",
"morgan": "~1.9.0",
"yn-mysql-utils": "^1.0.4"
}
}

View File

@@ -0,0 +1,38 @@
const config = require('../config')
const http = require('../http')
const cheerio = require('cheerio')
module.exports = {
async AnalyticalData(par) {
let urls = '';
if(par.hasOwnProperty('page')) {
urls = `${config.TargetUrl.class.title}${par.class}_${par.page}.html`
} else if(par.hasOwnProperty('siteUrl')){
urls = par.siteUrl
} else {
urls = config.TargetUrl.class.hotlist
}
// 网络请求获取 目标 页面的 html源码
return this.returnDOM(await http.get({ url: urls }))
},
returnDOM(data) {
return cheerio.load(data)
},
returnJson(status, data,res) {
config.apiStatus.status = status
config.apiStatus.data = data
res.json(config.apiStatus);
},
/**
* 返回客户端ip地址
* @returns {Promise<*|string>}
* @constructor
*/
async GetIp () {
let ipData = await http.get({ url: config.getIpAddres })
return ipData.match(/\[([\s\S]*?)\]/)[1]
}
}

View File

@@ -0,0 +1,54 @@
const { http, utils, db, config,router } = require("../../_");
// http://127.0.0.1:3000/api/json/1.0/class?class=1&page=1
router.get('/class', async function(req,res,next){
// 请求数据 and 返回dom数据
let $ = await utils.AnalyticalData({ class:req.query.class, page: req.query.page } ),
left = [],
father = null;
/**
* 存在 action 爬取每个分类下面的更多小说
*/
if(req.query.hasOwnProperty("action")){
// http://127.0.0.1:3000/api/json/1.0/class?class=2&page=1&action=more
father = $("#newscontent .l ul li")
father.each(function(){
left.push({
title: $(this).find(".s2 a").text(),
url: $(this).find(".s2 a").attr("href")
})
})
/**
* 不存在 action 爬取每个分类小面的推荐小说
*/
} else {
// http://127.0.0.1:3000/api/json/1.0/class?class=2
father = $("#hotcontent .ll .item")
// 插入数据之前先删除之前的数据
// await db.Query({ sql: config.sql.index.delete, par: [ req.query.class ] });
father.each(async function() {
left.push({
title: $(this).find("dl dt a").text(),
imgURL: $(this).find(".image img").attr("src"),
author: $(this).find("dl dt span").text(),
introduce: $(this).find("dl dd").text(),
url: $(this).find(".image a").attr("href")
})
})
}
return res.json(left)
})
module.exports = router

View File

@@ -0,0 +1,18 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}

View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@@ -0,0 +1,5 @@
/build/
/config/
/dist/
/*.js
/test/unit/coverage/

View File

@@ -0,0 +1,29 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

View File

@@ -0,0 +1,15 @@
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

View File

@@ -0,0 +1,10 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

View File

@@ -0,0 +1,27 @@
# xiaoshuo-fontend
> A Vue.js project
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
# run unit tests
npm run unit
# run all tests
npm test
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

View File

@@ -0,0 +1,41 @@
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@@ -0,0 +1,54 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

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