内容摘要:

  • 需求分析
  • 定义 interface
  • 定义 json 文件
  • 定义列表控件的 props
  • 基于 el-table 封装,实现依赖 json 渲染
  • 实现内置功能:选择行(单选、多选),格式化、锁定等。
  • 使用 slot 实现自定义扩展
  • 做个工具维护 json 文件(下篇介绍)

管理后台里面,列表是一个常用的功能,UI库提供了列表组件和分页组件实现功能。虽然功能强大,也很灵活,只是还不能称为低代码,不过没关系,我们可以写点代码让UI库变为摸鱼神器!

本篇介绍列表的设计思路和封装方式。

需求分析

如果基于原生HTML来实现显示数据列表的功能的话,那么需考虑如何创建 table,如何设置css等。

如果直接使用UI库的话,那么可以简单很多,只需要设置各种属性,然后绑定数据即可。

以 el-table 为例:

  <el-table
:data="tableData"
border
stripe
style="width: 100%"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>

设置好属性、记录集合,然后设置列(el-table-column)即可。

这样一个列表就搞定了,再加上 el-pagination 分页组件,编写一些代码即可实现分页的功能。

如果只是一个列表的话,这种方式没啥问题,但是管理后台项目,往往需要n个列表,而每个列表都大同小异,如果要一个一个手撸出来,那就有点麻烦了。

那么如何解决呢?我们可以参考低代码,基于 el-talbe 封装一个列表控件,

实现依赖 json 动态渲染列表,同时支持自定义扩展。

定义 interface

最近开始学习 Typescript,发现了一个现象,如果可以先定义好类型,那么代码就可以更清晰的展现出来。

另外 Vue3 的最新文档,也采用了通过 interface 来介绍API功能的方式,所以我们也可以借鉴一下。

依据 el-table 的属性,定义列表控件属性的 interface。

Vue3 的 props 有一套约束方式,这个似乎和TS的方式有点冲突,没想出了更好的方法(option API 和 script setup两种定义props的方式,都有不足 ),所以只好做两个 interface,一个用于定义组件的 props ,一个用于取值。

  • IGridPropsComp:定义组件的 props
/**
* 列表控件的属性的描述,基于el-table
*/
export interface IGridPropsComp {
/**
* 模块ID,number | string
*/
moduleId: IPropsValidation,
/**
* 主键字段的名称 String,对应 row-key
*/
idName: IPropsValidation,
/**
* table的高度, Number
*/
height: IPropsValidation,
/**
* 列(字段)显示的顺序 Array<number|string>
*/
colOrder: IPropsValidation,
/**
* 斑马纹,Boolean
*/
stripe: IPropsValidation,
/**
* 纵向边框,Boolean
*/
border: IPropsValidation,
/**
* 列的宽度是否自撑开,Boolean
*/
fit: IPropsValidation,
/**
* 要高亮当前行,Boolean
*/
highlightCurrentRow: IPropsValidation,
/**
* 锁定的列数 Number,设置到 el-table-column 的 fixed
*/
fixedIndex: IPropsValidation,
/**
* table的列的 IGridItem
* * id: number | string,
* * colName: string, 字段名称
* * label: string, 列的标签、标题
* * width: number, 列的宽度
* * align: EAlign, 内容对齐方式
* * headerAlign: EAlign 列标题对齐方式
*/
itemMeta: IPropsValidation, //
/**
* 记录选择的行:IGridSelection
* * dataId: '', 单选ID number 、string
* * row: {}, 单选的数据对象 {}
* * dataIds: [], 多选ID []
* * rows: [] 多选的数据对象 []
*/
selection: IPropsValidation, /**
* 绑定的数据 Array, 对应 data
*/
dataList: IPropsValidation // 其他扩展属性
[propName: string]: IPropsValidation }
  • moduleId:模块ID,一个模块菜单只能有一个列表,菜单可以嵌套。
  • itemMeta:列的属性集合,记录列表的列的属性。
  • selection:记录列表的单选、多选的 row。
  • dataList:显示的数据,对应 el-table 的 data
  • 其他:对应 el-table 的属性

