preface

Like the back end, the front end also has corresponding layering and CURD. This paper is mainly to customize the front-end CURD template sample, standardize the code, and pave the way for the subsequent code generator.

Functional analysis

Interface analysis

Let’s take a simple CURD interface to do a simple analysis. The following uses the user management interface as an example:

function Address of the interface
Add user /sys/user/save
Modify the user /sys/user/update
Delete user /sys/user/delete
Obtain the user by ID /sys/user/get
Paging query user list /sys/user/list

Page analysis

function Page type operation
List of users page Add, modify, delete, query (refresh), reset
Add user Popup window Submit, cancel
Modify the user Popup window Submit, cancel

Operation analysis

The methods here are js methods.

The method name Method statement
requestData Request to get a list of users
getDetails Obtain user information by ID
handleSearch Processing query operations
handleReset Resetting the search form
handleOpenAddDialog The dialog box for adding a user is displayed
handleOpenEditDialog The Modify User dialog box is displayed
handleOpenDetailsDialog The user details dialog box is displayed
handleCancel Cancel the submission
handleSubmit Submit user information – Add/modify
handleRemove Delete operation

Framework of hierarchical

The previous article modularized routing and simply modularized pages, and this article will further explain the pages of individual modules.

The directory structure

├─ SRC/API ├─ sys ├─ ├─ sys.user.service. └ ─ ─... ├─ SRC/Views/Modules ├─ Sys System Management Module ├─ User User Management ├─ Componets ├─ form-.vue Add/Modify Forms ├─ Search.vue Search Forms ├─ ├─ Edit.vue edit.vue edit.vue edit.vue Edit.vue Edit.vue Edit.vue Admin page-Add /details/ Edit/Search └ ─ ─... └ ─ ─...Copy the code

This is all the files you need to create for a single table operation. It’s just a simple operation. It’s easier to create CV files on an index.vue, but it’s not too much trouble because of the code generator that comes with it.

File,

  • src/views/modules/sys/user/index.vue

User management home page, here you can focus on the processing of the box, the use of vUE dynamic components.

<template>
  <div class="app-container">
    <!--start========头部折叠面板===========start-->
    <el-collapse accordion value="1" style="padding:0px;">
      <el-collapse-item name="1">
        <template slot="title">
          <el-page-header title="返回" content="搜索条件" @back="goBack"></el-page-header>
        </template>
        <!--搜索模块-->
        <m-search ref="searchForm" @on-search="handleSearch" />
      </el-collapse-item>
    </el-collapse>
    <!--start========头部折叠面板===========start-->
    <!--start========顶部工具栏===========start-->
    <el-row :gutter="10" class="mb8 mt10">
      <el-col :span="1.5">
        <el-button type="primary" icon="el-icon-plus" size="small" @click="handleOpenAddDialog">
          添加
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="success" icon="el-icon-edit" size="small" :disabled="single" @click="handleOpenEditDialog">
          修改
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="danger" icon="el-icon-delete" size="small" :disabled="multiple" @click="handleRemove">
          删除
        </el-button>
      </el-col>
    </el-row>
    <!--end========顶部工具栏===========end-->
    <!--start========表格列表===========start-->
    <el-table stripe :header-cell-style="{background:'#eef1f6',color:'#606266'}" v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column prop="userName" label="用户名">
        <template slot-scope="scope">
          {{ scope.row.userName }}
        </template>
      </el-table-column>
      <el-table-column prop="realName" label="姓名">
        <template slot-scope="scope">
          {{ scope.row.realName }}
        </template>
      </el-table-column>
      <el-table-column prop="mobilePhone" label="手机号">
        <template slot-scope="scope">
          {{ scope.row.mobilePhone }}
        </template>
      </el-table-column>
      <el-table-column prop="sex" label="性别">
        <template slot-scope="scope">
          <span v-if="scope.row.sex === 1">男</span>
          <span v-else-if="scope.row.sex === 2">女</span>
          <span v-else>未知</span>
        </template>
      </el-table-column>
      <el-table-column prop="isLocked" label="是否锁定">
        <template slot-scope="scope">
          <span v-if="scope.row.isLocked === 1">是</span>
          <span v-else-if="scope.row.isLocked === 2">否</span>
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="创建时间">
        <template slot-scope="scope">
          {{ scope.row.createTime }}
        </template>
      </el-table-column>
      <el-table-column
        label="操作"
        align="center">
        <template slot-scope="scope">
		  <!-- click.native.stop==>阻止单击事件冒泡 -->
          <el-button type="text" size="small" icon="el-icon-view" @click.native.stop="handleOpenDetailsDialog(scope.row)">查看</el-button>
          <el-button type="text" size="small" icon="el-icon-edit" @click.native.stop="handleOpenEditDialog(scope.row)">修改</el-button>
          <el-button type="text" size="small" icon="el-icon-delete" @click.native.stop="handleRemove(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!--end========表格列表===========end-->
    <!--start========分页===========start-->
    <pagination
      v-show="recordCount>0"
      :total="recordCount"
      :page.sync="pageNum"
      :limit.sync="pageSize"
      @pagination="requestData"
    />
    <!--end========分页===========end-->
    <!--start========弹框===========start-->
    <el-dialog :title="title" :visible.sync="isOpenDialog" width="500px" append-to-body @close="handleCancel">
      <!--动态组件-->
      <component :ref="currentView" :is="currentView" :id="id"></component>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" v-if="showOk" :loading="submitLoading" @click="handleSubmit">确 定</el-button>
        <el-button @click="handleCancel">取 消</el-button>
      </div>
    </el-dialog>
    <!--end========弹框===========end-->
  </div>
