简单易用的低代码平台+人性化的下拉选择器(效果演示)

74 阅读4分钟

个人开发的组件,不同于市面上那种拖拽生成的前端控件的组件form.making.link/#/zh-CN/ 说实话,作为前端开发,用这个写页面遇到要塞逻辑的就一脸懵逼,而我所开发的低代码,是完全结合前端手写代码的方式为出发点做的,甚至你可以不用这个低代码,依然可以用其他所有的组件,因为低代码是可选的,组件与低代码完全解耦

前后端约定:增、删、改、查、导入、导出接口规范,不符合规范直接打回。

下面说的单表是对应数据库一张表,多表是指数据库一对多,即一个单头,若干个订单明细这样的结构。

先介绍表单配置页

列表查询没啥好说 image.png 表单: 表格那里是单个字段的具体配置项,不用介绍也能看懂吧 image.png

结合页面配置具体应用

先上页面效果,上面两张图开始是手写代码的,后面也改成配置化了,这就是效果图。当然怕有人乱删页面配置的数据,我在代码里面存了一份json数据

image.png

品牌管理页面的效果图

image.png 来看代码量

image.png

// 列表文件
<template>
  <div class="app-container">
    <div class="mb8">
      <el-button
        v-hasPermi="['basic:brand:create']"
        type="primary"
        plain
        icon="el-icon-plus"
        size="mini"
        @click="handleAdd"
      >
        {{ $t("xin-zeng") }}
      </el-button>
      <el-button
        v-hasPermi="['basic:brand:update']"
        type="primary"
        plain
        icon="el-icon-edit"
        size="mini"
        :disabled="!currentRow.id"
        @click="handleEdit(currentRow)"
      >
        {{ $t("bian-ji") }}
      </el-button>
      <el-button
        v-hasPermi="['basic:brand:export']"
        type="primary"
        plain
        icon="el-icon-download"
        size="mini"
        :loading="exportLoading"
        @click="handleExport()"
      >
        {{ $t("dao-chu") }}
      </el-button>
      <el-button
        v-hasPermi="['basic:brand:delete']"
        plain
        icon="el-icon-delete"
        size="mini"
        :disabled="!selectedRows.length"
        @click="handleDeleteBatch"
      >
        {{ $t('pi-liang-shan-chu') }}
      </el-button>
      <el-button
        icon="el-icon-refresh"
        size="mini"
        @click="handleReset"
      >
        {{ $t("zhong-zhi") }}
      </el-button>
      <right-toolbar
        :columns.sync="tableConfig"
        :table-height.sync="tableHeight"
        @queryTable="loadData"
      />
    </div>

    <el-alert
      :type="selectedRows.length ? 'success' : 'info'"
      class="m-b-10"
      :closable="false"
    >
      <template slot="title">
        <span>{{ $t('yi-xuan') }}:{{ selectedRows.length }}</span>
        <a
          class="m-l-10"
          @click="clearAll"
        >
          {{ $t('qing-kong') }}
        </a>
      </template>
    </el-alert>

    <el-table
      v-if="tableHeight"
      ref="table"
      v-loading="loading"
      row-key="id"
      :data="dataSource"
      highlight-current-row
      class="fixed-height-table table"
      :height="tableHeight"
      border
      @selection-change="selectionChange"
      @current-change="tableRadioChange"
      @header-dragend="headerDragend"
      @sort-change="sortChange"
    >
      <el-table-column
        type="selection"
        width="55"
        reserve-selection
      />
      <s-table-column
        v-for="item in tableConfig"
        :key="item.dataIndex"
        :config="item"
        :query="query"
        @search="handleSearch"
        @reset="handleReset"
      >
        <template v-slot:name="{scope}">
          <a
            class="link-type"
            @click="handleDetail(scope.row)"
          >
            {{ scope.row.name }}
          </a>
        </template>

        <template
          v-if="$store.state.user.permissions.includes('basic:brand:update-status')"
          v-slot:status="{scope}"
        >
          <el-switch
            v-model="scope.row.status"
            :active-value="0"
            :inactive-value="1"
            @change="changeStatus(scope.row)"
          />
        </template>
      </s-table-column>
    </el-table>

    <pagination
      v-show="pagination.total > 0"
      :total="pagination.total"
      :page.sync="pagination.pageNo"
      :page-sizes="pagination.pageSizes"
      :limit.sync="pagination.pageSize"
      @pagination="loadData"
    />

    <Create
      ref="Create"
      :url="url"
      :config="formConfig"
      @refresh="refresh"
    />
  </div>
</template>

<script>
// 设备品牌
import listMixin from '@/s-mixins/index'
import Create from './components/create'

