mirror of
https://github.com/veops/cmdb.git
synced 2025-08-08 21:47:06 +08:00
feat(ui):i18n (#347)
* feat(acl-ui):i18n * feat(base-ui):i18n * feat(cmdb-ui):i18n
This commit is contained in:
@@ -1,29 +1,37 @@
|
||||
export const ruleTypeList = [
|
||||
{ value: 'and', label: '与' },
|
||||
{ value: 'or', label: '或' },
|
||||
// { value: 'not', label: '非' },
|
||||
]
|
||||
import i18n from '@/lang'
|
||||
|
||||
export const expList = [
|
||||
{ value: 'is', label: '等于' },
|
||||
{ value: '~is', label: '不等于' },
|
||||
{ value: 'contain', label: '包含' },
|
||||
{ value: '~contain', label: '不包含' },
|
||||
{ value: 'start_with', label: '以...开始' },
|
||||
{ value: '~start_with', label: '不以...开始' },
|
||||
{ value: 'end_with', label: '以...结束' },
|
||||
{ value: '~end_with', label: '不以...结束' },
|
||||
{ value: '~value', label: '为空' }, // 为空的定义有点绕
|
||||
{ value: 'value', label: '不为空' },
|
||||
]
|
||||
export const ruleTypeList = () => {
|
||||
return [
|
||||
{ value: 'and', label: i18n.t('cmdbFilterComp.and') },
|
||||
{ value: 'or', label: i18n.t('cmdbFilterComp.or') },
|
||||
// { value: 'not', label: '非' },
|
||||
]
|
||||
}
|
||||
|
||||
export const advancedExpList = [
|
||||
{ value: 'in', label: 'in查询' },
|
||||
{ value: '~in', label: '非in查询' },
|
||||
{ value: 'range', label: '范围' },
|
||||
{ value: '~range', label: '范围外' },
|
||||
{ value: 'compare', label: '比较' },
|
||||
]
|
||||
export const expList = () => {
|
||||
return [
|
||||
{ value: 'is', label: i18n.t('cmdbFilterComp.is') },
|
||||
{ value: '~is', label: i18n.t('cmdbFilterComp.~is') },
|
||||
{ value: 'contain', label: i18n.t('cmdbFilterComp.contain') },
|
||||
{ value: '~contain', label: i18n.t('cmdbFilterComp.~contain') },
|
||||
{ value: 'start_with', label: i18n.t('cmdbFilterComp.start_with') },
|
||||
{ value: '~start_with', label: i18n.t('cmdbFilterComp.~start_with') },
|
||||
{ value: 'end_with', label: i18n.t('cmdbFilterComp.end_with') },
|
||||
{ value: '~end_with', label: i18n.t('cmdbFilterComp.~end_with') },
|
||||
{ value: '~value', label: i18n.t('cmdbFilterComp.~value') }, // 为空的定义有点绕
|
||||
{ value: 'value', label: i18n.t('cmdbFilterComp.value') },
|
||||
]
|
||||
}
|
||||
|
||||
export const advancedExpList = () => {
|
||||
return [
|
||||
{ value: 'in', label: i18n.t('cmdbFilterComp.in') },
|
||||
{ value: '~in', label: i18n.t('cmdbFilterComp.~in') },
|
||||
{ value: 'range', label: i18n.t('cmdbFilterComp.range') },
|
||||
{ value: '~range', label: i18n.t('cmdbFilterComp.~range') },
|
||||
{ value: 'compare', label: i18n.t('cmdbFilterComp.compare') },
|
||||
]
|
||||
}
|
||||
|
||||
export const compareTypeList = [
|
||||
{ value: '1', label: '>' },
|
||||
|
@@ -1,293 +1,332 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||
<div :style="{ width: '50px', height: '24px', position: 'relative' }">
|
||||
<treeselect
|
||||
v-if="index"
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
|
||||
v-model="item.type"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="ruleTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
</treeselect>
|
||||
</div>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '130px', '--custom-height': '24px' }"
|
||||
v-model="item.property"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="canSearchPreferenceAttrList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.name,
|
||||
label: node.alias || node.name,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" />
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
slot="value-label"
|
||||
slot-scope="{ node }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '100px', '--custom-height': '24px' }"
|
||||
v-model="item.exp"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="[...getExpListByProperty(item.property), ...advancedExpList]"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
@select="(value) => handleChangeExp(value, item, index)"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '175px', '--custom-height': '24px' }"
|
||||
v-model="item.value"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
|
||||
:options="getChoiceValueByProperty(item.property)"
|
||||
placeholder="请选择"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node[0],
|
||||
label: node[0],
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<a-input-group
|
||||
size="small"
|
||||
compact
|
||||
v-else-if="item.exp === 'range' || item.exp === '~range'"
|
||||
:style="{ width: '175px' }"
|
||||
>
|
||||
<a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
|
||||
~
|
||||
<a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
|
||||
</a-input-group>
|
||||
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '60px', '--custom-height': '24px' }"
|
||||
v-model="item.compareType"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="compareTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
</treeselect>
|
||||
<a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
|
||||
</a-input-group>
|
||||
<a-input
|
||||
v-else-if="item.exp !== 'value' && item.exp !== '~value'"
|
||||
size="small"
|
||||
v-model="item.value"
|
||||
:placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
|
||||
class="ops-input"
|
||||
></a-input>
|
||||
<div v-else :style="{ width: '175px' }"></div>
|
||||
<a-tooltip title="复制">
|
||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="删除">
|
||||
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
<div class="table-filter-add">
|
||||
<a @click="handleAddRule">+ 新增</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
|
||||
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
|
||||
|
||||
export default {
|
||||
name: 'Expression',
|
||||
components: { ValueTypeMapIcon },
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ruleTypeList,
|
||||
expList,
|
||||
advancedExpList,
|
||||
compareTypeList,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ruleList: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getExpListByProperty(property) {
|
||||
if (property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||
return [
|
||||
{ value: 'is', label: '等于' },
|
||||
{ value: '~is', label: '不等于' },
|
||||
{ value: '~value', label: '为空' }, // 为空的定义有点绕
|
||||
{ value: 'value', label: '不为空' },
|
||||
]
|
||||
}
|
||||
return this.expList
|
||||
}
|
||||
return this.expList
|
||||
},
|
||||
isChoiceByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.is_choice
|
||||
}
|
||||
return false
|
||||
},
|
||||
handleAddRule() {
|
||||
this.ruleList.push({
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
})
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleCopyRule(item) {
|
||||
this.ruleList.push({ ...item, id: uuidv4() })
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleDeleteRule(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx, 1)
|
||||
}
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
getChoiceValueByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleChangeExp({ value }, item, index) {
|
||||
const _ruleList = _.cloneDeep(this.ruleList)
|
||||
if (value === 'range') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
min: '',
|
||||
max: '',
|
||||
exp: value,
|
||||
}
|
||||
} else if (value === 'compare') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
compareType: '1',
|
||||
exp: value,
|
||||
}
|
||||
} else {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
exp: value,
|
||||
}
|
||||
}
|
||||
this.ruleList = _ruleList
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<template>
|
||||
<div>
|
||||
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||
<div :style="{ width: '70px', height: '24px', position: 'relative' }">
|
||||
<treeselect
|
||||
v-if="index"
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '70px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
|
||||
v-model="item.type"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="ruleTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
</treeselect>
|
||||
</div>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '130px', '--custom-height': '24px' }"
|
||||
v-model="item.property"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="canSearchPreferenceAttrList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.name,
|
||||
label: node.alias || node.name,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" />
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
slot="value-label"
|
||||
slot-scope="{ node }"
|
||||
>
|
||||
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '100px', '--custom-height': '24px' }"
|
||||
v-model="item.exp"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="[...getExpListByProperty(item.property), ...advancedExpList]"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
@select="(value) => handleChangeExp(value, item, index)"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
</treeselect>
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '175px', '--custom-height': '24px' }"
|
||||
v-model="item.value"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
|
||||
:options="getChoiceValueByProperty(item.property)"
|
||||
:placeholder="$t('placeholder2')"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node[0],
|
||||
label: node[0],
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
<div
|
||||
:title="node.label"
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||
>
|
||||
{{ node.label }}
|
||||
</div>
|
||||
</treeselect>
|
||||
<a-input-group
|
||||
size="small"
|
||||
compact
|
||||
v-else-if="item.exp === 'range' || item.exp === '~range'"
|
||||
:style="{ width: '175px' }"
|
||||
>
|
||||
<a-input
|
||||
class="ops-input"
|
||||
size="small"
|
||||
v-model="item.min"
|
||||
:style="{ width: '78px' }"
|
||||
:placeholder="$t('min')"
|
||||
/>
|
||||
~
|
||||
<a-input
|
||||
class="ops-input"
|
||||
size="small"
|
||||
v-model="item.max"
|
||||
:style="{ width: '78px' }"
|
||||
:placeholder="$t('max')"
|
||||
/>
|
||||
</a-input-group>
|
||||
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
|
||||
<treeselect
|
||||
class="custom-treeselect"
|
||||
:style="{ width: '60px', '--custom-height': '24px' }"
|
||||
v-model="item.compareType"
|
||||
:multiple="false"
|
||||
:clearable="false"
|
||||
searchable
|
||||
:options="compareTypeList"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.value,
|
||||
label: node.label,
|
||||
children: node.children,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:zIndex="1050"
|
||||
>
|
||||
</treeselect>
|
||||
<a-input class="ops-input" v-model="item.value" size="small" style="width: 113px" />
|
||||
</a-input-group>
|
||||
<a-input
|
||||
v-else-if="item.exp !== 'value' && item.exp !== '~value'"
|
||||
size="small"
|
||||
v-model="item.value"
|
||||
:placeholder="item.exp === 'in' || item.exp === '~in' ? $t('cmdbFilterComp.split', { separator: ';' }) : ''"
|
||||
class="ops-input"
|
||||
:style="{ width: '175px' }"
|
||||
></a-input>
|
||||
<div v-else :style="{ width: '175px' }"></div>
|
||||
<a-tooltip :title="$t('copy')">
|
||||
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('delete')">
|
||||
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('cmdbFilterComp.addHere')" :needAddHere="needAddHere">
|
||||
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
<div class="table-filter-add">
|
||||
<a @click="handleAddRule">+ {{ $t('new') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants'
|
||||
import ValueTypeMapIcon from '../CMDBValueTypeMapIcon'
|
||||
|
||||
export default {
|
||||
name: 'Expression',
|
||||
components: { ValueTypeMapIcon },
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
needAddHere: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
compareTypeList,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ruleList: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
ruleTypeList() {
|
||||
return ruleTypeList()
|
||||
},
|
||||
expList() {
|
||||
return expList()
|
||||
},
|
||||
advancedExpList() {
|
||||
return advancedExpList()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getExpListByProperty(property) {
|
||||
if (property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||
return [
|
||||
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
|
||||
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
|
||||
{ value: '~value', label: this.$t('cmdbFilterComp.~value') }, // 为空的定义有点绕
|
||||
{ value: 'value', label: this.$t('cmdbFilterComp.value') },
|
||||
]
|
||||
}
|
||||
return this.expList
|
||||
}
|
||||
return this.expList
|
||||
},
|
||||
isChoiceByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.is_choice
|
||||
}
|
||||
return false
|
||||
},
|
||||
handleAddRule() {
|
||||
this.ruleList.push({
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
})
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleCopyRule(item) {
|
||||
this.ruleList.push({ ...item, id: uuidv4() })
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleDeleteRule(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx, 1)
|
||||
}
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
handleAddRuleAt(item) {
|
||||
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||
if (idx > -1) {
|
||||
this.ruleList.splice(idx, 0, {
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
})
|
||||
}
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
getChoiceValueByProperty(property) {
|
||||
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||
if (_find) {
|
||||
return _find.choice_value
|
||||
}
|
||||
return []
|
||||
},
|
||||
handleChangeExp({ value }, item, index) {
|
||||
const _ruleList = _.cloneDeep(this.ruleList)
|
||||
if (value === 'range') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
min: '',
|
||||
max: '',
|
||||
exp: value,
|
||||
}
|
||||
} else if (value === 'compare') {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
compareType: '1',
|
||||
exp: value,
|
||||
}
|
||||
} else {
|
||||
_ruleList[index] = {
|
||||
..._ruleList[index],
|
||||
exp: value,
|
||||
}
|
||||
}
|
||||
this.ruleList = _ruleList
|
||||
this.$emit('change', this.ruleList)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@@ -1,290 +1,296 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-popover
|
||||
v-if="isDropdown"
|
||||
v-model="visible"
|
||||
trigger="click"
|
||||
:placement="placement"
|
||||
overlayClassName="table-filter"
|
||||
@visibleChange="visibleChange"
|
||||
>
|
||||
<slot name="popover_item">
|
||||
<a-button type="primary" ghost>条件过滤<a-icon type="filter"/></a-button>
|
||||
</slot>
|
||||
<template slot="content">
|
||||
<Expression
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
/>
|
||||
<a-divider :style="{ margin: '10px 0' }" />
|
||||
<div style="width:534px">
|
||||
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
|
||||
<a-button type="primary" size="small" @click="handleSubmit">确定</a-button>
|
||||
<a-button size="small" @click="handleClear">清空</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
<Expression
|
||||
v-else
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Expression from './expression.vue'
|
||||
import { advancedExpList, compareTypeList } from './constants'
|
||||
|
||||
export default {
|
||||
name: 'FilterComp',
|
||||
components: { Expression },
|
||||
props: {
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
regQ: {
|
||||
type: String,
|
||||
default: '(?<=q=).+(?=&)|(?<=q=).+$',
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottomRight',
|
||||
},
|
||||
isDropdown: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
advancedExpList,
|
||||
compareTypeList,
|
||||
visible: false,
|
||||
ruleList: [],
|
||||
filterExp: '',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
visibleChange(open, isInitOne = true) {
|
||||
// isInitOne 初始化exp为空时,ruleList是否默认给一条
|
||||
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
|
||||
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
|
||||
: null
|
||||
if (open && exp) {
|
||||
const expArray = exp.split(',').map((item) => {
|
||||
let has_not = ''
|
||||
const key = item.split(':')[0]
|
||||
const val = item
|
||||
.split(':')
|
||||
.slice(1)
|
||||
.join(':')
|
||||
let type, property, exp, value, min, max, compareType
|
||||
if (key.includes('-')) {
|
||||
type = 'or'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(2)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key.substring(1)
|
||||
}
|
||||
} else {
|
||||
type = 'and'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(1)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key
|
||||
}
|
||||
}
|
||||
|
||||
const in_reg = /(?<=\().+(?=\))/g
|
||||
const range_reg = /(?<=\[).+(?=\])/g
|
||||
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
|
||||
if (val === '*') {
|
||||
exp = has_not + 'value'
|
||||
value = ''
|
||||
} else if (in_reg.test(val)) {
|
||||
exp = has_not + 'in'
|
||||
value = val.match(in_reg)[0]
|
||||
} else if (range_reg.test(val)) {
|
||||
exp = has_not + 'range'
|
||||
value = val.match(range_reg)[0]
|
||||
min = value.split('_TO_')[0]
|
||||
max = value.split('_TO_')[1]
|
||||
} else if (compare_reg.test(val)) {
|
||||
exp = has_not + 'compare'
|
||||
value = val.match(compare_reg)[0]
|
||||
const _compareType = val.substring(0, val.match(compare_reg)['index'])
|
||||
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
|
||||
compareType = compareTypeList[idx].value
|
||||
} else if (!val.includes('*')) {
|
||||
exp = has_not + 'is'
|
||||
value = val
|
||||
} else {
|
||||
const resList = [
|
||||
['contain', /(?<=\*).*(?=\*)/g],
|
||||
['end_with', /(?<=\*).+/g],
|
||||
['start_with', /.+(?=\*)/g],
|
||||
]
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const reg = resList[i]
|
||||
if (reg[1].test(val)) {
|
||||
exp = has_not + reg[0]
|
||||
value = val.match(reg[1])[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: uuidv4(),
|
||||
type,
|
||||
property,
|
||||
exp,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
compareType,
|
||||
}
|
||||
})
|
||||
this.ruleList = [...expArray]
|
||||
} else if (open) {
|
||||
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
|
||||
this.ruleList = isInitOne
|
||||
? [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property:
|
||||
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
|
||||
? _canSearchPreferenceAttrList[0].name
|
||||
: undefined,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
},
|
||||
handleClear() {
|
||||
this.ruleList = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0].name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
this.filterExp = ''
|
||||
this.visible = false
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
},
|
||||
handleSubmit() {
|
||||
if (this.ruleList && this.ruleList.length) {
|
||||
this.ruleList[0].type = 'and' // 增删后,以防万一第一个不是and
|
||||
this.filterExp = ''
|
||||
const expList = this.ruleList.map((rule) => {
|
||||
let singleRuleExp = ''
|
||||
let _exp = rule.exp
|
||||
if (rule.type === 'or') {
|
||||
singleRuleExp += '-'
|
||||
}
|
||||
if (rule.exp.includes('~')) {
|
||||
singleRuleExp += '~'
|
||||
_exp = rule.exp.split('~')[1]
|
||||
}
|
||||
singleRuleExp += `${rule.property}:`
|
||||
if (_exp === 'is') {
|
||||
singleRuleExp += `${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'contain') {
|
||||
singleRuleExp += `*${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'start_with') {
|
||||
singleRuleExp += `${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'end_with') {
|
||||
singleRuleExp += `*${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'value') {
|
||||
singleRuleExp += `*`
|
||||
}
|
||||
if (_exp === 'in') {
|
||||
singleRuleExp += `(${rule.value ?? ''})`
|
||||
}
|
||||
if (_exp === 'range') {
|
||||
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
|
||||
}
|
||||
if (_exp === 'compare') {
|
||||
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
|
||||
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
|
||||
}
|
||||
return singleRuleExp
|
||||
})
|
||||
this.filterExp = expList.join(',')
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
} else {
|
||||
this.$emit('setExpFromFilter', '')
|
||||
}
|
||||
this.visible = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table-filter {
|
||||
.table-filter-add {
|
||||
margin-top: 10px;
|
||||
& > a {
|
||||
padding: 2px 8px;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.table-filter-extra-icon {
|
||||
padding: 0px 2px;
|
||||
&:hover {
|
||||
display: inline-block;
|
||||
border-radius: 5px;
|
||||
background-color: #f0faff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.table-filter-extra-operation {
|
||||
.ant-popover-inner-content {
|
||||
padding: 3px 4px;
|
||||
.operation {
|
||||
cursor: pointer;
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 3px 4px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
}
|
||||
> .anticon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div>
|
||||
<a-popover
|
||||
v-if="isDropdown"
|
||||
v-model="visible"
|
||||
trigger="click"
|
||||
:placement="placement"
|
||||
overlayClassName="table-filter"
|
||||
@visibleChange="visibleChange"
|
||||
>
|
||||
<slot name="popover_item">
|
||||
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
|
||||
</slot>
|
||||
<template slot="content">
|
||||
<Expression
|
||||
:needAddHere="needAddHere"
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
/>
|
||||
<a-divider :style="{ margin: '10px 0' }" />
|
||||
<div style="width:554px">
|
||||
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
|
||||
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
|
||||
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
<Expression
|
||||
:needAddHere="needAddHere"
|
||||
v-else
|
||||
v-model="ruleList"
|
||||
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Expression from './expression.vue'
|
||||
import { advancedExpList, compareTypeList } from './constants'
|
||||
|
||||
export default {
|
||||
name: 'FilterComp',
|
||||
components: { Expression },
|
||||
props: {
|
||||
canSearchPreferenceAttrList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
regQ: {
|
||||
type: String,
|
||||
default: '(?<=q=).+(?=&)|(?<=q=).+$',
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottomRight',
|
||||
},
|
||||
isDropdown: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
needAddHere: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
advancedExpList,
|
||||
compareTypeList,
|
||||
visible: false,
|
||||
ruleList: [],
|
||||
filterExp: '',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
visibleChange(open, isInitOne = true) {
|
||||
// isInitOne 初始化exp为空时,ruleList是否默认给一条
|
||||
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
|
||||
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
|
||||
: null
|
||||
if (open && exp) {
|
||||
const expArray = exp.split(',').map((item) => {
|
||||
let has_not = ''
|
||||
const key = item.split(':')[0]
|
||||
const val = item
|
||||
.split(':')
|
||||
.slice(1)
|
||||
.join(':')
|
||||
let type, property, exp, value, min, max, compareType
|
||||
if (key.includes('-')) {
|
||||
type = 'or'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(2)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key.substring(1)
|
||||
}
|
||||
} else {
|
||||
type = 'and'
|
||||
if (key.includes('~')) {
|
||||
property = key.substring(1)
|
||||
has_not = '~'
|
||||
} else {
|
||||
property = key
|
||||
}
|
||||
}
|
||||
|
||||
const in_reg = /(?<=\().+(?=\))/g
|
||||
const range_reg = /(?<=\[).+(?=\])/g
|
||||
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
|
||||
if (val === '*') {
|
||||
exp = has_not + 'value'
|
||||
value = ''
|
||||
} else if (in_reg.test(val)) {
|
||||
exp = has_not + 'in'
|
||||
value = val.match(in_reg)[0]
|
||||
} else if (range_reg.test(val)) {
|
||||
exp = has_not + 'range'
|
||||
value = val.match(range_reg)[0]
|
||||
min = value.split('_TO_')[0]
|
||||
max = value.split('_TO_')[1]
|
||||
} else if (compare_reg.test(val)) {
|
||||
exp = has_not + 'compare'
|
||||
value = val.match(compare_reg)[0]
|
||||
const _compareType = val.substring(0, val.match(compare_reg)['index'])
|
||||
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
|
||||
compareType = compareTypeList[idx].value
|
||||
} else if (!val.includes('*')) {
|
||||
exp = has_not + 'is'
|
||||
value = val
|
||||
} else {
|
||||
const resList = [
|
||||
['contain', /(?<=\*).*(?=\*)/g],
|
||||
['end_with', /(?<=\*).+/g],
|
||||
['start_with', /.+(?=\*)/g],
|
||||
]
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const reg = resList[i]
|
||||
if (reg[1].test(val)) {
|
||||
exp = has_not + reg[0]
|
||||
value = val.match(reg[1])[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: uuidv4(),
|
||||
type,
|
||||
property,
|
||||
exp,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
compareType,
|
||||
}
|
||||
})
|
||||
this.ruleList = [...expArray]
|
||||
} else if (open) {
|
||||
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
|
||||
this.ruleList = isInitOne
|
||||
? [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property:
|
||||
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
|
||||
? _canSearchPreferenceAttrList[0].name
|
||||
: undefined,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
},
|
||||
handleClear() {
|
||||
this.ruleList = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0].name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
this.filterExp = ''
|
||||
this.visible = false
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
},
|
||||
handleSubmit() {
|
||||
if (this.ruleList && this.ruleList.length) {
|
||||
this.ruleList[0].type = 'and' // 增删后,以防万一第一个不是and
|
||||
this.filterExp = ''
|
||||
const expList = this.ruleList.map((rule) => {
|
||||
let singleRuleExp = ''
|
||||
let _exp = rule.exp
|
||||
if (rule.type === 'or') {
|
||||
singleRuleExp += '-'
|
||||
}
|
||||
if (rule.exp.includes('~')) {
|
||||
singleRuleExp += '~'
|
||||
_exp = rule.exp.split('~')[1]
|
||||
}
|
||||
singleRuleExp += `${rule.property}:`
|
||||
if (_exp === 'is') {
|
||||
singleRuleExp += `${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'contain') {
|
||||
singleRuleExp += `*${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'start_with') {
|
||||
singleRuleExp += `${rule.value ?? ''}*`
|
||||
}
|
||||
if (_exp === 'end_with') {
|
||||
singleRuleExp += `*${rule.value ?? ''}`
|
||||
}
|
||||
if (_exp === 'value') {
|
||||
singleRuleExp += `*`
|
||||
}
|
||||
if (_exp === 'in') {
|
||||
singleRuleExp += `(${rule.value ?? ''})`
|
||||
}
|
||||
if (_exp === 'range') {
|
||||
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
|
||||
}
|
||||
if (_exp === 'compare') {
|
||||
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
|
||||
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
|
||||
}
|
||||
return singleRuleExp
|
||||
})
|
||||
this.filterExp = expList.join(',')
|
||||
this.$emit('setExpFromFilter', this.filterExp)
|
||||
} else {
|
||||
this.$emit('setExpFromFilter', '')
|
||||
}
|
||||
this.visible = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table-filter {
|
||||
.table-filter-add {
|
||||
margin-top: 10px;
|
||||
& > a {
|
||||
padding: 2px 8px;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.table-filter-extra-icon {
|
||||
padding: 0px 2px;
|
||||
&:hover {
|
||||
display: inline-block;
|
||||
border-radius: 5px;
|
||||
background-color: #f0faff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.table-filter-extra-operation {
|
||||
.ant-popover-inner-content {
|
||||
padding: 3px 4px;
|
||||
.operation {
|
||||
cursor: pointer;
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 3px 4px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
background-color: #f0faff;
|
||||
}
|
||||
> .anticon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
export const iconTypeList = [
|
||||
import i18n from '@/lang'
|
||||
|
||||
export const iconTypeList = () => [
|
||||
// { value: '0', label: '常用' },
|
||||
{ value: '1', label: '线性' },
|
||||
{ value: '2', label: '实底' },
|
||||
{ value: '3', label: '多色' }
|
||||
{ value: '1', label: i18n.t('customIconSelect.outlined') },
|
||||
{ value: '2', label: i18n.t('customIconSelect.filled') },
|
||||
{ value: '3', label: i18n.t('customIconSelect.multicolor') }
|
||||
]
|
||||
|
||||
export const commonIconList = ['changyong-ubuntu',
|
||||
|
@@ -16,7 +16,7 @@
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div :class="`${currentIconType === '4' ? 'selected' : ''}`" @click="handleChangeIconType('4')">
|
||||
自定义
|
||||
{{ this.$t('customIconSelect.custom') }}
|
||||
</div>
|
||||
<a-upload
|
||||
slot="description"
|
||||
@@ -26,7 +26,7 @@
|
||||
accept=".svg,.png,.jpg,.jpeg"
|
||||
v-if="currentIconType === '4'"
|
||||
>
|
||||
<a-button icon="plus" size="small" type="primary">添加</a-button>
|
||||
<a-button icon="plus" size="small" type="primary">{{ $t('add') }}</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
<div class="custom-icon-select-popover-content">
|
||||
@@ -55,11 +55,11 @@
|
||||
@click="clickCustomIcon(icon)"
|
||||
>
|
||||
<div class="custom-icon-select-popover-content-img-box">
|
||||
<img :src="`/api/common-setting/v1/file/${icon.data.url}`" />
|
||||
<img v-if="icon.data && icon.data.url" :src="`/api/common-setting/v1/file/${icon.data.url}`" />
|
||||
<a-popconfirm
|
||||
overlayClassName="custom-icon-select-confirm-popover"
|
||||
:getPopupContainer="(trigger) => trigger.parentNode"
|
||||
title="确认删除?"
|
||||
:title="$t('confirmDelete')"
|
||||
@confirm="(e) => deleteIcon(e, icon)"
|
||||
@cancel="
|
||||
(e) => {
|
||||
@@ -102,27 +102,27 @@
|
||||
</template>
|
||||
<a-form class="custom-icon-select-form" :form="form" v-show="currentIconType === '4' && formVisible">
|
||||
<a-form-item
|
||||
label="名称"
|
||||
:label="$t('name')"
|
||||
:labelCol="{ span: 4 }"
|
||||
:wrapperCol="{ span: 16 }"
|
||||
><a-input
|
||||
v-decorator="['name', { rules: [{ required: true, message: '请输入名称' }] }]"
|
||||
v-decorator="['name', { rules: [{ required: true, message: $t('placeholder1') }] }]"
|
||||
/></a-form-item>
|
||||
<a-form-item label="预览" :labelCol="{ span: 4 }">
|
||||
<a-form-item :label="$t('customIconSelect.preview')" :labelCol="{ span: 4 }">
|
||||
<div class="custom-icon-select-form-img">
|
||||
<img :src="formImg" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label=" " :colon="false" :labelCol="{ span: 16 }">
|
||||
<a-space>
|
||||
<a-button size="small" @click="handleCancel">取消</a-button>
|
||||
<a-button size="small" type="primary" @click="handleOk">确定</a-button>
|
||||
<a-button size="small" @click="handleCancel">{{ $t('cancel') }}</a-button>
|
||||
<a-button size="small" type="primary" @click="handleOk">{{ $t('confirm') }}</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<div class="custom-icon-select-block" id="custom-icon-select-block" @click="showSelect">
|
||||
<div class="custom-icon-select-block" :id="`custom-icon-select-block-${uuid}`" @click="showSelect">
|
||||
<img v-if="value.id && value.url" :src="`/api/common-setting/v1/file/${value.url}`" />
|
||||
<ops-icon
|
||||
v-else
|
||||
@@ -134,6 +134,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ColorPicker } from 'element-ui'
|
||||
import {
|
||||
iconTypeList,
|
||||
@@ -166,7 +167,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
form: this.$form.createForm(this),
|
||||
iconTypeList,
|
||||
commonIconList,
|
||||
linearIconList,
|
||||
fillIconList,
|
||||
@@ -177,6 +177,7 @@ export default {
|
||||
formVisible: false,
|
||||
formImg: null,
|
||||
file: null,
|
||||
uuid: uuidv4(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -200,6 +201,9 @@ export default {
|
||||
const splitFileName = this.file.name.split('.')
|
||||
return splitFileName.splice(0, splitFileName.length - 1).join('')
|
||||
},
|
||||
iconTypeList() {
|
||||
return iconTypeList()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.eventListener)
|
||||
@@ -217,7 +221,7 @@ export default {
|
||||
eventListener(e) {
|
||||
if (this.visible) {
|
||||
const dom = document.getElementById(`custom-icon-select-popover`)
|
||||
const dom_icon = document.getElementById(`custom-icon-select-block`)
|
||||
const dom_icon = document.getElementById(`custom-icon-select-block-${this.uuid}`)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (dom) {
|
||||
@@ -249,12 +253,11 @@ export default {
|
||||
color: '',
|
||||
})
|
||||
} else {
|
||||
this.$emit('change', { name: icon.data.name, id: icon.id, url: icon.data.url })
|
||||
this.$emit('change', { name: icon.data.name, id: icon.id, url: icon?.data?.url })
|
||||
}
|
||||
},
|
||||
showSelect() {
|
||||
this.visible = true
|
||||
console.log(this.value)
|
||||
if (!this.value.name) {
|
||||
this.currentIconType = '3'
|
||||
return
|
||||
@@ -278,7 +281,7 @@ export default {
|
||||
beforeUpload(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
if (!isLt2M) {
|
||||
this.$message.error('图片大小不可超过2MB!')
|
||||
this.$message.error(this.$t('customIconSelect.sizeLimit'))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -306,7 +309,7 @@ export default {
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
addFileData('ops-custom-icon', { data: { name: values.name, url: res.file_name } }).then(() => {
|
||||
this.$message.success('上传成功!')
|
||||
this.$message.success(this.$t('uploadSuccess'))
|
||||
this.handleCancel()
|
||||
this.getFileData()
|
||||
})
|
||||
@@ -318,7 +321,7 @@ export default {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
deleteFileData('ops-custom-icon', icon.id).then(() => {
|
||||
this.$message.success('删除成功!')
|
||||
this.$message.success(this.$t('deleteSuccess'))
|
||||
this.handleCancel()
|
||||
this.getFileData()
|
||||
})
|
||||
|
@@ -6,17 +6,17 @@
|
||||
:flat="true"
|
||||
:multiple="true"
|
||||
:options="employeeTreeSelectOption"
|
||||
placeholder="请输入搜索内容"
|
||||
:placeholder="$t('placeholderSearch')"
|
||||
v-model="treeValue"
|
||||
:max-height="height - 50"
|
||||
noChildrenText="空"
|
||||
noOptionsText="空"
|
||||
:clearable="false"
|
||||
:always-open="true"
|
||||
:default-expand-level="1"
|
||||
:default-expand-level="showInternship ? 0 : 1"
|
||||
:class="{ 'employee-transfer': true, 'employee-transfer-has-input': !!inputValue }"
|
||||
@search-change="changeInputValue"
|
||||
noResultsText="暂无数据"
|
||||
:noResultsText="$t('noData')"
|
||||
openDirection="below"
|
||||
>
|
||||
</treeselect>
|
||||
@@ -85,6 +85,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showInternship: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -99,13 +103,22 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
employeeTreeSelectOption() {
|
||||
return formatOption(
|
||||
const formatOptions = formatOption(
|
||||
this.allTreeDepAndEmp,
|
||||
2,
|
||||
this.isDisabledAllCompany,
|
||||
this.uniqueKey || 'department_id',
|
||||
this.uniqueKey || 'employee_id'
|
||||
)
|
||||
if (this.showInternship) {
|
||||
formatOptions.push(
|
||||
...[
|
||||
{ id: -2, label: '全职' },
|
||||
{ id: -3, label: '实习生' },
|
||||
]
|
||||
)
|
||||
}
|
||||
return formatOptions
|
||||
},
|
||||
allTreeDepAndEmp() {
|
||||
if (this.getDataBySelf) {
|
||||
@@ -148,11 +161,15 @@ export default {
|
||||
const department = []
|
||||
const user = []
|
||||
this.rightData.forEach((item) => {
|
||||
const _split = item.split('-')
|
||||
if (_split[0] === 'department') {
|
||||
department.push(Number(_split[1]))
|
||||
if (item === -2 || item === -3) {
|
||||
department.push(item)
|
||||
} else {
|
||||
user.push(Number(_split[1]))
|
||||
const _split = item.split('-')
|
||||
if (_split[0] === 'department') {
|
||||
department.push(Number(_split[1]))
|
||||
} else {
|
||||
user.push(Number(_split[1]))
|
||||
}
|
||||
}
|
||||
})
|
||||
const _idx = department.findIndex((item) => item === 0)
|
||||
@@ -191,6 +208,12 @@ export default {
|
||||
}
|
||||
},
|
||||
getLabel(id) {
|
||||
if (id === -2) {
|
||||
return '全职'
|
||||
}
|
||||
if (id === -3) {
|
||||
return '实习生'
|
||||
}
|
||||
const _split = id.split('-')
|
||||
const type = _split[0]
|
||||
const _id = Number(_split[1])
|
||||
|
@@ -161,6 +161,9 @@ export default {
|
||||
}
|
||||
return null
|
||||
},
|
||||
renderI18n(title) {
|
||||
return this.$t(`${title}`)
|
||||
},
|
||||
renderMenuItem(menu) {
|
||||
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
|
||||
const isShowGrant = menu.path.substr(0, 20) === '/cmdb/relationviews/'
|
||||
@@ -183,7 +186,7 @@ export default {
|
||||
<tag {...{ props, attrs }}>
|
||||
{this.renderIcon({ icon: menu.meta.icon, customIcon: menu.meta.customIcon, name: menu.meta.name, typeId: menu.meta.typeId, routeName: menu.name, selectedIcon: menu.meta.selectedIcon, })}
|
||||
<span>
|
||||
<span class={menu.meta.title.length > 10 ? 'scroll' : ''}>{menu.meta.title}</span>
|
||||
<span class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>{this.renderI18n(menu.meta.title)}</span>
|
||||
{isShowDot &&
|
||||
<a-popover
|
||||
overlayClassName="custom-menu-extra-submenu"
|
||||
@@ -217,7 +220,7 @@ export default {
|
||||
<SubMenu {...{ key: menu.path }}>
|
||||
<span slot="title">
|
||||
{this.renderIcon({ icon: menu.meta.icon, selectedIcon: menu.meta.selectedIcon, routeName: menu.name })}
|
||||
<span>{menu.meta.title}</span>
|
||||
<span>{this.renderI18n(menu.meta.title)}</span>
|
||||
</span>
|
||||
{itemArr}
|
||||
</SubMenu>
|
||||
|
@@ -3,9 +3,9 @@
|
||||
<slot></slot>
|
||||
<template #empty>
|
||||
<slot name="empty">
|
||||
<div>
|
||||
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
|
||||
<div>暂无数据</div>
|
||||
<div :style="{ paddingTop: '10px' }">
|
||||
<img :style="{ width: '100px', height: '90px' }" :src="require('@/assets/data_empty.png')" />
|
||||
<div>{{ $t('noData') }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
|
@@ -3,12 +3,12 @@
|
||||
<a-switch
|
||||
class="role-transfer-switch"
|
||||
v-model="isUserRole"
|
||||
checked-children="用户"
|
||||
un-checked-children="虚拟"
|
||||
:checked-children="$t('user')"
|
||||
:un-checked-children="$t('visual')"
|
||||
@change="loadRoles"
|
||||
/>
|
||||
<div class="role-transfer-left">
|
||||
<a-input placeholder="请输入搜索内容" v-model="searchValue" />
|
||||
<a-input :placeholder="$t('placeholderSearch')" v-model="searchValue" />
|
||||
<div v-for="item in filterAllRoles" :key="item.id" @click="handleSelectedLeft(item.id)">
|
||||
<a-checkbox :checked="selectedLeft.includes(item.id)" />
|
||||
<div :title="item.name" class="role-transfer-left-role">{{ item.name }}</div>
|
||||
|
@@ -10,9 +10,10 @@
|
||||
>
|
||||
<a-icon type="setting" />
|
||||
</span>
|
||||
<span class="locale" @click="changeLang">{{ locale === 'zh' ? 'English' : '中文' }}</span>
|
||||
<a-popover
|
||||
trigger="click"
|
||||
:overlayStyle="{ width: '120px' }"
|
||||
:overlayStyle="{ width: '150px' }"
|
||||
placement="bottomRight"
|
||||
overlayClassName="custom-user"
|
||||
>
|
||||
@@ -20,12 +21,12 @@
|
||||
<router-link :to="{ name: 'setting_person' }" :style="{ color: '#000000a6' }">
|
||||
<div class="custom-user-item">
|
||||
<a-icon type="user" :style="{ marginRight: '10px' }" />
|
||||
<span>个人中心</span>
|
||||
<span>{{ $t('topMenu.personalCenter') }}</span>
|
||||
</div>
|
||||
</router-link>
|
||||
<div @click="handleLogout" class="custom-user-item">
|
||||
<a-icon type="logout" :style="{ marginRight: '10px' }" />
|
||||
<span>退出登录</span>
|
||||
<span>{{ $t('topMenu.logout') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<span class="action ant-dropdown-link user-dropdown-menu">
|
||||
@@ -44,8 +45,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapActions, mapGetters, mapMutations } from 'vuex'
|
||||
import DocumentLink from './DocumentLink.vue'
|
||||
import { mapState, mapActions, mapGetters } from 'vuex'
|
||||
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
|
||||
|
||||
export default {
|
||||
name: 'UserMenu',
|
||||
@@ -53,21 +55,24 @@ export default {
|
||||
DocumentLink,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
...mapState(['user', 'locale']),
|
||||
hasBackendPermission() {
|
||||
return this.user?.roles?.permissions.includes('acl_admin', 'backend_admin') || false
|
||||
return this.user?.detailPermissions?.backend?.length
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['Logout']),
|
||||
...mapGetters(['nickname', 'avatar']),
|
||||
...mapMutations(['SET_LOCALE']),
|
||||
handleLogout() {
|
||||
const that = this
|
||||
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确认注销登录 ?',
|
||||
content: '真的要注销登录吗 ?',
|
||||
onOk() {
|
||||
// localStorage.removeItem('ops_cityps_currentId')
|
||||
localStorage.clear()
|
||||
return that.Logout()
|
||||
},
|
||||
onCancel() {},
|
||||
@@ -76,9 +81,22 @@ export default {
|
||||
handleClick() {
|
||||
this.$router.push('/setting')
|
||||
},
|
||||
changeLang() {
|
||||
if (this.locale === 'zh') {
|
||||
this.SET_LOCALE('en')
|
||||
this.$i18n.locale = 'en'
|
||||
} else {
|
||||
this.SET_LOCALE('zh')
|
||||
this.$i18n.locale = 'zh'
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
setDocumentTitle(`${this.$t(this.$route.meta.title)} - ${domTitle}`)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '~@/style/static.less';
|
||||
.color {
|
||||
@@ -98,4 +116,11 @@ export default {
|
||||
color: #000000a6;
|
||||
}
|
||||
}
|
||||
|
||||
.locale {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #custom_colors[color_1];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user