double 5 роки тому
батько
коміт
e19e3dd117

+ 363 - 0
src/components/Query/index.vue

@@ -0,0 +1,363 @@
+<template>
+  <el-form ref="form" :form="form" :size="size" label-suffix=":" @submit.native.prevent>
+    <el-row :gutter="10">
+      <el-col v-for="item in formConfigs.formConfigA" :key="item.name" :span="item.span || 18">
+        <el-form-item :rules="item.rules" :label="item.label" :prop="item.name" :label-width="item.label ? '100px' : ''">
+          <el-input
+            v-if="item.type === 'input' || item.type === ''"
+            v-model="form[item.name]"
+            :style="item.style"
+            :disabled="item.disabled"
+            clearable
+            :prefix-icon="item.prefixIcon"
+            :suffix-icon="item.suffixIcon"
+            :show-word-limit="item.showWordLimit"
+            :type="item.inputType"
+            :rows="item.rows"
+            :autosize="item.autosize"
+            :placeholder="`请填写${item.placeholder}`"
+            @keyup.enter.native="onSubmit"
+          />
+          <qselect
+            v-else-if="item.type === 'select'"
+            :placeholder="`请选择${item.placeholder}`"
+            :code="item.code"
+            :remote-api="item.remoteApi"
+            :filters="item.filters"
+            :value.sync="form[item.name]"
+            :option-field="item.remoteApi?{ keyField: 'id', labelField: 'name', valueField: 'id' }:{ keyField: 'value', labelField: 'text', valueField: 'value' }"
+            :remote="!!item.remote"
+            :search-filed="item.searchFiled"
+            :remote-data-query="item.remoteDataQuery"
+          />
+          <qcascader
+            v-else-if="item.type === 'cascader'"
+            :api="item.api"
+            :data-query="item.dataquery"
+            :multiple="item.multiple"
+            :check-strictly="item.checkStrictly"
+            :value.sync="form[item.name]"
+          />
+          <el-date-picker
+            v-else-if="item.type === 'date'"
+            v-model="form[item.name]"
+            :type="item.dateType"
+            :style="item.style"
+            :align="item.align"
+            range-separator="至"
+            :start-placeholder="item.startPlaceholder"
+            :end-placeholder="item.endPlaceholder"
+            :disabled="item.disabled"
+            :unlink-panels="item.unlinkPanels || true"
+            :value-format="item.valueFormat"
+            :default-value="item.defaultValue"
+            :default-time="item.defaultTime"
+            :placeholder="`请选择${item.placeholder}`"
+            :picker-options="item.pickerOptions"
+          />
+          <el-time-select
+            v-else-if="item.type === 'time'"
+            v-model="form[item.name]"
+            :style="item.style"
+            :disabled="item.disabled"
+            :picker-options="item.pickerOptions?item.pickerOptions:{ start: '08:00', step: '00:05', end: '22:00' }"
+            :placeholder="`请选择${item.placeholder}`"
+          />
+          <div v-else-if="item.type === 'minmax'" class="minmax-box">
+            <el-input-number
+              v-model="form[item.name][0]"
+              :step="item.step || 1"
+              :min="item.min || 0"
+              :controls="false"
+              class="minmax-input"
+              :precision="item.precision"
+              :disabled="item.disabled"
+              :placeholder="`请输入${item.minPlaceholder}`"
+            />
+            <div class="division-line">-</div>
+            <el-input-number
+              v-model="form[item.name][1]"
+              :step="item.step || 1"
+              :min="item.min || 0"
+              class="minmax-input"
+              :controls="false"
+              :precision="item.precision"
+              :disabled="item.disabled"
+              :placeholder="`请输入${item.maxPlaceholder}`"
+            />
+          </div>
+        </el-form-item>
+      </el-col>
+      <el-col :offset="selfAdaption" :span="6" style="text-align: right;">
+        <el-button v-if="folding" :size="size" @click="show = !show">
+          {{ show ? "折叠" : "展开" }}
+        </el-button>
+        <el-button type="primary" :size="size" @click.prevent="onSubmit">查询</el-button>
+        <el-button :size="size" @click="resetForm()">重置</el-button>
+        <el-button :loading="isExporting" type="primary" :size="size" @click="onExport">{{ $t('common.export') }}</el-button>
+      </el-col>
+    </el-row>
+    <transition name="el-zoom-in-top">
+      <el-row v-show="show" :gutter="10">
+        <el-col v-for="item in formConfigs.formConfigB" :key="item.name" :span="item.span || 24">
+          <el-form-item :rules="item.rules" :label="item.label" :prop="item.name" :label-width="item.label ? '100px' : ''">
+            <el-input
+              v-if="(item.type || input) === 'input'"
+              v-model="form[item.name]"
+              :style="item.style"
+              :disabled="item.disabled"
+              :prefix-icon="item.prefixIcon"
+              :suffix-icon="item.suffixIcon"
+              :show-word-limit="item.showWordLimit"
+              :type="item.inputType"
+              :rows="item.rows"
+              :autosize="item.autosize"
+              :placeholder="`请填写${item.placeholder}`"
+              clearable
+              @keyup.enter.native="onSubmit"
+            />
+            <option-set
+              v-else-if="item.type === 'select'"
+              :placeholder="`请选择${item.placeholder}`"
+              :code="item.code"
+              :remote-api="item.remoteApi"
+              :filters="item.filters"
+              :value.sync="form[item.name]"
+              :option-field="item.remoteApi?{ keyField: 'id', labelField: 'name', valueField: 'id' }:{ keyField: 'value', labelField: 'text', valueField: 'value' }"
+              :remote="!!item.remote"
+              :search-filed="item.searchFiled"
+              :remote-data-query="item.remoteDataQuery"
+            />
+            <cascader
+              v-else-if="item.type === 'cascader'"
+              :api="item.api"
+              :data-query="item.dataquery"
+              :multiple="item.multiple"
+              :check-strictly="item.checkStrictly"
+              :value.sync="form[item.name]"
+            />
+            <el-date-picker
+              v-else-if="item.type === 'date'"
+              v-model="form[item.name]"
+              :type="item.dateType"
+              :style="item.style"
+              range-separator="至"
+              :start-placeholder="item.startPlaceholder"
+              :end-placeholder="item.endPlaceholder"
+              :disabled="item.disabled"
+              :unlink-panels="item.unlinkPanels || true"
+              :value-format="item.valueFormat"
+              :default-value="item.defaultValue"
+              :default-time="item.defaultTime"
+              :placeholder="`请选择${item.placeholder}`"
+            />
+            <el-time-select
+              v-else-if="item.type === 'time'"
+              v-model="form[item.name]"
+              :style="item.style"
+              :disabled="item.disabled"
+              :picker-options="item.pickerOptions?item.pickerOptions:{ start: '08:00', step: '00:05', end: '22:00' }"
+              :placeholder="`请选择${item.placeholder}`"
+            />
+            <div v-else-if="item.type === 'minmax'" class="minmax-box">
+              <el-input-number
+                v-model="form[item.name][0]"
+                :step="item.step || 1"
+                :min="item.min || ''"
+                :controls="false"
+                class="minmax-input"
+                :precision="item.precision"
+                :disabled="item.disabled"
+                :placeholder="`请输入${item.minPlaceholder}`"
+              />
+              <div class="division-line">-</div>
+              <el-input-number
+                v-model="form[item.name][1]"
+                :step="item.step || 1"
+                :min="item.min || ''"
+                class="minmax-input"
+                :controls="false"
+                :precision="item.precision"
+                :disabled="item.disabled"
+                :placeholder="`请输入${item.maxPlaceholder}`"
+              />
+            </div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </transition>
+  </el-form>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import OptionSet from '@/components/OptionSet/index'
+import Cascader from '@/components/Cascader/index'
+
+export default {
+  name: 'Query',
+  components: {
+    OptionSet,
+    Cascader
+  },
+  props: {
+    formConfig: {
+      type: [Array, Object],
+      default: () => {
+        return {
+          export: {
+            rows: 1000
+          },
+          items: []
+        }
+      }
+    }
+    // folding: {
+    //   type: Boolean,
+    //   default: () => true
+    // }
+    // form: {
+    //   type: Object,
+    //   default: () => {
+    //     return {}
+    //   }
+    // }
+  },
+  data() {
+    return {
+      isExporting: false,
+      show: false,
+      form: {},
+      folding: false
+    }
+  },
+  computed: {
+    ...mapGetters(['size', 'minMainHeight']),
+    selfAdaption() {
+      var totalSpan = 0
+      this.formConfigs.formConfigA.forEach(item => {
+        totalSpan += item.span === undefined ? 18 : item.span
+      })
+      return totalSpan > 18 ? 0 : 18 - totalSpan
+    },
+    formConfigs() {
+      const returnVal = {
+        formConfigA: [],
+        formConfigB: []
+      }
+      let count = 6
+      for (const v of this.formConfig.items) {
+        count += v.span
+        if (count <= 24) {
+          returnVal.formConfigA.push(v)
+        } else {
+          returnVal.formConfigB.push(v)
+        }
+      }
+      returnVal.export = this.formConfig.export
+      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
+      this.folding = !(count <= 24)
+      return returnVal
+    }
+  },
+  created() {
+    this.resetForm('created')
+  },
+  methods: {
+    isEmpty(val) {
+      if (!val && val !== 0 && val !== '') {
+        return ''
+      }
+      if (Array.prototype.isPrototypeOf(val) && val.length === 0) {
+        return []
+      }
+      if (Object.prototype.isPrototypeOf(val) && Object.keys(val).length === 0) {
+        return {}
+      }
+      return val
+    },
+    onSubmit() {
+      for (var k in this.form) {
+        this.form[k] = this.isEmpty(this.form[k])
+      }
+      // eslint-disable-next-line no-redeclare
+      for (var k in this.form) {
+        if (Array.isArray(this.form[k])) {
+          for (var i = this.form[k].length - 1; i >= 0; i--) {
+            if (this.form[k][i] === undefined) {
+              this.form[k].splice(i, 1)
+            }
+          }
+        }
+      }
+      this.$emit('onSubmit', this.form)
+    },
+    deepCopy(data) {
+      if (typeof data !== 'object') {
+        return data
+      }
+      if (Array.isArray(data)) {
+        return data.map(this.deepCopy)
+      }
+      const copyData = {}
+      for (const [key, value] of Object.entries(data)) {
+        copyData[key] = this.deepCopy(value)
+      }
+      return copyData
+    },
+    onExport() {
+      for (var k in this.form) {
+        this.form[k] = this.isEmpty(this.form[k])
+      }
+      // eslint-disable-next-line no-redeclare
+      for (var k in this.form) {
+        if (Array.isArray(this.form[k])) {
+          for (var i = this.form[k].length - 1; i >= 0; i--) {
+            if (this.form[k][i] === undefined) {
+              this.form[k].splice(i, 1)
+            }
+          }
+        }
+      }
+      const form = this.deepCopy(this.form)
+      form.page = 1
+      form.rows = this.formConfig.export.rows
+      form.__export__ = true
+      form.__name__ = this.formConfig.export.name
+      this.$emit('onSubmit', form)
+    },
+    resetForm(val) {
+      this.formCopy = {}
+      for (const v of this.formConfig.items) {
+        this.formCopy[v.name] = v.value === null ? '' : v.value || ''
+      }
+      this.form = JSON.parse(JSON.stringify(this.formCopy))
+      if (val === 'created') return
+      this.$emit('onSubmit', this.form)
+    },
+    handleClick(row) {
+      console.log(row)
+    }
+  }
+}
+</script>
+<style scoped lang="scss">
+.minmax-box {
+  display: flex;
+  .minmax-input {
+    width: calc((100% - 6px) / 2);
+  }
+
+  .division-line {
+    padding: 0 2px;
+    width: 6px;
+    display: flex;
+    justify-content: center;
+  }
+}
+.el-date-editor.el-input,
+.el-range-editor.el-input__inner,
+.el-select {
+  width: 100%;
+}
+</style>