IGridPropsComp 的作用是,约束列表控件需要设置哪些属性,属性的具体类型,就无法在这里约束了。

  • IPropsValidation (不知道vue内部有没有这样的 interface)
/**
* vue 的 props 的验证的类型约束
*/
export interface IPropsValidation {
/**
* 属性的类型,比较灵活,可以是 String、Number 等,也可以是数组、class等
*/
type: Array<any> | any,
/**
* 是否必须传递属性
*/
required?: boolean,
/**
* 自定义类型校验函数(箭头函数),value:属性值
*/
validator?: (value: any) => boolean,
/**
* 默认值,可以是值,也可以是函数(箭头函数)
*/
default?: any
}

取 props 用的 interface

IGridPropsComp 无法约束属性的具体类型,所以只好再做一个 interface。

  • IGridProps
/**
* 列表控件的属性的类型,基于el-table
*/
export interface IGridProps {
/**
* 模块ID,number | string
*/
moduleId: number | string,
/**
* 主键字段的名称 String,对应 row-key
*/
idName: String,
/**
* table的高度, Number
*/
height: number,
/**
* 列(字段)显示的顺序 Array<number|string>
*/
colOrder: Array<number|string>,
/**
* 斑马纹,Boolean
*/
stripe: boolean,
/**
* 纵向边框,Boolean
*/
border: boolean,
/**
* 列的宽度是否自撑开,Boolean
*/
fit: boolean,
/**
* 要高亮当前行,Boolean
*/
highlightCurrentRow: boolean,
/**
* 锁定的列数 Number,设置到 el-table-column 的 fixed
*/
fixedIndex: number,
/**
* table的列的 Object< IGridItem >
* * id: number | string,
* * colName: string, 字段名称
* * label: string, 列的标签、标题
* * width: number, 列的宽度
* * align: EAlign, 内容对齐方式
* * headerAlign: EAlign 列标题对齐方式
*/
itemMeta: {
[key:string | number]: IGridItem
}, //
/**
* 选择行的情况:IGridSelection
* * dataId: '', 单选ID number 、string
* * row: {}, 单选的数据对象 {}
* * dataIds: [], 多选ID []
* * rows: [] 多选的数据对象 []
*/
selection: IGridSelection, /**
* 绑定的数据 Array, 对应 data
*/
dataList: Array<any> // 其他扩展属性
[propName: string]: any
}

对比一下就会发现,属性的类型不一样。因为定义 props 需要使用一套特定的对象格式,而使用 props 的时候需要的是属性自己的类型。

理想情况下,应该可以在 script setup 里面,引入外部文件 定义的 interface ,然后设置给组件的 props,但是到目前为止还不支持,只能在( script setup方式的)组件内部定义 props。希望早日支持,支持了就不会这么纠结和痛苦了。

依据 el-table-column 定义列属性的 interface。

  • IGridItem:列表里面列的属性
/**
* 列的属性,基于 el-table-column
*/
export interface IGridItem {
/**
* 字段ID、列ID
*/
id: number | string,
/**
* 字段名称
*/
colName: string,
/**
* 列的标签、标题
*/
label: string,
/**
* 列的宽度
*/
width: number,
/**
* 内容对齐方式 EAlign
*/
align: EAlign,
/**
* 列标题对齐方式
*/
headerAlign: EAlign, // 其他扩展属性
[propName: string]: any
}

还是需要扩展属性的,因为这里只是列出来目前需要的属性,el-table-column 的其他属性、方法还有很多,而且以后也可能会新增。

这个属性不是直接设置给组件的 props,所以不用定义两套了。

对齐方式的枚举

枚举可以理解为常量,定义之后可以避免低级错误,避免手滑。

  • EAlign
export const enum EAlign {
left = 'left',
center = 'center',
right = 'right'
}

选择记录的 interface。

列表可以单选也可以多选,el-table 在默认情况下似乎是二选一,觉得有点不方便,为啥不能都要?

  • 单选:鼠标单一任意一行就是单选;(清空其他已选项)
  • 多选:单击第一列的(多个)复选框,就是多选;

这样用户就可以愉快的想单选就单选,想多选就多选了。

  • IGridSelection
