Admate / 管理后台伴侣
Admate
的目标是以快速简洁的方式开发管理后台页面,
并在此基础上确保灵活可配,杜绝过度封装。
Features
- 同时支持
Vue2
& Vue3
- 不限制UI框架,只要技术栈是
Vue
和 axios
,便可以使用 - 同一系统内,CRUD的请求配置通常是相似的,同一模块内,接口前缀通常是一致的,Admate可以帮助你减少冗余代码
- 提供列表、表单CRUD的贴心封装,你不再操心列表的读取状态、表单的读取和提交状态
- 支持监听筛选参数自动刷新列表(节流控制接口调用频率),也支持手动点击查询按钮筛选列表
- 支持表单是对话框的形式,也支持表单是独立页面的形式
周全的收尾工作,没有“后顾之忧”:
- 关闭表单时,自动将表单绑定的数据恢复至初始状态(不是直接清空)
- 删除当前分页最后一条记录时,自动切换至上一页(如果当前不在第一页)
- 离开页面时,自动终止尚未完成的请求
过往版本的文档
https://www.npmjs.com/package/admate/v/0.5.4
将链接末尾替换为你想要查看的版本号即可
Installation

Vue 3
npm add admate vue@3 axios
Vue 2
npm add admate vue@2 axios @vue/composition-api
初始化
示例代码
使用代码生成器
使用代码生成器生成页面模板
::: warning
目前仅支持 element-ui
:::
Installation
安装Chrome/Edge插件 YApi2Code
,或使用离线版:
:one: 下载离线包后解压
:two: 打开浏览器 扩展程序
,并开启 开发者模式
:three: 点击 加载已解压的扩展程序
,选择解压后的文件夹
Usage
:one: 访问YApi,选中相应模块的 查询列表
接口
:two: 点击浏览器右上角运行插件
:three: 点击 生成代码
,代码将被复制至剪贴板
:four: 创建页面文件 xxx.vue
,粘贴代码
搭配ElementPlus
示例代码
搭配ElementUI
示例代码
搭配AntDesignVue@2
示例代码
搭配AntDesignVue@1
示例代码
搭配Vuetify@3
Vuetify@3 仍处于Alpha阶段
搭配Vuetify@2
示例代码
搭配Quasar@2
Quasar@2尚不支持 Vite
,进度追踪
接口配置
axios
useAdmate({
axios,
})
axiosConfig
useAdmate({
axiosConfig: {
getList: {
method: 'GET',
},
c: {
method: 'POST',
},
r: {
method: 'GET',
},
u: {
method: 'PUT',
},
d: {
method: 'DELETE',
},
enable: {
method: 'PUT',
},
disable: {
method: 'PUT',
},
updateStatus: {
method: 'PUT',
},
},
})
useAdmate({
axiosConfig: {
getList: payload => ({
method: 'GET',
}),
c: payload => ({
method: 'POST',
}),
r: payload => ({
method: 'GET',
}),
u: payload => ({
method: 'PUT',
}),
d: payload => ({
method: 'DELETE',
}),
enable: payload => ({
method: 'PUT',
}),
disable: payload => ({
method: 'PUT',
}),
updateStatus: payload => ({
method: 'PUT',
}),
},
})
urlPrefix
useAdmate({
urlPrefix: '',
})
useAdmate({
urlPrefix: 'somepage',
axiosConfig: {
r: {
url: '/anotherpage/selectOne',
},
}
})
RESTful
如果接口地址需要进行动态拼接
<!-- 示例 -->
const { r, u } = useAdmate({
axiosConfig: {
r: config => ({
url: 'module/' + config.id
}),
}
})
r(form, 'config')
u(form, 'config')
FormData
axios的data默认以 application/json
作为MIME type,如果你需要使用 multipart/form-data
:
给你的axios配置 transformRequest
、headers['Content-Type']
getList
、r
、u
、d
、updateStatus
、enable
、disable
、submitForm
的参数1均支持FormData类型
<!-- 示例:局部配置 -->
<template>
<el-table>
<el-table-column label="操作">
<template #default="{ row: { id } }">
<el-button @click="r(FormData.from({ id }))">查看</el-button>
<el-button @click="u(FormData.from({ id }))">编辑</el-button>
<el-button @click="d(FormData.from({ id }))">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog>
<template #footer>
<el-button @click="submitForm(FormData.from( form.data ))">
确 定
</el-button>
</template>
</el-dialog>
</template>
<script>
import useAdmate from 'admate'
import { jsonToFormData, pickDeepBy } from 'kayran'
export default {
setup: () => {
// 过滤参数并转换为FormData
// 此处示例为将过滤方法绑定到window.FormData,方便其他地方使用
FormData.from = data => jsonToFormData(pickDeepBy(data, (v, k) => ![NaN, null, undefined].includes(v)))
// 直接转换为FormData
//FormData.from = jsonToFormData
return {
...useAdmate({
getListProxy (getList, trigger) {
getList(FormData.from(list.filter))
},
}),
FormData
}
}
}
</script>
列表
筛选参数
list.filter
useAdmate({
list: {
filter: {
[list.pageNumberKey]: 1,
},
pageNumberKey: undefined,
},
})
::: danger
如果你的参数筛选项中包含 el-checkbox
组件,则必须在 data 中声明其初始值,否则将导致无法正确重置(element-ui 的 bug)
:::
<!-- 示例 -->
<template>
<el-form ref="listFilterRef" :model="list.filter" inline>
<el-form-item prop="effect">
<el-checkbox
v-model="list.filter.effect"
label="生效"
border
/>
</el-form-item>
<el-button @click="reset">重置</el-button>
</el-form>
</template>
<script>
import useAdmate from 'admate'
export default {
setup: () => useAdmate({
urlPrefix: '',
list: {
filter: {
effect: null
}
}
}),
}
</script>
触发查询
- 点击专用的
查询
按钮触发
- :x: 操作相对繁琐。
- :x: 列表数据与筛选条件可能是无关的。可能产生“当前的列表数据是否基于筛选项?”的顾虑,导致徒增点击查询按钮的次数。
- :heavy_check_mark: 想要同时设置多个筛选条件时,只调用一次接口,不会造成资源浪费。
useAdmate({
list: {
watchFilter: false,
}
})
- 改变筛选条件后即时触发
- :heavy_check_mark: 操作相对简便。
- :heavy_check_mark: 列表数据与筛选条件即时绑定。
- :heavy_check_mark:
想要同时设置多个筛选条件时,接口会被多次调用,造成资源浪费(Admate已优化)。
useAdmate({
list: {
watchFilter: true,
throttleInterval: 500,
}
})
列表数据
list.data
useAdmate({
list: {
data: [],
dataAt: undefined,
totalAt: undefined,
}
})
读取状态
list.loading
axiosConfig.getList
被调用时值为 true
,否则为 false
<!-- 示例 -->
<template>
<el-table v-loading="list.loading"/>
</template>
<script>
import useAdmate from 'admate'
export default {
setup: () => {
const { list } = useAdmate()
return { list }
},
methods: {
handleTable () {
this.list.loading = true
this.$POST('').finally(() => {
this.list.loading = false
})
}
}
}
</script>
Hook: 查询列表时
getList
:获取列表,在首次进入页面、列表筛选参数改变、单条记录增删查改后会被调用
const {
getList
} = useAdmate()
getList()
getListProxy
:你可以使用 getListProxy
来代理 getList
,以便在getList前后做一些操作,或改变getList的行为
useAdmate({
getListProxy (getList, trigger) {},
})
useAdmate({
getListProxy (getList, trigger) {
if (trigger === 'filterChange') {
listFilterRef.value.validate().then(() => {
getList()
})
} else {
getList()
}
},
})
useAdmate({
getListProxy (getList, trigger) {
getList()
if (['c', 'u', 'd', 'updateStatus', 'enable', 'disable'].includes(caller)) {
currentInstance.value.$message.success('操作成功')
}
},
})
const { list } = useAdmate({
getListProxy (getList, trigger) {
getList().then(response => {
list.data = response.data?.filter(v => !v.disabled)
})
},
})
表单
新增
打开表单,提交时会调用 axiosConfig.c
const { form, openForm } = useAdmate()
form.status = 'c'
openForm()
查看
打开表单,并调用 axiosConfig.r
回显表单内容
const { form, openForm } = useAdmate()
form.status = 'r'
openForm()
payloadAs:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)'cache'
:将payload直接用作表单数据(不调用查询单条记录的接口)
编辑
打开表单,并调用 axiosConfig.r
回显表单内容,提交时会调用 axiosConfig.u
const { form, openForm } = useAdmate()
form.status = 'u'
openForm()
payloadAs:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)'cache'
:将payload直接用作表单数据(不调用查询单条记录的接口)
删除
const {
d
} = useAdmate()
参数2的可选值:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)
启用
const {
enable
} = useAdmate()
参数2的可选值:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)
停用
const {
disable
} = useAdmate()
参数2的可选值:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)
状态变更
const {
updateStatus
} = useAdmate()
参数2的可选值:
'data'
:将payload用作请求配置的 data
参数(请求方式为POST/PATCH/PUT/DELETE时默认)'params'
:将payload用作请求配置的 params
参数(请求方式为GET/HEAD时默认)'config'
:将payload仅用于构建请求配置(详见RESTful)
状态变更的两种方式:
- 调用同一个接口,传参指定新的状态:使用
updateStatus
<el-table-column label="操作" align="center">
<template slot-scope="{row:{id,status}}">
<el-switch
@change="updateStatus({id,status:status^1})"
/>
</template>
</el-table-column>
- 启用和停用是独立的两个接口:使用
enable
和 disable
<el-table-column label="操作" align="center">
<template slot-scope="{row:{id,status}}">
<el-switch
@change="[enable,disable][status]({id})"
/>
</template>
</el-table-column>
表单数据
form.data
useAdmate({
form: {
data: {},
dataAt: undefined,
mergeData: 'deep',
},
})
mergeData:
deep
: 深合并(默认)shallow
: 浅合并false
: 不合并,直接替换
::: tip 为什么默认是深合并?
在vue2中,template不支持 ?.
语法,要在template中判空,代码写起来会非常冗余,通常的做法是在data中声明空对象
比如给form.data提供默认值:
<template>
{{ form.data.a.b.c }}
</template>
<script>
import useAdmate from 'admate'
export default {
setup: () => {
const { form } = useAdmate({
form: {
data: {
a: {
b: {}
}
}
}
})
return { form }
}
}
</script>
如果 axiosConfig.r 的返回值为:
{ a: {} }
如果与默认值浅合并后将得到:
{ a: {} }
—— 默认值中的对象b丢失了,引发空指针异常。
如果与默认值深合并后将得到:
{ a: { b: {} } }
—— 代码正常工作。
:::
表单形态
form.show
: 表单是否打开
可能的值:
form.status
: 表单的形态
可能的值:
form.show为false时,form.status为''
表单默认是对话框的形式,但也支持表单是独立页面 的情况
两种表单形式对比:
- 对话框:体验好,割裂感低,表单的开闭不影响父页面状态
- 独立页面:体验较差,从表单返回父页面时,父页面的状态会丢失,比如列表筛选状态
读取状态
form.loading
axiosConfig.r
被调用时值为 true
,否则为 false
::: warning
不能将该值当作表单回显结束的标志,因为复用列表数据时不会调用axiosConfig.r
:::
<!-- 示例 -->
<template>
<el-dialog>
<el-form v-loading="form.loading"/>
</el-dialog>
</template>
<script>
import useAdmate from 'admate'
export default {
setup: () => {
const { form } = useAdmate()
return { form }
}
}
</script>
提交状态
form.submitting
axiosConfig.c
或 axiosConfig.u
被调用时值为 true
,否则为 false
<!-- 示例 -->
<template>
<el-dialog>
<template #footer>
<el-button :loading="form.submitting">
确 定
</el-button>
</template>
</el-dialog>
</template>
<script>
import useAdmate from 'admate'
export default {
setup: () => {
const { form } = useAdmate()
return { form }
}
}
</script>
Hook: 打开表单时
openForm
:打开表单,函数签名要分情况:
openFormProxy
:你可以使用 openFormProxy
来代理 openForm
,以便在openForm前后做一些操作,或改变openForm的行为
useAdmate({
openFormProxy (openForm) {},
})
const { form } = useAdmate({
openFormProxy (openForm) {
return new Promise((resolve, reject) => {
openForm()?.then(response => {
form.data.status = 1
resolve()
}).catch(() => {
reject()
})
})
},
})
useAdmate({
openFormProxy (openForm) {
return new Promise((resolve, reject) => {
openForm()?.finally(() => {
formRef.value.clearValidate()
}).then(() => {
resolve()
}).catch(() => {
reject()
})
})
},
})
useAdmate({
openFormProxy (openForm) {
return new Promise((resolve, reject) => {
openForm().then(() => {
resolve({
loading: false,
})
}).catch(() => {
reject({
show: false,
loading: false,
})
})
})
}
})
useAdmate({
openFormProxy (openForm) {
return {
loading: false
}
}
})
Hook: 提交表单时
submitForm
:提交表单,新增时调用 axiosConfig.c
,编辑时调用 axiosConfig.u
const {
submitForm
} = useAdmate()
submitFormProxy
:你可以使用 submitFormProxy
来代理 submitForm
,以便在submitForm前后做一些操作,或改变submitForm的行为
useAdmate({
submitFormProxy (submitForm) {}
})
submitForm({
...form.data,
status: 1,
})
useAdmate({
submitFormProxy (submitForm) {
return new Promise((resolve, reject) => {
submitForm({
...form.data,
status: 1,
}).then(() => {
resolve()
}).catch(() => {
reject()
})
})
}
})
useAdmate({
submitFormProxy (submitForm) {
return new Promise((resolve, reject) => {
formRef.value.validate().then(() => {
submitForm().then(() => {
resolve()
}).catch(() => {
reject()
})
})
})
}
})
useAdmate({
submitFormProxy (submitForm) {
return new Promise((resolve, reject) => {
formRef.value.validate().then(() => {
submitForm().then(() => {
resolve({
show: false,
submitting: false,
})
}).catch(() => {
reject({
show: true,
submitting: false,
})
})
})
})
}
})
useAdmate({
submitFormProxy (submitForm) {
return {
show: false,
submitting: false,
}
}
})
示例:表单是子组件
将表单抽离为子组件
示例代码
示例:表单是独立页面
操作单条记录时,跳转到专用的表单页面,操作完毕后返回
示例代码
示例:只有表单没有列表
表单默认打开,且无法关闭,通常用于列表中只有一条数据,故列表被省略的场景
示例代码
示例:嵌套使用
嵌套其它也使用Admate的页面
示例代码