wuqing3 2 سال پیش
والد
کامیت
e6044180e8

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 397 - 121
package-lock.json


+ 19 - 6
package.json

@@ -8,16 +8,29 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "@riophae/vue-treeselect": "^0.4.0",
+    "axios": "^0.21.1",
     "core-js": "^3.6.5",
-    "vue": "^2.6.11"
+    "echarts": "^5.3.2",
+    "element-ui": "^2.15.6",
+    "jquery": "^3.6.0",
+    "moment": "^2.29.1",
+    "vue": "^2.6.11",
+    "vue-amap": "^0.5.10",
+    "vue-echarts": "^6.0.2",
+    "vue-router": "^3.2.0",
+
+    "vuex": "^3.4.0",
+    "wangeditor": "^4.7.12"
   },
   "devDependencies": {
-    "@vue/cli-plugin-babel": "~4.5.12",
-    "@vue/cli-plugin-eslint": "~4.5.12",
-    "@vue/cli-service": "~4.5.12",
+    "@riophae/vue-treeselect": "^0.4.0",
+    "@vue/cli-plugin-babel": "^4.5.0",
+    "@vue/cli-service": "^4.5.0",
+    "@vue/composition-api": "^1.5.0",
     "babel-eslint": "^10.1.0",
-    "eslint": "^6.7.2",
-    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
     "vue-template-compiler": "^2.6.11"
   },
   "eslintConfig": {

+ 21 - 15
src/App.vue

@@ -1,28 +1,34 @@
 <template>
   <div id="app">
-    <img alt="Vue logo" src="./assets/logo.png">
-    <HelloWorld msg="Welcome to Your Vue.js App"/>
+    <router-view/>
   </div>
 </template>
 
-<script>
-import HelloWorld from './components/HelloWorld.vue'
-
-export default {
-  name: 'App',
-  components: {
-    HelloWorld
-  }
+<style lang="less">
+*{
+  margin: 0;
+  padding: 0;
+}
+html,body{
+  width:100%;
+  height:100%;
+  /*background: #252a2f;*/
+  background: #eeeeee;
 }
-</script>
-
-<style>
 #app {
   font-family: Avenir, Helvetica, Arial, sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
-  text-align: center;
+  /*text-align: center;*/
   color: #2c3e50;
-  margin-top: 60px;
+  width: 100%;
+  height: 100%;
+  font-size:13px;
 }
+.flex-center{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
 </style>

+ 143 - 0
src/api/index.js

@@ -0,0 +1,143 @@
+/*
+* chinanet 接口总文件
+* by wzdxx1314@163.com
+* 2021-6-1
+* */
+// import {Message } from 'view-design';
+
+// import path from "../../config/path";
+import qs from 'qs';
+
+const axios = require('axios');
+import {Message} from "element-ui";
+import router from "@/router";
+
+
+const instance = axios.create({
+
+    baseURL: '/api',
+
+    timeout: 30000,
+});
+
+
+/*
+*
+* 请求的预处理 by wzd
+* */
+instance.interceptors.request.use((config) => {
+    config.headers['Accept'] = '';
+    config.headers['X-Requested-With'] = 'XMLHttpRequest';
+    config.headers['Authorization'] = 'Bearer ' + localStorage.getItem('token');
+    return config;
+}, (error) => {
+    return Promise.reject(error);
+});
+
+/*
+*
+* 返回的预处理 by wzd
+* */
+instance.interceptors.response.use(function (response) {
+    // Do something with response data
+    return response.data;
+}, function (error) {
+    // console.log(error);
+    return Promise.reject(error);
+});
+
+
+const instanceMusic = axios.create(
+    {
+        baseURL:'musicApi',
+        timeout:5000,
+    }
+)
+
+instanceMusic.interceptors.request.use((config) => {
+    // config.headers['Accept-Encoding'] = 'gzip';
+    // config.headers['User-Agent'] = 'Dalvik/2.1.0 (Linux; U; Android 12; M2006J10C Build/SP1A.210812.016)';
+
+    return config;
+}, (error) => {
+    return Promise.reject(error);
+});
+
+instanceMusic.interceptors.response.use(function (response) {
+    // Do something with response data
+    return response.data;
+}, function (error) {
+    // console.log(error);
+    return Promise.reject(error);
+});
+
+function fetch(ourl, params = {}, methods = 'post') {
+    return new Promise((resolve, reject) => {
+        let url = ourl;
+        if (methods == 'get') {
+            url = ourl + '?' + qs.stringify(params);
+        }
+        instance[methods](url, params).then(res => {
+            if (res.code == 200 || res.code == 1) {
+                resolve(res)
+            } else if (res.code == 401) {
+                router.push({name:'login'})
+            } else {
+                Message.error(res.msg)
+                reject(res);
+            }
+
+
+        }).catch((err) => {
+            Message.error(err.message);
+            reject(err);
+        })
+    })
+}
+
+function fetchThrid(ourl, params = {}, methods = 'post') {
+    return new Promise((resolve, reject) => {
+        let url = ourl;
+        if (methods == 'get') {
+            url = ourl + '?' + qs.stringify(params);
+        }
+        instanceMusic[methods](url, params).then(res => {
+            if (res.status == 1 || res.code == 0) {
+                resolve(res)
+            } else if (res.code == 401) {
+                router.push({name:'login'})
+            } else {
+                Message.error(res.msg)
+                reject(res);
+            }
+        }).catch((err) => {
+            Message.error(err.message);
+            reject(err);
+        })
+    })
+}
+export default {
+    //登录
+    login(params) {
+        return fetch('/passport/', params,"post");
+    },
+
+    //活动List
+    voteList(params){
+        return fetch('/vote_activities/section/',params,'get');
+    },
+
+    //获取验证码
+
+    verificationCode(params){
+        return fetch('/verificationCode/',params,"get")
+    },
+
+    votes(params){
+        return fetch('/votes/',params,"get")
+    },
+
+
+
+}
+

BIN
src/assets/404_images/404.png


BIN
src/assets/404_images/404_cloud.png


BIN
src/assets/bgimg.jpg


+ 249 - 0
src/assets/styles/common.css

@@ -0,0 +1,249 @@
+* {
+    box-sizing: unset;
+}
+
+
+label{
+    font-weight: 700;
+}
+
+
+
+
+
+.container{
+    padding:  10px 14px;
+    /*width: calc(100% - 840px);*/
+    width:100%;
+    height:100%;
+    z-index:1;
+    flex:1;
+    box-sizing: border-box;
+    /*border:1px solid red;*/
+}
+
+.card{
+    background: #FFFFFF;
+
+    border: 1px solid #EBEBEB;
+    box-sizing: border-box;
+    border-radius: 8px;
+    padding: 10px;
+    width: 100%;
+    height: 100%;
+    overflow-y: auto;
+
+    /*overflow-y: auto;*/
+}
+.box-card .el-card__header {
+    padding: 10px 15px;
+}
+
+.form-item {
+    display: flex;
+}
+.form-item .label {
+    width:120px;
+    min-width: 100px;
+    box-sizing: border-box;
+    text-align: right;
+    padding-right: 12px;
+    color: #606266;
+    font-weight: 700;
+    font-size: 14px;
+
+}
+.form-item .value {
+    margin-left: 15px;
+    font-size: 14px;
+    font-weight: 400;
+}
+
+.card .class_input {
+    width:240px;
+
+}
+.el-button{
+    /*box-shadow: 0 6px 12px 0 rgb(0 0 0 / 20%);*/
+}
+.container .search-box, .dialog .search-box {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    padding: 0px 20px;
+    font-size: 14px;
+
+    font-family: Microsoft YaHei;
+}
+
+
+
+
+
+.dialog .search-box {
+    padding: 0
+}
+.container .search-box .form-item, .dialog .search-box .form-item {
+    margin-right: 20px;
+    padding-top: 15px;
+
+    display: flex;
+}
+
+.container .search-box .form-item label, .dialog .search-box .form-item label {
+    margin-right: 8px;
+    /*width:auto;*/
+    width: 60px;
+    /*border:1px solid red;*/
+    text-align: right;
+    font-size: 13px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+}
+.container .search-box .form-item .el-input, .dialog .search-box .form-item .el-input {
+    max-width: 240px;
+}
+.container .search-box .form-item .el-button, .dialog .search-box .form-item .el-button {
+    /*margin-top: 17px;*/
+}
+.container .search-box .form-item .el-button--primary, .dialog .search-box .form-item .el-button--primary {
+    margin-left: 20px;
+}
+
+.container .table {
+    /*width: 100%;*/
+    /*background-color: white;*/
+}
+
+.container .activate {
+    color: #67c23a;
+}
+.container .is-disabled {
+    color :#c0c4cc;
+}
+
+.el-table .el-button--text{
+    padding: 0;
+}
+
+
+
+.detail .btn {
+    width: 86%;
+    bottom: 20px;
+    position: fixed;
+    height: 50px;
+    background-color: #f6f8fa;
+    margin-left: 20px;
+    /*display:flex;*/
+}
+.detail .btn .el-button {
+    position: absolute;
+    margin-left: 20px;
+    margin-top: 10px;
+}
+.detail .el-textarea {
+    margin-top: 5px;
+}
+.detail .el-cascader--small {
+    width: 100%;
+}
+
+.el-dialog__header {
+    text-align: left;
+    border-bottom: 1px solid rgba(233, 233, 233, 1);
+}
+.el-dialog__header .el-dialog__title{
+    font-size: 14px;
+}
+.el-dialog--center .el-dialog__body {
+    padding: 25px 25px 3px;
+}
+
+
+.el-cascader-menu {
+    min-width: 120px;
+}
+.el-cascader-menu .el-cascader-menu__item{
+    font-size: 12px;
+    padding: 8px 5px;
+}
+
+.el-button--text{
+    font-size: 12px;
+}
+.btn .el-form-item__content{
+    margin-left: 140px !important;
+}
+
+.el-dialog{
+    max-height:calc(100% - 200px);
+
+    display:flex;
+    flex-direction:column;
+
+}
+.el-dialog__body{
+    overflow:auto
+}
+
+.dialog .el-dialog__header {
+    height: unset;
+    line-height: unset;
+}
+.dialog .el-dialog__header .el-dialog__headerbtn {
+    line-height: 1;
+}
+.btn_text_danger{
+    color:#f56c6c;
+}
+.btn_text_edit{
+    color:#e6a23c;
+}
+
+
+
+
+
+
+
+
+.search-more-box .el-input, .search-more-box .el-date-editor {
+    width: 240px !important;
+}
+
+
+.el-dialog.is-fullscreen .el-dialog__header {
+    padding: 0;
+}
+/*.el-dialog.is-fullscreen .el-table th {*/
+/*    background-color: rgb(246, 248, 250);*/
+/*}*/
+/*.el-dialog.is-fullscreen .table {*/
+/*    border-bottom: 1px solid #EBEEF5;*/
+/*}*/
+/*.el-table .el-table-column--selection .cell{*/
+/*    padding-right: 0;*/
+/*}*/
+
+
+/**/
+.baseContainer{
+    width:100%;
+    box-sizing:border-box;
+    padding: 20px;
+}
+.append{
+    text-align: right;
+    vertical-align: middle;
+    /*float: left;*/
+    font-size: 14px;
+    color: #606266;
+    line-height: 40px;
+    padding: 0 12px 0 0;
+    box-sizing: border-box;
+}
+.w240px{
+    width:240px;
+}

+ 22 - 0
src/assets/styles/globa.less

@@ -0,0 +1,22 @@
+//弹窗
+.dialog{
+  //width: 600px;
+    .el-form-item{
+      margin-bottom: 20px;
+
+      .el-input{
+        width:240px;
+        //margin-left: 15px;
+      }
+    }
+
+}
+
+.el-dialog{
+  ::-webkit-scrollbar {
+    width: 0;
+  }
+}
+
+
+

+ 220 - 0
src/components/RxTable.vue

@@ -0,0 +1,220 @@
+<template>
+  <div class="hltable">
+    <el-table
+
+        ref="table"
+        id="HLtableList"
+        :data="data"
+        :border="border"
+        :size="size"
+        :show-overflow-tooltip="showOverflowTooltip"
+        :show-summary="showSummary"
+        :tooltip-effect="tooltipEffect"
+        :height='autoHeigit === true ? computedHeight : height'
+        :search="search"
+        :header-cell-style="tableHeaderColor"
+        :stripe="stripe"
+        :highlight-current-row="currentRow"
+        @selection-change="selectChange"
+        @select="select"
+        @select-all="selectAll"
+        @row-click="rowClick"
+        @current-change="currentChange"
+        v-loading="loading"
+        style="width: 98%;margin: 5px auto 0 15px;border-bottom: 1px solid rgba(233, 233, 233, 1)"
+    >
+      <el-table-column v-if="multiple" type="selection" width="50" align="left"></el-table-column>
+
+      <el-table-column
+          v-if="serialize"
+          type="index"
+          label="序号"
+          align="center"
+          width="55"
+      ></el-table-column>
+
+      <slot></slot>
+      <el-table-column v-if="blankCol"></el-table-column>
+    </el-table>
+    <div class="containers" v-if="showPagination">
+      <slot name="count"></slot>
+      <el-pagination
+          background
+          class="page"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page.sync="currentPage"
+          :page-sizes="pageSizes"
+          :page-size="pageSize"
+          :layout="layout"
+          :total="total"
+          :page-count="pageCount"
+      ></el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import $ from "jquery";
+import _ from 'lodash'
+// import { setTimeout } from 'timers';
+export default {
+  name: "RxTable",
+  props: {
+    data: {
+      default: () => []
+    },
+    showPagination:{
+      default:true
+    },
+    size: {
+      default: "small"
+    },
+    pageCount:{
+      default:0,
+    },
+    border: {
+      default: true
+    },
+    showOverflowTooltip: {
+      default: false
+    },
+    multiple: {
+      default: false
+    },
+    serialize: {
+      default: true
+    },
+    showSummary: {
+      default: false
+    },
+    tooltipEffect: {
+      default: "dark"
+    },
+    loading: {
+      default: false
+    },
+    stripe: {
+      default: true
+    },
+    search: {
+      default: false
+    },
+    total: {
+      default: 0
+    },
+    pageSizes: {
+      default: () => [2, 20, 50, 100]
+    },
+    pageSize: {
+      default: 20
+    },
+    currentPage: {
+      default: 1
+    },
+    layout: {
+      default: "total,  prev, pager, next, jumper"
+    },
+    autoHeigit: {
+      type: Boolean,
+      default: true
+    },
+    height: {
+      type: Number,
+      default: 0
+    },
+    blankCol: {
+      type: Boolean,
+      default: true
+    },
+    currentRow: {
+      type: Boolean,
+      default: true
+    },
+  },
+  data() {
+    return {
+      computedHeight: 0
+    };
+  },
+  computed: {},
+  methods: {
+    // 修改table header的背景色
+    tableHeaderColor({rowIndex}) {
+      if (rowIndex === 0) {
+        return "background-color: #F6F8FA;color: #262626;font-weight: 500;";
+      }
+    },
+    selectChange(selection) {
+      this.$emit("selection-change", selection);
+    },
+    selectAll(selection){
+      console.log("selectAll="+selection)
+    },
+    select(selection, row){
+      console.log(selection)
+    },
+    rowClick(row, event, column) {
+      let data = {
+        row: row,
+        event: event,
+        column: column
+      };
+      this.$emit("row-click", data);
+    },
+    handleSizeChange(size) {
+      this.$emit("sizeChange", size);
+    },
+    handleCurrentChange(page) {
+
+      this.$emit("pageChange", page);
+    },
+    computedTableHeight() {
+      if (this.autoHeigit === true) {
+        this.computedHeight = this.getHlTableListHeight();
+      }
+    },
+    getHlTableListHeight() {
+      const $HLtableList = $("#HLtableList");
+
+      return window.innerHeight - $HLtableList.offset().top - 70;
+    },
+    currentChange(currentRow, oldCurrentRow) {
+      this.$emit('currentChange', currentRow, oldCurrentRow)
+    }
+  },
+  mounted() {
+    setTimeout(()=>{
+      this.computedTableHeight();
+    },400)
+
+    $(window).bind("resize", _.throttle(() => {
+      this.computedTableHeight();
+    }, 400));
+  },
+  destroyed () {
+    $(window).off("resize");
+  }
+};
+</script>
+
+<style scoped lang="less">
+.containers {
+  width: 98%;
+  margin-left: 20px;
+  height: 34px;
+  background-color: #f6f8fa;
+  position: relative;
+  /*margin-top: 15px;*/
+  margin-bottom: 15px;
+  .page {
+    display: block;
+    position: absolute;
+    right: 10px;
+    /*top:px;*/
+  }
+}
+.hltable /deep/ .el-table .is-center .cell {
+  padding-left: 0;
+}
+</style>

+ 155 - 0
src/components/searchForm.vue

@@ -0,0 +1,155 @@
+<template>
+  <div class="searchForm" @keypress="keypress">
+    <div class="labelBox">
+
+
+      <div class="label" v-if="keys.includes('key')">
+        <span class="name">关键词:</span>
+        <el-input v-model="form.key" placeholder="请输入" size="small" class="input"></el-input>
+      </div>
+
+
+
+      <div class="label" v-if="keys.includes('activity')">
+        <span class="name">活动主题:</span>
+        <el-select v-model="form.activityId" placeholder="请选择"  :clearable="true" class="input" size="small" style="width: 240px;">
+          <el-option v-for="item in activityOption" :key="item.id" :value="item.id" :label="item.title"></el-option>
+
+        </el-select>
+      </div>
+
+      <div class="label" v-if="keys.includes('optime')">
+        <span class="name">投票时间:</span>
+        <el-date-picker
+            size="small"
+            v-model="time1"
+            type="daterange"
+            range-separator="至"
+            value-format="timestamp"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期">
+        </el-date-picker>
+      </div>
+
+
+
+      <div class="btn-box">
+        <el-button type="primary" icon="el-icon-search" size="small" @click="$emit('on-search')">查询</el-button>
+        <el-button icon="el-icon-refresh" size="small" @click="reset">重置</el-button>
+      </div>
+    </div>
+    <div class="btn-box">
+      <slot></slot>
+    </div>
+  </div>
+
+</template>
+
+<script>
+let defaultForm = {
+  key:"",
+  activityId:""
+}
+export default {
+  name: "searchForm",
+  props: {
+    keys: {
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      form: {
+        ...defaultForm
+      },
+      time1: [],
+      dictValueOption: [],
+      roleOption: [],
+      shopOption: [],
+      countryOption:[],
+      activityOption:[]
+
+    }
+  },
+  mounted() {
+
+    if(this.keys.includes("activity")){
+      this.getActivitys()
+    }
+
+  },
+  methods: {
+    getActivitys(){
+
+      this.$api.voteList({
+        key:'',
+        pageNum:1,
+        pageCapacity: 1000,
+      }).then(res => {
+
+        console.log(res)
+        this.activityOption = res.content.voteActivityViewDtos;
+
+
+
+      })
+    },
+    getCountryDict(){
+      this.$api.countryDict().then(res => {
+        console.log(res)
+        this.countryOption = res.data;
+      })
+    },
+    keypress() {
+      // this.$emit('on-search')
+    },
+    reset() {
+      this.form = {...defaultForm}
+      this.$emit('on-search')
+    },
+  },
+
+}
+</script>
+
+<style scoped lang="less">
+.input {
+  width: 200px;
+}
+
+.searchForm {
+  display: flex;
+  justify-content: space-between;
+  padding: 10px 10px 5px 5px;
+  align-items: center;
+  flex-wrap: wrap;
+
+  .labelBox {
+    display: flex;
+    flex-wrap: wrap;
+  }
+
+  .label {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 10px;
+    margin-right: 10px;
+
+    .name {
+      min-width: 80px;
+      display: inline-block;
+      flex: 0;
+      max-width: 100px;
+      text-align: right;
+      font-size: 12px;
+      color: #666666;
+    }
+  }
+
+  .btn-box {
+    margin-left: 10px;
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 73 - 1
src/main.js

@@ -1,8 +1,80 @@
 import Vue from 'vue'
 import App from './App.vue'
+import router from './router'
+import store from './store'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import './assets/styles/common.css'
+import './assets/styles/globa.less'
+
+import api from '@/api/index.js'
+
+import util from '@/util/index.js'
+import moment from 'moment'
+
+
+
+
+
+
+Vue.prototype.$api = api;
+
+Vue.prototype.$util = util;
+Vue.prototype.$openUrl = (params)=>{
+  let {href} = router.resolve(params)
+  window.open(href,'_blank')
+
+}
+window.moment = moment;
+
+ElementUI.Dialog.props.closeOnClickModal.default = false; // 修改 el-dialog 默认点击遮照为不关闭
+Vue.use(ElementUI)
 
 Vue.config.productionTip = false
+// 全局混入
+
+Vue.filter('timeResolve', function (value) {
+  if (!value) return ''
+  return moment(value).format('YYYY-MM-DD HH:mm:SS')
+})
+Vue.filter('timeResolveD', function (value) {
+  if (!value) return ''
+  return moment(value).format('YYYY-MM-DD')
+})
+
+Vue.filter('StoM',function (value){
+  if(!value) return ''
+  let minutes = parseInt(value /60);
+  let seconds = value % 60;
+  if(minutes < 10){
+    minutes = '0' + minutes;
+  }
+  if(seconds < 10){
+    seconds = '0' + seconds
+  }
+
+  return minutes + ":"+seconds
+})
+
 
 new Vue({
-  render: h => h(App),
+  router,
+  store,
+  render: h => h(App)
 }).$mount('#app')
+
+
+//统一在这里做router 和tag的处理
+Vue.prototype.$router_ = (params={})=>{
+  params.canClose = params.name!=='home'
+  store.dispatch('pushMenu',params)
+  router.push(params)
+}
+
+//首先获取本地的tab组,如果有的话,将tab组初始化
+let list = JSON.parse(localStorage.getItem('rx-flq-menuList'))
+if(list && list.length){
+  store.dispatch('configMenu',list)
+}else{
+  Vue.prototype.$router_({name:'home',title:'控制台'})
+}

+ 89 - 0
src/router/index.js

@@ -0,0 +1,89 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import Main from "@/views/main/main";
+
+import login from "@/views/login/index";
+import {getDeepPrototy} from '@/util/index'
+
+Vue.use(VueRouter)
+
+const routes = [
+    {
+        path: '/login',
+        name: 'login',
+        component: login,
+        meta: {
+            title: ""
+        }
+    },
+    {
+        path: "/",
+        name: "Main",
+        component: Main,
+        meta: {
+            title: "",
+        },
+        children: [
+            {
+                path: '/home',
+                name: 'home',
+                component: () => import('@/views/home/home'),
+            },
+            {
+                path: '/404',
+                name: '404',
+                component: () => import('@/views/error-page/404'),
+            },
+            {
+                path: '/voteActivity',
+                name:'voteActivity',
+                component:() => import('@/views/vote/index')
+            },
+            {
+                path: '/vote',
+                name:'vote',
+                component:() => import('@/views/vote/vote')
+            },
+            {
+                path: '/voteActivityInfo',
+                name:'voteActivityInfo',
+                component:() => import('@/views/vote/voteActivityInfo')
+            },
+
+
+
+        ]
+    }
+]
+
+let routerNameList = getDeepPrototy(routes, 'name');
+
+
+const router = new VueRouter({
+    routes
+})
+
+router.beforeEach((to, from, next) => {
+    // LoadingBar.start();
+    // console.log(to)
+    // console.log(from)
+    console.log(to.name);
+    if (!routerNameList.includes(to.name)) {
+
+        next({name: '404'})
+    }
+    if (to.name == 'Main') {
+        next({name: 'home'})
+    } else {
+        next();
+    }
+});
+
+// 重写vueRouter的push方法,让点击重复路由的情况不会报错 其实这个报错本身也没事
+const originalPush = VueRouter.prototype.push
+VueRouter.prototype.push = function push(location) {
+    console.log(location)
+    return originalPush.call(this, location).catch(err => err)
+}
+
+export default router

+ 81 - 0
src/store/index.js

@@ -0,0 +1,81 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+
+const homeTab = {
+    title:'控制台',
+    name:'home',
+    canClose:false
+}
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+    state: {
+        leftMenuCollapsed:false,
+        menuTabs:[
+            homeTab
+        ],
+        userInfo:{
+
+        }
+    },
+    getters:{
+        leftMenuCollapsed:state=>state.leftMenuCollapsed,
+        menuTabs:state=>state.menuTabs,
+        userInfo:state=>state.userInfo,
+    },
+    mutations: {
+        CHANGECOLL(state,payload){
+            state.leftMenuCollapsed = !state.leftMenuCollapsed
+        },
+        //添加路由
+        PUSH_MENU(state,payload){
+            let bool = true
+            state.menuTabs.some(target=>{
+                if(target.name == payload.name){
+                    bool = false
+                    return true
+                }
+            })
+            if(bool){
+                state.menuTabs.push(payload)
+            }
+            if(state.menuTabs.length>12){
+                state.menuTabs = state.menuTabs.filter((x,i)=>{
+                    return i!=1
+                })
+            }
+            localStorage.setItem('rx-flq-menuList',JSON.stringify(state.menuTabs))
+        },
+        //set路由
+        CONFIG_MENU(state,payload){
+            state.menuTabs = payload
+        },
+        SET_USERINFO(state,payload){
+            state.userInfo = payload
+        },
+        //删除路由
+        DELETE_MENU(state,payload){
+            state.menuTabs = state.menuTabs.filter(target=>{
+                return target.name != payload.name
+            })
+            localStorage.setItem('rx-flq-menuList',JSON.stringify(state.menuTabs))
+        },
+    },
+    actions: {
+        pushMenu({commit},payload){
+            commit('PUSH_MENU',payload)
+        },
+        configMenu({commit},payload){
+            commit('CONFIG_MENU',payload)
+        },
+        deleteMenu({commit},payload){
+            commit('DELETE_MENU',payload)
+        },
+        setUserInfo({commit},payload){
+            commit('SET_USERINFO',payload)
+        },
+    },
+    modules: {
+    }
+})

+ 188 - 0
src/util/index.js

@@ -0,0 +1,188 @@
+/*
+* by wzdxx1314@163.com
+*
+* */
+//深层次获取value by arr
+export const getDeepPrototy = (arr,prop)=>{
+    let finArr = [];
+    const getFn = (obj)=>{
+        finArr.push(obj[prop])
+        if(obj.children && obj.children.length!==0){
+            obj.children.forEach(target=>{
+                getFn(target)
+            })
+        }
+    }
+    arr.forEach(target=>{
+        getFn(target)
+    })
+    return  finArr
+}
+
+
+//通过属性获取对应的值
+export const getDeepObj = (arr,prop,value)=>{
+    let object;
+    const getObj = (obj)=>{
+        if(obj[prop] == value){
+            object = obj
+            return;
+        }else{
+            if(obj.children && obj.children.length!==0){
+                obj.children.forEach(target=>{
+                    getObj(target)
+                })
+            }
+        }
+    }
+    arr.forEach(target=>{
+        getObj(target)
+    })
+    return object
+}
+//进入全屏
+export const  launchFullscreen = (element) =>{
+    if(element.requestFullscreen) {
+        element.requestFullscreen();
+    } else if(element.mozRequestFullScreen) {
+        element.mozRequestFullScreen();
+    } else if(element.webkitRequestFullscreen) {
+        element.webkitRequestFullscreen();
+    } else if(element.msRequestFullscreen) {
+        element.msRequestFullscreen();
+    }
+}
+// 判断浏览器种类退出全屏
+export const exitFullscreen = ()=> {
+    if(document.exitFullscreen) {
+        document.exitFullscreen();
+    } else if(document.mozCancelFullScreen) {
+        document.mozCancelFullScreen();
+    } else if(document.webkitExitFullscreen) {
+        document.webkitExitFullscreen();
+    }
+}
+
+export const getUuid = ()=>{
+    var s = [];
+    var hexDigits = "0123456789abcdef";
+    for (var i = 0; i < 32; i++) {
+        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+    }
+    s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
+    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
+    s[8] = s[13] = s[18] = s[23];
+    var uuid = s.join("");
+    return uuid;
+}
+
+export const  getTimestamp = (date)=>{
+    date = date || new Date()
+    var tmp = Date.parse( date ).toString();
+    tmp = tmp.substr(0,10);
+    return tmp;
+}
+
+export const deepResolve = (obj)=>{
+    obj = Object
+        .keys(obj).filter(t=>(params[t]!==false && params[t]!==null && params[t]!==''))
+        .sort()
+    Object.keys(obj).forEach(target=>{
+
+        if(Object.prototype.toString.call(obj[target]) == "[object Array]" ){
+            obj[target].forEach(tar=>{
+                if(Object.prototype.toString.call(tar) =="[object Object]" ){
+                    deepResolve(tar)
+                }
+            })
+        }
+        if( Object.prototype.toString.call(obj[target]) == "[object Object]"){
+            deepResolve(obj[target])
+        }
+    })
+    return obj
+}
+
+export const addIndex = (arr)=>{
+    arr.forEach((x,i)=>{
+        x.index =i
+    })
+    return arr
+}
+
+export const arrayHas = (arr,val) =>{
+    let bool = false
+    arr.some(x=>{
+        if(x == val){
+            bool = true
+            return true
+        }
+    })
+    return bool
+}
+
+export const combination = (arr) =>{
+    if(arr.length==0) {return []}
+    function com(arr1,arr2){
+        let result = []
+        arr1.forEach(target=>{
+            arr2.forEach(tar=>{
+                result.push([...target,tar])
+            })
+        })
+        return result
+    }
+    let x = arr[0].map(target=>[target])
+    for(var i=1;i<arr.length;i++){
+        x = com(x,arr[i])
+    }
+    return x
+}
+
+/*时间戳转换成-年月日时分秒*/
+function formatDateTime(inputTime) {
+    var date = new Date(inputTime*1000);
+    var y = date.getFullYear();
+    var m = date.getMonth() + 1;
+    m = m < 10 ? ('0' + m) : m;
+    var d = date.getDate();
+    d = d < 10 ? ('0' + d) : d;
+    var h = date.getHours();
+    h = h < 10 ? ('0' + h) : h;
+    var minute = date.getMinutes();
+    var second = date.getSeconds();
+    minute = minute < 10 ? ('0' + minute) : minute;
+    second = second < 10 ? ('0' + second) : second;
+    return y + '-' + m + '-' + d + ' '+ h + ':' + minute + ':' + second;
+}
+//获取key-value map给下拉选择器用,并把键值统一成key-value
+async function getOptions(list,options={}){
+    options={...options};
+
+    for(let item of list){
+        const res=await this.$api[item.api]();
+        //兼容一下后端有的人爱往res.data.data放有的人爱往res.data放的情况
+        const list=res['data']['data']||res['data'];
+        options[item.param]=list.map(e=>{
+            return {
+                id:e[item.idName],
+                label:e[item.valueName]||e[Object.keys(e)[1]]//兼容labelKey没写对的情况
+            };
+        });
+    }
+    return options;
+}
+
+export default {
+    getDeepPrototy,
+    exitFullscreen,
+    launchFullscreen,
+    getDeepObj,
+    getUuid,
+    getTimestamp,
+    addIndex,
+    arrayHas,
+    combination,
+    formatDateTime,
+    getOptions
+}

+ 243 - 0
src/views/error-page/404.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="wscn-http404-container">
+    <div class="wscn-http404">
+      <div class="pic-404">
+        <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
+        <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
+        <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
+        <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
+      </div>
+      <div class="bullshit">
+        <div class="bullshit__oops">链接走丢了!</div>
+        <!--                <div class="bullshit__info">All rights reserved-->
+        <!--                    <a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>-->
+        <!--                </div>-->
+        <div class="bullshit__headline">{{ message }}</div>
+        <div class="bullshit__info"></div>
+        <a @click="goHome" class="bullshit__return-home">Back to home</a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+
+export default {
+  name: 'Page404',
+  computed: {
+    message() {
+      return '请检查路由是否正确,或者点击按钮回到控制台...'
+    }
+  },
+  methods:{
+    goHome(){
+      this.$router.push({name:'home'})
+    },
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.wscn-http404-container{
+  /*border:1px solid red;*/
+  height: 100%;
+  width: 100%;
+  // transform: translate(-50%,-50%);
+  /*position: absolute;*/
+  /*top: 0%;*/
+  /*left: 0%;*/
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+.wscn-http404 {
+  /*position: relative;*/
+
+  width: 80%;
+  margin: 0 auto;
+  /*border:1px solid red;*/
+  /*width: 1200px;*/
+  padding: 0 50px;
+  overflow: hidden;
+  .pic-404 {
+    position: relative;
+    float: left;
+    width: 600px;
+    overflow: hidden;
+    &__parent {
+      width: 100%;
+    }
+    &__child {
+      position: absolute;
+      &.left {
+        width: 80px;
+        top: 17px;
+        left: 220px;
+        opacity: 0;
+        animation-name: cloudLeft;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1s;
+      }
+      &.mid {
+        width: 46px;
+        top: 10px;
+        left: 420px;
+        opacity: 0;
+        animation-name: cloudMid;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1.2s;
+      }
+      &.right {
+        width: 62px;
+        top: 100px;
+        left: 500px;
+        opacity: 0;
+        animation-name: cloudRight;
+        animation-duration: 2s;
+        animation-timing-function: linear;
+        animation-fill-mode: forwards;
+        animation-delay: 1s;
+      }
+      @keyframes cloudLeft {
+        0% {
+          top: 17px;
+          left: 220px;
+          opacity: 0;
+        }
+        20% {
+          top: 33px;
+          left: 188px;
+          opacity: 1;
+        }
+        80% {
+          top: 81px;
+          left: 92px;
+          opacity: 1;
+        }
+        100% {
+          top: 97px;
+          left: 60px;
+          opacity: 0;
+        }
+      }
+      @keyframes cloudMid {
+        0% {
+          top: 10px;
+          left: 420px;
+          opacity: 0;
+        }
+        20% {
+          top: 40px;
+          left: 360px;
+          opacity: 1;
+        }
+        70% {
+          top: 130px;
+          left: 180px;
+          opacity: 1;
+        }
+        100% {
+          top: 160px;
+          left: 120px;
+          opacity: 0;
+        }
+      }
+      @keyframes cloudRight {
+        0% {
+          top: 100px;
+          left: 500px;
+          opacity: 0;
+        }
+        20% {
+          top: 120px;
+          left: 460px;
+          opacity: 1;
+        }
+        80% {
+          top: 180px;
+          left: 340px;
+          opacity: 1;
+        }
+        100% {
+          top: 200px;
+          left: 300px;
+          opacity: 0;
+        }
+      }
+    }
+  }
+  .bullshit {
+    position: relative;
+    float: left;
+    width: 300px;
+    padding: 30px 0;
+    overflow: hidden;
+    &__oops {
+      font-size: 32px;
+      font-weight: bold;
+      line-height: 40px;
+      color: #1482f0;
+      opacity: 0;
+      margin-bottom: 20px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-fill-mode: forwards;
+    }
+    &__headline {
+      font-size: 20px;
+      line-height: 24px;
+      color: #222;
+      font-weight: bold;
+      opacity: 0;
+      margin-bottom: 10px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.1s;
+      animation-fill-mode: forwards;
+    }
+    &__info {
+      font-size: 13px;
+      line-height: 21px;
+      color: grey;
+      opacity: 0;
+      margin-bottom: 30px;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.2s;
+      animation-fill-mode: forwards;
+    }
+    &__return-home {
+      display: block;
+      float: left;
+      width: 110px;
+      height: 36px;
+      background: #1482f0;
+      border-radius: 100px;
+      text-align: center;
+      color: #ffffff;
+      opacity: 0;
+      font-size: 14px;
+      line-height: 36px;
+      cursor: pointer;
+      animation-name: slideUp;
+      animation-duration: 0.5s;
+      animation-delay: 0.3s;
+      animation-fill-mode: forwards;
+    }
+    @keyframes slideUp {
+      0% {
+        transform: translateY(60px);
+        opacity: 0;
+      }
+      100% {
+        transform: translateY(0);
+        opacity: 1;
+      }
+    }
+  }
+}
+</style>

+ 13 - 0
src/views/home/home.vue

@@ -0,0 +1,13 @@
+<template>
+ <div></div>
+</template>
+
+<script>
+export default {
+  name: "home"
+}
+</script>
+
+<style scoped>
+
+</style>

+ 155 - 0
src/views/login/index.vue

@@ -0,0 +1,155 @@
+<template>
+  <div>
+    <!--        <img :src="bcg" alt="" class="pos">-->
+    <div class="container" @keypress="keypress">
+      <div class="bigTitle">业委会管理系统登录</div>
+
+    <div class="module" v-if="step == 0" style="margin-top: 50px;width: 100%; display: flex;flex-direction:column;align-items: center ;">
+      <div class="floor">
+
+        <el-button style="width: 240px; height: 50px;border:1px solid rgb(64,158,225);color: rgb(64,158,225)" @click="changeType('1')">密码登录</el-button>
+      </div>
+
+      <div class="floor">
+        <el-button style="width: 240px; height: 50px;border:1px solid rgb(64,158,225);color: rgb(64,158,225)" @click="changeType('2')">验证码登录</el-button>
+      </div>
+    </div>
+
+    <div v-if="step == 1" class="module">
+      <div class="floor">
+        <div class="title">手机号</div>
+        <el-input class="input" type="text" v-model="loginForm.mobile"/>
+      </div>
+
+      <div class="floor" v-if="loginForm.type == 1">
+        <div class="title">密码</div>
+        <el-input class="input" show-password type="text" v-model="loginForm.securityCode"/>
+      </div>
+
+      <div class="floor" v-if="loginForm.type == 2">
+        <div class="title">验证码</div>
+
+        <el-input class="codeInput" type="text" v-model="loginForm.securityCode"/>
+        <el-button type="success" @click="getSecurityCode" style="width: 100px;display: flex;justify-content: center;">获取验证码</el-button>
+      </div>
+      <div class="floor">
+        <el-button style="width: 100%" @click="handleLogin" type="success">登录</el-button>
+      </div>
+    </div>
+
+    </div>
+  </div>
+</template>
+
+<script>
+import {Loading} from 'element-ui';
+
+export default {
+  name: "index",
+  data: () => {
+    return {
+      step:0,
+      loginForm: {
+
+        securityCode: "",
+        mobile: '',
+        type:""
+      },
+      codeSrc: ""
+    }
+  },
+  created() {
+    this.captchaImage();
+  },
+  methods: {
+
+    changeType(type){
+        this.loginForm.type = type;
+        this.step = 1;
+
+
+    },
+    getSecurityCode(){
+      let mobile = this.loginForm.mobile;
+
+      if(mobile == ""){
+        this.$message.warning("请先输入手机号码")
+        return false;
+      }
+        this.$api.verificationCode({
+          mobile
+        }).then(res => {
+          console.log(res)
+          this.loginForm.securityCode = res.content.verificationCode;
+
+        })
+    },
+    keypress(e){
+      if(e.keyCode == 13){
+        this.handleLogin();
+      }
+    },
+
+    handleLogin() {
+       this.$api.login(this.loginForm).then(res => {
+         // console.log(res)
+         this.$router.push("home")
+       })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.code {
+  height: 50px;
+  width: 100px;
+  cursor: pointer;
+  padding-left: 10px;
+  box-sizing: border-box;
+}
+
+.floor {
+  display: flex;
+  margin-top: 20px;
+  align-items: center;
+}
+
+.floor .title {
+  flex-grow: 1;
+}
+
+.input {
+  width: 240px;
+}
+
+.codeInput {
+  width: 140px;
+}
+
+.container {
+  position: fixed;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%); /* 50%为自身尺寸的一半 */
+  z-index: 4;
+  background-color: #fff;
+  width: 360px;
+  height: 300px;
+  padding: 30px;
+  border-radius: 20px;
+}
+
+.pos {
+  position: fixed;
+  height: 100vh;
+  width: 100vw;
+  z-index: 3;
+}
+
+.bigTitle {
+  font-weight: bold;
+  font-size: 24px;
+  text-align: center;
+}
+</style>

+ 58 - 0
src/views/main/main.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="Main">
+    <leftMenu></leftMenu>
+    <div class="body">
+      <rxHeader></rxHeader>
+      <rxBreadcrumb></rxBreadcrumb>
+      <div class="main-router">
+        <keep-alive>
+          <router-view v-if="$route.meta.keepAlive"></router-view>
+        </keep-alive>
+        <router-view v-if="!$route.meta.keepAlive"></router-view>
+      </div>
+    </div>
+  </div>
+
+</template>
+
+<script>
+import leftMenu from "@/views/main/modules/leftMenu";
+import rxHeader from "./modules/rx-header";
+import rxBreadcrumb from './modules/rx-breadcrumb'
+export default {
+  name: "Main",
+  components:{
+    leftMenu,
+    rxHeader,
+    rxBreadcrumb
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.Main {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  overflow-x: hidden;
+
+  .body {
+    flex: 1;
+    display: flex;
+    width: calc(100% - 240px);
+    flex-direction: column;
+
+  }
+
+  .main-router {
+    width: 100%;
+    height: 100%;
+    flex: 1;
+    overflow-y: auto;
+    /*border:10px solid black;*/
+    /*overflow-x: scroll;*/
+    /*border:1px solid black;*/
+    /*position: relative;*/
+  }
+}
+</style>

+ 181 - 0
src/views/main/modules/leftMenu.vue

@@ -0,0 +1,181 @@
+<template>
+  <div :class="['leftMenu',{collapsed:leftMenuCollapsed}]">
+    <div class="logo" @click="goHome">
+      <!--      <img :class="['logo-img',{'small-img':leftMenuCollapsed}]" src="../../../assets/logo.png" alt="">-->
+      <span v-if="!leftMenuCollapsed" class="title">TV STATION</span>
+    </div>
+    <el-menu
+        :unique-opened="true"
+        @select="handlerSelect"
+        mode="vertical"
+        :collapse="leftMenuCollapsed">
+      <template v-for="(item,index) in menuList">
+        <el-menu-item :index="item.name" v-if="item.children.length == 0">
+          <template slot="title">
+            <i :class="item.icon" style="width:1.2em;height: 1.2em"></i>
+            <span style="font-size: 1em;">主页</span>
+          </template>
+        </el-menu-item>
+        <el-submenu :index="item.name" style="text-align: left;" v-if="item.children.length != 0" class="submenu">
+          <template slot="title">
+            <i :class="item.icon" style="width:1.2em;height: 1.2em;color: #333333"></i>
+            <span style="font-size: 1em;">{{ item.title }}</span>
+          </template>
+          <el-menu-item :index="tar.name" v-for="(tar,inde) in item.children" :key="inde">
+            {{ tar.title }}
+          </el-menu-item>
+        </el-submenu>
+      </template>
+
+
+    </el-menu>
+  </div>
+
+</template>
+
+<script>
+import {mapGetters} from 'vuex'
+import {getDeepObj} from "@/util";
+let getMenus = (list)=>{
+  list.forEach(x=>{
+    changeData(x)
+  })
+  function changeData(obj){
+    obj.title = obj.meta.title
+    obj.name = obj.path
+    obj.icon = obj.meta.icon
+    if(obj.children){
+      obj.children.forEach(x=>{
+        changeData(x)
+      })
+    }
+  }
+  return list
+}
+export default {
+  name: "leftMenu",
+  data() {
+    return {
+      menuList: [
+        {
+            icon:"",
+            title:'投票',
+            children:[
+              {
+                name:'voteActivity',
+                icon:"",
+                title:'投票活动'
+              },
+              {
+                name:'vote',
+                icon:"",
+                title:'表决事项'
+              },
+            ]
+        },
+
+      ],
+    };
+  },
+
+  computed: {
+    ...mapGetters(['leftMenuCollapsed']),
+    roleId() {
+      return localStorage.getItem('id');
+    }
+  },
+  watch: {},
+  mounted() {
+
+  },
+  methods: {
+
+    goHome() {
+      this.$router.push({name: 'home'})
+    },
+    handlerSelect(index) {
+      if (index) {
+        this.defaultName = index;
+
+        this.$router_(getDeepObj(this.menuList, 'name', index),)
+      } else {
+        return
+      }
+
+
+    }
+  }
+}
+</script>
+
+<style scoped lang="less">
+::-webkit-scrollbar {
+  display: none; /*ChromeSafari*/
+}
+
+.leftMenu {
+  /*border:1px solid red;*/
+  width: 160px;
+  height: 100%;
+  overflow-y: auto;
+  //background-color: rgb(48, 65, 86);
+  background-color: white;
+  transition: all 0.3s;
+  z-index: 10;
+  flex: 0 0 auto;
+  border-right: 1px solid #e6e6e6;
+
+  .logo {
+    height: 60px;
+    padding: 0 24px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    border-bottom: 1px solid #e8e8e8;
+
+    .logo-img {
+      width: 40px;
+      height: 40px;
+      border-radius: 100%;
+    }
+
+    .small-img {
+      width: 30px;
+      height: 30px;
+    }
+
+    .title {
+      font-size: 16px;
+      font-weight: 700;
+      display: inline-block;
+    }
+  }
+}
+
+.collapsed {
+  width: 80px;
+}
+
+
+i {
+  font-size: 18px !important;
+}
+
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  min-height: 400px;
+}
+
+.leftMenu {
+  height: 100%;
+  overflow-y: auto;
+
+}
+.el-menu {
+  border-right: 0px;
+}
+
+</style>

+ 177 - 0
src/views/main/modules/rx-breadcrumb.vue

@@ -0,0 +1,177 @@
+<template>
+  <div class="rxBreadcrumb">
+    <!-- <div class="item item-icon icon-w">
+        <a-icon type="left" @click="toLeft"/>
+    </div> -->
+    <div class="box">
+      <div class="box-in">
+        <template v-for="(item,index) in menuTabs">
+          <el-tag :closable ="item.name == 'home'?false:true" :class="['item',{active:item.name == routerName}]" @click="handlerClick(item)" @close="closeItem(item)">
+            <span class="title">{{item.title}}</span>
+            <!--                        <i class="icon el-icon-error" />-->
+          </el-tag>
+        </template>
+      </div>
+
+    </div>
+
+    <!-- <div class="item item-icon icon-w" id="right" @click="toRight">
+        <a-icon type="right" />
+    </div> -->
+  </div>
+</template>
+
+<script>
+import {mapGetters} from 'vuex'
+export default {
+  name: "rxBreadcrumb",
+  watch: {
+    routerName:{
+      handler(newValue, oldValue){
+        //处理视窗
+        this.$nextTick(target=>{
+          this.slide();
+        })
+      }
+    }
+  },
+  methods:{
+    closeItem(item){
+      console.log(item)
+      if(item.name == this.routerName){
+        this.$router.go(-1)
+      }
+      this.$store.dispatch('deleteMenu',item)
+    },
+    handlerClick(item){
+      this.$router_(item)
+      this.slide()
+    },
+    slide(){
+      let elmnt = this.$el.getElementsByClassName('active');
+      if(elmnt){
+        elmnt[0].scrollIntoView({behavior :'smooth'});
+      }
+    },
+
+  },
+  computed: {
+    ...mapGetters(['menuTabs']),
+    title() {
+      return this.$route.meta.title
+    },
+    routerName(){
+      return this.$route.name
+    }
+  },
+  mounted() {
+
+    this.slide()
+  }
+}
+</script>
+
+<style scoped lang="less">
+.rxBreadcrumb {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: 35px;
+  background-color: white;
+  border-top:1px solid #eeeeee;
+  border-bottom:1px solid #eeeeee;
+  position: relative;
+
+  .box{
+    /*border: 1px solid black;*/
+    width:100%;
+    // width:calc(100% - 64px);
+    overflow: auto;
+    height:35px;
+    line-height: 30px;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    right: 30px;
+    .item{
+      border-radius: 3px;
+      /*height: 25px;*/
+
+      display: inline-block;
+      position: relative;
+      cursor: pointer;
+      height: 26px;
+      line-height: 26px;
+      border: 1px solid #d8dce5;
+      color: #495060;
+      background: #fff;
+      padding: 0 8px;
+      font-size: 13px;
+      margin-left: 5px;
+      margin-top: 4px;
+      /deep/ .el-tag__close{
+        color: #000;
+        &:hover{
+          background: grey;
+          color:#ffffff;
+        }
+      }
+      &:first-of-type {
+        margin-left: 15px;
+      }
+      &:last-of-type {
+        margin-right: 15px;
+      }
+      &.active {
+        /deep/ .el-tag__close{
+          color: #fff;
+        }
+        background-color: #42b983;
+        color: #fff;
+        border-color: #42b983;
+        &::before {
+          content: '';
+          background: #fff;
+          display: inline-block;
+          width: 9px;
+          height: 9px;
+          border-radius: 50%;
+          position: relative;
+          margin-right: 4px;
+        }
+      }
+
+    }
+    .box-in{
+      display: flex;
+      align-items: center;
+      position: absolute;
+    }
+  }
+  .box::-webkit-scrollbar {/*滚动条整体样式*/
+    height: 0px;
+  }
+  .box:hover::-webkit-scrollbar {/*滚动条整体样式*/
+    height: 4px;
+  }
+  #right{
+    position: absolute;
+    top:3px;
+    right: 0px;
+  }
+
+  .item-icon{
+    padding: 0 3px;
+  }
+  .active{
+    color: #1890ff;
+    background: #e6f7ff;
+    border-color: #91d5ff;
+  }
+
+  .rxBreadcrumb-item{
+    margin-left: 20px;
+  }
+}
+
+</style>

+ 128 - 0
src/views/main/modules/rx-header.vue

@@ -0,0 +1,128 @@
+<template>
+  <div class="rxHeader">
+    <div class="icon-box" @click="CHANGECOLL">
+      <i class="el-icon-s-fold" style="width: 25px;height: 25px;font-size: 25px;" v-if="!leftMenuCollapsed"/>
+      <i class="el-icon-s-unfold" style="width: 25px;height: 25px;font-size: 25px;" v-if="leftMenuCollapsed"/>
+    </div>
+    <!--        <Breadcrumb></Breadcrumb>-->
+    <div class="avator-box">
+
+      <i class="el-icon-bell" style="width: 25px;height: 25px;font-size: 25px;margin-right: 20px;"/>
+    </div>
+  </div>
+</template>
+
+<script>
+import {mapGetters, mapMutations} from 'vuex'
+import {exitFullscreen, launchFullscreen} from '@/util/index'
+
+
+export default {
+  name: "rxHeader",
+
+  methods: {
+
+    getUserInfo(){
+      this.$api.getUserInfo().then(res=>{
+        this.$store.dispatch('setUserInfo',res.data.sysUser)
+      })
+    },
+    changeShop(){
+
+    },
+    getAuthShop(){
+      this.$api.getAuthShop().then(res=>{
+        this.$nextTick(()=>{
+          this.$refs.userBox.users = res.data
+        })
+      })
+    },
+    ...mapMutations(['CHANGECOLL']),
+    handleCommand(command) {
+      if (command == 'logOut') {
+        document.cookie = `sh-token=`
+        this.$router.push({name: "login"})
+      }
+      if (command == 'changeShop') {
+        this.dialogVisible = true
+        this.getAuthShop()
+      }
+    },
+  },
+  mounted() {
+    this.getUserInfo()
+  },
+  computed: {
+    ...mapGetters(['leftMenuCollapsed','userInfo']),
+
+  },
+  watch: {
+    isFull(v) {
+      if (v) {
+        launchFullscreen(document.documentElement)
+      } else {
+        exitFullscreen();
+      }
+    },
+  },
+  data() {
+    return {
+      isFull: false,
+      dialogVisible: false,
+      employeeName: '',
+      employeeId: '',
+      url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
+    }
+  },
+}
+</script>
+
+<style scoped lang="less">
+.rxHeader {
+  padding: 0 20px;
+  box-sizing: border-box;
+  height: 61px;
+  background-color: white;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  /*border-bottom: 1px solid #e6e6e6;*/
+
+  .screen-icon-box {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .icon-box {
+    /*margin-left: 20px;*/
+    font-size: 20px;
+    cursor: pointer;
+
+    &:hover {
+      color: #001529;
+    }
+  }
+
+  .avator-box {
+    display: flex;
+    align-items: center;
+    flex-direction: row;
+
+    .username {
+      margin-left: 15px;
+      font-size: 16px;
+    }
+
+
+    .avatar {
+      margin-right: 20px;
+    }
+  }
+
+  .el-dropdown-link {
+    display: flex;
+    align-items: center;
+  }
+}
+</style>

+ 246 - 0
src/views/vote/index.vue

@@ -0,0 +1,246 @@
+<template>
+  <div class="container">
+
+    <div class="card">
+
+      <searchForm :keys="['key','optime']" ref="searchForm"
+                  @on-search="handleSearch">
+        <el-button type="primary" icon="el-icon-plus" plain size="small" >新增</el-button>
+      </searchForm>
+
+      <RxTable
+          ref="rxTable"
+          @sizeChange="changePageSize"
+          @pageChange="changePage"
+
+          :currentPage="pageInfo.page"
+          :pageSize="pageInfo.pageSize"
+          :page-count="listData.pageQuantity"
+          :data="listData.list"
+          :blank-col="true"
+          :loading="loading"
+      >
+        <el-table-column
+            :align="item.align || 'left'"
+            :prop="item.prop"
+
+            :label="item.label"
+            :key="item.id"
+            v-for="(item) in tableHeader"
+            :show-overflow-tooltip="true"
+            :width="item.width"
+            :min-width="item.minWidth"
+        >
+          <template slot-scope="scope">
+            <div v-if="item.prop == 'flag'" style="height: 36px;">
+              <el-image fit="fill" :src="scope.row[item.prop]" style="height: 36px;width: 80px;"></el-image>
+            </div>
+
+            <div v-else-if="item.prop == 'tvCounts'">
+              <el-tag size="small" style="width: 50px;text-align: center;" @click="goTvList(scope.row)">{{scope.row[item.prop]}}</el-tag>
+            </div>
+            <span v-else>{{ scope.row[item.prop] || scope.row[item.prop] == 0 ? scope.row[item.prop] : '-' }} </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" align="left" width="200">
+          <template slot-scope="scope">
+                <el-button type="text" icon="el-icon-search" @click="handleInfo(scope.row)">查看</el-button>
+                <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑 </el-button>
+            <!--            <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">绑定微信-->
+            <!--            </el-button>-->
+            <el-button class="btn_text_danger" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </RxTable>
+    </div>
+    <!--    <el-dialog-->
+    <!--        title="添加用户"-->
+    <!--        :visible.sync="dialogVisible"-->
+    <!--        width="600px"-->
+    <!--        :lock-scroll="false">-->
+    <!--      <userForm ref="userForm"></userForm>-->
+    <!--      <span slot="footer" class="dialog-footer">-->
+    <!--                <el-button @click="dialogVisible = false">取 消</el-button>-->
+    <!--                <el-button type="primary" @click="onSubmit">确 定</el-button>-->
+    <!--            </span>-->
+    <!--    </el-dialog>-->
+    <!--    <el-dialog-->
+    <!--        title="编辑角色"-->
+    <!--        :visible.sync="dialogVisibleUpdate"-->
+    <!--        width="600px"-->
+    <!--        :lock-scroll="false">-->
+    <!--      <userForm ref="userFormu"></userForm>-->
+    <!--      <span slot="footer" class="dialog-footer">-->
+    <!--                <el-button @click="dialogVisibleUpdate = false">取 消</el-button>-->
+    <!--                <el-button type="primary" @click="onSubmit">确 定</el-button>-->
+    <!--            </span>-->
+    <!--    </el-dialog>-->
+  </div>
+</template>
+
+<script>
+import searchForm from "@/components/searchForm";
+import RxTable from "@/components/RxTable";
+
+
+export default {
+  name: "index",
+  components: {
+    searchForm,
+
+    RxTable,
+  },
+  data() {
+    return {
+
+      searchParams: {},
+      loading: false,
+      dialogVisible: false,
+      tableHeader: [
+        {
+          prop: "title",
+          label: "活动主题",
+          width: "300"
+        },
+        {
+          prop: "content",
+          label: "详情",
+          width: "300"
+        },
+        {
+          prop: "launchTime",
+          label: "开始时间",
+          width: "120"
+        },
+        {
+          prop: "deadline",
+          label: "截止时间",
+          width: "120"
+        },
+
+      ],
+      listData: {
+        list: [],
+        pageQuantity:0,
+      },
+      pageInfo: {
+        pageNum: 1,
+        pageCapacity: 10,
+      },
+      addVisible: false,
+      dialogVisibleUpdate: false,
+    }
+  },
+  methods: {
+    handleInfo(row){
+      console.log(row)
+      this.$router_({
+        name:"voteActivityInfo",
+        title:"活动详情",
+        canClose:true,
+        params:{
+            ...row
+        }
+      })
+        // this.$router.push({
+        //   name:"voteActivityInfo",
+        //   params:{
+        //     activityId:row.activityId
+        //   }
+        // })
+    },
+    handleEdit(row) {
+
+    },
+
+    handleDelete(row){
+      this.$confirm('确认刪除, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+
+      })
+
+    },
+
+    onSubmit() {
+      // let form = this.$refs.userForm.getParams();
+      // this.$api.addUser(form).then(res => {
+      //   this.$message.success("新增成功!");
+      //   this.dialogVisible = false;
+      //   this.handleSearch();
+      // })
+    },
+
+    goDeleteSelect() {
+
+    },
+    //搜索
+    handleSearch() {
+      this.pageInfo.pageNum = 1
+
+      let optime = this.$refs.searchForm.time1;
+
+      let startTime,
+          key,
+          endTime;
+      key = this.$refs.searchForm.form.key;
+
+      if(optime.length){
+        startTime = optime[0];
+        endTime = optime[1];
+      }
+
+      this.searchParams = {
+        startTime,
+        endTime,
+        key,
+        ...this.pageInfo
+      }
+
+
+      this.voteList()
+    },
+
+    //国家list
+    voteList(params) {
+      this.loading = true;
+
+      this.$api.voteList(this.searchParams).then(res => {
+        this.loading = false;
+        console.log(res)
+        this.listData.list = res.content.voteActivityViewDtos;
+
+        this.listData.pageQuantity = res.content.pageQuantity;
+
+      })
+    },
+
+
+    changePageSize() {
+
+    },
+    changePage(page) {
+
+      this.pageInfo.pageNum = page
+      this.voteList()
+    },
+
+  },
+  mounted() {
+    this.handleSearch()
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.container {
+  /*position: relative;*/
+
+  .btnBox {
+    margin-left: 20px;
+  }
+}
+</style>

+ 221 - 0
src/views/vote/vote.vue

@@ -0,0 +1,221 @@
+<template>
+  <div class="container">
+
+    <div class="card">
+
+      <searchForm :keys="['activity']" ref="searchForm"
+                  @on-search="handleSearch">
+        <el-button type="primary" icon="el-icon-plus" plain size="small" >新增</el-button>
+      </searchForm>
+
+      <RxTable
+          ref="rxTable"
+          @sizeChange="changePageSize"
+          @pageChange="changePage"
+          :total="listData.total"
+          :currentPage="pageInfo.page"
+          :pageSize="pageInfo.pageSize"
+          :data="listData.list"
+          :blank-col="true"
+          :loading="loading"
+      >
+        <el-table-column
+            :align="item.align || 'left'"
+            :prop="item.prop"
+
+            :label="item.label"
+            :key="item.id"
+            v-for="(item) in tableHeader"
+            :show-overflow-tooltip="true"
+            :width="item.width"
+            :min-width="item.minWidth"
+        >
+          <template slot-scope="scope">
+            <div v-if="item.prop == 'flag'" style="height: 36px;">
+              <el-image fit="fill" :src="scope.row[item.prop]" style="height: 36px;width: 80px;"></el-image>
+            </div>
+
+            <div v-else-if="item.prop == 'tvCounts'">
+              <el-tag size="small" style="width: 50px;text-align: center;" @click="goTvList(scope.row)">{{scope.row[item.prop]}}</el-tag>
+            </div>
+            <span v-else>{{ scope.row[item.prop] || scope.row[item.prop] == 0 ? scope.row[item.prop] : '-' }} </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" align="left" width="200">
+          <template slot-scope="scope">
+            <!--            <el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.row)">查看</el-button>-->
+            <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑
+            </el-button>
+            <!--            <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">绑定微信-->
+            <!--            </el-button>-->
+            <el-button class="btn_text_danger" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </RxTable>
+    </div>
+    <!--    <el-dialog-->
+    <!--        title="添加用户"-->
+    <!--        :visible.sync="dialogVisible"-->
+    <!--        width="600px"-->
+    <!--        :lock-scroll="false">-->
+    <!--      <userForm ref="userForm"></userForm>-->
+    <!--      <span slot="footer" class="dialog-footer">-->
+    <!--                <el-button @click="dialogVisible = false">取 消</el-button>-->
+    <!--                <el-button type="primary" @click="onSubmit">确 定</el-button>-->
+    <!--            </span>-->
+    <!--    </el-dialog>-->
+    <!--    <el-dialog-->
+    <!--        title="编辑角色"-->
+    <!--        :visible.sync="dialogVisibleUpdate"-->
+    <!--        width="600px"-->
+    <!--        :lock-scroll="false">-->
+    <!--      <userForm ref="userFormu"></userForm>-->
+    <!--      <span slot="footer" class="dialog-footer">-->
+    <!--                <el-button @click="dialogVisibleUpdate = false">取 消</el-button>-->
+    <!--                <el-button type="primary" @click="onSubmit">确 定</el-button>-->
+    <!--            </span>-->
+    <!--    </el-dialog>-->
+  </div>
+</template>
+
+<script>
+import searchForm from "@/components/searchForm";
+import RxTable from "@/components/RxTable";
+
+
+export default {
+  name: "vote",
+  components: {
+    searchForm,
+
+    RxTable,
+  },
+  data() {
+    return {
+
+      searchParams: {},
+      loading: false,
+      dialogVisible: false,
+      tableHeader: [
+        {
+          prop: "title",
+          label: "活动主题",
+          width: "300"
+        },
+        {
+          prop: "content",
+          label: "详情",
+          width: "300"
+        },
+        {
+          prop: "launchTime",
+          label: "开始时间",
+          width: "120"
+        },
+        {
+          prop: "deadline",
+          label: "截止时间",
+          width: "120"
+        },
+
+      ],
+      listData: {
+        list: [],
+        total: 0,
+      },
+      pageInfo: {
+        pageNum: 1,
+        pageCapacity: 10,
+      },
+      addVisible: false,
+      dialogVisibleUpdate: false,
+    }
+  },
+  methods: {
+    handleEdit(row) {
+
+    },
+    goTvList(item){
+      console.log(item)
+    },
+    handleDelete(row){
+      this.$confirm('确认刪除, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+
+      })
+
+    },
+
+    onSubmit() {
+      // let form = this.$refs.userForm.getParams();
+      // this.$api.addUser(form).then(res => {
+      //   this.$message.success("新增成功!");
+      //   this.dialogVisible = false;
+      //   this.handleSearch();
+      // })
+    },
+
+    goDeleteSelect() {
+
+    },
+    //搜索
+    handleSearch() {
+      this.pageInfo.pageNum = 1
+
+      let activityId = this.$refs.searchForm.form.activityId;
+
+      this.searchParams = {
+          activityId,
+      }
+
+
+      this.votes()
+    },
+
+
+    votes() {
+      this.loading = true;
+
+
+      this.$api.votes(this.searchParams).then(res => {
+        this.loading = false;
+        this.listData.list = res.content;
+      })
+
+    },
+
+
+
+    changePageSize() {
+
+    },
+    changePage(page) {
+      this.pageInfo.page = page
+      this.countryList();
+
+    },
+
+
+    addPay() {
+      this.addVisible = true;
+    }
+  },
+  mounted() {
+    // this.handleSearch()
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.container {
+  /*position: relative;*/
+
+  .btnBox {
+    margin-left: 20px;
+  }
+}
+</style>

+ 156 - 0
src/views/vote/voteActivityInfo.vue

@@ -0,0 +1,156 @@
+<template>
+    <div class="container">
+      <div class="card">
+
+        <div class="form">
+          <div class="form-item">
+            <div class="label">活动主题:</div>
+            <div class="text"> {{form.title}}</div>
+          </div>
+          <div class="form-item" style="margin-bottom: 100px;">
+            <div class="label">活动详情:</div>
+            <div class="text"> {{form.content}}</div>
+          </div>
+          <div class="form-item">
+            <div class="label">活动时间:</div>
+            <div class="text">{{form.launchTime}} - {{form.deadline}}</div>
+          </div>
+        </div>
+
+
+
+        <RxTable
+            ref="rxTable"
+
+
+            :data="listData.list"
+            :blank-col="true"
+            :loading="loading"
+            :showPagination="false"
+        >
+          <el-table-column
+              :align="item.align || 'left'"
+              :prop="item.prop"
+
+              :label="item.label"
+              :key="item.id"
+              v-for="(item) in tableHeader"
+              :show-overflow-tooltip="true"
+              :width="item.width"
+              :min-width="item.minWidth"
+          >
+            <template slot-scope="scope">
+              <div v-if="item.prop == 'flag'" style="height: 36px;">
+                <el-image fit="fill" :src="scope.row[item.prop]" style="height: 36px;width: 80px;"></el-image>
+              </div>
+
+              <div v-else-if="item.prop == 'tvCounts'">
+                <el-tag size="small" style="width: 50px;text-align: center;" @click="goTvList(scope.row)">{{scope.row[item.prop]}}</el-tag>
+              </div>
+              <span v-else>{{ scope.row[item.prop] || scope.row[item.prop] == 0 ? scope.row[item.prop] : '-' }} </span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" fixed="right" align="left" width="200">
+            <template slot-scope="scope">
+              <!--            <el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.row)">查看</el-button>-->
+              <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑
+              </el-button>
+              <!--            <el-button type="text" class="btn_text_edit" icon="el-icon-edit" @click="handleEdit(scope.row)">绑定微信-->
+              <!--            </el-button>-->
+              <el-button class="btn_text_danger" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除
+              </el-button>
+            </template>
+          </el-table-column>
+        </RxTable>
+      </div>
+    </div>
+</template>
+
+<script>
+import RxTable from "@/components/RxTable";
+export default {
+  name: "voteActivityInfo",
+  components:{
+    RxTable
+  },
+  data(){
+    return {
+
+      searchParams: {},
+      loading: false,
+      dialogVisible: false,
+      tableHeader: [
+        {
+          prop: "title",
+          label: "活动主题",
+          width: "300"
+        },
+        {
+          prop: "content",
+          label: "详情",
+          width: "300"
+        },
+        {
+          prop: "launchTime",
+          label: "开始时间",
+          width: "120"
+        },
+        {
+          prop: "deadline",
+          label: "截止时间",
+          width: "120"
+        },
+
+      ],
+      listData: {
+        list: [],
+        total: 0,
+      },
+
+        form:{}
+    }
+  },
+  methods:{
+
+
+  },
+  mounted() {
+
+     this.form = this.$route.params;
+      let activityId = this.$route.params.id.toString();
+
+
+      this.$api.votes({
+        activityId,
+      }).then(res => {
+        console.log(res)
+        this.loading = false;
+        this.listData.list = res.content;
+      })
+  }
+}
+</script>
+
+<style lang="less" scoped>
+    .form{
+      //display: flex;
+      height: 200px;
+      .form-item{
+          //border:1px solid red;
+        font-size: 14px;
+
+        .label{
+          //border:1px solid red;
+          text-align: right;
+          height: 30px;
+          line-height: 30px;
+        }
+        .text{
+          //border:1px solid black;
+          height: 30px;
+          line-height: 30px;
+        }
+
+      }
+    }
+</style>

+ 20 - 0
vue.config.js

@@ -0,0 +1,20 @@
+module.exports = {
+    devServer: {
+        proxy: {
+            '/api': {
+                // target: 'http://wuqing3.tpddns.cn:8084/apiXZTVEtYzDDHEKOP6Zx',//生产
+                target: 'http://121.5.58.50:8080/',//生产
+                changeOrigin: true,
+                ws: true,
+                pathRewrite: {
+                    '^/api': ''
+                },
+                headers: {
+
+                }
+            },
+
+        }
+    },
+    lintOnSave: false
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است