"images": "^3.0.2",
app.js
const koa = require('koa');
const Router = require('koa-router');
let koabody = require('koa-body');
const fs = require('fs')
const app = new koa();
const router = new Router();
const session = require('koa-session');
app.keys = ['some secret hurr'];
const path = require('path')
const static = require('koa-static');
//app.use(session(CONFIG, app));
const port = process.env.PORT || 5001;
router.get("/",async ctx => {
ctx.body = {msg:'Hello koa Interfaces'}
})
let Article = require('./model/Article');
let Comment = require('./model/Comment');
let User = require('./model/User');
router.get("/article/list",async ctx => {
const articles = await Article.findAll();
let rows = []
for(let i=0;i<articles.length;i++){
let row = articles[i].dataValues
console.log(row)
/*
let img = fs.readFileSync(row.id+'.base64','utf-8')
row.img = img
*/
rows.push(row)
}
console.log(rows)
ctx.body = rows
})
const mysqlApi = require('./model/mysqlApi')
const query = mysqlApi.query
router.post('/comment', async (ctx, next) => {
let row = ctx.request.body;
console.log(row)
let {articleid,content} = row
let sqlStr = `select * from comment where article_id = ${articleid} order by id asc LIMIT 1`
console.log(sqlStr)
let ret = await query( sqlStr )
console.log(ret)
console.log(ret.length)
let art ={}
let comment_no = 1
if(ret.length > 0){
comment_no = ret[0].comment_no+1
}
let tt = {
article_id:articleid,
content:content,
comment_no:comment_no
}
art = await Comment.create(tt)
ctx.body = art
})
router.get("/comment",async ctx => {
let request = ctx.request;
let req_query = request.query;
const id = req_query.id;
let sqlStr = `select * from comment where article_id = ${id} order by id asc `
console.log(sqlStr)
let ret = await query( sqlStr )
ctx.body = ret
})
router.get("/article",async ctx => {
let request = ctx.request;
let req_query = request.query;
const id = req_query.id;
const articles = await Article.findAll({where:{id:id}});
for(let i=0;i<articles.length;i++){
let row = articles[i].dataValues
console.log(row)
//let img = fs.readFileSync(row.id+'.base64','utf-8')
//row.img = img
//rows.push(row)
}
console.log(ctx.session.username)
console.log(id)
console.log(articles)
ctx.body = articles
})
let images = require("images");
router.post('/add', async (ctx, next) => {
let row = ctx.request.body;
console.log(row.title)
//console.log(row.img)
let tt = {
title:row.title,
content:row.content,
newsid:row.newsid
}
console.log(tt)
let art = await Article.create(tt)
let id = art.dataValues.id
let fileType = row.filetype;
//console.log(art.dataValues.id)
//fs.writeFileSync(art.dataValues.id+'.base64',row.img);
var base64Data = row.img.replace(/^data:image/w+;base64,/, "");
let img = new Buffer(base64Data, 'base64');
fs.writeFileSync(`${id}.${fileType}`,img);
images("11.png")
//加载图像文件
.size(180)
.save(`${id}_small.jpg`, {
quality : 50 //保存图片到文件,图片质量为50
});
//if(row.name == 'admin' && row.password == 'admin')
ctx.response.body = {code:0};
//ctx.response.body = 'hah';
});
app.use(async (ctx, next) => {
console.log(`Process ${ctx.request.method} ${ctx.request.url}...1`);
console.log( ctx.session)
if(ctx.request.method=='post')
if( undefined == ctx.session.sesStr ){
ctx.response.body = {code:-1,sessionConnect:-1};
return;
}
await next();
});
router.post('/login', async (ctx, next) => {
let row = ctx.request.body;
console.log( ctx.request)
console.log( row)
console.log(row.name)
let name = row.name
//if(row.name == 'admin' && row.password == 'admin')
const user = await User.findAll({where:{name:name}});
if(user.length > 0)
{
console.log(user)
if(user[0].passwd == row.password){
ctx.session.username = name;
ctx.session.id = user.id;
ctx.response.body = {code:0};
}
else
ctx.response.body = {code:-1};
}else{
ctx.response.body = {code:-1};
}
//ctx.response.body = 'hah';
});
router.get("/test",async ctx => {
ctx.body ="1"
})
// 配置静态web服务的中间件
//app.use(static(path.join(__dirname,'./images')));
app.use(static(__dirname+'/images'));
app.use(koabody({
multipart: true,
formidable: {
maxFileSize: 200*1024*1024 // 设置上传文件大小最大限制,默认2M
},
formLimit:20*1024*1024,
}));
app.keys = ['some secret hurr'];
const CONFIG = {
key: 'koa:sess', //cookie key (default is koa:sess)
maxAge: 3600000, // cookie的过期时间 maxAge in ms (default is 1 days)
overwrite: true, //是否可以overwrite (默认default true)
httpOnly: true, //cookie是否只有服务器端可以访问 httpOnly or not (default true)
signed: true, //签名默认true
rolling: false, //在每次请求时强行设置cookie,这将重置cookie过期时间(默认:false)
renew: false, //(boolean) renew session when session is nearly expired,
};
app.use(session(CONFIG, app));
//app.use(static(__dirname+'/images'));
app.use(router.routes()).use(router.allowedMethods);
app.listen(port,() =>{
console.log(`server started on ${port}!`)
})
let Sequelize = require('sequelize');
let mySequelize = require('./configdb');
var Article = mySequelize.define('article', {
id:{ //自增长id,主键,整形
type:Sequelize.INTEGER,
primaryKey: true,
autoIncrement:true
},
title: { //名字
type: Sequelize.STRING(100)
},
content: { //名字
type: Sequelize.STRING(500)
},
image_url: { //名字
type: Sequelize.STRING(100)
},
source: { //名字
type: Sequelize.STRING(50)
},
newsid: { //类型 在tab区别
type: Sequelize.STRING(50)
},
comment_count: { //类型 在tab区别
type: Sequelize.INTEGER
},
},{
timestamps: true,
freezeTableName: true,
});
Article.sync(); //创建表
module.exports = Article
login
<template> <div > <van-nav-bar title="登陆" left-text="返回" left-arrow @click-left="onClickLeft" @click-right="onClickRight" /> <van-cell-group class="user-group" style="margin-top:50px;margin-bottom:10px"> <van-cell title="没有注册?先去注册" style="color: rgba(69, 90, 100, 0.6);" @click="onClickRegister" is-link /> </van-cell-group> <van-cell-group> <van-field v-model="username" required clearable label="用户名" right-icon="question-o" placeholder="请输入用户名" @click-right-icon="$toast('question')" /> <van-field v-model="password" type="password" label="密码" placeholder="请输入密码" required /> <van-cell > <van-button @click="onClick" type="primary" style="margin-top:30px;100%;" size="normal">登录</van-button> </van-cell> </van-cell-group> </div> </template> <script> import { api } from '../../common/api' export default { data() { return { username:'', password:'', ifWrite:false, }; }, mounted: function() { }, methods: { async onClick(){ //alert(this.username) const row = {name:this.username,password:this.password} let ret = await api.post('api/login?time='+new Date().getTime(),row,this) if(ret.code == 0){ this.$store.state.logined = true; this.$store.state.user = this.username // this.$router.push({path: '/list'}); } }, onClickLeft(){ this.$router.go(-1) }, onClickRegister(){ this.$router.push({path: 'register'}); } } }; </script>
user
<template> <div> <van-nav-bar title="用户" left-text="返回" left-arrow @click-left="onClickLeft" @click-right="onClickRight" /> <van-cell-group class="user-group" style=" margin-top:10%;padding:15px;padding-left:20px;"> <div style="display:flex;font-size: 12px;"> <div style="border:1px solid"> <svg class="icon" style=" 2em; height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="564"><path d="M982.862052 804.664259c-25.592502-60.582251-62.281753-114.966318-109.068046-161.752612S772.623645 559.536073 712.041394 533.843601c-23.693059-9.997071-47.885971-18.094699-72.478766-24.392854 28.891536-12.996193 55.383774-31.290833 78.477009-54.284097 50.085327-50.085327 77.577272-116.56585 77.577272-187.445084s-27.591916-137.359758-77.577272-187.445085C667.95431 30.291126 601.373816 2.699209 530.594552 2.699209S393.234794 30.291126 343.249439 80.376452 265.572196 196.942302 265.572196 267.721566s27.591916 137.359758 77.577272 187.445084c20.194084 20.194084 42.987406 36.689251 67.680172 49.185591-32.490481 6.698038-64.181197 16.495167-94.972176 29.59133-60.582251 25.592502-114.966318 62.281753-161.752612 109.068047S70.729279 744.082007 45.136776 804.664259C18.644538 867.445865 5.148492 934.026359 5.148492 1002.706238l0.09997 21.293762H1022.850337l0.099971-21.293762c-0.099971-68.679879-13.596017-135.260373-40.088256-198.041979zM305.56048 267.721566c0-124.063653 100.970419-225.034072 225.034072-225.034072s225.034072 100.970419 225.034072 225.034072-100.970419 225.034072-225.034072 225.034072-225.034072-100.970419-225.034072-225.034072zM45.536659 984.111686C55.333789 734.184907 261.673338 533.843601 513.999414 533.843601c252.326076 0 458.665625 200.241336 468.462755 450.168114H45.536659z" fill="#727171" p-id="565"></path></svg> </div> <span style="padding-left:20px;padding-top:7px"> admin </span> </div> </van-cell-group> <van-cell-group> <van-cell title="我发表的" value="10篇文章" is-link /> <van-cell title="我的手机号" value="15862365445" is-link /> </van-cell-group> <van-cell > <van-button @click="onClick" type="warning" style="margin-top:20px;100%;" size="normal">注销</van-button> </van-cell> <div style=" position:fixed;bottom:0;100%;"> <van-row class="user-links" > <van-col span="8"> 首页 </van-col> <van-col span="8"> 发文 </van-col> <van-col span="8"> 未登陆 </van-col> </van-row> </div> </div> </template> <script> import { Row, Col, Icon, Cell, CellGroup } from 'vant'; export default { components: { [Row.name]: Row, [Col.name]: Col, [Icon.name]: Icon, [Cell.name]: Cell, [CellGroup.name]: CellGroup }, methods: { onClickLeft(){ this.$router.go(-1) }, async onClick(){ this.$store.state.logined = false; this.$store.state.user = null this.$router.push({path: '/login'}); }, } }; </script>
add
<template> <div > <van-nav-bar title="写文章" left-text="返回" right-text="按钮" left-arrow @click-right="onClickRight" @click-left="onClickLeft" /> <van-cell-group> <van-field readonly clickable v-model=classValue placeholder="选择类别" @click="onClickField" /> <van-popup v-model="showPicker" position="bottom"> <van-picker show-toolbar :columns=columns @cancel="onCancel2" @confirm="onConfirm2" /> </van-popup> </van-cell-group> <van-cell-group> <van-field v-model=titleValue placeholder="请输入标题" bind:change="onChange" /> </van-cell-group> <van-cell-group> <van-cell-group> <div style="border:1px solid #ededed;"> <van-field style="height:250px" v-model=contentValue type="textarea" placeholder="请输入内容" autosize /> <van-uploader style="margin-top:5px;margin-left:5px" v-model="fileList" multiple :max-count="1" :after-read="afterRead" /> </div> </van-cell-group> </van-cell-group> <div style="position:fixed;bottom:0;100%;"> <van-cell-group> </van-cell-group> <van-row class="user-links" > <van-col span="8"> 首页 </van-col> <van-col span="8"> 发文 </van-col> <van-col span="8"> 未登陆 </van-col> </van-row> </div> </div> </template> <script> import { api } from '../../common/api' export default { data() { return { columns: ['杭州', '宁波', '温州', '嘉兴', '湖州'], showPicker: false, classValue: '', titleValue:'', contentValue:'', img:'', fileType:'', fileList: [] } }, methods: { oversize(){ alert('too big') }, async onClickRight(){ debugger; console.log(this.classValue,this.titleValue,this.contentValue) const row = {newsid:this.classValue, title:this.titleValue, content:this.contentValue, img:this.img, filetype:this.fileType} let ret = await api.post('api/add?time='+new Date().getTime(),row,this) if(ret.code == 0){ alert('上传成功') } }, onClickField() { this.showPicker = true; }, onConfirm2(value) { this.showPicker = false; this.classValue = value; }, onCancel2() { this.showPicker = false; }, afterRead(file) { // 此时可以自行将文件上传至服务器 console.log(file) let arr = file.file.name.split('.'); let fileType = arr[arr.length-1] console.log(fileType); //console.log(file.content); this.img = file.content this.fileType = fileType }, onClickLeft(){ this.$router.go(-1) } } } </script>
register
<van-nav-bar
title="注册"
left-text="返回"
left-arrow
@click-left="onClickLeft"
@click-right="onClickRight"
/>
<van-cell-group class="user-group" style="margin-top:20px;margin-bottom:10px">
<van-cell title="已注册?去登陆" style="color: rgba(69, 90, 100, 0.6);" is-link />
</van-cell-group>
detail
<template>
<div>
<van-nav-bar style=" position:fixed;top:0;100%;"
:title=options1.title
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
<van-cell-group style="margin-top:20px;margin-bottom:100px;">
<van-cell>
<h4>{{options1.title}} </h4>
<small>{{options1.author_name}} 发表于 {{options1.published_at}} </small>
</van-cell>
<van-cell>
<img style="100%;height:auto;" :src=options1.image_url >
{{options1.content}}
</van-cell>
<van-cell>
<small>评论{{options1.comments_count}}</small>
</van-cell>
<van-cell>
<div class="comment-item" v-for="item in comments">
<span class="first-line">
<span class="author">
{{item.comment_no}}# {{item.author}}
</span>
<span class="zan">{{item.zans}}赞</span>
</span>
<span class="content">
<div v-html="item.content"> </div>
</span>
<span class="info">
<span> {{item.time}}</span>
<span @click="onClickReply(item)">回复</span>
</span>
</div>
</van-cell>
</van-cell-group>
<van-cell class="foot">
<div v-show="!ifWrite" class="orgin-comment" @click="onClick()">
<span> 写评论...</span>
</div>
<div v-show="ifWrite" class="write-comment" >
<div style="border:1px solid #ededed;border-radius: 5px;100%">
<van-field
@blur='onBlur'
autofocus
ref='focusTextarea'
v-model="cmdtext"
type="textarea"
:placeholder=placeholder
autosize
/>
<svg class="icon" style="margin:5px; 2em; height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1196"><path d="M271.738 465.729c56.472 0 102.438-46.461 102.438-103.514 0-57.083-45.966-103.515-102.438-103.515S169.3 305.132 169.3 362.215C169.3 419.268 215.266 465.729 271.738 465.729zM271.738 288.5c40.058 0 72.637 33.074 72.637 73.715 0 40.654-32.579 73.714-72.637 73.714-40.059 0-72.638-33.06-72.638-73.714C199.1 321.574 231.679 288.5 271.738 288.5zM65 154.4 65 869.6l894 0L959 154.4 65 154.4zM929.2 839.8 123.582 839.8l288.76-244.322L560.57 724.965 929.2 430.355 929.2 839.8zM929.2 392.218 561.414 686.144 412.662 556.176 94.8 825.118 94.8 184.2l834.4 0L929.2 392.218z" p-id="1197"></path></svg>
</div>
<!--记录why不行??? -->
<span @click="onPost()" style="margin-left:5px;40px;line-height: 80px;">提交</span>
<span @click="onCancel()" style="margin-left:5px;40px;line-height: 80px;">取消</span>
</div>
</van-cell>
</div>
</template>
<script>
import { api } from '../../common/api'
export default {
data() {
return {
placeholder:"请输入评论",
options1: {
author_name: "36氪出海",
comments_count: 10,
content: "- Node.js是V..",
article_type: 1,
/* //cover:"https://pic.36krcnd.com/201910/25072501/lvaqh1a5mp8mduj3!heading",
image_url:
"https://pic.36krcnd.com/201910/25072501/lvaqh1a5mp8mduj3!heading",
id: 101711,
post_id: "5259650",
published_at: "2019-10-26 09:31:06",
title:
"出海创投周报 | 拉美独角兽 Rappi 进军哥斯达黎加;Gojek 表示将为双重上市做准备"
*/
},
comments: [{
comment_no:1,
zans:12,
author:'正阳山大猴子',
content:'如设置了width则元素尺寸由width决定',
time:'1天前',},
{ comment_no:2,
zans:2,
author:'正阳山大猴子',
content:' flex-basis为auto时'+' //<small><b> 1# @正阳山大猴子</b> </small>',
time:'1天前',
}],
ifWrite:false,
cmdtext:'',
replytxt:''
};
},
mounted: function() {
//this.options.image_url = this.options.cover
this.getData()
//alert('1,'+this.$route.query.id)
this.getComment()
},
methods: {
async onPost(){
//alert(this.cmdtext)
if(this.cmdtext=='') return;
let row = {articleid:this.$route.query.id,content:this.cmdtext+this.replytxt}
console.log(row)
let ret = await api.post('api/comment?time='+new Date().getTime(),row,this)
console.log('ret')
this.cmdtext = ''
this.ifWrite = false
},
async getComment(){
let ret = await api.get('api/comment?time='+new Date().getTime()+'&id='+this.$route.query.id)
console.log(ret)
this.comments = ret
},
async getData(){
let ret = await api.get('api/article?time='+new Date().getTime()+'&id='+this.$route.query.id)
let row = ret[0]
this.options1.image_url ='api/'+row.id+'.jpg'
this.options1.title = row.title
this.options1.content = row.content
console.log(ret)
},
onClick(){
this.ifWrite = true
this.$refs.focusTextarea.focus()
this.placeholder = '请输入内容'
this.replytxt = ''
console.log( this.$refs.focusTextarea.focused)
},
onClickReply(item){
this.ifWrite = true
this.placeholder = '回复'+item.author
this.replytxt = ` //<small><b> ${item.comment_no}# @${item.author}</b> </small>`
this.$refs.focusTextarea.focus()
//alert(item.no)
},
onFocus(){
//alert(1)
},
onBlur(){
//this.ifWrite = false
},
onCancel(){
this.ifWrite = false
},
onClickLeft(){
this.$router.go(-1)
}
}
};
</script>
<style lang="less">
.foot{
100%;
display: block;
border: 1px solid #ebebeb;
margin:0 auto;
//margin-bottom: 30px;
background:#fff;
position:fixed;
z-index: 99999;
bottom:0;
text-align:center;
.orgin-comment{
120px;
background-color:#eee ;
border-radius: 20px;
display: block;
font-size: 15px;
margin: 5px 5px;
}
.write-comment{
display: flex;
flex-direction: row;
}
}
.comment-line{
display: flex;
flex-direction: row;
}
.comment-item{
display: flex;
flex-direction: column;
margin-bottom:9px;
.first-line{
justify-content:space-between;
.comment-line();
.author{
color: rgb(30, 102, 204);
font-size: 13px;
}
.zan{
font-size: 11px;
color: gray;
}
}
.content{
padding-left: 5px;
font-size: 13px;
line-height: 18px;
small{
background-color: #eee;
}
}
.info{
padding-left: 5px;
.comment-line();
font-size: 10px;
color: gray;
span{
50%;
}
}
span{
padding-right:5px;
}
}
</style>