管理後臺 表管理 數據設計(vue)

很久沒有再寫管理後臺, 每次coding都有不同的想法, 爲了更易於擴展, 更快捷, 更模塊話…說白了就是懶
基於Vue element-ui構建 並舉例用戶表的增刪改查

根頁面 user.js

<!--
 * @Descripttion: 用戶管理頁面
 * @version: 1.0.0
 * @Author: 仲灝 Izhaong<164165005@qq.com>
 * @Date: 2020-06-27 11:41:32
 * @LastEditors: 仲灝 Izhaong<164165005@qq.com>
 * @LastEditTime: 2020-06-30 10:42:05
-->
<template>
  <div class="user_container">
    <complex-table
      ref="table"
      :columns="tableColumns"
      :operates="tableOperates"
      :filters="tableFilters"
      :api="api"
      @createItem="handleCreate"
    />

    <el-dialog
      destroy-on-close
      :title="dialogForm.textMap[dialogForm.status]"
      :visible.sync="dialogForm.visible"
    >
      <FormGenerate ref="form" :items="formItems" @submit="submitForm" />
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogForm.visible = false">取消</el-button>
        <el-button type="primary" @click="$refs.form.submitForm()">確認</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getUserList, createUser, updateUser, deleteUser } from '@/api/user'
import { getDiffObj } from '@/utils/tools'
import { deepClone } from '@/utils'
import ComplexTable from '../../components/ComplexTable'
import FormGenerate from '../../components/FormGenerate'
export default {
  name: 'User',

  components: { ComplexTable, FormGenerate },

  data() {
    return {
      api: getUserList,
      tableColumns: [
        { label: '用戶名稱', prop: 'username', align: 'center' },
        { label: '姓名', prop: 'name', align: 'center' },
        { label: '手機號', prop: 'phone', align: 'center' },
        { label: '郵箱', prop: 'email', align: 'center' },
        { label: '創建時間', prop: 'createTime', align: 'center' },
        {
          label: '狀態',
          prop: 'status',
          align: 'center',
          formatter: row => {
            return row.status === 0 ? '正常' : '禁用'
          }
        }
      ],
      tableOperates: [
        {
          id: 1,
          label: '編輯',
          type: 'primary',
          isPop: false,
          method: row => {
            this.handleEdit(row)
          }
        },
        {
          id: 2,
          label: '刪除',
          type: 'danger',
          isPop: true,
          method: row => {
            this.handleDel(row)
          }
        }
      ],
      tableFilters: [
        {
          is: 'el-input',
          prop: 'username',
          attrs: { placeHolder: '用戶名稱', style: 'width: 200px;' }
        },
        {
          is: 'el-input',
          prop: 'name',
          attrs: { placeHolder: '姓名', style: 'width: 200px;' }
        },
        {
          is: 'el-select',
          prop: 'status',
          attrs: { placeholder: '狀態', clearable: true },
          options: [
            { label: '正常', value: 0 },
            { label: '禁用', value: 1 }
          ]
        }
      ],

      formAttrs: {
        'label-position': 'right',
        'label-width': '100px',
        size: 'mini'
      },
      formItems: [
        {
          attrs: {
            prop: 'username',
            label: '用戶名稱',
            rules: [
              { required: true, message: '用戶名不能爲空', trigger: 'blur' }
            ]
          },
          component: { is: 'el-input' }
        },
        {
          attrs: {
            prop: 'name',
            label: '姓名',
            rules: [
              { required: true, message: '姓名不能爲空', trigger: 'blur' }
            ]
          },
          component: { is: 'el-input' }
        },
        {
          attrs: {
            prop: 'phone',
            label: '手機號',
            rules: [
              { required: true, message: '手機號不能爲空', trigger: 'blur' },
              {
                pattern: /^1[3456789]\d{9}$/,
                message: '目前只支持中國大陸的手機號碼'
              }
            ]
          },
          component: { is: 'el-input' }
        },
        {
          attrs: {
            prop: 'email',
            label: '郵箱',
            rules: [
              { required: true, message: '郵箱不能爲空', trigger: 'blur' },
              { type: 'email', message: '請輸入正確的郵箱' }
            ]
          },
          component: { is: 'el-input' }
        },
        {
          attrs: {
            prop: 'status',
            label: '狀態',
            rules: [
              { required: true, message: '狀態不能爲空', trigger: 'change' }
            ]
          },
          component: { is: 'el-radio-group' },
          compTemps: [
            { is: 'el-radio', label: 1, key: 1, content: '禁用' },
            { is: 'el-radio', label: 0, key: 0, content: '正常' }
          ]
        }
      ],

      dialogForm: {
        visible: false,
        textMap: {
          update: '編輯',
          create: '添加'
        },
        status: 'create'
      }
    }
  },

  methods: {
    submitForm(formData) {
      console.log(formData)
      const isCreate = this.dialogForm.status === 'create'
      if (isCreate) {
        createUser(formData).then(_ => {
          this.$refs.table.getList()
        })
      } else {
        const diffObj = getDiffObj(this.actionRow, formData)
        updateUser({ ...diffObj, id: this.actionRow.id })
      }
      this.dialogForm.visible = false
    },
    handleCreate() {
      this.dialogForm.status = 'create'
      this.dialogForm.visible = true
    },
    handleEdit(row) {
      this.actionRow = deepClone(row)
      this.dialogForm.status = 'update'
      this.dialogForm.visible = true
      this.$nextTick(() => {
        this.$refs.form.formData = row
      })
    },
    handleDel(row) {
      const { id: userId } = row
      deleteUser({ userId })
    }
  }
}
</script>

