mirror of
https://github.com/veops/cmdb.git
synced 2025-09-03 19:26:56 +08:00
Compare commits
6 Commits
v2.4.16
...
fix_acl_da
Author | SHA1 | Date | |
---|---|---|---|
|
ea4ea9d6b6 | ||
|
1c5d2c8e9e | ||
|
6bd3de8951 | ||
|
a0ff3d69cb | ||
|
fccf5db886 | ||
|
95b55d2963 |
@@ -256,10 +256,10 @@ def cmdb_trigger():
|
||||
trigger2cis[trigger.id] = (trigger, ready_cis)
|
||||
else:
|
||||
cur = trigger2cis[trigger.id]
|
||||
cur_ci_ids = {i.ci_id for i in cur[1]}
|
||||
cur_ci_ids = {_ci.ci_id for _ci in cur[1]}
|
||||
trigger2cis[trigger.id] = (
|
||||
trigger, cur[1] + [i for i in ready_cis if i.ci_id not in cur_ci_ids
|
||||
and i.ci_id not in trigger2completed.get(trigger.id, {})])
|
||||
trigger, cur[1] + [_ci for _ci in ready_cis if _ci.ci_id not in cur_ci_ids
|
||||
and _ci.ci_id not in trigger2completed.get(trigger.id, {})])
|
||||
|
||||
for tid in trigger2cis:
|
||||
trigger, cis = trigger2cis[tid]
|
||||
|
@@ -295,7 +295,7 @@ class CIManager(object):
|
||||
db.session.commit()
|
||||
|
||||
value_table = TableMap(attr_name=attr.name).table
|
||||
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name)):
|
||||
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name), expire=10):
|
||||
max_v = value_table.get_by(attr_id=attr.id, only_query=True).order_by(
|
||||
getattr(value_table, 'value').desc()).first()
|
||||
if max_v is not None:
|
||||
@@ -393,7 +393,7 @@ class CIManager(object):
|
||||
ci = None
|
||||
record_id = None
|
||||
password_dict = {}
|
||||
with redis_lock.Lock(rd.r, ci_type.name):
|
||||
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
|
||||
db.session.commit()
|
||||
|
||||
if (unique_key.default and unique_key.default.get('default') == AttributeDefaultValueEnum.AUTO_INC_ID and
|
||||
@@ -550,7 +550,7 @@ class CIManager(object):
|
||||
limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {}
|
||||
|
||||
record_id = None
|
||||
with redis_lock.Lock(rd.r, ci_type.name):
|
||||
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
|
||||
db.session.commit()
|
||||
|
||||
self._valid_unique_constraint(ci.type_id, ci_dict, ci_id)
|
||||
@@ -1268,7 +1268,9 @@ class CIRelationManager(object):
|
||||
else:
|
||||
type_relation = CITypeRelation.get_by_id(relation_type_id)
|
||||
|
||||
with redis_lock.Lock(rd.r, "ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id)):
|
||||
with redis_lock.Lock(rd.r,
|
||||
"ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id),
|
||||
expire=10):
|
||||
|
||||
cls._check_constraint(first_ci_id, first_ci.type_id, second_ci_id, second_ci.type_id, type_relation)
|
||||
|
||||
|
@@ -122,7 +122,7 @@ class RackManager(DCIMBase):
|
||||
CIManager().update(rack['_id'], **payload)
|
||||
|
||||
def add_device(self, rack_id, device_id, u_start, u_count=None):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
|
||||
self.calc_u_free_count(rack_id, device_id, u_start, u_count)
|
||||
|
||||
self.add_relation(rack_id, device_id)
|
||||
@@ -139,7 +139,7 @@ class RackManager(DCIMBase):
|
||||
OperateHistoryManager().add(operate_type=OperateTypeEnum.ADD_DEVICE, rack_id=rack_id, ci_id=device_id)
|
||||
|
||||
def remove_device(self, rack_id, device_id):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
|
||||
CIRelationManager.delete_3(rack_id, device_id, apply_async=False, valid=False)
|
||||
|
||||
payload = {RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id)}
|
||||
@@ -151,7 +151,7 @@ class RackManager(DCIMBase):
|
||||
OperateHistoryManager().add(operate_type=OperateTypeEnum.REMOVE_DEVICE, rack_id=rack_id, ci_id=device_id)
|
||||
|
||||
def move_device(self, rack_id, device_id, to_u_start):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
|
||||
payload = {RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id, device_id, to_u_start)}
|
||||
CIManager().update(rack_id, _sync=True, **payload)
|
||||
|
||||
@@ -160,7 +160,7 @@ class RackManager(DCIMBase):
|
||||
OperateHistoryManager().add(operate_type=OperateTypeEnum.MOVE_DEVICE, rack_id=rack_id, ci_id=device_id)
|
||||
|
||||
def migrate_device(self, rack_id, device_id, to_rack_id, to_u_start):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
|
||||
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
|
||||
self.calc_u_free_count(to_rack_id, device_id, to_u_start)
|
||||
|
||||
if rack_id != to_rack_id:
|
||||
|
@@ -92,7 +92,7 @@ class IpAddressManager(object):
|
||||
else:
|
||||
return abort(400, ErrFormat.ipam_address_model_not_found)
|
||||
|
||||
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id))):
|
||||
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id), expire=10)):
|
||||
cis = self._get_cis(subnet_id, ips)
|
||||
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis}
|
||||
|
||||
|
@@ -163,7 +163,7 @@ class CIFilterPermsCRUD(DBMixin):
|
||||
|
||||
def add(self, **kwargs):
|
||||
kwargs = self._can_add(**kwargs) or kwargs
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
|
||||
request_id_filter = {}
|
||||
if kwargs.get('id_filter'):
|
||||
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
|
||||
@@ -232,7 +232,7 @@ class CIFilterPermsCRUD(DBMixin):
|
||||
pass
|
||||
|
||||
def delete(self, **kwargs):
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
|
||||
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
|
||||
rid=kwargs.get('rid'),
|
||||
id_filter=None,
|
||||
@@ -249,7 +249,7 @@ class CIFilterPermsCRUD(DBMixin):
|
||||
|
||||
def delete2(self, **kwargs):
|
||||
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
|
||||
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
|
||||
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
|
||||
rid=kwargs.get('rid'),
|
||||
ci_filter=None,
|
||||
|
@@ -138,14 +138,14 @@ class HasResourceRoleCache(object):
|
||||
|
||||
@classmethod
|
||||
def add(cls, rid, app_id):
|
||||
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
|
||||
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
|
||||
c = cls.get(app_id)
|
||||
c[rid] = 1
|
||||
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
|
||||
|
||||
@classmethod
|
||||
def remove(cls, rid, app_id):
|
||||
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
|
||||
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
|
||||
c = cls.get(app_id)
|
||||
c.pop(rid, None)
|
||||
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
|
||||
|
@@ -145,7 +145,7 @@ class RoleRelationCRUD(object):
|
||||
|
||||
@classmethod
|
||||
def add(cls, role, parent_id, child_ids, app_id):
|
||||
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD"):
|
||||
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD", expire=10):
|
||||
db.session.commit()
|
||||
|
||||
result = []
|
||||
|
@@ -105,8 +105,8 @@ class User(CRUDModel, SoftDeleteMixin):
|
||||
_password = db.Column("password", db.String(80))
|
||||
key = db.Column(db.String(32), nullable=False)
|
||||
secret = db.Column(db.String(32), nullable=False)
|
||||
date_joined = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
last_login = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
date_joined = db.Column(db.DateTime, default=datetime.now)
|
||||
last_login = db.Column(db.DateTime, default=datetime.now)
|
||||
block = db.Column(db.Boolean, default=False)
|
||||
has_logined = db.Column(db.Boolean, default=False)
|
||||
wx_id = db.Column(db.String(32))
|
||||
|
@@ -32,7 +32,7 @@ from api.models.acl import Trigger
|
||||
def role_rebuild(rids, app_id):
|
||||
rids = rids if isinstance(rids, list) else [rids]
|
||||
for rid in rids:
|
||||
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id)):
|
||||
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id), expire=10):
|
||||
RoleRelationCache.rebuild(rid, app_id)
|
||||
|
||||
current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id))
|
||||
|
@@ -145,7 +145,7 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
|
||||
@flush_db
|
||||
@reconnect_db
|
||||
def ci_relation_cache(parent_id, child_id, ancestor_ids):
|
||||
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
|
||||
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
|
||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||
children = json.loads(children) if children is not None else {}
|
||||
|
||||
@@ -223,7 +223,7 @@ def ci_relation_add(parent_dict, child_id, uid):
|
||||
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
|
||||
@reconnect_db
|
||||
def ci_relation_delete(parent_id, child_id, ancestor_ids):
|
||||
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
|
||||
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
|
||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||
children = json.loads(children) if children is not None else {}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_PREVIEW=false
|
||||
VUE_APP_API_BASE_URL=/api
|
||||
VUE_APP_API_BASE_URL=http://127.0.0.1:5000/api
|
||||
VUE_APP_BUILD_PACKAGES="ticket,calendar,acl"
|
||||
VUE_APP_IS_OUTER=true
|
||||
VUE_APP_IS_OPEN_SOURCE=true
|
||||
|
@@ -11,10 +11,19 @@
|
||||
|
||||
.ant-input {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
background-color: #F7F8FA;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 30px;
|
||||
|
||||
&:focus {
|
||||
border: solid 1px #B1C9FF;
|
||||
}
|
||||
}
|
||||
|
||||
.cmdb-side-menu-search-focused {
|
||||
.ant-input {
|
||||
border: solid 1px #B1C9FF;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-suffix {
|
||||
|
@@ -92,7 +92,7 @@ export default {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
this.$confirm({
|
||||
title: this.$t('alert'),
|
||||
title: this.$t('warning'),
|
||||
content: this.$t('cmdb.preference.confirmcancelSub2', { name: menu.meta.title }),
|
||||
onOk() {
|
||||
const citypeId = menu.meta.typeId
|
||||
@@ -313,10 +313,7 @@ export default {
|
||||
<Item class={styles['cmdb-side-menu-search']}>
|
||||
<a-input
|
||||
ref="cmdbSideMenuSearchInputRef"
|
||||
class={styles['cmdb-side-menu-search-input']}
|
||||
style={{
|
||||
border: this.$route.name === 'cmdb_resource_search' ? 'solid 1px #B1C9FF' : ''
|
||||
}}
|
||||
class={`ops-input ${this.$route.name === 'cmdb_resource_search' ? 'cmdb-side-menu-search-focused' : ''}`}
|
||||
placeholder={this.$t('cmdbSearch')}
|
||||
onPressEnter={(e) => {
|
||||
this.jumpCMDBSearch(e.target.value)
|
||||
|
@@ -1,121 +1,121 @@
|
||||
<template>
|
||||
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
|
||||
<slot></slot>
|
||||
<template #empty>
|
||||
<slot name="empty">
|
||||
<div :style="{ paddingTop: '10px' }">
|
||||
<img :style="{ width: '140px', height: '90px' }" :src="require('@/assets/data_empty.png')" />
|
||||
<div>{{ $t('noData') }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<template #loading>
|
||||
<slot name="loading"></slot>
|
||||
</template>
|
||||
</vxe-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
// 该组件使用方法与vxe-table一致,但调用它的方法时,需先调用getVxetableRef()获取到vxe-table实体
|
||||
export default {
|
||||
name: 'OpsTable',
|
||||
data() {
|
||||
return {
|
||||
// isShifting: false,
|
||||
// lastIndex: -1,
|
||||
lastSelected: [],
|
||||
currentSelected: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
new$listeners() {
|
||||
if (!Object.keys(this.$listeners).length) {
|
||||
return this.$listeners
|
||||
}
|
||||
return Object.assign(this.$listeners, {
|
||||
// 在这里覆盖原有的change事件
|
||||
// 'checkbox-change': this.selectChangeEvent,
|
||||
'checkbox-range-change': this.checkboxRangeChange,
|
||||
'checkbox-range-start': this.checkboxRangeStart,
|
||||
'checkbox-range-end': this.checkboxRangeEnd,
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// window.onkeydown = (e) => {
|
||||
// if (e.key === 'Shift') {
|
||||
// this.isShifting = true
|
||||
// }
|
||||
// }
|
||||
// window.onkeyup = (e) => {
|
||||
// if (e.key === 'Shift') {
|
||||
// this.isShifting = false
|
||||
// this.lastIndex = -1
|
||||
// }
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {
|
||||
// window.onkeydown = ''
|
||||
// window.onkeyup = ''
|
||||
},
|
||||
methods: {
|
||||
getVxetableRef() {
|
||||
return this.$refs.xTable
|
||||
},
|
||||
// selectChangeEvent(e) {
|
||||
// const xTable = this.$refs.xTable
|
||||
// const { lastIndex } = this
|
||||
// const currentIndex = e.rowIndex
|
||||
// const { tableData } = xTable.getTableData()
|
||||
// if (lastIndex > -1 && this.isShifting) {
|
||||
// let start = lastIndex
|
||||
// let end = currentIndex
|
||||
// if (lastIndex > currentIndex) {
|
||||
// start = currentIndex
|
||||
// end = lastIndex
|
||||
// }
|
||||
// const rangeData = tableData.slice(start, end + 1)
|
||||
// xTable.setCheckboxRow(rangeData, true)
|
||||
// }
|
||||
// this.lastIndex = currentIndex
|
||||
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
|
||||
// },
|
||||
checkboxRangeStart(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
const lastSelected = xTable.getCheckboxRecords()
|
||||
const selectedReserve = xTable.getCheckboxReserveRecords()
|
||||
this.lastSelected = [...lastSelected, ...selectedReserve]
|
||||
this.$emit('checkbox-range-start', e)
|
||||
},
|
||||
checkboxRangeChange(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
xTable.setCheckboxRow(this.lastSelected, true)
|
||||
this.currentSelected = e.records
|
||||
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
|
||||
this.$emit('checkbox-range-change', {
|
||||
...e,
|
||||
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
|
||||
})
|
||||
},
|
||||
checkboxRangeEnd(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
const isAllSelected = this.currentSelected.every((item) => {
|
||||
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
|
||||
return _idx > -1
|
||||
})
|
||||
if (isAllSelected) {
|
||||
xTable.setCheckboxRow(this.currentSelected, false)
|
||||
}
|
||||
this.currentSelected = []
|
||||
this.lastSelected = []
|
||||
this.$emit('checkbox-range-end', {
|
||||
...e,
|
||||
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
<template>
|
||||
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
|
||||
<slot></slot>
|
||||
<template #empty>
|
||||
<slot name="empty">
|
||||
<div :style="{ paddingTop: '10px' }">
|
||||
<img :style="{ width: '140px', height: '120px' }" :src="require('@/assets/data_empty.png')" />
|
||||
<div>{{ $t('noData') }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<template #loading>
|
||||
<slot name="loading"></slot>
|
||||
</template>
|
||||
</vxe-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
// 该组件使用方法与vxe-table一致,但调用它的方法时,需先调用getVxetableRef()获取到vxe-table实体
|
||||
export default {
|
||||
name: 'OpsTable',
|
||||
data() {
|
||||
return {
|
||||
// isShifting: false,
|
||||
// lastIndex: -1,
|
||||
lastSelected: [],
|
||||
currentSelected: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
new$listeners() {
|
||||
if (!Object.keys(this.$listeners).length) {
|
||||
return this.$listeners
|
||||
}
|
||||
return Object.assign(this.$listeners, {
|
||||
// 在这里覆盖原有的change事件
|
||||
// 'checkbox-change': this.selectChangeEvent,
|
||||
'checkbox-range-change': this.checkboxRangeChange,
|
||||
'checkbox-range-start': this.checkboxRangeStart,
|
||||
'checkbox-range-end': this.checkboxRangeEnd,
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// window.onkeydown = (e) => {
|
||||
// if (e.key === 'Shift') {
|
||||
// this.isShifting = true
|
||||
// }
|
||||
// }
|
||||
// window.onkeyup = (e) => {
|
||||
// if (e.key === 'Shift') {
|
||||
// this.isShifting = false
|
||||
// this.lastIndex = -1
|
||||
// }
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {
|
||||
// window.onkeydown = ''
|
||||
// window.onkeyup = ''
|
||||
},
|
||||
methods: {
|
||||
getVxetableRef() {
|
||||
return this.$refs.xTable
|
||||
},
|
||||
// selectChangeEvent(e) {
|
||||
// const xTable = this.$refs.xTable
|
||||
// const { lastIndex } = this
|
||||
// const currentIndex = e.rowIndex
|
||||
// const { tableData } = xTable.getTableData()
|
||||
// if (lastIndex > -1 && this.isShifting) {
|
||||
// let start = lastIndex
|
||||
// let end = currentIndex
|
||||
// if (lastIndex > currentIndex) {
|
||||
// start = currentIndex
|
||||
// end = lastIndex
|
||||
// }
|
||||
// const rangeData = tableData.slice(start, end + 1)
|
||||
// xTable.setCheckboxRow(rangeData, true)
|
||||
// }
|
||||
// this.lastIndex = currentIndex
|
||||
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
|
||||
// },
|
||||
checkboxRangeStart(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
const lastSelected = xTable.getCheckboxRecords()
|
||||
const selectedReserve = xTable.getCheckboxReserveRecords()
|
||||
this.lastSelected = [...lastSelected, ...selectedReserve]
|
||||
this.$emit('checkbox-range-start', e)
|
||||
},
|
||||
checkboxRangeChange(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
xTable.setCheckboxRow(this.lastSelected, true)
|
||||
this.currentSelected = e.records
|
||||
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
|
||||
this.$emit('checkbox-range-change', {
|
||||
...e,
|
||||
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
|
||||
})
|
||||
},
|
||||
checkboxRangeEnd(e) {
|
||||
const xTable = this.$refs.xTable
|
||||
const isAllSelected = this.currentSelected.every((item) => {
|
||||
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
|
||||
return _idx > -1
|
||||
})
|
||||
if (isAllSelected) {
|
||||
xTable.setCheckboxRow(this.currentSelected, false)
|
||||
}
|
||||
this.currentSelected = []
|
||||
this.lastSelected = []
|
||||
this.$emit('checkbox-range-end', {
|
||||
...e,
|
||||
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
|
@@ -195,11 +195,11 @@ export default {
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 64px);
|
||||
margin-bottom: -24px;
|
||||
padding: 24px;
|
||||
padding: 20px;
|
||||
.acl-resource-types-header {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
@@ -358,11 +358,11 @@ export default {
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 64px);
|
||||
margin-bottom: -24px;
|
||||
padding: 12px 24px 24px 24px;
|
||||
padding: 8px 20px 20px 20px;
|
||||
.acl-resources-header {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.ant-switch {
|
||||
|
@@ -291,11 +291,11 @@ export default {
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 64px);
|
||||
margin-bottom: -24px;
|
||||
padding: 24px;
|
||||
padding: 20px;
|
||||
.acl-roles-header {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
.ant-checkbox-wrapper {
|
||||
margin-left: auto;
|
||||
|
@@ -326,11 +326,11 @@ export default {
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 64px);
|
||||
margin-bottom: -24px;
|
||||
padding: 24px;
|
||||
padding: 20px;
|
||||
.acl-trigger-header {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
@@ -1,371 +1,370 @@
|
||||
<template>
|
||||
<div class="cmdb-grant" :style="{ }">
|
||||
<template v-if="cmdbGrantType.includes('ci_type')">
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
|
||||
<CiTypeGrant
|
||||
:CITypeId="CITypeId"
|
||||
:tableData="tableData"
|
||||
grantType="ci_type"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_ci_type"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
cmdbGrantType.includes('ci_type,ci') || (cmdbGrantType.includes('ci') && !cmdbGrantType.includes('ci_type'))
|
||||
"
|
||||
>
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciGrant') }}</div>
|
||||
<CiTypeGrant
|
||||
:CITypeId="CITypeId"
|
||||
:tableData="tableData"
|
||||
grantType="ci"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
@openReadGrantModal="openReadGrantModal"
|
||||
ref="grant_ci"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('type_relation')">
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.relationGrant') }}</div>
|
||||
<TypeRelationGrant
|
||||
:typeRelationIds="typeRelationIds"
|
||||
:tableData="tableData"
|
||||
grantType="type_relation"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_type_relation"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('relation_view')">
|
||||
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
|
||||
<RelationViewGrant
|
||||
:resourceTypeName="resourceTypeName"
|
||||
:tableData="tableData"
|
||||
grantType="relation_view"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_relation_view"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('TopologyView')">
|
||||
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
|
||||
<TopologyViewGrant
|
||||
:resourceTypeName="resourceTypeName"
|
||||
:tableData="tableData"
|
||||
:viewId="CITypeId"
|
||||
grantType="TopologyView"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grantTopologyView"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<GrantModal ref="grantModal" @handleOk="handleOk" />
|
||||
<ReadGrantModal ref="readGrantModal" :CITypeId="CITypeId" @updateTableDataRead="updateTableDataRead" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import CiTypeGrant from './ciTypeGrant.vue'
|
||||
import TypeRelationGrant from './typeRelationGrant.vue'
|
||||
import { searchResource } from '@/modules/acl/api/resource'
|
||||
import { getResourcePerms } from '@/modules/acl/api/permission'
|
||||
import GrantModal from './grantModal.vue'
|
||||
import ReadGrantModal from './readGrantModal'
|
||||
import RelationViewGrant from './relationViewGrant.vue'
|
||||
import TopologyViewGrant from './topologyViewGrant.vue'
|
||||
import { getCITypeGroupById, ciTypeFilterPermissions } from '../../api/CIType'
|
||||
|
||||
export default {
|
||||
name: 'GrantComp',
|
||||
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, TopologyViewGrant, GrantModal, ReadGrantModal },
|
||||
props: {
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
resourceTypeName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
resourceType: {
|
||||
type: String,
|
||||
default: 'CIType',
|
||||
},
|
||||
app_id: {
|
||||
type: String,
|
||||
default: 'cmdb',
|
||||
},
|
||||
cmdbGrantType: {
|
||||
type: String,
|
||||
default: 'ci_type,ci',
|
||||
},
|
||||
typeRelationIds: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
isModal: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
inject: ['resource_type'],
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
grantType: '',
|
||||
resource_id: null,
|
||||
attrGroup: [],
|
||||
filerPerimissions: {},
|
||||
loading: false,
|
||||
addedRids: [], // added rid this time
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
allEmployees: (state) => state.user.allEmployees,
|
||||
allDepartments: (state) => state.user.allDepartments,
|
||||
}),
|
||||
child_resource_type() {
|
||||
return this.resource_type()
|
||||
},
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
attrGroup: () => {
|
||||
return this.attrGroup
|
||||
},
|
||||
filerPerimissions: () => {
|
||||
return this.filerPerimissions
|
||||
},
|
||||
loading: () => {
|
||||
return this.loading
|
||||
},
|
||||
isModal: this.isModal,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resourceTypeName: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.init()
|
||||
},
|
||||
},
|
||||
CITypeId: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.CITypeId && this.cmdbGrantType.includes('ci')) {
|
||||
this.getFilterPermissions()
|
||||
this.getAttrGroup()
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
getAttrGroup() {
|
||||
getCITypeGroupById(this.CITypeId, { need_other: true }).then((res) => {
|
||||
this.attrGroup = res
|
||||
})
|
||||
},
|
||||
getFilterPermissions() {
|
||||
ciTypeFilterPermissions(this.CITypeId).then((res) => {
|
||||
this.filerPerimissions = res
|
||||
})
|
||||
},
|
||||
async init() {
|
||||
const _find = this.child_resource_type.groups.find((item) => item.name === this.resourceType)
|
||||
const resource_type_id = _find?.id ?? 0
|
||||
const res = await searchResource({
|
||||
app_id: this.app_id,
|
||||
resource_type_id,
|
||||
page_size: 9999,
|
||||
})
|
||||
const _tempFind = res.resources.find((item) => item.name === this.resourceTypeName)
|
||||
console.log(this.resourceTypeName)
|
||||
this.resource_id = _tempFind?.id || 0
|
||||
this.getTableData()
|
||||
},
|
||||
async getTableData() {
|
||||
this.loading = true
|
||||
const _tableData = await getResourcePerms(this.resource_id, { need_users: 0 })
|
||||
const perms = []
|
||||
for (const key in _tableData) {
|
||||
const obj = {}
|
||||
obj.name = key
|
||||
_tableData[key].perms.forEach((perm) => {
|
||||
obj[`${perm.name}`] = true
|
||||
obj.rid = perm?.rid ?? null
|
||||
})
|
||||
perms.push(obj)
|
||||
}
|
||||
this.tableData = perms
|
||||
this.loading = false
|
||||
},
|
||||
// Grant the department in common-setting and get the roleid from it
|
||||
grantDepart(grantType) {
|
||||
this.$refs.grantModal.open('depart')
|
||||
this.grantType = grantType
|
||||
},
|
||||
// Grant the oldest role permissions
|
||||
grantRole(grantType) {
|
||||
this.$refs.grantModal.open('role')
|
||||
this.grantType = grantType
|
||||
},
|
||||
handleOk(params, type) {
|
||||
const { grantType } = this
|
||||
let rids
|
||||
if (type === 'depart') {
|
||||
rids = [
|
||||
...params.department.map((rid) => {
|
||||
const _find = this.allDepartments.find((dep) => dep.acl_rid === rid)
|
||||
return { rid, name: _find?.department_name ?? rid }
|
||||
}),
|
||||
...params.user.map((rid) => {
|
||||
const _find = this.allEmployees.find((dep) => dep.acl_rid === rid)
|
||||
return { rid, name: _find?.nickname ?? rid }
|
||||
}),
|
||||
]
|
||||
}
|
||||
if (type === 'role') {
|
||||
rids = [
|
||||
...params.map((role) => {
|
||||
return { rid: role.id, name: role.name }
|
||||
}),
|
||||
]
|
||||
}
|
||||
if (grantType === 'ci_type') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
const _find = this.tableData.find((item) => item.rid === rid)
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
conifg: false,
|
||||
grant: false,
|
||||
..._find,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'ci') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
const _find = this.tableData.find((item) => item.rid === rid)
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read_attr: false,
|
||||
read_ci: false,
|
||||
create: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
..._find,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'type_relation') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
create: false,
|
||||
grant: false,
|
||||
delete: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'relation_view') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read: false,
|
||||
grant: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'TopologyView') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
grant: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
this.addedRids = rids
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs[`grant_${grantType}`].$refs.xTable.elemStore['main-body-wrapper'].scrollTo(0, 0)
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
openReadGrantModal(col, row) {
|
||||
this.$refs.readGrantModal.open(col, row)
|
||||
},
|
||||
updateTableDataRead(row, hasRead) {
|
||||
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
|
||||
this.$set(this.tableData, _idx, { ...this.tableData[_idx], read: hasRead })
|
||||
this.getFilterPermissions()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.cmdb-grant {
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
overflow: auto;
|
||||
.cmdb-grant-title {
|
||||
border-left: 4px solid @primary-color;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
.cmdb-grant {
|
||||
.grant-button {
|
||||
padding: 6px 8px;
|
||||
color: @primary-color;
|
||||
background-color: @primary-color_5;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
margin: 15px 0;
|
||||
display: inline-block;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 2px 3px 4px @primary-color_5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="cmdb-grant" :style="{ }">
|
||||
<template v-if="cmdbGrantType.includes('ci_type')">
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
|
||||
<CiTypeGrant
|
||||
:CITypeId="CITypeId"
|
||||
:tableData="tableData"
|
||||
grantType="ci_type"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_ci_type"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
cmdbGrantType.includes('ci_type,ci') || (cmdbGrantType.includes('ci') && !cmdbGrantType.includes('ci_type'))
|
||||
"
|
||||
>
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciGrant') }}</div>
|
||||
<CiTypeGrant
|
||||
:CITypeId="CITypeId"
|
||||
:tableData="tableData"
|
||||
grantType="ci"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
@openReadGrantModal="openReadGrantModal"
|
||||
ref="grant_ci"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('type_relation')">
|
||||
<div class="cmdb-grant-title">{{ $t('cmdb.components.relationGrant') }}</div>
|
||||
<TypeRelationGrant
|
||||
:typeRelationIds="typeRelationIds"
|
||||
:tableData="tableData"
|
||||
grantType="type_relation"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_type_relation"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('relation_view')">
|
||||
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
|
||||
<RelationViewGrant
|
||||
:resourceTypeName="resourceTypeName"
|
||||
:tableData="tableData"
|
||||
grantType="relation_view"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grant_relation_view"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="cmdbGrantType.includes('TopologyView')">
|
||||
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
|
||||
<TopologyViewGrant
|
||||
:resourceTypeName="resourceTypeName"
|
||||
:tableData="tableData"
|
||||
:viewId="CITypeId"
|
||||
grantType="TopologyView"
|
||||
@grantDepart="grantDepart"
|
||||
@grantRole="grantRole"
|
||||
@getTableData="getTableData"
|
||||
ref="grantTopologyView"
|
||||
:addedRids="addedRids"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<GrantModal ref="grantModal" @handleOk="handleOk" />
|
||||
<ReadGrantModal ref="readGrantModal" :CITypeId="CITypeId" @updateTableDataRead="updateTableDataRead" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import CiTypeGrant from './ciTypeGrant.vue'
|
||||
import TypeRelationGrant from './typeRelationGrant.vue'
|
||||
import { searchResource } from '@/modules/acl/api/resource'
|
||||
import { getResourcePerms } from '@/modules/acl/api/permission'
|
||||
import GrantModal from './grantModal.vue'
|
||||
import ReadGrantModal from './readGrantModal'
|
||||
import RelationViewGrant from './relationViewGrant.vue'
|
||||
import TopologyViewGrant from './topologyViewGrant.vue'
|
||||
import { getCITypeGroupById, ciTypeFilterPermissions } from '../../api/CIType'
|
||||
|
||||
export default {
|
||||
name: 'GrantComp',
|
||||
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, TopologyViewGrant, GrantModal, ReadGrantModal },
|
||||
props: {
|
||||
CITypeId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
resourceTypeName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
resourceType: {
|
||||
type: String,
|
||||
default: 'CIType',
|
||||
},
|
||||
app_id: {
|
||||
type: String,
|
||||
default: 'cmdb',
|
||||
},
|
||||
cmdbGrantType: {
|
||||
type: String,
|
||||
default: 'ci_type,ci',
|
||||
},
|
||||
typeRelationIds: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
isModal: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
inject: ['resource_type'],
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
grantType: '',
|
||||
resource_id: null,
|
||||
attrGroup: [],
|
||||
filerPerimissions: {},
|
||||
loading: false,
|
||||
addedRids: [], // added rid this time
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
allEmployees: (state) => state.user.allEmployees,
|
||||
allDepartments: (state) => state.user.allDepartments,
|
||||
}),
|
||||
child_resource_type() {
|
||||
return this.resource_type()
|
||||
},
|
||||
windowHeight() {
|
||||
return this.$store.state.windowHeight
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
attrGroup: () => {
|
||||
return this.attrGroup
|
||||
},
|
||||
filerPerimissions: () => {
|
||||
return this.filerPerimissions
|
||||
},
|
||||
loading: () => {
|
||||
return this.loading
|
||||
},
|
||||
isModal: this.isModal,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resourceTypeName: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.init()
|
||||
},
|
||||
},
|
||||
CITypeId: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.CITypeId && this.cmdbGrantType.includes('ci')) {
|
||||
this.getFilterPermissions()
|
||||
this.getAttrGroup()
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
getAttrGroup() {
|
||||
getCITypeGroupById(this.CITypeId, { need_other: true }).then((res) => {
|
||||
this.attrGroup = res
|
||||
})
|
||||
},
|
||||
getFilterPermissions() {
|
||||
ciTypeFilterPermissions(this.CITypeId).then((res) => {
|
||||
this.filerPerimissions = res
|
||||
})
|
||||
},
|
||||
async init() {
|
||||
const _find = this.child_resource_type.groups.find((item) => item.name === this.resourceType)
|
||||
const resource_type_id = _find?.id ?? 0
|
||||
const res = await searchResource({
|
||||
app_id: this.app_id,
|
||||
resource_type_id,
|
||||
page_size: 9999,
|
||||
})
|
||||
const _tempFind = res.resources.find((item) => item.name === this.resourceTypeName)
|
||||
console.log(this.resourceTypeName)
|
||||
this.resource_id = _tempFind?.id || 0
|
||||
this.getTableData()
|
||||
},
|
||||
async getTableData() {
|
||||
this.loading = true
|
||||
const _tableData = await getResourcePerms(this.resource_id, { need_users: 0 })
|
||||
const perms = []
|
||||
for (const key in _tableData) {
|
||||
const obj = {}
|
||||
obj.name = key
|
||||
_tableData[key].perms.forEach((perm) => {
|
||||
obj[`${perm.name}`] = true
|
||||
obj.rid = perm?.rid ?? null
|
||||
})
|
||||
perms.push(obj)
|
||||
}
|
||||
this.tableData = perms
|
||||
this.loading = false
|
||||
},
|
||||
// Grant the department in common-setting and get the roleid from it
|
||||
grantDepart(grantType) {
|
||||
this.$refs.grantModal.open('depart')
|
||||
this.grantType = grantType
|
||||
},
|
||||
// Grant the oldest role permissions
|
||||
grantRole(grantType) {
|
||||
this.$refs.grantModal.open('role')
|
||||
this.grantType = grantType
|
||||
},
|
||||
handleOk(params, type) {
|
||||
const { grantType } = this
|
||||
let rids
|
||||
if (type === 'depart') {
|
||||
rids = [
|
||||
...params.department.map((rid) => {
|
||||
const _find = this.allDepartments.find((dep) => dep.acl_rid === rid)
|
||||
return { rid, name: _find?.department_name ?? rid }
|
||||
}),
|
||||
...params.user.map((rid) => {
|
||||
const _find = this.allEmployees.find((dep) => dep.acl_rid === rid)
|
||||
return { rid, name: _find?.nickname ?? rid }
|
||||
}),
|
||||
]
|
||||
}
|
||||
if (type === 'role') {
|
||||
rids = [
|
||||
...params.map((role) => {
|
||||
return { rid: role.id, name: role.name }
|
||||
}),
|
||||
]
|
||||
}
|
||||
if (grantType === 'ci_type') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
const _find = this.tableData.find((item) => item.rid === rid)
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
conifg: false,
|
||||
grant: false,
|
||||
..._find,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'ci') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
const _find = this.tableData.find((item) => item.rid === rid)
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read_attr: false,
|
||||
read_ci: false,
|
||||
create: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
..._find,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'type_relation') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
create: false,
|
||||
grant: false,
|
||||
delete: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'relation_view') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read: false,
|
||||
grant: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
if (grantType === 'TopologyView') {
|
||||
this.tableData.unshift(
|
||||
...rids.map(({ rid, name }) => {
|
||||
return {
|
||||
rid,
|
||||
name,
|
||||
read: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
grant: false,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
this.addedRids = rids
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs[`grant_${grantType}`].$refs.xTable.elemStore['main-body-wrapper'].scrollTo(0, 0)
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
openReadGrantModal(col, row) {
|
||||
this.$refs.readGrantModal.open(col, row)
|
||||
},
|
||||
updateTableDataRead(row, hasRead) {
|
||||
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
|
||||
this.$set(this.tableData, _idx, { ...this.tableData[_idx], read: hasRead })
|
||||
this.getFilterPermissions()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.cmdb-grant {
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
overflow: auto;
|
||||
.cmdb-grant-title {
|
||||
border-left: 4px solid @primary-color;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.cmdb-grant {
|
||||
.grant-button {
|
||||
padding: 6px 8px;
|
||||
color: @primary-color;
|
||||
background-color: @primary-color_5;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
margin: 15px 0;
|
||||
display: inline-block;
|
||||
transition: all 0.3s;
|
||||
z-index: 1;
|
||||
|
||||
.btn-wave-hover(@primary-color_4, -1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,122 +1,118 @@
|
||||
<template>
|
||||
<a-modal :visible="visible" @cancel="handleCancel" @ok="handleOK" :title="$t('revoke')">
|
||||
<a-form-model :model="form" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-model-item :label="$t('user')">
|
||||
<EmployeeTreeSelect
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:multiple="true"
|
||||
v-model="form.users"
|
||||
:placeholder="$t('cmdb.serviceTree.userPlaceholder')"
|
||||
:idType="2"
|
||||
departmentKey="acl_rid"
|
||||
employeeKey="acl_rid"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('role')">
|
||||
<treeselect
|
||||
v-model="form.roles"
|
||||
:multiple="true"
|
||||
:options="filterAllRoles"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:limit="10"
|
||||
:limitText="(count) => `+ ${count}`"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
zIndex="1050"
|
||||
:placeholder="$t('cmdb.serviceTree.rolePlaceholder')"
|
||||
@search-change="searchRole"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||
import { getAllDepAndEmployee } from '@/api/company'
|
||||
import { searchRole } from '@/modules/acl/api/role'
|
||||
|
||||
export default {
|
||||
name: 'RevokeModal',
|
||||
components: { EmployeeTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
form: {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
},
|
||||
allTreeDepAndEmp: [],
|
||||
allRoles: [],
|
||||
filterAllRoles: [],
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
provide_allTreeDepAndEmp: () => {
|
||||
return this.allTreeDepAndEmp
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAllDepAndEmployee()
|
||||
this.loadRoles()
|
||||
},
|
||||
methods: {
|
||||
async loadRoles() {
|
||||
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
||||
this.allRoles = res.roles
|
||||
this.filterAllRoles = this.allRoles.slice(0, 100)
|
||||
},
|
||||
getAllDepAndEmployee() {
|
||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||
this.allTreeDepAndEmp = res
|
||||
})
|
||||
},
|
||||
open() {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
this.form = {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
},
|
||||
searchRole(searchQuery) {
|
||||
this.filterAllRoles = this.allRoles
|
||||
.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
.slice(0, 100)
|
||||
},
|
||||
handleOK() {
|
||||
this.$emit('handleRevoke', this.form)
|
||||
this.handleCancel()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<template>
|
||||
<a-modal :visible="visible" @cancel="handleCancel" @ok="handleOK" :title="$t('revoke')">
|
||||
<a-form-model :model="form" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-model-item :label="$t('user')">
|
||||
<EmployeeTreeSelect
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:multiple="true"
|
||||
v-model="form.users"
|
||||
:placeholder="$t('cmdb.serviceTree.userPlaceholder')"
|
||||
:idType="2"
|
||||
departmentKey="acl_rid"
|
||||
employeeKey="acl_rid"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t('role')">
|
||||
<treeselect
|
||||
v-model="form.roles"
|
||||
:multiple="true"
|
||||
:options="filterAllRoles"
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
:limit="10"
|
||||
:limitText="(count) => `+ ${count}`"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
zIndex="1050"
|
||||
:placeholder="$t('cmdb.serviceTree.rolePlaceholder')"
|
||||
@search-change="searchRole"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||
import { getAllDepAndEmployee } from '@/api/company'
|
||||
import { searchRole } from '@/modules/acl/api/role'
|
||||
|
||||
export default {
|
||||
name: 'RevokeModal',
|
||||
components: { EmployeeTreeSelect },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
form: {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
},
|
||||
allTreeDepAndEmp: [],
|
||||
allRoles: [],
|
||||
filterAllRoles: [],
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
provide_allTreeDepAndEmp: () => {
|
||||
return this.allTreeDepAndEmp
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAllDepAndEmployee()
|
||||
this.loadRoles()
|
||||
},
|
||||
methods: {
|
||||
async loadRoles() {
|
||||
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
||||
this.allRoles = res.roles
|
||||
this.filterAllRoles = this.allRoles.slice(0, 100)
|
||||
},
|
||||
getAllDepAndEmployee() {
|
||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||
this.allTreeDepAndEmp = res
|
||||
})
|
||||
},
|
||||
open() {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
this.form = {
|
||||
users: undefined,
|
||||
roles: undefined,
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false
|
||||
},
|
||||
searchRole(searchQuery) {
|
||||
this.filterAllRoles = this.allRoles
|
||||
.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
.slice(0, 100)
|
||||
},
|
||||
handleOK() {
|
||||
this.$emit('handleRevoke', this.form)
|
||||
this.handleCancel()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@@ -3,12 +3,10 @@
|
||||
:disabled="disabled"
|
||||
ref="cmdb_type_select"
|
||||
:disable-branch-nodes="true"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
'--custom-height': '30px',
|
||||
lineHeight: '30px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
lineHeight: '30px'
|
||||
}"
|
||||
v-model="currenCiType"
|
||||
:multiple="multiple"
|
||||
|
@@ -35,8 +35,8 @@
|
||||
:key="`${item.id}_${index}`"
|
||||
class="preference-search-tag"
|
||||
:style="{
|
||||
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '#fafafa',
|
||||
color: item.id === currentPreferenceSearch ? '#fff' : '#000000a6',
|
||||
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '',
|
||||
color: item.id === currentPreferenceSearch ? '#fff' : '',
|
||||
}"
|
||||
>
|
||||
<span @click="clickPreferenceSearch(item)">{{ item.name }}</span>
|
||||
@@ -189,6 +189,10 @@ export default {
|
||||
> i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.preference-search-delete {
|
||||
color: #a9a9a9;
|
||||
|
@@ -5,13 +5,11 @@
|
||||
<a-space>
|
||||
<treeselect
|
||||
v-if="type === 'resourceSearch'"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
class="custom-treeselect"
|
||||
:style="{
|
||||
width: '200px',
|
||||
marginRight: '10px',
|
||||
'--custom-height': '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '16px',
|
||||
}"
|
||||
v-model="currenCiType"
|
||||
|
@@ -135,9 +135,15 @@ export default {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.authorization-input {
|
||||
border: none;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@
|
||||
<tr v-for="(item, index) in headers" :key="item.id">
|
||||
<td><a-input class="headers-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
|
||||
<td><a-input class="headers-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
|
||||
<td>
|
||||
<td class="headers-delete">
|
||||
<a style="color:red">
|
||||
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
|
||||
</a>
|
||||
@@ -92,10 +92,20 @@ export default {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.headers-input {
|
||||
border: none;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.headers-delete {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -3,12 +3,10 @@
|
||||
<a-input-group compact>
|
||||
<treeselect
|
||||
:disable-branch-nodes="true"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder"
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
'--custom-height': '30px',
|
||||
lineHeight: '30px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
display: 'inline-block',
|
||||
width: '100px',
|
||||
}"
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<tr v-for="(item, index) in parameters" :key="item.id">
|
||||
<td><a-input class="parameters-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
|
||||
<td><a-input class="parameters-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
|
||||
<td>
|
||||
<td class="parameters-delete">
|
||||
<a style="color:red">
|
||||
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
|
||||
</a>
|
||||
@@ -91,10 +91,20 @@ export default {
|
||||
border: 1px solid #f3f4f6;
|
||||
}
|
||||
.parameters-input {
|
||||
border: none;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.parameters-delete {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -104,6 +104,16 @@ export default {
|
||||
.cmdb-batch-upload-tips {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(90deg, @primary-color_2 50%, transparent 0) repeat-x,
|
||||
linear-gradient(90deg, @primary-color_2 50%, transparent 0) repeat-x,
|
||||
linear-gradient(0deg, @primary-color_2 50%, transparent 0) repeat-y,
|
||||
linear-gradient(0deg, @primary-color_2 50%, transparent 0) repeat-y;
|
||||
background-size: 15px 1px, 15px 1px, 1px 15px, 1px 15px;
|
||||
background-position: 0 0, 0 100%, 0 0, 100% 0;
|
||||
background-color: @primary-color_7;
|
||||
}
|
||||
}
|
||||
.ant-upload.ant-upload-drag .ant-upload-drag-container {
|
||||
vertical-align: baseline;
|
||||
|
@@ -9,22 +9,18 @@
|
||||
"
|
||||
:title="$t('cmdb.ci.attributeDesc')"
|
||||
width="72%"
|
||||
:bodyStyle="{ height: '100vh' }"
|
||||
:bodyStyle="{ height: '100vh', paddingTop: '16px' }"
|
||||
>
|
||||
<vxe-toolbar>
|
||||
<template #buttons>
|
||||
<a-input
|
||||
v-model="searchKey"
|
||||
:style="{ display: 'inline-block', width: '244px' }"
|
||||
class="ops-input ops-input-radius"
|
||||
type="search"
|
||||
:placeholder="$t('cmdb.ci.tips5')"
|
||||
@keyup="searchAttributes"
|
||||
>
|
||||
<a-icon type="search" slot="suffix" />
|
||||
</a-input>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<a-input
|
||||
v-model="searchKey"
|
||||
:style="{ display: 'inline-block', width: '244px', marginBottom: '16px' }"
|
||||
class="ops-input ops-input-radius"
|
||||
type="search"
|
||||
:placeholder="$t('cmdb.ci.tips5')"
|
||||
@keyup="searchAttributes"
|
||||
>
|
||||
<a-icon type="search" slot="suffix" />
|
||||
</a-input>
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<vxe-table
|
||||
|
@@ -33,8 +33,8 @@
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tab_3">
|
||||
<span slot="tab"><a-icon type="clock-circle" />{{ $t('cmdb.ci.history') }}</span>
|
||||
<div :style="{ padding: '24px', height: '100%' }">
|
||||
<a-space :style="{ 'margin-bottom': '10px', display: 'flex' }">
|
||||
<div :style="{ padding: '16px 24px 24px', height: '100%' }">
|
||||
<a-space :style="{ marginBottom: '16px', display: 'flex' }">
|
||||
<a-button type="primary" class="ops-button-ghost" ghost @click="handleRollbackCI()">
|
||||
<ops-icon type="shishizhuangtai" />{{ $t('cmdb.ci.rollback') }}
|
||||
</a-button>
|
||||
@@ -180,7 +180,7 @@ export default {
|
||||
ci_types: [],
|
||||
hasPermission: true,
|
||||
itsmInstalled: true,
|
||||
tableHeight: this.attributeHistoryTableHeight || (this.$store.state.windowHeight - 120),
|
||||
tableHeight: this.attributeHistoryTableHeight || (this.$store.state.windowHeight - 130),
|
||||
initQueryLoading: true,
|
||||
}
|
||||
},
|
||||
|
@@ -61,16 +61,20 @@ export default {
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #4E5969;
|
||||
background-color: #F7F8FA;
|
||||
color: @text-color_2;
|
||||
background-color: @primary-color_7;
|
||||
width: 105px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
|
||||
&-active {
|
||||
border: solid 1px #B1C9FF;
|
||||
background-color: #E1EFFF;
|
||||
color: #2F54EB;
|
||||
border: solid 1px @primary-color_8;
|
||||
background-color: @primary-color_4;
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -154,6 +154,17 @@ export default {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @primary-color_5;
|
||||
|
||||
.attr-ad-tab-edit {
|
||||
display: inline-block;
|
||||
}
|
||||
.attr-ad-tab-delete {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
&_active {
|
||||
border: solid 1px @primary-color_8;
|
||||
background-color: @primary-color_6;
|
||||
@@ -161,14 +172,9 @@ export default {
|
||||
.attr-ad-tab-name {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.attr-ad-tab-edit {
|
||||
display: inline-block;
|
||||
}
|
||||
.attr-ad-tab-delete {
|
||||
display: inline-block;
|
||||
&:hover {
|
||||
background-color: @primary-color_6;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,6 +184,11 @@ export default {
|
||||
background-color: @primary-color_7;
|
||||
font-size: 12px;
|
||||
color: @text-color_4;
|
||||
|
||||
&:hover {
|
||||
background-color: @primary-color_5;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -3,12 +3,14 @@
|
||||
<div class="attr-ad-header attr-ad-header-margin">{{ $t('cmdb.ciType.configCheckTitle') }}</div>
|
||||
<div class="attr-ad-content">
|
||||
<div class="ad-test-title-info">{{ $t('cmdb.ciType.checkTestTip') }}</div>
|
||||
<div
|
||||
class="ad-test-btn"
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ops-button-ghost ad-test-btn"
|
||||
ghost
|
||||
@click="showCheckModal"
|
||||
>
|
||||
{{ $t('cmdb.ciType.checkTestBtn') }}
|
||||
</div>
|
||||
</a-button>
|
||||
<div class="ad-test-btn-info">{{ $t('cmdb.ciType.checkTestTip2') }}</div>
|
||||
<!-- <div
|
||||
class="ad-test-btn"
|
||||
@@ -140,15 +142,6 @@ export default {
|
||||
|
||||
.ad-test-btn {
|
||||
margin-top: 30px;
|
||||
padding: 5px 12px;
|
||||
background-color: #F4F9FF;
|
||||
border: solid 1px @primary-color_8;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
|
||||
color: @link-color;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.ad-test-btn-info {
|
||||
|
@@ -1194,7 +1194,7 @@ export default {
|
||||
.selected {
|
||||
background-color: @primary-color_3;
|
||||
.ci-types-left-detail-title {
|
||||
font-weight: 700;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -61,13 +61,11 @@
|
||||
:disable-branch-nodes="true"
|
||||
:class="{
|
||||
'custom-treeselect': true,
|
||||
'custom-treeselect-bgcAndBorder': true,
|
||||
'custom-treeselect-white': true,
|
||||
}"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '14px',
|
||||
}"
|
||||
v-model="choice_other.type_ids"
|
||||
|
@@ -198,13 +198,11 @@
|
||||
:disable-branch-nodes="true"
|
||||
:class="{
|
||||
'custom-treeselect': true,
|
||||
'custom-treeselect-bgcAndBorder': true,
|
||||
'custom-treeselect-white': true,
|
||||
}"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-bg-color': '#fff',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '14px',
|
||||
}"
|
||||
v-model="selectedBot"
|
||||
|
@@ -195,12 +195,14 @@
|
||||
</template>
|
||||
</div>
|
||||
<a-form-model-item
|
||||
:label="$t('cmdb.custom_dashboard.showIcon')"
|
||||
prop="showIcon"
|
||||
:label-col="{ span: 5 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
:label-col="{ span: 0 }"
|
||||
:wrapper-col="{ span: 23 }"
|
||||
>
|
||||
<a-switch v-model="form.showIcon"></a-switch>
|
||||
<div class="chart-left-show-icon">
|
||||
<span class="chart-left-show-icon-label">{{ $t('cmdb.custom_dashboard.showIcon') }}:</span>
|
||||
<a-switch v-model="form.showIcon"></a-switch>
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
@@ -733,6 +735,9 @@ export default {
|
||||
width: 92%;
|
||||
position: relative;
|
||||
padding: 12px;
|
||||
margin-top: 4px;
|
||||
display: inline-block;
|
||||
|
||||
.chart-left-preview-operation {
|
||||
color: #86909c;
|
||||
position: absolute;
|
||||
@@ -753,12 +758,26 @@ export default {
|
||||
background-position-x: center;
|
||||
background-position-y: center;
|
||||
}
|
||||
|
||||
&-show-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&-label {
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-right {
|
||||
width: 50%;
|
||||
h4 {
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
.chart-right-type {
|
||||
display: flex;
|
||||
@@ -797,7 +816,7 @@ export default {
|
||||
<style lang="less">
|
||||
.chart-wrapper {
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -170,6 +170,7 @@ export default {
|
||||
position: relative;
|
||||
margin-bottom: 40px;
|
||||
margin-right: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
&-inner {
|
||||
position: absolute;
|
||||
@@ -294,6 +295,12 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&, &.discovery-card-small {
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 20px 0px rgba(122, 140, 204, 0.30);
|
||||
}
|
||||
}
|
||||
|
||||
&-http {
|
||||
width: 263px;
|
||||
height: 142px;
|
||||
@@ -305,6 +312,10 @@ export default {
|
||||
max-width: 30px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 28px 0px rgba(122, 140, 204, 0.30);
|
||||
}
|
||||
}
|
||||
}
|
||||
.discovery-card-small {
|
||||
@@ -312,7 +323,7 @@ export default {
|
||||
height: 80px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.discovery-card-small:hover,
|
||||
|
||||
.discovery-card-small-selected {
|
||||
.discovery-top {
|
||||
background-color: #f0f1f5;
|
||||
|
@@ -25,18 +25,24 @@
|
||||
:fileList="[]"
|
||||
:beforeUpload="beforeUpload"
|
||||
>
|
||||
<a class="setting-discovery-header-action-btn">
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ops-button-ghost"
|
||||
ghost
|
||||
>
|
||||
<a-icon type="upload" />
|
||||
{{ $t('cmdb.ad.upload') }}
|
||||
</a>
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ops-button-ghost"
|
||||
ghost
|
||||
@click="download"
|
||||
class="setting-discovery-header-action-btn"
|
||||
>
|
||||
<a-icon type="download" />
|
||||
{{ $t('cmdb.ad.download') }}
|
||||
</a>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@@ -46,10 +46,15 @@
|
||||
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedCount }) }}</span>
|
||||
</span>
|
||||
<div @click="clickLog" class="discovery-ci-log">
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
class="ops-button-ghost discovery-ci-log"
|
||||
@click="clickLog"
|
||||
>
|
||||
<ops-icon type="a-cmdb-log1" />
|
||||
<span>{{ $t('cmdb.ad.log') }}</span>
|
||||
</div>
|
||||
</a-button>
|
||||
</div>
|
||||
<ops-table
|
||||
show-overflow
|
||||
@@ -458,16 +463,7 @@ export default {
|
||||
}
|
||||
|
||||
.discovery-ci-log {
|
||||
cursor: pointer;
|
||||
background-color: #F4F9FF;
|
||||
border: solid 1px @primary-color_8;
|
||||
color: @primary-color;
|
||||
font-size: 12px;
|
||||
padding: 5px 12px;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.checkbox-hover-table {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="operation-history">
|
||||
<a-card :bordered="false">
|
||||
<a-tabs default-active-key="1">
|
||||
<a-tab-pane key="1" :tab="$t('cmdb.history.ciChange')">
|
||||
@@ -40,4 +40,10 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="less" scoped>
|
||||
.operation-history {
|
||||
/deep/ .ant-tabs-tab {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-form :colon="false">
|
||||
<a-form
|
||||
:colon="false"
|
||||
:labelCol="{ span:4 }"
|
||||
:wrapperCol="{ span:20 }"
|
||||
labelAlign="left"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col
|
||||
:sm="24"
|
||||
@@ -10,12 +15,7 @@
|
||||
v-for="attr in attrList.slice(0,3)"
|
||||
:key="attr.name"
|
||||
>
|
||||
<a-form-item
|
||||
:label="attr.alias || attr.name "
|
||||
:labelCol="{span:4}"
|
||||
:wrapperCol="{span:20}"
|
||||
labelAlign="right"
|
||||
>
|
||||
<a-form-item :label="attr.alias || attr.name">
|
||||
<a-select
|
||||
v-model="queryParams[attr.name]"
|
||||
:placeholder="$t('cmdb.history.pleaseSelect')"
|
||||
@@ -57,12 +57,7 @@
|
||||
:key="'expand_' + item.name"
|
||||
v-for="item in attrList.slice(3)"
|
||||
>
|
||||
<a-form-item
|
||||
:label="item.alias || item.name"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
labelAlign="right"
|
||||
>
|
||||
<a-form-item :label="item.alias || item.name">
|
||||
<a-select
|
||||
v-model="queryParams[item.name]"
|
||||
:placeholder="$t('cmdb.history.pleaseSelect')"
|
||||
@@ -97,7 +92,7 @@
|
||||
</template>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="24" :style="{ textAlign: 'right' , marginBottom: '10px' }">
|
||||
<a-col :span="24" :style="{ textAlign: 'right', marginBottom: '20px', marginTop: '-4px'}">
|
||||
<a-button type="primary" html-type="submit" @click="handleSearch">
|
||||
{{ $t('query') }}
|
||||
</a-button>
|
||||
|
@@ -114,7 +114,11 @@
|
||||
:placeholder="$t('cmdb.preference.searchPlaceholder')"
|
||||
/>
|
||||
<div v-for="group in filterCiTypeData" :key="group.id">
|
||||
<p @click="changeGroupExpand(group)" :style="{ display: 'inline-block', cursor: 'pointer' }">
|
||||
<p
|
||||
@click="changeGroupExpand(group)"
|
||||
:style="{ display: 'inline-block', cursor: 'pointer' }"
|
||||
class="cmdb-preference-right-group-title"
|
||||
>
|
||||
<a-icon :type="expandKeys.includes(group.id) ? 'caret-down' : 'caret-right'" />{{ group.name }}({{
|
||||
group.ci_types ? group.ci_types.length : 0
|
||||
}})
|
||||
@@ -624,6 +628,16 @@ export default {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding-top: 24px;
|
||||
|
||||
&-group-title {
|
||||
width: 300px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.cmdb-preference-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@@ -1697,6 +1697,10 @@ export default {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: @border-radius-box;
|
||||
|
||||
.ant-tabs-tab {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -19,13 +19,11 @@
|
||||
>
|
||||
<treeselect
|
||||
:value="selectCITypeIds"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder filter-content-ciTypes"
|
||||
class="custom-treeselect custom-treeselect-white filter-content-ciTypes"
|
||||
:style="{
|
||||
width: '400px',
|
||||
zIndex: '1000',
|
||||
'--custom-height': '32px',
|
||||
'--custom-bg-color': '#FFF',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '32px',
|
||||
}"
|
||||
:multiple="true"
|
||||
|
@@ -78,21 +78,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.save-condition-modal {
|
||||
/deep/ .ant-modal-close-x {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
/deep/ .ant-modal-body {
|
||||
padding: 24px 18px;
|
||||
}
|
||||
|
||||
/deep/ .ant-modal-footer {
|
||||
padding: 10px 18px 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -512,15 +512,31 @@ export default {
|
||||
padding: 14px 18px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
/deep/ .filter-content-ciTypes {
|
||||
&:not(.vue-treeselect--disabled):not(.vue-treeselect--focused) {
|
||||
.vue-treeselect__control {
|
||||
border: solid 1px transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
|
||||
/deep/ .ant-input {
|
||||
border: none;
|
||||
border: solid 1px transparent;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-suffix {
|
||||
@@ -532,8 +548,12 @@ export default {
|
||||
width: 100%;
|
||||
|
||||
/deep/ .ant-select-selection {
|
||||
border: none;
|
||||
border: solid 1px transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -164,8 +164,12 @@ export default {
|
||||
/deep/ & > input {
|
||||
height: 100%;
|
||||
margin-left: 10px;
|
||||
border: none;
|
||||
border: solid 1px transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -116,6 +116,10 @@ body {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 10px;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.topmenu {
|
||||
@@ -560,7 +564,7 @@ body {
|
||||
top: 10px;
|
||||
border-radius: 5px;
|
||||
&:hover {
|
||||
background: #c1bfbf;
|
||||
color: @primary-color;
|
||||
}
|
||||
.anticon {
|
||||
margin-right: 0;
|
||||
@@ -661,7 +665,7 @@ body {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #e7e7e7;
|
||||
background-color: @primary-color_5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -869,6 +873,10 @@ body {
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #597ef7 !important;
|
||||
}
|
||||
}
|
||||
.vue-treeselect__placeholder,
|
||||
.vue-treeselect__single-value {
|
||||
@@ -885,15 +893,46 @@ body {
|
||||
border-radius: 2px !important;
|
||||
border-color: #e4e7ed;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #597ef7 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-vue-treeselect__control(@bgColor:@primary-color_7,@border:none) {
|
||||
background-color: @bgColor;
|
||||
border: @border;
|
||||
}
|
||||
.custom-treeselect {
|
||||
.custom-vue-treeselect__control(
|
||||
@bgColor: @primary-color_7,
|
||||
@border: none,
|
||||
@hoverBgColor: none,
|
||||
@hoverBorderColor: none,
|
||||
) {
|
||||
.vue-treeselect__control {
|
||||
background-color: @bgColor;
|
||||
border: @border;
|
||||
}
|
||||
|
||||
.mixin(@borderColor) when (iscolor(@borderColor)) {
|
||||
border-color: @borderColor !important;
|
||||
}
|
||||
|
||||
&:not(.vue-treeselect--disabled):not(.vue-treeselect--focused) {
|
||||
.vue-treeselect__control {
|
||||
&:hover {
|
||||
background-color: if(iscolor(@hoverBgColor), @hoverBgColor, @bgColor);
|
||||
.mixin(@hoverBorderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-treeselect {
|
||||
.custom-vue-treeselect__control(
|
||||
@primary-color_7,
|
||||
none,
|
||||
@primary-color_5,
|
||||
none
|
||||
);
|
||||
|
||||
.vue-treeselect__control {
|
||||
.custom-vue-treeselect__control();
|
||||
height: var(--custom-height) !important;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
@@ -909,12 +948,34 @@ body {
|
||||
.vue-treeselect__limit-tip-text {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.vue-treeselect--focused {
|
||||
.vue-treeselect__control {
|
||||
border: 1px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义背景颜色和border
|
||||
.custom-treeselect-bgcAndBorder .vue-treeselect__control {
|
||||
.custom-vue-treeselect__control(var(--custom-bg-color), var(--custom-border));
|
||||
.custom-treeselect-bgcAndBorder {
|
||||
.custom-vue-treeselect__control(
|
||||
var(--custom-bg-color),
|
||||
var(--custom-border),
|
||||
var(--custom-hover-bg-color),
|
||||
var(--custom-hover-border-color)
|
||||
);
|
||||
}
|
||||
|
||||
// 白色背景
|
||||
.custom-treeselect-white {
|
||||
.custom-vue-treeselect__control(
|
||||
#fff,
|
||||
1px solid #d9d9d9,
|
||||
none,
|
||||
@primary-color
|
||||
);
|
||||
}
|
||||
|
||||
// 自定义背景颜色和border
|
||||
.custom-treeselect.vue-treeselect--multi {
|
||||
.vue-treeselect__multi-value,
|
||||
@@ -1025,6 +1086,20 @@ body {
|
||||
background-color: @primary-color_7;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&,
|
||||
.ant-input,
|
||||
.ant-time-picker-input {
|
||||
&:not([disabled]) {
|
||||
&:hover {
|
||||
background-color: @primary-color_5;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: @primary-color_7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ops-input.ant-input {
|
||||
background-color: @primary-color_7;
|
||||
@@ -1163,13 +1238,31 @@ body {
|
||||
}
|
||||
|
||||
// button
|
||||
.ops-button-ghost.ant-btn-background-ghost.ant-btn-primary {
|
||||
background-color: @primary-color_5!important;
|
||||
border-color: @primary-color_8;
|
||||
.ops-button-ghost {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&.ant-btn-background-ghost.ant-btn-primary {
|
||||
border-color: @primary-color_8;
|
||||
background-color: @primary-color_5 !important;
|
||||
box-shadow: none;
|
||||
|
||||
.btn-wave-hover(@primary-color_4);
|
||||
|
||||
&[disabled] {
|
||||
border-color: #d9d9d9;
|
||||
background-color: @primary-color_7!important;
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: #3F75FF;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ops-button-ghost.ant-btn-background-ghost.ant-btn-primary[disabled] {
|
||||
border-color: #d9d9d9;
|
||||
background-color: @primary-color_7!important;
|
||||
|
||||
.ant-btn-primary:not(.ant-btn-background-ghost) {
|
||||
.btn-wave-hover(#3F75FF);
|
||||
}
|
||||
|
||||
// button
|
||||
@@ -1214,15 +1307,25 @@ body {
|
||||
|
||||
//modal
|
||||
.ant-modal-content {
|
||||
.ant-modal-close-x {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
border-bottom: none;
|
||||
padding: 22px 22px;
|
||||
|
||||
.ant-modal-title {
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-top: none;
|
||||
padding: 16px 22px 22px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -70,3 +70,46 @@
|
||||
background-color: @primary-color_3;
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.btn-wave-hover(
|
||||
@hoverBgColor,
|
||||
@bgZIndex: 0
|
||||
) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:not([disabled])::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: @bgZIndex;
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 50%;
|
||||
|
||||
background-color: @hoverBgColor;
|
||||
-webkit-transform: scale(0) translate(-50%, -50%);
|
||||
-ms-transform: scale(0) translate(-50%, -50%);
|
||||
transform: scale(0) translate(-50%, -50%);
|
||||
transform-origin: left top;
|
||||
|
||||
-webkit-transition: -webkit-transform 0.3s ease-out;
|
||||
transition: -webkit-transform 0.3s ease-out;
|
||||
transition: transform 0.3s ease-out;
|
||||
transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
&::after {
|
||||
-webkit-transform: scale(2) translate(-50%, -50%);
|
||||
-ms-transform: scale(2) translate(-50%, -50%);
|
||||
transform: scale(2) translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user