/**
* 列表里选择的数据
*/
export interface IGridSelection {
/**
* 单选ID number 、string
*/
dataId: number | string,
/**
* 单选的数据对象 {}
*/
row: any,
/**
* 多选ID []
*/
dataIds: Array<number | string>,
/**
* 多选的数据对象 []
*/
rows: Array<any>
}

其实我觉得只记录ID即可,不过既然 el-talble 提供的 row,那么还是都记录下来吧。

定义 json 文件

接口定义好之后,我们可以依据 interface 编写 json 文件:

{
"moduleId": 142,
"height": 400,
"idName": "ID",
"colOrder": [
90, 100, 101
],
"stripe": true,
"border": true,
"fit": true,
"highlightCurrentRow": true,
"highlight-current-row": true,
"itemMeta": {
"90": {
"id": 90,
"colName": "kind",
"label": "分类",
"width": 140,
"title": "分类",
"align": "center",
"header-align": "center"
},
"100": {
"id": 100,
"colName": "area",
"label": "多行文本",
"width": 140,
"title": "多行文本",
"align": "center",
"header-align": "center"
},
"101": {
"id": 101,
"colName": "text",
"label": "文本",
"width": 140,
"title": "文本",
"align": "center",
"header-align": "center"
}
}
}
  • 为什么直接设置 json 文件而不是 js 对象呢?

    因为对象会比较长,如果是代码形式的话,那还不如直接使用UI库组件来的方便呢。

  • 你可能又会问了,既然直接用 json文件,为啥还要设计 interface 呢?

    当然是为了明确各种类型,interface 可以当做文档使用,另外封装UI库的组件的时候,也可以用到这些 interface。使用列表控件的时候也可以使用这些 interface。

其实json文件不用手动编写,而是通过工具来编写和维护。

定义列表控件的 props

封装组件之前需要先定义一下组件需要的 props:

  • props-grid.ts
import type { PropType } from 'vue'

import type {
IGridProps,
IGridItem,
IGridSelection
} from '../types/50-grid' /**
* 表单控件需要的属性propsForm
*/
export const gridProps: IGridProps = {
/**
* 模块ID,number | string
*/
moduleId: {
type: Number,
required: true
},
/**
* 主键字段的名称
*/
idName: {
type: String,
default: 'ID'
},
/**
* 字段显示的顺序
*/
colOrder: {
type: Array as PropType<Array<number | string>>,
default: () => []
},
/**
* 锁定的列数
*/
fixedIndex: {
type: Number,
default: 0
},
/**
* table的列的 meta
*/
itemMeta: {
type: Object as PropType<{
[key:string | number]: IGridItem
}>
},
/**
* 选择的情况 IGridSelection
*/
selection: {
type: Object as PropType<IGridSelection>,
default: () => {
return {
dataId: '', // 单选ID number 、string
row: {}, // 单选的数据对象 {}
dataIds: [], // 多选ID []
rows: [] // 多选的数据对象 []
}
}
},
/**
* 绑定的数据
*/
dataList: {
type: Array as PropType<Array<any>>,
default: () => []
},
其他略。。。
}

按照 Option API 的方式设置 props 的定义,这样便于共用属性的定义(好吧似乎也没有需要共用的地方,不过我还是喜欢把 props 的定义写在一个单独的文件里)。

封装列表控件

定义好 json 、props之后,我们基于 el-table 封装列表控件:

  • template 模板
  <el-table
ref="gridRef"
v-bind="$attrs"
:data="dataList"
:height="height"
:stripe="stripe"
:border="border"
:fit="fit"
:highlight-current-row="highlightCurrentRow"
:row-key="idName"
@selection-change="selectionChange"
@current-change="currentChange"
>
<!--多选框,实现多选功能-->
<el-table-column
type="selection"
width="55"
align="center"
header-align="center"
@click="clickCheck"
>
</el-table-column>
<!--依据 json 渲染的字段列表-->
<el-table-column
v-for="(id, index) in colOrder"
:key="'grid_list_' + index + '_' + id"
v-bind="itemMeta[id]"
:column-key="'col_' + id"
:fixed="index < fixedIndex"
:prop="itemMeta[id].colName"
>
</el-table-column>
</el-table>

