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