</template>
<script>
// 搜索组件
import MSearch from './components/search'
// 添加
import Add from './add'
// 修改
import Edit from './edit'
// 详情
import Details from './details'
// 接口服务
import { list as listUser, remove as removeUser } from '@/api/sys/sys.user.service.js'

export default {
  components: {
    MSearch,
    Add,
    Edit,
    Details
  },
  data() {
    return {
      // 当前id
      id: undefined,
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 加载中
      loading: false,
      // 总记录数
      recordCount: 0,
      // 当前页
      pageNum: 1,
      // 每页大小
      pageSize: 10,
      // 列表数据
      tableData: [],
      // 当前勾选行id
      ids: [],
      // 当前勾选行集合
      selection: [],
      // 当前弹出框页面
      currentView: 'add',
      // 弹框标题
      title: '我是标题',
      // 是否打开弹出框
      isOpenDialog: false,
      // 是否显示弹出框确认按钮
      showOk: true,
      // 提交加载中
      submitLoading: false
    }
  },
  created() {
    this.requestData()
  },
  methods: {
    // 查询
    handleSearch() {
      this.requestData()
    },
    // 打开添加弹出框
    handleOpenAddDialog() {
      this.title = '添加用户'
      this.isOpenDialog = true
      this.showOk = true
      this.currentView = 'Add'
    },
    // 打开修改弹出框
    handleOpenEditDialog(row) {
      if (row.id) {
        this.id = row.id
      } else {
        this.id = this.ids.length ? this.ids[0] : row.id
      }
      this.title = '修改用户'
      this.isOpenDialog = true
      this.showOk = true
      this.currentView = 'Edit'
    },
    // 打开详情
    handleOpenDetailsDialog(row) {
      this.id = row.id
      this.title = '用户详情'
      this.isOpenDialog = true
      this.showOk = false
      this.currentView = 'Details'
    },
    // 删除
    handleRemove(row) {
      this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        var ids = []
        if (row.id) {
          ids.push(row.id)
        } else {
          ids = this.ids
        }
        removeUser({
          ids: ids
        }).then(res => {
          if (res.code === 0) {
            this.$message({ message: '删除成功', type: 'success' })
            this.requestData()
          } else {
            this.$message({ message: res.msg || '删除失败', type: 'error' })
          }
        })
      }).catch(() => {
        this.$message({ type: 'info', message: '已取消删除' })
      })
    },
    // 表格选中事件
    handleSelectionChange(selection) {
      this.selection = selection
      this.ids = selection.map(item => item.id)
      this.single = selection.length !== 1
      this.multiple = !selection.length
    },
    // 请求数据
    requestData(page) {
      if (!page) {
        page = {
          page: this.pageNum,
          limit: this.pageSize
        }
      }
      this.loading = true
      listUser({
        pageNum: page.page,
        pageSize: page.limit,
        ...this.$refs['searchForm'] ? this.$refs['searchForm'].getData() : {}
      }).then(res => {
        this.loading = false
        if (res.code === 0) {
          this.tableData = res.data.rows
          this.recordCount = res.data.recordCount
        }
      }).catch(() => {
        this.loading = false
      })
    },
    // 表单提交
    handleSubmit() {
      this.submitLoading = true
      this.$refs[this.currentView].submit().then(() => {
        this.submitLoading = false
        this.handleCancel()
        this.requestData()
      }).catch(() => {
        this.submitLoading = false
      })
    },
    // 取消提交
    handleCancel() {
      this.id = undefined
      this.isOpenDialog = false
    }
  }
}
</script>

Copy the code
  • src/api/sys/sys.user.service.js

User interface service, corresponding to sys- user management interface

