init
This commit is contained in:
268
studio/micro-ui/src/components/Crud/CRUD.operation.vue
Normal file
268
studio/micro-ui/src/components/Crud/CRUD.operation.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="crud-opts">
|
||||
<span class="crud-opts-left">
|
||||
<!--左侧插槽-->
|
||||
<slot name="left" />
|
||||
<el-button
|
||||
v-if="crud.optShow.add"
|
||||
v-permission="permission.add"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="crud.toAdd"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.edit"
|
||||
v-permission="permission.edit"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="success"
|
||||
icon="el-icon-edit"
|
||||
:disabled="crud.selections.length !== 1"
|
||||
@click="crud.toEdit(crud.selections[0])"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.del"
|
||||
slot="reference"
|
||||
v-permission="permission.del"
|
||||
class="filter-item"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:loading="crud.delAllLoading"
|
||||
:disabled="crud.selections.length === 0"
|
||||
@click="toDelete(crud.selections)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="crud.optShow.download"
|
||||
:loading="crud.downloadLoading"
|
||||
:disabled="!crud.data.length"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="warning"
|
||||
icon="el-icon-download"
|
||||
@click="crud.doExport"
|
||||
>导出</el-button>
|
||||
<!--右侧-->
|
||||
<slot name="right" />
|
||||
</span>
|
||||
<el-button-group class="crud-opts-right">
|
||||
<el-button
|
||||
size="mini"
|
||||
plain
|
||||
type="info"
|
||||
icon="el-icon-search"
|
||||
@click="toggleSearch()"
|
||||
/>
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-refresh"
|
||||
@click="crud.refresh()"
|
||||
/>
|
||||
<el-popover
|
||||
placement="bottom-end"
|
||||
width="150"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
slot="reference"
|
||||
size="mini"
|
||||
icon="el-icon-s-grid"
|
||||
>
|
||||
<i
|
||||
class="fa fa-caret-down"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</el-button>
|
||||
<el-checkbox
|
||||
v-model="allColumnsSelected"
|
||||
:indeterminate="allColumnsSelectedIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
<el-checkbox
|
||||
v-for="item in tableColumns"
|
||||
:key="item.property"
|
||||
v-model="item.visible"
|
||||
@change="handleCheckedTableColumnsChange(item)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-popover>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CRUD, { crud } from '@crud/crud'
|
||||
|
||||
function sortWithRef(src, ref) {
|
||||
const result = Object.assign([], ref)
|
||||
let cursor = -1
|
||||
src.forEach(e => {
|
||||
const idx = result.indexOf(e)
|
||||
if (idx === -1) {
|
||||
cursor += 1
|
||||
result.splice(cursor, 0, e)
|
||||
} else {
|
||||
cursor = idx
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
permission: {
|
||||
type: Object,
|
||||
default: () => { return {} }
|
||||
},
|
||||
hiddenColumns: {
|
||||
type: Array,
|
||||
default: () => { return [] }
|
||||
},
|
||||
ignoreColumns: {
|
||||
type: Array,
|
||||
default: () => { return [] }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableColumns: [],
|
||||
allColumnsSelected: true,
|
||||
allColumnsSelectedIndeterminate: false,
|
||||
tableUnwatcher: null,
|
||||
// 忽略下次表格列变动
|
||||
ignoreNextTableColumnsChange: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'crud.props.table'() {
|
||||
this.updateTableColumns()
|
||||
this.tableColumns.forEach(column => {
|
||||
if (this.hiddenColumns.indexOf(column.property) !== -1) {
|
||||
column.visible = false
|
||||
this.updateColumnVisible(column)
|
||||
}
|
||||
})
|
||||
},
|
||||
'crud.props.table.store.states.columns'() {
|
||||
this.updateTableColumns()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.crud.updateProp('searchToggle', true)
|
||||
},
|
||||
methods: {
|
||||
updateTableColumns() {
|
||||
const table = this.crud.getTable()
|
||||
if (!table) {
|
||||
this.tableColumns = []
|
||||
return
|
||||
}
|
||||
let cols = null
|
||||
const columnFilter = e => e && e.type === 'default' && e.property && this.ignoreColumns.indexOf(e.property) === -1
|
||||
const refCols = table.columns.filter(columnFilter)
|
||||
if (this.ignoreNextTableColumnsChange) {
|
||||
this.ignoreNextTableColumnsChange = false
|
||||
return
|
||||
}
|
||||
this.ignoreNextTableColumnsChange = false
|
||||
const columns = []
|
||||
const fullTableColumns = table.$children.map(e => e.columnConfig).filter(columnFilter)
|
||||
cols = sortWithRef(fullTableColumns, refCols)
|
||||
cols.forEach(config => {
|
||||
const column = {
|
||||
property: config.property,
|
||||
label: config.label,
|
||||
visible: refCols.indexOf(config) !== -1
|
||||
}
|
||||
columns.push(column)
|
||||
})
|
||||
this.tableColumns = columns
|
||||
},
|
||||
toDelete(datas) {
|
||||
this.$confirm(`确认删除选中的${datas.length}条数据?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.crud.delAllLoading = true
|
||||
this.crud.doDelete(datas)
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleCheckAllChange(val) {
|
||||
if (val === false) {
|
||||
this.allColumnsSelected = true
|
||||
return
|
||||
}
|
||||
this.tableColumns.forEach(column => {
|
||||
if (!column.visible) {
|
||||
column.visible = true
|
||||
this.updateColumnVisible(column)
|
||||
}
|
||||
})
|
||||
this.allColumnsSelected = val
|
||||
this.allColumnsSelectedIndeterminate = false
|
||||
},
|
||||
handleCheckedTableColumnsChange(item) {
|
||||
let totalCount = 0
|
||||
let selectedCount = 0
|
||||
this.tableColumns.forEach(column => {
|
||||
++totalCount
|
||||
selectedCount += column.visible ? 1 : 0
|
||||
})
|
||||
if (selectedCount === 0) {
|
||||
this.crud.notify('请至少选择一列', CRUD.NOTIFICATION_TYPE.WARNING)
|
||||
this.$nextTick(function() {
|
||||
item.visible = true
|
||||
})
|
||||
return
|
||||
}
|
||||
this.allColumnsSelected = selectedCount === totalCount
|
||||
this.allColumnsSelectedIndeterminate = selectedCount !== totalCount && selectedCount !== 0
|
||||
this.updateColumnVisible(item)
|
||||
},
|
||||
updateColumnVisible(item) {
|
||||
const table = this.crud.props.table
|
||||
const vm = table.$children.find(e => e.prop === item.property)
|
||||
const columnConfig = vm.columnConfig
|
||||
if (item.visible) {
|
||||
// 找出合适的插入点
|
||||
const columnIndex = this.tableColumns.indexOf(item)
|
||||
vm.owner.store.commit('insertColumn', columnConfig, columnIndex + 1, null)
|
||||
} else {
|
||||
vm.owner.store.commit('removeColumn', columnConfig, null)
|
||||
}
|
||||
this.ignoreNextTableColumnsChange = true
|
||||
},
|
||||
toggleSearch() {
|
||||
this.crud.props.searchToggle = !this.crud.props.searchToggle
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.crud-opts {
|
||||
padding: 4px 0;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.crud-opts .crud-opts-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
.crud-opts .crud-opts-right span {
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
18
studio/micro-ui/src/components/Crud/Pagination.vue
Normal file
18
studio/micro-ui/src/components/Crud/Pagination.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<!--分页-->
|
||||
<template>
|
||||
<el-pagination
|
||||
:page-size.sync="page.size"
|
||||
:total="page.total"
|
||||
:current-page.sync="page.page"
|
||||
style="margin-top: 8px;"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="crud.sizeChangeHandler($event)"
|
||||
@current-change="crud.pageChangeHandler"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import { pagination } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [pagination()]
|
||||
}
|
||||
</script>
|
||||
20
studio/micro-ui/src/components/Crud/RR.operation.vue
Normal file
20
studio/micro-ui/src/components/Crud/RR.operation.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--搜索与重置-->
|
||||
<template>
|
||||
<span>
|
||||
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button>
|
||||
<el-button v-if="crud.optShow.reset" class="filter-item" size="mini" type="warning" icon="el-icon-refresh-left" @click="crud.resetQuery()">重置</el-button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import { crud } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
itemClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
71
studio/micro-ui/src/components/Crud/UD.operation.vue
Normal file
71
studio/micro-ui/src/components/Crud/UD.operation.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click="crud.toEdit(data)" />
|
||||
<el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
|
||||
<p>{{ msg }}</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="doCancel">取消</el-button>
|
||||
<el-button :loading="crud.dataStatus[crud.getDataId(data)].delete === 2" type="primary" size="mini" @click="crud.doDelete(data)">确定</el-button>
|
||||
</div>
|
||||
<el-button slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click="toDelete" />
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CRUD, { crud } from '@crud/crud'
|
||||
export default {
|
||||
mixins: [crud()],
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
permission: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
disabledEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledDle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
msg: {
|
||||
type: String,
|
||||
default: '确定删除本条数据吗?'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pop: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doCancel() {
|
||||
this.pop = false
|
||||
this.crud.cancelDelete(this.data)
|
||||
},
|
||||
toDelete() {
|
||||
this.pop = true
|
||||
},
|
||||
[CRUD.HOOK.afterDelete](crud, data) {
|
||||
if (data === this.data) {
|
||||
this.pop = false
|
||||
}
|
||||
},
|
||||
onPopoverShow() {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', this.handleDocumentClick)
|
||||
}, 0)
|
||||
},
|
||||
onPopoverHide() {
|
||||
document.removeEventListener('click', this.handleDocumentClick)
|
||||
},
|
||||
handleDocumentClick(event) {
|
||||
this.pop = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
864
studio/micro-ui/src/components/Crud/crud.js
Normal file
864
studio/micro-ui/src/components/Crud/crud.js
Normal file
@@ -0,0 +1,864 @@
|
||||
import { initData, download } from '@/api/data'
|
||||
import { parseTime, downloadFile } from '@/utils/index'
|
||||
import Vue from 'vue'
|
||||
|
||||
/**
|
||||
* CRUD配置
|
||||
* @author moxun
|
||||
* @param {*} options <br>
|
||||
* @return crud instance.
|
||||
* @example
|
||||
* 要使用多crud时,请在关联crud的组件处使用crud-tag进行标记,如:<jobForm :job-status="dict.job_status" crud-tag="job" />
|
||||
*/
|
||||
function CRUD(options) {
|
||||
const defaultOptions = {
|
||||
tag: 'default',
|
||||
// id字段名
|
||||
idField: 'id',
|
||||
// 标题
|
||||
title: '',
|
||||
// 请求数据的url
|
||||
url: '',
|
||||
// 表格数据
|
||||
data: [],
|
||||
// 选择项
|
||||
selections: [],
|
||||
// 待查询的对象
|
||||
query: {},
|
||||
// 查询数据的参数
|
||||
params: {},
|
||||
// Form 表单
|
||||
form: {},
|
||||
// 重置表单
|
||||
defaultForm: () => {},
|
||||
// 排序规则,默认 id 降序, 支持多字段排序 ['id,desc', 'createTime,asc']
|
||||
sort: ['id,desc'],
|
||||
// 等待时间
|
||||
time: 50,
|
||||
// CRUD Method
|
||||
crudMethod: {
|
||||
add: (form) => {},
|
||||
del: (id) => {},
|
||||
edit: (form) => {},
|
||||
get: (id) => {}
|
||||
},
|
||||
// 主页操作栏显示哪些按钮
|
||||
optShow: {
|
||||
add: true,
|
||||
edit: true,
|
||||
del: true,
|
||||
download: true,
|
||||
reset: true
|
||||
},
|
||||
// 自定义一些扩展属性
|
||||
props: {},
|
||||
// 在主页准备
|
||||
queryOnPresenterCreated: true,
|
||||
// 调试开关
|
||||
debug: false
|
||||
}
|
||||
options = mergeOptions(defaultOptions, options)
|
||||
const data = {
|
||||
...options,
|
||||
// 记录数据状态
|
||||
dataStatus: {},
|
||||
status: {
|
||||
add: CRUD.STATUS.NORMAL,
|
||||
edit: CRUD.STATUS.NORMAL,
|
||||
// 添加或编辑状态
|
||||
get cu() {
|
||||
if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) {
|
||||
return CRUD.STATUS.NORMAL
|
||||
} else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) {
|
||||
return CRUD.STATUS.PREPARED
|
||||
} else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) {
|
||||
return CRUD.STATUS.PROCESSING
|
||||
}
|
||||
throw new Error('wrong crud\'s cu status')
|
||||
},
|
||||
// 标题
|
||||
get title() {
|
||||
return this.add > CRUD.STATUS.NORMAL ? `新增${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${crud.title}` : crud.title
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
submit: '提交成功',
|
||||
add: '新增成功',
|
||||
edit: '编辑成功',
|
||||
del: '删除成功'
|
||||
},
|
||||
page: {
|
||||
// 页码
|
||||
page: 0,
|
||||
// 每页数据条数
|
||||
size: 10,
|
||||
// 总数据条数
|
||||
total: 0
|
||||
},
|
||||
// 整体loading
|
||||
loading: false,
|
||||
// 导出的 Loading
|
||||
downloadLoading: false,
|
||||
// 删除的 Loading
|
||||
delAllLoading: false
|
||||
}
|
||||
const methods = {
|
||||
/**
|
||||
* 通用的提示
|
||||
*/
|
||||
submitSuccessNotify() {
|
||||
crud.notify(crud.msg.submit, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
addSuccessNotify() {
|
||||
crud.notify(crud.msg.add, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
editSuccessNotify() {
|
||||
crud.notify(crud.msg.edit, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
delSuccessNotify() {
|
||||
crud.notify(crud.msg.del, CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
},
|
||||
// 搜索
|
||||
toQuery() {
|
||||
crud.page.page = 1
|
||||
crud.refresh()
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) {
|
||||
return
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
crud.loading = true
|
||||
// 请求数据
|
||||
initData(crud.url, crud.getQueryParams()).then(data => {
|
||||
const table = crud.getTable()
|
||||
if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据
|
||||
table.store.states.treeData = {}
|
||||
table.store.states.lazyTreeNodeMap = {}
|
||||
}
|
||||
crud.page.total = data.totalElements
|
||||
crud.data = data.content
|
||||
crud.resetDataStatus()
|
||||
// time 毫秒后显示表格
|
||||
setTimeout(() => {
|
||||
crud.loading = false
|
||||
callVmHook(crud, CRUD.HOOK.afterRefresh)
|
||||
}, crud.time)
|
||||
resolve(data)
|
||||
}).catch(err => {
|
||||
crud.loading = false
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 启动添加
|
||||
*/
|
||||
toAdd() {
|
||||
crud.resetForm()
|
||||
if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
|
||||
callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
|
||||
},
|
||||
/**
|
||||
* 启动编辑
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
toEdit(data) {
|
||||
crud.resetForm(JSON.parse(JSON.stringify(data)))
|
||||
if (!(callVmHook(crud, CRUD.HOOK.beforeToEdit, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.PREPARED
|
||||
crud.getDataStatus(crud.getDataId(data)).edit = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form)
|
||||
callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
|
||||
},
|
||||
/**
|
||||
* 启动删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
toDelete(data) {
|
||||
crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.PREPARED
|
||||
},
|
||||
/**
|
||||
* 取消删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
cancelDelete(data) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) {
|
||||
return
|
||||
}
|
||||
crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.NORMAL
|
||||
callVmHook(crud, CRUD.HOOK.afterDeleteCancel, data)
|
||||
},
|
||||
/**
|
||||
* 取消新增/编辑
|
||||
*/
|
||||
cancelCU() {
|
||||
const addStatus = crud.status.add
|
||||
const editStatus = crud.status.edit
|
||||
if (addStatus === CRUD.STATUS.PREPARED) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeAddCancel, crud.form)) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.NORMAL
|
||||
}
|
||||
if (editStatus === CRUD.STATUS.PREPARED) {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeEditCancel, crud.form)) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.NORMAL
|
||||
crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
|
||||
}
|
||||
crud.resetForm()
|
||||
if (addStatus === CRUD.STATUS.PREPARED) {
|
||||
callVmHook(crud, CRUD.HOOK.afterAddCancel, crud.form)
|
||||
}
|
||||
if (editStatus === CRUD.STATUS.PREPARED) {
|
||||
callVmHook(crud, CRUD.HOOK.afterEditCancel, crud.form)
|
||||
}
|
||||
// 清除表单验证
|
||||
if (crud.findVM('form').$refs['form']) {
|
||||
crud.findVM('form').$refs['form'].clearValidate()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 提交新增/编辑
|
||||
*/
|
||||
submitCU() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
|
||||
return
|
||||
}
|
||||
crud.findVM('form').$refs['form'].validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
|
||||
return
|
||||
}
|
||||
if (crud.status.add === CRUD.STATUS.PREPARED) {
|
||||
crud.doAdd()
|
||||
} else if (crud.status.edit === CRUD.STATUS.PREPARED) {
|
||||
crud.doEdit()
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行添加
|
||||
*/
|
||||
doAdd() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
|
||||
return
|
||||
}
|
||||
crud.status.add = CRUD.STATUS.PROCESSING
|
||||
crud.crudMethod.add(crud.form).then(() => {
|
||||
crud.status.add = CRUD.STATUS.NORMAL
|
||||
crud.resetForm()
|
||||
crud.addSuccessNotify()
|
||||
callVmHook(crud, CRUD.HOOK.afterSubmit)
|
||||
crud.toQuery()
|
||||
}).catch(() => {
|
||||
crud.status.add = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterAddError)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行编辑
|
||||
*/
|
||||
doEdit() {
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
|
||||
return
|
||||
}
|
||||
crud.status.edit = CRUD.STATUS.PROCESSING
|
||||
crud.crudMethod.edit(crud.form).then(() => {
|
||||
crud.status.edit = CRUD.STATUS.NORMAL
|
||||
crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
|
||||
crud.editSuccessNotify()
|
||||
crud.resetForm()
|
||||
callVmHook(crud, CRUD.HOOK.afterSubmit)
|
||||
crud.refresh()
|
||||
}).catch(() => {
|
||||
crud.status.edit = CRUD.STATUS.PREPARED
|
||||
callVmHook(crud, CRUD.HOOK.afterEditError)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 执行删除
|
||||
* @param {*} data 数据项
|
||||
*/
|
||||
doDelete(data) {
|
||||
let delAll = false
|
||||
let dataStatus
|
||||
const ids = []
|
||||
if (data instanceof Array) {
|
||||
delAll = true
|
||||
data.forEach(val => {
|
||||
ids.push(this.getDataId(val))
|
||||
})
|
||||
} else {
|
||||
ids.push(this.getDataId(data))
|
||||
dataStatus = crud.getDataStatus(this.getDataId(data))
|
||||
}
|
||||
if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) {
|
||||
return
|
||||
}
|
||||
if (!delAll) {
|
||||
dataStatus.delete = CRUD.STATUS.PROCESSING
|
||||
}
|
||||
return crud.crudMethod.del(ids).then(() => {
|
||||
if (delAll) {
|
||||
crud.delAllLoading = false
|
||||
} else dataStatus.delete = CRUD.STATUS.PREPARED
|
||||
crud.dleChangePage(1)
|
||||
crud.delSuccessNotify()
|
||||
callVmHook(crud, CRUD.HOOK.afterDelete, data)
|
||||
crud.refresh()
|
||||
}).catch(() => {
|
||||
if (delAll) {
|
||||
crud.delAllLoading = false
|
||||
} else dataStatus.delete = CRUD.STATUS.PREPARED
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 通用导出
|
||||
*/
|
||||
doExport() {
|
||||
crud.downloadLoading = true
|
||||
download(crud.url + '/download', crud.getQueryParams()).then(result => {
|
||||
downloadFile(result, crud.title + '数据', 'xlsx')
|
||||
crud.downloadLoading = false
|
||||
}).catch(() => {
|
||||
crud.downloadLoading = false
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取查询参数
|
||||
*/
|
||||
getQueryParams: function() {
|
||||
// 清除参数无值的情况
|
||||
Object.keys(crud.query).length !== 0 && Object.keys(crud.query).forEach(item => {
|
||||
if (crud.query[item] === null || crud.query[item] === '') crud.query[item] = undefined
|
||||
})
|
||||
Object.keys(crud.params).length !== 0 && Object.keys(crud.params).forEach(item => {
|
||||
if (crud.params[item] === null || crud.params[item] === '') crud.params[item] = undefined
|
||||
})
|
||||
return {
|
||||
page: crud.page.page - 1,
|
||||
size: crud.page.size,
|
||||
sort: crud.sort,
|
||||
...crud.query,
|
||||
...crud.params
|
||||
}
|
||||
},
|
||||
// 当前页改变
|
||||
pageChangeHandler(e) {
|
||||
crud.page.page = e
|
||||
crud.refresh()
|
||||
},
|
||||
// 每页条数改变
|
||||
sizeChangeHandler(e) {
|
||||
crud.page.size = e
|
||||
crud.page.page = 1
|
||||
crud.refresh()
|
||||
},
|
||||
// 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
|
||||
dleChangePage(size) {
|
||||
if (crud.data.length === size && crud.page.page !== 1) {
|
||||
crud.page.page -= 1
|
||||
}
|
||||
},
|
||||
// 选择改变
|
||||
selectionChangeHandler(val) {
|
||||
crud.selections = val
|
||||
},
|
||||
/**
|
||||
* 重置查询参数
|
||||
* @param {Boolean} toQuery 重置后进行查询操作
|
||||
*/
|
||||
resetQuery(toQuery = true) {
|
||||
const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery))
|
||||
const query = crud.query
|
||||
Object.keys(query).forEach(key => {
|
||||
query[key] = defaultQuery[key]
|
||||
})
|
||||
// 重置参数
|
||||
this.params = {}
|
||||
if (toQuery) {
|
||||
crud.toQuery()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 重置表单
|
||||
* @param {Array} data 数据
|
||||
*/
|
||||
resetForm(data) {
|
||||
const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form')))
|
||||
const crudFrom = crud.form
|
||||
for (const key in form) {
|
||||
if (crudFrom.hasOwnProperty(key)) {
|
||||
crudFrom[key] = form[key]
|
||||
} else {
|
||||
Vue.set(crudFrom, key, form[key])
|
||||
}
|
||||
}
|
||||
// add by ghl 2020-10-04 页面重复添加信息时,下拉框的校验会存在,需要找工取消
|
||||
if (crud.findVM('form').$refs['form']) {
|
||||
crud.findVM('form').$refs['form'].clearValidate()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 重置数据状态
|
||||
*/
|
||||
resetDataStatus() {
|
||||
const dataStatus = {}
|
||||
function resetStatus(datas) {
|
||||
datas.forEach(e => {
|
||||
dataStatus[crud.getDataId(e)] = {
|
||||
delete: 0,
|
||||
edit: 0
|
||||
}
|
||||
if (e.children) {
|
||||
resetStatus(e.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
resetStatus(crud.data)
|
||||
crud.dataStatus = dataStatus
|
||||
},
|
||||
/**
|
||||
* 获取数据状态
|
||||
* @param {Number | String} id 数据项id
|
||||
*/
|
||||
getDataStatus(id) {
|
||||
return crud.dataStatus[id]
|
||||
},
|
||||
/**
|
||||
* 用于树形表格多选, 选中所有
|
||||
* @param selection
|
||||
*/
|
||||
selectAllChange(selection) {
|
||||
// 如果选中的数目与请求到的数目相同就选中子节点,否则就清空选中
|
||||
if (selection && selection.length === crud.data.length) {
|
||||
selection.forEach(val => {
|
||||
crud.selectChange(selection, val)
|
||||
})
|
||||
} else {
|
||||
crud.getTable().clearSelection()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 用于树形表格多选,单选的封装
|
||||
* @param selection
|
||||
* @param row
|
||||
*/
|
||||
selectChange(selection, row) {
|
||||
// 如果selection中存在row代表是选中,否则是取消选中
|
||||
if (selection.find(val => { return crud.getDataId(val) === crud.getDataId(row) })) {
|
||||
if (row.children) {
|
||||
row.children.forEach(val => {
|
||||
crud.getTable().toggleRowSelection(val, true)
|
||||
selection.push(val)
|
||||
if (val.children) {
|
||||
crud.selectChange(selection, val)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
crud.toggleRowSelection(selection, row)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 切换选中状态
|
||||
* @param selection
|
||||
* @param data
|
||||
*/
|
||||
toggleRowSelection(selection, data) {
|
||||
if (data.children) {
|
||||
data.children.forEach(val => {
|
||||
selection.splice(selection.findIndex(item => this.getDataId(item) === this.getDataId(val)), 1)
|
||||
crud.getTable().toggleRowSelection(val, false)
|
||||
if (val.children) {
|
||||
crud.toggleRowSelection(selection, val)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
findVM(type) {
|
||||
return crud.vms.find(vm => vm && vm.type === type).vm
|
||||
},
|
||||
notify(title, type = CRUD.NOTIFICATION_TYPE.INFO) {
|
||||
crud.vms[0].vm.$notify({
|
||||
title,
|
||||
type,
|
||||
duration: 2500
|
||||
})
|
||||
},
|
||||
updateProp(name, value) {
|
||||
Vue.set(crud.props, name, value)
|
||||
},
|
||||
getDataId(data) {
|
||||
return data[this.idField]
|
||||
},
|
||||
getTable() {
|
||||
return this.findVM('presenter').$refs.table
|
||||
},
|
||||
attchTable() {
|
||||
const table = this.getTable()
|
||||
this.updateProp('table', table)
|
||||
const that = this
|
||||
table.$on('expand-change', (row, expanded) => {
|
||||
if (!expanded) {
|
||||
return
|
||||
}
|
||||
const lazyTreeNodeMap = table.store.states.lazyTreeNodeMap
|
||||
row.children = lazyTreeNodeMap[crud.getDataId(row)]
|
||||
if (row.children) {
|
||||
row.children.forEach(ele => {
|
||||
const id = crud.getDataId(ele)
|
||||
if (that.dataStatus[id] === undefined) {
|
||||
that.dataStatus[id] = {
|
||||
delete: 0,
|
||||
edit: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const crud = Object.assign({}, data)
|
||||
// 可观测化
|
||||
Vue.observable(crud)
|
||||
// 附加方法
|
||||
Object.assign(crud, methods)
|
||||
// 记录初始默认的查询参数,后续重置查询时使用
|
||||
Object.assign(crud, {
|
||||
defaultQuery: JSON.parse(JSON.stringify(data.query)),
|
||||
// 预留4位存储:组件 主页、头部、分页、表单,调试查看也方便找
|
||||
vms: Array(4),
|
||||
/**
|
||||
* 注册组件实例
|
||||
* @param {String} type 类型
|
||||
* @param {*} vm 组件实例
|
||||
* @param {Number} index 该参数内部使用
|
||||
*/
|
||||
registerVM(type, vm, index = -1) {
|
||||
const vmObj = {
|
||||
type,
|
||||
vm: vm
|
||||
}
|
||||
if (index < 0) {
|
||||
this.vms.push(vmObj)
|
||||
return
|
||||
}
|
||||
if (index < 4) { // 内置预留vm数
|
||||
this.vms[index] = vmObj
|
||||
return
|
||||
}
|
||||
this.vms.length = Math.max(this.vms.length, index)
|
||||
this.vms.splice(index, 1, vmObj)
|
||||
},
|
||||
/**
|
||||
* 取消注册组件实例
|
||||
* @param {*} vm 组件实例
|
||||
*/
|
||||
unregisterVM(type, vm) {
|
||||
for (let i = this.vms.length - 1; i >= 0; i--) {
|
||||
if (this.vms[i] === undefined) {
|
||||
continue
|
||||
}
|
||||
if (this.vms[i].type === type && this.vms[i].vm === vm) {
|
||||
if (i < 4) { // 内置预留vm数
|
||||
this.vms[i] = undefined
|
||||
} else {
|
||||
this.vms.splice(i, 1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 冻结处理,需要扩展数据的话,使用crud.updateProp(name, value),以crud.props.name形式访问,这个是响应式的,可以做数据绑定
|
||||
Object.freeze(crud)
|
||||
return crud
|
||||
}
|
||||
|
||||
// hook VM
|
||||
function callVmHook(crud, hook) {
|
||||
if (crud.debug) {
|
||||
console.log('callVmHook: ' + hook)
|
||||
}
|
||||
const tagHook = crud.tag ? hook + '$' + crud.tag : null
|
||||
let ret = true
|
||||
const nargs = [crud]
|
||||
for (let i = 2; i < arguments.length; ++i) {
|
||||
nargs.push(arguments[i])
|
||||
}
|
||||
// 有些组件扮演了多个角色,调用钩子时,需要去重
|
||||
const vmSet = new Set()
|
||||
crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
|
||||
vmSet.forEach(vm => {
|
||||
if (vm[hook]) {
|
||||
ret = vm[hook].apply(vm, nargs) !== false && ret
|
||||
}
|
||||
if (tagHook && vm[tagHook]) {
|
||||
ret = vm[tagHook].apply(vm, nargs) !== false && ret
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
function mergeOptions(src, opts) {
|
||||
const optsRet = {
|
||||
...src
|
||||
}
|
||||
for (const key in src) {
|
||||
if (opts.hasOwnProperty(key)) {
|
||||
optsRet[key] = opts[key]
|
||||
}
|
||||
}
|
||||
return optsRet
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找crud
|
||||
* @param {*} vm
|
||||
* @param {string} tag
|
||||
*/
|
||||
function lookupCrud(vm, tag) {
|
||||
tag = tag || vm.$attrs['crud-tag'] || 'default'
|
||||
// function lookupCrud(vm, tag) {
|
||||
if (vm.$crud) {
|
||||
const ret = vm.$crud[tag]
|
||||
if (ret) {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
return vm.$parent ? lookupCrud(vm.$parent, tag) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* crud主页
|
||||
*/
|
||||
function presenter(crud) {
|
||||
if (crud) {
|
||||
console.warn('[CRUD warn]: ' + 'please use $options.cruds() { return CRUD(...) or [CRUD(...), ...] }')
|
||||
}
|
||||
return {
|
||||
data() {
|
||||
// 在data中返回crud,是为了将crud与当前实例关联,组件观测crud相关属性变化
|
||||
return {
|
||||
crud: this.crud
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.$crud = this.$crud || {}
|
||||
let cruds = this.$options.cruds instanceof Function ? this.$options.cruds() : crud
|
||||
if (!(cruds instanceof Array)) {
|
||||
cruds = [cruds]
|
||||
}
|
||||
cruds.forEach(ele => {
|
||||
if (this.$crud[ele.tag]) {
|
||||
console.error('[CRUD error]: ' + 'crud with tag [' + ele.tag + ' is already exist')
|
||||
}
|
||||
this.$crud[ele.tag] = ele
|
||||
ele.registerVM('presenter', this, 0)
|
||||
})
|
||||
this.crud = this.$crud['defalut'] || cruds[0]
|
||||
},
|
||||
methods: {
|
||||
parseTime
|
||||
},
|
||||
created() {
|
||||
for (const k in this.$crud) {
|
||||
if (this.$crud[k].queryOnPresenterCreated) {
|
||||
this.$crud[k].toQuery()
|
||||
}
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
for (const k in this.$crud) {
|
||||
this.$crud[k].unregisterVM('presenter', this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 如果table未实例化(例如使用了v-if),请稍后在适当时机crud.attchTable刷新table信息
|
||||
if (this.$refs.table !== undefined) {
|
||||
this.crud.attchTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 头部
|
||||
*/
|
||||
function header() {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
query: this.crud.query
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('header', this, 1)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('header', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
function pagination() {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
page: this.crud.page
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('pagination', this, 2)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('pagination', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单
|
||||
*/
|
||||
function form(defaultForm) {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud,
|
||||
form: this.crud.form
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM('form', this, 3)
|
||||
},
|
||||
created() {
|
||||
this.crud.defaultForm = defaultForm
|
||||
this.crud.resetForm()
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM('form', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* crud
|
||||
*/
|
||||
function crud(options = {}) {
|
||||
const defaultOptions = {
|
||||
type: undefined
|
||||
}
|
||||
options = mergeOptions(defaultOptions, options)
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
crud: this.crud
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.crud = lookupCrud(this)
|
||||
this.crud.registerVM(options.type, this)
|
||||
},
|
||||
destroyed() {
|
||||
this.crud.unregisterVM(options.type, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD钩子
|
||||
*/
|
||||
CRUD.HOOK = {
|
||||
/** 刷新 - 之前 */
|
||||
beforeRefresh: 'beforeCrudRefresh',
|
||||
/** 刷新 - 之后 */
|
||||
afterRefresh: 'afterCrudRefresh',
|
||||
/** 删除 - 之前 */
|
||||
beforeDelete: 'beforeCrudDelete',
|
||||
/** 删除 - 之后 */
|
||||
afterDelete: 'afterCrudDelete',
|
||||
/** 删除取消 - 之前 */
|
||||
beforeDeleteCancel: 'beforeCrudDeleteCancel',
|
||||
/** 删除取消 - 之后 */
|
||||
afterDeleteCancel: 'afterCrudDeleteCancel',
|
||||
/** 新建 - 之前 */
|
||||
beforeToAdd: 'beforeCrudToAdd',
|
||||
/** 新建 - 之后 */
|
||||
afterToAdd: 'afterCrudToAdd',
|
||||
/** 编辑 - 之前 */
|
||||
beforeToEdit: 'beforeCrudToEdit',
|
||||
/** 编辑 - 之后 */
|
||||
afterToEdit: 'afterCrudToEdit',
|
||||
/** 开始 "新建/编辑" - 之前 */
|
||||
beforeToCU: 'beforeCrudToCU',
|
||||
/** 开始 "新建/编辑" - 之后 */
|
||||
afterToCU: 'afterCrudToCU',
|
||||
/** "新建/编辑" 验证 - 之前 */
|
||||
beforeValidateCU: 'beforeCrudValidateCU',
|
||||
/** "新建/编辑" 验证 - 之后 */
|
||||
afterValidateCU: 'afterCrudValidateCU',
|
||||
/** 添加取消 - 之前 */
|
||||
beforeAddCancel: 'beforeCrudAddCancel',
|
||||
/** 添加取消 - 之后 */
|
||||
afterAddCancel: 'afterCrudAddCancel',
|
||||
/** 编辑取消 - 之前 */
|
||||
beforeEditCancel: 'beforeCrudEditCancel',
|
||||
/** 编辑取消 - 之后 */
|
||||
afterEditCancel: 'afterCrudEditCancel',
|
||||
/** 提交 - 之前 */
|
||||
beforeSubmit: 'beforeCrudSubmitCU',
|
||||
/** 提交 - 之后 */
|
||||
afterSubmit: 'afterCrudSubmitCU',
|
||||
afterAddError: 'afterCrudAddError',
|
||||
afterEditError: 'afterCrudEditError'
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD状态
|
||||
*/
|
||||
CRUD.STATUS = {
|
||||
NORMAL: 0,
|
||||
PREPARED: 1,
|
||||
PROCESSING: 2
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD通知类型
|
||||
*/
|
||||
CRUD.NOTIFICATION_TYPE = {
|
||||
SUCCESS: 'success',
|
||||
WARNING: 'warning',
|
||||
INFO: 'info',
|
||||
ERROR: 'error'
|
||||
}
|
||||
|
||||
export default CRUD
|
||||
|
||||
export {
|
||||
presenter,
|
||||
header,
|
||||
form,
|
||||
pagination,
|
||||
crud
|
||||
}
|
||||
Reference in New Issue
Block a user