components/ComplexTable.vue

<!--
 * @Descripttion: 數據化表格
 * @version: 1.0.0
 * @Author: 仲灝 Izhaong<164165005@qq.com>
 * @Date: 2020-06-27 15:13:00
 * @LastEditors: 仲灝 Izhaong<164165005@qq.com>
 * @LastEditTime: 2020-06-30 10:50:25
-->
<template>
  <div class="app-container">
    <div class="filter-container">
      <component
        :is="filter.is"
        v-for="(filter, index) in filters"
        :key="index"
        v-model="filterForm[filter.prop]"
        v-bind="filter.attrs"
        class="filter-item"
      >
        <template v-if="filter.options">
          <el-option
            v-for="(option, i) in filter.options"
            :key="`${index}_${i}`"
            :value="option.value"
            :label="option.label"
          />
        </template>
      </component>

      <el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
      <el-button
        v-if="crud.includes('c')"
        class="filter-item"
        style="margin-left: 10px;"
        type="primary"
        icon="el-icon-edit"
        @click="$emit('createItem')"
      >添加</el-button>
    </div>

    <el-table :key="tableKey" v-loading="listLoading" :data="list" style="width: 100%;">
      <el-table-column v-for="col in columns" :key="col.prop" v-bind="col" />

      <el-table-column v-if="operates.length" v-bind="actionsColumn">
        <template slot-scope="scope">
          <template v-for="(btn, index) in operates">
            <el-button
              v-if="!btn.isPop"
              :key="index"
              style="margin: 5px;"
              size="mini"
              :type="btn.type"
              @click.native.prevent="btn.method(scope.row,scope)"
            >{{ btn.label }}</el-button>

            <el-popconfirm
              v-if="btn.isPop"
              :key="index"
              placement="right"
              confirm-button-text="確定"
              cancel-button-text="取消"
              icon="el-icon-info"
              icon-color="red"
              title="這是一段內容確定刪除嗎?"
              @onConfirm="btn.method(scope.row, scope)"
            >
              <el-button
                slot="reference"
                style="margin: 5px;"
                size="mini"
                :type="btn.type"
              >{{ btn.label }}</el-button>
            </el-popconfirm>
          </template>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="listQuery.page"
      :limit.sync="listQuery.size"
      @pagination="getList"
    />
  </div>
</template>

<script>
import Pagination from '@/components/Pagination'

export default {
  name: 'ComplexTable',
  components: { Pagination },

  props: {
    columns: { type: Array, default: () => [] },
    operates: { type: Array, default: () => [] },
    filters: { type: Array, default: () => [] },
    crud: { type: String, default: 'crud' },
    actionsColumn: {
      type: Object,
      default: () => ({
        label: '操作',
        align: 'center'
      })
    },
    api: [Function, Object]
  },
  data() {
    return {
      filterForm: {},
      tableKey: 0,
      list: null,
      total: 0,
      listLoading: true,
      listQuery: {
        page: 1,
        size: 20
      }
    }
  },

  created() {
    this.initFilters()
    this.getList()
  },
  methods: {
    initFilters() {
      const props = this.filters.map(item => item.prop)
      props.forEach(key => {
        this.$set(this.filterForm, key, '')
      })
    },
    getList() {
      this.listLoading = true
      this.api({ ...this.listQuery, ...this.filterForm }).then(response => {
        this.list = response.data.records
        this.total = response.data.total
        this.listLoading = false
      })
    },
    handleFilter() {
      this.listQuery.page = 1
      this.getList()
    }
  }
}
</script>

components/Pagination.vue (非原創)

<template>
  <div :class="{'hidden':hidden}" class="pagination-container">
    <el-pagination
      :background="background"
      :current-page.sync="currentPage"
      :page-size.sync="pageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :total="total"
      v-bind="$attrs"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script>
import { scrollTo } from '@/utils/scroll-to'

