mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-28 15:55:24 +00:00
Merge branch 'master' into jym_dev
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"js-cookie": "2.2.0",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.frompairs": "^4.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
|
9
src/api/common.js
Normal file
9
src/api/common.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function createSourceIdCache(ids) {
|
||||
return request({
|
||||
url: '/api/v1/common/resources/cache/',
|
||||
method: 'post',
|
||||
data: {resources: ids}
|
||||
})
|
||||
}
|
@@ -1,62 +1,91 @@
|
||||
<template>
|
||||
<elFormRender ref="dataForm" :content="content" v-bind="$attrs" v-on="$listeners">
|
||||
<ElFormRender
|
||||
ref="dataForm"
|
||||
:content="fields"
|
||||
v-bind="$attrs"
|
||||
:form="basicForm"
|
||||
label-position="right"
|
||||
label-width="17%"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<!-- slot 透传 -->
|
||||
<slot v-for="item in content" :slot="`id:${item.id}`" :name="`id:${item.id}`" />
|
||||
<slot v-for="item in content" :slot="`$id:${item.id}`" :name="`$id:${item.id}`" />
|
||||
<slot v-for="item in fields" :slot="`id:${item.id}`" :name="`id:${item.id}`" />
|
||||
<slot v-for="item in fields" :slot="`$id:${item.id}`" :name="`$id:${item.id}`" />
|
||||
|
||||
<el-form-item v-if="defaultButton">
|
||||
<el-button size="small" type="primary" @click="submitForm('dataForm')">submit</el-button>
|
||||
<el-button size="small" @click="resetForm('dataForm')">reset</el-button>
|
||||
<el-button size="small" @click="resetForm('dataForm')">{{ $tc('Reset') }}</el-button>
|
||||
<el-button size="small" type="primary" @click="submitForm('dataForm')">{{ $tc('Submit') }}</el-button>
|
||||
</el-form-item>
|
||||
<slot name="Actions" />
|
||||
</elFormRender>
|
||||
</ElFormRender>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import elFormRender from './components/el-form-renderer'
|
||||
import ElFormRender from './components/el-form-renderer'
|
||||
export default {
|
||||
components: {
|
||||
elFormRender
|
||||
ElFormRender
|
||||
},
|
||||
props: {
|
||||
defaultButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
content: {
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 初始值
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => { return {} }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
basicForm: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.basicForm = this.form
|
||||
},
|
||||
methods: {
|
||||
// 获取表单数据
|
||||
submitForm(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit')
|
||||
this.$emit('submit', this.$refs[formName].getFormValue())
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
// 重置表单
|
||||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
getBasic() {
|
||||
if (this.url) {
|
||||
console.log('has Url')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-form /deep/ .el-form-item {
|
||||
.el-form /deep/ .el-form-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
margin-left:12%;
|
||||
width:73%;
|
||||
}
|
||||
.el-form /deep/ .el-form-item__content {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.el-form /deep/ .el-form-item__label {
|
||||
padding: 0 30px 0 0;
|
||||
}
|
||||
|
||||
.el-form /deep/ .el-form-item__error {
|
||||
position: inherit;
|
||||
}
|
||||
|
||||
.el-form /deep/ .form-group-header {
|
||||
margin-left: 50px;
|
||||
}
|
||||
</style>
|
||||
|
14
src/components/DataForm/rules/index.js
Normal file
14
src/components/DataForm/rules/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const Required = {
|
||||
required: true, message: i18n.t('common.' + 'This field is required'), trigger: 'blur'
|
||||
}
|
||||
|
||||
export const RequiredChange = {
|
||||
required: true, message: i18n.t('common.' + 'This field is required'), trigger: 'change'
|
||||
}
|
||||
|
||||
export default {
|
||||
Required,
|
||||
RequiredChange
|
||||
}
|
@@ -102,8 +102,9 @@
|
||||
<div
|
||||
:is="col.formatter"
|
||||
:key="row.id"
|
||||
:setting="data"
|
||||
:table-data="data"
|
||||
:row="row"
|
||||
:reload="getList"
|
||||
:col="col"
|
||||
:cell-value="row[col.prop]"
|
||||
>
|
||||
@@ -730,7 +731,8 @@ export default {
|
||||
// JSON.stringify是为了后面深拷贝作准备
|
||||
initExtraQuery: JSON.stringify(this.extraQuery || this.customQuery || {}),
|
||||
isSearchCollapse: false,
|
||||
showNoData: false
|
||||
showNoData: false,
|
||||
innerQuery: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -852,6 +854,7 @@ export default {
|
||||
formValue = this.$refs.searchForm.getFormValue()
|
||||
Object.assign(query, formValue)
|
||||
}
|
||||
Object.assign(query, this.innerQuery)
|
||||
Object.assign(query, this._extraQuery)
|
||||
|
||||
query[this.pageSizeKey] = this.hasPagination
|
||||
@@ -942,18 +945,9 @@ export default {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
async search() {
|
||||
const form = this.$refs.searchForm
|
||||
const valid = await new Promise(r => form.validate(r))
|
||||
if (!valid) return
|
||||
|
||||
try {
|
||||
await this.beforeSearch(form.getFormValue())
|
||||
this.page = defaultFirstPage
|
||||
this.getList()
|
||||
} catch (err) {
|
||||
this.$emit('error', err)
|
||||
}
|
||||
search(attrs) {
|
||||
this.innerQuery = Object.assign(this.innerQuery, attrs)
|
||||
return this.getList()
|
||||
},
|
||||
/**
|
||||
* 重置查询,相当于点击「重置」按钮
|
||||
@@ -1168,15 +1162,12 @@ export default {
|
||||
return record[this.treeChildKey] && record[this.treeChildKey].length > 0
|
||||
},
|
||||
onSortChange({ column, prop, order }) {
|
||||
if (!this.extraQuery) {
|
||||
this.extraQuery = {}
|
||||
}
|
||||
if (!order) {
|
||||
delete this.extraQuery['sort']
|
||||
delete this.extraQuery['direction']
|
||||
delete this.innerQuery['sort']
|
||||
delete this.innerQuery['direction']
|
||||
} else {
|
||||
this.extraQuery['sort'] = prop
|
||||
this.extraQuery['direction'] = order
|
||||
this.innerQuery['sort'] = prop
|
||||
this.innerQuery['direction'] = order
|
||||
}
|
||||
this.getList()
|
||||
}
|
||||
|
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<ActionsGroup :size="'mini'" :actions="actions" :more-actions="moreActions" @actionClick="handleActionClick"></ActionsGroup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'ActionsFormatter',
|
||||
components: { ActionsGroup },
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
const defaultActions = [
|
||||
{
|
||||
name: 'update',
|
||||
title: this.$tc('Update'),
|
||||
type: 'primary',
|
||||
has: this.col.actions.hasUpdate || true,
|
||||
can: this.col.actions.canUpdate || true,
|
||||
callback: this.col.actions.onUpdate
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
title: this.$tc('Delete'),
|
||||
type: 'danger',
|
||||
has: this.col.actions.hasDelete || true,
|
||||
can: this.col.actions.canDelete || true,
|
||||
callback: this.col.actions.onDelete
|
||||
}
|
||||
]
|
||||
const extraActions = this.col.actions.extraActions || []
|
||||
return {
|
||||
defaultActions: defaultActions,
|
||||
extraActions: extraActions
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
validActions() {
|
||||
const actions = [...this.defaultActions, ...this.extraActions]
|
||||
const validActions = []
|
||||
for (const v of actions) {
|
||||
const has = this.checkItem(v, 'has')
|
||||
if (!has) {
|
||||
continue
|
||||
}
|
||||
const can = this.checkItem(v, 'can')
|
||||
v.disabled = !can
|
||||
validActions.push(v)
|
||||
}
|
||||
return validActions
|
||||
},
|
||||
actions() {
|
||||
return this.validActions.slice(0, 2)
|
||||
},
|
||||
moreActions() {
|
||||
return this.validActions.slice(2, this.validActions.length)
|
||||
},
|
||||
namedValidActions() {
|
||||
const actions = {}
|
||||
for (const action of this.validActions) {
|
||||
if (!action || !action.hasOwnProperty('name')) {
|
||||
continue
|
||||
}
|
||||
actions[action.name] = action
|
||||
}
|
||||
return actions
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleActionClick(item) {
|
||||
const action = this.namedValidActions[item]
|
||||
if (action && action.callback) {
|
||||
console.log(this.setting)
|
||||
action.callback(this.row, this.col, this.cellValue)
|
||||
}
|
||||
},
|
||||
checkItem(item, attr) {
|
||||
let ok = item[attr]
|
||||
if (typeof ok === 'function') {
|
||||
ok = ok(this.row, this.cellValue)
|
||||
} else if (ok == null) {
|
||||
ok = true
|
||||
}
|
||||
return ok
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ElDatableTable class="el-table" v-bind="tableConfig" v-on="$listeners"></ElDatableTable>
|
||||
<ElDatableTable ref="table" class="el-table" v-bind="tableConfig" v-on="$listeners"></ElDatableTable>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -36,16 +36,12 @@ export default {
|
||||
hasDelete: userTableActions.hasDelete !== false,
|
||||
hasNew: false,
|
||||
// editText: this.$t('action.update'), // 编辑按钮文案
|
||||
operationAttrs: {
|
||||
align: 'center',
|
||||
width: '150px'
|
||||
},
|
||||
operationButtonType: 'button',
|
||||
buttonSize: 'mini',
|
||||
tableAttrs: {
|
||||
stripe: true, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true // 宽度自适应
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark'
|
||||
},
|
||||
extraButtons: userTableActions.extraButtons,
|
||||
onEdit: (row) => {
|
||||
@@ -90,6 +86,15 @@ export default {
|
||||
const config = Object.assign(this.defaultConfig, this.config)
|
||||
return config
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$refs.table.clearSelection()
|
||||
return this.$refs.table.getList()
|
||||
},
|
||||
search(attrs) {
|
||||
return this.$refs.table.search(attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -102,7 +107,6 @@ export default {
|
||||
}
|
||||
.el-table /deep/ .el-table__row > td> div > span {
|
||||
text-overflow: ellipsis;
|
||||
-moz-text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
40
src/components/Dialog/index.vue
Normal file
40
src/components/Dialog/index.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-dialog :title="title" v-bind="$attrs" v-on="$listeners">
|
||||
<slot></slot>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<slog name="footer">
|
||||
<el-button size="small" @click="onCancel">{{ $t('Cancel') }}</el-button>
|
||||
<el-button type="primary" size="small" @click="onConfirm">{{ $tc('Ok') }}</el-button>
|
||||
</slog>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Dialog',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCancel() {
|
||||
|
||||
},
|
||||
onConfirm() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -1,96 +1,237 @@
|
||||
<template>
|
||||
<div class="table-header">
|
||||
<slot name="header">
|
||||
<!--TODO: 事件交互 -->
|
||||
<div class="table-header-left-side">
|
||||
<ActionsGroup :actions="actions" :more-actions="moreActions" class="header-action" @actionClick="handleActionClick"></ActionsGroup>
|
||||
</div>
|
||||
<!-- TODO: 事件交互 -->
|
||||
<div class="table-action-right-side">
|
||||
<el-input v-model="keyword" suffix-icon="el-icon-search" :placeholder="$tc('Search')" class="right-side-item action-search" size="small" clearable @change="handleSearch" @input="handleSearch"></el-input>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" @actionClick="handleActionClick"></ActionsGroup>
|
||||
<ActionsGroup :is-fa="true" :actions="defaultRightSideActions" class="right-side-actions right-side-item" @actionClick="handleActionClick"></ActionsGroup>
|
||||
</div>
|
||||
<Dialog :title="$t('Export')">
|
||||
<el-form>
|
||||
<el-form-item label="导出范围" :label-width="'100px'">
|
||||
<el-radio v-model="exportValue" class="export-item" label="1">导出全部</el-radio>
|
||||
<el-radio v-model="exportValue" class="export-item" label="2">仅导出选中项</el-radio>
|
||||
<el-radio v-model="exportValue" class="export-item" label="3">仅导出搜索项</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import { Dialog } from '../Dialog'
|
||||
import _ from 'lodash'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
const defaultFalse = { type: Boolean, default: false }
|
||||
|
||||
export default {
|
||||
name: 'TableAction',
|
||||
components: {
|
||||
ActionsGroup
|
||||
ActionsGroup,
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
hasExport: defaultTrue,
|
||||
hasImport: defaultTrue,
|
||||
hasRefresh: defaultTrue,
|
||||
hasCreate: defaultTrue,
|
||||
hasDelete: defaultTrue,
|
||||
hasUpdate: defaultTrue,
|
||||
hasBulkDelete: defaultTrue,
|
||||
hasBulkUpdate: defaultFalse,
|
||||
hasLeftActions: defaultTrue,
|
||||
hasSearch: defaultTrue,
|
||||
hasRightActions: defaultTrue
|
||||
hasRightActions: defaultTrue,
|
||||
tableUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
createRoute: {
|
||||
type: String,
|
||||
default: '404'
|
||||
},
|
||||
reloadTable: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
performBulkDelete: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
searchTable: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
selectedRows: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
extraActions: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
extraMoreActions: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
extraRightSideActions: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: {
|
||||
Export: { name: 'actionExport', fa: 'fa-download' },
|
||||
Import: { name: 'actionImport', fa: 'fa-upload' },
|
||||
Refresh: { name: 'actionRefresh', fa: 'fa-refresh' }
|
||||
},
|
||||
defaultCreateAction: {
|
||||
keyword: '',
|
||||
defaultRightSideActions: [
|
||||
{ name: 'actionExport', fa: 'fa-download', has: this.hasExport, callback: this.handleExport },
|
||||
{ name: 'actionImport', fa: 'fa-upload', has: this.hasImport, callback: this.handleImport },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', has: this.hasRefresh, callback: this.handleRefresh }
|
||||
],
|
||||
defaultActions: [
|
||||
{
|
||||
name: 'actionCreate',
|
||||
title: this.$tc('Create'),
|
||||
type: 'primary'
|
||||
},
|
||||
keyword: '',
|
||||
defaultMoreActions: {
|
||||
Delete: {
|
||||
type: 'primary',
|
||||
has: this.hasCreate,
|
||||
can: true,
|
||||
callback: this.handleCreate
|
||||
}
|
||||
],
|
||||
defaultMoreActions: [
|
||||
{
|
||||
title: this.$tc('Delete selected'),
|
||||
name: 'actionDeleteSelected'
|
||||
name: 'actionDeleteSelected',
|
||||
can: (rows) => rows.length > 0,
|
||||
has: this.hasBulkDelete,
|
||||
callback: this.defaultBulkDeleteCallback
|
||||
},
|
||||
Update: {
|
||||
{
|
||||
title: this.$tc('Update selected'),
|
||||
name: 'actionUpdateSelected'
|
||||
}
|
||||
name: 'actionUpdateSelected',
|
||||
can: (rows) => rows.length > 0,
|
||||
has: this.hasBulkUpdate,
|
||||
callback: this.handleBulkUpdate
|
||||
}
|
||||
],
|
||||
dialogExportVisible: false,
|
||||
exportValue: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rightSideActions() {
|
||||
const actions = []
|
||||
for (const k in this.defaultRightSideActions) {
|
||||
if (this['has' + k]) {
|
||||
actions.push(this.defaultRightSideActions[k])
|
||||
}
|
||||
}
|
||||
return actions
|
||||
const actions = [...this.defaultRightSideActions, ...this.extraRightSideActions]
|
||||
return this.cleanActions(actions)
|
||||
},
|
||||
actions() {
|
||||
const actions = []
|
||||
if (this.hasCreate) {
|
||||
actions.push(this.defaultCreateAction)
|
||||
}
|
||||
return actions
|
||||
const actions = [...this.defaultActions, ...this.extraActions]
|
||||
return this.cleanActions(actions)
|
||||
},
|
||||
moreActions() {
|
||||
const actions = []
|
||||
for (const k in this.defaultMoreActions) {
|
||||
if (this['has' + k]) {
|
||||
actions.push(this.defaultMoreActions[k])
|
||||
const actions = [...this.defaultMoreActions, ...this.extraMoreActions]
|
||||
return this.cleanActions(actions)
|
||||
},
|
||||
|
||||
namedActions() {
|
||||
const totalActions = [...this.actions, ...this.moreActions, ...this.rightSideActions]
|
||||
const actions = {}
|
||||
for (const action of totalActions) {
|
||||
if (!action || !action.hasOwnProperty('name')) {
|
||||
continue
|
||||
}
|
||||
actions[action.name] = action
|
||||
}
|
||||
return actions
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSearch(keyword) {
|
||||
console.log('Search: ', keyword)
|
||||
},
|
||||
handleSearch: _.debounce(function() {
|
||||
this.searchTable({search: this.keyword})
|
||||
}, 500),
|
||||
handleActionClick(item) {
|
||||
this.$emit('clickAction', item)
|
||||
console.log('name cations', this.namedActions)
|
||||
let handler = this.namedActions[item] ? this.namedActions[item].callback : null
|
||||
if (!handler) {
|
||||
handler = () => {
|
||||
console.log('No handler found for ', item)
|
||||
}
|
||||
}
|
||||
handler(this.selectedRows)
|
||||
},
|
||||
handleCreate() {
|
||||
const routeName = this.createRoute
|
||||
this.$router.push({ name: routeName })
|
||||
console.log('handle create')
|
||||
},
|
||||
defaultBulkDeleteCallback(rows) {
|
||||
const msg = this.$tc('Are you sure to delete') + ' ' + rows.length + ' ' + this.$tc('rows')
|
||||
const title = this.$tc('Info')
|
||||
const performDelete = this.performBulkDelete || this.defaultPerformBulkDelete
|
||||
this.$alert(msg, title, {
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'el-button--danger',
|
||||
showCancelButton: true,
|
||||
beforeClose: async(action, instance, done) => {
|
||||
if (action !== 'confirm') return done()
|
||||
instance.confirmButtonLoading = true
|
||||
try {
|
||||
await performDelete(rows)
|
||||
done()
|
||||
this.reloadTable()
|
||||
this.$message.success(this.$tc('Delete success'))
|
||||
} catch (error) {
|
||||
this.$message.error(this.$tc('Delete failed'))
|
||||
console.warn(error)
|
||||
} finally {
|
||||
instance.confirmButtonLoading = false
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
/* 取消*/
|
||||
})
|
||||
},
|
||||
async defaultPerformBulkDelete(rows) {
|
||||
const ids = rows.map((v) => {
|
||||
return v.id
|
||||
})
|
||||
const data = await createSourceIdCache(ids)
|
||||
const url = `${this.tableUrl}?spm=` + data.spm
|
||||
return this.$axios.delete(url)
|
||||
},
|
||||
handleBulkUpdate(rows) {
|
||||
},
|
||||
handleExport() {
|
||||
this.dialogExportVisible = true
|
||||
},
|
||||
handleImport() {
|
||||
},
|
||||
handleRefresh() {
|
||||
this.reloadTable()
|
||||
},
|
||||
cleanActions(actions) {
|
||||
const validActions = []
|
||||
for (const action of actions) {
|
||||
let ok = this.checkItem(action, 'has')
|
||||
if (!ok) {
|
||||
continue
|
||||
}
|
||||
ok = this.checkItem(action, 'can')
|
||||
action.disabled = !ok
|
||||
validActions.push(action)
|
||||
}
|
||||
return validActions
|
||||
},
|
||||
checkItem(item, attr) {
|
||||
let ok = item[attr]
|
||||
if (typeof ok === 'function') {
|
||||
ok = ok(this.selectedRows)
|
||||
} else if (ok == null) {
|
||||
ok = true
|
||||
}
|
||||
return ok
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,4 +282,9 @@ export default {
|
||||
justify-content:center;
|
||||
}
|
||||
|
||||
.export-item {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
139
src/components/ListTable/formatters/ActionsFormatter.vue
Normal file
139
src/components/ListTable/formatters/ActionsFormatter.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<ActionsGroup :size="'mini'" :actions="actions" :more-actions="moreActions" @actionClick="handleActionClick"></ActionsGroup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
import BaseFormatter from './base'
|
||||
|
||||
const defaultPerformDelete = function({row, col}) {
|
||||
const id = row.id
|
||||
const url = `/api/v1/users/groups/${id}/`
|
||||
return this.$axios.delete(url)
|
||||
}
|
||||
const defaultUpdateCallback = function({row, col}) {
|
||||
const id = row.id
|
||||
const routeName = col.actions.updateRoute || '404'
|
||||
this.$router.push({name: routeName, params: {id: id}})
|
||||
}
|
||||
|
||||
const defaultDeleteCallback = function({row, col, cellValue, reload}) {
|
||||
const msg = this.$tc('Are you sure to delete') + ' "' + row.name + '"'
|
||||
const title = this.$tc('Info')
|
||||
const performDelete = col.actions.performDelete || defaultPerformDelete.bind(this)
|
||||
this.$alert(msg, title, {
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'el-button--danger',
|
||||
showCancelButton: true,
|
||||
beforeClose: async(action, instance, done) => {
|
||||
if (action !== 'confirm') return done()
|
||||
instance.confirmButtonLoading = true
|
||||
try {
|
||||
await performDelete({row: row, col: col})
|
||||
done()
|
||||
reload()
|
||||
this.$message.success(this.$tc('Delete success'))
|
||||
} catch (error) {
|
||||
this.$message.error(this.$tc('Delete failed'))
|
||||
console.warn(error)
|
||||
} finally {
|
||||
instance.confirmButtonLoading = false
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
/* 取消*/
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ActionsFormatter',
|
||||
components: { ActionsGroup },
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
const colActions = this.col.actions || {}
|
||||
const defaultActions = [
|
||||
{
|
||||
name: 'update',
|
||||
title: this.$tc('Update'),
|
||||
type: 'primary',
|
||||
has: colActions.hasUpdate || true,
|
||||
can: colActions.canUpdate || true,
|
||||
callback: colActions.onUpdate || defaultUpdateCallback.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
title: this.$tc('Delete'),
|
||||
type: 'danger',
|
||||
has: colActions.hasDelete || true,
|
||||
can: colActions.canDelete || true,
|
||||
callback: colActions.onDelete || defaultDeleteCallback.bind(this)
|
||||
}
|
||||
]
|
||||
const extraActions = colActions.extraActions || []
|
||||
return {
|
||||
defaultActions: defaultActions,
|
||||
extraActions: extraActions
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
validActions() {
|
||||
const actions = [...this.defaultActions, ...this.extraActions]
|
||||
const validActions = []
|
||||
for (const v of actions) {
|
||||
const has = this.checkItem(v, 'has')
|
||||
if (!has) {
|
||||
continue
|
||||
}
|
||||
const can = this.checkItem(v, 'can')
|
||||
v.disabled = !can
|
||||
validActions.push(v)
|
||||
}
|
||||
return validActions
|
||||
},
|
||||
actions() {
|
||||
return this.validActions.slice(0, 2)
|
||||
},
|
||||
moreActions() {
|
||||
return this.validActions.slice(2, this.validActions.length)
|
||||
},
|
||||
namedValidActions() {
|
||||
const actions = {}
|
||||
for (const action of this.validActions) {
|
||||
if (!action || !action.hasOwnProperty('name')) {
|
||||
continue
|
||||
}
|
||||
actions[action.name] = action
|
||||
}
|
||||
return actions
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleActionClick(item) {
|
||||
const action = this.namedValidActions[item]
|
||||
if (action && action.callback) {
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
action.callback(attrs)
|
||||
}
|
||||
},
|
||||
checkItem(item, attr) {
|
||||
let ok = item[attr]
|
||||
if (typeof ok === 'function') {
|
||||
ok = ok(this.row, this.cellValue)
|
||||
} else if (ok == null) {
|
||||
ok = true
|
||||
}
|
||||
return ok
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -5,6 +5,10 @@
|
||||
export default {
|
||||
name: 'BaseFormatter',
|
||||
props: {
|
||||
reload: {
|
||||
type: Function,
|
||||
default: ({reloading}) => ({})
|
||||
},
|
||||
row: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
@@ -17,7 +21,7 @@ export default {
|
||||
type: [String, Boolean, Number, Object],
|
||||
default: null
|
||||
},
|
||||
setting: {
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => ({})
|
||||
}
|
@@ -1,11 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<TableAction v-bind="headerActions" @clickAction="handleActionClick"></TableAction>
|
||||
<TableAction :table-url="tableConfig.url" :search-table="search" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable"></TableAction>
|
||||
<el-card class="table-content" shadow="never">
|
||||
<DataTable :config="tableConfig" @selection-change="handleSelectionChange">
|
||||
<template v-slot:actions="row">
|
||||
{{ row.id }}
|
||||
</template>
|
||||
<DataTable ref="dataTable" :config="tableConfig" @selection-change="handleSelectionChange">
|
||||
</DataTable>
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -15,6 +12,7 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import DataTable from '../DataTable'
|
||||
import TableAction from './TableAction'
|
||||
|
||||
export default {
|
||||
name: 'ListTable',
|
||||
components: {
|
||||
@@ -35,56 +33,24 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectRows: [],
|
||||
selectedRows: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
actionColumn() {
|
||||
const actions = []
|
||||
let tc = this.tableConfig
|
||||
if (tc.hasEdit !== false) {
|
||||
actions.push({
|
||||
name: 'update',
|
||||
title: this.$tc('Update')
|
||||
})
|
||||
}
|
||||
|
||||
if (tc.hasDelete !== false) {
|
||||
actions.push({
|
||||
name: 'delete',
|
||||
title: this.$tc('Delete')
|
||||
})
|
||||
}
|
||||
hasSelected() {
|
||||
return this.selectedRows.length > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange(val) {
|
||||
this.selectRows = val
|
||||
this.multipleSelection = val;
|
||||
(val.length > 0) ? (this.selectDisable = false) : (this.selectDisable = true)
|
||||
this.selectedRows = val
|
||||
console.log(this.selectedRows)
|
||||
},
|
||||
handleActionClick(item) {
|
||||
const handler = this.getActionHandler(item)
|
||||
handler(this.selectRows)
|
||||
reloadTable() {
|
||||
this.$refs.dataTable.getList()
|
||||
},
|
||||
handleActionCreate() {
|
||||
const routeName = this.headerActions.createRoute || ''
|
||||
this.$router.push({ name: routeName })
|
||||
console.log('handle create')
|
||||
},
|
||||
getActionHandler(item) {
|
||||
let handler = this.headerActions.item
|
||||
const defaultHandlerName = 'handle' + item[0].toUpperCase() + item.slice(1, item.length)
|
||||
if (!handler) {
|
||||
handler = this[defaultHandlerName]
|
||||
}
|
||||
if (!handler) {
|
||||
handler = () => {
|
||||
console.log('No handler found for ', item)
|
||||
}
|
||||
}
|
||||
console.log(handler)
|
||||
return handler
|
||||
search(attrs) {
|
||||
return this.$refs.dataTable.search(attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -144,7 +144,6 @@ export default {
|
||||
}
|
||||
data.results.forEach((v) => {
|
||||
this.options.push(v)
|
||||
console.log(v)
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="form-group-header">
|
||||
<div v-if="line" class="hr-line-dashed" />
|
||||
<h3>{{ title }}</h3>
|
||||
</div>
|
||||
|
@@ -38,9 +38,19 @@ const cn = {
|
||||
'More actions': '更多操作',
|
||||
'Delete selected': '删除所选',
|
||||
'Update selected': '更新所选',
|
||||
'Delete success': '删除成功',
|
||||
'Search': '搜索',
|
||||
'Source': '来源',
|
||||
'Status': '状态'
|
||||
'Status': '状态',
|
||||
'Actions': '动作',
|
||||
'Monitor': '监控',
|
||||
'Run': '执行',
|
||||
'Are you sure to delete': '你确定要删除',
|
||||
'Info': '提示',
|
||||
'More': '更多',
|
||||
'Submit': '提交',
|
||||
'Reset': '重置',
|
||||
'This field is required': '这个字段是必填项'
|
||||
},
|
||||
route: {
|
||||
'dashboard': '仪表盘',
|
||||
@@ -81,6 +91,7 @@ const cn = {
|
||||
'OperateLog': '操作日志',
|
||||
'PasswordChangeLog': '改密日志',
|
||||
'Settings': '系统设置',
|
||||
'UserCreate': '创建用户'
|
||||
},
|
||||
// 用户模块翻译
|
||||
users: {
|
||||
|
@@ -67,13 +67,20 @@ export const constantRoutes = [
|
||||
},
|
||||
{
|
||||
path: 'users/create',
|
||||
component: () => import('@/views/users/UserEdit.vue'), // Parent router-view
|
||||
component: () => import('@/views/users/UserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'UserCreate',
|
||||
hidden: true,
|
||||
meta: { title: 'UserCreate' }
|
||||
meta: { title: 'UserCreate', activeMenu: '/users/users'}
|
||||
},
|
||||
{
|
||||
path: 'users/:id',
|
||||
path: 'users/update/:id',
|
||||
component: () => import('@/views/users/UserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'UserEdit',
|
||||
hidden: true,
|
||||
meta: { title: 'UserEdit' }
|
||||
},
|
||||
{
|
||||
path: 'users/detail/:id',
|
||||
component: () => import('@/views/users/UserDetail.vue'), // Parent router-view
|
||||
name: 'UserDetail',
|
||||
hidden: true,
|
||||
@@ -87,10 +94,10 @@ export const constantRoutes = [
|
||||
},
|
||||
{
|
||||
path: 'groups/:id/update',
|
||||
component: () => import('@/views/users/UserGroupEdit.vue'), // Parent router-view
|
||||
name: 'UserGroupEdit',
|
||||
component: () => import('@/views/users/UserGroupUpdate.vue'), // Parent router-view
|
||||
name: 'UserGroupUpdate',
|
||||
hidden: true,
|
||||
meta: { title: 'UserGroupEdit' }
|
||||
meta: { title: 'UserGroupUpdate' }
|
||||
},
|
||||
{
|
||||
path: 'groups/:id',
|
||||
@@ -101,7 +108,7 @@ export const constantRoutes = [
|
||||
},
|
||||
{
|
||||
path: 'groups/create',
|
||||
component: () => import('@/views/users/UserEdit.vue'), // Parent router-view
|
||||
component: () => import('@/views/users/UserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'UserGroupCreate',
|
||||
hidden: true,
|
||||
meta: { title: 'UserGroupCreate' }
|
||||
|
@@ -162,7 +162,15 @@ td .el-button.el-button--mini {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
height: 35px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.el-input--small .el-input__inner {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.el-input--small .el-input__icon {
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
@@ -223,3 +231,7 @@ td .el-button.el-button--mini {
|
||||
.text-success {
|
||||
color: $--color-success;
|
||||
}
|
||||
|
||||
.el-radio__input.is-checked+.el-radio__label {
|
||||
color: inherit;
|
||||
}
|
||||
|
254
src/views/users/UserCreateUpdate.vue
Normal file
254
src/views/users/UserCreateUpdate.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<Page>
|
||||
<IBox>
|
||||
<DataForm :form="form" :fields="fields">
|
||||
<FormGroupHeader slot="id:name" title="账户" :line="false" />
|
||||
<FormGroupHeader slot="id:passwordrule" title="认证" :line="true" />
|
||||
<FormGroupHeader slot="id:role" title="角色安全" :line="true" />
|
||||
<FormGroupHeader slot="id:phone" title="认证" :line="true" />
|
||||
</DataForm>
|
||||
</IBox>
|
||||
</page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable vue/no-unused-components */
|
||||
import FormGroupHeader from '@/components/formGroupHeader'
|
||||
import { Page, IBox } from '@/layout/components'
|
||||
import DataForm from '@/components/DataForm'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import select2 from '@/components/Select2'
|
||||
export default {
|
||||
components: {
|
||||
Page,
|
||||
IBox,
|
||||
DataForm,
|
||||
select2,
|
||||
FormGroupHeader
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
passwordrule: '1',
|
||||
mfa_level: 0,
|
||||
source: 'local',
|
||||
role: 'Admin',
|
||||
date_expired: '2099-12-31 00:00:00 +0800'
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'input',
|
||||
id: 'name',
|
||||
label: this.$t('users.name'),
|
||||
el: {
|
||||
},
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'username',
|
||||
label: this.$t('users.username'),
|
||||
el: {
|
||||
},
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'email',
|
||||
label: this.$t('users.email'),
|
||||
el: {
|
||||
},
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'users',
|
||||
label: '用户组',
|
||||
el: {
|
||||
placeholder: '添加到用户组',
|
||||
value: [
|
||||
{
|
||||
label: 'hello',
|
||||
value: '1a775bbf-6861-4acb-8ae4-2f684794c8cc'
|
||||
},
|
||||
{
|
||||
label: 'test',
|
||||
value: '4dccdf84-7728-4de0-a507-67c905b3091b'
|
||||
},
|
||||
{
|
||||
label: 'whold',
|
||||
value: 'c5ec4b91-1fb2-478e-89bc-5a4abc0f9c6c'
|
||||
}
|
||||
],
|
||||
url: '/api/v1/users/users/'
|
||||
},
|
||||
// 自定义组件
|
||||
// 可以取到自定义组件的值
|
||||
// https://femessage.github.io/el-form-renderer/#/Guide?id=guide-custom-component
|
||||
|
||||
component: select2
|
||||
}, {
|
||||
type: 'radio-group',
|
||||
id: 'passwordrule',
|
||||
label: '密码策略',
|
||||
el: {
|
||||
},
|
||||
hidden: (formValue, item) => {
|
||||
return this.$route.params.id
|
||||
},
|
||||
options: [{
|
||||
label: '生成重置密码链接,通过邮件发送给用户',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '设置密码',
|
||||
value: '2'
|
||||
}],
|
||||
rules: [
|
||||
{ required: true, message: 'miss resource', trigger: 'change' }
|
||||
]
|
||||
}, {
|
||||
type: 'input',
|
||||
id: 'password',
|
||||
label: '密码',
|
||||
hidden: (formValue, item) => {
|
||||
if (this.$route.params.id === undefined) {
|
||||
return (formValue.passwordrule !== '2')
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
el: {
|
||||
type: 'password'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'sshkey',
|
||||
label: 'ssh公钥',
|
||||
hidden: (formValue, item) => {
|
||||
return !this.$route.params.id
|
||||
},
|
||||
el: {
|
||||
placeholder: 'ssh-rsa AAAA...',
|
||||
type: 'textarea',
|
||||
rows: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'radio-group',
|
||||
id: 'mfa_level',
|
||||
label: '多因子认证',
|
||||
el: {
|
||||
},
|
||||
size: 0,
|
||||
options: [{
|
||||
label: '禁用',
|
||||
value: 0
|
||||
}, {
|
||||
label: '启用',
|
||||
value: 1
|
||||
}, {
|
||||
label: '强制启用',
|
||||
value: 2
|
||||
}],
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
}, {
|
||||
type: 'select',
|
||||
id: 'source',
|
||||
label: '来源',
|
||||
el: {
|
||||
},
|
||||
default: '数据库',
|
||||
options: [{
|
||||
label: '数据库',
|
||||
value: 'local'
|
||||
}],
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
id: 'role',
|
||||
label: '角色',
|
||||
el: {
|
||||
},
|
||||
default: 'User',
|
||||
options: [{
|
||||
label: '管理员',
|
||||
value: 'Admin'
|
||||
}, {
|
||||
label: '用户',
|
||||
value: 'User'
|
||||
}, {
|
||||
label: '审计员',
|
||||
value: 'Auditor'
|
||||
}],
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'date-picker',
|
||||
id: 'date_expired',
|
||||
label: '过期时间',
|
||||
el: {
|
||||
type: 'datetime',
|
||||
placeholder: 'select date'
|
||||
},
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'phone',
|
||||
label: '手机',
|
||||
el: {
|
||||
}
|
||||
}, {
|
||||
type: 'input',
|
||||
id: 'wechat',
|
||||
label: '微信',
|
||||
el: {
|
||||
}
|
||||
}, {
|
||||
type: 'input',
|
||||
id: 'comment',
|
||||
label: '备注',
|
||||
el: {
|
||||
type: 'textarea',
|
||||
row: '4'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
debug() {
|
||||
console.log(this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('>>>>>>>>>>')
|
||||
console.log(rules)
|
||||
console.log(rules.Required)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-form /deep/ .el-select{
|
||||
width:100%;
|
||||
}
|
||||
.el-form /deep/ .el-form-item__content > .el-date-editor{
|
||||
width:100%;
|
||||
}
|
||||
</style>
|
@@ -1,162 +0,0 @@
|
||||
<template>
|
||||
<Page>
|
||||
<template>
|
||||
<el-card>
|
||||
<dataform :content="content" label-position="left" label-width="140px" :form="form">
|
||||
<formgroupheader slot="id:name" title="账户" :line="false" style="margin:0 50px;" />
|
||||
<formgroupheader slot="id:passwordrule" title="认证" :line="true" style="margin:0 50px;" />
|
||||
</dataform>
|
||||
</el-card>
|
||||
</template>
|
||||
</page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable vue/no-unused-components */
|
||||
import formgroupheader from '@/components/formGroupHeader'
|
||||
import { Page } from '@/layout/components'
|
||||
import dataform from '@/components/DataForm'
|
||||
import select2 from '@/components/Select2'
|
||||
export default {
|
||||
components: {
|
||||
Page,
|
||||
dataform,
|
||||
select2,
|
||||
formgroupheader
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'input',
|
||||
id: 'name',
|
||||
label: this.$t('users.name'),
|
||||
el: {
|
||||
size: 'mini'
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: 'miss name', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'username',
|
||||
label: this.$t('users.username'),
|
||||
el: {
|
||||
size: 'mini'
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: 'miss name', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'email',
|
||||
label: this.$t('users.email'),
|
||||
el: {
|
||||
size: 'mini'
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: 'miss name', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'users',
|
||||
label: '用户组',
|
||||
el: {
|
||||
size: 'mini',
|
||||
placeholder: '添加到用户组',
|
||||
value: [
|
||||
{
|
||||
label: 'hello',
|
||||
value: '1a775bbf-6861-4acb-8ae4-2f684794c8cc'
|
||||
},
|
||||
{
|
||||
label: 'test',
|
||||
value: '4dccdf84-7728-4de0-a507-67c905b3091b'
|
||||
},
|
||||
{
|
||||
label: 'whold',
|
||||
value: 'c5ec4b91-1fb2-478e-89bc-5a4abc0f9c6c'
|
||||
}
|
||||
],
|
||||
url: '/api/v1/users/users/'
|
||||
},
|
||||
// 自定义组件
|
||||
// 可以取到自定义组件的值
|
||||
// https://femessage.github.io/el-form-renderer/#/Guide?id=guide-custom-component
|
||||
|
||||
component: select2
|
||||
}, {
|
||||
type: 'radio-group',
|
||||
id: 'passwordrule',
|
||||
label: '密码策略',
|
||||
el: {
|
||||
size: 'small'
|
||||
},
|
||||
options: [{
|
||||
label: '生成重置密码链接,通过邮件发送给用户',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '设置密码',
|
||||
value: '2'
|
||||
}],
|
||||
rules: [
|
||||
{ required: true, message: 'miss resource', trigger: 'change' }
|
||||
]
|
||||
}, {
|
||||
type: 'input',
|
||||
id: 'password',
|
||||
label: '密码',
|
||||
hidden: (formValue, item) => {
|
||||
console.log(formValue, item)
|
||||
formValue.passwordrule !== '2'
|
||||
},
|
||||
el: {
|
||||
size: 'mini',
|
||||
type: 'password'
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: 'miss name', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'radio-group',
|
||||
id: 'mfa',
|
||||
label: '密码策略',
|
||||
el: {
|
||||
size: 'small'
|
||||
},
|
||||
options: [{
|
||||
label: '禁用'
|
||||
}, {
|
||||
label: '启用'
|
||||
}, {
|
||||
label: '强制启用'
|
||||
}],
|
||||
rules: [
|
||||
{ required: true, message: 'miss resource', trigger: 'change' }
|
||||
]
|
||||
}, {
|
||||
type: 'select',
|
||||
id: 'source',
|
||||
label: '来源',
|
||||
el: {
|
||||
size: 'small'
|
||||
},
|
||||
options: [{
|
||||
label: 'area1',
|
||||
value: 'shanghai'
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-form /deep/ .el-select{
|
||||
width:100%;
|
||||
}
|
||||
</style>
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { DetailFormatter, ActionsFormatter } from '@/components/DataTable/formatters/index'
|
||||
import { DetailFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -15,21 +15,17 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/users/groups/',
|
||||
columns: [
|
||||
{
|
||||
label: 'ID',
|
||||
type: 'index'
|
||||
},
|
||||
{
|
||||
prop: 'name',
|
||||
label: this.$tc('Name'),
|
||||
formatter: DetailFormatter,
|
||||
sortable: true,
|
||||
route: 'UserDetail'
|
||||
route: 'UserGroupDetail'
|
||||
},
|
||||
{
|
||||
prop: 'users_amount',
|
||||
label: this.$t('users.User'),
|
||||
key: 'users_amount',
|
||||
key: 'users_amount'
|
||||
},
|
||||
{
|
||||
prop: 'comment',
|
||||
@@ -38,55 +34,27 @@ export default {
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
label: this.$tc('Action'),
|
||||
label: this.$tc('Actions'),
|
||||
align: 'center',
|
||||
formatter: ActionsFormatter,
|
||||
width: '200px',
|
||||
actions: {
|
||||
hasUpdate: (row, cellValue) => {
|
||||
return true
|
||||
},
|
||||
canUpdate: (row, cellValue) => {
|
||||
console.log('On table update')
|
||||
return true
|
||||
},
|
||||
hasDelete: true,
|
||||
canDelete: (row, cellValue) => {
|
||||
return true
|
||||
},
|
||||
onDelete: (row, cellValue) => {
|
||||
this.$confirm('你好啊', '提示', {
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'el-button--danger',
|
||||
beforeClose: async(action, instance, done) => {
|
||||
}
|
||||
}).catch(() => {
|
||||
/* 取消*/
|
||||
})
|
||||
},
|
||||
updateRoute: 'UserGroupUpdate',
|
||||
extraActions: [
|
||||
{
|
||||
name: 'run',
|
||||
title: this.$tc('Run'),
|
||||
callback: (row, cellValue) => {
|
||||
console.log('On run')
|
||||
title: this.$tc('Run')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'monitor',
|
||||
title: this.$tc('Monitor')
|
||||
}
|
||||
],
|
||||
order: []
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
// 写路由名字,table组件会自动传作为参数
|
||||
tableActions: {
|
||||
editRoute: '404'
|
||||
}
|
||||
]
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: 'UserGroupCreate'
|
||||
createRoute: 'UserGroupCreate',
|
||||
performBulkDelete: function(rows) {
|
||||
console.log('hello')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<Page>
|
||||
<template>
|
||||
<el-card>
|
||||
<dataform :content="content" label-position="right" label-width="100px" :form="form" />
|
||||
<dataform fields="content" label-position="right" label-width="100px" :form="form" />
|
||||
</el-card>
|
||||
</template>
|
||||
</page>
|
||||
@@ -11,7 +11,7 @@
|
||||
<script>
|
||||
/* eslint-disable vue/no-unused-components */
|
||||
import { Page } from '@/layout/components'
|
||||
import dataform from '@/components/DataForm'
|
||||
import DataForm from '@/components/DataForm'
|
||||
import select2 from '@/components/Select2'
|
||||
export default {
|
||||
components: {
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { DetailFormatter, DisplayFormatter, ChoicesFormatter } from '@/components/DataTable/formatters/index'
|
||||
import { DetailFormatter, DisplayFormatter, ChoicesFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -49,6 +49,22 @@ export default {
|
||||
formatter: ChoicesFormatter,
|
||||
align: 'center',
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
label: this.$tc('Actions'),
|
||||
align: 'center',
|
||||
formatter: ActionsFormatter,
|
||||
width: '200px',
|
||||
actions: {
|
||||
updateRoute: 'UserUpdate',
|
||||
extraActions: [
|
||||
{
|
||||
name: 'run',
|
||||
title: this.$tc('Run')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
// 写路由名字,table组件会自动传作为参数
|
||||
@@ -57,7 +73,17 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: 'UserGroupCreate'
|
||||
createRoute: 'UserCreate',
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'deactiveSelected',
|
||||
title: this.$tc('Deactive selected')
|
||||
},
|
||||
{
|
||||
name: 'activeSelected',
|
||||
title: this.$tc('Active selected')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user