设置 type="selection"列,实现多选的功能。

使用 v-for 的方式,遍历出动态列。

设置 :fixed="index < fixedIndex",实现锁定左面列的功能。

  • js 代码
  import { defineComponent, ref } from 'vue'
// 列表控件的属性
import { gridProps } from '../map' /**
* 普通列表控件
*/
export default defineComponent({
name: 'nf-elp-grid-list',
inheritAttrs: false,
props: {
...gridProps // 解构共用属性
},
setup (props, ctx) {
// 获取 el-table
const gridRef = ref(null) return {
gridRef
}
}
})

把 props 的定义放在单独的 ts文件 里面,组件内部的代码就可以简洁很多。

实现内置功能

可以按照自己的喜好,设置一些内部功能,比如单选/多选的功能,格式化的功能等。

  • 定义控制函数 controller.ts
import type { ElTable } from 'element-plus'

// 列表控件的属性
import type { IGridProps } from '../map' export interface IRow {
[key: string | number]: any
} /**
* 列表的单选和多选的事件
* @param props 列表组件的 props
* @param gridRef el-table 的 $ref
*/
export default function choiceManage<T extends IGridProps, V extends typeof ElTable>(props: T, gridRef: V) {
// 是否单选触发
let isCurrenting = false
// 是否多选触发
let isMoring = false // 单选
const currentChange = (row: IRow) => {
if (isMoring) return // 多选代码触发
if (!row) return // 清空 if (gridRef.value) {
isCurrenting = true
gridRef.value.clearSelection() // 清空多选
gridRef.value.toggleRowSelection(row) // 设置复选框
gridRef.value.setCurrentRow(row) // 设置单选
// 记录
props.selection.dataId = row[props.idName]
props.selection.dataIds = [ row[props.idName] ]
props.selection.row = row
props.selection.rows = [ row ] isCurrenting = false
}
} // 多选
const selectionChange = (rows: Array<IRow>) => {
if (isCurrenting) return
// 记录
if (typeof props.selection.dataIds === 'undefined') {
props.selection.dataIds = []
}
props.selection.dataIds.length = 0 // 清空
// 设置多选
rows.forEach((item: IRow) => {
if (typeof item !== 'undefined' && item !== null) {
props.selection.dataIds.push(item[props.idName])
}
})
props.selection.rows = rows
// 设置单选
switch (rows.length) {
case 0:
// 清掉单选
gridRef.value.setCurrentRow()
props.selection.dataId = ''
props.selection.row = {}
break
case 1:
isMoring = true
// 设置新单选
gridRef.value.setCurrentRow(rows[0])
isMoring = false
props.selection.row = rows[0]
props.selection.dataId = rows[0][props.idName]
break
default:
// 去掉单选
gridRef.value.setCurrentRow()
props.selection.row = rows[rows.length - 1]
props.selection.dataId = props.selection.row[props.idName]
}
} return {
currentChange, // 单选
selectionChange // 多选
}
}
  • 列表控件的 setup 里调用
setup (props, ctx) {
// 获取 el-table
const gridRef = ref<InstanceType<typeof ElTable>>() // 列表选项的事件
const {
currentChange, // 单选
selectionChange // 多选
} = choiceManage(props, gridRef) return {
selectionChange, // 多选
currentChange, // 单选
gridRef // table 的 dom
}
}

这里有一个“度”的问题:

  • el-table 完全通过 slot 的方式实现各种功能,这种方法的特点是:非常灵活,可以各种组合;缺点是比较繁琐。

    而我们需要寻找到一个适合的“折中点”,显然这个折中点很难统一,这也是过渡封装带来的问题。

  • 不能遇到新的需求,就增加内部功能,这样就陷入了《人月神话》里说的“焦油坑”,进去了就很难出来。

这也是低代码被诟病的因素。

支持扩展

那么如何找到这个折中点呢?可以按照 “开闭原则”,按照不同的需求,设置多个不同功能的列表控件,使用 slot 实现扩展功能。或者干脆改为直接使用 el-table 的方式。(要灵活,不要一刀切)

比如简单需求,不需要扩展功能的情况,设置一个基础列表控件:nf-grid。