export default {
  name: 'Brand',

  components: {
    Create,
  },

  mixins: [listMixin],

  data () {
    return {
      pageCode: 'equipmentBrand',

      url: {
        list: '/basic/brand/page',
        add: '/basic/brand/create',
        edit: '/basic/brand/update',
        detail: '/basic/brand/get',
        delete: '/basic/brand/delete',
        export: '/basic/brand/export',
        updateStatus: '/basic/brand/update-status',
      },
    }
  },

  computed: {
    config () {
      const config = this.$getConfigByCode(this.pageCode)
      return config.pageFieldList
    }
  },
}
</script>

// 表单文件 新增、编辑、详情
<template>
  <el-drawer
    :title="title"
    size="700px"
    :visible.sync="visible"
    direction="rtl"
    destroy-on-close
    append-to-body
    :wrapper-closable="type === 'detail'"
    @close="handleBack"
  >
    <header-btn
      :disabled="type === 'detail'"
      :loading="loading"
      @back="handleBack"
      @submit="handleSave"
    />

    <el-form
      ref="form"
      v-loading="isInit"
      :model="form"
      label-width="120px"
      :class="{'detail-form': type ==='detail'}"
    >
      <s-row :span="24">
        <s-cell
          v-for="item in formConfig"
          :key="item.dataIndex"
          :config="item"
          :form="form"
          :detail="type === 'detail'"
          :span="item.span"
        >
          <!-- 对特定字段渲染特殊逻辑的控件,也就是说其他没特殊逻辑的,你都不用写代码 -->
          <!-- 使用插槽覆盖标准控件 -->
          <template slot="name">
            <el-input
              v-if="type !== 'detail'"
              v-model="form[item.dataIndex]"
              :placeholder="$t('qing-shu-ru')"
              :disabled="item.disabled"
              class="w-full"
              size="small"
              clearable
              @change="form[item.dataIndex] = form[item.dataIndex].toUpperCase()"
            />
          </template>
        </s-cell>
      </s-row>
    </el-form>
  </el-drawer>
</template>

<script>
import createMixin from '@/s-mixins/create'
import PageConfig from '@/utils/pageConfig/index'

export default {
  mixins: [createMixin],

  props: {
    // 高阶选择器有新增功能,请确保url和index中的完全一致【TIPS:没有高阶选择器,可以删除这块props】
    url: {
      type: Object,
      default: () => ({
        list: '/basic/brand/page',
        add: '/basic/brand/create',
        edit: '/basic/brand/update',
        detail: '/basic/brand/get',
        delete: '/basic/brand/delete',
        export: '/basic/brand/export',
        updateStatus: '/basic/brand/update-status',
      })
    },
  },

  data () {
    return {
      pageName: this.$t('pin-pai')
    }
  },

  computed: {
    formConfig () {
      const config = PageConfig(this.$getConfigByCode('equipmentBrand')?.pageFieldList || []).formConfig
      return config.map(item => {
        if (item.dataIndex === 'name') { // 对特定字段写逻辑,禁止编辑名称
          item.disabled = !!this.form.id
        }
        return item
      })
    },
  },

  methods: {
    Form () {
      return {
        status: 0 // 默认值
      }
    }
  },
}
</script>

你看到了mixins文件对吧,里面写的都是和后端约定好的增删改查的标准代码,不需要展示吧

很简单对吧,看了跟没看一样,因为就是太简单了。。。不就是for循环渲染控件s-cell,s-cell里面根据不同字段渲染不同的控件,思路是简单的,难得逻辑在于选择器那里。

选择器介绍(组件化、可选配置化)

功能介绍:这个也是该框架最复杂的逻辑了,本人也不是一次性写出来的,不断的优化才有了今天的效果。

  1. 滚动分页加载
  2. 远程搜索
  3. 快捷新增,打开新增表单
  4. 弹窗表格选择器,给用户展示更多字段,可以更准确的选择数据
  5. 单选、多选模式
  6. 数据回显,兼容单选、多选、以及跨页勾选数据的正确回显,思路是回显的时候直接查询一次接口把当前所选的全部查出来,选择器绑定的key展示的name,所以要查询数据才能正确展示name(这里在表格用的时候,多条数据回显的时候会查询多次接口,下面做了优化)
  7. 更强的数据回显:数据库存了key,但是要求后端在page和get接口返回数据的时候,把对应的name查回来,这样前端回显就不需要查询接口,也就是说我打开表单回显的时候,只需要调一次get查询表单数据即可
  8. 以上功能,下拉和弹窗均完全匹配,完全联动的,不用多想

image.png

image.png 以机器中心选择器为例子展示联动效果 image.png

image.png

多表(复杂表单)

展示 image.png

image.png 表格里面怎么用? image.png

image.png

好了,来活了,我要搬砖了,s-cell、s-table-column、s-select等等还好多组件没介绍

image.png

expand-table 0912be64b128ce971e843a8daecf21d.png