vue-cnodejs
阅读原文时间:2023年07月15日阅读:2

感谢那些无私开源的程序员,你们是最可爱的人儿~~~~

//根app app.js
<template>
  <div id="app">
    <v-header></v-header>
     <div class="main">
       <router-view></router-view>
     </div>
    <v-footer></v-footer>
  </div>
</template>

<script>
import vHeader from './components/header'
import vFooter from './components/footer'

export default {
  name: 'app',
  components: {
    vHeader,
    vFooter
  }
}
</script>

<style>
#app {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 14px;
  background: #E2E2E2;
  color: #778087;
}
img {
  height: auto;
  vertical-align: top;
}
.main {
  width: 1100px;
  margin: 0 auto;
}
</style>

公共header

//src/components/header.vue
<template>
  <div class="header">
    <div class="header-wrapper">
      <h1 class="title">
        <router-link to="/tag/all">
          <img src="./../assets/logo.png">
          <span>cNodeJS</span>
        </router-link>
      </h1>
      <p class="about">关于</p>
    </div>
  </div>
</template>

<script>
export default {
};
</script>

<style scoped>
.header {
  background: #fff;
  margin-bottom: 35px;
}
.header .header-wrapper {
  width: 1100px;
  margin: 0 auto;
  overflow: hidden;
}
.header .header-wrapper .title {
  float: left;
  padding: 10px 0;
}
.header .header-wrapper .title img {
  width: 30px;
  height: 30px;
  vertical-align: middle;
  margin-right: 10px;
}
.header .header-wrapper .title span {
  font-size: 18px;
  font-weight: bold;
  color: #778087;
}
.header .header-wrapper .about {
  float: right;
  margin-top: 15px;
}
</style>

//src/components/footer.vue
<template>
  <div class="footer">
    <p>由 Vue 强力驱动 | Write - By XMit</p>
  </div>
</template>

<script>
export default {
  name: 'footer',
}
</script>

<style scoped>
.footer {
  background: #fff;
  margin-top: 35px;
  padding: 50px 0 30px;
  text-align: center;
}
</style>

路由部分

//src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'

import main from '@/components/main';
import detail from '@/components/detail';
import user from '@/components/user';

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: main,
      children: [
        {
          path: '/tag/:id',
        }
      ]
    },
    {
      path: '/topic/:id',
      name: 'detail',
      component: detail
    },
    {
      path: '/user/:id',
      name: 'user',
      component: user
    },
  ]
})


//src/components/user.vue
<template>
  <div class="user" v-if="user.length !== 0">
    <div class="user-info">
      <div class="user-info-hd">个人用户信息</div>
      <div class="user-info-bd">
        <ul>
          <li>
            <img :src="user.avatar_url" alt="img">
            <p class="username">{{user.loginname}}</p>
          </li>
          <li>{{user.score}} 积分</li>
          <li>github <a :href="'http://github.com/' + user.githubUsername">{{'http://github.com/' + user.githubUsername}}</a></li>
          <li>注册时间 {{getTime(user.create_at)}}</li>
        </ul>
      </div>
    </div>
    <div class="new-topic" v-if="user.recent_topics.length !== 0">
      <div class="new-topic-hd">最近创建的话题</div>
      <div class="new-topic-bd">
        <ul>
          <li v-for="item in user.recent_topics">
            <img :src="user.avatar_url" alt="img">
            <router-link :to="{ name: 'detail', params: {id: item.id} }" class="title">{{item.title}}</router-link>
          </li>
        </ul>
      </div>
    </div>
    <div class="participate" v-if="user.recent_replies.length !== 0">
      <div class="participate-hd">最近参与的话题</div>
      <div class="participate-bd">
        <ul>
          <li v-for="item in user.recent_replies">
            <img :src="user.avatar_url" alt="img">
            <router-link :to="{ name: 'detail', params: {id: item.id} }" class="title">{{item.title}}</router-link>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ['userdata'],
  data: function() {
    return {
      user: []
    };
  },
  methods: {
    getData: function() {
      this.$http
        .get("https://cnodejs.org/api/v1/user/" + (this.userdata || this.$route.params.id), {
          params: {}
        })
        .then(it => {
          this.user = it.data.data;
          console.log(this.user);
        })
        .catch(it => {
          console.log("user.vue:", it);
        });
    },
    getDateTimeStamp: function (dateStr){
      return Date.parse(dateStr.replace(/-/gi,"/").replace(/[A-Z]+/gi," "));
    },
    getTime: function(hisTime, nowTime) {
      var now = nowTime ? nowTime : new Date().getTime(),
        diffValue = now - this.getDateTimeStamp(hisTime),
        result = "",
        minute = 1000 * 60,
        hour = minute * 60,
        day = hour * 24,
        halfamonth = day * 15,
        month = day * 30,
        year = month * 12,
        _year = diffValue / year,
        _month = diffValue / month,
        _week = diffValue / (7 * day),
        _day = diffValue / day,
        _hour = diffValue / hour,
        _min = diffValue / minute;
      if (_year >= 1) result = parseInt(_year) + "年前";
      else if (_month >= 1) result = parseInt(_month) + "个月前";
      else if (_week >= 1) result = parseInt(_week) + "周前";
      else if (_day >= 1) result = parseInt(_day) + "天前";
      else if (_hour >= 1) result = parseInt(_hour) + "个小时前";
      else if (_min >= 1) result = parseInt(_min) + "分钟前";
      else result = "刚刚";
      return result;
    }
  },
  created() {
    this.getData();
  }
};
</script>