需要扩展列的情况,设置一个可以扩展的列表控件:nf-grid-slot。

如果需要多表头、树形数据等需求,可以设置一个新的列表控件,不过需要先想想,是不是直接用 el-table 更方便。

要不要新增一个控件,不要惯性思维,而要多方面全局考虑。

这里介绍一下支持 slot 扩展的列表控件的封装方式:

  <el-table
ref="gridDom"
v-bind="$attrs"
size="mini"
style="width: 100%"
:data="dataList"
:height="height"
:stripe="stripe"
:border="border"
:fit="fit"
:highlight-current-row="highlightCurrentRow"
:current-row-key="idName"
:row-key="idName"
@selection-change="selectionChange"
@current-change="currentChange"
>
<!--显示选择框-->
<el-table-column
type="selection"
width="55">
</el-table-column>
<!--显示字段列表-->
<template
v-for="(id, index) in colOrder"
:key="'grid_list_' + index + '_' + id"
>
<!--检查插槽里是否包含 字段名,作为判断依据-->
<!--不带插槽的列-->
<el-table-column
v-if="!(slotsKey.includes(itemMeta[id].colName))"
:fixed="index < fixedIndex"
v-bind="itemMeta[id]"
:prop="itemMeta[id].colName"
:min-width="50"
>
</el-table-column>
<!--带插槽的列-->
<el-table-column v-else
v-bind="itemMeta[id]"
>
<template #default="scope">
<!--读取外部插槽内容,并且传递 scope -->
<slot :name="itemMeta[id].colName" v-bind="scope"></slot>
</template>
</el-table-column>
</template>
</el-table>

模板部分,首先判断一下是否需要使用 slot,做一个分支。

需要使用 slot 的列,通过 <template #default="scope"> 设置 slot。

  • 代码部分
  import { defineComponent, ref } from 'vue'
// 表单控件的属性
import { gridProps } from '../map'
import choiceManage from './controller' export default defineComponent({
name: 'nf-elp-grid-slot',
inheritAttrs: false,
props: {
...gridProps
},
setup (props, ctx) {
// 记录插槽 的 名称
const slots = ctx.slots
const slotsKey = Object.keys(slots) // 列表选项的事件
const {
currentChange, // 单选
selectionChange // 多选
} = choiceManage(props, gridRef) return {
slotsKey,
selectionChange, // 多选
currentChange // 单选
}
}
})

一般列表的使用方法

封装之后,使用起来就很方便了,引入 json文件,设置属性即可。

  • template
  <nf-grid
v-bind="gridMeta"
:dataList="dataList"
:selection="selection"
size="small"
/>

是不是简单多了。

  • 代码部分
  import { defineComponent, reactive } from 'vue'
import { nfGrid, createDataList } from '../../../../lib-elp/main'
import _gridMeta from '../../grid/grid.json'
import _formMeta from '../../form/form.json' export default defineComponent({
name: 'nf-elp-grid-page',
components: { nfGrid },
setup(props) {
// 不需要动态改变的话,可以不使用 reactive。
const gridMeta = reactive(_gridMeta)
// 设置选择的行
const selection = reactive({
dataId: '', // 单选ID number 、string
row: {}, // 单选的数据对象 {}
dataIds: [], // 多选ID []
rows: [] // 多选的数据对象 []
})
// 设置记录集。
const dataList = reactive(_dataList) return {
dataList,
selection,
gridMeta
}
}
})

控件可以做成全局组件的形式。

  • 看看效果

扩展列表的使用方法

首先还是依据 json 渲染列表,然后根据需要设置插槽即可,设置插槽后会替换默认的列。

  • template
  可以使用 slot 自定义扩展列 <br>
<!--表单控件-->
<nf-grid
v-grid-drag="gridMeta"
v-bind="gridMeta"
:dataList="dataList"
:selection="selection"
size="small"
>
<!--普通字段,用字段名作为插槽的名称-->
<template #text="scope">
<div style="display: flex; align-items: center">
<el-icon><timer /></el-icon>
<span style="margin-left: 10px">扩展:{{ scope.row.text }}</span>
</div>
</template>
<!--普通字段-->
<template #week="scope">
<span style="margin-left: 10px">{{ scope.row.week.replace('-w','年 第') + '周' }}</span>
</template>
<!--操作按钮-->
<template #option="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</nf-grid>

