mirror of
https://github.com/jumpserver/lina.git
synced 2025-04-27 03:00:47 +00:00
360 lines
9.3 KiB
Vue
360 lines
9.3 KiB
Vue
<template>
|
||
<IBox :title="title" :type="type" class="the-box" v-bind="$attrs">
|
||
<table class="CardTable" style="width: 100%;table-layout:fixed;">
|
||
<tr>
|
||
<td colspan="2">
|
||
<Select2 ref="select2" v-model="select2.value" :disabled="iDisabled" show-select-all v-bind="select2" />
|
||
</td>
|
||
</tr>
|
||
<slot />
|
||
<tr>
|
||
<td colspan="2">
|
||
<el-button :disabled="iDisabled" :loading="submitLoading" :type="type" size="small" @click="addObjects">
|
||
{{ $t('Add') }}
|
||
</el-button>
|
||
</td>
|
||
</tr>
|
||
<template v-if="showHasObjects">
|
||
<tr v-for="obj of iHasObjects" :key="obj.value" class="item">
|
||
<td style="width: 100%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
|
||
<el-tooltip
|
||
:content="obj.label.toString()"
|
||
:open-delay="500"
|
||
effect="dark"
|
||
placement="left"
|
||
style="margin: 4px;"
|
||
>
|
||
<b>{{ obj.label }}</b>
|
||
</el-tooltip>
|
||
</td>
|
||
<td>
|
||
<el-button :disabled="iDisabled" size="mini" style="float: right" type="danger" @click="removeObject(obj)">
|
||
<i class="fa fa-minus" />
|
||
</el-button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
<tr v-if="params.hasMore && showHasMore" class="item">
|
||
<td colspan="2">
|
||
<el-button :disabled="iDisabled" :type="type" size="small" style="width: 100%" @click="loadMore">
|
||
<i class="fa fa-arrow-down" />
|
||
{{ $t('More') }}
|
||
</el-button>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</IBox>
|
||
</template>
|
||
|
||
<script>
|
||
import { createSourceIdCache } from '@/api/common'
|
||
import { Select2 } from '@/components/Form/FormFields'
|
||
import IBox from '@/components/IBox/index.vue'
|
||
import { mapGetters } from 'vuex'
|
||
|
||
export default {
|
||
name: 'RelationCard',
|
||
components: {
|
||
Select2,
|
||
IBox
|
||
},
|
||
props: {
|
||
title: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
icon: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
type: {
|
||
type: String,
|
||
default: 'primary'
|
||
},
|
||
// 地址,发送给select2的,查询所有的objects, 和select2 ajax一样
|
||
objectsAjax: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
objects: {
|
||
type: [Array, null],
|
||
default: null
|
||
},
|
||
// 已选择的objects Id, 会转换成select2的value, 作为默认选择项, 和objectsAjax类似
|
||
hasObjectsId: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
hasObjects: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
showHasObjects: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
value: {
|
||
type: [Array, Number, String],
|
||
default: () => []
|
||
},
|
||
disabled: {
|
||
type: [Boolean, Function],
|
||
default: null
|
||
},
|
||
showHasMore: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
performDelete: {
|
||
type: Function,
|
||
default: (obj, that) => {
|
||
}
|
||
},
|
||
allowCreate: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
select2Config: {
|
||
type: Object,
|
||
default: () => {
|
||
}
|
||
},
|
||
onDeleteSuccess: {
|
||
type: Function,
|
||
default(obj, that) {
|
||
// 从 hasObjects 中移除这个object
|
||
const theRemoveIndex = that.iHasObjects.findIndex((v) => v.value === obj.value)
|
||
that.iHasObjects.splice(theRemoveIndex, 1)
|
||
|
||
// 从 disabled values 中移除这个 value
|
||
while (that.select2.disabledValues.indexOf(obj.value) !== -1) {
|
||
const i = that.select2.disabledValues.indexOf(obj.value)
|
||
that.$log.debug('disabled values remove index: ', i)
|
||
that.select2.disabledValues.splice(i, 1)
|
||
}
|
||
|
||
that.$message.success(that.$t('RemoveSuccessMsg'))
|
||
}
|
||
},
|
||
onDeleteFail: {
|
||
type: Function,
|
||
default(error, that) {
|
||
let msg = ''
|
||
const data = error.response.data
|
||
for (const item of Object.keys(data)) {
|
||
const value = data[item]
|
||
if (value instanceof Array) {
|
||
msg = value.join(',')
|
||
} else {
|
||
msg = value
|
||
}
|
||
}
|
||
that.$message.error(msg)
|
||
}
|
||
},
|
||
performAdd: {
|
||
type: Function,
|
||
default: (objects, that) => {
|
||
}
|
||
},
|
||
showAddAll: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
onAddSuccess: {
|
||
type: Function,
|
||
default(objects, that) {
|
||
that.$log.debug('Select value', that.select2.value)
|
||
const oldValues = that.iHasObjects.map(item => item.value)
|
||
that.iHasObjects = [...that.iHasObjects, ...objects.filter(item => !oldValues.includes(item.value))]
|
||
that.$refs.select2.clearSelected()
|
||
that.$message.success(that.$t('AddSuccessMsg'))
|
||
this.$refs.select2.refresh()
|
||
this.$emit('addSuccess')
|
||
}
|
||
},
|
||
getHasObjects: {
|
||
type: Function,
|
||
default: null // (objectIds) => {}
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
iHasObjects: this.hasObjects || [],
|
||
totalHasObjectsLength: 0,
|
||
submitLoading: false,
|
||
selectAllDisabled: false,
|
||
params: {
|
||
page: 1,
|
||
hasMore: false,
|
||
pageSize: 10
|
||
},
|
||
select2: {
|
||
ajax: this.objectsAjax,
|
||
options: this.objects,
|
||
value: this.value,
|
||
disabled: this.disabled,
|
||
disabledValues: [],
|
||
allowCreate: this.allowCreate,
|
||
...this.select2Config
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
...mapGetters(['currentOrgIsRoot']),
|
||
iAjax() {
|
||
return this.$refs.select2.iAjax
|
||
},
|
||
safeMakeParams() {
|
||
return this.$refs.select2.safeMakeParams
|
||
},
|
||
hasObjectLeftLength() {
|
||
return this.totalHasObjectsLength - this.iHasObjects.length
|
||
},
|
||
iDisabled() {
|
||
if (this.disabled !== null) {
|
||
return this.disabled
|
||
}
|
||
return this.currentOrgIsRoot
|
||
}
|
||
},
|
||
watch: {
|
||
hasObjectsId(iNew, iOld) {
|
||
this.$log.debug('hasObject id change')
|
||
this.select2.disabledValues = iNew
|
||
},
|
||
iHasObjects(iNew, iOld) {
|
||
const newValues = iNew.map(v => v.value)
|
||
const oldValues = iOld.map(v => v.value)
|
||
const addValues = _.difference(newValues, oldValues)
|
||
const removeValues = _.difference(oldValues, newValues)
|
||
this.$log.debug('hasObjects change, add ', addValues, 'remove ', removeValues)
|
||
let disabledValues = this.select2.disabledValues
|
||
if (removeValues.length > 0) {
|
||
disabledValues = disabledValues.filter((v) => {
|
||
return removeValues.indexOf(v) === -1
|
||
})
|
||
}
|
||
if (addValues.length > 0) {
|
||
disabledValues = [...disabledValues, ...addValues]
|
||
}
|
||
this.select2.disabledValues = disabledValues
|
||
}
|
||
},
|
||
mounted() {
|
||
if (this.hasObjectsId && this.hasObjectsId.length !== 0) {
|
||
setTimeout(() => {
|
||
this.getHasObjectsByIds()
|
||
}, 50)
|
||
}
|
||
},
|
||
methods: {
|
||
async loadMore() {
|
||
if (this.loading) {
|
||
return
|
||
}
|
||
if (!this.params.hasMore) {
|
||
return
|
||
}
|
||
this.params.page = this.params.page ? this.params.page + 1 : 1
|
||
try {
|
||
this.loading = true
|
||
await this.loadHasObjects()
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
async loadHasObjects() {
|
||
this.$log.debug('Start loadHasObject: ', this.params)
|
||
const params = this.safeMakeParams(this.params)
|
||
let data = await this.$axios.get(this.iAjax.url, {
|
||
params: params,
|
||
validateStatus: (status) => {
|
||
if (status === 403) {
|
||
return 200
|
||
}
|
||
return status
|
||
}
|
||
})
|
||
data = this.iAjax.processResults.bind(this)(data)
|
||
if (data.results) {
|
||
data.results.forEach((v) => {
|
||
if (!this.iHasObjects.find((item) => item.value === v.value)) {
|
||
this.iHasObjects.push(v)
|
||
}
|
||
})
|
||
}
|
||
// 如果还有其它页,继续获取, 如果没有就停止
|
||
this.params.hasMore = !!data.pagination
|
||
this.totalHasObjectsLength = data.total
|
||
},
|
||
async getHasObjectsByIds() {
|
||
if (!this.$refs.select2 || !this.iAjax || !this.safeMakeParams) {
|
||
return
|
||
}
|
||
this.select2.disabledValues = this.hasObjectsId
|
||
|
||
if (this.getHasObjects) {
|
||
this.getHasObjects(this.hasObjectsId).then((data) => {
|
||
this.iHasObjects = data
|
||
})
|
||
} else {
|
||
const resp = await createSourceIdCache(this.hasObjectsId)
|
||
this.params.spm = resp.spm
|
||
await this.loadHasObjects()
|
||
}
|
||
},
|
||
removeObject(obj) {
|
||
this.performDelete(obj, this).then(() => {
|
||
this.onDeleteSuccess(obj, this)
|
||
}).catch(error => {
|
||
this.onDeleteFail(error, this)
|
||
})
|
||
},
|
||
addObjects() {
|
||
const objects = this.$refs.select2.$refs.select.selected.map(item => ({ label: item.label, value: item.value }))
|
||
if (objects.length === 0) {
|
||
return
|
||
}
|
||
this.performAdd(objects, this).then(
|
||
() => {
|
||
this.onAddSuccess(objects, this)
|
||
}
|
||
)
|
||
},
|
||
async selectAll() {
|
||
this.selectAllDisabled = true
|
||
this.disabled = true
|
||
await this.$refs.select2.selectAll()
|
||
this.selectAllDisabled = false
|
||
this.disabled = false
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang='scss' scoped>
|
||
b, strong {
|
||
font-weight: 700;
|
||
font-size: 13px;
|
||
}
|
||
|
||
tr td {
|
||
line-height: 1.4;
|
||
padding: 8px 0;
|
||
vertical-align: top;
|
||
}
|
||
|
||
tr.item td {
|
||
border-top: 1px dashed #EBEEF5;
|
||
}
|
||
|
||
.box-margin {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.the-box ::v-deep .el-card__body {
|
||
padding: 20px;
|
||
}
|
||
</style>
|