高仿IT之家微信小程序(附精选源码32套,涵盖商城团购等)

项目预览

主要包含主页资讯,圈子俩大模块

主页

高仿IT之家微信小程序(附精选源码32套,涵盖商城团购等)_第1张图片
资讯详情
高仿IT之家微信小程序(附精选源码32套,涵盖商城团购等)_第2张图片
圈子
高仿IT之家微信小程序(附精选源码32套,涵盖商城团购等)_第3张图片

相关代码

网络请求

import wx from 'wx'
import Fly from 'flyio'

const request = new Fly()

request.interceptors.request.use((request) => {
  wx.showNavigationBarLoading()
  return request
})

request.interceptors.response.use(
  (response, promise) => {
    wx.hideNavigationBarLoading()
    return promise.resolve(response.data)
  },
  (err, promise) => {
    wx.hideNavigationBarLoading()
    wx.showToast({
      title: err.message,
      icon: 'none'
    })
    return promise.resolve()
  }
)

export default request

部分 api 列表

  • 新闻列表 https://api.ithome.com/json/newslist/news?r=0
  • 文章详情 https://api.ithome.com/xml/newscontent/350/412.xml
  • 相关文章 https://api.ithome.com/json/tags/0350/350362.json
  • 最热评论 https://dyn.ithome.com/json/hotcommentlist/350/87a8e5b144d81938.json
  • 评论列表 https://dyn.ithome.com/json/commentlist/350/87a8e5b144d81938.json
  • 评论详情 https://dyn.ithome.com/json/commentcontent/d739ee8f2ceb0a27.json
  • 轮播新闻 https://api.ithome.com/xml/slide/slide.xml
  • 圈子列表 https://apiquan.ithome.com/api/post?categoryid=0&type=0&orderTime=&visistCount&pageLength
  • 圈子详情 https://apiquan.ithome.com/api/post/236076
  • 圈子评论 https://apiquan.ithome.com/api/reply?postid=236076&replyidlessthan=3241294

api数据获取

import request from './request'

const baseUrlApi = 'https://api.ithome.com'
const baseUrlDyn = 'https://dyn.ithome.com'
const baseUrlQuan = 'https://apiquan.ithome.com'

const api = {
  getNewsList: (r) => request.get('/json/newslist/news', null, {
    baseURL: baseUrlApi
  }),
  getNews: (id) => request.get(`/xml/newscontent/${id}.xml`, null, {
    baseURL: baseUrlApi
  }),
  getRelatedNews: (id) => request.get(`/json/tags/0${id.slice(0, 3)}/${id}.json`, null, {
    baseURL: baseUrlApi,
    parseJson: false
  }),
  getNewsComments: (id) => request.get(`/json/commentlist/350/87a8e5b144d81938.json`, null, {
    baseURL: baseUrlDyn
  }),
  getSlides: () => request.get('/xml/slide/slide.xml', null, {
    baseURL: baseUrlApi
  }),
  getTopics: (r) => request.get('/api/post', {
    categoryid: 0,
    type: 0,
    orderTime: r,
    visistCount: '',
    pageLength: ''
  }, {
    baseURL: baseUrlQuan
  }),
  getTopic: (id) => request.get(`/api/post/${id}`, null, {
    baseURL: baseUrlQuan
  }),
  getTopicComments: (id, last) => request.get('/api/reply', {
    postid: id,
    replyidlessthan: last
  }, {
    baseURL: baseUrlQuan
  })
}

export default api

数据存取