import request from '@/utils/request'
/** * Add user * @param {*} data */
export function save(data) {
  return request({
    url: '/sys/user/save'.method: 'post',
    data
  })
}
/** * alter user * @param {*} data */
export function update(data) {
  return request({
    url: '/sys/user/update'.method: 'post',
    data
  })
}
@param {*} data */
export function remove(data) {
  return request({
    url: '/sys/user/remove'.method: 'post',
    data
  })
}
/** * Get user * @param {*} data */
export function get(data) {
  return request({
    url: '/sys/user/get'.method: 'post',
    data
  })
}
@param {*} data */
export function list(data) {
  return request({
    url: '/sys/user/list'.method: 'post',
    data
  })
}
Copy the code
  • src/views/modules/sys/user/componets/form.vue
<template> <el-form ref="form" :model="form" :rules="rules" label-width="100px"> <el-form-item label=" username" Prop ="userName"> <el-input V-model ="form.userName" placeholder=" please input userName"> </el-input> </el-form-item> <el-form-item Label =" name "prop="realName"> <el-input V-model ="form.realName" placeholder=" please input name "></el-input> </el-form-item> <el-form-item label=" mailbox "prop="email"> < EL-input V-model ="form.email" placeholder=" please input email"> </el-input> </el-form-item> <el-form-item label=" placeholder "prop="mobilePhone"> < EL-input V-model ="form.mobilePhone" placeholder=" placeholder "></el-input> </el-form-item> <el-form-item label=" sex" prop="sex"> <el-select V-model ="form. Sex "> <el-option label=" male" : value = "1" > < / el - option > < el - option label = "female" : value = "2" > < / el - option > < / el - select > < / el - form - item > < el - form - the item < EL-select V-model ="form.isLocked"> <el-option label=" Yes ":value="2"></el-option> <el-option label=" No ":value="1"></el-option> </el-select> </el-form-item> </el-form> </template> <script> import {save as saveUser, update as updateUser, get as getUser } from '@/api/sys/sys.user.service.js' export default { props: { isEdit: { type: Boolean, default: false }, id: { type: [String, Number], default: undefined } }, data() { return { form: { id: undefined, userName: undefined, realName: undefined, email: undefined, mobilePhone: undefined, sex: 1, isLocked: 2 }, rules: { userName: [ { required: true, message: 'User name cannot be blank ', trigger: 'blur'}], realName: [{required: true, message:' Name cannot be blank ', trigger: 'blur'}], mobilePhone: [{required: true, message: 'Mobile phone number cannot be blank ', trigger: 'blur'}], sex: [{required: true, message:' Gender cannot be blank ', trigger: 'blur'}], isLocked: [{required: true, message: 'Lock cannot be null ', trigger: 'blur'}]}}}, watch: {this.getdetails ()}, mounted() {this.getdetails ()}, methods: {id(n, o) {this.getdetails ()}} {submit(isShowMessage = 1) {// isShowMessage=> Default 1 return new Promise((resolve, Reject) => {this.$refs['form'].validate((valid) => {if (valid) {if (this.isEdit) {// Call modify user interface service updateUser(this.form).then(res => { if (isShowMessage) { if (res.code === 0) { this.$message({ message: Res. MSG | | 'operation is successful, type: 'SUCCESS'})} resolve(res)}). Catch (e => {reject(e)})} else {delete this.form.id // call add user interface service saveUser(this.form).then(res => { if (isShowMessage) { if (res.code === 0) { this.$message({ message: Res. MSG | | 'operation is successful, type: 'success' }) } } resolve(res) }).catch(e => { reject(e) }) } } else { reject(new Error('error')) } }) }) }, $refs['form'].resetFields() {this.$refs['form'].clearvalidate ()} GetDetails () {if (this.isedit && this.id) {// If (this.isedit && this.id) {getUser({id: this.id }).then(res => { this.form = this.$util.copy(res.data, this.form) }) } } } } </script>Copy the code
  • src/views/modules/sys/user/componets/search.vue

The search component uses a special m_ drop argument to process the global SRC /utils/request.js data to the back end, as in: SRC /utils/request.js

m_EQ_userName====>

{
    operateType: "EQ",
    propertyName: "userName",
    propertyValue: value
}
Copy the code
<template> < EL-form ref=" FORM ":model=" FORM" :inline="true"> <el-form-item label=" user name "prop="m_EQ_userName"> < EL-input V -model=" form.m_eq_username "placeholder=" size="small" style="width: 240px"></el-input> </el-form-item> <el-form-item label=" name "prop="m_EQ_realName"> < EL-input V-model =" form.m_eq_realName" Placeholder =" size="small" style="width: 240px"></el-input> </el-form-item> <el-form-item label=" mobile phone number "prop="m_EQ_mobilePhone"> < EL-input V -model=" form.m_eq_mobilephone "placeholder=" size="small" style="width: 240px"></ EL-input ></ el-form-item> <el-form-item label=" Whether prop="m_EQ_isLocked"> <el-select V-model =" form.m_eq_ISlocked "size="small"> <el-option label=" all" :value="undefined"> all </el-option> <el-option label=" Yes" : value = "1" > < / el - option > < el - option label = "no" : value = "2" > < / el - option > < / el - select > < / el - form - item > < el - form - the item <el-date-picker V-model ="form.m_BT_createTime" size="small" style="width: 240px" value-format=" YYYY-MM-DD "type="daterange" range-separator="-" start-placeholder=" start date" end-placeholder=" end date" ></el-date-picker> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="small" </el-button> <el-button icon="el-icon-refresh" size="small" @click=" resetForm "> reset </el-button> </el-form-item> </el-form> </template> <script> export default { props: { }, data() { return { form: { m_EQ_userName: undefined, m_EQ_realName: undefined, m_EQ_mobilePhone: undefined, m_EQ_isLocked: undefined, m_BT_createTime: undefined } } }, methods: $emit('on-search', this.form, e)}; Resetform (e) {this.$refs['form'].resetFields()}, {var data = object.assign ({}, this.form) return data } } } </script>Copy the code
  • src/views/modules/sys/user/add.vue

/ Componts /form.vue

<template>
  <m-form ref="form" :is-edit="false" :id="id"></m-form>
</template>
<script>
import MForm from './components/form'

export default {
  components: { MForm },
  props: {
    id: {
      type: [String, Number],
      default: undefined
    }
  },
  methods: {
    submit() {
      return this.$refs.form.submit()
    },
    resetFields() {
      this.$refs.form.resetFields()
    }
  }
}
</script>
Copy the code
  • src/views/modules/sys/user/details.vue

/ Componts /form.vue

<template>
  <m-form ref="form" :is-edit="true" :id="id"></m-form>
</template>
<script>
import MForm from './components/form'

export default {
  components: { MForm },
  props: {
    id: {
      type: [String, Number],
      default: undefined
    }
  },
  methods: {
  }
}
</script>

Copy the code
  • src/views/modules/sys/user/edit.vue

To modify the page, introduce the./ Componts /form.vue component as you would add a page

<template>
  <m-form ref="form" :is-edit="true" :id="id"></m-form>
</template>
<script>
import MForm from './components/form'

export default {
  components: { MForm },
  props: {
    id: {
      type: [String, Number],
      default: undefined
    }
  },
  methods: {
    submit() {
      return this.$refs.form.submit()
    },
    resetFields() {
      this.$refs.form.resetFields()
    }
  }
}
</script>

Copy the code
  • src/utils/request.js

Here added special request parameter handling, code snippet

// request interceptor
service.interceptors.request.use(
  config= > {
    // do something before request is sent
    if (store.getters.token) {
      // If there is a token, put it in the request header
      X-token -> auth-token
      config.headers['Auth-Token'] = getToken()
    }
    if (config.data) {
      // Request parameters are processed globally, mainly by concatenating query conditions
      var whereParams = []
      Object.keys(config.data).forEach(item= > {
        if (item.startsWith('m_')) {
          // Process arguments starting with m_
          var value = config.data[item]
          if (value) {
            var arr = item.split('_')
            if (arr.length === 3) {
              whereParams.push({
                operateType: arr[1].propertyName: arr[2].propertyValue: value
              })
            }
          }
          // Delete after processing
          delete config.data[item]
        }
      })
      if (whereParams.length) {
        config.data.whereParams = whereParams
      }
    }

    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
Copy the code

rendering

  • List of pp.

  • Add bounced

  • Modify the bounced

  • View details (not done in detail)

summary

This article is just a quick look at the CURD template sample at the front end. This page is not yet final and should not be used as a base template for a code generator. Later articles will cover this in more detail. Here is a brief preview:

  1. How to elegantly handle row by column and row by column;

  2. How to make some common service components, such as upload, dictionary, drop-down association table (single selection, multiple selection), rich text, etc.

  3. The design of button level permissions

  4. Of course, the most core is how to do the front-end code generator

Project source code address

  • The back-end

Gitee.com/mldong/mldo…

  • The front end

Gitee.com/mldong/mldo…

Related articles

Create a suitable for their own rapid development framework – the pilot

Build a suitable for their own rapid development framework – front-end scaffolding

Build a suitable for their own rapid development framework – front-end login and routing modular