Quellcode durchsuchen

添加 /admin 路由, 页面和接口

reghao vor 2 Jahren
Ursprung
Commit
690e9a0357

+ 73 - 68
package-lock.json

@@ -1432,9 +1432,9 @@
           },
           "dependencies": {
             "@babel/parser": {
-              "version": "7.23.4",
-              "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.4.tgz",
-              "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ=="
+              "version": "7.23.5",
+              "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.5.tgz",
+              "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ=="
             },
             "@liripeng/vue-audio-player": {
               "version": "1.4.1-beta1",
@@ -1481,15 +1481,15 @@
                           },
                           "dependencies": {
                             "vue": {
-                              "version": "3.3.8",
-                              "resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
-                              "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
+                              "version": "3.3.10",
+                              "resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.10.tgz",
+                              "integrity": "sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==",
                               "requires": {
-                                "@vue/compiler-dom": "3.3.8",
-                                "@vue/compiler-sfc": "3.3.8",
-                                "@vue/runtime-dom": "3.3.8",
-                                "@vue/server-renderer": "3.3.8",
-                                "@vue/shared": "3.3.8"
+                                "@vue/compiler-dom": "3.3.10",
+                                "@vue/compiler-sfc": "3.3.10",
+                                "@vue/runtime-dom": "3.3.10",
+                                "@vue/server-renderer": "3.3.10",
+                                "@vue/shared": "3.3.10"
                               }
                             },
                             "vue-router": {
@@ -1535,103 +1535,103 @@
               }
             },
             "@vue/compiler-core": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
-              "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.10.tgz",
+              "integrity": "sha512-doe0hODR1+i1menPkRzJ5MNR6G+9uiZHIknK3Zn5OcIztu6GGw7u0XUzf3AgB8h/dfsZC9eouzoLo3c3+N/cVA==",
               "requires": {
-                "@babel/parser": "^7.23.0",
-                "@vue/shared": "3.3.8",
+                "@babel/parser": "^7.23.5",
+                "@vue/shared": "3.3.10",
                 "estree-walker": "^2.0.2",
                 "source-map-js": "^1.0.2"
               }
             },
             "@vue/compiler-dom": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
-              "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.10.tgz",
+              "integrity": "sha512-NCrqF5fm10GXZIK0GrEAauBqdy+F2LZRt3yNHzrYjpYBuRssQbuPLtSnSNjyR9luHKkWSH8we5LMB3g+4z2HvA==",
               "requires": {
-                "@vue/compiler-core": "3.3.8",
-                "@vue/shared": "3.3.8"
+                "@vue/compiler-core": "3.3.10",
+                "@vue/shared": "3.3.10"
               }
             },
             "@vue/compiler-sfc": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
-              "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.10.tgz",
+              "integrity": "sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==",
               "requires": {
-                "@babel/parser": "^7.23.0",
-                "@vue/compiler-core": "3.3.8",
-                "@vue/compiler-dom": "3.3.8",
-                "@vue/compiler-ssr": "3.3.8",
-                "@vue/reactivity-transform": "3.3.8",
-                "@vue/shared": "3.3.8",
+                "@babel/parser": "^7.23.5",
+                "@vue/compiler-core": "3.3.10",
+                "@vue/compiler-dom": "3.3.10",
+                "@vue/compiler-ssr": "3.3.10",
+                "@vue/reactivity-transform": "3.3.10",
+                "@vue/shared": "3.3.10",
                 "estree-walker": "^2.0.2",
                 "magic-string": "^0.30.5",
-                "postcss": "^8.4.31",
+                "postcss": "^8.4.32",
                 "source-map-js": "^1.0.2"
               }
             },
             "@vue/compiler-ssr": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
-              "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.10.tgz",
+              "integrity": "sha512-12iM4jA4GEbskwXMmPcskK5wImc2ohKm408+o9iox3tfN9qua8xL0THIZtoe9OJHnXP4eOWZpgCAAThEveNlqQ==",
               "requires": {
-                "@vue/compiler-dom": "3.3.8",
-                "@vue/shared": "3.3.8"
+                "@vue/compiler-dom": "3.3.10",
+                "@vue/shared": "3.3.10"
               }
             },
             "@vue/reactivity": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