通过 slot 扩展列,可以按照 Table-column 的匿名插槽的方式进行设置。

列的先后顺序还是由 colOrder 控制,和插槽的先后顺序无关。

  • 代码部分
  import { defineComponent, reactive } from 'vue'
// 使用 图标
import { Timer } from '@element-plus/icons-vue'
import { nfGridSlot, createDataList } from '../../../../lib-elp/main'
import _gridMeta from '../../grid/grid.json'
import _formMeta from '../../form/form.json' import { EAlign } from '../../../../lib/types/enum'
import type { IGridSelection, IGridItem } from '../../../../lib/types/50-grid' export default defineComponent({
name: 'nf-elp-grid-slot-page',
components: {
Timer,
nfGrid: nfGridSlot
},
props: {
moduleID: { // 模块ID
type: [Number, String],
default: 1
}
},
setup(props) {
const gridMeta = reactive(_gridMeta) // 设置列的先后顺序和是否显示
gridMeta.colOrder = [90, 100, 101, 102, 105, 113, 115, 116, 120,121,150, 2000] // 设置一个操作按钮列
const optionCol: IGridItem = {
id: 2000,
colName: "option",
label: "操作",
width: 180,
fixed: EAlign.right,
align: EAlign.center, // 使用枚举
headerAlign: EAlign.center
} gridMeta.itemMeta['2000'] = optionCol // 设置操作列,也可以直接在json文件里设置。
const dataList = reactive(_dataList) const handleEdit = (index: number, row: any) => {
console.log(index, row)
}
const handleDelete = (index: number, row: any) => {
console.log(index, row)
} return {
handleEdit,
handleDelete,
dataList,
gridMeta
}
}
})

使用字段名称作为插槽的名称,可以把任意字段变成插槽的形式。

如果要添加操作按钮这类的列,可以给 itemMeta 添加对应的列属性。

  • 看看效果:

管理 json

其实,前面介绍的那些大家可能都会想到,也许早就实践过了,然后发现虽然看着挺好,但是其实没有解决根本问题!只是把 template 里的问题转移到 json 里面。

虽然不需要设置模板,但是需要设置 json,还不是一样,有啥本质区别吗?

其实不一样的,管理 json 的难度明显比管理模板要简单得多。

比如我们可以做一个维护 json 的小工具:

  • 首先从数据库文档生成基础的 json(毛坯房);
  • 然后使用可视化+拖拽的方式设置格子细节(精装修)。

这样就可以很方便的维护 json 了。具体实现方式,将在下一篇再介绍。

源码

https://gitee.com/naturefw-code/nf-rollup-ui-controller

https://gitee.com/naturefw-code/nf-rollup-ui-element-plus