+ 56 - 25
src/components/Sheet/index.vue

@@ -10,7 +10,7 @@
       :selection-data="selectionData"
       :selection-data="selectionData"
       :height="tableMaxHeight"
       :height="tableMaxHeight"
       @selection-change="selectionChange"
       @selection-change="selectionChange"
-@row-click="rowdata"
+      @row-click="rowdata"
       @cell-mouse-enter="cellMouseEnter"
       @cell-mouse-enter="cellMouseEnter"
     >
     >
       <el-table-column fixed :type="selectType" align="center" width="50" />
       <el-table-column fixed :type="selectType" align="center" width="50" />
@@ -29,7 +29,6 @@
           <template slot-scope="scope">
           <template slot-scope="scope">
             <template v-if="!column.render">
             <template v-if="!column.render">
               <template v-if="typeof column.formatter == 'string'">
               <template v-if="typeof column.formatter == 'string'">
-                <!--<span v-html="column.formatter(scope.row, column)" />-->
                 <span v-html="formatter(scope.row, column)" />
                 <span v-html="formatter(scope.row, column)" />
               </template>
               </template>
               <template v-else-if="typeof column.formatter == 'function'">
               <template v-else-if="typeof column.formatter == 'function'">
@@ -105,6 +104,8 @@
 </template>
 </template>
 <script>
 <script>
 import { scrollTo } from '@/utils/scroll-to'
 import { scrollTo } from '@/utils/scroll-to'