<style scoped>
.user {
  border-radius: 4px;
}
.user a {
  color: #778087;
}
.user-info,
.new-topic,
.participate {
  margin-bottom: 20px;
  background: #fff;
  border-radius: 4px;
}
.user .user-info-hd,
.new-topic .new-topic-hd,
.participate .participate-hd {
  padding: 10px 20px;
  background: #F6F6F6;
  border-radius: 4px;
}
.user .user-info-bd img {
  width: 50px;
  height: auto;
  border-radius: 4px;
  margin-bottom: 5px;
}
.new-topic .new-topic-bd img,
.participate .participate-bd img {
  width: 30px;
  height: auto;
  border-radius: 4px;
  margin-bottom: 5px;
  margin-right: 10px;
  vertical-align: middle;
}
.user .user-info-bd {
  padding: 20px;
}
.user .user-info-bd li {
  margin-bottom: 10px;
}
.new-topic .new-topic-bd li,
.participate .participate-bd li {
  border-bottom: 1px solid #ddd;
  padding: 5px 20px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.new-topic .new-topic-bd li:last-child,
.participate .participate-bd li:last-child {
  border-bottom: 0
}
</style>

//src/components/main.vue
<template>
  <div class="main">
    <div class="main-wrapper">
      <ul class="tab-list">
        <li class="item" @click="tabs('all')">全部</li>
        <li class="item" @click="tabs('good')">精华</li>
        <li class="item" @click="tabs('share')">分享</li>
        <li class="item" @click="tabs('ask')">问答</li>
        <li class="item" @click="tabs('job')">招聘</li>
        <li class="item" @click="tabs('dev')">客户端测试</li>
      </ul>
      <ul class="list">
        <li v-for="(item,index) in content" :key="index" class="item">
          <div class="item-hd">
            <router-link :to="{ name: 'user', params: {id: item.author.loginname} }" class="user">
              <img :src="item.author.avatar_url">
            </router-link>
          </div>
          <div class="item-bd">
            <router-link :to="{ name: 'detail', params: {id: item.id} }" class="title">{{item.title}}</router-link>
            <p class="footer">
              <span class="reply" v-show="item.reply_count !== 0">{{item.reply_count}}</span>
              <span class="authour">{{item.author.loginname}} • </span>
              <span class="time">创建于: {{getTime(item.create_at)}}</span>
            </p>
            <p :class="{top: item.top}" v-if="item.top">置顶</p>
          </div>
        </li>
      </ul>
    </div>
    <p class="pagination">
      <!-- <a class="button" @click="prev" >GO PREV</a>
      <a class="button" @click="next" >GO NEXT</a> -->
      <v-tab v-on:changePage="test"></v-tab>
    </p>
  </div>
</template>

<script>
import vTab from "./tab";

export default {
  data: function() {
    return {
      content: [],
      page: 1,
      tab: 'all',
      show: true
    };
  },
  methods: {
    test(page) {
      // console.log('page:' + page)
      this.page = page
    },
    tabs(data) {
      this.tab = data
      this.$router.push({ path: `/tag/${data}` })
    },
    prev() {
      if(this.page > 1) {
        return this.page--
      }
    },
    next() {
      return this.page++
    },
    getData: function(page,tab) {
      this.$http
        .get("https://cnodejs.org/api/v1/topics", {
          params: {
            page: page,
            limit: 0,
            tab: tab,
            mdrender: true
          }
        })
        .then(it => {
          this.content = it.data.data
        })
        .catch(it => {
          console.log("main.vue:", it);
        });
    },
    getDateTimeStamp: function (dateStr){
      return Date.parse(dateStr.replace(/-/gi,"/").replace(/[A-Z]+/gi," "));
    },
    getTime: function(hisTime, nowTime) {
      var now = nowTime ? nowTime : new Date().getTime(),
        diffValue = now - this.getDateTimeStamp(hisTime),
        result = "",
        minute = 1000 * 60,
        hour = minute * 60,
        day = hour * 24,
        halfamonth = day * 15,
        month = day * 30,
        year = month * 12,
        _year = diffValue / year,
        _month = diffValue / month,
        _week = diffValue / (7 * day),
        _day = diffValue / day,
        _hour = diffValue / hour,
        _min = diffValue / minute;
      if (_year >= 1) result = parseInt(_year) + "年前";
      else if (_month >= 1) result = parseInt(_month) + "个月前";
      else if (_week >= 1) result = parseInt(_week) + "周前";
      else if (_day >= 1) result = parseInt(_day) + "天前";
      else if (_hour >= 1) result = parseInt(_hour) + "个小时前";
      else if (_min >= 1) result = parseInt(_min) + "分钟前";
      else result = "刚刚";
      return result;
    }
  },
  created() {
    this.getData(this.page)
  },
  watch: {
    page(val) {
      this.getData(val)
    },
    tab(newVal, oldVal) {
      this.getData(this.page, newVal)
    },
    $route(to, from) {
      // this.getData(to.params.id)
      this.getData(this.page, to.params.id)
    }
  },
  components: {
    vTab
  }
};
</script>

<style scoped>
.tab-list {
  overflow: hidden;
  padding: 10px 15px;
  background: #f6f6f6;
  border-radius: 4px 4px 0 0;
}
.tab-list .item {
  float: left;
  margin-right: 15px;
}
.button {
  display: inline-block;
  background: #212121;
  color: #fff;
  font-weight: bold;
  text-align: center;
  padding: 1em;
  cursor: pointer;
  text-decoration: none;
}
.pagination {
  padding: 20px 0 0;
  text-align: center;
}
.main .main-wrapper {
  background: #fff;
  border-radius: 4px;
}
.main .main-wrapper .list .item {
  overflow: hidden;
  padding: 10px;
  border-bottom: 1px solid #ddd;
}
.main .main-wrapper .list .item:last-child {
  border: 0;
}
.main .main-wrapper .list .item .item-hd {
  float: left;
}
.main .main-wrapper .list .item .item-hd .user {
  display: inline-block;
}
.main .main-wrapper .list .item .item-hd img {
  width: 50px;
  height: 50px;
  display: inline-block;
  border-radius: 4px;
}
.main .main-wrapper .list .item .item-bd {
  margin-left: 70px;
}
.main .main-wrapper .list .item .item-bd .title {
  font-size: 16px;
  margin-bottom: 5px;
  color: #778087;
}
.main .main-wrapper .list .item .item-bd .footer {
  color: #8492a6;
}
.main .main-wrapper .list .item .item-bd .footer .reply {
  display: inline-block;
  position: absolute;
  right: 10px;
  top: 15px;
  background: #aab0c5;
  border-radius: 10px;
  width: 30px;
  text-align: center;
  color: #fff;
  font-size: 12px;
}
.main .main-wrapper .list .item .item-bd {
  position: relative;
}
.main .main-wrapper .list .item .item-bd .top {
  position: absolute;
  right: -20px;
  top: -20px;
  background: #81bb24;
  color: #fff;
  padding: 3px 3px;
  border-radius: 2px;
  font-size: 12px;
  text-indent: -9999px;
  transform: rotate(-45deg);
}
</style>

//src/components/detail.vue
<template>
  <div class="content" v-if="itemdetail">
    <div class="detail-left">
      <div class="detail">
        <div class="detail-wrapper">
          <div class="detail-hd">
            <h2 class="title">{{itemdetail.title}}</h2>
            <span class="time">发布于 {{getTime(itemdetail.create_at)}} •</span>
            <span class="author">作者 {{itemdetail.author.loginname}} •</span>
            <span class="visit">{{itemdetail.visit_count}} 次浏览</span>
          </div>
          <div class="detail-bd">
            <div id="content" v-html="itemdetail.content"></div>
          </div>
        </div>
      </div>
      <v-reply :replydata="itemdetail.replies"></v-reply>
    </div>
    <div class="detail-right">
      <v-user :userdata="itemdetail.author.loginname"></v-user>
    </div>
  </div>
</template>

<script>
import vReply from "./reply";
import vUser from "./user";

export default {
  name: "detail",
  data() {
    return {
      itemdetail: ""
    };
  },
  methods: {
    getData: function(id) {
      this.$http
        .get("https://cnodejs.org/api/v1/topic/" + id, {
          params: {
            accesstoken: true,
            mdrender: true
          }
        })
        .then(it => {
          this.itemdetail = it.data.data;
          // console.log(this.itemdetail);
        })
        .catch(it => {
          //  console.log('detail.vue:',it)
        });
    },
    getDateTimeStamp: function (dateStr){
      return Date.parse(dateStr.replace(/-/gi,"/").replace(/[A-Z]+/gi," "));
    },
    getTime: function(hisTime, nowTime) {
      var now = nowTime ? nowTime : new Date().getTime(),
        diffValue = now - this.getDateTimeStamp(hisTime),
        result = "",
        minute = 1000 * 60,
        hour = minute * 60,
        day = hour * 24,
        halfamonth = day * 15,
        month = day * 30,
        year = month * 12,
        _year = diffValue / year,
        _month = diffValue / month,
        _week = diffValue / (7 * day),
        _day = diffValue / day,
        _hour = diffValue / hour,
        _min = diffValue / minute;
      if (_year >= 1) result = parseInt(_year) + "年前";
      else if (_month >= 1) result = parseInt(_month) + "个月前";
      else if (_week >= 1) result = parseInt(_week) + "周前";
      else if (_day >= 1) result = parseInt(_day) + "天前";
      else if (_hour >= 1) result = parseInt(_hour) + "个小时前";
      else if (_min >= 1) result = parseInt(_min) + "分钟前";
      else result = "刚刚";
      return result;
    }
  },
  created() {
    this.getData(this.$route.params.id);
  },
  watch: {
    $route(to, from) {
      this.getData(to.params.id)
      // console.log(to, from)
    }
  },
  components: {
    vReply,
    vUser
  }
};
</script>

<style scoped>
.content {
  overflow: hidden;
}
.detail-left {
  float: left;
  width: 70%;
}
.detail-right {
  float: right;
  width: 28%;
}
.detail .detail-wrapper {
  background: #fff;
  border-radius: 4px;
}
.detail .detail-wrapper .detail-hd,
.detail .detail-wrapper .detail-bd {
  padding: 20px;
}
.detail .detail-wrapper .detail-hd {
  border-bottom: 1px solid #ddd;
}
.detail .detail-wrapper .detail-hd .title {
  font-size: 20px;
  margin-bottom: 15px;
}
</style>

//src/components/reply.vue
<template>
  <div class="reply" v-if="replydata && replydata.length !== 0">
    <div class="reply-wrapper">
      <p class="reply-head">{{replydata.length}} 回复</p>
      <ul class="list">
        <li class="item" v-for="(item, index) in replydata" :key="index">
          <div class="item-hd">
            <router-link :to="{ name: 'user', params: {id: item.author.loginname} }" class="user">
              <img :src="item.author.avatar_url">
            </router-link>
          </div>
          <div class="item-bd">
            <div class="top">
              <span class="user">{{item.author.loginname}}</span>
              <span class="floor">{{index+1}} 楼</span>
              <span class="time">{{getTime(item.create_at)}}</span>
            </div>
            <div class="content" v-html="item.content"></div>
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  name: "reply",
  props: ["replydata"],
  methods: {
    getDateTimeStamp: function (dateStr){
      return Date.parse(dateStr.replace(/-/gi,"/").replace(/[A-Z]+/gi," "));
    },
    getTime: function(hisTime, nowTime) {
      var now = nowTime ? nowTime : new Date().getTime(),
        diffValue = now - this.getDateTimeStamp(hisTime),
        result = "",
        minute = 1000 * 60,
        hour = minute * 60,
        day = hour * 24,
        halfamonth = day * 15,
        month = day * 30,
        year = month * 12,
        _year = diffValue / year,
        _month = diffValue / month,
        _week = diffValue / (7 * day),
        _day = diffValue / day,
        _hour = diffValue / hour,
        _min = diffValue / minute;
      if (_year >= 1) result = parseInt(_year) + "年前";
      else if (_month >= 1) result = parseInt(_month) + "个月前";
      else if (_week >= 1) result = parseInt(_week) + "周前";
      else if (_day >= 1) result = parseInt(_day) + "天前";
      else if (_hour >= 1) result = parseInt(_hour) + "个小时前";
      else if (_min >= 1) result = parseInt(_min) + "分钟前";
      else result = "刚刚";
      return result;
    }
  }
};
</script>

<style scoped>
.reply {
  margin-top: 30px;
  background: #fff;
  border-radius: 4px;
}
.reply .reply-wrapper .reply-head {
  padding: 8px 10px;
  background: #f6f6f6;
  border-radius: 4px;
}
.reply .reply-wrapper .list .item {
  overflow: hidden;
  padding: 20px;
  border-bottom: 1px solid #ddd;
}
.reply .reply-wrapper .list .item:last-child {
  border: 0;
}
.reply .reply-wrapper .list .item .item-hd {
  float: left;
}
.reply .reply-wrapper .list .item .item-hd img {
  border-radius: 4px;
  width: 50px;
}
.reply .reply-wrapper .list .item .item-bd {
  margin-left: 60px;
}
.reply .reply-wrapper .list .item .item-bd .top {
  margin-bottom: 10px;
}
.reply .reply-wrapper .list .item .item-bd .user {
  color: #666;
}
</style>

点进去就可以看到项目哇~