-              "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.10.tgz",
+              "integrity": "sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==",
               "requires": {
-                "@vue/shared": "3.3.8"
+                "@vue/shared": "3.3.10"
               }
             },
             "@vue/reactivity-transform": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
-              "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.10.tgz",
+              "integrity": "sha512-0xBdk+CKHWT+Gev8oZ63Tc0qFfj935YZx+UAynlutnrDZ4diFCVFMWixn65HzjE3S1iJppWOo6Tt1OzASH7VEg==",
               "requires": {
-                "@babel/parser": "^7.23.0",
-                "@vue/compiler-core": "3.3.8",
-                "@vue/shared": "3.3.8",
+                "@babel/parser": "^7.23.5",
+                "@vue/compiler-core": "3.3.10",
+                "@vue/shared": "3.3.10",
                 "estree-walker": "^2.0.2",
                 "magic-string": "^0.30.5"
               }
             },
             "@vue/runtime-core": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
-              "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.10.tgz",
+              "integrity": "sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==",
               "requires": {
-                "@vue/reactivity": "3.3.8",
-                "@vue/shared": "3.3.8"
+                "@vue/reactivity": "3.3.10",
+                "@vue/shared": "3.3.10"
               }
             },
             "@vue/runtime-dom": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
-              "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.10.tgz",
+              "integrity": "sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==",
               "requires": {
-                "@vue/runtime-core": "3.3.8",
-                "@vue/shared": "3.3.8",
+                "@vue/runtime-core": "3.3.10",
+                "@vue/shared": "3.3.10",
                 "csstype": "^3.1.2"
               }
             },
             "@vue/server-renderer": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
-              "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.10.tgz",
+              "integrity": "sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==",
               "requires": {
-                "@vue/compiler-ssr": "3.3.8",
-                "@vue/shared": "3.3.8"
+                "@vue/compiler-ssr": "3.3.10",
+                "@vue/shared": "3.3.10"
               }
             },
             "@vue/shared": {
-              "version": "3.3.8",
-              "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
-              "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
+              "version": "3.3.10",
+              "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.10.tgz",
+              "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw=="
             },
             "magic-string": {
               "version": "0.30.5",
@@ -1641,12 +1641,17 @@
                 "@jridgewell/sourcemap-codec": "^1.4.15"
               }
             },
+            "nanoid": {
+              "version": "3.3.7",
+              "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+              "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
+            },
             "postcss": {
-              "version": "8.4.31",
-              "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz",
-              "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+              "version": "8.4.32",
+              "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.32.tgz",
+              "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
               "requires": {
-                "nanoid": "^3.3.6",
+                "nanoid": "^3.3.7",
                 "picocolors": "^1.0.0",
                 "source-map-js": "^1.0.2"
               }

+ 27 - 0
src/api/admin.js

@@ -0,0 +1,27 @@
+import { get, post } from '@/utils/request'
+
+const adminApi = {
+  siteNoticeApi: '/api/user/account/check/username',
+  userListApi: '/api/user/account/check/username',
+  postListApi: '/api/user/account/select/username'
+}
+
+// 获取站点公告
+export function getSiteNotice() {
+  return get(adminApi.siteNoticeApi)
+}
+
+// 更新站点公告
+export function updateSiteNotice(data) {
+  return post(adminApi.siteNoticeApi, data)
+}
+
+// 获取用户列表
+export function getUserList(page) {
+  return get(adminApi.userListApi + '?page=' + page)
+}
+
+// 获取稿件列表
+export function getPostList(page) {
+  return get(adminApi.postListApi + '?page=' + page)
+}

+ 43 - 7
src/router/index.js

@@ -23,7 +23,6 @@ const DiscoverIndex = () => import('views/home/Discover')
 const BdMap = () => import('views/home/BdMap')
 const AMap = () => import('views/home/AMap')
 const Vip = () => import('views/vip/Vip')
-const DataSource = () => import('views/home/DataSource')
 
 // ********************************************************************************************************************
 // 用户前台主页
@@ -64,6 +63,15 @@ const PostEditImage = () => import('components/upload/EditImage')
 const UserPostArticle = () => import('views/post/ArticlePost')
 const PostEditArticle = () => import('components/upload/EditArticle')
 
+// ********************************************************************************************************************
+// 后台主页
+const Admin = () => import('views/admin/Admin')
+const SiteConfig = () => import('views/admin/SiteConfig')
+const UserList = () => import('views/admin/UserList')
+const PostList = () => import('views/admin/PostList')
+const DataSource = () => import('views/admin/DataSource')
+
+// ********************************************************************************************************************
 // 使用安装路由插件
 Vue.use(VueRouter)
 const routes = [
@@ -223,6 +231,40 @@ const routes = [
       }
     ]
   },