import Vue from 'vue'
import Vuex from 'vuex'
import xml2json from 'xmlstring2json'
import { formatSlideList, formatNewsList, formatTopicList } from '@/utils'
import api from '@/utils/api'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    slides: [],
    news: [],
    topics: []
  },
  mutations: {
    slides (state, data) {
      state.slides = data
    },
    news (state, data) {
      state.news = data
    },
    topics (state, data) {
      state.topics = data
    }
  },
  actions: {
    async getSlides ({ commit }) {
      const slides = await api.getSlides()
      if (!slides) return
      const parsedSlides = xml2json(slides).rss.channel.item
      const filtedSlides = parsedSlides.filter(
        slide => slide.opentype['#text'] === '1'
      )
      const formatedSlides = filtedSlides.map(formatSlideList)
      commit('slides', formatedSlides)
    },
    async getNewsList ({ state, commit }, init) {
      const news = await api.getNewsList()
      if (!news) return
      const formatedNews = news.newslist.map(formatNewsList)
      if (init) {
        commit('news', formatedNews)
      } else {
        commit('news', state.news.concat(formatedNews))
      }
    },
    async getTopics ({ state, commit }, init) {
      let replytime = Date.now()
      if (!init) {
        const lastTopic = state.topics[state.topics.length - 1]
        replytime = lastTopic.replytime.replace(/[^0-9]/ig, '')
      }
      const topics = await api.getTopics(replytime)
      if (!topics) return
      const formatedTopics = topics.map(formatTopicList)
      if (init) {
        commit('topics', formatedTopics)
      } else {
        commit('topics', state.topics.concat(formatedTopics))
      }
    }
  }
})

export default store

新闻资讯列表

<template lang="pug">
.container
  swiper.slider-wrap(
    autoplay,
    indicator-dots,
    circular,
    indicator-color="rgba(255, 255, 255, .3)",
    indicator-active-color="rgba(210, 34, 34, .7)")
    swiper-item(
      v-for="slide of slides",
      :key="slide.title")
      .slider-item(@click="$router.push(slide.link)")
        .slider-title {{slide.title}}
        img.slider-img(:src="slide.image", mode="aspectFill")
  .news-wrap
    news-item(
      v-for="item of news",
      :news="item"
      :key="item.newsid")
  .nomore 只给看这么多
template>

<script>
import wx from 'wx'
import { mapState, mapActions } from 'vuex'
import newsItem from '@/components/news-item'

export default {
  components: {
    newsItem
  },
  computed: {
    ...mapState([
      'slides',
      'news'
    ])
  },
  mounted () {
    this.refresh()
  },
  onPullDownRefresh () {
    this.refresh()
  },
  // onReachBottom () {
  //   this.loadmore()
  // },
  methods: {
    ...mapActions([
      'getSlides',
      'getNewsList'
    ]),
    async refresh () {
      await Promise.all([
        this.getNewsList(true),
        this.getSlides()
      ])
      wx.stopPullDownRefresh()
    }
    // loadmore () {
    //   this.getNewsList()
    // }
  }
}
script>

<style lang="less" scoped>
.slider-wrap {
  width: 100%;
  height: 200px;
}
.slider-item {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}
.slider-title {
  max-width: 90vw;
  position: absolute;
  top: 10px;
  right: 0;
  background-color: rgba(0, 0, 0, .3);
  color: #fff;
  padding: 2px 6px;
  font-size: 18px;
}
.slider-img {
  width: 100%;
  height: 100%;
}

.news-wrap {
  padding: 0 10px;
}

.nomore {
  width: 100%;
  line-height: 50px;
  text-align: center;
  font-size: 14px;
  color: #ddd;
}
style>

新闻资讯详情

<template lang="pug">
.container
  .header
    h1.news-title {{title}}
    .auth-info {{news.newssource}}({{news.newsauthor}})
  .news-content(v-html="news.detail")
  h2.related-title(v-if="relatedNews.length") 相关文章
  .news-wrap
    news-item(
      v-for="news of relatedNews",
      :news="news"
      :key="news.newsid")
  //- .comment-btn(@click="turnToComment")
  //-   img.comment-icon(src="/static/assets/comment.png")
template>

<script>
import xml2json from 'xmlstring2json'
import api from '@/utils/api'
import newsItem from '@/components/news-item'

const dataArr = []

