2637309949@qq.com před 5 roky
rodič
revize
c4e0e084d9

+ 1 - 1
package.json

@@ -5,7 +5,7 @@
   "author": "Double <2637309949@qq.com>",
   "license": "MIT",
   "scripts": {
-    "dev": "vue-cli-service serve --port 8081",
+    "dev": "vue-cli-service serve",
     "build:prod": "vue-cli-service build",
     "build:stage": "vue-cli-service build --mode staging",
     "preview": "node build/index.js --preview",

+ 301 - 0
src/components/Sheet/index.vue

@@ -0,0 +1,301 @@
+<template>
+  <div>
+    <el-table
+      ref="multipleTable"
+      v-loading="dataLoading"
+      stripe
+      border
+      :header-cell-style="{background:'#F5F7FA'}"
+      :data="tableData"
+      :selection-data="selectionData"
+      :height="tableMaxHeight"
+      @selection-change="selectionChange"
+      @row-click="rowdata"
+      @cell-mouse-enter="cellMouseEnter"
+    >
+      <el-table-column fixed :type="selectType" align="center" width="50" />
+
+      <el-table-column v-if="showCover" label="Cover" :align="columns[0].align">
+        <template slot-scope="scope">
+          <img :src="src+scope.row.cover" width="40" height="40">
+        </template>
+      </el-table-column>
+
+      <el-table-column v-if="showFace" label="Cover" :align="columns[0].align">
+        <template slot-scope="scope">
+          <img :src="src+scope.row.face" width="40" height="40">
+        </template>
+      </el-table-column>
+
+      <template v-for="(column, index) in columns">
+        <el-table-column
+          :key="column.label"
+          :prop="column.prop"
+          :label="column.label"
+          :align="column.align"
+          :min-width="column.minWidth"
+          :max-width="column.maxWidth"
+        >
+          <template slot-scope="scope">
+            <template v-if="!column.render">
+              <template v-if="typeof column.formatter == 'string'">
+                <!--<span v-html="column.formatter(scope.row, column)" />-->
+                <span v-html="formatter(scope.row, column)" />
+              </template>
+              <template v-else-if="typeof column.formatter == 'function'">
+                <span v-html="column.formatter(scope.row, column)" />
+              </template>
+              <template v-else>
+                <span>{{ scope.row[column.prop] }}</span>
+              </template>
+            </template>
+            <template v-else>
+              <expand-dom :column="column" :row="scope.row" :render="column.render" :index="index" />
+            </template>
+          </template>
+        </el-table-column>
+      </template>
+
+      <el-table-column
+        v-if="operates.list.length > 0 && (operates.list.filter(_x=>_x.show === true).length > 0 || operates.dropdown.filter(_x=>_x.show === true).length > 0) "
+        ref="fixedColumn"
+        label="Operate"
+        align="center"
+        :width="operates.width"
+        :fixed="operates.fixed"
+      >
+        <template slot-scope="scope">
+          <div class="operate-group">
+            <template v-for="btn in operates.list">
+              <el-button
+                v-if="btn.show"
+                :key="btn.id"
+                :type="btn.type"
+                size="mini"
+                :icon="btn.icon"
+                :disabled="btn.disabled"
+                :plain="btn.plain"
+                @click.native.prevent="btn.method(scope.row)"
+              >{{ btn.label }}</el-button>
+            </template>
+            <div
+              v-if="operates.dropdown != undefined && operates.dropdown.filter(_x=>_x.show === true).length > 0"
+              style="display: inline-block;"
+            >
+              <el-dropdown style="font-size: 12px" size="mini">
+                <el-button type="primary" size="mini">More</el-button>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item
+                    v-for="dropbtn in operates.dropdown"
+                    :key="dropbtn.id"
+                    @click.native.prevent="dropbtn.method(scope.row)"
+                  >{{ dropbtn.label }}</el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <!--endregion-->
+    </el-table>
+    <div class="clearfix" style="height: 32px;margin: 10px 24px 0 10px;">
+      <el-pagination
+        class="pagination-site"
+        background
+        :class="floatType"
+        :current-page="dataQuery.page"
+        :page-sizes="page.pageSizes"
+        :page-size="dataQuery.rows"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="page.dataTotal"
+        @size-change="sizeChange"
+        @current-change="currentChange"
+      />
+    </div>
+  </div>
+</template>
+<script>
+import { scrollTo } from '@/utils/scroll-to'
+export default {
+  name: 'Sheet',
+  props: {
+    showFace: {
+      type: Boolean,
+      default: false
+    },
+    showCover: {
+      type: Boolean,
+      default: false
+    },
+    headerName: {
+      type: String,
+      default: () => 'searchForm'
+    },
+    callBack: {
+      type: Function,
+      default: () => {}
+    },
+    api: {
+      type: Function,
+      default: () => {}
+    },
+    columns: {
+      type: Array,
+      default: () => []
+    },
+    floatType: {
+      type: String,
+      default: () => ''
+    },
+    clearSelection: {
+      type: String,
+      default: () => ''
+    },
+    selectType: {
+      type: String,
+      default: () => ''
+    },
+    dataQuery: {
+      type: Object,
+      default: () => {
+        return { page: 1, rows: 10 }
+      }
+    },
+    page: {
+      type: Object,
+      default: () => {
+        return {
+          pageSizes: [5, 10, 20, 50, 100],
+          dataTotal: 0
+        }
+      }
+    },
+    operates: {
+      type: Object,
+      default: () => {
+        return {
+          list: [],
+          width: 150
+        }
+      }
+    }
+  },
+  data: function() {
+    return {
+      src: window.Domain.static_url,
+      tableData: [],
+      dataLoading: false,
+      optionsets: {},
+      selectionData: []
+    }
+  },
+  computed: {
+    tableMaxHeight() {
+      if (
+        this.$store === undefined ||
+        this.$store.getters.pageTableHeaderHeight === 0
+      ) { return null }
+      const height = 240 + 20 + this.$store.getters.pageTableHeaderHeight
+      return 'calc(100vh - ' + height + 'px)'
+    }
+  },
+  watch: {
+    clearSelection(newdata, olddata) {
+      this.$refs.multipleTable.clearSelection()
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+    search(obj) {
+      this.dataQuery.page = 1
+      Object.assign(this.dataQuery, obj)
+      this.getData()
+    },
+    cellMouseEnter(row, column) {
+      this.$emit('cell-mouse-enter', row, column)
+    },
+    formatter(row, column) {
+      if (this.optionsets[column.formatter] !== undefined) {
+        let colValue
+        typeof row[column.prop] === 'number'
+          ? (colValue = row[column.prop].toString())
+          : (colValue = row[column.prop])
+        for (var i = 0; i < this.optionsets[column.formatter].length; i++) {
+          if (this.optionsets[column.formatter][i].value === colValue) {
+            return this.optionsets[column.formatter][i].text
+          }
+        }
+      }
+      return ''
+    },
+    rowdata(row, column, event) {
+      if (column.label !== '操作') {
+        this.$refs.multipleTable.toggleRowSelection(row)
+        this.$emit('getRow', row)
+      }
+    },
+    toggleRowSelection(row) {
+      this.$refs.multipleTable.toggleRowSelection(row)
+    },
+    selectionChange(selection) {
+      this.selectionData = selection
+      this.$emit('update:selectionData', selection)
+    },
+    async getData() {
+      const codes = []
+      this.columns.forEach(item => {
+        if (
+          item.formatter !== undefined &&
+          typeof item.formatter === 'string'
+        ) {
+          codes.push(item.formatter)
+        }
+      })
+      for (var i = 0; i < codes.length; i++) {
+        this.optionsets[codes[i]] = await this.$store.dispatch(
+          'optionset/formatterData',
+          codes[i]
+        )
+      }
+      this.dataLoading = true
+      this.dataQuery
+      this.api(this.dataQuery).then(res => {
+        if (res.code === 200) {
+          this.page.dataTotal = res.data.totalrecords
+          this.tableData = res.data.data
+          this.$emit('callBack', res.data.content)
+        } else {
+          this.page.dataTotal = 0
+          this.tableData = []
+        }
+        this.dataLoading = false
+      })
+    },
+    sizeChange(val) {
+      scrollTo(0, 600)
+      this.dataQuery.rows = val
+      this.dataQuery.page = 1
+      this.getData()
+    },
+    currentChange(val) {
+      scrollTo(0, 600)
+      this.dataQuery.page = val
+      this.getData()
+    }
+  }
+}
+</script>
+<style>
+.right {
+  float: right;
+}
+.left {
+  float: left;
+}
+.el-table--border th.gutter:last-of-type {
+    display: block !important;
+    width: 17px !important;
+}
+</style>

+ 94 - 0
src/components/Tree/index.vue

@@ -0,0 +1,94 @@
+<template>
+  <div>
+    <el-container>
+      <el-header class="el-button--primary" height="40px" style="line-height: 40px;">
+        {{ title }}
+      </el-header>
+      <el-main class="tree-main" style="padding: 15px 0;">
+        <el-tree
+          :data="treeData"
+          node-key="id"
+          :props="defaultProps"
+          :default-expanded-keys="defaultExpends"
+          :style="styleObject"
+          :highlight-current="false"
+          @node-click="click"
+        />
+      </el-main>
+    </el-container>
+  </div>
+</template>
+<script>
+import { addNodesAttr } from '@/utils/tree'
+export default {
+  name: 'Tree',
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    api: {
+      type: Function,
+      default: () => {}
+    },
+    dataQuery: {
+      type: Object,
+      default: () => {}
+    },
+    callBack: {
+      type: Function,
+      default: () => {}
+    },
+    click: {
+      type: Function,
+      default: () => {}
+    },
+    isShowAll: {
+      type: Boolean,
+      default: () => true
+    },
+    styleObject: {
+      type: Object,
+      default: () => {}
+    },
+    defaultExpends: {
+      type: Array,
+      default: () => []
+    },
+    props: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  data: function() {
+    return {
+      treeData: [],
+      defaultProps: {
+        children: 'nodes',
+        label: 'name',
+        value: 'id'
+      }
+    }
+  },
+  created() {
+    if (typeof this.props === 'object') {
+      Object.assign(this.defaultProps, this.props)
+    }
+    this.api(this.dataQuery).then(res => {
+      this.treeData = addNodesAttr(res.data || [], 'opened', false) || []
+      if (this.isShowAll) {
+        this.treeData.unshift({ id: '', nodes: null, opened: false, parent: '', tag: { id: '' }, text: 'All' })
+      }
+      this.callBack(res.data)
+    })
+  }
+}
+</script>
+<style>
+  .el-tree-node > .el-tree-node__children {
+    overflow-x: auto
+  }
+  .tree-aside .tree-main{
+    max-height: calc(100vh - 240px);
+  }
+</style>

+ 1 - 0
src/icons/svg/application.svg

@@ -0,0 +1 @@
+<svg t="1586659373960" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15083" width="128" height="128"><path d="M898.048 178.176c-77.824-40.96-178.176-93.184-257.024-135.168C533.504-15.36 489.472-12.288 387.072 40.96c-77.824 40.96-178.176 93.184-257.024 135.168C12.288 247.808 0 262.144 0 378.88v269.312C0 768 21.504 790.528 125.952 846.848c78.848 40.96 179.2 94.208 258.048 135.168 104.448 55.296 155.648 55.296 254.976 0 77.824-40.96 177.152-93.184 256-134.144 92.16-54.272 129.024-99.328 129.024-195.584V380.928c0-105.472-27.648-158.72-125.952-202.752z m-57.344 280.576l-264.192 102.4v286.72c0 36.864-28.672 67.584-63.488 67.584-35.84 0-63.488-29.696-63.488-67.584v-286.72l-264.192-102.4c-40.96-16.384-65.536-60.416-54.272-99.328 11.264-38.912 53.248-58.368 94.208-41.984l286.72 110.592 288.768-111.616c40.96-16.384 82.944 3.072 94.208 41.984 10.24 38.912-13.312 83.968-54.272 100.352z" p-id="15084"></path></svg>

+ 2 - 0
src/store/getters.js

@@ -1,6 +1,8 @@
 const getters = {
   sidebar: state => state.app.sidebar,
   size: state => state.app.size,
+  pageTableHeaderHeight: state => state.app.pageTableHeaderHeight,
+  treeWidth: state => state.app.treeWidth,
   device: state => state.app.device,
   visitedViews: state => state.tagsView.visitedViews,
   cachedViews: state => state.tagsView.cachedViews,

+ 10 - 1
src/store/modules/app.js

@@ -6,10 +6,16 @@ const state = {
     withoutAnimation: false
   },
   device: 'desktop',
-  size: Cookies.get('size') || 'medium'
+  size: Cookies.get('size') || 'medium',
+  treeWidth: '18%',
+  minMainHeight: window.screen.height - 465,
+  pageTableHeaderHeight: 0
 }
 
 const mutations = {
+  PAGE_TABLE_HEADER_HEIGHT: (state, height) => {
+    state.pageTableHeaderHeight = height
+  },
   TOGGLE_SIDEBAR: state => {
     state.sidebar.opened = !state.sidebar.opened
     state.sidebar.withoutAnimation = false
@@ -34,6 +40,9 @@ const mutations = {
 }
 
 const actions = {
+  pageTableHeaderHeight({ commit }, { height }) {
+    commit('PAGE_TABLE_HEADER_HEIGHT', height)
+  },
   toggleSideBar({ commit }) {
     commit('TOGGLE_SIDEBAR')
   },

+ 1 - 1
src/store/modules/permission.js

@@ -51,7 +51,7 @@ const mutations = {
 function buildRouters(accessedRoutes) {
   return (accessedRoutes || []).map(item => {
     const routerItem = {}
-    routerItem.name = item.tag.code
+    routerItem.name = !item.parent && !item.nodes ? null : item.tag.code
     routerItem.path = !item.parent && !item.nodes ? path.join('/', item.tag.url) : path.join(!item.parent ? '/' : '', item.tag.url)
     routerItem.component = !item.parent ? Layout : () => import(`@/views/${item.tag.component}`)
     routerItem.meta = { 'title': item.tag.name, 'icon': item.tag.icon, 'activeMenu': item.tag.active_menu }

+ 9 - 0
src/utils/tree.js

@@ -0,0 +1,9 @@
+export function addNodesAttr(nodes, attr, val) {
+  nodes.forEach((item, i) => {
+    nodes[i][attr] = val
+    if (nodes[i].nodes !== null && nodes[i].nodes.length > 0) {
+      addNodesAttr(nodes[i].nodes, attr, val)
+    }
+  })
+  return nodes
+}

+ 223 - 0
src/views/menu/index.vue

@@ -0,0 +1,223 @@
+<template>
+  <el-container>
+    <el-main class="page-main">
+      <el-card>
+        <el-container>
+          <el-aside :width="treeWidth">
+            <tree
+              title="Menu"
+              :api="this.$api.sysMenu.tree"
+              :data-query="treeQuery"
+              :call-back="getTreeDataCallBack"
+              :click="treeClick"
+            />
+          </el-aside>
+          <el-container>
+            <el-header height="120">
+              <el-form ref="searchForm" :size="size" label-position="left" label-width="80px">
+                <el-row :gutter="20">
+                  <el-col :span="6">
+                    <el-form-item label="Name:" class="notice-input" label-width="60px">
+                      <el-input
+                        v-model="dataQuery.name"
+                        placeholder="Please input name"
+                        clearable
+                        @keyup.enter.native="search"
+                      />
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="6">
+                    <el-form-item label="Code:" class="notice-input" label-width="60px">
+                      <el-input
+                        v-model="dataQuery.code"
+                        placeholder="Please input code"
+                        clearable
+                        @keyup.enter.native="search"
+                      />
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="12" style="text-align: right">
+                    <el-form-item>
+                      <el-button
+                        type="primary"
+                        icon="el-icon-search"
+                        :size="size"
+                        @click="search"
+                      >Search</el-button>
+                      <el-button icon="el-icon-refresh" :size="size" @click="resetFields">Reset</el-button>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-button type="primary" icon="el-icon-plus" :size="size" @click="create">New</el-button>
+                  <el-button :size="size" @click="deleteBatch">BatchDelete</el-button>
+                </el-row>
+              </el-form>
+            </el-header>
+            <el-main class="table-main">
+              <sheet
+                ref="qtable"
+                :api="this.$api.sysMenu.page"
+                :columns="tableColumns"
+                :data-query="dataQuery"
+                :operates="operates"
+                :float-type="'right'"
+                :select-type="'selection'"
+              />
+            </el-main>
+          </el-container>
+        </el-container>
+      </el-card>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import Tree from '@/components/Tree'
+import Sheet from '@/components/Sheet'
+
+export default {
+  name: 'Menu',
+  components: { Tree, Sheet },
+  data() {
+    return {
+      tableColumns: [
+        {
+          prop: 'name',
+          label: 'Name',
+          align: 'center',
+          minWidth: 150,
+          maxWidth: 220
+        },
+        {
+          prop: 'url',
+          label: 'Url',
+          align: 'center',
+          minWidth: 150,
+          maxWidth: 220
+        },
+        {
+          prop: 'component',
+          label: 'Component',
+          align: 'center',
+          minWidth: 180,
+          maxWidth: 220
+        },
+        {
+          prop: 'code',
+          label: 'Code',
+          align: 'center',
+          minWidth: 150,
+          maxWidth: 180
+        },
+        {
+          prop: 'order_num',
+          label: 'Order',
+          align: 'center',
+          minWidth: 150,
+          maxWidth: 180
+        }
+      ],
+      operates: {
+        list: [
+          {
+            label: 'Edit',
+            show: true,
+            type: 'primary',
+            method: row => {
+              this.edit(row)
+            }
+          },
+          {
+            label: 'Delete',
+            show: true,
+            type: 'danger',
+            method: row => {
+              this.deleteData(row)
+            }
+          }
+        ],
+        width: 160,
+        fixed: 'right'
+      },
+      dataQuery: {
+        page: 1,
+        rows: 10,
+        name: '',
+        code: '',
+        cn_id: '',
+        type_op: '>=',
+        type: '0',
+        hidden: 0
+      },
+      treeQuery: { del_falg: 0, hidden: 0, type_op: '>=', type: 0 },
+      temp: {
+        id: undefined,
+        name: '',
+        code: '',
+        url: '',
+        component: '',
+        parent: this.parent,
+        inheritance: '',
+        icon: '',
+        order_num: 0,
+        perms: '',
+        type: '',
+        hidden: 0
+      },
+      rules: {
+        code: [{ required: true, message: 'Please input code', trigger: 'blur' }],
+        name: [{ required: true, message: 'Please input menu name', trigger: 'blur' }],
+        url: [{ required: true, message: 'Please input url', trigger: 'blur' }]
+      },
+      cascaderData: [],
+      defaultProps: {
+        children: 'nodes',
+        label: 'text',
+        value: 'id'
+      },
+      dataLoading: false,
+      dialogStatus: '',
+      dialogVisible: false,
+      menuTypes: [
+        { id: 0, name: 'Dir' },
+        { id: 1, name: 'Menu' },
+        { id: 2, name: 'Button' }
+      ]
+    }
+  },
+  computed: {
+    ...mapGetters([
+      'size',
+      'minMainHeight',
+      'treeWidth'
+    ])
+  },
+  mounted() {
+    var height = this.$refs.searchForm.$el.offsetHeight
+    this.$store.dispatch('app/pageTableHeaderHeight', { height: height })
+  },
+  destroyed() {
+    this.$store.dispatch('app/pageTableHeaderHeight', { height: 0 })
+  },
+  created() {},
+  methods: {
+    treeClick(node) {},
+    getTreeDataCallBack(tree) {},
+    handleChange(value) {},
+    createData() {},
+    updateData() {},
+    search() {},
+    resetFields() {},
+    deleteData(row) {},
+    deleteBatch() {},
+    dialogClose() {},
+    create() {},
+    edit(row) {}
+  }
+}
+</script>
+
+<style scoped lang="scss">
+</style>