+  // ********************************************************************************************************************
+  {
+    path: '/admin',
+    name: 'Admin',
+    component: Admin,
+    meta: { needAuth: true },
+    children: [
+      {
+        path: '/admin/site',
+        name: '站点配置',
+        component: SiteConfig,
+        meta: { needAuth: true }
+      },
+      {
+        path: '/admin/user',
+        name: '用户列表',
+        component: UserList,
+        meta: { needAuth: true }
+      },
+      {
+        path: '/admin/post',
+        name: '稿件列表',
+        component: PostList,
+        meta: { needAuth: true }
+      },
+      {
+        path: '/admin/datasource',
+        name: '数据源',
+        component: DataSource,
+        meta: { needAuth: true }
+      }
+    ]
+  },
+  // ********************************************************************************************************************
   {
     path: '/timeline',
     name: 'TimelineIndex',
@@ -410,12 +452,6 @@ const routes = [
     component: Vip,
     meta: { needAuth: false }
   },
-  {
-    path: '/datasource',
-    name: 'DataSource',
-    component: DataSource,
-    meta: { needAuth: false }
-  },
   {
     path: '/login',
     name: 'Login',

+ 85 - 0
src/views/admin/Admin.vue

@@ -0,0 +1,85 @@
+<template>
+  <el-container>
+    <el-aside id="aside-style">
+      <el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
+        <el-radio-button :label="false">展开</el-radio-button>
+        <el-radio-button :label="true">收起</el-radio-button>
+      </el-radio-group>
+      <el-menu
+        :default-active="this.$route.path"
+        router
+        class="el-menu-vertical-demo"
+        :collapse="isCollapse"
+        @open="handleOpen"
+        @close="handleClose"
+      >
+        <el-menu-item index="/admin/site">
+          <i class="el-icon-apple" />
+          <span slot="title">站点配置</span>
+        </el-menu-item>
+        <el-menu-item index="/admin/user">
+          <i class="el-icon-user" />
+          <span slot="title">用户列表</span>
+        </el-menu-item>
+        <el-menu-item index="/admin/post">
+          <i class="el-icon-postcard" />
+          <span slot="title">稿件列表</span>
+        </el-menu-item>
+        <el-menu-item index="/admin/datasource">
+          <i class="el-icon-date" />
+          <span slot="title">数据源</span>
+        </el-menu-item>
+      </el-menu>
+    </el-aside>
+    <el-main>
+      <!--      <el-breadcrumb separator-class="el-icon-arrow-right">
+        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+        <el-breadcrumb-item v-text="this.$router.currentRoute.name"></el-breadcrumb-item>
+      </el-breadcrumb>-->
+      <router-view />
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      isCollapse: false,
+      navList: [
+        { path: '/my/account', name: '我的帐号', icon: 'el-icon-upload' }
+      ]
+    }
+  },
+  /*  watch: {
+    // 地址栏 url 发生变化时重新加载本页面
+    $route() {
+      this.$router.go()
+    }
+  },*/
+  created() {
+    document.title = '后台主页'
+  },
+  methods: {
+    handleOpen(key, keyPath) {
+      console.log(key, keyPath)
+    },
+    handleClose(key, keyPath) {
+      console.log(key, keyPath)
+    }
+  }
+}
+</script>
+
+<style>
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  min-height: 800px;
+}
+
+#aside-style {
+  min-width: 120px;
+  max-width: 240px;
+  width: 30%;
+}
+</style>

