个人开发的组件,不同于市面上那种拖拽生成的前端控件的组件form.making.link/#/zh-CN/ 说实话,作为前端开发,用这个写页面遇到要塞逻辑的就一脸懵逼,而我所开发的低代码,是完全结合前端手写代码的方式为出发点做的,甚至你可以不用这个低代码,依然可以用其他所有的组件,因为低代码是可选的,组件与低代码完全解耦
前后端约定:增、删、改、查、导入、导出接口规范,不符合规范直接打回。
下面说的单表是对应数据库一张表,多表是指数据库一对多,即一个单头,若干个订单明细这样的结构。
先介绍表单配置页
列表查询没啥好说
表单:
表格那里是单个字段的具体配置项,不用介绍也能看懂吧
结合页面配置具体应用
先上页面效果,上面两张图开始是手写代码的,后面也改成配置化了,这就是效果图。当然怕有人乱删页面配置的数据,我在代码里面存了一份json数据
品牌管理页面的效果图
来看代码量
// 列表文件
<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里面根据不同字段渲染不同的控件,思路是简单的,难得逻辑在于选择器那里。
选择器介绍(组件化、可选配置化)
功能介绍:这个也是该框架最复杂的逻辑了,本人也不是一次性写出来的,不断的优化才有了今天的效果。
- 滚动分页加载
- 远程搜索
- 快捷新增,打开新增表单
- 弹窗表格选择器,给用户展示更多字段,可以更准确的选择数据
- 单选、多选模式
- 数据回显,兼容单选、多选、以及跨页勾选数据的正确回显,思路是回显的时候直接查询一次接口把当前所选的全部查出来,选择器绑定的key展示的name,所以要查询数据才能正确展示name(这里在表格用的时候,多条数据回显的时候会查询多次接口,下面做了优化)
- 更强的数据回显:数据库存了key,但是要求后端在page和get接口返回数据的时候,把对应的name查回来,这样前端回显就不需要查询接口,也就是说我打开表单回显的时候,只需要调一次get查询表单数据即可
- 以上功能,下拉和弹窗均完全匹配,完全联动的,不用多想
以机器中心选择器为例子展示联动效果
多表(复杂表单)
展示
表格里面怎么用?
好了,来活了,我要搬砖了,s-cell、s-table-column、s-select等等还好多组件没介绍
expand-table