reghao 3 tahun lalu
induk
melakukan
4c2943d82c

+ 4 - 7
src/components/card/status-card.vue

@@ -11,11 +11,11 @@
           >
         </v-avatar>
         <span class="text-body-1 font-weight-light">{{ item.username }}</span>
-        <span class="text-body-1 font-weight-light">{{ item.createAt }}</span>
+        <span class="text-body-1 font-weight-light">{{ item.createdAt }}</span>
       </v-card-title>
 
       <v-card-text class-name="text-h5 font-weight-bold">
-        <span v-html="formatContent(item.text)" />
+        <span v-html="item.text" />
         <v-row v-if="item.imageUrls.length !== 0">
           <v-col
             v-for="imageUrl in item.imageUrls"
@@ -24,8 +24,8 @@
             cols="4"
           >
             <v-img
-              :src="imageUrl"
-              :lazy-src="imageUrl"
+              :src="imageUrl.thumbnailUrl"
+              :lazy-src="imageUrl.thumbnailUrl"
               aspect-ratio="1"
               class="grey lighten-2"
               @click="showImage(item.imageUrls)"
@@ -96,9 +96,6 @@ export default {
     formatTime(time) {
       return DateUtils.format(time)
     },
-    formatContent(content) {
-      return StringUtils.formatContent(content)
-    },
     compareImgSize(size) {
       const count = size / 3
       if (count >= 1) {

+ 6 - 3
src/components/login-form.vue

@@ -80,16 +80,18 @@ export default {
       return encryptor.encrypt(pubkeyR + password)
     },
     submitLogin() {
+      const loginType = 1
       const username = this.username
       const password = this.$options.methods.encryptPassword(this.password, this.pubkey, this.pubkeyR)
       // const password = this.password
-      const captcha = this.captcha
+      const captchaCode = this.captcha
       const r = this.r
       const rememberMe = this.rememberMe
       const user = {
+        loginType,
         username,
         password,
-        captcha,
+        captchaCode,
         r,
         rememberMe
       }
@@ -101,7 +103,7 @@ export default {
         alert('password 字段不符合要求')
         return
       }
-      if (captcha === '') {
+      if (captchaCode === '') {
         alert('captcha 字段不符合要求')
         return
       }
@@ -119,6 +121,7 @@ export default {
           this.pubkey = res.data.pubkey
           this.pubkeyR = res.data.r
         } else {
+          console.log(res.data)
           alert(res.data)
         }
       }).catch(error => {

+ 2 - 2
src/components/register-from.vue

@@ -80,7 +80,7 @@
 
 <script>
 import { randomString, getCaptchaUrl } from '@/utils'
-import { getRegVerifyCode, isUsernameExist, selectUsername, isEmailExist } from '@/api/user/account'
+import { getVerifyCode, isUsernameExist, selectUsername, isEmailExist } from '@/api/user/account'
 
 export default {
   name: 'Register',
@@ -196,7 +196,7 @@ export default {
 
       this.verifyCode.receiver = email
       this.verifyCode.type = 1
-      getRegVerifyCode(this.verifyCode)
+      getVerifyCode(this.verifyCode)
         .then(res => {
           if (res.code === 0) {
             alert(res.msg)

+ 1 - 93
src/components/setting/user-login-log.vue

@@ -1,100 +1,8 @@
 <template>
-  <v-row justify="center" align="center">
-    <v-col>
-      <v-card
-        class="mx-auto"
-        outlined
-      >
-        <v-row justify="center">
-          <v-col cols="10">
-            <span><h2>登录历史</h2>如果发现不熟悉设备,请及时修改密码</span>
-          </v-col>
-        </v-row>
-        <v-row justify="center">
-          <div id="top" />
-          <v-col cols="12">
-            <v-data-table
-              :headers="headers"
-              :items="logList"
-              hide-default-footer
-              :items-per-page="size"
-              :page.sync="page"
-            >
-              <template v-slot:item.loginTime="{ item }">
-                {{ TimeUtil.renderTime(item.loginTime) }}
-              </template>
-
-              <template v-slot:no-data>
-                <v-btn color="primary" @click="getLog">Reset</v-btn>
-              </template>
-            </v-data-table>
-          </v-col>
-        </v-row>
-        <v-row justify="center">
-          <v-pagination
-            v-model="page"
-            :length="length"
-            @input="pageChange"
-          />
-        </v-row>
-      </v-card>
-    </v-col>
-  </v-row>
+  <span><h2>手机换绑</h2></span>
 </template>
 
 <script>
-import TimeUtil from '@/utils/time-util.vue'
-export default {
-  name: 'LoginLog',
-  data() {
-    return {
-      TimeUtil,
-      userInfo: {},
-      logList: [],
-      size: 20,
-      length: 1,
-      page: 1,
-      headers: [
-        {
-          text: '登录时间',
-          sortable: false,
-          value: 'loginTime'
-        },
-        { text: 'IP', sortable: false, value: 'ip' },
-        { text: '登录设备', sortable: false, value: 'ua' }
-      ]
-    }
-  },
-  created() {
-    this.userInfo = this.$store.state.user.userInfo
-    this.getLog()
-  },
-  methods: {
-    getLog() {
-      fetch(`/api/login/log/list?page=${this.page}&limit=${this.size}`, {
-        headers: {
-          'Content-Type': 'application/json; charset=UTF-8',
-          'X-XSRF-TOKEN': this.$cookies.get('XSRF-TOKEN')
-        },
-        method: 'GET',
-        credentials: 'include'
-      }).then(response => response.json())
-        .then(json => {
-          this.logList = json.data.list
-          this.page = json.data.currPage
-          this.length = json.data.totalPage
-        })
-        .catch(e => {
-          return null
-        })
-    },
-    pageChange(value) {
-      this.page = value
-      this.videoList()
-      document.querySelector('#top').scrollIntoView()
-    }
-  }
-}
 </script>
 
 <style>

+ 1 - 1
src/components/upload/filepond-image.vue

@@ -14,7 +14,7 @@
       max-file-size="10MB"
       accepted-file-types="image/png, image/jpeg, image/jpg, image/gif"
       :allow-multiple="true"
-      :max-files="9"
+      :max-files="12"
       :server="server"
       :instant-upload="true"
       @init="handleFilePondInit"

+ 1 - 1
src/layout/index.vue

@@ -98,7 +98,7 @@ export default {
     drawer: true,
     keyword: '',
     items: [
-      { icon: 'mdi-youtube-subscription', text: '直播', link: '/live' },
+      { icon: 'mdi-youtube-subscription', text: '分区', link: '/live' },
       { icon: 'mdi-trending-up', text: '状态', link: '/mblog' },
       { icon: 'mdi-trending-up', text: '知乎', link: '/zhihu' },
       { icon: 'mdi-trending-up', text: '草榴', link: '/t66y' }

+ 2 - 2
src/router/index.js

@@ -297,7 +297,7 @@ const routes = [
       {
         path: '/mblog',
         name: 'Mblog',
-        component: () => import('@/views/home/mblog.vue'),
+        component: () => import('@/views/mblog/mblog.vue'),
         meta: { title: 'bili 状态' }
       },
       {
@@ -394,7 +394,7 @@ router.beforeEach((to, from, next) => {
   if (router.app.$options.store.state.webInfo.name == null) {
     fetch(`/api/media/web/info`, {
       headers: {
-        'Content-Type': 'application/json; charset=UTF-8'
+        'mblog-Type': 'application/json; charset=UTF-8'
       },
       method: 'GET',
       credentials: 'include'

+ 13 - 11
src/views/login.vue

@@ -27,16 +27,16 @@
               <h1>{{ this.$store.state.webInfo.name }} &nbsp; &nbsp;  {{ type }}</h1>
             </v-row>
             <v-row style="height: 48px" />
-            <MobileLoginForm v-show="showLogin" @login="userLogin" />
-            <!--<LoginForm v-show="showLogin === null" @login="userLogin" />
-            <RegisterFrom v-show="showLogin === false" @register="userRegister" />-->
+            <!--            <MobileLoginForm v-show="showLogin" @login="userLogin" />-->
+            <LoginForm v-show="showLogin === true" @login="userLogin" />
+            <RegisterFrom v-show="showLogin === false" @register="userRegister" />
             <v-row justify="center">
-              <!--<v-col cols="5">
+              <v-col cols="5">
                 <v-btn text color="primary">忘记密码</v-btn>
-              </v-col>-->
-              <!--<v-col cols="5" style="text-align:right">
+              </v-col>
+              <v-col cols="5" style="text-align:right">
                 <v-btn text @click="moveRegister">{{ moveMessage }}</v-btn>
-              </v-col>-->
+              </v-col>
             </v-row>
           </v-card>
         </v-col>
@@ -63,14 +63,16 @@
 </template>
 
 <script>
-import MobileLoginForm from '@/components/mobile-login-form.vue'
-/* import LoginForm from '@/components/login-form.vue'
-import RegisterFrom from '@/components/register-from.vue' */
+// import MobileLoginForm from '@/components/mobile-login-form.vue'
+import LoginForm from '@/components/login-form.vue'
+import RegisterFrom from '@/components/register-from.vue'
 
 export default {
   name: 'Login',
   components: {
-    MobileLoginForm
+    // MobileLoginForm
+    LoginForm,
+    RegisterFrom
   },
   data() {
     return {

+ 0 - 0
src/views/Content/src/Content.vue → src/views/mblog/Content.vue


+ 2 - 2
src/components/DetailContent.vue → src/views/mblog/DetailContent.vue

@@ -29,7 +29,7 @@
  
 <script>
 import { mapActions, mapGetters } from 'vuex'
-import * as StringUtils from '../utils/string-utils'
+import * as StringUtils from '../../utils/string-utils'
 export default {
     name: "detail-content",
     computed: {
@@ -179,4 +179,4 @@ export default {
     margin-top: .8rem;
     text-align: center;
 }
-</style>
+</style>

+ 1 - 1
src/views/Content/index.js → src/views/mblog/index.js

@@ -1,5 +1,5 @@
 /*
-import PixelContent from './src/Content'
+import PixelContent from './src/mblog'
 
 PixelContent.install = function(Vue) {
   Vue.component(PixelContent.name, PixelContent)

+ 123 - 57
src/views/home/mblog.vue → src/views/mblog/mblog.vue

@@ -22,7 +22,30 @@
               <v-row>
                 <v-col>
                   <!-- 接收 filepond-image 中 this.$emit('resp', resp) 的数据 -->
-                  <FilePondUploadImage @resp="uploadCallback" />
+                  <!--<FilePondUploadImage @resp="uploadCallback" />-->
+                  <div>
+                    <file-pond
+                      ref="pond"
+                      name="file"
+                      label-idle="选择图片或拖动图片到此处"
+                      label-file-processing="图片正在上传,请稍后"
+                      label-file-processing-aborted="图片上传被取消"
+                      label-tap-to-retry="尝试重试"
+                      label-file-processing-complete="图片上传成功!"
+                      label-max-file-size="上传的图片大小不能超过 10MB"
+                      label-max-file-size-exceeded="上传的图片大小不能超过 10MB"
+                      allow-file-size-validation="true"
+                      max-file-size="10MB"
+                      accepted-file-types="image/png, image/jpeg, image/jpg, image/gif"
+                      :allow-multiple="true"
+                      :max-files="12"
+                      :server="server"
+                      :instant-upload="true"
+                      @init="handleFilePondInit"
+                      @processfile="success"
+                      @removefile="removeFile"
+                    />
+                  </div>
                 </v-col>
               </v-row>
               <v-row dense>
@@ -39,49 +62,30 @@
           <v-col>
             <v-sheet
               class="mx-auto"
-              max-width="700"
-            >
-              <v-slide-group
-                show-arrows
-              >
-                <v-slide-item
-                  v-for="item in 10"
-                  :key="item"
-                  v-slot="{ active }"
-                >
-                  <v-btn
-                    class="mx-2"
-                    :input-value="active"
-                    active-class="purple white--text"
-                    depressed
-                    rounded
-                    @click="selectFollowingUser"
-                  >
-                    关注的用户 {{ item }}
-                    <!--{{ item.username }}-->
-                    <!--{{ this.$store.state.user.userInfo.username }}-->
-                  </v-btn>
-                </v-slide-item>
-              </v-slide-group>
-            </v-sheet>
+              max-width="720"
+            />
           </v-col>
-          <div v-infinite-scroll="loadMore" infinite-scroll-disabled="true" infinite-scroll-distance="10">
-            <v-col cols="12">
-              <v-tabs>
-                <v-tab @click="setType(0)">状态</v-tab>
-                <v-tab @click="setType(1)">视频</v-tab>
-                <v-tab @click="setType(2)">回答</v-tab>
-              </v-tabs>
+          <v-col>
+            <div v-infinite-scroll="loadMore" infinite-scroll-disabled="true" infinite-scroll-distance="10">
+              <v-col cols="12">
+                <v-tabs>
+                  <v-tab @click="setType(0)">状态</v-tab>
+                  <v-tab @click="setType(1)">视频</v-tab>
+                  <v-tab @click="setType(2)">回答</v-tab>
+                </v-tabs>
 
-              <v-row
+                <v-row
                   v-for="item in cardList"
                   :key="item.statusId"
-              >
-                <status-card v-if="cardType === 'status'" :item="item" />
-                <item-card v-if="cardType === 'video'" :video="item" />
-              </v-row>
-            </v-col>
-          </div>
+                  dense
+                >
+                  <status-card v-if="cardType === 'status'" :item="item" />
+                  <item-card v-if="cardType === 'video'" :video="item" />
+                  <status-card v-if="cardType === 'answer'" :item="item" />
+                </v-row>
+              </v-col>
+            </div>
+          </v-col>
         </v-row>
       </v-col>
       <v-col md="4">
@@ -176,20 +180,49 @@ import { pubStatus, statusRecommend } from '@/api/mblog/status'
 import { videoTimeline } from '@/api/media/video'
 import StatusCard from '@/components/card/status-card'
 import ItemCard from '@/components/card/item-card'
-import FilePondUploadImage from '@/components/upload/filepond-image.vue'
+import VueFilePond from 'vue-filepond'
+import 'filepond/dist/filepond.min.css'
+import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
+import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
+import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
+import FilePondPluginFileValidateSize from 'filepond-plugin-file-validate-size'
+
+const FilePond = VueFilePond(
+  FilePondPluginFileValidateType,
+  FilePondPluginImagePreview,
+  FilePondPluginFileValidateSize
+)
+var resp = ''
 
 export default {
   name: 'Home',
   components: {
+    FilePond,
     StatusCard,
-    ItemCard,
-    FilePondUploadImage
+    ItemCard
   },
   data() {
     return {
+      imgMap: {},
+      server: {
+        url: '//file.reghao.cn/api/file/upload/image',
+        revert: null,
+        process: {
+          headers: {
+            'Authorization': this.$store.getters.token
+          },
+          ondata(formData) {
+            return formData
+          },
+          onload(response) {
+            resp = JSON.parse(response)
+          }
+        }
+      },
       statusPost: {
-        imageFileIds: [],
-        content: ''
+        imageUrls: [],
+        content: '',
+        location: ''
       },
       cardType: null,
       cardList: [],
@@ -239,6 +272,49 @@ export default {
     ...mapActions([
       'getMyContent'
     ]),
+    handleFilePondInit() {
+      // FilePond instance methods are available on `this.$refs.pond`
+    },
+    success(error, metadata) {
+      if (error === null) {
+        if (resp.code === 0) {
+          const file = metadata.file
+          const filename = file.name
+          this.imgMap[filename] = resp.data.uploadId
+
+          const imageUrl = {}
+          imageUrl.imageFileId = resp.data.imageFileId
+          imageUrl.thumbnailUrl = resp.data.thumbnailUrl
+          imageUrl.originalUrl = resp.data.originalUrl
+          this.statusPost.imageUrls.push(imageUrl)
+        } else {
+          if (resp.msg != null) {
+            this.message = '上传文件出现异常,请重新上传!' + resp.msg
+          } else {
+            this.message = '上传文件出现异常,请重新上传!'
+          }
+          this.showMessage = true
+        }
+      }
+    },
+    removeFile(error, metadata) {
+      if (error === null) {
+        const file = metadata.file
+        const uploadId = this.imgMap[file.name]
+        fetch('http://file.reghao.cn/api/file/rm/' + uploadId, {
+          headers: {
+            'Authorization': this.$store.getters.token
+          },
+          method: 'DELETE'
+        }).then(response => response.json())
+          .then(json => {
+            console.log('删除 this.statusPost.imageUrls 中相应的图片')
+          })
+          .catch(e => {
+            return null
+          })
+      }
+    },
     setType(type) {
       if (type === this.type) {
         return
@@ -326,27 +402,17 @@ export default {
     selectFollowingUser() {
       console.log('加载选中用户的状态...')
     },
-    uploadCallback(resp) {
-      if (resp.code === 0) {
-        this.statusPost.imageFileIds.push(resp.data.uploadId)
-      } else {
-        if (resp.msg != null) {
-          this.message = '上传文件出现异常,请重新上传!' + resp.msg
-        } else {
-          this.message = '上传文件出现异常,请重新上传!'
-        }
-        this.showMessage = true
-      }
-    },
     publish() {
       if (this.statusPost.content === '') {
         this.message = '内容不能为空'
         this.showMessage = true
+        return
       }
 
       pubStatus(this.statusPost)
         .then(res => {
           if (res.code === 0) {
+            // TODO 发布成功后应该清除本地文本和图片数据
             this.message = '状态已发布'
             this.showMessage = true
           } else {

+ 260 - 0
src/views/mblog/src/Content.vue

@@ -0,0 +1,260 @@
+<template lang="html">
+  <div class="content" @click="goDetailContent">
+    <div class="list-header">
+      <img v-if="x.user" class="avatar" :src="x.user.avatar_large">
+      <div class="user-info">
+        <h3 v-if="x.user" class="user-name">{{ x.user.name }}</h3>
+        <span class="user-source" v-html="x.source" />
+      </div>
+      <span class="user-time">{{ formatTime(x.created_at) }}</span>
+    </div>
+    <div class="list-content">
+      <span class="content-text" v-html="formatContent(x.text)" />
+      <div class="content-img">
+        <ul class="content-img-ul clear-fix">
+          <li v-for="y in x.pic_urls" class="img-li-default" :class="imgClass(x.pic_urls.length)">
+            <div class="img-div" :style="{backgroundImage:'url(' + formatThumbImg(y.thumbnail_pic) + ')'}" @click.stop="imageZoom(y.thumbnail_pic)" />
+          </li>
+        </ul>
+      </div>
+      <div v-if="x.retweeted_status" class="content-re-content">
+        <span
+          class="re-content-text"
+          v-html="formatContent( '@' + x.retweeted_status.user.name + ': '
+            + x.retweeted_status.text)"
+        />
+        <div v-if="x.retweeted_status.pic_urls" class="content-img">
+          <ul class="content-img-ul clear-fix">
+            <li v-for="z in x.retweeted_status.pic_urls" class="img-li-default" :class="imgClass(x.retweeted_status.pic_urls.length)">
+              <div
+                class="img-div"
+                :style="{backgroundImage:'url(' + formatThumbImg(z.thumbnail_pic) + ')'}"
+                @click.stop="imageZoom(z.thumbnail_pic)"
+              />
+            </li>
+          </ul>
+        </div>
+      </div>
+    </div>
+    <div class="list-footer">
+      <div class="footer-tag">
+        <svg viewBox="0 0 62 72" style="display: inline-block; fill: currentcolor; height: 1.25rem; max-width: 100%; position: relative; user-select: none; vertical-align: text-bottom;"><g><path d="M41 31h-9V19a2.999 2.999 0 0 0-4.817-2.386l-21 16a3 3 0 0 0-.001 4.773l21 16a3.006 3.006 0 0 0 3.15.301A2.997 2.997 0 0 0 32 51V39h9c5.514 0 10 4.486 10 10a4 4 0 0 0 8 0c0-9.925-8.075-18-18-18z" /></g></svg>
+        <span class="tag-style">{{ formatNum(x.reposts_count) }}</span>
+      </div>
+      <div class="footer-tag">
+        <svg class="" viewBox="0 0 74 72" style="display: inline-block; fill: currentcolor; height: 1.25rem; max-width: 100%; position: relative; user-select: none; vertical-align: text-bottom;"><g><path d="M70.676 36.644A3 3 0 0 0 68 35h-7V19a4 4 0 0 0-4-4H34a4 4 0 0 0 0 8h18a1 1 0 0 1 1 .998V35h-7a3.001 3.001 0 0 0-2.419 4.775l11 15a3.003 3.003 0 0 0 4.839-.001l11-15a3.001 3.001 0 0 0 .256-3.13zM40.001 48H22a.995.995 0 0 1-.992-.96L21.001 36h7a3.001 3.001 0 0 0 2.419-4.775l-11-15a3.003 3.003 0 0 0-4.839.001l-11 15A3 3 0 0 0 6.001 36h7l.011 16.003a4 4 0 0 0 4 3.997h22.989a4 4 0 0 0 0-8z" /></g></svg>
+        <span class="tag-style">{{ formatNum(x.comments_count) }}</span>
+      </div>
+      <div class="footer-tag">
+        <svg class="" viewBox="0 0 54 72" style="display: inline-block; fill: currentcolor; height: 1.25rem; max-width: 100%; position: relative; user-select: none; vertical-align: text-bottom;"><g><path d="M38.723 12c-7.187 0-11.16 7.306-11.723 8.131C26.437 19.306 22.504 12 15.277 12 8.791 12 3.533 18.163 3.533 24.647 3.533 39.964 21.891 55.907 27 56c5.109-.093 23.467-16.036 23.467-31.353C50.467 18.163 45.209 12 38.723 12z" /></g></svg>
+        <span class="tag-style">{{ formatNum(x.attitudes_count) }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as DateUtils from '../../../utils/date-utils'
+import * as StringUtils from '../../../utils/string-utils'
+import { mapActions } from 'vuex'
+export default {
+  name: 'PixelContent',
+  props: [
+    'x'
+  ],
+  data() {
+    return {
+    }
+  },
+  methods: {
+    ...mapActions([
+      'setImageZoom',
+      'setDetailContent'
+    ]),
+    goDetailContent() {
+      this.setDetailContent(this.x)
+      this.$router.push({ name: 'detail-content' })
+    },
+    formatTime(time) {
+      return DateUtils.format(time)
+    },
+    formatContent(content) {
+      return StringUtils.formatContent(content)
+    },
+    compareImgSize(size) {
+      const count = size / 3
+      if (count >= 1) {
+        return true
+      } else {
+        return false
+      }
+    },
+    formatThumbImg(img) {
+      return StringUtils.formatImgThumb(img)
+    },
+    formatMidImg(img) {
+      return StringUtils.formatImgMiddle(img)
+    },
+    formatNum(num) {
+      return StringUtils.formatNum(num)
+    },
+    imageZoom(url) {
+      this.setImageZoom(this.formatMidImg(url))
+      this.$router.push({ name: 'imageZoom' })
+    },
+    imgClass(size) {
+      let clazz = ''
+      switch (size) {
+        case 1:
+          clazz = 'img-li-one'
+          break
+        case 2:
+        case 4:
+          clazz = 'img-li-two'
+          break
+        default:
+          clazz = 'img-li-other'
+          break
+      }
+      return clazz
+    }
+  }
+}
+</script>
+
+<style lang="css">
+a {
+    color: #007AFF;
+}
+
+.content .list-header {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-flow: row;
+}
+
+.content .list-footer {
+    width: 68%;
+    margin-top: .7rem;
+    color: #cdcdcd;
+    display: flex;
+    flex-flow: row;
+}
+
+.content .list-footer .footer-tag {
+    width: 100%;
+    height: 1.3rem;
+    color: inherit;
+}
+
+.content .list-footer .footer-tag .tag-style {
+    font-size: 12px;
+}
+
+.content .avatar {
+    width: 3.5rem;
+    height: 3.5rem;
+    border-radius: 50%;
+    border: 1px solid rgba(0, 0, 0, .05);
+}
+
+.content .user-info {
+    margin-left: 1rem;
+    display: flex;
+    flex-flow: column;
+    flex: 1;
+}
+
+.content .user-time {
+    font-size: 1rem;
+    color: #A4A8AC;
+    height: 100%;
+    display: table-cell
+}
+
+.content .user-info .user-name {
+    margin: 0;
+    flex: 1;
+    font-size: 1.5rem;
+}
+
+.content .user-info .user-source {
+    margin: 0;
+    flex: 1;
+    font-size: 1rem;
+    color: #A4A8AC
+}
+
+.content .user-info .user-source a {
+    color: #A4A8AC;
+}
+
+.content .list-content {
+    margin-top: .7rem;
+}
+
+.content .list-content .content-text {
+    font-size: 1.3rem;
+    line-height: 1rem;
+}
+
+.content .list-content .content-at {
+    color: #007AFF;
+}
+
+.clear-fix::after {
+    content: '';
+    display: block;
+    clear: both;
+}
+
+.content .list-content .content-img .content-img-ul {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+}
+
+.content-img .content-img-ul .img-li-default {
+    float: left;
+    height: 0;
+    margin-top: .4rem;
+    margin-right: .4rem
+}
+
+.content-img .content-img-ul .img-li-one {
+    width: 52%;
+    padding-bottom: 52%;
+}
+
+.content-img .content-img-ul .img-li-two {
+    width: 43%;
+    padding-bottom: 43%;
+}
+
+.content-img .content-img-ul .img-li-other {
+    width: 28%;
+    padding-bottom: 28%;
+}
+
+.content-img .content-img-ul .img-div {
+    width: 100%;
+    padding-bottom: 100%;
+    background-position: center;
+    background-repeat: no-repeat;
+}
+
+.content .list-content .content-re-content {
+    width: 100%;
+    margin-top: .7rem;
+    border: 1px solid rgba(0, 0, 0, .05);
+    border-radius: 3px;
+    background-color: #f5f5f5;
+    padding: .5rem;
+}
+
+.content .list-content .content-re-content .re-content-text {
+    font-size: 1.3rem;
+    line-height: 1rem;
+}
+</style>

+ 91 - 4
src/views/user/LoginHistory.vue

@@ -1,12 +1,99 @@
 <template>
-  <div>
-    登录历史
-  </div>
+  <v-row justify="center" align="center">
+    <v-col>
+      <v-card
+        class="mx-auto"
+        outlined
+      >
+        <v-row justify="center">
+          <v-col cols="10">
+            <span><h2>登录历史</h2>如果发现不熟悉设备,请及时修改密码</span>
+          </v-col>
+        </v-row>
+        <v-row justify="center">
+          <div id="top" />
+          <v-col cols="12">
+            <v-data-table
+              :headers="headers"
+              :items="logList"
+              hide-default-footer
+              :items-per-page="size"
+              :page.sync="page"
+            >
+              <template v-slot:item.loginTime="{ item }">
+                {{ TimeUtil.renderTime(item.loginTime) }}
+              </template>
+
+              <template v-slot:no-data>
+                <v-btn color="primary" @click="getLog">Reset</v-btn>
+              </template>
+            </v-data-table>
+          </v-col>
+        </v-row>
+        <v-row justify="center">
+          <v-pagination
+            v-model="page"
+            :length="length"
+            @input="pageChange"
+          />
+        </v-row>
+      </v-card>
+    </v-col>
+  </v-row>
 </template>
 
 <script>
+import TimeUtil from '@/utils/time-util.vue'
 export default {
-  name: 'UserLoginHistory'
+  name: 'LoginLog',
+  data() {
+    return {
+      TimeUtil,
+      userInfo: {},
+      logList: [],
+      size: 20,
+      length: 1,
+      page: 1,
+      headers: [
+        {
+          text: '登录时间',
+          sortable: false,
+          value: 'loginTime'
+        },
+        { text: 'IP', sortable: false, value: 'ip' },
+        { text: '登录设备', sortable: false, value: 'ua' }
+      ]
+    }
+  },
+  created() {
+    this.userInfo = this.$store.state.user.userInfo
+    this.getLog()
+  },
+  methods: {
+    getLog() {
+      fetch(`/api/login/log/list?page=${this.page}&limit=${this.size}`, {
+        headers: {
+          'Content-Type': 'application/json; charset=UTF-8',
+          'X-XSRF-TOKEN': this.$cookies.get('XSRF-TOKEN')
+        },
+        method: 'GET',
+        credentials: 'include'
+      }).then(response => response.json())
+        .then(json => {
+          this.logList = json.data.list
+          this.page = json.data.currPage
+          this.length = json.data.totalPage
+        })
+        .catch(e => {
+          return null
+        })
+    },
+    pageChange(value) {
+      this.page = value
+      this.videoList()
+      document.querySelector('#top').scrollIntoView()
+    }
+  }
 }
 </script>
 

+ 7 - 8
src/views/user/account.vue

@@ -1,15 +1,15 @@
 <template>
   <v-container fill-height>
     <v-tabs>
-      <v-tab @click="setType(0)">基本</v-tab>
+      <v-tab @click="setType(0)">基本信息</v-tab>
       <v-tab @click="setType(1)">头像设置</v-tab>
-      <v-tab @click="setType(2)">首页顶部图</v-tab>
-      <v-tab @click="setType(3)">密码邮箱手机设置</v-tab>
-      <v-tab @click="setType(4)">登录历史</v-tab>
+      <v-tab @click="setType(2)">首页背景</v-tab>
+      <v-tab @click="setType(3)">密码修改</v-tab>
+      <v-tab @click="setType(4)">手机修改</v-tab>
     </v-tabs>
-    <BaseSetting v-if="type == 0" />
-    <HeadSetting v-if="type == 1" />
-    <TopSetting v-if="type == 2" />
+    <BaseSetting v-if="type === 0" />
+    <HeadSetting v-if="type === 1" />
+    <TopSetting v-if="type === 2" />
     <PasswordSetting v-if="type === 3" />
     <LoginLog v-if="type === 4" />
   </v-container>
@@ -47,5 +47,4 @@ export default {
 </script>
 
 <style>
-
 </style>

+ 10 - 1
src/views/user/home.vue

@@ -17,7 +17,8 @@
             <v-img :src="userInfo.avatarUrl" />
           </v-avatar>
           <h2 style="margin-top: 20px;margin-left: 100px;">
-            {{ userInfo.username }}
+            <!--{{ userInfo.username }}-->
+            {{ userInfo.screenName }}
             <!--<v-chip color="yellow" @click="goToVip()">
               <v-chip v-if="Power.checkPower(userInfo) === 'vip'" color="yellow" @click="goTOVIP()">
               小会员
@@ -141,6 +142,14 @@ export default {
     }
   },
   created() {
+    const loginUserInfo = this.$store.state.user.userInfo
+    if (loginUserInfo === null) {
+      console.log('匿名用户')
+    } else {
+      console.log('已登录用户')
+      console.log(loginUserInfo)
+    }
+
     const userIdStr = this.$route.params.userId
     if (userIdStr === undefined) {
       // 从"个人中心"加载

+ 2 - 2
src/views/user/userindex.vue

@@ -58,8 +58,8 @@ export default {
       { icon: 'mdi-account-multiple', text: '收藏列表', link: '/user/favlist' },
       { icon: 'mdi-account-multiple', text: '历史记录', link: '/user/hislist' },
       { icon: 'mdi-account-multiple', text: '关注管理', link: '/user/follow' },
-      { icon: 'mdi-account-multiple', text: '小会员', link: '/user/vip' },
-      { icon: 'mdi-account-multiple', text: '登录历史', link: '/user/loginhistory' }
+      { icon: 'mdi-account-multiple', text: '登录历史', link: '/user/loginhistory' },
+      { icon: 'mdi-account-multiple', text: '小会员', link: '/user/vip' }
     ],
     adminList: [
       { icon: 'mdi-playlist-edit', text: '分类管理', link: '/studio/admin/category' },