+import download from '@/utils/download'
+
 export default {
 export default {
   name: 'Sheet',
   name: 'Sheet',
   props: {
   props: {
@@ -197,11 +198,6 @@ export default {
     this.getData()
     this.getData()
   },
   },
   methods: {
   methods: {
-    search(obj) {
-      this.dataQuery.page = 1
-      Object.assign(this.dataQuery, obj)
-      this.getData()
-    },
     cellMouseEnter(row, column) {
     cellMouseEnter(row, column) {
       this.$emit('cell-mouse-enter', row, column)
       this.$emit('cell-mouse-enter', row, column)
     },
     },
@@ -229,32 +225,67 @@ export default {
       this.selectionData = selection
       this.selectionData = selection
       this.$emit('update:selectionData', selection)
       this.$emit('update:selectionData', selection)
     },
     },
-    async getData() {
+    checkFunc(data) {
+      return typeof data === 'function' ? data() : data
+    },
+    deepCopy(data) {
+      if (typeof data !== 'object') {
+        return data
+      }
+      if (Array.isArray(data)) {
+        return data.map(this.deepCopy)
+      }
+      const copyData = {}
+      for (const [key, value] of Object.entries(data)) {
+        copyData[key] = this.deepCopy(value)
+      }
+      return copyData
+    },
+    getExportColumns() {
+      const columns = this.deepCopy(this.checkFunc(this.columns))
+      return columns
+        .filter(column => typeof column.show === 'undefined' || column.show)
+        .map(column => {
+          if (column.width) {
+            column.width = column.width * 0.125 || 0
+          }
+          return column
+        })
+    },
+    async getData(obj = {}) {
+      const dataQuery = Object.assign(this.dataQuery, obj)
       const codes = []
       const codes = []
       this.columns.forEach(item => {
       this.columns.forEach(item => {
-        if (
-          item.formatter !== undefined &&
-          typeof item.formatter === 'string'
-        ) {
+        if (item.formatter !== undefined && typeof item.formatter === 'string') {
           codes.push(item.formatter)
           codes.push(item.formatter)
         }
         }
       })
       })
       for (var i = 0; i < codes.length; i++) {
       for (var i = 0; i < codes.length; i++) {
         this.optionsets[codes[i]] = await this.$store.dispatch('optionset/formatterData', codes[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
-      }).catch(() => {})
+      if (dataQuery.__export__) {
+        this.isExporting = true
+        dataQuery.__columns__ = JSON.stringify(this.getExportColumns())
+        this.api(dataQuery).then(({ data }) => {
+          download(data).then(() => {
+            this.isExporting = false
+          })
+        })
+      } else {
+        this.dataLoading = true
+        this.api(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
+          return res
+        })
+      }
     },
     },
     sizeChange(val) {
     sizeChange(val) {
       scrollTo(0, 600)
       scrollTo(0, 600)

+ 11 - 0
src/router/index.js

@@ -12,6 +12,17 @@ import Layout from '@/layout'
  * all roles can be accessed
  * all roles can be accessed
  */
  */
 export const constantRoutes = [
 export const constantRoutes = [
+  {
+    path: '/redirect',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: '/redirect/:path(.*)',
+        component: () => import('@/views/redirect/index')
+      }
+    ]
+  },
   {
   {
     path: '/login',
     path: '/login',
     component: () => import('@/views/login/index'),
     component: () => import('@/views/login/index'),

+ 12 - 0
src/views/redirect/index.vue

@@ -0,0 +1,12 @@
+<script>
+export default {
+  created() {
+    const { params, query } = this.$route
+    const { path } = params
+    this.$router.replace({ path: '/' + path, query })
+  },
+  render: function(h) {
+    return h() // avoid warning message
+  }
+}
+</script>

+ 8 - 3
src/views/user/index.vue

@@ -8,6 +8,7 @@
           </el-aside>
           </el-aside>
           <el-container>
           <el-container>
             <el-header height="82">
             <el-header height="82">
+              <query ref="searchForm2" :form-config="query1" @onSubmit="search" />
               <el-form ref="searchForm" :model="dataQuery" :size="size" label-position="left" label-width="80px">
               <el-form ref="searchForm" :model="dataQuery" :size="size" label-position="left" label-width="80px">
                 <el-row :gutter="20">
                 <el-row :gutter="20">
                   <el-col :span="6">
                   <el-col :span="6">
@@ -115,14 +116,18 @@ import checkPermission from '@/utils/permission'
 import Tree from '@/components/Tree'
 import Tree from '@/components/Tree'
 import Sheet from '@/components/Sheet'
 import Sheet from '@/components/Sheet'
 import ExportButton from '@/components/ExportButton'
 import ExportButton from '@/components/ExportButton'
+import Query from '@/components/Query'
+import { user } from './query'
 
 
 export default {
 export default {
   name: 'User',
   name: 'User',
   components: {
   components: {
     Tree,
     Tree,
     Sheet,
     Sheet,
-    ExportButton
+    ExportButton,
+    Query
   },
   },
+  mixins: [user],
   data() {
   data() {
     return {
     return {
       tableColumns: [
       tableColumns: [
@@ -378,8 +383,8 @@ export default {
         })
         })
       }).catch(() => {})
       }).catch(() => {})
     },
     },
-    search() {
-      this.$refs.qtable.getData()
+    search(obj) {
+      return this.$refs.qtable.getData(obj)
     },
     },
     resetFields() {
     resetFields() {
       this.$refs['searchForm'].resetFields()
       this.$refs['searchForm'].resetFields()

+ 30 - 0
src/views/user/query.js

@@ -0,0 +1,30 @@
+export const user = {
+  data() {
+    return {
+      query1: {
+        export: {
+          name: 'users.xlsx',
+          rows: 1000
+        },
+        items: [
+          {
+            name: 'mobile',
+            placeholder: 'Mobile',
+            span: 4,
+            style: '',
+            type: '',
+            value: ''
+          },
+          {
+            name: 'name',
+            placeholder: 'Name',
+            span: 4,
+            style: '',
+            type: '',
+            code: ''
+          }
+        ]
+      }
+    }
+  }
+}