david 2 лет назад
Родитель
Сommit
79efd76fb4

+ 6 - 0
package.json

@@ -98,6 +98,12 @@
     "management-system"
   ],
   "license": "MIT",
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
   "husky": {
     "hooks": {
       "pre-commit": "lint-staged"

+ 77 - 0
src/api/ad.js

@@ -0,0 +1,77 @@
+import request from '@/utils/request'
+
+export function fetchList(query) {
+  return request({
+    url: '/v1/ad/location',
+    method: 'get'
+  })
+}
+
+export function fetchAdList(query) {
+  return request({
+    url: '/v1/ad/list/' + query.adId.ID,
+    method: 'get',
+    params: {
+      page: query.page,
+      pageSize: query.pageSize
+    }
+  })
+}
+
+export function fetchChangeSort(query) {
+  return request({
+    url: '/v1/ad/sort',
+    method: 'get',
+    params: {
+      id: query.id,
+      sort: query.sort
+    }
+  })
+}
+
+export function fetchDetail(query) {
+  return request({
+    url: '/v1/ad/edit/' + query,
+    method: 'get'
+  })
+}
+
+export function fetchEdit(query, data) {
+  return request({
+    url: '/v1/ad/edit/' + query,
+    method: 'post',
+    data
+  })
+}
+
+export function fetchDelete(data) {
+  return request({
+    url: '/v1/ad/ad-delete',
+    method: 'post',
+    data
+  })
+}
+
+export function fetchHide(data) {
+  return request({
+    url: '/v1/ad/ad-hide',
+    method: 'post',
+    data
+  })
+}
+
+export function fetchUnhide(data) {
+  return request({
+    url: '/v1/ad/ad-un-hide',
+    method: 'post',
+    data
+  })
+}
+
+export function fetchAdd(data) {
+  return request({
+    url: '/v1/ad/add',
+    method: 'post',
+    data
+  })
+}

+ 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
   })

+ 54 - 0
src/api/log.js

@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+export function fetchAdminLoginList(query) {
+  return request({
+    url: '/v1/log/admin-login',
+    method: 'get',
+    data: 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
+  })
+}

+ 16 - 0
src/api/upload.js

@@ -0,0 +1,16 @@
+import request from '@/utils/request'
+
+export function getToken() {
+  return request({
+    url: '/v1/file/token',
+    method: 'get'
+  })
+}
+
+export function upload(data) {
+  return request({
+    url: '/v1/ad/upload',
+    method: 'post',
+    data
+  })
+}

+ 109 - 109
src/components/FilterUser.vue

@@ -174,115 +174,6 @@ export default {
       }
     }
   },
