vant 移动helloworld

"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>&nbsp;&nbsp;&nbsp;&nbsp;写评论...</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时'+' &nbsp;&nbsp;&nbsp;//<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 = `&nbsp;&nbsp;&nbsp;//<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>
原文地址:https://www.cnblogs.com/cnchengv/p/12039944.html