[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "README.md",
    "content": "# vue-finance mobile web \n这是一个基于Vue2开发的移动端财经网站\n\n## Project setup\n```\nnpm install\n```\n\n### Compiles and hot-reloads for development\n```\nnpm run serve\n```\n\n### Compiles and minifies for production\n```\nnpm run build\n```\n\n### Customize configuration\nSee [Configuration Reference](https://cli.vuejs.org/config/).\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vue-finance\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^0.26.1\",\n    \"core-js\": \"^3.6.5\",\n    \"vant\": \"^2.12.47\",\n    \"vue\": \"^2.6.11\",\n    \"vue-router\": \"^3.2.0\",\n    \"vuex\": \"^3.4.0\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-plugin-babel\": \"~4.5.13\",\n    \"@vue/cli-plugin-router\": \"~4.5.13\",\n    \"@vue/cli-plugin-vuex\": \"~4.5.13\",\n    \"@vue/cli-service\": \"~4.5.13\",\n    \"babel-plugin-import\": \"^1.13.5\",\n    \"less\": \"^3.0.4\",\n    \"less-loader\": \"^5.0.0\",\n    \"vue-template-compiler\": \"^2.6.11\"\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title><%= htmlWebpackPlugin.options.title %></title>\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <keep-alive>\n      <router-view>\n      </router-view>\n    </keep-alive>\n    <BottomNav></BottomNav>\n  </div>\n</template>\n\n<script>\n  import Home from \"@/views/Home\";\n  import BottomNav from \"@/components/Home/BottomNav\";\n  import Stock from \"@/views/Stock\";\n  import Me from \"@/views/Me\";\n  import Login from \"@/views/Login\";\n  export default {\n    name:\"App\",\n    data() {\n      return {\n        active:null,\n      }\n    },\n    components:{\n      Home,BottomNav,Stock,Me,Login,\n    }\n  }\n</script>\n<style lang=\"less\">\n* {\n  padding: 0;\n  margin: 0;\n}\nhtml {\n  background-color: rgb(245, 247, 249);\n}\n</style>\n"
  },
  {
    "path": "src/api/articleApi.js",
    "content": "import request from \"@/api/index\";\n\nlet getComment = (params) => {\n\treturn request({\n\t\turl: '/article/comments',\n\t\tmethod:\"GET\",\n\t\tparams,\n\t})\n}\n\nexport {\n\tgetComment,\n}\n"
  },
  {
    "path": "src/api/fundApi.js",
    "content": "import request from \"@/api/index\";\n\n//获取基金轮播图\nlet getFund = () => {\n\treturn request({\n\t\turl: '/fund/banner',\n\t\tmethod: \"GET\",\n\t})\n}\n\nlet getFundTabList = () => {\n\treturn request({\n\t\turl:'/fund/tablist',\n\t\tmethod:\"GET\",\n\t})\n}\n\nexport {\n\tgetFund,getFundTabList\n}\n"
  },
  {
    "path": "src/api/homeApi.js",
    "content": "import request from \"@/api/index\";\n\nlet getHotspot = () => {\n\treturn request({\n\t\tmethod:'GET',\n\t\turl:'/quote',\n\t})\n}\n\nlet getArticle = (params) => {\n\treturn request({\n\t\tmethod:\"GET\",\n\t\turl:'/articles',\n\t\tparams,\n\t})\n}\n\nexport {\n\tgetHotspot,getArticle,\n}\n"
  },
  {
    "path": "src/api/index.js",
    "content": "import axios from \"axios\";\nimport store from '@/store/index'\n\nconst request = axios.create({\n\tbaseURL: 'http://api.cpengx.cn/finance/api',\n})\n\nrequest.interceptors.request.use(function (config) {\n\tif(store.state.user&&store.state.user.token) {\n\t\tconfig.headers.Authorization = 'Bearer '+store.state.user.token;\n\t}\n\treturn config;\n}, function (error) {\n\treturn Promise.reject(error);\n})\n\nrequest.interceptors.response.use(function (response) {\n\tif(response.status===200) {\t\t\t//代表拦截到的回应没问题\n\t\treturn response.data;\t\t\t\t\t//返回数据\n\t}else {\n\t\treturn response;\t\t\t\t\t\t\t//返回数据有问题\n\t}\n},function (error) {\n\treturn Promise.reject(error);\t\t//出错了，显示错误\n})\n\nexport default request;\n"
  },
  {
    "path": "src/api/loginApi.js",
    "content": "import request from \"@/api/index\";\n\nlet getLogin = (data) => {\n\treturn request({\n\t\turl: '/login',\n\t\tmethod: \"POST\",\n\t\tdata,\n\t})\n}\nlet getVerifyCode = (params) => {\n\treturn request({\n\t\turl: '/code',\n\t\tmethod: \"GET\",\n\t\tparams,\n\t})\n}\nexport {\n\tgetLogin,getVerifyCode\n}\n"
  },
  {
    "path": "src/api/stockApi.js",
    "content": "import request from \"@/api/index\";\n\nlet getStockRank = (params) => {\n\treturn request({\n\t\turl: '/stocks',\n\t\tmethod: \"GET\",\n\t\tparams,\n\t})\n}\n\nexport {\n\tgetStockRank,\n}\n"
  },
  {
    "path": "src/assets/iconfont.css",
    "content": "@font-face {\n    font-family: \"iconfont\"; /* Project id 3336648 */\n    src: url('//at.alicdn.com/t/font_3336648_zc9svdlbwz.woff2?t=1650537850371') format('woff2'),\n    url('//at.alicdn.com/t/font_3336648_zc9svdlbwz.woff?t=1650537850371') format('woff'),\n    url('//at.alicdn.com/t/font_3336648_zc9svdlbwz.ttf?t=1650537850371') format('truetype');\n}\n\n.iconfont {\n    font-family: \"iconfont\" !important;\n    font-size: 16px;\n    font-style: normal;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-comment:before {\n    content: \"\\e667\";\n}\n\n.icon-star:before {\n    content: \"\\e7df\";\n}\n\n.icon-like:before {\n    content: \"\\e707\";\n}\n\n.icon-youxiang1:before {\n    content: \"\\e664\";\n}\n\n.icon-gerenzhongxin:before {\n    content: \"\\e61d\";\n}\n"
  },
  {
    "path": "src/components/Article/Comment.vue",
    "content": "<template>\n  <div class=\"comment\"  style=\"z-index: 99\">\n    <h4>评论</h4>\n    <div class=\"commentUnit\" v-for=\"(item,i) in commentArr.comments\" :key=\"i\">\n      <div class=\"header\" style=\"margin-top: 0.1rem;\">\n        <img :src=\"item.user.photo_domain+item.user.profile_image_url.split(',')[0]\">\n        <span style=\"margin-left: 0.1rem;\">{{item.user.screen_name}}</span>\n      </div>\n      <p v-html=\"item.text\"></p>\n      <div class=\"comment\"></div>\n    </div>\n    <div style=\"font-size: 0.14rem;text-align:center; margin-top: 0.09rem;\">因为后台服务限制,目前暂不支持发表评论</div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"Comment\",\n  props:['commentArr'],\n  mounted() {\n    console.log(this.commentArr);\n  },\n  methods:{\n    alert(hello) {\n      alert('ksksks');\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n.commentUnit {\n  background-color: #efefef;\n  display: flex;\n  flex-direction: column;\n  margin: 0.08rem 0.1rem 0;\n  height: 1.2rem;\n  .header {\n    display: flex;\n    align-items: center;\n    justify-content: start;\n  }\n  img {\n    width: 0.3rem;\n    height: 0.3rem;\n    border-radius: 0.3rem;\n  }\n  span {\n    font-size: 0.14rem;\n  }\n  p {\n    margin-top: 0.2rem;\n    font-size: 0.14rem;\n  }\n}\n\nh4 {\n  font-size: 0.2rem;\n  margin: 0.15rem 0.1rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Home/Article.vue",
    "content": "<template>\n  <div class=\"article\">\n    <van-icon name=\"back-top\" class=\"back\" size=\"0.32rem\" @click=\"returnTop\"/>\n    <div class=\"articleUnit\" @load=\"getArticleList\" v-for=\"(item,i) in articleArr\" v-if=\"item!==undefined\" :key=\"i\" @click=\"jump(item.original_status.id,item)\">\n      <div class=\"header\">\n        <img\n            :src=\"item.original_status.user.photo_domain+item.original_status.user.profile_image_url.split(',')[0]\"\n            alt=\"\">\n        <div style=\"margin-left: 0.1rem;\">\n          <div class=\"name\">{{ item.original_status.user.screen_name }}</div>\n          <div class=\"publishDate\">{{ item.original_status.timeBefore }}</div>\n        </div>\n      </div>\n      <div class=\"content\" style=\"margin: 0 0.05rem 0 0.03rem\">\n        <p v-html=\"item.original_status.description\"></p>\n      </div>\n      <div class=\"footer\">\n        <van-icon name=\"share-o\" size=\"0.2rem\" style=\"margin-left: 0.05rem;\"/>\n        <span>{{ item.original_status.retweet_count }}</span>\n        <van-icon name=\"good-job-o\" size=\"0.2rem\"/>\n        <span>{{ item.original_status.like_count }}</span>\n        <van-icon name=\"chat-o\" size=\"0.2rem\"/>\n        <span>{{ item.original_status.reply_count }}</span>\n      </div>\n    </div>\n    <div class=\"bottom\" v-if=\"loading\">正在加载中</div>\n    <div class=\"bottom\" v-if=\"end\">你到了世界的尽头</div>\n  </div>\n</template>\n\n<script>\nimport {getArticle} from \"@/api/homeApi\";\n\nexport default {\n  name: \"Article\",\n  data() {\n    return {\n      articleArr: [],\n      page: 1,\n      end: false,\n      loading: false,\n    }\n  },\n  methods: {\n    async getArticleList() {\n      if (!this.end) {\n        this.loading = true;\n        let res = await getArticle({page: this.page});\n        if (res.result && res.result.indexOf('已加载完') !== -1) {\n          this.end = true;\n          this.loading = false;\n        }\n        this.page++;\n        this.articleArr = this.articleArr.concat(res.items);\n      }\n    },\n    returnTop() {\n      cancelAnimationFrame(timer);\n      let speed = document.documentElement.scrollTop / 60;\n      let timer = requestAnimationFrame(function fn() {\n        var oTop = document.body.scrollTop || document.documentElement.scrollTop;\n        if (oTop > 0) {\n          document.documentElement.scrollTop -= speed;\n          timer = requestAnimationFrame(fn);\n        } else {\n          cancelAnimationFrame(timer);\n        }\n      });\n    },\n    debounce(fn, delay) {\n      let timer = null;\n      return function () {\n        if (!timer) {\n          timer = setTimeout(fn, delay);\n        } else {\n          clearTimeout(timer);\n          timer = setTimeout(fn, delay);\n        }\n      }\n    },\n    show() {\n      console.log('loading');\n    },\n    jump(articleId,params) {\n      this.$router.push({name:'Article',params});\n    }\n  },\n  mounted() {\n    this.getArticleList();\n    let timer = null;\n    window.addEventListener('scroll', this.debounce(() => {\n      let scrollTop = document.documentElement.scrollTop;\n      let scrollHeight = document.documentElement.scrollHeight;\n      let innerHeight = window.innerHeight;\n      if (scrollHeight - scrollTop - innerHeight < 50) {\n        this.getArticleList();\n      }\n    }, 250));\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n.back {\n  position: fixed;\n  right: 0;\n  top: 50%;\n  border-radius: 0.25rem;\n  background-color: #f8f6f6;\n}\n\n.articleUnit {\n  span, div {\n    font-size: 0.15rem;\n  }\n\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  width: 3.68rem;\n  border-radius: 0.1rem;\n  margin: 0.1rem auto;\n  background-color: white;\n  box-shadow: 0.01rem 0.01rem 0.01rem #dcdcdc;\n  .header {\n    margin: 0.12rem 0 0.06rem 0.05rem;\n    display: flex;\n    justify-content: start;\n    align-items: center;\n    img {\n      width: 0.3rem;\n      height: 0.3rem;\n      border-radius: 0.3rem;\n    }\n\n    div {\n      font-size: 0.08rem;\n      color: darkgrey;\n    }\n  }\n\n  .content {\n    margin: 0.12rem 0;\n\n    p {\n      font-size: 0.12rem;\n      line-height: 0.25rem;\n    }\n  }\n\n  .footer {\n    margin: 0.06rem 0 0.12rem;\n    display: flex;\n    align-items: center;\n    justify-content: start;\n\n    span {\n      margin: 0 0.2rem 0 0.07rem;\n      font-size: 0.12rem;\n    }\n  }\n}\n\n.bottom {\n  font-size: 0.12rem;\n  color: darkgray;\n  text-align: center;\n  margin-bottom: 0.1rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Home/BottomNav.vue",
    "content": "<template>\n  <div class=\"bottomNav\">\n    <van-tabbar v-model=\"active\" v-show=\"show\">\n      <van-tabbar-item icon=\"wap-home-o\" to=\"/\">首页</van-tabbar-item>\n      <van-tabbar-item icon=\"chart-trending-o\" to=\"/stock\">股票</van-tabbar-item>\n      <van-tabbar-item icon=\"after-sale\" to=\"/fund\">基金</van-tabbar-item>\n      <van-tabbar-item icon=\"contact\" to=\"/me\">我的</van-tabbar-item>\n    </van-tabbar>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"BottomNav\",\n  data() {\n    return {\n      active:null,\n      show:true,\n      showNavPath : ['/','/stock','/fund','/me'],\n    }\n  },\n  watch:{\n    $route:{\n      handler(newRoute,OldRoute) {\n        this.show = this.showNavPath.indexOf(newRoute.path) !== -1;\n        if(this.show) {\n          this.active = this.showNavPath.indexOf(newRoute.path);\n        }\n      },\n      immediate:true,\n    },\n  }\n}\n</script>\n\n<style scoped>\n.bottomNav {\n  margin-top: 0.65rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Home/HeaderNav.vue",
    "content": "<template>\n  <div class=\"headerNav\" >\n    <van-nav-bar :fixed=\"true\">\n      <template #left>\n        <span class=\"iconfont icon-gerenzhongxin\"></span>\n      </template>\n      <template #title>\n        <van-search\n            v-model=\"value\"\n            shape=\"round\"\n            placeholder=\"请输入搜索关键词\"\n        />\n      </template>\n      <template #right>\n        <span class=\"icon-youxiang1 iconfont\" style=\"font-size: 0.26rem\"></span>\n      </template>\n    </van-nav-bar>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"HeaderNav\",\n  data() {\n    return {\n      value:\"\",\n      show:false,\n    }\n  },\n  mounted() {\n    window.addEventListener('scroll',()=>{\n      if(document.documentElement.scrollTop<40) {\n        this.show = false;\n      }\n      else {\n        this.show = true;\n      }\n    })\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n.iconfont {\n  font-size: 0.22rem;\n}\n::v-deep .van-nav-bar__title {\n  max-width: 100%;\n  width: 2.5rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Home/HotSpot.vue",
    "content": "<template>\n  <div class=\"hotspot\">\n    <h5 class=\"title\">今日市场</h5>\n    <van-swipe class=\"my-swipe\" :autoplay=\"3000\" indicator-color=\"skyblue\">\n      <van-swipe-item v-for=\"i in 3\" >\n        <van-grid :column-num=\"3\" :border=\"false\">\n          <van-grid-item v-for=\"item in hotspotData.slice((i-1)*3,(i-1)*3+3)\" >\n            <h5>{{ item.quote.name }}</h5>\n            <h5 :class=\"parseFloat(item.quote.chg)>0?'red':'green'\">{{ item.quote.chg }}%</h5>\n            <h5 :class=\"parseFloat(item.quote.chg)>0?'red':'green'\">{{ item.quote.current }}</h5>\n          </van-grid-item>\n        </van-grid>\n      </van-swipe-item>\n    </van-swipe>\n  </div>\n</template>\n\n<script>\nimport * as api from '@/api/homeApi'\n\nexport default {\n  name: \"HotSpot\",\n  data() {\n    return {\n      hotspotData: [],\n    }\n  },\n  async mounted() {\n    let res = await api.getHotspot();\n    this.hotspotData = res.data.items;\n  },\n  computed: {\n    hotLength() {\n      return Math.ceil(this.hotspotData.length/3);\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\nh5 {\n  font-size: 0.17rem;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  background-color: white;\n  padding: 0.02rem;\n}\n\n.title {\n  margin: auto auto;\n  text-align: center;\n}\n\n::v-deep .van-grid-item {\n  max-width: 33.33%;\n\n  h5 {\n    font-size: 0.15rem;\n    margin: 0.02rem;\n  }\n\n  .green {\n    color: forestgreen;\n  }\n\n  .red {\n    color: orangered;\n  }\n\n  .bg {\n    background-color: pink;\n  }\n}\n::v-deep .van-swipe__indicators {\n  bottom: 0;\n  .van-swipe__indicator {\n    background-color: grey;\n  }\n}\n.test {\n  width: 1.2rem;\n  height: 10rem;\n  background-color: pink;\n}\n</style>\n"
  },
  {
    "path": "src/components/Home/NaviList.vue",
    "content": "<template>\n  <div class=\"naviList\" style=\"margin-top: 0.5rem;\">\n    <van-grid direction=\"horizontal\" :column-num=\"4\" :border=\"false\">\n      <van-grid-item v-for=\"item in navListArr\" :icon=\"item.imgSrc\"  :text=\"item.title\">\n      </van-grid-item>\n    </van-grid>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"NaviList\",\n  data() {\n    return {\n      navListArr: [\n        {title:'大盘行情',imgSrc:'./imgs/nav1.png',linkTo:''},\n        {title:'股票组合',imgSrc:'./imgs/nav2.png',linkTo:''},\n        {title:'股票开户',imgSrc:'./imgs/nav3.png',linkTo:''},\n        {title:'选股攻略',imgSrc:'./imgs/nav4.png',linkTo:''},\n        {title:'指数选基',imgSrc:'./imgs/nav5.png',linkTo:''},\n        {title:'基金组合',imgSrc:'./imgs/nav6.png',linkTo:''},\n        {title:'私募基金',imgSrc:'./imgs/nav7.png',linkTo:''},\n        {title:'基金必看',imgSrc:'./imgs/nav8.png',linkTo:''},\n      ]\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n::v-deep .van-grid-item__content--horizontal {\n  flex-direction: column;\n}\n::v-deep .van-grid-item__content--horizontal .van-grid-item__icon+.van-grid-item__text {\n  margin-top: 0.1rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Me/MeContent.vue",
    "content": "<template>\n  <div class=\"meContent\">\n    <h5>服务</h5>\n    <van-cell icon=\"edit\" title=\"申请专栏\" is-link />\n    <van-cell icon=\"cash-back-record\" title=\"创作者权益\" is-link />\n    <van-cell icon=\"comment-circle-o\" title=\"建议反馈\" is-link />\n\n    <h5>其他</h5>\n    <van-cell icon=\"service-o\" title=\"帮助与客服\" is-link />\n    <van-cell icon=\"setting-o\" title=\"设置\" is-link />\n  </div>\n</template>\n\n<script>\nexport default {\n  name: \"MeContent\"\n}\n</script>\n\n<style scoped lang=\"less\">\nh5 {\n  font-size: 0.18rem;\n  margin: 0.3rem 0.2rem 0.15rem 0.14rem;\n}\n::v-deep .van-cell__title {\n  height: 0.24rem;\n  line-height: 0.24rem;\n}\n</style>\n"
  },
  {
    "path": "src/components/Me/MeHeader.vue",
    "content": "<template>\n  <div class=\"meHeader\">\n    <div class=\"bg\" style=\"background-image:url('./imgs/meBg.png');\"></div>\n    <div class=\"content\">\n      <div class=\"upperInfo\">\n        <van-cell-group inset v-if=\"!user\">\n          <van-cell title=\"登录财经通\" to=\"/login\" is-link value=\"登录个人账号\"/>\n        </van-cell-group>\n\n        <van-cell v-else style=\"border-radius: 0.07rem;border-bottom: 0.01rem solid #efefef;\">\n          <template #title>\n            <div class=\"profile\">\n              <img :src=\"user.avatar\">\n              <span >{{user.nickname}}</span>\n            </div>\n          </template>\n        </van-cell>\n      </div>\n      <div class=\"bottomInfo\">\n        <span class=\"bookmark\">\n          <img src=\"/imgs/me1.png\">\n          <div>收藏</div>\n        </span>\n        <span class=\"comment\">\n          <img src=\"/imgs/me2.png\">\n          <div>评论</div>\n        </span>\n        <span class=\"liked\">\n          <img src=\"/imgs/me3.png\">\n          <div>赞过</div>\n        </span>\n        <span class=\"history\">\n          <img src=\"/imgs/me4.png\">\n          <div>最近</div>\n        </span>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport {mapState} from 'vuex';\n\nexport default {\n  name: \"MeHeader\",\n  computed: {\n    ...mapState({\n      user: state => state.user,\n    })\n  },\n  methods:{\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n.bg {\n  height: 1.4rem;\n  background-repeat: no-repeat;\n  background-size: 100% 100%;\n}\n\n.meHeader {\n  position: relative;\n}\n\n.content {\n  width: 3.4rem;\n  height: 1.8rem;\n  background-color: white;\n  margin: -1rem auto 0;\n  border-radius: 0.1rem;\n}\n\n.bottomInfo {\n  display: flex;\n  justify-content: space-evenly;\n  align-items: center;\n  margin-top: 0.2rem;\n\n  span {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n\n    div {\n      font-size: 0.13rem;\n      margin-top: 0.1rem;\n    }\n\n    img {\n      width: 0.3rem;\n      height: 0.3rem;\n    }\n  }\n}\n.profile {\n  display: flex;\n  justify-content: start;\n  margin-left:0.04rem;\n  border-radius: 0.3rem;\n  img {\n    width: 0.4rem;\n    height: 0.4rem;\n    border-radius: 0.4rem;\n  }\n  span {\n    margin-top: 0.05rem;\n    margin-left: 0.1rem;\n    line-height: 0.38rem;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/main.js",
    "content": "import Vue from 'vue'\nimport App from './App.vue'\nimport router from './router'\nimport store from './store'\nimport './utils/rem'\nimport Vant from 'vant';\nimport 'vant/lib/index.css';\nimport '@/assets/iconfont.css';\n\nVue.use(Vant);\nconst bus = new Vue();\nVue.prototype.$bus = bus;\nVue.config.productionTip = false\n\nnew Vue({\n  router,\n  store,\n  render: h => h(App)\n}).$mount('#app')\n"
  },
  {
    "path": "src/router/index.js",
    "content": "import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport Home from '../views/Home.vue'\nimport ArticlePage from \"@/views/ArticlePage\";\nimport Stock from \"@/views/Stock\";\nimport Fund from \"@/views/Fund\";\nimport Me from \"@/views/Me\";\nimport Login from \"@/views/Login\";\n\nVue.use(VueRouter)\n\nconst routes = [\n\t{\n\t\tpath: '/',\n\t\tname: 'Home',\n\t\tcomponent: Home\n\t},\n\t{\n\t\tpath: '/article/:id',\n\t\tname: 'Article',\n\t\tprops: route => ({ article: route.params }),\n\t\tcomponent: ArticlePage\n\t},\n\t{\n\t\tpath: '/stock',\n\t\tname: 'Stock',\n\t\tcomponent: Stock,\n\t},\n\t{\n\t\tpath:'/fund',\n\t\tname:'Fund',\n\t\tcomponent: Fund,\n\t},\n\t{\n\t\tpath: '/me',\n\t\tname: 'Me',\n\t\tcomponent: Me,\n\t},\n\t{\n\t\tpath: '/login',\n\t\tname:'Login',\n\t\tcomponent: Login,\n\t}\n]\n\nconst router = new VueRouter({\n\tmode: 'hash',\n\tbase: process.env.BASE_URL,\n\troutes\n})\n\nexport default router\n"
  },
  {
    "path": "src/store/index.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\nimport {getLogin} from \"@/api/loginApi\";\nimport {Toast} from \"vant\";\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n  state: {\n    user:localStorage.user?JSON.parse(localStorage.user):null,\n  },\n  mutations: {\n    setUser(state,payload){\n      state.user = payload;\n      console.log(state.user);\n    }\n  },\n  actions: {\n    async asyncLogin(context,payload) {\n      let res =await getLogin(payload);\n      console.log(res)\n      if(res.msg ==='操作成功') {\n        Toast.success('登录成功');\n        context.commit('setUser',res.result);\n        localStorage.user = JSON.stringify(res.result);\n        return {user:res.result,successLogin:true};\n      }\n      else {\n        Toast.fail(res.result);\n        return {user:res,successLogin:false};\n      }\n    }\n  },\n  modules: {\n  }\n})\n"
  },
  {
    "path": "src/utils/rem.js",
    "content": "function setRem() {\n\tlet width = document.documentElement.clientWidth;\n\tlet htmlFontsize = width/3.75;\n\tdocument.documentElement.style.fontSize = htmlFontsize+'px';\n}\nwindow.addEventListener('resize',()=>{\n\tsetRem();\n})\nsetRem();\n"
  },
  {
    "path": "src/views/ArticlePage.vue",
    "content": "<template>\n  <div class=\"articlePage\">\n    <div class=\"articleUnit\">\n      <van-nav-bar\n          left-arrow\n          left-text=\"返回\"\n          @click-left=\"quit\"\n          :fixed=\"true\"\n          :placeholder=\"true\"\n      />\n      <div class=\"header\">\n        <img\n            :src=\"articleDetails.original_status.user.photo_domain+articleDetails.original_status.user.profile_image_url.split(',')[0]\"\n            alt=\"\">\n        <div style=\"margin-left: 0.1rem;\">\n          <div class=\"name\">{{ articleDetails.original_status.user.screen_name }}</div>\n          <div class=\"publishDate\">{{ articleDetails.original_status.timeBefore }}</div>\n        </div>\n      </div>\n      <h5 class=\"title\">{{ articleDetails.original_status.title }}</h5>\n      <p class=\"content\" v-html=\"articleDetails.original_status.text\"></p>\n    </div>\n    <div class=\"position\" ref=\"hook\"></div>\n    <Comment :commentArr=\"commentArr\"></Comment>\n    <div class=\"wrap\" style=\"margin-top: 0.4rem;\">\n      <div class=\"bottomNav\">\n      <span class=\"operation\" @click=\"like($event)\">\n        <div class=\"iconfont icon-like\"></div>\n        <div>{{ articleDetails.original_status.like_count }}</div>\n      </span>\n        <span class=\"operation\" @click=\"star($event)\">\n        <div class=\"iconfont icon-star\"></div>\n        <div>{{ articleDetails.original_status.retweet_count }}</div>\n      </span>\n        <span class=\"operation\" @click=\"moveComment\">\n        <div class=\"iconfont icon-comment\"></div>\n        <div>{{ commentArr.count }}</div>\n      </span>\n      </div>\n    </div>\n  </div>\n\n</template>\n\n<script>\nimport {getComment} from \"@/api/articleApi\";\nimport Comment from \"@/components/Article/Comment\";\n\nexport default {\n  name: \"ArticlePage\",\n  props: ['article'],\n  data() {\n    return {\n      articleId: this.$route.params.id,\n      commentArr: [],\n      liked: false,\n      stared: false,\n      showComment: false,\n    }\n  },\n  components: {\n    Comment,\n  },\n  async mounted() {\n    if (this.article && this.article.original_status) {\n      console.log(this.articleId);\n      sessionStorage[this.article.id] = JSON.stringify(this.article);\n    }\n    console.log(this.articleId);\n    this.commentArr = await getComment({id: this.articleId.toString()});\n  },\n  updated() {\n    if (this.article && this.article.original_status) {\n      console.log(this.articleId);\n      sessionStorage[this.article.id] = JSON.stringify(this.article);\n    }\n  },\n  computed: {\n    articleDetails() {\n      document.documentElement.scrollTop = 0;\n      return (this.article && this.article.original_status) ? this.article : JSON.parse(sessionStorage[this.articleId]);\n    }\n  },\n  methods: {\n    quit() {\n      this.$router.go(-1);\n    },\n    like(event) {\n      this.liked = !this.liked;\n      event.currentTarget.style.color = this.liked ? 'red' : 'black';\n      if (this.liked) {\n        this.articleDetails.original_status.like_count++;\n      } else {\n        this.articleDetails.original_status.like_count--;\n      }\n      this.$forceUpdate();\n    },\n    star(event) {\n      this.stared = !this.stared;\n      event.currentTarget.style.color = this.stared ? 'orange' : 'black';\n      if (this.stared) {\n        this.articleDetails.original_status.retweet_count++;\n      } else {\n        this.articleDetails.original_status.retweet_count--;\n      }\n      this.$forceUpdate();\n    },\n    moveComment() {\n      console.log(this.$refs.hook);\n      document.documentElement.scrollTop = this.$refs.hook.offsetTop;\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n.articlePage {\n  height: 100%;\n}\n\n.articleUnit {\n  span, div {\n    font-size: 0.12rem;\n  }\n\n  h5 {\n    font-size: 0.16rem;\n    margin: 0.1rem 0.1rem;\n  }\n\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin: 0 auto;\n  background-color: white;\n  box-shadow: 0.01rem 0.01rem 0.01rem #dcdcdc;\n\n  .header {\n    margin: 0.12rem 0 0.06rem 0.05rem;\n    display: flex;\n    justify-content: start;\n    align-items: center;\n\n    img {\n      width: 0.3rem;\n      height: 0.3rem;\n      border-radius: 0.3rem;\n    }\n\n    div {\n      font-size: 0.08rem;\n      color: darkgrey;\n    }\n  }\n\n  .content {\n    margin: 0.1rem 0.1rem;\n    font-size: 0.135rem;\n    line-height: 0.2rem;\n\n    ::v-deep img {\n      width: 3.4rem;\n      display: block;\n      margin: 0.08rem auto;\n    }\n  }\n}\n\nh5 {\n  font-size: 0.17rem;\n}\n\n.bottomNav {\n  width: 100%;\n  background-color: white;\n  display: flex;\n  position: fixed;\n  bottom: 0;\n  right: 0;\n  justify-content: end;\n  border-top: 1px #e5e4e4 solid;\n\n  .operation {\n    height: 0.35rem;\n    margin: 0 0.1rem 0.1rem;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: space-evenly;\n  }\n\n  .iconfont {\n    margin: 0.08rem 0.05rem 0.05rem;\n    font-size: 0.18rem;\n    transition: 0.5s all ease;\n  }\n\n  span {\n    font-size: 0.14rem;\n    display: inline-block;\n  }\n;\n}\n\n.show-enter-active, .show-leave-active {\n  transition: all 0.5s;\n}\n\n.show-enter, .show-leave-to /* .fade-leave-active below version 2.1.8 */ {\n  transform: translateY(1rem);\n}\n</style>\n"
  },
  {
    "path": "src/views/Fund.vue",
    "content": "<template>\n  <div class=\"fund\">\n    <van-swipe class=\"my-swipe\" :autoplay=\"3000\" indicator-color=\"white\">\n      <van-swipe-item v-for=\"(item,i) in bannerArr\">\n        <van-image :src=\"item.icon_url\" fit=\"fill\"/>\n      </van-swipe-item>\n    </van-swipe>\n    <van-tabs type=\"card\" v-if=\"loadingEnd\">\n      <van-tab :title=\"item.tab_name\" v-for=\"item in tabListArr\">\n        <div class=\"content\">\n          <div class=\"title\">{{ item.comment }}</div>\n          <div class=\"tips\">{{ item.tips }}</div>\n          <div class=\"box\" style=\"margin: 0.1rem auto\">\n            <div class=\"data\" v-for=\"(content) in item.items\">\n              <img :src=\"content.img_src\">\n              <span class=\"name\">{{ content.name }}</span>\n              <span class=\"pe\">\n              <span class=\"description\">{{ content.content[0].desc }}</span>\n              <span class=\"description\">{{ content.content[0].value }}</span>\n            </span>\n              <span class=\"percentage\">\n              <span class=\"description\">{{ content.content[1].desc }}</span>\n              <span class=\"description\">{{ content.content[1].value }}</span>\n            </span>\n            </div>\n          </div>\n        </div>\n      </van-tab>\n    </van-tabs>\n    <div class=\"loading\" style=\"margin: 0 auto\" v-if=\"!loadingEnd\">\n      <van-loading style=\"text-align:center;\"/>\n    </div>\n  </div>\n</template>\n\n<script>\nimport {getFund, getFundTabList} from \"@/api/fundApi\";\n\nexport default {\n  name: \"Fund\",\n  data() {\n    return {\n      bannerArr: [],\n      tabListArr: [],\n      loadingEnd:false,\n    }\n  },\n  async mounted() {\n    getFund().then(res => {\n      this.bannerArr = res.data.items;\n    });\n    getFundTabList().then(res => {\n      this.tabListArr = res.data.tab;\n      console.log(this.tabListArr);\n      this.loadingEnd = true;\n    });\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\n::v-deep .van-swipe__indicators {\n  bottom: 0.32rem;\n}\n\n::v-deep .van-swipe__indicator {\n  background-color: #666;\n}\n.box {\n  background-color: white;\n  width: 3.5rem;\n  border-radius: 0.1rem;\n}\n\n.content {\n  display: flex;\n  justify-content: center;\n  align-items: start;\n  flex-direction: column;\n  div {\n    margin: 0.1rem 0.1rem 0.04rem 0.1rem;\n    font-size: 0.14rem;\n  }\n\n  .title {\n    color: orange;\n    font-weight: 800;\n    margin-top: 0.18rem;\n  }\n\n  .tips {\n    color: grey;\n    line-height: 0.20rem;\n  }\n  .data {\n    display: flex;\n    justify-content: space-evenly;\n    align-items: center;\n    border-bottom: 0.02rem rgb(245,247,249) solid;\n    div {\n      font-size: 0.12rem;\n    }\n    img {\n      width: 0.90rem;\n      height: 0.90rem;\n    }\n    .name {\n      flex: 2;\n      text-align: center;\n      line-height: 0.25rem;\n    }\n    .pe,.percentage {\n      flex: 1.5;\n      display: flex;\n      text-align: center;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      span {\n        margin-bottom: 0.11rem;\n      };\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "src/views/Home.vue",
    "content": "<template>\n  <div class=\"home\">\n    <!--    这个是Header导航栏-->\n    <div style=\"margin-bottom: 0.3rem;\">\n      <HeaderNav></HeaderNav>\n    </div>\n    <!--    这个是导航列表-->\n    <NaviList></NaviList>\n    <!--    这是热度内容-->\n    <HotSpot></HotSpot>\n    <!--这是文章推荐列表-->\n    <Article></Article>\n  </div>\n</template>\n\n<script>\n// @ is an alias to /src\nimport HeaderNav from \"@/components/Home/HeaderNav\";\nimport NaviList from \"@/components/Home/NaviList\";\nimport HotSpot from \"@/components/Home/HotSpot\";\nimport Article from \"@/components/Home/Article\";\n\nexport default {\n  name: 'Home',\n  components: {\n    HeaderNav, NaviList, HotSpot, Article,\n  },\n  data() {\n    return {}\n  }\n}\n</script>\n"
  },
  {
    "path": "src/views/Login.vue",
    "content": "<template>\n  <div class=\"login\">\n    <div class=\"header\">\n      <div style=\"flex: 1;\">\n        <img class=\"logo\" src=\"/imgs/logo.png\"><span class=\"name\">熊熊财经通</span>\n      </div>\n      <div @click=\"$router.go(-1)\" style=\"flex: 1\">\n        <img class=\"quit\" src=\"/imgs/quit.png\"><span style=\"line-height: 0.22rem\">退出</span>\n      </div>\n    </div>\n    <div class=\"content\">\n      <div class=\"logo\">\n        <img class=\"logoImg\" src=\"/imgs/logo.png\">\n        <div>登录账户</div>\n      </div>\n      <div class=\"loginBox\">\n        <div class=\"choose\">\n          <span @click=\"move('left')\">验证码登录</span>\n          <span @click=\"move('right')\">密码登录</span>\n          <div ref=\"activeLine\" class=\"activeLine\"></div>\n        </div>\n        <transition :name=\"show\" mode=\"out-in\">\n          <div class=\"input\" style=\"margin-top: 0.1rem;\" v-if=\"show==='right'\" key=\"password\">\n            <input v-model=\"account\" placeholder=\"账户名/手机号\" class=\"phoneNumber\">\n            <input v-model=\"password\" placeholder=\"密码\" class=\"password\">\n          </div>\n          <div class=\"input\" style=\"margin-top: 0.1rem;position: relative\" v-if=\"show==='left'\" key=\"verifyCode\">\n            <input v-model=\"account\" placeholder=\"手机号\" class=\"phoneNumber\">\n            <input placeholder=\"验证码\" class=\"password\">\n            <button class=\"verify\" @click=\"getVerifyCode\">获取验证码</button>\n          </div>\n        </transition>\n      </div>\n      <van-button @click=\"login\" type=\"info\" style=\"width: 3rem;border-radius: 0.06rem;margin-top: 0.2rem;\">登录\n      </van-button>\n    </div>\n  </div>\n</template>\n\n<script>\nimport {getVerifyCode} from \"@/api/loginApi\";\nexport default {\n  name: \"Login\",\n  data() {\n    return {\n      show: 'left',\n      account: '',\n      password: '',\n    }\n  },\n  methods: {\n    move(direction) {\n      if (direction === 'right') {\n        this.$refs.activeLine.style.left = '1.03rem'\n        this.show = direction;\n      } else {\n        this.$refs.activeLine.style.left = '0';\n        this.show = direction;\n      }\n    },\n    async login() {\n      let res =await this.$store.dispatch('asyncLogin', {account: this.account, password: this.password});\n      if(res.successLogin) {    //登录成功,跳转回到/me\n        this.$bus.$emit('hello',res.user);\n        this.$router.go(-1);\n      }\n    },\n    async getVerifyCode() {\n      let res = await getVerifyCode({mobile:this.account});\n      console.log(res);\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\nspan {\n  font-size: 0.16rem;\n}\n\n.login {\n  background-color: white;\n  width: 100%;\n  position: absolute;\n  bottom: 0;\n  top: 0;\n  height: 98vh;\n  .header {\n    margin-top: 0.1rem;\n    height: 0.5rem;\n    position: relative;\n    border-bottom: 0.01rem lightgray solid;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    span, img {\n      width: 0.40rem;\n      height: 0.25rem;\n      display: block;\n      top: 0.09rem;\n      right: 0.1rem;\n      position: absolute;\n      font-size: 0.16rem;\n      color: grey;\n    }\n\n    .logo {\n      left: 0.15rem;\n      height: 0.3rem;\n      width: 0.3rem;\n      margin-bottom: 0.03rem;\n      top: 0.07rem;\n    }\n\n    .quit {\n      top: 0.1rem;\n      right: 0.587rem;\n      width: 0.2rem;\n      height: 0.2rem;\n    }\n\n    .name {\n      top: 0.12rem;\n      left: 0.53rem;\n      height: 0.2rem;\n      width: 1rem;\n    }\n  }\n\n  .content {\n    width: 3.7rem;\n    margin: 0.6rem auto;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n\n    .logo {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      flex-direction: column;\n      margin-top: 0.2rem;\n\n      img {\n        width: 0.4rem;\n        height: 0.4rem;\n      }\n\n      div {\n        font-size: 0.28rem;\n        margin-top: 0.2rem;\n        font-weight: 400;\n      }\n    }\n\n    .loginBox {\n      display: flex;\n      flex-direction: column;\n      //align-items: center;\n      justify-content: center;\n\n      span {\n        width: 0.9rem;\n        margin: 0.1rem;\n      }\n\n      .choose {\n        border-bottom: 1px solid lightgrey;\n        height: 1.05rem;\n        width: 3.0rem;\n        position: relative;\n\n        span {\n          font-size: 0.14rem;\n          margin: 0.2rem;\n          text-align: center;\n          width: 1.2rem;\n        }\n\n        .activeLine {\n          position: absolute;\n          height: 0.01rem;\n          width: 1.1rem;\n          background-color: blue;\n          bottom: 0;\n          left: 0;\n          transition: all 0.5s ease;\n        }\n      }\n\n      input {\n        width: 3.0rem;\n        height: 0.3rem;\n        font-size: 0.14rem;\n        margin: 0.24rem auto 0.3rem;\n        display: block;\n        border: none;\n        border-bottom: 0.01rem solid darkgray;\n      }\n\n      .verify {\n        font-size: 0.15rem;\n        background-color: rgba(51, 137, 245);\n        border: none;\n        color: white;\n        width: 1rem;\n        height: 0.33rem;\n        position: absolute;\n        bottom: 0.38rem;\n        right: 0;\n        border-radius: 0.1rem;\n      }\n    }\n  }\n}\n\n\n.left-enter-active, .left-leave-active {\n  transition: all 0.3s;\n}\n\n.left-enter {\n  opacity: 0;\n  transform: translateX(-0.2rem);\n}\n\n.left-leave-to {\n  opacity: 0;\n  transform: translateX(0.2rem);\n}\n\n.right-enter-active, .right-leave-active {\n  transition: all 0.3s;\n}\n\n.right-enter {\n  opacity: 0;\n  transform: translateX(0.2rem);\n}\n\n.right-leave-to {\n  opacity: 0;\n  transform: translateX(-0.2rem);\n}\n</style>\n"
  },
  {
    "path": "src/views/Me.vue",
    "content": "<template>\n  <div class=\"me\">\n    <MeHeader></MeHeader>\n    <me-content></me-content>\n  </div>\n</template>\n\n<script>\nimport MeHeader from \"@/components/Me/MeHeader\";\nimport MeContent from \"@/components/Me/MeContent\";\nexport default {\n  name: \"Me\",\n  components:{\n    MeHeader,MeContent\n  }\n}\n</script>\n\n<style scoped>\n\n</style>\n"
  },
  {
    "path": "src/views/Stock.vue",
    "content": "<template>\n  <div class=\"stock\">\n    <div class=\"title\">\n      <h5>股票榜单</h5>\n    </div>\n    <van-tabs v-model=\"active\">\n      <van-tab :title=\"item\" v-for=\"(item,i) in stockType\">\n        <div class=\"rank\">\n          <div class=\"content\" v-for=\"(item,index) in stockRanking[i]\">\n            <span class=\"number\">{{ index + 1 }}</span>\n            <span class=\"name\">{{ item.name }}</span>\n            <span class=\"arrow\">\n              <img src=\"/imgs/up.png\" v-if=\"item.chg>0\">\n              <img src=\"/imgs/down.png\" v-else>\n            </span>\n            <span class=\"current\">{{ item.current }}</span>\n          </div>\n        </div>\n      </van-tab>\n    </van-tabs>\n    <div class=\"loading\" style=\"margin: 0 auto\" v-if=\"!loadingEnd\">\n      <van-loading style=\"text-align:center;\"/>\n    </div>\n  </div>\n</template>\n\n<script>\nimport {getStockRank} from \"@/api/stockApi\";\n\nexport default {\n  name: \"Stock\",\n  data() {\n    return {\n      active: null,\n      stockRanking: [],\n      stockType: ['全球', '沪深', '港股', '美股'],\n      loadingEnd:false,\n    }\n  },\n  async mounted() {\n    for (let i = 0; i < 4; i++) {\n      let res = await getStockRank({type: i});\n      this.loadingEnd = true;\n      this.stockRanking[i] = res.data.items;\n      this.$forceUpdate();\n    }\n  }\n}\n</script>\n\n<style scoped lang=\"less\">\nh5 {\n  font-size: 0.15rem;\n  margin: 0 0 0 0.1rem;\n  line-height: 0.5rem;\n  height: 0.5rem;\n}\n\n.title {\n  background-color: white;\n  border-bottom: 1px #e2e2e2 solid;\n}\n\n.rank {\n  width: 3.5rem;\n  background-color: white;\n  margin: 0.15rem auto;\n  border-radius: 0.1rem;\n  overflow: hidden;\n}\n\n.content {\n  display: flex;\n  margin: 0.1rem 0.1rem 0.2rem;\n  align-items: center;\n\n  span {\n    font-size: 0.14rem;\n    line-height: 0.2rem;\n    height: 0.2rem;\n  }\n\n  .name {\n    flex: 7;\n  }\n\n  .number {\n    flex: 1;\n  }\n\n  .current {\n    flex: 2;\n  }\n\n  .arrow {\n    flex: 1;\n    margin-right: 0.08rem;\n    img {\n      width: 0.14rem;\n      height: 0.14rem;\n      margin: auto auto;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue.config.js",
    "content": "module.exports = {\n\tpublicPath: \"./\",\n};\n"
  }
]