-  computed: {
-    filterCustomType() {
-       return {
-          decLevel: {
-          placeholder: this.$t('filter.pleaseSelectDecLevel'),
-          labelField: 'LEVEL_NAME',
-          valueField: 'ID',
-          values: baseInfo.decLevels()
-        },
-        empLevel: {
-          placeholder: this.$t('filter.pleaseSelectEmpLevel'),
-          labelField: 'LEVEL_NAME',
-          valueField: 'ID',
-          values: baseInfo.empLevels()
-        },
-        decRole: {
-          placeholder: '请选择报单中心级别',
-          labelField: 'ROLE_NAME',
-          valueField: 'ID',
-          values: baseInfo.decRoles()
-        },
-        subCompany: {
-          placeholder: '请选择分公司',
-          labelField: 'COM_NAME',
-          valueField: 'ID',
-          values: baseInfo.subCompanies()
-        },
-        status: {
-          placeholder: '请选择会员状态',
-          labelField: 'label',
-          valueField: 'value',
-          values: baseInfo.allStatus()
-        },
-        dealTypes: {
-          placeholder: '请选择交易类型',
-          labelField: 'TYPE_NAME',
-          valueField: 'ID',
-          values: baseInfo.dealTypes()
-        },
-        systems: {
-          placeholder: '请选择体系',
-          labelField: 'SYSTEM_NAME',
-          valueField: 'ID',
-          values: baseInfo.systems()
-        },
-        sex: {
-          placeholder: '请选择性别',
-          labelField: 'label',
-          valueField: 'value',
-          values: {
-            '男': { label: '男', value: '男' },
-            '女': { label: '女', value: '女' }
-          }
-        },
-        yesOrNo: {
-          placeholder: '请选择是否',
-          labelField: 'label',
-          valueField: 'value',
-          values: {
-            1: { label: '是', value: 1 },
-            0: { label: '否', value: 0 }
-          }
-        },
-        banks: {
-          placeholder: '请选择开户行',
-          labelField: 'BANK_NAME',
-          valueField: 'BANK_CODE',
-          values: baseInfo.allOpenBank()
-        },
-        nations: {
-          placeholder: '请选择民族',
-          labelField: 'name',
-          valueField: 'id',
-          values: baseInfo.allNation()
-        },
-        location: {
-          placeholder: '请选择市场',
-          labelField: 'label',
-          valueField: 'value',
-          values: {
-            1: { label: '一市场', value: 1 },
-            2: { label: '二市场', value: 2 },
-            3: { label: '三市场', value: 3 },
-            4: { label: '四市场', value: 4 },
-            5: { label: '五市场', value: 5 }
-          }
-        }
-       }
-    },
-    filterSymbols() {
-      return {
-        '=': this.$t('filter.eq'),
-        'like': this.$t('filter.like'),
-        '<>': this.$t('filter.noeq'),
-        'notLike': this.$t('filter.nolike'),
-        '>': this.$t('filter.moreThan'),
-        '>=': this.$t('filter.moreThanEq'),
-        '<': this.$t('filter.less'),
-        '<=': this.$t('filter.lessThan')
-      }
-    },
-    filterRelations() {
-      return {
-        'and': this.$t('filter.and'),
-        'or': this.$t('filter.or')
-      }
-    },
-
-  },
   data() {
     return {
       loading: false,
@@ -410,6 +301,115 @@ export default {
       filterRequest: {}
     }
   },
+  computed: {
+    filterCustomType() {
+      return {
+        decLevel: {
+          placeholder: this.$t('filter.pleaseSelectDecLevel'),
+          labelField: 'LEVEL_NAME',
+          valueField: 'ID',
+          values: baseInfo.decLevels()
+        },
+        empLevel: {
+          placeholder: this.$t('filter.pleaseSelectEmpLevel'),
+          labelField: 'LEVEL_NAME',
+          valueField: 'ID',
+          values: baseInfo.empLevels()
+        },
+        decRole: {
+          placeholder: '请选择报单中心级别',
+          labelField: 'ROLE_NAME',
+          valueField: 'ID',
+          values: baseInfo.decRoles()
+        },
+        subCompany: {
+          placeholder: '请选择分公司',
+          labelField: 'COM_NAME',
+          valueField: 'ID',
+          values: baseInfo.subCompanies()
+        },
+        status: {
+          placeholder: '请选择会员状态',
+          labelField: 'label',
+          valueField: 'value',
+          values: baseInfo.allStatus()
+        },
+        dealTypes: {
+          placeholder: '请选择交易类型',
+          labelField: 'TYPE_NAME',
+          valueField: 'ID',
+          values: baseInfo.dealTypes()
+        },
+        systems: {
+          placeholder: '请选择体系',
+          labelField: 'SYSTEM_NAME',
+          valueField: 'ID',
+          values: baseInfo.systems()
+        },
+        sex: {
+          placeholder: '请选择性别',
+          labelField: 'label',
+          valueField: 'value',
+          values: {
+            '男': { label: '男', value: '男' },
+            '女': { label: '女', value: '女' }
+          }
+        },
+        yesOrNo: {
+          placeholder: '请选择是否',
+          labelField: 'label',
+          valueField: 'value',
+          values: {
+            1: { label: '是', value: 1 },
+            0: { label: '否', value: 0 }
+          }
+        },
+        banks: {
+          placeholder: '请选择开户行',
+          labelField: 'BANK_NAME',
+          valueField: 'BANK_CODE',
+          values: baseInfo.allOpenBank()
+        },
+        nations: {
+          placeholder: '请选择民族',
+          labelField: 'name',
+          valueField: 'id',
+          values: baseInfo.allNation()
+        },
+        location: {
+          placeholder: '请选择市场',
+          labelField: 'label',
+          valueField: 'value',
+          values: {
+            1: { label: '一市场', value: 1 },
+            2: { label: '二市场', value: 2 },
+            3: { label: '三市场', value: 3 },
+            4: { label: '四市场', value: 4 },
+            5: { label: '五市场', value: 5 }
+          }
+        }
+      }
+    },
+    filterSymbols() {
+      return {
+        '=': this.$t('filter.eq'),
+        'like': this.$t('filter.like'),
+        '<>': this.$t('filter.noeq'),
+        'notLike': this.$t('filter.nolike'),
+        '>': this.$t('filter.moreThan'),
+        '>=': this.$t('filter.moreThanEq'),
+        '<': this.$t('filter.less'),
+        '<=': this.$t('filter.lessThan')
+      }
+    },
+    filterRelations() {
+      return {
+        'and': this.$t('filter.and'),
+        'or': this.$t('filter.or')
+      }
+    }
+
+  },
   watch: {
     filterTypes(newVal) {
       this.allFilterTypes = this.filterTypes

+ 75 - 75
src/components/Pagination/index.vue

@@ -1,92 +1,92 @@
 <template>
   <div style="float: right;">
-      <el-pagination
-          :page-sizes="pageSizes"
-          :page-size="pageSize"
-          :background="background"
-          :layout="pageLayout"
-          @size-change="sizeChange"
-          @current-change="currentChange"
-          :total="pageTotal">
-      </el-pagination>
+    <el-pagination
+      :page-sizes="pageSizes"
+      :page-size="pageSize"
+      :background="background"
+      :layout="pageLayout"
+      :total="pageTotal"
+      @size-change="sizeChange"
+      @current-change="currentChange"
+    />
   </div>
 </template>
 
 <script>
-  export default {
-    name: "pagination",
-    props: {
-      total: {
-        default: 0,
-        type: Number
-      },
-      page_sizes:{
-        default: function(){
-          return [1, 2, 5, 10, 20]
-        },
-        type: Array
-      },
-      page_size: {
-        default: 20,
-        type: Number
-      },
-      layout: {
-        default: "total, sizes, prev, pager, next, jumper",
-        type: String
-      },
-      page: {
-        default: 1,
-        type: Number
-      },
-      background: {
-        default: true,
-        type: Boolean
+export default {
+  name: 'Pagination',
+  props: {
+    total: {
+      default: 0,
+      type: Number
+    },
+    page_sizes: {
+      default: function() {
+        return [1, 2, 5, 10, 20]
       },
+      type: Array
     },
-    data(){
-      return {
-        pageTotal: 1,
-        pageSizes:null,
-        pageSize:20,
-        pageLayout: null,
-        colSpan:18
-      };
+    page_size: {
+      default: 20,
+      type: Number
     },
-    created(){
-      this.pageTotal = this.total;
-      this.pageSizes = this.page_sizes;
-      this.pageSize = this.page_size;
-      this.pageLayout = this.layout;
-      this.colSpan = this.span;
+    layout: {
+      default: 'total, sizes, prev, pager, next, jumper',
+      type: String
     },
-    watch:{
-      total(){
-        this.pageTotal = this.total;
-      },
-      page_sizes(){
-        this.pageSizes = this.page_sizes
-      },
-      page_size(){
-        this.pageSize = this.page_size;
-      },
-      layout(){
-        this.pageLayout = this.layout;
-      },
-      span(){
-        this.colSpan = this.span;
-      }
+    page: {
+      default: 1,
+      type: Number
     },
-    methods:{
-      currentChange(currentPage){
-        this.$emit('current-change', currentPage);
-      },
-      sizeChange(size){
-        this.$emit('size-change', size);
-      }
+    background: {
+      default: true,
+      type: Boolean
+    }
+  },
+  data() {
+    return {
+      pageTotal: 1,
+      pageSizes: null,
+      pageSize: 20,
+      pageLayout: null,
+      colSpan: 18
+    }
+  },
+  watch: {
+    total() {
+      this.pageTotal = this.total
+    },
+    page_sizes() {
+      this.pageSizes = this.page_sizes
+    },
+    page_size() {
+      this.pageSize = this.page_size
+    },
+    layout() {
+      this.pageLayout = this.layout
+    },
+    span() {
+      this.colSpan = this.span
+    }
+  },
+  created() {
+    this.pageTotal = this.total
+    this.pageSizes = this.page_sizes
+    this.pageSize = this.page_size
+    this.pageLayout = this.layout
+    this.colSpan = this.span
+  },
+  mounted() {
+  },
+  methods: {
+    currentChange(currentPage) {
+      this.$emit('current-change', currentPage)
     },
-    mounted(){
+    sizeChange(size) {
+      this.$emit('size-change', size)
     }
   }
+}
 </script>
 
 <style scoped>

+ 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}" >`))
     }
   }
 }

+ 213 - 0
src/components/Upload/index.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="singleImageUpload2 upload-container">
+    <el-upload
+      v-show="uploaderShow"
+      v-loading="uploaderLoading"
+      class="image-uploader"
+      :action="uploaderRequestUrl"
+      name="file"
+      :headers="uploaderHeaders"
+      :data="uploaderFormData"
+      :show-file-list="false"
+      :before-upload="uploaderHandleBefore"
+      :on-success="uploaderHandleSuccess"
+      :disabled="uploaderDisabled"
+      :style="isImgUpload ? `width:${width};height:${height};` : ''"
+    >
+      <template v-if="isImgUpload">
+        <img
+          v-if="uploaderImageUrl"
+          :src="uploaderImageUrl"
+          class="image-preview"
+          :style="`width:${width};height:${height};display: block;`"
+        >
+        <i
+          v-else
+          class="el-icon-plus uploader-icon"
+          :style="`width:${width};height:${height};line-height:${height};`"
+        />
+      </template>
+      <template v-else>
+        <el-button type="primary">{{ uploadBtnTitle }}</el-button>
+      </template>
+    </el-upload>
+    <template v-if="isImgUpload">
+      <div v-show="!uploaderShow" class="image-show">
+        <img :src="uploaderImageUrl" alt="" :width="width" :height="height">
+      </div>
+    </template>
+  </div>
+</template>
+
+<script>
+import { getToken } from '@/api/upload'
+export default {
+  name: 'Uploader',
+  props: {
+    isImgUpload: {
+      type: Boolean,
+      default: true
+    },
+    uploadBtnTitle: {
+      type: String,
+      default: '上传'
+    },
+    requestRoute: {
+      type: String,
+      required: true
+    },
+    defaultImageUrl: {
+      type: String,
+      default: null
+    },
+    uploaderSuccessCanChange: {
+      type: Boolean,
+      default: true
+    },
+    width: {
+      type: String,
+      default: '100px'
+    },
+    height: {
+      type: String,
+      default: '100px'
+    }
+  },
+  data() {
+    return {
+      uploaderShow: true,
+      uploaderLoading: false,
+      uploaderFormData: {
+        'uploadToken': ''
+      },
+      uploaderRequestUrl: `${this.requestRoute}`,
+      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() {
+    console.log(this.uploaderImage)
+  },
+  methods: {
+    uploaderHandleBefore() {
+      const auth_token = localStorage.accessToken
+      this.uploaderHeaders.Authorization = 'Bearer  ' + auth_token
+      return new Promise((resolve, reject) => {
+        getToken().then(response => {
+          console.log(response)
+          this.uploaderFormData.uploadToken = response.data
+          resolve(true)
+        }).catch(() => {
+          reject(false)
+        })
+      })
+    },
+    uploaderHandleSuccess(response, file) {
+      // console.log(response)
+      if (response.success) {
+        this.$message({
+          message: 'Successful',
+          type: 'success'
+        })
+        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
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.upload-container {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .image-uploader {
+    height: 100%;
+  }
+  .image-preview {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    border: 1px dashed #d9d9d9;
+    .image-preview-wrapper {
+      position: relative;
+      width: 100%;
+      height: 100%;
+      img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+    .image-preview-action {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      left: 0;
+      top: 0;
+      cursor: default;
+      text-align: center;
+      color: #fff;
+      opacity: 0;
+      font-size: 20px;
+      background-color: rgba(0, 0, 0, .5);
+      transition: opacity .3s;
+      cursor: pointer;
+      text-align: center;
+      line-height: 200px;
+      .el-icon-delete {
+        font-size: 36px;
+      }
+    }
+    &:hover {
+      .image-preview-action {
+        opacity: 1;
+      }
+    }
+  }
+}
+</style>

+ 26 - 1
src/lang/en.js

@@ -79,6 +79,30 @@ export default {
     password: 'Password',
     verifyCode: 'Verification Code'
   },
+  ad: {
+    ad: 'Ad',
+    description: 'Description',
+    type: 'Type',
+    creator: 'Creator',
+    createTime: 'Create time',
+    action: 'Action',
+    view: 'View',
+    externalLinks: 'External Links',
+    article: 'Article',
+    slideshow: 'Slideshow',
+    image: 'Image',
+    adTitle: 'Ad Title',
+    content: 'Content',
+    sort: 'Sort',
+    modifiedBy: 'Modified By',
+    modifiedTime: 'Modified Time',
+    status: 'Status',
+    adLocation: 'Ad Location',
+    selectData: 'Select Data',
+    add: 'Add',
+    hide: 'Hide',
+    unhide: 'Unhide'
+  },
   documentation: {
     documentation: 'Documentation',
     github: 'Github Repository'
@@ -210,8 +234,9 @@ export default {
     pleaseInputContent: '请输入内容',
     relation: '关系',
     reset: '重置',
-    desc: '描述',
     exportExcel:'Export Excel',
+    desc: '描述',
+    upload: 'Upload'
   },
 
   filter: {

+ 28 - 3
src/lang/zh.js

@@ -79,6 +79,30 @@ export default {
     password: '密码',
     verifyCode: '验证码'
   },
+  ad: {
+    ad: '广告名称',
+    description: '描述',
+    type: '类型',
+    creator: '创建人',
+    createTime: '创建时间',
+    action: '操作',
+    view: '查看详情',
+    externalLinks: '外部链接',
+    article: '文章',
+    slideshow: '幻灯',
+    image: '图片',
+    adTitle: '标题',
+    content: '内容',
+    sort: '排序',
+    modifiedBy: '更新人',
+    modifiedTime: '更新时间',
+    status: '类型',
+    adLocation: '位置',
+    selectData: '所选数据',
+    add: '添加数据',
+    hide: '展示',
+    unhide: '不展示'
+  },
   documentation: {
     documentation: '文档',
     github: 'Github 地址'
@@ -99,9 +123,9 @@ export default {
     button: '打开引导'
   },
   components: {
-    documentation: '文档',
-    tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
-    dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
+    documentation: '',
+    tinymceTips: '',
+    dropzoneTips: '',
     stickyTips: '当页面滚动到预设的位置会吸附在顶部',
     backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
     backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素',
@@ -213,6 +237,7 @@ export default {
     reset: '重置',
     desc: '描述',
     exportExcel:'导出Excel',
+    upload: '上传'
   },
 
   filter: {

+ 150 - 4
src/router/index.js

@@ -202,12 +202,160 @@ export const constantRoutes = [
  */
 export const asyncRoutes = [
   /** when your routing map is too long, you can split it into small modules **/
-
   // 会员
   memberRouter,
   // 设置
   configRouter,
-
+  {
+    path: '/example',
+    component: Layout,
+    redirect: '/example/list',
+    name: 'Example',
+    meta: {
+      title: 'example',
+      icon: 'el-icon-s-help'
+    },
+    children: [
+      {
+        path: 'create',
+        component: () => import('@/views/example/create'),
+        name: 'CreateArticle',
+        meta: { title: 'createArticle', icon: 'edit' }
+      },
+      {
+        path: 'edit/:id(\\d+)',
+        component: () => import('@/views/example/edit'),
+        name: 'EditArticle',
+        meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' },
+        hidden: true
+      },
+      {
+        path: 'list',
+        component: () => import('@/views/example/list'),
+        name: 'ArticleList',
+        meta: { title: 'articleList', icon: 'list' }
+      }
+    ]
+  },
+  {
+    path: '/ad',
+    component: Layout,
+    redirect: '/ad/list',
+    hidden: true,
+    children: [
+      {
+        path: 'location',
+        component: () => import('@/views/ad/index'),
+        name: 'ad-location',
+        meta: { title: 'adLocation', icon: 'user', noCache: true }
+      },
+      {
+        path: 'list/:ID(\\w+)',
+        component: () => import('@/views/ad/list'),
+        name: 'ad-list',
+        meta: { title: 'adList', noCache: true, activeMenu: '/ad/location' },
+        hidden: true
+      },
+      {
+        path: 'edit/:ID(\\w+)',
+        component: () => import('@/views/ad/edit'),
+        name: 'ad-edit',
+        meta: { title: 'adEdit', noCache: true, activeMenu: '/ad/location' },
+        hidden: true
+      },
+      {
+        path: 'add',
+        component: () => import('@/views/ad/edit'),
+        name: 'ad-add',
+        meta: { title: 'adAdd', noCache: true, activeMenu: '/ad/location' },
+        hidden: true
+      }
+    ]
+  },
+  {
+    path: '/log',
+    component: Layout,
+    redirect: '/log/admin-login',
+    hidden: true,
+    children: [
+      {
+        path: 'admin-login',
+        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
+      }
+    ]
+  },
+  {
+    path: '/tab',
+    component: Layout,
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/tab/index'),
+        name: 'Tab',
+        meta: { title: 'tab', icon: 'tab' }
+      }
+    ]
+  },
   {
     path: '/error',
     component: Layout,
@@ -232,7 +380,6 @@ export const asyncRoutes = [
       }
     ]
   },
-
   {
     path: '/error-log',
     component: Layout,
@@ -271,4 +418,3 @@ export function selfAddRoutes(params) {
 }
 
 export default router
-

+ 3 - 3
src/utils/tool.js

@@ -149,8 +149,8 @@ const tool = {
    * @param obj
    * @returns {boolean}
    */
-  isEmptyObject (obj) {
-    let objArr = Object.keys(obj)
+  isEmptyObject(obj) {
+    const objArr = Object.keys(obj)
     return objArr.length === 0
   },
   isInArray(arr, value) {
@@ -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}`
   },
 

+ 166 - 0
src/views/ad/edit.vue

@@ -0,0 +1,166 @@
+<template>
+  <div v-loading="loading">
+    <div class="white-box">
+      <el-form ref="form" :model="form" label-width="250px" class="form-page">
+        <el-form-item :label="$t('ad.adTitle')">
+          <el-input v-model="form.title" />
+        </el-form-item>
+        <el-form-item :label="$t('ad.adLocation')">
+          <el-select v-model="form.lid" placeholder="Select Type"> <!-- 请选择广告类型 -->
+            <el-option
+              v-for="item in allLocation"
+              :key="item.ID"
+              :label="item.LOCATION_NAME"
+              :value="item.ID"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="$t('ad.type')"> <!-- 广告类型 -->
+          <el-select v-model="form.type" placeholder="Select Type" style="width: 400px;"> <!-- 请选择广告类型 -->
+            <el-option :key="1" label="External Link" value="1" /> <!-- 外链 -->
+            <el-option :key="2" label="Article" value="2" /> <!-- 文章 -->
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="form.type==='1'" label="Url"> <!-- '链接地址' '文章ID' -->
+          <el-input v-model="url" style="width: 400px;" />
+          <span class="note" /> <!-- 注:外链请明确输入 http://或https://,文章则不需要输入 -->
+        </el-form-item>
+        <el-form-item :label="$t('ad.article')">
+          <el-select v-model="article" placeholder="Select Article"> <!-- 请选择广告类型 -->
+            <el-option
+              v-for="item in allArticle"
+              :key="item.ID"
+              :label="item.TITLE"
+              :value="item.ID"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="$t('ad.image')" prop="image">
+          <Upload
+            v-model="form.image"
+            :request-route="'v1/ad/upload'"
+            :default-image-url="defaultImageUrl"
+            width="400px"
+            height="160px"
+            @on-success="handleSuccess"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" :loading="submitButtonStat" @click="onSubmit">{{ $t('common.save') }}</el-button>
+        </el-form-item>
+        </el-form-item></el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import { fetchDetail, fetchEdit, fetchAdd } from '@/api/ad'
+import tool from '@/utils/tool'
+import Upload from '@/components/Upload'
+export default {
+  name: 'RoleAdd',
+  components: { Upload },
+  data() {
+    return {
+      allLocation: null,
+      form: {
+        content: '',
+        image: '',
+        lid: '',
+        title: '',
+        type: ''
+      },
+      url: '',
+      article: '',
+      allArticle: '',
+      loading: false,
+      submitButtonStat: false,
+      defaultImageUrl: null
+    }
+  },
+  watch: {
+    'form.type': {
+      deep: true,
+      handler: function handler(modern) {
+        if (modern === '1') {
+          this.article = ''
+        } else {
+          this.url = ''
+        }
+      }
+    }
+  },
+  mounted() {
+    if (this.$route.name === 'ad-edit') {
+      fetchDetail(this.$route.params.ID).then(response => {
+        this.form.title = response.data.oneData.TITLE
+        this.allLocation = response.data.allLocation
+        this.form.lid = response.data.oneData.LID
+        this.form.type = response.data.oneData.TYPE
+        this.allArticle = response.data.allArticle
+        this.form.image = response.data.oneData.IMAGE
+        this.form.content = response.data.oneData.CONTENT
+        this.defaultImageUrl = response.data.oneData.IMAGE
+        if (response.data.oneData.TYPE === '1') {
+          this.url = response.data.oneData.CONTENT
+        } else {
+          this.article = response.data.oneData.CONTENT
+        }
+        this.loading = false
+      }).catch(err => {
+        console.log('err---------' + err)
+      })
+    } else {
+      fetchDetail(this.$route.params.ID).then(response => {
+        this.allLocation = response.data.allLocation
+        this.allArticle = response.data.allArticle
+
+        this.loading = false
+      }).catch(err => {
+        console.log('err---------' + err)
+      })
+    }
+  },
+  methods: {
+    onSubmit() {
+      this.form.content = this.form.type === '1' ? this.url : this.article
+      this.submitButtonStat = true
+      if (this.$route.name === 'ad-edit') {
+        fetchEdit(this.$route.params.ID, this.form).then(response => {
+          this.submitButtonStat = false
+          console.log(response)
+          this.$message({
+            message: response.data,
+            type: "$t('common.successfully')"
+          })
+          this.$router.go(-1)
+        }).catch(err => {
+          console.log('err---------' + err)
+        })
+      } else {
+        console.log(this.form)
+        fetchAdd(this.form).then(response => {
+          this.submitButtonStat = false
+          console.log(response)
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          this.$router.go(-1)
+        }).catch(err => {
+          console.log('err---------' + err)
+        })
+      }
+    },
+    handleSuccess(imageUrl) {
+      this.imageAd = imageUrl
+      this.form.image = tool.getArImage(imageUrl, '/files/')
+      this.defaultImageUrl = tool.getArImage(imageUrl, '/files/')
+    }
+  }
+}
+</script>
+
+  <style scoped>
+
+  </style>

+ 183 - 0
src/views/ad/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="white-box">
+    <el-table
+      :key="tableKey"
+      v-loading="listLoading"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      style="width: 100%;"
+    >
+      <el-table-column :label="$t('ad.ad')" prop="id" align="center" :class-name="getSortClass('id')">
+        <template slot-scope="{row}">
+          <span>{{ row.LOCATION_NAME }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('ad.description')" prop="id" align="center" :class-name="getSortClass('id')">
+        <template slot-scope="{row}">
+          <span>{{ row.REMARK }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('ad.type')" align="center"> <!-- 类型 -->
+        <template slot-scope="{row}">
+          <template v-if="row.TYPE === '1'">
+            <el-tag type="success">Slideshow</el-tag> <!-- 外链 -->
+          </template>
+          <template v-else-if="row.TYPE === '2'">
+            <el-tag>Image</el-tag> <!-- 文章 -->
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('ad.creator')" prop="id" align="center" width="80" :class-name="getSortClass('id')">
+        <template slot-scope="{row}">
+          <span>{{ row.CREATE_ADMIN_NAME }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('ad.createTime')" align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.CREATED_AT | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('ad.action')" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="{row}">
+          <el-button type="primary" size="mini" @click="handleView(row)">
+            {{ $t('ad.view') }}
+          </el-button>
+          <!-- <router-link :to="'/ad/list/'+ row.id" class="link-type">
+            <el-button type="primary" size="small" icon="el-icon-edit">
+              {{ $t('ad.view') }}
+            </el-button>
+          </router-link> -->
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
+  </div>
+  </div></template>
+
+<script>
+import { fetchList } from '@/api/ad'
+import waves from '@/directive/waves' // waves directive
+import { parseTime } from '@/utils'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+
+const calendarTypeOptions = [
+  { key: 'CN', display_name: 'China' },
+  { key: 'US', display_name: 'USA' }
+]
+
+// arr to obj, such as { CN : "China", US : "USA" }
+const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
+  acc[cur.key] = cur.display_name
+  return acc
+}, {})
+
+export default {
+  name: 'ComplexTable',
+  components: { Pagination },
+  directives: { waves },
+  filters: {
+    statusFilter(status) {
+      const statusMap = {
+        published: 'success',
+        draft: 'info',
+        deleted: 'danger'
+      }
+      return statusMap[status]
+    },
+    typeFilter(type) {
+      return calendarTypeKeyValue[type]
+    }
+  },
+  data() {
+    return {
+      tableKey: 0,
+      list: null,
+      total: 0,
+      listLoading: true,
+      listQuery: {
+        page: 1,
+        limit: 20
+      },
+      importanceOptions: [1, 2, 3],
+      calendarTypeOptions,
+      sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }],
+      statusOptions: ['published', 'draft', 'deleted'],
+      showReviewer: false,
+      temp: {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        type: '',
+        status: 'published'
+      },
+      dialogFormVisible: false,
+      dialogStatus: '',
+      textMap: {
+        update: 'Edit',
+        create: 'Create'
+      },
+      dialogPvVisible: false,
+      pvData: [],
+      rules: {
+        type: [{ required: true, message: 'type is required', trigger: 'change' }],
+        timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
+        title: [{ required: true, message: 'title is required', trigger: 'blur' }]
+      },
+      downloadLoading: false
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.totalCount
+
+        // Just to simulate the time of the request
+        setTimeout(() => {
+          this.listLoading = false
+        }, 1.5 * 1000)
+      })
+    },
+    handleFilter() {
+      this.listQuery.page = 1
+      this.getList()
+    },
+    resetTemp() {
+      this.temp = {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        status: 'published',
+        type: ''
+      }
+    },
+    handleView(row) {
+      this.$router.push(`/ad/list/${row.ID}`)
+    },
+    formatJson(filterVal) {
+      return this.list.map(v => filterVal.map(j => {
+        if (j === 'timestamp') {
+          return parseTime(v[j])
+        } else {
+          return v[j]
+        }
+      }))
+    },
+    getSortClass: function(key) {
+      const sort = this.listQuery.sort
+      return sort === `+${key}` ? 'ascending' : 'descending'
+    }
+  }
+}
+</script>

+ 387 - 0
src/views/ad/list.vue

@@ -0,0 +1,387 @@
+<template>
+  <div class="white-box">
+    <div class="filter-box">
+      <el-table
+        :key="tableKey"
+        v-loading="listLoading"
+        :data="list"
+        border
+        fit
+        highlight-current-row
+        style="width: 100%;"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="55" />
+        <el-table-column :label="$t('ad.adTitle')" prop="id" align="center" :class-name="getSortClass('id')">
+          <template slot-scope="{row}">
+            <span>{{ row.TITLE }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.adLocation')" prop="id" align="center" :class-name="getSortClass('id')">
+          <template :value="{allLocation}">
+            <span>{{ allLocation.LOCATION_NAME }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.type')" align="center"> <!-- 类型 -->
+          <template slot-scope="{row}">
+            <template v-if="row.TYPE === '1'">
+              <el-tag type="success">External Links</el-tag> <!-- 外链 -->
+            </template>
+            <template v-else-if="row.TYPE === '2'">
+              <el-tag>Article</el-tag> <!-- 文章 -->
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.content')" prop="id" align="center" :class-name="getSortClass('id')">
+          <template slot-scope="{row}">
+            <div v-if="row.TYPE === '1'">
+              <el-link type="primary" target="_blank" :href="getHref(row.CONTENT)">{{ row.CONTENT }}</el-link>
+            </div><!-- 链接 -->
+            <div v-else-if="row.TYPE === '2'">
+              <router-link :to="`/article/detail/${row.CONTENT}`" target="_blank" style="cursor: pointer;">{{ getContent(row.CONTENT) }}</router-link>
+            </div><!-- 文章 -->
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.sort')" prop="id" align="center" :class-name="getSortClass('id')">
+          <template slot-scope="{row}">
+            <el-input
+              v-model="row.SORT"
+              min="0"
+              max="99"
+              @change="handleChangeSort(row.ID, row.SORT)"
+              @click.native.stop=""
+            />
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.creator')" prop="id" align="center" :class-name="getSortClass('id')">
+          <template slot-scope="{row}">
+            <span>{{ row.CREATE_ADMIN_NAME }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.createTime')" align="center">
+          <template slot-scope="{row}">
+            <span>{{ row.CREATED_AT | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.modifiedBy')" prop="id" align="center" width="80" :class-name="getSortClass('id')">
+          <template slot-scope="{row}">
+            <span>{{ row.UPDATE_ADMIN_NAME }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.modifiedTime')" align="center">
+          <template slot-scope="{row}">
+            <span>{{ tool.formatDate(row.UPDATED_AT) }}</span>
+
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.status')" align="center"> <!-- 类型 -->
+          <template slot-scope="{row}">
+            <template v-if="row.STATUS === '1'">
+              <el-tag type="success">show</el-tag> <!-- 外链 -->
+            </template>
+            <template v-else>
+              <el-tag>hide</el-tag> <!-- 文章 -->
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('ad.action')" align="center">
+
+          <template slot-scope="{row}">
+            <el-dropdown v-if="permission.hasPermission(`ad/ad-delete`) || permission.hasPermission(`ad/edit`)" size="small" trigger="click">
+              <el-button type="primary" size="small" @click.stop="">
+                {{ $t('ad.action') }}<i class="el-icon-arrow-down el-icon--right" />
+              </el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item v-if="permission.hasPermission(`ad/edit`)" command="edit" @click.native="handleEdit(row)">{{ $t('common.edit') }}</el-dropdown-item>
+                <el-dropdown-item v-if="permission.hasPermission(`ad/ad-delete`)" command="delete" @click.native="handleDeleteOne(row)">{{ $t('common.delete') }}</el-dropdown-item>
+                <el-dropdown-item v-if="permission.hasPermission(`ad/ad-hide`)" command="hide" @click.native="handleHideOne(row)">{{ $t('ad.hide') }}</el-dropdown-item>
+                <el-dropdown-item v-if="permission.hasPermission(`ad/ad-un-hide`)" command="un-hide" @click.native="handleUnHideOne(row)">{{ $t('ad.unhide') }}</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="white-box-footer">
+        <el-dropdown v-if="permission.hasPermission(`ad/ad-delete`)" size="small">
+          <el-button type="primary" size="small">
+            {{ $t('ad.selectData') }}<i class="el-icon-arrow-down el-icon--right" />
+          </el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item command="delete" @click.native="handleMuliDel()">{{ $t('table.delete') }}</el-dropdown-item>
+            <el-dropdown-item command="hide" @click.native="handleMultiHide()">{{ $t('ad.hide') }}</el-dropdown-item>
+            <el-dropdown-item command="un-hide" @click.native="handleMultiUnHide()">{{ $t('ad.unhide') }}</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+        <el-button v-if="permission.hasPermission(`ad/ad-add`)" type="primary" size="small" icon="el-icon-plus" @click="handleAdd">{{ $t('ad.add') }}</el-button>
+        <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.pageSize" @pagination="getList" />
+      </div>
+
+      </el-table></div>
+  </div>
+</template>
+
+<script>
+import { fetchAdList, fetchChangeSort, fetchDelete, fetchHide, fetchUnhide } from '@/api/ad'
+import waves from '@/directive/waves' // waves directive
+import { parseTime } from '@/utils'
+import tool from '@/utils/tool'
+import permission from '@/utils/permission'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+
+const calendarTypeOptions = [
+  { key: 'CN', display_name: 'China' },
+  { key: 'US', display_name: 'USA' }
+]
+
+// arr to obj, such as { CN : "China", US : "USA" }
+const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
+  acc[cur.key] = cur.display_name
+  return acc
+}, {})
+
+export default {
+  name: 'ComplexTable',
+  components: { Pagination },
+  directives: { waves },
+  filters: {
+    statusFilter(status) {
+      const statusMap = {
+        published: 'success',
+        draft: 'info',
+        deleted: 'danger'
+      }
+      return statusMap[status]
+    },
+    typeFilter(type) {
+      return calendarTypeKeyValue[type]
+    }
+  },
+  data() {
+    return {
+      tableKey: 0,
+      list: null,
+      total: 0,
+      loading: true,
+      listLoading: true,
+      tool: tool,
+      permission: permission,
+      multipleSelection: null,
+      listQuery: {
+        page: 1,
+        pageSize: 20,
+        adId: null
+      },
+      changeSort: {
+        id: null,
+        sort: null
+      },
+      sort: null,
+      importanceOptions: [1, 2, 3],
+      calendarTypeOptions,
+      sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }],
+      statusOptions: ['published', 'draft', 'deleted'],
+      showReviewer: false,
+      temp: {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        type: '',
+        status: 'published'
+      },
+      dialogFormVisible: false,
+      dialogStatus: '',
+      textMap: {
+        update: 'Edit',
+        create: 'Create'
+      },
+      dialogPvVisible: false,
+      pvData: [],
+      rules: {
+        type: [{ required: true, message: 'type is required', trigger: 'change' }],
+        timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
+        title: [{ required: true, message: 'title is required', trigger: 'blur' }]
+      },
+      downloadLoading: false,
+      allArticle: [],
+      allLocation: {
+        LOCATION_NAME: null
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    handleSelectionChange(val) {
+      this.multipleSelection = val
+    },
+    getList() {
+      this.listLoading = true
+      this.listQuery.adId = this.$route.params
+      fetchAdList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.totalCount
+        this.allArticle = response.data.allArticle
+        const ad_id = this.listQuery.adId.ID
+        this.allLocation = response.data.allLocation[ad_id]
+        // Just to simulate the time of the request
+        setTimeout(() => {
+          this.listLoading = false
+        }, 1.5 * 1000)
+      })
+    },
+    handleFilter() {
+      this.listQuery.page = 1
+      this.getList()
+    },
+    resetTemp() {
+      this.temp = {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        status: 'published',
+        type: ''
+      }
+    },
+    handleView(row) {
+      this.$router.push(`/ad/list/${row.ID}`)
+    },
+    formatJson(filterVal) {
+      return this.list.map(v => filterVal.map(j => {
+        if (j === 'timestamp') {
+          return parseTime(v[j])
+        } else {
+          return v[j]
+        }
+      }))
+    },
+    getSortClass: function(key) {
+      const sort = this.listQuery.sort
+      return sort === `+${key}` ? 'ascending' : 'descending'
+    },
+    handleChangeSort(id, sort) {
+      this.changeSort.id = id
+      this.changeSort.sort = sort
+      fetchChangeSort(this.changeSort).then(response => {
+        setTimeout(() => {
+          this.listLoading = false
+        }, 1.5 * 1000)
+      })
+    },
+    getContent(aid) {
+      const titles = this.allArticle.filter(article => article.ID === aid).map(article => article.TITLE)
+      console.log(titles)
+      return titles.length > 0 ? titles[0] : aid
+    },
+    getHref(link) {
+      return link.indexOf('http') > -1 ? link : 'http://' + link
+    },
+    handleEdit(row) {
+      this.$router.push({ path: `/ad/edit/${row.ID}` })
+    },
+    handleAdd() {
+      this.$router.push({ path: `/ad/add` })
+    },
+    handleMuliDel() {
+      this.handleDelete()
+    },
+    handleMultiHide() {
+      this.handleHide()
+    },
+    handleHideOne(row) {
+      this.handleHide(row.ID)
+    },
+    handleMultiUnHide() {
+      this.handleUnhide()
+    },
+    handleDeleteOne(row) {
+      this.handleDelete(row.ID)
+    },
+    handleDelete(id = null) {
+      const obj = this
+      obj.$confirm('Are you sure to delete 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)
+        }
+        fetchDelete({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getList(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    },
+    handleHide(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)
+        }
+        fetchHide({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getList(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    },
+    handleUnHideOne(row) {
+      this.handleUnhide(row.ID)
+    },
+    handleUnhide(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)
+        }
+        fetchUnhide({ selected: selectedIds }).then(response => {
+          this.$message({
+            message: response.data,
+            type: 'success'
+          })
+          obj.getList(obj.currentPage, obj.pageSize)
+        }).catch(response => {
+
+        })
+      })
+    }
+  }
+}
+</script>

+ 62 - 57
src/views/admin/role-add.vue

@@ -1,5 +1,5 @@
 <template>
-    <div v-loading="loading">
+  <div v-loading="loading">
       <div class="whitebox-bg">
         <el-form ref="form" :model="form" class="role-add-form" >
           <el-form-item :label="$t('Administrator.roleName')">
@@ -13,64 +13,69 @@
           </el-form-item>
         </el-form>
       </div>
-    </div>
-  </template>
-  
-  <script>
-  import { roleDetail,addOREditRole } from '@/api/filter'
-  export default {
-    name: 'role-add',
-    mounted () {
+  </div>
+</template>
+
+<script>
+import { roleDetail, addOREditRole } from '@/api/filter'
+export default {
+  name: 'RoleAdd',
+  data() {
+    return {
+      form: {
+        roleName: null,
+        remark: null
+      },
+      loading: false,
+      submitButtonStat: false
+    }
+  },
+  mounted() {
+    if (this.$route.name === 'admin_role-edit') {
+      roleDetail(this.$route.params.id).then(response => {
+        this.form.roleName = response.data.ROLE_NAME
+        this.form.remark = response.data.REMARK
+        this.loading = false
+      }).catch(err => {
+        console.log('err---------' + err)
+      })
+    }
+  },
+  methods: {
+    onSubmit() {
+      this.submitButtonStat = true
+      let path = 'admin/role-add'
       if (this.$route.name === 'admin_role-edit') {
-        roleDetail(this.$route.params.id).then(response => {
-            this.form.roleName = response.data.ROLE_NAME
-            this.form.remark = response.data.REMARK
-            this.loading = false
-        }).catch(err => {
-          console.log('err---------' + err)
-        })
-      }
-    },
-    data () {
-      return {
-        form: {
-          roleName:null,
-          remark:null,
-        },
-        loading: false,
-        submitButtonStat: false,
+        path = 'admin/role-edit/' + this.$route.params.id
       }
-    },
-    methods: {
-      onSubmit () {
-        this.submitButtonStat = true
-        let path = 'admin/role-add'
-        if (this.$route.name === 'admin_role-edit') {
-          path = 'admin/role-edit/' + this.$route.params.id
-        }
-        addOREditRole(path,this.form).then(response => {
-          this.submitButtonStat = false
-          this.$message({
-            message: response.data,
-            type: 'success'
-          })
-          this.$router.go(-1)
-        }).catch(err => {
-          console.log('err---------' + err)
+      addOREditRole(path, this.form).then(response => {
+        this.submitButtonStat = false
+        this.$message({
+          message: response.data,
+          type: 'success'
         })
-      }
+        this.$router.go(-1)
+      }).catch(err => {
+        this.submitButtonStat = false
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        console.log('err---------' + err)
+      })
     }
   }
-  </script>
-  
-  <style scoped>
-  .whitebox-bg {
-    background: #fff;
-    padding: 25px;
-  }
-  @media (min-width: 760px) {
-   /* /deep/ .el-form-item__content {
-      width: 700px;
-    } */
-  }
-  </style>
+}
+</script>
+
+<style scoped>
+.whitebox-bg {
+  background: #fff;
+  padding: 25px;
+}
+@media (min-width: 760px) {
+  /* /deep/ .el-form-item__content {
+    width: 700px;
+  } */
+}
+</style>

+ 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>

+ 103 - 0
src/views/log/admin-login.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 { fetchAdminLoginList } 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)
+      fetchAdminLoginList(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/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>

+ 3 - 2
vue.config.js

@@ -27,7 +27,8 @@ module.exports = {
   publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
   outputDir: 'dist',
   assetsDir: 'static',
-  lintOnSave: process.env.NODE_ENV === 'development',
+  // lintOnSave: process.env.NODE_ENV === 'development',
+  lintOnSave: false,
   productionSourceMap: false,
   devServer: {
     port: port,
@@ -45,7 +46,7 @@ module.exports = {
         }
       }
     }
-    /*before: require('./mock/mock-server.js')*/
+    /* before: require('./mock/mock-server.js')*/
   },
   configureWebpack: {
     // provide the app's title in webpack's name field, so that