【摸鱼神器】UI库秒变LowCode工具——列表篇(一)设计与实现的更多相关文章

  1. 【摸鱼神器】UI库秒变LowCode工具——列表篇(二)维护json的小工具

    上一篇介绍了一下如何实现一个可以依赖 json 渲染的列表控件,既然需要 json 文件,那么要如何维护这个 json 文件就成了重点,如果没有好的维护方案的话,那么还不如直接用UI库. 所以需要我们 ...

  2. 【摸鱼神器】UI库秒变低代码工具——表单篇(二)子控件

    上一篇介绍了表单控件,这一篇介绍一下表单里面的各种子控件的封装方式. 主要内容 需求分析 子控件的分类 子控件属性的分类 定义 interface. 定义子控件的的 props. 定义 json 文件 ...

  3. 【摸鱼神器】UI库秒变低代码工具——表单篇(一)设计

    前面说了列表的低代码化的方法,本篇介绍一下表单的低代码化. 内容摘要 需求分析. 定义 interface. 定义表单控件的 props. 定义 json 文件. 基于 el-form 封装,实现依赖 ...

  4. Thief-Book 上班摸鱼神器

    Thief-Book 上班摸鱼神器 介绍 Thief-Book 是一款真正的摸鱼神器,可以更加隐秘性大胆的看小说. 隐蔽性 自定义透明背景,随意调整大小,完美融入各种软件界面 快捷性 三个快捷键,实现 ...

  5. vscode插件(摸鱼神器-小霸王游戏机

    vscode插件(摸鱼神器-小霸王游戏机 步骤 vscode扩展搜索小霸王,点击下载即可. 使用 默认有一个demo小游戏,即超级玛丽. 本地仓库 可以通过local菜单上的添加按钮添加本地nes r ...

  6. vue-cli3.0结合lib-flexible、px2rem实现移动端适配,完美解决第三方ui库样式变小问题

    公司最近做的一个移动端项目从搭框架到前端开发由我独立完成,以前做移动端适配用的媒体查询,这次想用点别的适配方案,然后就采用了vue-cli3.0结合lib-flexible.px2rem实现移动端适配 ...

  7. 【转】让Chrome化身成为摸鱼神器,利用Chorme运行布卡漫画以及其他安卓APK应用教程

    下周就是十一了,无论是学生党还是工作党,大家的大概都会有点心不在焉,为了让大家更好的心不在焉,更好的在十一前最后一周愉快的摸鱼,今天就写一个如何让Chrome(google浏览器)运行安卓APK应用的 ...

  8. 【摸鱼神器】UCode Cms管理系统 内置超好用的代码生成器 解决多表连接痛点

    一.序言 UCode Cms管理系统是面向企业级应用软件开发的脚手架.当前版本1.3.4.快速体验: git clone https://gitee.com/decsa/demo-cms.git (一 ...

  9. 【摸鱼神器】基于SSM风格的Java源代码生成器 单表生成 一对一、一对多、多对多连接查询生成

    一.序言 UCode Cms 是一款Maven版的Java源代码生成器,是快速构建项目的利器.代码生成器模块属于可拆卸模块,即按需引入.代码生成器生成SSM(Spring.SpringBoot.Myb ...

随机推荐

  1. 关于data自定义属性

    新的HTML5标准允许你在普通的元素标签里,嵌入类似data-*的属性,来实现一些简单数据的存取.它的数量不受限制,并且也能由JavaScript动态修改,也支持CSS选择器进行样式设置.这使得dat ...

  2. Java报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.sirifeng.babytun.dao.GoodsDAO.findById

    前言 最近学vue学得差不多了,想来搭个项目实战一下,结果刚开始搭建SSM框架的时候就来到了我们最喜欢的debug环节 org.apache.ibatis.binding.BindingExcepti ...

  3. python函数基础算法简介

    一.多层语法糖本质 """ 语法糖会将紧挨着的被装饰对象名字当参数自动传入装饰器函数中""" def outter(func_name): ...

  4. js的split函数

    split() 方法用于把一个字符串分割成字符串数组. separator 必需.字符串或正则表达式,从该参数指定的地方分割 stringObject. ***如果把空字符串 ("" ...

  5. Java学习day18

    学习了三种简单的布局结构 做了一个简单的多按键窗口 Panel无法单独存在而显示出来,需要借助一个容器,例如Frame 明天学习输入框监听和画笔

  6. Vue+element搭建后台管理系统-二、安装插件

    我们继续上一章的内容,上一章讲到我们已经能将项目成功跑起来了,那么我们接下来把项目必用的东西完善一下. 一.安装elementUI 终于到了我们的男二了,继续在VSCode中新建一个终端,然后通过这个 ...

  7. HTML续集

    计算机中PC:电脑 移动端:智能手机/智能电脑 html:超文本标记语言 图片标签<img src=" "> 图片的格式类型都有哪些? jpg,peg,gif(动图) ...

  8. Rancher部署PostgreSQL容器

    1.打开工作负载,选择部署服务 2.选择合适的PostgreSQL镜像 镜像地址https://registry.hub.docker.com/_/postgres,也可使用公司内部镜像库 网络模式选 ...

  9. springmvc05-json交互处理

    什么是json: JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛. *采用完全独立于编程语言的文本格式来存储和表示数据 ...

  10. golang /js index 转换excel字母表头

    Golang 1 package main 2 3 import "fmt" 4 5 func main() { 6 var Letters = []string{"A& ...