export default {
  components: {
    newsItem
  },
  data () {
    return {
      id: null,
      title: '',
      news: {},
      relatedNews: []
    }
  },
  async mounted () {
    Object.assign(this.$data, this.$options.data())
    this.id = this.$route.query.id
    this.title = this.$route.query.title
    await Promise.all([
      this.getNews(),
      this.getRelatedNews()
    ])
    dataArr.push({ ...this.$data })
  },
  onUnload () {
    dataArr.pop()
    const dataNum = dataArr.length
    if (!dataNum) return
    Object.assign(this.$data, dataArr[dataNum - 1])
  },
  methods: {
    turnToComment () {
      this.$router.push({
        path: '/pages/news/comment',
        query: {
          id: this.id
        }
      })
    },
    async getNews () {
      let { id } = this
      id = `${id.slice(0, 3)}/${id.slice(3, 6)}`
      const news = await api.getNews(id)
      if (!news) return
      const parsedNews = xml2json(news).rss.channel.item
      this.news = {
        newssource: parsedNews.newssource['#text'],
        detail: parsedNews.detail['#text'].replace(//g, '),
        newsauthor: parsedNews.newsauthor['#text']
      }
    },
    async getRelatedNews () {
      const newslist = await api.getRelatedNews(this.id)
      if (!newslist) return
      const parsedNews = JSON.parse(newslist.replace('var tag_jsonp =', ''))
      this.relatedNews = parsedNews.slice(0, 3).map(news => {
        return {
          title: news.newstitle,
          image: news.img,
          link: `/pages/news/detail?id=${news.newsid}&title=${news.newstitle}`,
          postdate: news.postdate
        }
      })
    }
  }
}
script>

<style lang="less">
@import url("~@/styles/index.less");

.header {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: #fff;
  background-color: @primary-color;
  padding: 10px;
  box-sizing: border-box;
}
.news-title {
  font-size: 22px;
}
.auth-info {
  font-size: 12px;
  margin-top: 10px;
  align-self: flex-end;
}
.news-content {
  width: 100%;
  box-sizing: border-box;
  padding: 10px;
  font-size: 16px;
  line-height: 1.6;
}

.related-title {
  font-size: 18px;
  font-weight: 600;
  align-self: flex-start;
  border-left: 4px solid @primary-color;
  padding: 2px 5px;
}

.news-wrap {
  width: 100%;
  box-sizing: border-box;
  padding: 0 10px;
}

.comment-btn {
  width: 55px;
  height: 55px;
  border-radius: 50%;
  background-color: @primary-color;
  position: fixed;
  right: 20px;
  bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.comment-icon {
  width: 40px;
  height: 40px;
}
style>

新闻资讯组件

<template lang="pug">
.news-item(
  @click="turn",
  v-if="!news.lapinid")
  img.news-img(:src="news.image", mode="aspectFill")
  .news-text
    .news-title {{news.title}}
    .news-info
      text {{news.postdate}}
      text(v-if="news.commentcount") {{news.commentcount}}评
template>

<script>
export default {
  props: {
    news: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  methods: {
    turn () {
      const { link } = this.news
      this.$router.push(link)
    }
  }
}
script>

<style lang="less" scoped>
.news-item {
  display: flex;
  height: 90px;
  align-items: center;
  border-bottom: 1px solid #eee;
}
.news-img {
  width: 100px;
  height: 75px;
  margin-right: 10px;
}
.news-text {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.news-title {
  font-size: 15px;
}
.news-info {
  color: #aaa;
  font-size: 12px;
  display: flex;
  justify-content: space-between;
}
style>

圈子列表

<template lang="pug">
.container
  .topic-wrap
    topicItem(
      v-for="topic of topics",
      :topic="topic"
      :key="topic.id")
template>

<script>
import wx from 'wx'
import { mapState, mapActions } from 'vuex'
import topicItem from '@/components/topic-item'

export default {
  components: {
    topicItem
  },
  computed: {
    ...mapState([
      'topics'
    ])
  },
  mounted () {
    this.refresh()
  },
  onPullDownRefresh () {
    this.refresh()
  },
  onReachBottom () {
    this.loadmore()
  },
  methods: {
    ...mapActions([
      'getTopics'
    ]),
    async refresh () {
      await this.getTopics(true)
      wx.stopPullDownRefresh()
    },
    loadmore () {
      this.getTopics()
    }
  }
}
script>

<style lang="less">
style>

圈子详情

<template lang="pug">
.container
  .topic-title {{topic.title}}
  .topic-num 1楼
  .topic-info
    .topic-info-item
      img.topic-info-icon(src="/static/assets/quan_hit.png")
      span.topic-info-text {{topic.vc}}
    .topic-info-item
      img.topic-info-icon(src="/static/assets/quan_comment.png")
      span.topic-info-text {{topic.rc}}
  .topic-content(v-html="topic.content")
  .comment-wrap
    comment-item(
      v-for="comment of topic.reply",
      :key="comment.id",
      :comment="comment")
template>

<script>
import api from '@/utils/api'
import commentItem from '@/components/comment-item'
import { formatComment } from '@/utils'

export default {
  components: {
    commentItem
  },
  data () {
    return {
      loading: false,
      topic: {}
    }
  },
  mounted () {
    Object.assign(this.$data, this.$options.data())
    this.getTopic()
  },
  onReachBottom () {
    this.getComments()
  },
  methods: {
    async getTopic () {
      const { query } = this.$route
      const topic = await api.getTopic(query.id)
      if (!topic) return
      topic.content = topic.content.replace('!--IMG_1--', `img src="${topic.imgs[0]}" width="100%" /`)
      topic.reply = topic.reply.map(formatComment)
      this.topic = Object.assign({
        title: query.title,
        vc: query.vc
      }, topic)
    },
    async getComments () {
      if (this.loading) return
      this.loading = true
      const { query } = this.$route
      const comments = this.topic.reply
      const lastComment = comments[comments.length - 1]
      const newComments = await api.getTopicComments(query.id, lastComment.id)
      if (!newComments) return
      const formatedComments = newComments.map(formatComment)
      this.topic.reply = this.topic.reply.concat(formatedComments)
      this.loading = false
    }
  }
}
script>

<style lang="less">
.topic-title {
  width: 100%;
  padding: 10px;
  box-sizing: border-box;
  font-size: 22px;
}
.topic-num {
  font-size: 12px;
  position: absolute;
  right: 10px;
  top: 10px;
}
.topic-info {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 0 10px 10px;
  box-sizing: border-box;
  border-bottom: 1px solid #eee;
}
.topic-info-item {
  margin-right: 10px;
  font-size: 12px;
  color: #aaa;
  display: flex;
  align-items: center;
}
.topic-info-icon {
  width: 15px;
  height: 15px;
  margin-right: 4px;
}
.topic-content {
  width: 100%;
  font-size: 16px;
  padding: 10px;
  line-height: 1.6;
  border-bottom: 1px solid #eee;
  box-sizing: border-box;
  min-height: 100px;
}

.comment-wrap {
  width: 100%;
}
style>

圈子组件

<template lang="pug">
.topic-item(@click="turn")
  img.topic-headimg(src="/static/assets/avatar_default.png")
  img.topic-headimg(:src="topic.author.headimg")
  .topic-title {{topic.tag}} {{topic.title}}
  .topic-info
    .topic-info-item {{topic.author.nickname}}
    .topic-info-item {{topic.type}}
    .topic-info-item
      img.topic-info-icon(src="/static/assets/quan_hit.png")
      span.topic-info-text {{topic.viewcount}}
    .topic-info-item
      img.topic-info-icon(src="/static/assets/quan_comment.png")
      span.topic-info-text {{topic.replycount}}
template>

<script>
export default {
  props: {
    topic: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  methods: {
    turn () {
      const { id, title, author, viewcount } = this.topic
      this.$router.push({
        path: '/pages/quanzi/detail',
        query: {
          id,
          title,
          author,
          vc: viewcount
        }
      })
    }
  }
}
script>

<style lang="less" scoped>
.topic-item {
  padding: 10px 10px 10px 60px;
  border-bottom: 1px solid #eee;
  position: relative;
}
.topic-headimg {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  position: absolute;
  left: 10px;
  top: 15px;
}
.topic-title {
  font-size: 16px;
}
.topic-info {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-top: 10px;
}
.topic-info-item {
  margin-left: 10px;
  font-size: 12px;
  color: #aaa;
  display: flex;
  align-items: center;
}
.topic-info-icon {
  width: 15px;
  height: 15px;
  margin-right: 4px;
}
style>

相关依赖

  • flyio - 同时支持浏览器、小程序、Node、Weex 及快应用的基于 Promise 的跨平台请求库
  • mpvue-entry - 集中式页面配置,不再需要重复编辑各页面的 main.js 文件
  • mpvue-router-patch - 在 mpvue 中使用 vue-router 兼容的路由写法
  • xmlstring2json - xml字符串转换 json 格式,适用于微信小程序

Tips

  • flyio 使用方法

具体内容参见 微信小程序中使用flyio,这里提示下小程序中需要引入的是 flyio/dist/npm/wx.js 这个文件,可以配置下 webpack 的 alias 方便调用

alias: {
  '@': resolve('src'),
  vue: 'mpvue',
  flyio: 'flyio/dist/npm/wx',
  wx: resolve('src/utils/wx')
}
  • vuex 使用方法

建立 src/store/index.js 文件

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  }
})

export default store

src/main.js 中引用

import Vue from 'vue'
import store from '@/store'
import App from '@/App'

const app = new Vue({
  store,
  ...App
}).$mount()

最后在需要使用 vuex 的页面相对应的 main.js 文件中像 src/main.js 一样引用即可

精选32套源码目录:

IT之家小程序版客户端(使用 Mpvue 开发,兼容 Web)ithome-lite-master.zip

mpvue 仿网易严选mpvue-shop-master.zip

mpvue-音乐播放器mpvue-music-master.zip

mpvue性能测试与体验miniweibo-master.zip

mpvue改造的日历.zip

mpvue框架仿滴滴出行didi-master.zip

mpVue高仿美团小程序教程mpvue-meituan-master.zip

uni APP自动更新并安装.vue

uni-app nvue沉浸式状态栏(线性渐变色).vue

uni-app 二维码生成器分享wxqrcode.zip

uni-app 侧边导航分类,适合商品分类页面uni-app-left-navigation-master.zip

uni-app 自定义底部导航栏uni-app-bottom-navigation-master.zip

uni-app全局变量的几种实现方式.zip

uni-app的markdown富文本编辑器插件uniapp-markdown-master.zip

uni-app自定义导航栏title-custom.zip

uniapp聊天实例,支持图片,语音,表情.zip

uniapp选择器,包含一级,二级级联,三级级联uniapp-picker-master.zip

vue-mpvue-ChatRobot聊天机器人vue-mpvue-ChatRobot-master.zip

【小程序】CNode社区mpvue-cnode-master.zip

【插件、图表】7种图表漂亮丰富uniCharts.zip

一款播课类小程序, 基于 mpvue 构建mp-podcast-mpvue-master.zip

云档新版小程序端,基于mpvue开发cloud-doc-v2-master.zip

仿uc浏览器列表.vue

仿扎克新闻mpZAKER-master.zip

仿网易云UImusic播放器mpvue-music-master.zip

仿追书神器的小说阅读器小程序wx-book-master.zip

参照米家APP布局和样式,编写的一款智能家居小程序smart-home-master.zip

商城实例mpvue-xbyjShop-master.zip

基于 mpvue 实现豆瓣电影微信小程序mpvue-douban-master.zip

基于mpvue的优酷mpvue-youku-master.zip

校园助手示例SHUhelper-master.zip

类似mui中的chat(聊天窗口)实现uniapp-chat-master.zip

美团外卖(第三方)开源程序mpvue-master.zip

美食搜索mpvue-FG-master.zip

豆瓣平分mpvue-douban-pingfen-master.zip

顶部tabbar.vue

源码截图:
在这里插入图片描述

说明

如果本项目对您有帮助,欢迎 “点赞,关注” 支持一下 谢谢~

源码获取关注公众号「码农园区」,回复 【uniapp源码】
在这里插入图片描述

你可能感兴趣的:(Uniapp,微信小程序,notepad++,小程序)