joway 2 лет назад
Родитель
Сommit
9d1af71edc

+ 56 - 11
src/api/article.js

@@ -2,39 +2,84 @@ import request from '@/utils/request'
 
 export function fetchList(query) {
   return request({
-    url: '/vue-element-admin/article/list',
+    url: 'v1/article/category',
     method: 'get',
     params: query
   })
 }
 
-export function fetchArticle(id) {
+export function fetchChangeSort(query) {
   return request({
-    url: '/vue-element-admin/article/detail',
+    url: 'v1/article/category-sort',
     method: 'get',
-    params: { id }
+    params: query
   })
 }
 
-export function fetchPv(pv) {
+export function fetchArticleIndex(query) {
   return request({
-    url: '/vue-element-admin/article/pv',
+    url: 'v1/article/index',
     method: 'get',
-    params: { pv }
+    params: query
+  })
+}
+
+export function fetchChangeArticleSort(query) {
+  return request({
+    url: 'v1/article/sort',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchArticleHide(query) {
+  return request({
+    url: 'v1/article/article-hide',
+    method: 'get',
+    params: query
+  })
+}
+export function fetchArticleUnhide(query) {
+  return request({
+    url: 'v1/article/article-un-hide',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchArticleDel(query) {
+  return request({
+    url: 'v1/article/article-delete',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchArticleDetail(query) {
+  return request({
+    url: 'v1/article/edit/' + query,
+    method: 'get'
+  })
+}
+
+export function fetchArticleAddList() {
+  return request({
+    url: 'v1/article/add',
+    method: 'get'
   })
 }
 
-export function createArticle(data) {
+export function fetchArticleEdit(data, query) {
   return request({
-    url: '/vue-element-admin/article/create',
+    url: 'v1/article/edit/' + query,
     method: 'post',
     data
   })
 }
 
-export function updateArticle(data) {
+export function fetchArticleAdd(data) {
   return request({
-    url: '/vue-element-admin/article/update',
+    url: 'v1/article/add',
     method: 'post',
     data
   })

+ 44 - 0
src/api/log.js

@@ -8,3 +8,47 @@ export function fetchAdminLoginList(query) {
     params: query
   })
 }
+
+export function fetchUserLoginList(query) {
+  return request({
+    url: '/v1/log/user-login',
+    method: 'get',
+    data: query,
+    params: query
+  })
+}
+
+export function fetchAdminHandle(query) {
+  return request({
+    url: '/v1/log/admin-handle',
+    method: 'get',
+    data: query,
+    params: query
+  })
+}
+
+export function fetchUserHandle(query) {
+  return request({
+    url: '/v1/log/user-handle',
+    method: 'get',
+    data: query,
+    params: query
+  })
+}
+
+export function fetchSystem(query) {
+  return request({
+    url: '/v1/log/system',
+    method: 'get',
+    data: query,
+    params: query
+  })
+}
+
+export function fetchUserLoginExport(query) {
+  return request({
+    url: '/v1/log/user-login-export',
+    method: 'get',
+    params: query
+  })
+}

+ 95 - 17
src/components/Tinymce/components/EditorImage.vue

@@ -9,11 +9,13 @@
         :file-list="fileList"
         :show-file-list="true"
         :on-remove="handleRemove"
-        :on-success="handleSuccess"
+        :on-success="uploaderHandleSuccess"
         :before-upload="beforeUpload"
         class="editor-slide-upload"
-        action="https://httpbin.org/post"
+        :action="requestRoute"
         list-type="picture-card"
+        :data="uploaderFormData"
+        :headers="uploaderHeaders"
       >
         <el-button size="small" type="primary">
           Click upload
@@ -31,6 +33,8 @@
 
 <script>
 // import { getToken } from 'api/qiniu'
+import { getToken } from '@/api/upload'
+import tool from '@/utils/tool'
 
 export default {
   name: 'EditorSlideUpload',
@@ -38,15 +42,62 @@ export default {
     color: {
       type: String,
       default: '#1890ff'
+    },
+    requestRoute: {
+      type: String,
+      default: ''
     }
   },
   data() {
     return {
       dialogVisible: false,
       listObj: {},
-      fileList: []
+      fileList: [],
+      uploaderShow: true,
+      uploaderLoading: false,
+      uploaderFormData: {
+        'uploadToken': ''
+      },
+      uploaderRequestUrl: '',
+      uploaderHeaders: {
+        'Device-Type': 'pc',
+        'Suppress-Response-Code': '1',
+        'Authorization': ''
+      },
+      uploaderDisabled: false,
+      successImageUrl: null,
+      uploaderImage: null
+    }
+  },
+  computed: {
+    uploaderImageUrl() {
+      if (this.uploaderImage != null) {
+        return this.uploaderImage
+      }
+      console.log(this.defaultImageUrl)
+      if (this.successImageUrl !== null) {
+        return this.successImageUrl
+      } else if (this.defaultImageUrl !== null) {
+        // this.uploaderShow = false
+        // this.uploaderDisabled = true
+
+        // this.uploaderLoading = false
+        return this.defaultImageUrl
+      } else {
+        return ''
+      }
+    }
+  },
+  watch: {
+    defaultImageUrl(newVal, oldVal) {
+      this.uploaderImage = newVal
+      // console.log(oldVal)
     }
   },
+  created() {
+    // this.uploaderRequestUrl = this.requestRoute
+    console.log(this.requestRoute)
+  },
   methods: {
     checkAllSuccess() {
       return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
@@ -62,15 +113,35 @@ export default {
       this.fileList = []
       this.dialogVisible = false
     },
-    handleSuccess(response, file) {
-      const uid = file.uid
-      const objKeyArr = Object.keys(this.listObj)
-      for (let i = 0, len = objKeyArr.length; i < len; i++) {
-        if (this.listObj[objKeyArr[i]].uid === uid) {
-          this.listObj[objKeyArr[i]].url = response.files.file
-          this.listObj[objKeyArr[i]].hasSuccess = true
-          return
+    uploaderHandleSuccess(response, file) {
+      console.log(response)
+      if (response.success) {
+        this.$message({
+          message: 'Successful',
+          type: 'success'
+        })
+        const uid = file.uid
+        const objKeyArr = Object.keys(this.listObj)
+        for (let i = 0, len = objKeyArr.length; i < len; i++) {
+          if (this.listObj[objKeyArr[i]].uid === uid) {
+            this.listObj[objKeyArr[i]].url = tool.getArImage(response.data, '/files/')
+            this.listObj[objKeyArr[i]].hasSuccess = true
+            return
+          }
         }
+        this.successImageUrl = URL.createObjectURL(file.raw)
+        if (!this.uploaderSuccessCanChange) {
+          this.uploaderShow = false
+          this.uploaderDisabled = true
+          this.uploaderLoading = false
+        }
+        this.$emit('on-success', response.data)
+      } else {
+        this.$message({
+          message: response.data.message,
+          type: 'warning'
+        })
+        this.uploaderLoading = false
       }
     },
     handleRemove(file) {
@@ -88,13 +159,20 @@ export default {
       const _URL = window.URL || window.webkitURL
       const fileName = file.uid
       this.listObj[fileName] = {}
+      const auth_token = localStorage.accessToken
+      this.uploaderHeaders.Authorization = 'Bearer  ' + auth_token
       return new Promise((resolve, reject) => {
-        const img = new Image()
-        img.src = _URL.createObjectURL(file)
-        img.onload = function() {
-          _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
-        }
-        resolve(true)
+        getToken().then(response => {
+          this.uploaderFormData.uploadToken = response.data
+          const img = new Image()
+          img.src = _URL.createObjectURL(file)
+          img.onload = function() {
+            _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
+          }
+          resolve(true)
+        }).catch(() => {
+          reject(false)
+        })
       })
     }
   }

+ 10 - 2
src/components/Tinymce/index.vue

@@ -2,7 +2,7 @@
   <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
     <textarea :id="tinymceId" class="tinymce-textarea" />
     <div class="editor-custom-btn-container">
-      <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
+      <editorImage color="#1890ff" class="editor-upload-btn" :request-route="'v1/ad/upload'" @successCBK="imageSuccessCBK" />
     </div>
   </div>
 </template>
@@ -54,6 +54,10 @@ export default {
       type: [Number, String],
       required: false,
       default: 'auto'
+    },
+    requestRoute: {
+      type: String,
+      default: ''
     }
   },
   data() {
@@ -108,8 +112,12 @@ export default {
   destroyed() {
     this.destroyTinymce()
   },
+  created() {
+    console.log(this.requestRoute)
+  },
   methods: {
     init() {
+      console.log(this.uploaderRequestUrl)
       // dynamic load tinymce from cdn
       load(tinymceCDN, (err) => {
         if (err) {
@@ -211,7 +219,7 @@ export default {
       window.tinymce.get(this.tinymceId).getContent()
     },
     imageSuccessCBK(arr) {
-      arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
+      arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" width="100%" src="${v.url}" >`))
     }
   }
 }

+ 3 - 3
src/lang/zh.js

@@ -123,9 +123,9 @@ export default {
     button: '打开引导'
   },
   components: {
-    documentation: '文档',
-    tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
-    dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
+    documentation: '',
+    tinymceTips: '',
+    dropzoneTips: '',
     stickyTips: '当页面滚动到预设的位置会吸附在顶部',
     backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
     backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素',

+ 58 - 0
src/router/index.js

@@ -209,6 +209,64 @@ export const asyncRoutes = [
         component: () => import('@/views/log/admin-login'),
         name: 'admin-login',
         meta: { title: 'admin-login', icon: 'user', noCache: true }
+      },
+      {
+        path: 'user-login',
+        component: () => import('@/views/log/user-login'),
+        name: 'user-login',
+        meta: { title: 'user-login', icon: 'user', noCache: true }
+      },
+      {
+        path: 'admin-handle',
+        component: () => import('@/views/log/admin-handle'),
+        name: 'admin-handle',
+        meta: { title: 'admin-handle', icon: 'user', noCache: true }
+      },
+      {
+        path: 'user-handle',
+        component: () => import('@/views/log/user-handle'),
+        name: 'user-handle',
+        meta: { title: 'user-handle', icon: 'user', noCache: true }
+      },
+      {
+        path: 'system',
+        component: () => import('@/views/log/system'),
+        name: 'system',
+        meta: { title: 'system', icon: 'user', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/article',
+    component: Layout,
+    redirect: '/article/category',
+    hidden: true,
+    children: [
+      {
+        path: 'category',
+        component: () => import('@/views/article/category'),
+        name: 'category',
+        meta: { title: 'category', icon: 'user', noCache: true }
+      },
+      {
+        path: 'index',
+        component: () => import('@/views/article/index'),
+        name: 'index',
+        meta: { title: 'index', icon: 'user', noCache: true }
+      },
+      {
+        path: 'edit/:ID(\\w+)',
+        component: () => import('@/views/article/edit'),
+        name: 'edit',
+        meta: { title: 'edit', noCache: true },
+        hidden: true
+      },
+      {
+        path: 'add',
+        component: () => import('@/views/article/edit'),
+        name: 'add',
+        meta: { title: 'edit', noCache: true },
+        hidden: true
       }
     ]
   },

+ 1 - 1
src/utils/tool.js

@@ -238,7 +238,7 @@ const tool = {
 	 * @param path 路径
 	 * @returns {string}
 	 */
-  getArImage(imageUrl, path) {
+  getArImage(imageUrl, path = null) {
     return process.env.VUE_APP_CDN_API + `/${path}/${imageUrl}`
   },
 

+ 1 - 1
src/views/ad/edit.vue

@@ -100,7 +100,7 @@ export default {
         this.allArticle = response.data.allArticle
         this.form.image = response.data.oneData.IMAGE
         this.form.content = response.data.oneData.CONTENT
-        this.defaultImageUrl = tool.getArImage(response.data.oneData.IMAGE, '/files/')
+        this.defaultImageUrl = response.data.oneData.IMAGE
         if (response.data.oneData.TYPE === '1') {
           this.url = response.data.oneData.CONTENT
         } else {

+ 128 - 0
src/views/article/category.vue

@@ -0,0 +1,128 @@
+<template>
+  <div v-loading="listLoading">
+    <div class="white-box">
+      <el-table :data="tableData" stripe style="width: 100%;" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" />
+        <el-table-column label="Category Name" prop="CATE_NAME" /><!--分类名称-->
+        <el-table-column label="Order" width="100"><!--排序-->
+          <template slot-scope="scope">
+            <el-input
+              v-model="scope.row.SORT"
+              min="0"
+              max="99"
+              @change="handleChangeSort(scope.row, scope.row.SORT)"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="Creation Time"><!--创建时间-->
+          <template slot-scope="scope">
+            {{ tool.formatDate(scope.row.CREATED_AT) }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="white-box-footer">
+        <el-pagination
+          :current-page="currentPage"
+          :page-sizes="[20, 50, 100, 200]"
+          :page-size="pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="totalCount"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { fetchList, fetchChangeSort } from '@/api/article'
+import tool from '@/utils/tool'
+
+export default {
+  name: 'ArticleCategory',
+  data() {
+    return {
+      tableData: null,
+      listLoading: true,
+      multipleSelection: [],
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleSelectionChange(val) {
+      this.multipleSelection = val
+    },
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleAdd() {
+      this.$router.push({ path: `/article/category-add` })
+    },
+    handleDelete(row) {
+      this.delData(row.ID)
+    },
+    handleMuliDel() {
+      this.delData()
+    },
+    handleChangeSort(row, sort) {
+      fetchChangeSort({ id: row.ID, sort: sort }).then(response => {
+        this.getData({ page: this.currentPage, pageSize: this.pageSize })
+        // Just to simulate the time of the request
+      })
+    },
+    getData(page, pageSize) {
+      this.listLoading = true
+      if (page === undefined) page = 1
+      if (pageSize === undefined) pageSize = 20
+      fetchList({ page: page, pageSize: pageSize }).then(response => {
+        this.tableData = response.data.list
+        // Just to simulate the time of the request
+      })
+      setTimeout(() => {
+        this.listLoading = false
+      }, 1.5 * 1000)
+    },
+    delData(id = null) {
+      const obj = this
+      obj.$confirm('Are you sure to delete the selected data?', 'Hint', { // 确定删除选定的数据
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        const selectedIds = []
+        if (id === null) {
+          for (const val of obj.multipleSelection) {
+            selectedIds.push(val.ID)
+          }
+        } else {
+          selectedIds.push(id)
+        }
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        obj.getData(obj.currentPage, obj.pageSize)
+      }).catch(response => {
+
+      })
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 111 - 0
src/views/article/edit.vue

@@ -0,0 +1,111 @@
+<template>
+  <div v-loading="loading">
+    <div class="white-box">
+      <el-form ref="form" :model="form" label-width="100px">
+        <el-form-item label="Title" style="width: 500px;"><!--标题-->
+          <el-input v-model="form.title" />
+        </el-form-item>
+        <el-form-item label="Category"><!--分类-->
+          <el-select v-model="form.cid" placeholder="please select category"><!--请选择分类-->
+            <el-option v-for="item in allCategory" :key="item.ID" :label="item.CATE_NAME" :value="item.ID" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="Order" style="width: 500px;"><!--排序值-->
+          <el-input v-model="form.sort" />
+        </el-form-item>
+        <el-form-item label="Content"><!--内容-->
+          <div class="components-container" style="margin:0">
+            <!-- <aside>
+              {{ $t('components.tinymceTips') }}
+              <a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html"> {{ $t('components.documentation') }}</a>
+            </aside> -->
+            <div>
+              <tinymce v-model="form.content" :request-route="'v1/ad/upload'" :height="300" />
+            </div>
+            <!-- <div class="editor-content" v-html="form.content" /> -->
+          </div>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" :loading="submitButtonStat" @click="onSubmit">Confirm<!-- 提交 --></el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import Tinymce from '@/components/Tinymce'
+import { fetchArticleDetail, fetchArticleAdd, fetchArticleAddList, fetchArticleEdit } from '@/api/article'
+
+export default {
+  name: 'ArticleEdit',
+  components: { Tinymce },
+  data() {
+    return {
+      form: {
+        title: '',
+        cid: null,
+        content: '',
+        sort: 1
+      },
+      content: '',
+      allCategory: null,
+      loading: false,
+      submitButtonStat: false,
+      isEdit: false
+    }
+  },
+  mounted() {
+    if (this.$route.name === 'edit') {
+      fetchArticleDetail(this.$route.params.ID).then(response => {
+        console.log(response)
+        this.form.title = response.data.oneData.TITLE
+        this.form.cid = response.data.oneData.CID
+        this.form.content = response.data.oneData.CONTENT
+        this.form.sort = response.data.oneData.SORT
+        this.allCategory = response.allCategory
+        this.loading = false
+        this.isEdit = true
+      })
+    } else {
+      fetchArticleAddList().then(response => {
+        this.allCategory = response.data.allCategory
+        this.loading = false
+      })
+    }
+  },
+  methods: {
+    onSubmit() {
+      this.submitButtonStat = true
+      if (this.$route.name === 'edit') {
+        // const path = 'article/edit/' + this.$route.params.id
+        fetchArticleEdit(this.form, this.$route.params.ID).then(response => {
+          this.submitButtonStat = false
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          this.$router.go(-1)
+        }).catch(() => {
+          this.submitButtonStat = false
+        })
+      } else {
+        fetchArticleAdd(this.form).then(response => {
+          this.submitButtonStat = false
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          this.$router.go(-1)
+        }).catch(() => {
+          this.submitButtonStat = false
+        })
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 244 - 0
src/views/article/index.vue

@@ -0,0 +1,244 @@
+<template>
+  <div v-loading="loading">
+    <div class="white-box">
+      <el-table :data="tableData" stripe style="width: 100%;" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" />
+        <el-table-column label="ID" prop="ID">
+          <template slot-scope="scope">
+            <router-link :to="`/article/detail/${scope.row.ID}`" target="_blank" class="islide">
+              <span>{{ scope.row.ID }}</span>
+            </router-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="Title" prop="TITLE">
+          <template slot-scope="scope">
+            <router-link :to="`/article/detail/${scope.row.ID}`" target="_blank" class="islide">
+              <span>{{ scope.row.TITLE }}</span>
+            </router-link>
+          </template>
+        </el-table-column><!--标题-->
+        <el-table-column label="Category"><!--分类-->
+          <template slot-scope="scope">
+            {{ allData.allCategory[scope.row.CID].CATE_NAME }}
+          </template>
+        </el-table-column>
+        <el-table-column label="Sort" width="100"> <!-- 排序 -->
+          <template slot-scope="scope">
+            <el-input v-model="scope.row.SORT" min="0" max="99" @change="handleChangeSort(scope.row, scope.row.SORT)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="Creation Time"><!--创建时间-->
+          <template slot-scope="scope">
+            {{ tool.formatDate(scope.row.CREATED_AT) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="Status"> <!-- 状态 -->
+          <template slot-scope="scope">
+            <div v-if="scope.row.STATUS === '1'">Show</div>
+            <div v-else>Hide</div>
+          </template>
+        </el-table-column>
+
+        <el-table-column fixed="right" label="Action" width="180"><!--操作-->
+          <template slot-scope="scope">
+            <el-dropdown size="small" trigger="click">
+              <el-button type="primary" size="small" @click.stop="">
+                Action<i class="el-icon-arrow-down el-icon--right" />
+              </el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item command="edit" @click.native="handleEdit(scope.row)">Edit</el-dropdown-item>
+                <el-dropdown-item command="delete" @click.native="handleDelete(scope.row)">Delete</el-dropdown-item>
+                <el-dropdown-item command="hide" @click.native="handleHide(scope.row)">Hide</el-dropdown-item>
+                <el-dropdown-item command="un-hide" @click.native="handleUnHide(scope.row)">Unhide</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="white-box-footer">
+        <el-dropdown size="small" trigger="click">
+          <el-button type="primary" size="small">
+            Selected data<!--所选数据--><i class="el-icon-arrow-down el-icon--right" />
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item command="delete" @click.native="handleMuliDel()">Delete</el-dropdown-item>
+            <el-dropdown-item command="hide" @click.native="handleMultiHide()">Hide</el-dropdown-item>
+            <el-dropdown-item command="un-hide" @click.native="handleMultiUnHide()">Unhide</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+        <el-button type="primary" size="small" @click="handleAdd">New article</el-button><!--添加文章-->
+        <el-pagination
+          :current-page="currentPage"
+          :page-sizes="[20, 50, 100, 200]"
+          :page-size="pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="totalCount"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { fetchArticleIndex, fetchChangeArticleSort, fetchArticleHide, fetchArticleUnhide, fetchArticleDel } from '@/api/article'
+import tool from '@/utils/tool'
+
+export default {
+  name: 'ArticleIndex',
+  data() {
+    return {
+      allData: null,
+      tableData: null,
+      loading: true,
+      multipleSelection: [],
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleSelectionChange(val) {
+      this.multipleSelection = val
+    },
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleAdd() {
+      this.$router.push({ path: `/article/add` })
+    },
+    handleEdit(row) {
+      this.$router.push({ path: `/article/edit/${row.ID}` })
+    },
+    handleDelete(row) {
+      this.delData(row.ID)
+    },
+    handleHide(row) {
+      this.hideData(row.ID)
+    },
+    handleUnHide(row) {
+      this.unHideData(row.ID)
+    },
+    handleMuliDel() {
+      this.delData()
+    },
+    handleMultiHide() {
+      this.hideData()
+    },
+    handleMultiUnHide() {
+      this.unHideData()
+    },
+    handleChangeSort(row, sort) {
+      fetchChangeArticleSort({ id: row.ID, sort: sort }).then(response => {
+        this.getData({ page: this.currentPage, pageSize: this.pageSize })
+        // Just to simulate the time of the request
+      })
+    },
+    getData(page, pageSize) {
+      this.loading = true
+      if (page === undefined) page = 1
+      if (pageSize === undefined) pageSize = 20
+      fetchArticleIndex({ page: page, pageSize: pageSize }).then(response => {
+        this.allData = response.data
+        this.tableData = response.data.list
+        // Just to simulate the time of the request
+      })
+      setTimeout(() => {
+        this.loading = false
+      }, 1.5 * 1000)
+    },
+    delData(id = null) {
+      const obj = this
+      obj.$confirm('Are you sure to delete the selected data?', 'Hint', { // 确定删除选定的数据
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        const selectedIds = []
+        if (id === null) {
+          for (const val of obj.multipleSelection) {
+            selectedIds.push(val.ID)
+          }
+        } else {
+          selectedIds.push(id)
+        }
+        fetchArticleDel({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getData(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    },
+    hideData(id = null) {
+      const obj = this
+      obj.$confirm('Are you sure to hide the selected data?', 'Notice', { // 确定删除选定的数据?
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        const selectedIds = []
+        if (id === null) {
+          for (const val of obj.multipleSelection) {
+            selectedIds.push(val.ID)
+          }
+        } else {
+          selectedIds.push(id)
+        }
+        fetchArticleHide({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getData(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    },
+    unHideData(id = null) {
+      const obj = this
+      obj.$confirm('Are you sure to un-hide the selected data?', 'Notice', { // 确定删除选定的数据?
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        const selectedIds = []
+        if (id === null) {
+          for (const val of obj.multipleSelection) {
+            selectedIds.push(val.ID)
+          }
+        } else {
+          selectedIds.push(id)
+        }
+        fetchArticleUnhide({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getData(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 3 - 7
src/views/components-demo/tinymce.vue

@@ -1,9 +1,9 @@
 <template>
   <div class="components-container">
-    <aside>
+    <!--  <aside>
       {{ $t('components.tinymceTips') }}
       <a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html"> {{ $t('components.documentation') }}</a>
-    </aside>
+    </aside> -->
     <div>
       <tinymce v-model="content" :height="300" />
     </div>
@@ -19,10 +19,7 @@ export default {
   components: { Tinymce },
   data() {
     return {
-      content:
-      `<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1><p style="text-align: center; font-size: 15px;"><img title="TinyMCE Logo" src="//www.tinymce.com/images/glyph-tinymce@2x.png" alt="TinyMCE Logo" width="110" height="97" /><ul>
-        <li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li><li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li><li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li>
-      </ul>`
+      content: ''
     }
   }
 }
@@ -33,4 +30,3 @@ export default {
   margin-top: 20px;
 }
 </style>
-

+ 103 - 0
src/views/log/admin-handle.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="white-box">
+    <div class="filter-box">
+      <filter-user
+        :filter-types.sync="filterTypes"
+        :filter-btn-name="$t('common.screen')"
+        @select-value="handleFilterUser"
+      />
+    </div>
+    <el-table ref="multipleTable" :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight()">
+      <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''" :prop="tableHeader.other.prop ? tableHeader.other.prop : null">
+        <template slot-scope="scope">
+          <template v-if="scope.row[tableHeader.index].other.tag">
+            <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null">{{ scope.row[tableHeader.index].value }}</el-tag>
+          </template>
+          <template v-else>
+            <div v-html="scope.row[tableHeader.index].value" />
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="white-box-footer">
+      <pagination
+        :total="totalCount"
+        :page_size="pageSize"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import tool from '@/utils/tool'
+import permission from '@/utils/permission'
+import { fetchAdminHandle } from '@/api/log'
+import filterHelper from '@/utils/filterHelper'
+import FilterUser from '@/components/FilterUser'
+import Pagination from '@/components/Pagination'
+
+export default {
+  name: 'LogAdminLogin',
+  components: { FilterUser, Pagination },
+  data() {
+    return {
+      tableHeaders: null,
+      allData: null,
+      tableData: null,
+      loading: true,
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool,
+      permission: permission,
+      filterTypes: null,
+      filterModel: {},
+      filterData: null
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleFilterUser(filterData) {
+      filterHelper.handleFilterUser(this, filterData)
+    },
+    getData(page, pageSize) {
+      const filterData = this.filterModel
+      console.log(filterData)
+      const vueObj = this
+      const paramsData = Object.assign({
+        page: (page === null || page === undefined) ? 1 : page,
+        pageSize: (pageSize === null || pageSize === undefined) ? vueObj.pageSize : pageSize
+      }, filterData)
+      fetchAdminHandle(paramsData).then(response => {
+        vueObj.filterTypes = response.data.filterTypes
+        vueObj.tableData = response.data.list
+        vueObj.tableHeaders = response.data.columnsShow
+        vueObj.currentPage = page
+        vueObj.totalPages = parseInt(response.data.totalPages)
+        vueObj.totalCount = parseInt(response.data.totalCount)
+        vueObj.pageSize = pageSize
+        this.loading = false
+      })
+    },
+    handleFilter() {
+      this.getData()
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 9 - 8
src/views/log/admin-login.vue

@@ -3,7 +3,7 @@
     <div class="filter-box">
       <filter-user
         :filter-types.sync="filterTypes"
-        filter-btn-name="Select"
+        :filter-btn-name="$t('common.screen')"
         @select-value="handleFilterUser"
       />
     </div>
@@ -54,24 +54,26 @@ export default {
       tool: tool,
       permission: permission,
       filterTypes: null,
-      filterModel: {}
+      filterModel: {},
+      filterData: null
     }
   },
   mounted() {
-    this.getList()
+    this.getData()
   },
   methods: {
     handleCurrentChange(page) {
-      this.getList(page, this.pageSize)
+      this.getData(page, this.pageSize)
     },
     handleSizeChange(pageSize) {
-      this.getList(this.currentPage, pageSize)
+      this.getData(this.currentPage, pageSize)
     },
     handleFilterUser(filterData) {
       filterHelper.handleFilterUser(this, filterData)
     },
-    getList(page, pageSize) {
+    getData(page, pageSize) {
       const filterData = this.filterModel
+      console.log(filterData)
       const vueObj = this
       const paramsData = Object.assign({
         page: (page === null || page === undefined) ? 1 : page,
@@ -89,8 +91,7 @@ export default {
       })
     },
     handleFilter() {
-      this.listQuery.page = 1
-      this.getList()
+      this.getData()
     }
   }
 }

+ 103 - 0
src/views/log/system.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="white-box">
+    <div class="filter-box">
+      <filter-user
+        :filter-types.sync="filterTypes"
+        :filter-btn-name="$t('common.screen')"
+        @select-value="handleFilterUser"
+      />
+    </div>
+    <el-table ref="multipleTable" :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight()">
+      <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''" :prop="tableHeader.other.prop ? tableHeader.other.prop : null">
+        <template slot-scope="scope">
+          <template v-if="scope.row[tableHeader.index].other.tag">
+            <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null">{{ scope.row[tableHeader.index].value }}</el-tag>
+          </template>
+          <template v-else>
+            <div v-html="scope.row[tableHeader.index].value" />
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="white-box-footer">
+      <pagination
+        :total="totalCount"
+        :page_size="pageSize"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import tool from '@/utils/tool'
+import permission from '@/utils/permission'
+import { fetchSystem } from '@/api/log'
+import filterHelper from '@/utils/filterHelper'
+import FilterUser from '@/components/FilterUser'
+import Pagination from '@/components/Pagination'
+
+export default {
+  name: 'LogAdminLogin',
+  components: { FilterUser, Pagination },
+  data() {
+    return {
+      tableHeaders: null,
+      allData: null,
+      tableData: null,
+      loading: true,
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool,
+      permission: permission,
+      filterTypes: null,
+      filterModel: {},
+      filterData: null
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleFilterUser(filterData) {
+      filterHelper.handleFilterUser(this, filterData)
+    },
+    getData(page, pageSize) {
+      const filterData = this.filterModel
+      console.log(filterData)
+      const vueObj = this
+      const paramsData = Object.assign({
+        page: (page === null || page === undefined) ? 1 : page,
+        pageSize: (pageSize === null || pageSize === undefined) ? vueObj.pageSize : pageSize
+      }, filterData)
+      fetchSystem(paramsData).then(response => {
+        vueObj.filterTypes = response.data.filterTypes
+        vueObj.tableData = response.data.list
+        vueObj.tableHeaders = response.data.columnsShow
+        vueObj.currentPage = page
+        vueObj.totalPages = parseInt(response.data.totalPages)
+        vueObj.totalCount = parseInt(response.data.totalCount)
+        vueObj.pageSize = pageSize
+        this.loading = false
+      })
+    },
+    handleFilter() {
+      this.getData()
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 103 - 0
src/views/log/user-handle.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="white-box">
+    <div class="filter-box">
+      <filter-user
+        :filter-types.sync="filterTypes"
+        :filter-btn-name="$t('common.screen')"
+        @select-value="handleFilterUser"
+      />
+    </div>
+    <el-table ref="multipleTable" :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight()">
+      <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''" :prop="tableHeader.other.prop ? tableHeader.other.prop : null">
+        <template slot-scope="scope">
+          <template v-if="scope.row[tableHeader.index].other.tag">
+            <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null">{{ scope.row[tableHeader.index].value }}</el-tag>
+          </template>
+          <template v-else>
+            <div v-html="scope.row[tableHeader.index].value" />
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="white-box-footer">
+      <pagination
+        :total="totalCount"
+        :page_size="pageSize"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import tool from '@/utils/tool'
+import permission from '@/utils/permission'
+import { fetchUserHandle } from '@/api/log'
+import filterHelper from '@/utils/filterHelper'
+import FilterUser from '@/components/FilterUser'
+import Pagination from '@/components/Pagination'
+
+export default {
+  name: 'LogAdminLogin',
+  components: { FilterUser, Pagination },
+  data() {
+    return {
+      tableHeaders: null,
+      allData: null,
+      tableData: null,
+      loading: true,
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool,
+      permission: permission,
+      filterTypes: null,
+      filterModel: {},
+      filterData: null
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleFilterUser(filterData) {
+      filterHelper.handleFilterUser(this, filterData)
+    },
+    getData(page, pageSize) {
+      const filterData = this.filterModel
+      console.log(filterData)
+      const vueObj = this
+      const paramsData = Object.assign({
+        page: (page === null || page === undefined) ? 1 : page,
+        pageSize: (pageSize === null || pageSize === undefined) ? vueObj.pageSize : pageSize
+      }, filterData)
+      fetchUserHandle(paramsData).then(response => {
+        vueObj.filterTypes = response.data.filterTypes
+        vueObj.tableData = response.data.list
+        vueObj.tableHeaders = response.data.columnsShow
+        vueObj.currentPage = page
+        vueObj.totalPages = parseInt(response.data.totalPages)
+        vueObj.totalCount = parseInt(response.data.totalCount)
+        vueObj.pageSize = pageSize
+        this.loading = false
+      })
+    },
+    handleFilter() {
+      this.getData()
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 120 - 0
src/views/log/user-login.vue

@@ -0,0 +1,120 @@
+<template>
+  <div class="white-box">
+    <div class="filter-box">
+      <filter-user
+        :filter-types.sync="filterTypes"
+        :filter-btn-name="$t('common.screen')"
+        @select-value="handleFilterUser"
+      />
+    </div>
+    <el-table ref="multipleTable" :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight()">
+      <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''" :prop="tableHeader.other.prop ? tableHeader.other.prop : null">
+        <template slot-scope="scope">
+          <template v-if="scope.row[tableHeader.index].other.tag">
+            <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null">{{ scope.row[tableHeader.index].value }}</el-tag>
+          </template>
+          <template v-else>
+            <div v-html="scope.row[tableHeader.index].value" />
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="white-box-footer">
+      <el-button v-show="permission.hasPermission(`v1/log/user-login-export`)" type="success" size="small" @click="handleExport">Export Excel<!-- 导出Excel --></el-button>
+      <pagination
+        :total="totalCount"
+        :page_size="pageSize"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import tool from '@/utils/tool'
+import permission from '@/utils/permission'
+import { fetchUserLoginList, fetchUserLoginExport } from '@/api/log'
+import filterHelper from '@/utils/filterHelper'
+import FilterUser from '@/components/FilterUser'
+import Pagination from '@/components/Pagination'
+
+export default {
+  name: 'LogAdminLogin',
+  components: { FilterUser, Pagination },
+  data() {
+    return {
+      tableHeaders: null,
+      allData: null,
+      tableData: null,
+      loading: true,
+      currentPage: 1,
+      totalPages: 1,
+      totalCount: 1,
+      pageSize: 20,
+      tool: tool,
+      permission: permission,
+      filterTypes: null,
+      filterModel: {},
+      filterData: null
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(page) {
+      this.getData(page, this.pageSize)
+    },
+    handleSizeChange(pageSize) {
+      this.getData(this.currentPage, pageSize)
+    },
+    handleFilterUser(filterData) {
+      filterHelper.handleFilterUser(this, filterData)
+    },
+    getData(page, pageSize) {
+      const filterData = this.filterModel
+      console.log(filterData)
+      const vueObj = this
+      const paramsData = Object.assign({
+        page: (page === null || page === undefined) ? 1 : page,
+        pageSize: (pageSize === null || pageSize === undefined) ? vueObj.pageSize : pageSize
+      }, filterData)
+      fetchUserLoginList(paramsData).then(response => {
+        vueObj.filterTypes = response.data.filterTypes
+        vueObj.tableData = response.data.list
+        vueObj.tableHeaders = response.data.columnsShow
+        vueObj.currentPage = page
+        vueObj.totalPages = parseInt(response.data.totalPages)
+        vueObj.totalCount = parseInt(response.data.totalCount)
+        vueObj.pageSize = pageSize
+        this.loading = false
+      })
+    },
+    handleFilter() {
+      this.getData()
+    },
+    handleExport() {
+      this.$confirm(`Are you sure you want to export the current data?`, 'Hint', { // `确定要导出当前数据吗?`, '提示',
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return fetchUserLoginExport(this.filterModel)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+      }).catch(response => {
+
+      })
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>