export default {
  name: 'Pagination',
  props: {
    total: {
      required: true,
      type: Number
    },
    page: {
      type: Number,
      default: 1
    },
    limit: {
      type: Number,
      default: 20
    },
    pageSizes: {
      type: Array,
      default() {
        return [10, 20, 30, 50]
      }
    },
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper'
    },
    background: {
      type: Boolean,
      default: true
    },
    autoScroll: {
      type: Boolean,
      default: true
    },
    hidden: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    currentPage: {
      get() {
        return this.page
      },
      set(val) {
        this.$emit('update:page', val)
      }
    },
    pageSize: {
      get() {
        return this.limit
      },
      set(val) {
        this.$emit('update:limit', val)
      }
    }
  },
  methods: {
    handleSizeChange(val) {
      this.$emit('pagination', { page: this.currentPage, limit: val })
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    },
    handleCurrentChange(val) {
      this.$emit('pagination', { page: val, limit: this.pageSize })
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    }
  }
}
</script>

<style scoped>
.pagination-container {
  background: #fff;
  padding: 32px 16px;
}
.pagination-container.hidden {
  display: none;
}
</style>

components/FormGenerate.vue

<!--
 * @Descripttion: 數據化表單
 * @version: 1.0.0
 * @Author: 仲灝 Izhaong<164165005@qq.com>
 * @Date: 2020-06-28 14:50:34
 * @LastEditors: 仲灝 Izhaong<164165005@qq.com>
 * @LastEditTime: 2020-06-30 10:49:16
-->
<template>
  <el-form
    ref="formData"
    :model="formData"
    v-bind="attrs"
  >
    <el-form-item v-for="item in items" :key="item.attrs.prop" v-bind="item.attrs">
      <template>
        <!-- eslint-disable-next-line vue/require-component-is -->
        <component v-model="formData[item.attrs.prop]" v-bind="item.component">
          <template v-if="item.compTemps && item.compTemps.length">
            <component :is="temp.is" v-for="temp in item.compTemps" :key="`${item.attrs.prop}_${temp.key}`" :label="temp.label" :value="temp.value">{{ temp.content }}</component>
          </template>
        </component>
      </template>
    </el-form-item>

    <el-form-item v-if="showActions">
      <el-button type="primary" @click="submitForm">提交</el-button>
      <el-button @click="resetForm">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  name: 'FormGernerate',
  props: {
    attrs: { type: Object, default: () => ({
      'label-position': 'right',
      'label-width': '100px',
      size: 'mini'
    }) },
    items: { type: Array, default: () => [] },
    showActions: { type: Boolean, default: false }
  },
  data() {
    return {
      formData: {}
    }
  },
  created() {
    this.initForm()
  },
  methods: {
    submitForm() {
      this.$refs.formData.validate((valid) => {
        if (valid) {
          this.$emit('submit', this.formData)
        } else {
          console.log(valid)
          return false
        }
      })
    },
    resetForm() {
      this.$refs.formData.resetFields()
    },
    initForm() {
      const props = this.items.map(i => i.attrs.prop)
      props.forEach(k => {
        this.$set(this.formData, k, undefined)
      })
    },
    resetFormData() {
      const props = this.items.map(i => i.attrs.prop)
      props.forEach(k => {
        this.formData[k] = undefined
      })
    }
  }
}
</script>

最後是api和函數工具

api

/*
 * @Descripttion: 管理後臺用戶接口
 * @version: 1.0.0
 * @Author: 仲灝 Izhaong<[email protected]>
 * @Date: 2020-06-24 17:18:03
 * @LastEditors: 仲灝 Izhaong<[email protected]>
 * @LastEditTime: 2020-06-30 10:32:36
 */
import request from '@/utils/request'

const userUrl = '/api/user'

...

export function getUserList(params = { page: 1, size: 20 }) {
  return request({
    url: userUrl + '/list',
    method: 'get',
    params
  })
}

export function createUser(data) {
  return request({
    url: userUrl,
    method: 'post',
    data
  })
}

export function updateUser(data) {
  return request({
    url: userUrl,
    method: 'put',
    data
  })
}

export function deleteUser(params) {
  return request({
    url: userUrl,
    method: 'delete',
    params
  })
}


utils.js

深拷貝網上一大堆這裏就不放了

/**
 * @Descripttion: 獲取發生變動的對象屬性並組成一個新的對象 Obj1 >= obj2 >= diffObj
 * @Author: 仲灝 Izhaong<[email protected]>
 * @param {Object} obj1 大對象
 * @param {Object} obj2 小對象
 * @return: 小對象中的屬性值不等於大對象中屬性值組合的對象
 * @LastEditors: 仲灝 Izhaong<[email protected]>
 * @LastEditTime: Do not Edit
 */
export const getDiffObj = (obj1, obj2) => {
  const diffObj = {}
  const keysArr1 = Object.keys(obj1)
  keysArr1.forEach(key => {
    if (obj1[key] !== obj2[key]) diffObj[key] = obj2[key]
  })
  return diffObj
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章