mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-25 06:19:51 +00:00
[Update] 拆分GenricCreateUpdateForm
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
<IBox :title="title" fa="fa-info-circle">
|
||||
<div class="content">
|
||||
<el-row v-for="item in items" :key="'card-' + item.key" class="item" :gutter="10">
|
||||
<el-col :span="6"><div class="item-label" :style="{ 'text-align': align}"><label>{{ item.key }}: </label></div></el-col>
|
||||
<el-col :span="18"><div class="item-text">{{ item.value }}</div></el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</IBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IBox from '../IBox'
|
||||
|
||||
export default {
|
||||
name: 'DetailCard',
|
||||
components: { IBox },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-card :class="'ibox ' + type" shadow="never" v-bind="$attrs">
|
||||
<div v-if="title" slot="header" class="clearfix ibox-title">
|
||||
<h5><i v-if="fa" :class="'fa ' + fa" /> {{ title }}</h5>
|
||||
<i v-if="fa" :class="'fa ' + fa" /> {{ title }}
|
||||
</div>
|
||||
<slot />
|
||||
</el-card>
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
default: 'default'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -44,14 +44,16 @@ export default {
|
||||
border-color: #e7eaec;
|
||||
border-image: none;
|
||||
margin-bottom: 0;
|
||||
padding: 14px 15px 7px;
|
||||
min-height: 48px;
|
||||
padding: 10px 15px;
|
||||
min-height: 30px;
|
||||
line-height: 1.32;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.ibox-title h5 {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
margin: 0 0 7px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-overflow: ellipsis;
|
||||
float: left;
|
||||
|
@@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<IBox fa="fa-edit" :title="title" v-bind="$attrs" />
|
||||
<IBox fa="fa-edit" :title="title" v-bind="$attrs">
|
||||
<div class="quick-actions">
|
||||
<slot />
|
||||
</div>
|
||||
</IBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -22,5 +26,22 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.quick-actions >>> table {
|
||||
width: 100%;
|
||||
}
|
||||
.quick-actions >>> tr > td {
|
||||
line-height: 1.43;
|
||||
padding: 8px;
|
||||
vertical-align: top;
|
||||
font-size: 13px;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.quick-actions >>> tr > td > span:last-child {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.quick-actions >>> button {
|
||||
padding: 1px 13px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<IBox :fa="icon" :title="title" v-bind="$attrs">
|
||||
<IBox :fa="icon" :type="type" :title="title" v-bind="$attrs">
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
@@ -8,7 +8,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<el-button type="primary" size="small" :loading="submitLoading" @click="addObjects">{{ $tc('Add') }}</el-button>
|
||||
<el-button :type="type" size="small" :loading="submitLoading" @click="addObjects">{{ $tc('Add') }}</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="obj of iHasObjects" :key="obj.value" style="width: 100%" class="item">
|
||||
@@ -21,7 +21,7 @@
|
||||
</tr>
|
||||
<tr v-if="params.hasMore" class="item">
|
||||
<td colspan="2">
|
||||
<el-button type="primary" size="small" style="width: 100%" @click="loadMore">
|
||||
<el-button :type="type" size="small" style="width: 100%" @click="loadMore">
|
||||
<i class="fa fa-arrow-down" />
|
||||
{{ $tc('More') }}
|
||||
</el-button>
|
||||
@@ -51,6 +51,10 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
// 地址,发送给select2的,查询所有的objects, 和select2 ajax一样
|
||||
objectsAjax: {
|
||||
type: Object,
|
||||
|
57
src/components/Swicher/index.vue
Normal file
57
src/components/Swicher/index.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-switch
|
||||
v-model="iValue"
|
||||
:active-color="activeColor"
|
||||
inactive-color="#dcdfe6"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Switcher',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeColor() {
|
||||
// $--color-primary: #1ab394;
|
||||
// $--color-success: #1c84c6;
|
||||
// $--color-info: #23c6c8;
|
||||
// $--color-warning: #f8ac59;
|
||||
// $--color-danger: #ed5565;
|
||||
switch (this.type) {
|
||||
case 'primary':
|
||||
return '#1ab394'
|
||||
case 'danger':
|
||||
return '#ed5565'
|
||||
default:
|
||||
return '#1ab394'
|
||||
}
|
||||
},
|
||||
iValue: {
|
||||
set: function(newValue) {
|
||||
this.$emit('input', newValue)
|
||||
},
|
||||
get: function() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -19,3 +19,4 @@ export { default as SvgIcon } from './SvgIcon'
|
||||
export { default as TreeTable } from './TreeTable'
|
||||
export { default as IBox } from './IBox'
|
||||
export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './Swicher'
|
||||
|
@@ -84,7 +84,8 @@ const cn = {
|
||||
'Username': '用户名',
|
||||
'Email': '邮件',
|
||||
'Role': '角色',
|
||||
'Date expired': '失效日期'
|
||||
'Date expired': '失效日期',
|
||||
'Quick update': '快速更新'
|
||||
},
|
||||
route: {
|
||||
'dashboard': '仪表盘',
|
||||
|
174
src/layout/components/GenericCreateUpdateForm/index.vue
Normal file
174
src/layout/components/GenericCreateUpdateForm/index.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
ref="form"
|
||||
:method="method"
|
||||
:form="form"
|
||||
:fields="fields"
|
||||
:url="totalUrl"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@submit="handleSubmit"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
export default {
|
||||
name: 'GenericCreateUpdateForm',
|
||||
components: {
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
object: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
initial: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
cleanFormValue: {
|
||||
type: Function,
|
||||
default: (value) => value
|
||||
},
|
||||
onSubmit: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
performSubmit: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
createSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$tc('Create success')
|
||||
}
|
||||
},
|
||||
updateSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$tc('Update success')
|
||||
}
|
||||
},
|
||||
createSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
const routeName = this.$route.name.replace('Create', 'List')
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
updateSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
const routeName = this.$route.name.replace('Update', 'List')
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
getMethod: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
},
|
||||
getUrl: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = `${url}/${params.id}/`
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
method() {
|
||||
return this.getMethod(this)
|
||||
},
|
||||
totalUrl() {
|
||||
return this.getUrl()
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.loading = true
|
||||
try {
|
||||
this.form = await this.getFormValue()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit(values) {
|
||||
let handler = this.onSubmit || this.defaultOnSubmit
|
||||
handler = handler.bind(this)
|
||||
values = this.cleanFormValue(values)
|
||||
return handler(values)
|
||||
},
|
||||
defaultPerformSubmit(validValues) {
|
||||
return this.$axios[this.method](this.totalUrl, validValues)
|
||||
},
|
||||
defaultOnSubmit(validValues) {
|
||||
const performSubmit = this.performSubmit || this.defaultPerformSubmit
|
||||
const msg = this.method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
|
||||
const route = this.method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute
|
||||
performSubmit(validValues).then(() => {
|
||||
this.$message.success(msg)
|
||||
this.$router.push(route)
|
||||
}).catch(error => {
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
async getFormValue() {
|
||||
if (this.method !== 'put') {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
if (this.object === null) {
|
||||
this.object = await this.getObjectDetail()
|
||||
}
|
||||
return this.object
|
||||
},
|
||||
async getObjectDetail() {
|
||||
return this.$axios.get(this.totalUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ibox >>> .el-card__body {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
</style>
|
@@ -1,173 +1,18 @@
|
||||
<template>
|
||||
<Page v-loading="loading">
|
||||
<IBox v-if="!loading">
|
||||
<AutoDataForm
|
||||
ref="form"
|
||||
:method="method"
|
||||
:form="form"
|
||||
:fields="fields"
|
||||
:url="totalUrl"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@submit="handleSubmit"
|
||||
/>
|
||||
<Page>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$attrs" v-on="$listeners" />
|
||||
</IBox>
|
||||
</Page>
|
||||
</template>
|
||||
<script>
|
||||
import { IBox } from '@/components'
|
||||
import { Page } from '@/layout/components'
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import GenericCreateUpdateForm from '../GenericCreateUpdateForm'
|
||||
export default {
|
||||
name: 'GenericCreateUpdatePage',
|
||||
components: {
|
||||
Page, IBox, AutoDataForm
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
object: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
initial: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
cleanFormValue: {
|
||||
type: Function,
|
||||
default: (value) => value
|
||||
},
|
||||
onSubmit: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
performSubmit: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
createSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$tc('Create success')
|
||||
}
|
||||
},
|
||||
updateSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$tc('Update success')
|
||||
}
|
||||
},
|
||||
createSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
const routeName = this.$route.name.replace('Create', 'List')
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
updateSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
const routeName = this.$route.name.replace('Update', 'List')
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
getMethod: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
},
|
||||
getUrl: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = `${url}/${params.id}/`
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
method() {
|
||||
return this.getMethod(this)
|
||||
},
|
||||
totalUrl() {
|
||||
return this.getUrl()
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.loading = true
|
||||
try {
|
||||
this.form = await this.getFormValue()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit(values) {
|
||||
let handler = this.onSubmit || this.defaultOnSubmit
|
||||
handler = handler.bind(this)
|
||||
values = this.cleanFormValue(values)
|
||||
return handler(values)
|
||||
},
|
||||
defaultPerformSubmit(validValues) {
|
||||
return this.$axios[this.method](this.totalUrl, validValues)
|
||||
},
|
||||
defaultOnSubmit(validValues) {
|
||||
const performSubmit = this.performSubmit || this.defaultPerformSubmit
|
||||
const msg = this.method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
|
||||
const route = this.method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute
|
||||
performSubmit(validValues).then(() => {
|
||||
this.$message.success(msg)
|
||||
this.$router.push(route)
|
||||
}).catch(error => {
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
async getFormValue() {
|
||||
if (this.method !== 'put') {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
if (this.object === null) {
|
||||
this.object = await this.getObjectDetail()
|
||||
}
|
||||
return this.object
|
||||
},
|
||||
async getObjectDetail() {
|
||||
return this.$axios.get(this.totalUrl)
|
||||
}
|
||||
Page, IBox, GenericCreateUpdateForm
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -4,21 +4,82 @@
|
||||
<DetailCard :title="cardTitle" :items="detailItems" />
|
||||
</el-col>
|
||||
<el-col :md="10" :sm="24">
|
||||
<QuickActions :type="'success'" />
|
||||
<RelationCard v-bind="relationConfig" />
|
||||
<QuickActions type="primary">
|
||||
<table>
|
||||
<tr>
|
||||
<td>激活中:</td>
|
||||
<td>
|
||||
<span>
|
||||
<Switcher v-model="value" />
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>多因子认证:</td>
|
||||
<td>
|
||||
<span>
|
||||
<el-slider
|
||||
v-model="value2"
|
||||
:min="0"
|
||||
:max="2"
|
||||
:step="1"
|
||||
format-tooltip=""
|
||||
show-stops
|
||||
style="width: 50px"
|
||||
/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>重置多因子认证:</td>
|
||||
<td>
|
||||
<span>
|
||||
<el-button type="primary" size="mini">重置</el-button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>发送重置密码邮件:</td>
|
||||
<td>
|
||||
<span>
|
||||
<el-button type="primary" size="mini">发送</el-button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>发送重置密钥邮件:</td>
|
||||
<td>
|
||||
<span>
|
||||
<el-button type="primary" size="mini">发送</el-button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="display:none">
|
||||
<td>解除登录限制</td>
|
||||
<td>
|
||||
<span>
|
||||
<el-button type="primary" size="mini">解除</el-button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</QuickActions>
|
||||
<RelationCard type="info" style="margin-top: 15px" v-bind="relationConfig" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DetailCard, RelationCard, QuickActions } from '@/components'
|
||||
import { DetailCard, RelationCard, QuickActions, Switcher } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'UserInfo',
|
||||
components: {
|
||||
DetailCard,
|
||||
RelationCard,
|
||||
QuickActions
|
||||
QuickActions,
|
||||
Switcher
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -28,6 +89,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: false,
|
||||
value2: 0,
|
||||
relationConfig: {
|
||||
icon: 'fa-user',
|
||||
title: this.$t('users.User groups'),
|
||||
@@ -116,8 +179,7 @@ export default {
|
||||
this.relationConfig.hasObjectsId = iNew.users
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
Reference in New Issue
Block a user