+ 0 - 0
src/views/home/DataSource.vue → src/views/admin/DataSource.vue


+ 431 - 0
src/views/admin/PostList.vue

@@ -0,0 +1,431 @@
+<template>
+  <el-row>
+    <el-row>
+      <el-table
+        :data="dataList"
+        border
+        style="width: 100%"
+      >
+        <el-table-column
+          fixed="left"
+          label="No"
+          type="index"
+        />
+        <el-table-column
+          prop="pubDate"
+          label="发布时间"
+          width="150"
+        />
+        <el-table-column
+          prop="coverUrl"
+          label="封面"
+          width="90"
+        >
+          <template slot-scope="scope">
+            <el-image :src="scope.row.coverUrl" min-width="30" height="20" />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="videoId"
+          label="视频 ID"
+          width="120"
+        >
+          <template slot-scope="scope">
+            <router-link target="_blank" :to="`/video/${scope.row.videoId}`">
+              <span>{{ scope.row.videoId }}</span>
+            </router-link>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="title"
+          label="标题"
+          width="150"
+        />
+        <el-table-column
+          prop="description"
+          label="描述"
+        >
+          <template slot-scope="scope">
+            <span v-if="scope.row.description !== null">-</span>
+            <span v-else>{{ scope.row.description }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="duration"
+          label="时长"
+        />
+        <el-table-column
+          prop="direction"
+          label="方向"
+        />
+        <el-table-column
+          prop="scope"
+          label="可见范围"
+          width="120"
+        >
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" content="点击修改可见范围" placement="top-end">
+              <el-button
+                v-if="scope.row.scope === 1"
+                size="mini"
+                @click="handleScope(scope.$index, scope.row)"
+              >本人可见</el-button>
+              <el-button
+                v-else-if="scope.row.scope === 2"
+                size="mini"
+                type="success"
+                @click="handleScope(scope.$index, scope.row)"
+              >所有人可见</el-button>
+              <el-button
+                v-else-if="scope.row.scope === 3"
+                size="mini"
+                type="warning"
+                @click="handleScope(scope.$index, scope.row)"
+              >VIP 可见</el-button>
+              <el-button
+                v-else
+                size="mini"
+                type="danger"
+                @click="handleScope(scope.$index, scope.row)"
+              >验证码可见</el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="scope"
+          label="审核状态"
+          width="120"
+        >
+          <template slot-scope="scope">
+            <el-tag v-if="scope.row.status === 1" :type="'warning'" disable-transitions>
+              审核中
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 2" :type="'success'" disable-transitions>
+              审核通过
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 3" :type="'danger'" disable-transitions>
+              审核未通过
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 4" :type="'danger'" disable-transitions>
+              下架
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 5" :type="'danger'" disable-transitions>
+              待缓存
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 6" :type="'danger'" disable-transitions>
+              缓存中
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 7" :type="'danger'" disable-transitions>
+              缓存失败
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 8" :type="'danger'" disable-transitions>
+              封面不存在
+            </el-tag>
+            <el-tag v-else :type="'danger'" disable-transitions>
+              视频不存在
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="duration"
+          label="发布用户"
+        />
+        <el-table-column
+          label="视频资源"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              @click="handleVideoResource(scope.$index, scope.row)"
+            >查看</el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          fixed="right"
+          label="操作"
+          width="280"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              @click="handlePreview(scope.$index, scope.row)"
+            >预览</el-button>
+            <el-button
+              size="mini"
+              @click="handleEdit(scope.$index, scope.row)"
+            >编辑</el-button>
+            <el-button
+              size="mini"
+              type="danger"
+              @click="handleDelete(scope.$index, scope.row)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 视频资源对话框 -->
+      <el-dialog
+        append-to-body
+        :visible.sync="showVideoResourceDialog"
+        width="70%"
+        center
+      >
+        <el-table
+          :data="videoResources"
+          border
+          style="width: 100%"
+        >
+          <el-table-column
+            label="No"
+            type="index"
+          />
+          <el-table-column
+            prop="videoCodec"
+            label="原始视频"
+          />
+          <el-table-column
+            prop="videoCodec"
+            label="视频编码"
+          />
+          <el-table-column
+            prop="audioCodec"
+            label="音频编码"
+          />
+          <el-table-column
+            prop="quality"
+            label="画质"
+          />
+          <el-table-column
+            prop="urlType"
+            label="URL 类型"
+          />
+          <el-table-column
+            prop="url"
+            label="URL"
+          />
+          <el-table-column
+            fixed="right"
+            label="操作"
+            width="280"
+          >
+            <template slot-scope="scope">
+              <el-button
+                size="mini"
+                @click="handleConvert(scope.$index, scope.row)"
+              >转码</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-dialog>
+      <!-- 修改视频可见范围对话框 -->
+      <el-dialog
+        append-to-body
+        :visible.sync="showEditScopeDialog"
+        width="30%"
+        center
+      >
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>修改视频可见范围</span>
+            <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateScope">更新</el-button>
+          </div>
+          <div class="text item">
+            <el-select v-model="form.scope" placeholder="选择可见范围">
+              <el-option label="本人可见" value="1" />
+              <el-option label="所有人可见" value="2" />
+              <el-option label="VIP 可见" value="3" />
+              <el-option label="验证码可见" value="4" />
+            </el-select>
+          </div>
+        </el-card>
+      </el-dialog>
+      <!-- 视频预览对话框 -->
+      <el-dialog
+        title="预览视频"
+        append-to-body
+        :visible.sync="showPreviewDialog"
+        :before-close="handleDialogClose"
+        width="70%"
+        center
+      >
+        <template>
+          <video-preview-player :video-prop.sync="this.videoProp" />
+        </template>
+      </el-dialog>
+    </el-row>
+    <el-row>
+      <el-pagination
+        background
+        :small="screenWidth <= 768"
+        layout="prev, pager, next"
+        :page-size="pageSize"
+        :current-page="currentPage"
+        :total="totalSize"
+        @current-change="handleCurrentChange"
+        @prev-click="handleCurrentChange"
+        @next-click="handleCurrentChange"
+      />
+    </el-row>
+  </el-row>
+</template>
+
+<script>
+import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
+import { updateVideoScope, videoInfo, deleteVideoPost, getVideoResource, convertVideo, getVideoPosts } from '@/api/video'
+
+export default {
+  name: 'PostList',
+  components: { VideoPreviewPlayer },
+  data() {
+    return {
+      // 屏幕宽度, 为了控制分页条的大小
+      screenWidth: document.body.clientWidth,
+      currentPage: 1,
+      pageSize: 12,
+      totalSize: 0,
+      dataList: [],
+      // **********************************************************************
+      videoProp: null,
+      showVideoResourceDialog: false,
+      showEditScopeDialog: false,
+      showPreviewDialog: false,
+      form: {
+        videoId: null,
+        scope: 1
+      },
+      videoResources: []
+    }
+  },
+  created() {
+    document.title = '稿件列表'
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(pageNumber) {
+      this.currentPage = pageNumber
+      this.getData()
+      // 回到顶部
+      scrollTo(0, 0)
+    },
+    getData() {
+      this.dataList = []
+      getVideoPosts(this.currentPage).then(resp => {
+        if (resp.code === 0) {
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+        } else {
+          this.$notify({
+            title: '提示',
+            message: resp.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'error',
+          duration: 3000
+        })
+      })
+    },
+    handleVideoResource(index, row) {
+      const videoId = row.videoId
+      this.showVideoResourceDialog = true
+
+      getVideoResource(videoId).then(resp => {
+        if (resp.code === 0) {
+          this.videoResources = resp.data
+        }
+      })
+    },
+    handleScope(index, row) {
+      this.form.videoId = row.videoId
+      this.form.scope = '' + row.scope
+      this.showEditScopeDialog = true
+    },
+    handleDialogClose(done) {
+      this.showPreviewDialog = false
+      this.videoProp = {
+        videoId: null,
+        play: false
+      }
+      done()
+    },
+    handleConvert(index, row) {
+      convertVideo(row.videoId).then(res => {
+        if (res.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '视频转码请求已提交',
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      })
+    },
+    handlePreview(index, row) {
+      videoInfo(row.videoId).then(res => {
+        if (res.code === 0) {
+          this.showPreviewDialog = true
+          this.videoProp = {
+            videoId: res.data.videoId,
+            play: true
+          }
+        }
+      })
+    },
+    handleEdit(index, row) {
+      const path = '/my/post/edit/video/' + row.videoId
+      this.$router.push(path)
+    },
+    handleDelete(index, row) {
+      this.$confirm('确定要删除 ' + row.title + '?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteVideoPost(row.videoId).then(res => {
+          if (res.code === 0) {
+            this.$notify({
+              title: '提示',
+              message: '稿件已删除',
+              type: 'warning',
+              duration: 3000
+            })
+            this.$router.go(0)
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+    onUpdateScope() {
+      this.showEditScopeDialog = false
+      updateVideoScope(this.form).then(res => {
+        if (res.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '视频可见范围已更新',
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'warning',
+          duration: 3000
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style>
+</style>

+ 60 - 0
src/views/admin/SiteConfig.vue

@@ -0,0 +1,60 @@
+<template>
+  <el-row class="movie-list">
+    <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+      <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>当前公告</span>
+          </div>
+          <div class="text item">
+            <span v-html="loginUser.signature"></span>
+          </div>
+        </el-card>
+      </el-row>
+    </el-col>
+    <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+      <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>新公告</span>
+            <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdate">更新</el-button>
+          </div>
+          <div class="text item">
+            <el-form ref="form" :model="loginUser">
+              <el-form-item>
+                <el-input v-model="loginUser.signature" type="textarea" autosize style="padding-right: 1px" />
+              </el-form-item>
+            </el-form>
+          </div>
+        </el-card>
+      </el-row>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+import { getAuthedUser } from '@/utils/auth'
+
+export default {
+  name: 'SiteConfig',
+  data() {
+    return {
+      loginUser: null
+    }
+  },
+  created() {
+    document.title = '站点配置'
+    this.loginUser = getAuthedUser()
+  },
+  mounted() {
+  },
+  methods: {
+    onUpdate() {
+      console.log(this.loginUser.signature)
+    }
+  }
+}
+</script>
+
+<style>
+</style>

+ 257 - 0
src/views/admin/UserList.vue

@@ -0,0 +1,257 @@
+<template>
+  <el-row>
+    <el-row>
+      <el-table
+        :data="dataList"
+        border
+        style="width: 100%"
+      >
+        <el-table-column
+          fixed="left"
+          label="No"
+          type="index"
+        />
+        <el-table-column
+          prop="coverUrl"
+          label="头像"
+          width="90"
+        >
+          <template slot-scope="scope">
+            <el-image :src="scope.row.coverUrl" min-width="30" height="20" />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="videoId"
+          label="用户 ID"
+          width="120"
+        >
+          <template slot-scope="scope">
+            <router-link target="_blank" :to="`/video/${scope.row.videoId}`">
+              <span>{{ scope.row.videoId }}</span>
+            </router-link>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="title"
+          label="性别"
+          width="150"
+        />
+        <el-table-column
+          prop="description"
+          label="签名"
+        >
+          <template slot-scope="scope">
+            <span v-if="scope.row.description !== null">-</span>
+            <span v-else>{{ scope.row.description }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="duration"
+          label="关注数"
+        />
+        <el-table-column
+          prop="direction"
+          label="粉丝数"
+        />
+        <el-table-column
+          prop="scope"
+          label="帐号状态"
+          width="120"
+        >
+          <template slot-scope="scope">
+            <el-tag v-if="scope.row.status === 1" :type="'warning'" disable-transitions>
+              正常
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 2" :type="'success'" disable-transitions>
+              禁言
+            </el-tag>
+            <el-tag v-else-if="scope.row.status === 3" :type="'danger'" disable-transitions>
+              封禁
+            </el-tag>
+            <el-tag v-else :type="'danger'" disable-transitions>
+              注销
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          fixed="right"
+          label="操作"
+          width="280"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              @click="handleEdit(scope.$index, scope.row)"
+            >编辑</el-button>
+            <el-button
+              size="mini"
+              type="danger"
+              @click="handleDelete(scope.$index, scope.row)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 修改视频可见范围对话框 -->
+      <el-dialog
+        append-to-body
+        :visible.sync="showEditScopeDialog"
+        width="30%"
+        center
+      >
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>修改视频可见范围</span>
+            <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateScope">更新</el-button>
+          </div>
+          <div class="text item">
+            <el-select v-model="form.scope" placeholder="选择可见范围">
+              <el-option label="本人可见" value="1" />
+              <el-option label="所有人可见" value="2" />
+              <el-option label="VIP 可见" value="3" />
+              <el-option label="验证码可见" value="4" />
+            </el-select>
+          </div>
+        </el-card>
+      </el-dialog>
+    </el-row>
+    <el-row>
+      <el-pagination
+        background
+        :small="screenWidth <= 768"
+        layout="prev, pager, next"
+        :page-size="pageSize"
+        :current-page="currentPage"
+        :total="totalSize"
+        @current-change="handleCurrentChange"
+        @prev-click="handleCurrentChange"
+        @next-click="handleCurrentChange"
+      />
+    </el-row>
+  </el-row>
+</template>
+
+<script>
+import { updateVideoScope, videoInfo, deleteVideoPost, getVideoResource, convertVideo, getVideoPosts } from '@/api/video'
+
+export default {
+  name: 'UserList',
+  data() {
+    return {
+      // 屏幕宽度, 为了控制分页条的大小
+      screenWidth: document.body.clientWidth,
+      currentPage: 1,
+      pageSize: 12,
+      totalSize: 0,
+      dataList: [],
+      // **********************************************************************
+      videoProp: null,
+      showVideoResourceDialog: false,
+      showEditScopeDialog: false,
+      showPreviewDialog: false,
+      form: {
+        videoId: null,
+        scope: 1
+      },
+      videoResources: []
+    }
+  },
+  created() {
+    document.title = '用户列表'
+  },
+  methods: {
+    handleCurrentChange(pageNumber) {
+      this.currentPage = pageNumber
+      this.getData()
+      // 回到顶部
+      scrollTo(0, 0)
+    },
+    getData() {
+      this.dataList = []
+      getVideoPosts(this.currentPage).then(resp => {
+        if (resp.code === 0) {
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+        } else {
+          this.$notify({
+            title: '提示',
+            message: resp.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'error',
+          duration: 3000
+        })
+      })
+    },
+    handleScope(index, row) {
+      this.form.videoId = row.videoId
+      this.form.scope = '' + row.scope
+      this.showEditScopeDialog = true
+    },
+    handleDialogClose(done) {
+      this.showPreviewDialog = false
+      this.videoProp = {
+        videoId: null,
+        play: false
+      }
+      done()
+    },
+    handleEdit(index, row) {
+      const path = '/my/post/edit/video/' + row.videoId
+      this.$router.push(path)
+    },
+    handleDelete(index, row) {
+      this.$confirm('确定要删除 ' + row.title + '?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteVideoPost(row.videoId).then(res => {
+          if (res.code === 0) {
+            this.$notify({
+              title: '提示',
+              message: '稿件已删除',
+              type: 'warning',
+              duration: 3000
+            })
+            this.$router.go(0)
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+    onUpdateScope() {
+      this.showEditScopeDialog = false
+      updateVideoScope(this.form).then(res => {
+        if (res.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '视频可见范围已更新',
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'warning',
+          duration: 3000
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style>
+</style>