feat(ui): update ci type choice config

This commit is contained in:
songlh 2024-08-26 15:05:11 +08:00
parent 6e02f6a21f
commit 631871a8cf
37 changed files with 1948 additions and 120 deletions

View File

@ -54,6 +54,12 @@
<div class="content unicode" style="display: block;"> <div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe998;</span>
<div class="name">veops-department</div>
<div class="code-name">&amp;#xe998;</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe997;</span> <span class="icon iconfont">&#xe997;</span>
<div class="name">duose-changwenben (1)</div> <div class="name">duose-changwenben (1)</div>
@ -5514,9 +5520,9 @@
<pre><code class="language-css" <pre><code class="language-css"
>@font-face { >@font-face {
font-family: 'iconfont'; font-family: 'iconfont';
src: url('iconfont.woff2?t=1724135954264') format('woff2'), src: url('iconfont.woff2?t=1724653006782') format('woff2'),
url('iconfont.woff?t=1724135954264') format('woff'), url('iconfont.woff?t=1724653006782') format('woff'),
url('iconfont.ttf?t=1724135954264') format('truetype'); url('iconfont.ttf?t=1724653006782') format('truetype');
} }
</code></pre> </code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -5542,6 +5548,15 @@
<div class="content font-class"> <div class="content font-class">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont veops-department"></span>
<div class="name">
veops-department
</div>
<div class="code-name">.veops-department
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont duose-changwenben1"></span> <span class="icon iconfont duose-changwenben1"></span>
<div class="name"> <div class="name">
@ -13732,6 +13747,14 @@
<div class="content symbol"> <div class="content symbol">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-department"></use>
</svg>
<div class="name">veops-department</div>
<div class="code-name">#veops-department</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#duose-changwenben1"></use> <use xlink:href="#duose-changwenben1"></use>

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3857903 */ font-family: "iconfont"; /* Project id 3857903 */
src: url('iconfont.woff2?t=1724135954264') format('woff2'), src: url('iconfont.woff2?t=1724653006782') format('woff2'),
url('iconfont.woff?t=1724135954264') format('woff'), url('iconfont.woff?t=1724653006782') format('woff'),
url('iconfont.ttf?t=1724135954264') format('truetype'); url('iconfont.ttf?t=1724653006782') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.veops-department:before {
content: "\e998";
}
.duose-changwenben1:before { .duose-changwenben1:before {
content: "\e997"; content: "\e997";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "", "css_prefix_text": "",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "41570722",
"name": "veops-department",
"font_class": "veops-department",
"unicode": "e998",
"unicode_decimal": 59800
},
{ {
"icon_id": "41437322", "icon_id": "41437322",
"name": "duose-changwenben (1)", "name": "duose-changwenben (1)",

Binary file not shown.

View File

@ -107,7 +107,9 @@
:type="choice[1].icon.name" :type="choice[1].icon.name"
/> />
</template> </template>
<span>{{ choice[0] }}</span> <a-tooltip placement="topLeft" :title="choice[1].label || choice[0]">
<span>{{ choice[1].label || choice[0] }}</span>
</a-tooltip>
</span> </span>
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -138,7 +140,7 @@
" "
target="_blank" target="_blank"
> >
{{ item }} {{ getChoiceValueLabel(col, item) || item }}
</a> </a>
</template> </template>
<PasswordField <PasswordField
@ -163,7 +165,7 @@
:style="{ color: getChoiceValueIcon(col, value).color, marginRight: '5px' }" :style="{ color: getChoiceValueIcon(col, value).color, marginRight: '5px' }"
:type="getChoiceValueIcon(col, value).name" :type="getChoiceValueIcon(col, value).name"
/> />
{{ value }} {{ getChoiceValueLabel(col, value) || value }}
</span> </span>
</template> </template>
</template> </template>
@ -397,6 +399,14 @@ export default {
return {} return {}
}, },
getChoiceValueLabel(col, colValue) {
const _find = col?.filters?.find((item) => String(item[0]) === String(colValue))
if (_find) {
return _find[1]?.label || ''
}
return ''
},
/** /**
* 开启当前 ci 详情弹窗 * 开启当前 ci 详情弹窗
*/ */

View File

@ -105,7 +105,7 @@ export default {
return { method, url, parameters, body, headers, authorization } return { method, url, parameters, body, headers, authorization }
}, },
setParams(params) { setParams(params) {
const { method, url, parameters, body, headers, authorization = {} } = params ?? {} const { method = 'GET', url = '', parameters = {}, body = '', headers = {}, authorization = {} } = params ?? {}
this.method = method this.method = method
this.url = url this.url = url
this.$refs.Parameters.parameters = this.$refs.Parameters.parameters =

View File

@ -117,7 +117,7 @@ const cmdb_en = {
advancedSettings: 'Advanced Settings', advancedSettings: 'Advanced Settings',
font: 'Font', font: 'Font',
color: 'Color', color: 'Color',
choiceValue: 'Predefined value', choiceValue: 'Select List',
computedAttribute: 'Computed Attribute', computedAttribute: 'Computed Attribute',
computedAttributeTips: 'The value of this attribute is calculated through an expression constructed from other attributes of the CIType or by executing a piece of code. The reference method of the attribute is: {{ attribute name }}', computedAttributeTips: 'The value of this attribute is calculated through an expression constructed from other attributes of the CIType or by executing a piece of code. The reference method of the attribute is: {{ attribute name }}',
addAttribute: 'New attribute', addAttribute: 'New attribute',
@ -293,7 +293,19 @@ const cmdb_en = {
cascadeAttrTip: 'Cascading attributes note the order', cascadeAttrTip: 'Cascading attributes note the order',
enumValue: 'Value', enumValue: 'Value',
label: 'Label', label: 'Label',
valueInputTip: 'Please input value' valueInputTip: 'Please input value',
enumValueTip2: 'Enumeration values cannot be repeated',
builtin: 'Built In',
department: 'Department',
user: 'User',
userGroup: 'User Group',
departmentTip: 'Scroll down to select all departments in the company structure for common settings',
userGroupSelectTip: 'Please select user group',
displayValue: 'Display Value',
displayValueSelectTip: 'Please select Display Value',
departmentCascadeDisplay: 'Cascade Display',
filterUsers: 'Filter Users',
enum: 'Enum',
}, },
components: { components: {
unselectAttributes: 'Unselected', unselectAttributes: 'Unselected',

View File

@ -117,7 +117,7 @@ const cmdb_zh = {
advancedSettings: '高级设置', advancedSettings: '高级设置',
font: '字体', font: '字体',
color: '颜色', color: '颜色',
choiceValue: '预定义值', choiceValue: '下拉列表',
computedAttribute: '计算属性', computedAttribute: '计算属性',
computedAttributeTips: '该属性的值是通过模型的其它属性构建的表达式或者执行一段代码的方式计算而来,属性的引用方法为: {{ 属性名 }}', computedAttributeTips: '该属性的值是通过模型的其它属性构建的表达式或者执行一段代码的方式计算而来,属性的引用方法为: {{ 属性名 }}',
addAttribute: '新增属性', addAttribute: '新增属性',
@ -293,7 +293,19 @@ const cmdb_zh = {
cascadeAttrTip: '级联属性注意顺序', cascadeAttrTip: '级联属性注意顺序',
enumValue: '枚举值', enumValue: '枚举值',
label: '标签', label: '标签',
valueInputTip: '请输入枚举值' valueInputTip: '请输入枚举值',
enumValueTip2: '枚举值不能重复',
builtin: '内置',
department: '部门',
user: '用户',
userGroup: '用户组',
departmentTip: '下拉选择为通用设置公司架构里的所有部门',
userGroupSelectTip: '请选择用户组',
displayValue: '展示值',
displayValueSelectTip: '请选择展示值',
departmentCascadeDisplay: '部门级联显示',
filterUsers: '筛选用户',
enum: '枚举',
}, },
components: { components: {
unselectAttributes: '未选属性', unselectAttributes: '未选属性',

View File

@ -91,6 +91,7 @@ export function getCITableColumns(data, attrList, width = 1600, height) {
value_type: attr.value_type, value_type: attr.value_type,
sortable: !!attr.is_sortable, sortable: !!attr.is_sortable,
filters: attr.is_choice ? attr.choice_value : null, filters: attr.is_choice ? attr.choice_value : null,
choice_builtin: null,
width: Math.min(Math.max(100, ...data.map(item => strLength(item[attr.name]))), 350), width: Math.min(Math.max(100, ...data.map(item => strLength(item[attr.name]))), 350),
is_link: attr.is_link, is_link: attr.is_link,
is_password: attr.is_password, is_password: attr.is_password,

View File

@ -128,7 +128,9 @@
v-if="choice[1] && choice[1].icon && choice[1].icon.name" v-if="choice[1] && choice[1].icon && choice[1].icon.name"
:type="choice[1].icon.name" :type="choice[1].icon.name"
/> />
{{ choice[0] }} <a-tooltip placement="topLeft" :title="choice[1].label || choice[0]" >
{{ choice[1].label || choice[0] }}
</a-tooltip>
</span> </span>
</a-select-option> </a-select-option>
</a-select> </a-select>

View File

@ -42,7 +42,7 @@
:style="{ color: getChoiceValueIcon(attr, value).color, marginRight: '5px' }" :style="{ color: getChoiceValueIcon(attr, value).color, marginRight: '5px' }"
:type="getChoiceValueIcon(attr, value).name" :type="getChoiceValueIcon(attr, value).name"
/> />
{{ value }}</span {{ getChoiceValueLabel(attr, value) || value }}</span
> >
</template> </template>
<span <span
@ -66,7 +66,7 @@
:style="{ color: getChoiceValueIcon(attr, ci[attr.name]).color, marginRight: '5px' }" :style="{ color: getChoiceValueIcon(attr, ci[attr.name]).color, marginRight: '5px' }"
:type="getChoiceValueIcon(attr, ci[attr.name]).name" :type="getChoiceValueIcon(attr, ci[attr.name]).name"
/> />
{{ ci[attr.name] }} {{ getChoiceValueLabel(attr, ci[attr.name]) || ci[attr.name] }}
</span> </span>
</template> </template>
<template v-else-if="attr.is_list"> <template v-else-if="attr.is_list">
@ -134,7 +134,7 @@
:type="choice[1].icon.name" :type="choice[1].icon.name"
/> />
</template> </template>
{{ choice[0] }} {{ choice[1].label || choice[0] }}
</span> </span>
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -318,6 +318,15 @@ export default {
} }
return {} return {}
}, },
getChoiceValueLabel(col, colValue) {
const _find = col.choice_value.find((item) => String(item[0]) === String(colValue))
if (_find) {
return _find[1]?.label || ''
}
return ''
},
getName(name) { getName(name) {
return name ?? '' return name ?? ''
}, },

View File

@ -233,6 +233,7 @@ export default {
this.firstCIJsonAttr[item._type].forEach((attr) => { this.firstCIJsonAttr[item._type].forEach((attr) => {
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : '' item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
}) })
this.formatCI(item, this.firstCIColumns)
if (item.ci_type in firstCIs) { if (item.ci_type in firstCIs) {
firstCIs[item.ci_type].push(item) firstCIs[item.ci_type].push(item)
} else { } else {
@ -251,6 +252,7 @@ export default {
this.secondCIJsonAttr[item._type].forEach((attr) => { this.secondCIJsonAttr[item._type].forEach((attr) => {
item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : '' item[`${attr}`] = item[`${attr}`] ? JSON.stringify(item[`${attr}`]) : ''
}) })
this.formatCI(item, this.secondCIColumns)
if (item.ci_type in secondCIs) { if (item.ci_type in secondCIs) {
secondCIs[item.ci_type].push(item) secondCIs[item.ci_type].push(item)
} else { } else {
@ -261,6 +263,26 @@ export default {
}) })
.catch((e) => {}) .catch((e) => {})
}, },
formatCI(ci, columns) {
Object.keys(ci).forEach((key) => {
const attr = columns?.[ci?._type]?.find((item) => item?.params?.attr?.name === key)?.params?.attr
if (attr?.is_choice && attr?.choice_value?.length) {
if (attr?.is_list) {
ci[key] = ci[key].map((value) => {
const label = attr?.choice_value?.find((choice) => choice?.[0] === value)?.[1]?.label
return label || ci[key]
})
} else {
const label = attr?.choice_value?.find((choice) => choice?.[0] === ci[key])?.[1]?.label
ci[key] = label || ci[key]
}
}
})
return ci
},
async getParentCITypes() { async getParentCITypes() {
const res = await getCITypeParent(this.typeId) const res = await getCITypeParent(this.typeId)
this.parentCITypes = res.parents this.parentCITypes = res.parents

View File

@ -79,7 +79,9 @@
:type="choice[1].icon.name" :type="choice[1].icon.name"
/> />
</template> </template>
{{ choice[0] }} <a-tooltip placement="topLeft" :title="choice[1].label || choice[0]">
{{ choice[1].label || choice[0] }}
</a-tooltip>
</span> </span>
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -90,7 +92,7 @@
attr.name, attr.name,
{ {
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }], rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : '', initialValue: attr.default && attr.default.default ? Array.isArray(attr.default.default) ? attr.default.default.join(',') : attr.default.default : '',
}, },
]" ]"
> >

View File

@ -49,7 +49,18 @@ export default {
tableData: [], tableData: [],
} }
}, },
inject: ['providerGroupsData'], inject: {
providerGroupsData: {
default: () => {
return () => {
return {
CITypeGroups: [],
otherGroupAttributes: [],
}
}
}
}
},
computed: { computed: {
...mapState({ ...mapState({
windowHeight: (state) => state.windowHeight, windowHeight: (state) => state.windowHeight,

View File

@ -74,7 +74,8 @@
!property.is_password && !property.is_password &&
!property.is_list && !property.is_list &&
!property.is_reference && !property.is_reference &&
!property.is_bool !property.is_bool &&
!(Array.isArray(property.choice_value) ? property.choice_value.length > 0 : false)
" "
:title="$t(isShowId ? 'cmdb.ciType.cancelSetAsShow' : 'cmdb.ciType.setAsShow')" :title="$t(isShowId ? 'cmdb.ciType.cancelSetAsShow' : 'cmdb.ciType.setAsShow')"
> >

View File

@ -73,7 +73,7 @@
> >
<a-form-item <a-form-item
:label-col="{ span: currentValueType === '6' ? 4 : 8 }" :label-col="{ span: currentValueType === '6' ? 4 : 8 }"
:wrapper-col="{ span: currentValueType === '6' ? 18 : 12 }" :wrapper-col="{ span: currentValueType === '6' ? 18 : 15 }"
:label="$t('cmdb.ciType.defaultValue')" :label="$t('cmdb.ciType.defaultValue')"
> >
<template> <template>
@ -367,7 +367,10 @@
ref="preValueArea" ref="preValueArea"
:disabled="isShowComputedArea" :disabled="isShowComputedArea"
:CITypeId="CITypeId" :CITypeId="CITypeId"
:enumValueType="enumValueType"
/> />
<a-button type="primary" size="small" ghost @click="resetPreValue" >{{ $t('reset') }}</a-button>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24" v-if="!['6', '7', '10', '11'].includes(currentValueType)"> <a-col :span="24" v-if="!['6', '7', '10', '11'].includes(currentValueType)">
@ -442,6 +445,7 @@ import PreValueArea from './preValueArea.vue'
import FontArea from './fontArea.vue' import FontArea from './fontArea.vue'
import RegSelect from '@/components/RegexSelect' import RegSelect from '@/components/RegexSelect'
import ReferenceModelSelect from './attributeEdit/referenceModelSelect.vue' import ReferenceModelSelect from './attributeEdit/referenceModelSelect.vue'
import { ENUM_VALUE_TYPE } from './preValueAttr/constants.js'
export default { export default {
name: 'AttributeEditForm', name: 'AttributeEditForm',
@ -474,6 +478,7 @@ export default {
defaultForDatetime: '', defaultForDatetime: '',
re_check: {}, re_check: {},
enumValueType: ENUM_VALUE_TYPE.INPUT
} }
}, },
@ -686,6 +691,22 @@ export default {
} }
const _find = attributes.find((item) => item.id === _record.id) const _find = attributes.find((item) => item.id === _record.id)
if (!['6', '7', '10', '11'].includes(_record.value_type)) { if (!['6', '7', '10', '11'].includes(_record.value_type)) {
switch (_record.value_type) {
case '0':
case '1':
this.enumValueType = ENUM_VALUE_TYPE.NUMBER
break
case '3':
this.enumValueType = ENUM_VALUE_TYPE.DATE_TIME
break
case '4':
this.enumValueType = ENUM_VALUE_TYPE.DATE
break
default:
this.enumValueType = ENUM_VALUE_TYPE.INPUT
break
}
this.$refs.preValueArea.setData({ this.$refs.preValueArea.setData({
choice_value: (_find || {}).choice_value || [], choice_value: (_find || {}).choice_value || [],
choice_web_hook: _record.choice_web_hook, choice_web_hook: _record.choice_web_hook,
@ -715,8 +736,6 @@ export default {
}) })
} }
delete values['default_show']
delete values['is_required']
const { default_value } = values const { default_value } = values
if (values.value_type === '10') { if (values.value_type === '10') {
values.default = { default: values.is_list ? default_value : Boolean(default_value) } values.default = { default: values.is_list ? default_value : Boolean(default_value) }
@ -748,7 +767,6 @@ export default {
values.default = { default: null } values.default = { default: null }
} }
delete values.default_value
if (values.is_computed) { if (values.is_computed) {
const computedAreaData = this.$refs.computedArea.getData() const computedAreaData = this.$refs.computedArea.getData()
values = { ...values, ...computedAreaData } values = { ...values, ...computedAreaData }
@ -756,9 +774,18 @@ export default {
// If it is a non-computed attribute, check to see if there is a predefined value // If it is a non-computed attribute, check to see if there is a predefined value
if (!['6', '7', '10', '11'].includes(values.value_type)) { if (!['6', '7', '10', '11'].includes(values.value_type)) {
const preValueAreaData = this.$refs.preValueArea.getData() const preValueAreaData = this.$refs.preValueArea.getData()
// 预定义值校验错误
if (preValueAreaData?.isError) {
return
}
values = { ...values, ...preValueAreaData } values = { ...values, ...preValueAreaData }
} }
} }
delete values['default_show']
delete values['is_required']
delete values.default_value
const fontOptions = this.$refs.fontArea.getData() const fontOptions = this.$refs.fontArea.getData()
if (!['6', '10', '11'].includes(values.value_type)) { if (!['6', '10', '11'].includes(values.value_type)) {
@ -831,10 +858,19 @@ export default {
}, },
changeDefaultForDatetime(value) { changeDefaultForDatetime(value) {
this.defaultForDatetime = value this.defaultForDatetime = value
if (value === '$custom_time') { switch (value) {
this.form.setFieldsValue({ case '$custom_time':
default_value: undefined, this.form.setFieldsValue({
}) default_value: undefined,
})
break
case '$updated_at':
this.form.setFieldsValue({
is_dynamic: true,
})
break
default:
break
} }
}, },
onClick({ key }) { onClick({ key }) {
@ -862,6 +898,12 @@ export default {
} }
return [] return []
}, },
resetPreValue() {
if (this.$refs.preValueArea) {
this.$refs.preValueArea.resetData()
}
}
}, },
watch: {}, watch: {},
} }

View File

@ -359,7 +359,10 @@
:canDefineScript="canDefineScript" :canDefineScript="canDefineScript"
:disabled="isShowComputedArea" :disabled="isShowComputedArea"
:CITypeId="CITypeId" :CITypeId="CITypeId"
:enumValueType="enumValueType"
/> />
<a-button type="primary" size="small" ghost @click="resetPreValue" >{{ $t('reset') }}</a-button>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24" v-if="!['6', '7', '10', '11'].includes(currentValueType)"> <a-col :span="24" v-if="!['6', '7', '10', '11'].includes(currentValueType)">
@ -416,6 +419,7 @@ import FontArea from './fontArea.vue'
import RegSelect from '@/components/RegexSelect' import RegSelect from '@/components/RegexSelect'
import { getPropertyIcon } from '../../utils/helper' import { getPropertyIcon } from '../../utils/helper'
import ReferenceModelSelect from './attributeEdit/referenceModelSelect.vue' import ReferenceModelSelect from './attributeEdit/referenceModelSelect.vue'
import { ENUM_VALUE_TYPE } from './preValueAttr/constants.js'
export default { export default {
name: 'CreateNewAttribute', name: 'CreateNewAttribute',
@ -458,6 +462,7 @@ export default {
defaultForDatetime: '', defaultForDatetime: '',
re_check: {}, re_check: {},
enumValueType: ENUM_VALUE_TYPE.INPUT
} }
}, },
computed: { computed: {
@ -485,8 +490,7 @@ export default {
console.log(values) console.log(values)
const { is_required, default_show, default_value, is_dynamic } = values const { is_required, default_show, default_value, is_dynamic } = values
const data = { is_required, default_show, is_dynamic } const data = { is_required, default_show, is_dynamic }
delete values.is_required
delete values.default_show
if (values.value_type === '10') { if (values.value_type === '10') {
values.default = { default: values.is_list ? (default_value || null) : Boolean(default_value) } values.default = { default: values.is_list ? (default_value || null) : Boolean(default_value) }
} else if (values.value_type === '0' && default_value) { } else if (values.value_type === '0' && default_value) {
@ -516,7 +520,7 @@ export default {
} else { } else {
values.default = { default: null } values.default = { default: null }
} }
delete values.default_value
if (values.is_computed) { if (values.is_computed) {
const computedAreaData = this.$refs.computedArea.getData() const computedAreaData = this.$refs.computedArea.getData()
values = { ...values, ...computedAreaData } values = { ...values, ...computedAreaData }
@ -524,9 +528,18 @@ export default {
// If it is a non-computed attribute, check to see if there is a predefined value // If it is a non-computed attribute, check to see if there is a predefined value
if (!['6', '7', '10', '11'].includes(values.value_type)) { if (!['6', '7', '10', '11'].includes(values.value_type)) {
const preValueAreaData = this.$refs.preValueArea.getData() const preValueAreaData = this.$refs.preValueArea.getData()
// 预定义值校验错误
if (preValueAreaData?.isError) {
return
}
values = { ...values, ...preValueAreaData } values = { ...values, ...preValueAreaData }
} }
} }
delete values.is_required
delete values.default_show
delete values.default_value
const fontOptions = this.$refs.fontArea.getData() const fontOptions = this.$refs.fontArea.getData()
// 索引 // 索引
@ -587,6 +600,28 @@ export default {
if (['6', '10', '11'].includes(value)) { if (['6', '10', '11'].includes(value)) {
this.re_check = {} this.re_check = {}
} }
switch (value) {
case '0':
case '1':
this.enumValueType = ENUM_VALUE_TYPE.NUMBER
break
case '3':
this.enumValueType = ENUM_VALUE_TYPE.DATE_TIME
break
case '4':
this.enumValueType = ENUM_VALUE_TYPE.DATE
break
default:
this.enumValueType = ENUM_VALUE_TYPE.INPUT
break
}
if (['0', '1', '3', '4'].includes(value)) {
if (this.$refs.preValueArea) {
this.$refs.preValueArea.initEnumValue()
}
}
this.handleSwitchType({ valueType: value }) this.handleSwitchType({ valueType: value })
}) })
}, },
@ -660,10 +695,19 @@ export default {
}, },
changeDefaultForDatetime(value) { changeDefaultForDatetime(value) {
this.defaultForDatetime = value this.defaultForDatetime = value
if (value === '$custom_time') { switch (value) {
this.form.setFieldsValue({ case '$custom_time':
default_value: undefined, this.form.setFieldsValue({
}) default_value: undefined,
})
break
case '$updated_at':
this.form.setFieldsValue({
is_dynamic: true,
})
break
default:
break
} }
}, },
onClick({ key }) { onClick({ key }) {
@ -688,6 +732,12 @@ export default {
} }
return [] return []
}, },
resetPreValue() {
if (this.$refs.preValueArea) {
this.$refs.preValueArea.resetData()
}
}
}, },
} }
</script> </script>

View File

@ -545,7 +545,7 @@ export default {
}, },
showIdSelectOptions() { showIdSelectOptions() {
const _showIdSelectOptions = this.currentTypeAttrs.filter( const _showIdSelectOptions = this.currentTypeAttrs.filter(
(item) => item.id !== this.unique_id && !['6'].includes(item.value_type) && !item.is_password && !item.is_list && !item.is_bool && !item.is_reference (item) => item.id !== this.unique_id && !['6'].includes(item.value_type) && !item.is_password && !item.is_list && !item.is_bool && !item.is_reference && !item?.choice_value?.length
) )
if (this.showIdFilterInput) { if (this.showIdFilterInput) {
return _showIdSelectOptions.filter( return _showIdSelectOptions.filter(

View File

@ -7,33 +7,22 @@
:tabBarStyle="{ borderBottom: 'none' }" :tabBarStyle="{ borderBottom: 'none' }"
> >
<a-tab-pane key="define" :disabled="disabled"> <a-tab-pane key="define" :disabled="disabled">
<span style="font-size:12px;" slot="tab">{{ $t('define') }}</span> <span style="font-size:14px;" slot="tab">{{ $t('cmdb.ciType.enum') }}</span>
<PreValueTag type="add" :item="[]" @add="addNewValue" :disabled="disabled"> <PreValueDefine
<template #default> v-model="valueList"
<a-button :disabled="disabled"
:style="{ marginBottom: '10px', fontSize: '12px', padding: '1px 7px' }" :enumValueType="enumValueType"
type="primary" />
ghost </a-tab-pane>
:disabled="disabled" <a-tab-pane key="builtin" :disabled="disabled">
size="small" <div class="tab-builtin" slot="tab">
> <span class="tab-builtin-title">{{ $t('cmdb.ciType.builtin') }}</span>
<a-icon type="plus" />{{ $t('add') }}</a-button <span v-if="isOpenSource" class="tab-builtin-tag">Pro</span>
> </div>
</template> <PreValueBuiltIn ref="builtInRef" />
</PreValueTag>
<draggable :list="valueList" handle=".handle" :disabled="disabled">
<PreValueTag
:disabled="disabled"
v-for="(item, index) in valueList"
:key="`${item[0]}_${index}`"
:item="item"
@deleteValue="deleteValue"
@editValue="editValue"
/>
</draggable>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="webhook" :disabled="disabled"> <a-tab-pane key="webhook" :disabled="disabled">
<span style="font-size:12px;" slot="tab">Webhook</span> <span style="font-size:14px;" slot="tab">Webhook</span>
<Webhook ref="webhook" style="margin-top:10px" /> <Webhook ref="webhook" style="margin-top:10px" />
<a-form-model :model="form"> <a-form-model :model="form">
<a-col :span="24"> <a-col :span="24">
@ -59,7 +48,7 @@
</a-form-model> </a-form-model>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="choice_other" :disabled="disabled"> <a-tab-pane key="choice_other" :disabled="disabled">
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.choiceOther') }}</span> <span style="font-size:14px;" slot="tab">{{ $t('cmdb.ciType.choiceOther') }}</span>
<a-row :gutter="[24, 24]"> <a-row :gutter="[24, 24]">
<a-col :span="24"> <a-col :span="24">
<a-form-item <a-form-item
@ -179,11 +168,11 @@
</a-row> </a-row>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="script" :disabled="disabled || !canDefineScript"> <a-tab-pane key="script" :disabled="disabled || !canDefineScript">
<span style="font-size:12px;" slot="tab">{{ $t('cmdb.ciType.code') }}</span> <span style="font-size:14px;" slot="tab">{{ $t('cmdb.ciType.code') }}</span>
<a-form-item <a-form-item
:style="{ marginBottom: '5px' }" :style="{ marginBottom: '5px' }"
:label="$t('cmdb.ciType.cascadeAttr')" :label="$t('cmdb.ciType.cascadeAttr')"
:label-col="{ span: 3 }" :label-col="{ span: $i18n.locale === 'en' ? 3 : 2 }"
:wrapper-col="{ span: 19 }" :wrapper-col="{ span: 19 }"
:extra="scriptCodeExtraText" :extra="scriptCodeExtraText"
labelAlign="left" labelAlign="left"
@ -191,7 +180,7 @@
<a-select <a-select
mode="multiple" mode="multiple"
style="width: 100%" style="width: 100%"
placeholder="Please select" :placeholder="$t('placeholder2')"
optionFilterProp="title" optionFilterProp="title"
v-model="cascade_attributes" v-model="cascade_attributes"
> >
@ -211,9 +200,11 @@
<div>2. {{ $t('cmdb.ciType.computedAttrTip2') }}</div> <div>2. {{ $t('cmdb.ciType.computedAttrTip2') }}</div>
</div> </div>
<a-button size="small" @click="showAllPropDrawer"> <div class="all-attr-btn">
{{ $t('cmdb.ciType.viewAllAttr') }} <a-button size="small" @click="showAllPropDrawer">
</a-button> {{ $t('cmdb.ciType.viewAllAttr') }}
</a-button>
</div>
<AllAttrDrawer ref="allAttrDrawer" /> <AllAttrDrawer ref="allAttrDrawer" />
<CustomCodeMirror <CustomCodeMirror
@ -237,6 +228,9 @@ import { getCITypeGroups } from '../../api/ciTypeGroup'
import { getCITypeCommonAttributesByTypeIds, getCITypeAttributesById } from '../../api/CITypeAttr' import { getCITypeCommonAttributesByTypeIds, getCITypeAttributesById } from '../../api/CITypeAttr'
import AttrFilter from './preValueAttr/attrFilter/index.vue' import AttrFilter from './preValueAttr/attrFilter/index.vue'
import AllAttrDrawer from './allAttrDrawer.vue' import AllAttrDrawer from './allAttrDrawer.vue'
import PreValueDefine from './preValueAttr/define/index.vue'
import PreValueBuiltIn from './preValueAttr/builtin/index.vue'
import { ENUM_VALUE_TYPE } from './preValueAttr/constants.js'
import CustomCodeMirror from '@/components/CustomCodeMirror' import CustomCodeMirror from '@/components/CustomCodeMirror'
import 'codemirror/lib/codemirror.css' import 'codemirror/lib/codemirror.css'
@ -245,7 +239,7 @@ require('codemirror/mode/python/python.js')
export default { export default {
name: 'PreValueArea', name: 'PreValueArea',
components: { draggable, PreValueTag, ColorPicker, Webhook, AttrFilter, CustomCodeMirror, AllAttrDrawer }, components: { draggable, PreValueTag, ColorPicker, Webhook, AttrFilter, CustomCodeMirror, AllAttrDrawer, PreValueDefine, PreValueBuiltIn },
props: { props: {
disabled: { disabled: {
type: Boolean, type: Boolean,
@ -259,12 +253,26 @@ export default {
type: Number, type: Number,
default: null, default: null,
}, },
// eslint-disable-next-line vue/require-default-prop
enumValueType: {
type: String,
defualt: ENUM_VALUE_TYPE.INPUT
}
}, },
data() { data() {
return { return {
defautValueColor, defautValueColor,
activeKey: 'define', // define webhook activeKey: 'define', // define webhook
valueList: [], valueList: [
[
'',
{
style: {},
icon: {},
label: ''
}
]
],
form: { form: {
ret_key: '', ret_key: '',
}, },
@ -331,6 +339,10 @@ export default {
}, },
methods: { methods: {
async getCITypeAttributesById() { async getCITypeAttributesById() {
if (!this.CITypeId) {
this.curModelAttrList = []
return
}
const res = await getCITypeAttributesById(this.CITypeId) const res = await getCITypeAttributesById(this.CITypeId)
let curModelAttrList = [] let curModelAttrList = []
if (res?.attributes?.length) { if (res?.attributes?.length) {
@ -339,47 +351,36 @@ export default {
this.curModelAttrList = curModelAttrList this.curModelAttrList = curModelAttrList
}, },
addNewValue(newValue, newStyle, newIcon) {
if (newValue) {
const idx = this.valueList.findIndex((v) => v[0] === newValue)
if (idx > -1) {
this.$message.warning(this.$t('cmdb.ciType.valueExisted'))
} else {
this.valueList.push([newValue, { style: newStyle, icon: { ...newIcon } }])
}
}
},
deleteValue(item) {
const _valueList = _.cloneDeep(this.valueList)
const idx = _valueList.findIndex((v) => v[0] === item[0])
if (idx > -1) {
_valueList.splice(idx, 1)
this.valueList = _valueList
}
},
editValue(item, newValue, newStyle, newIcon) {
const _valueList = _.cloneDeep(this.valueList)
const idx = _valueList.findIndex((v) => v[0] === item[0])
if (idx > -1) {
_valueList[idx] = [newValue, { style: newStyle, icon: { ...newIcon } }]
this.valueList = _valueList
}
},
getData() { getData() {
if (this.activeKey === 'define') { if (this.activeKey === 'builtin') {
return { return {
choice_value: this.valueList, choice_value: this.valueList,
choice_web_hook: null, choice_web_hook: null,
choice_other: null, choice_other: null,
choice_builtin: null,
}
} else if (this.activeKey === 'define') {
if (this.validateDefine()) {
return {
isError: true
}
}
return {
choice_value: this.valueList.filter((item) => item?.[0]),
choice_web_hook: null,
choice_other: null,
choice_builtin: null
} }
} else if (this.activeKey === 'webhook') { } else if (this.activeKey === 'webhook') {
const choice_web_hook = this.$refs.webhook.getParams() const choice_web_hook = this.$refs.webhook.getParams()
choice_web_hook.ret_key = this.form.ret_key choice_web_hook.ret_key = this.form.ret_key
return { choice_value: [], choice_web_hook, choice_other: null } return { choice_value: [], choice_web_hook, choice_other: null, choice_builtin: null }
} else if (this.activeKey === 'script') { } else if (this.activeKey === 'script') {
return { return {
choice_value: [], choice_value: [],
choice_web_hook: null, choice_web_hook: null,
choice_builtin: null,
choice_other: { choice_other: {
script: this.script, script: this.script,
cascade_attributes: this.cascade_attributes, cascade_attributes: this.cascade_attributes,
@ -395,9 +396,22 @@ export default {
choice_value: [], choice_value: [],
choice_web_hook: null, choice_web_hook: null,
choice_other, choice_other,
choice_builtin: null,
} }
} }
}, },
validateDefine() {
const valueList = this.valueList.filter((item) => item?.[0])
const isRepeat = _.uniq(valueList.map(item => item?.[0] || '')).length !== valueList.length
if (isRepeat) {
this.$message.warning(this.$t('cmdb.ciType.enumValueTip2'))
return true
}
return false
},
setData({ choice_value, choice_web_hook, choice_other }) { setData({ choice_value, choice_web_hook, choice_other }) {
if (choice_web_hook) { if (choice_web_hook) {
this.activeKey = 'webhook' this.activeKey = 'webhook'
@ -425,7 +439,26 @@ export default {
} }
} }
} else { } else {
this.valueList = choice_value let valueList = [[
'',
{
style: {},
icon: {},
label: ''
}
]]
if (choice_value?.length) {
valueList = choice_value.map((item) => {
return [
item[0],
{
...item[1],
label: item?.[1]?.['label'] || item[0]
}
]
})
}
this.valueList = valueList
this.activeKey = 'define' this.activeKey = 'define'
} }
const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar') const dom = document.querySelector('#preValueArea .ant-tabs-ink-bar')
@ -436,6 +469,56 @@ export default {
dom.style.backgroundColor = '#2f54eb' dom.style.backgroundColor = '#2f54eb'
} }
}, },
resetData() {
this.activeKey = 'define'
this.$set(this, 'valueList', [
[
'',
{
style: {},
icon: {},
label: ''
}
]
])
this.$nextTick(() => {
if (this.$refs.builtInRef) {
this.$refs.builtInRef.setData({})
}
if (this.$refs.webhook) {
this.$refs.webhook.setParams({})
}
this.form.ret_key = ''
this.script = ''
this.cascade_attributes = []
if (this.$refs.codemirror) {
this.$refs.codemirror.initCodeMirror('')
}
this.choice_other = {
type_ids: undefined,
attr_id: undefined
}
if (this.$refs.attrFilter) {
this.$refs.attrFilter.init(true, false)
}
})
},
initEnumValue() {
if (this.valueList) {
const valueList = _.cloneDeep(this.valueList)
valueList.forEach((item) => {
item[0] = ''
})
this.$set(this, 'valueList', valueList)
}
},
setExpFromFilter(filterExp) { setExpFromFilter(filterExp) {
if (filterExp) { if (filterExp) {
this.filterExp = `${filterExp}` this.filterExp = `${filterExp}`
@ -462,6 +545,24 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.tab-builtin {
display: flex;
align-items: center;
&-title {
font-size: 14px;
}
&-tag {
background-color: #E1EFFF;
color: #2F54EB;
font-size: 10px;
font-weight: 400;
padding: 0 3px;
margin-left: 3px;
}
}
.pre-value-edit-color { .pre-value-edit-color {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -481,6 +582,12 @@ export default {
line-height: 22px; line-height: 22px;
color: #a5a9bc; color: #a5a9bc;
} }
.all-attr-btn {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
</style> </style>
<style lang="less"> <style lang="less">

View File

@ -88,7 +88,7 @@
(node) => { (node) => {
return { return {
id: node[0], id: node[0],
label: node[0], label: node[1].label || node[0],
children: node.children, children: node.children,
} }
} }

View File

@ -0,0 +1,104 @@
export const BUILT_IN_TYPE = {
DEPARTMENT: 'department', // 部门
USER: 'user', // 用户
USER_GROUP: 'userGroup' // 用户组
}
export const DISPLAY_VALUE_SELECT = [
{ label: 'cs.companyStructure.nickname', value: 'nickname' },
{ label: 'cs.companyStructure.email', value: 'email' },
{ label: 'cs.companyStructure.mobile', value: 'mobile' },
]
export const USER_FILTER_SELECT = [
{ label: 'cs.companyStructure.nickname', value: 'nickname' },
{ label: 'cs.companyStructure.username', value: 'username' },
{ label: 'cs.companyStructure.email', value: 'email' },
{
label: 'cs.companyStructure.sex',
value: 'sex',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.male', value: '男' },
{ label: 'cs.companyStructure.female', value: '女' },
],
},
{ label: 'cs.companyStructure.mobile', value: 'mobile' },
{ label: 'cs.companyStructure.departmentName', value: 'department_name' },
{ label: 'cs.companyStructure.positionName', value: 'position_name' },
{ label: 'cs.companyStructure.supervisor', value: 'direct_supervisor_id' },
{ label: 'cs.companyStructure.annualLeave', value: 'annual_leave' },
{ label: 'cs.companyStructure.currentCompany', value: 'current_company' },
{ label: 'cs.companyStructure.dfcEntryDate', value: 'dfc_entry_date' },
{ label: 'cs.companyStructure.entryDate', value: 'entry_date' },
{
label: 'cs.companyStructure.isInternship',
value: 'is_internship',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.fullTime', value: 0 },
{ label: 'cs.companyStructure.internship', value: 1 },
],
},
{ label: 'cs.companyStructure.leaveDate', value: 'leave_date' },
{ label: 'cs.companyStructure.idCard', value: 'id_card' },
{ label: 'cs.companyStructure.nation', value: 'nation' },
{ label: 'cs.companyStructure.idPlace', value: 'id_place' },
{
label: 'cs.companyStructure.party',
value: 'party',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.partyMember', value: '党员' },
{ label: 'cs.companyStructure.member', value: '团员' },
{ label: 'cs.companyStructure.masses', value: '群众' },
],
},
{
label: 'cs.companyStructure.householdRegistrationType',
value: 'household_registration_type',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.town', value: '城镇' },
{ label: 'cs.companyStructure.agriculture', value: '农业' },
],
},
{ label: 'cs.companyStructure.hometown', value: 'hometown' },
{
label: 'cs.companyStructure.marry',
value: 'marry',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.unmarried', value: '未婚' },
{ label: 'cs.companyStructure.married', value: '已婚' },
],
},
{
label: 'cs.companyStructure.maxDegree',
value: 'max_degree',
is_choice: true,
choice_value: [
{ label: 'cs.companyStructure.phd', value: '博士' },
{ label: 'cs.companyStructure.master', value: '硕士' },
{
label: 'cs.companyStructure.undergraduate',
value: '本科',
},
{ label: 'cs.companyStructure.specialist', value: '专科' },
{ label: 'cs.companyStructure.highSchool', value: '高中' },
],
},
{ label: 'cs.companyStructure.emergencyPerson', value: 'emergency_person' },
{ label: 'cs.companyStructure.emergencyPhone', value: 'emergency_phone' },
{ label: 'cs.companyStructure.bankCardNumber', value: 'bank_card_number' },
{ label: 'cs.companyStructure.bankCardName', value: 'bank_card_name' },
{ label: 'cs.companyStructure.openingBank', value: 'opening_bank' },
{ label: 'cs.companyStructure.accountOpeningLocation', value: 'account_opening_location' },
{ label: 'cs.companyStructure.birthDate', value: 'birth_date' },
{ label: 'cs.companyStructure.nationalityRegion', value: 'nationality_region' },
{ label: 'cs.companyStructure.birthPlace', value: 'birth_place' },
{ label: 'cs.companyStructure.firstEntryDate', value: 'first_entry_date' },
{ label: 'cs.companyStructure.estimatedDepartureDate', value: 'estimated_departure_date' },
// { label: '角色', value: 'roles' },
{ label: 'cs.companyStructure.lastLogin', value: 'last_login' },
]

View File

@ -0,0 +1,279 @@
<template>
<div class="builtin">
<div class="builtin-tab">
<div
v-for="(item) in tabList"
:key="item.key"
:class="['builtin-tab-item', activeKey === item.key ? 'builtin-tab-item_active' : '']"
@click="clickTab(item.key)"
>
<ops-icon :type="item.icon" class="builtin-tab-item-icon" />
<span class="builtin-tab-item-title">{{ $t(item.title) }}</span>
</div>
</div>
<div
v-if="activeKey === BUILT_IN_TYPE.DEPARTMENT"
class="builtin-department"
>
<a-icon class="builtin-department-icon" type="info-circle" />
<span class="builtin-department-tip">{{ $t('cmdb.ciType.departmentTip') }}</span>
</div>
<a-form
:form="form"
v-show="activeKey === BUILT_IN_TYPE.USER"
class="builtin-user"
>
<a-form-item
:label="$t('cmdb.ciType.filterUsers')"
:label-col="formLayout.labelCol"
:wrapper-col="formLayout.wrapperCol"
>
<UserFilterComp
ref="userFilterRef"
/>
</a-form-item>
<a-form-item
:label="$t('cmdb.ciType.departmentCascadeDisplay')"
v-if="activeKey === BUILT_IN_TYPE.USER"
:label-col="formLayout.labelCol"
:wrapper-col="formLayout.wrapperCol"
>
<a-switch
v-decorator="['cascade_display', { rules: [{ required: false }], valuePropName: 'checked', initialValue: false }]"
></a-switch>
</a-form-item>
<a-form-item
v-if="activeKey === BUILT_IN_TYPE.USER"
:label="$t('cmdb.ciType.displayValue')"
:label-col="formLayout.labelCol"
:wrapper-col="formLayout.wrapperCol"
>
<a-select
class="builtin-select"
v-decorator="['display_value', { rules: [{ required: true, message: $t('cmdb.ciType.displayValueSelectTip') }], initialValue: 'nickname' }]"
showSearch
optionFilterProp="title"
:placeholder="$t('cmdb.ciType.displayValueSelectTip')"
>
<a-select-option
v-for="(item) in DISPLAY_VALUE_SELECT"
:value="item.value"
:key="item.value"
:title="$t(item.label)"
>
{{ $t(item.label) }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
<a-form
:form="form"
v-if="activeKey === BUILT_IN_TYPE.USER_GROUP"
class="builtin-usergroup"
>
<a-form-item
:label="$t('cmdb.ciType.userGroup')"
:label-col="formLayout.labelCol"
:wrapper-col="formLayout.wrapperCol"
>
<a-select
class="builtin-select"
v-decorator="['user_group_key', { rules: [{ required: true, message: $t('cmdb.ciType.userGroupSelectTip') }] }]"
showSearch
optionFilterProp="title"
:placeholder="$t('cmdb.ciType.userGroupSelectTip')"
>
<a-select-option
v-for="(item) in userGroupList"
:value="item.group_id"
:key="item.group_id"
:title="item.group_name"
>
{{ item.group_name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
:label-col="formLayout.labelCol"
:wrapper-col="formLayout.wrapperCol"
:label="$t('cmdb.ciType.displayValue')"
>
<a-select
class="builtin-select"
v-decorator="['display_value', { rules: [{ required: true, message: $t('cmdb.ciType.displayValueSelectTip') }], initialValue: 'nickname' }]"
showSearch
optionFilterProp="title"
:placeholder="$t('cmdb.ciType.displayValueSelectTip')"
>
<a-select-option
v-for="(item) in DISPLAY_VALUE_SELECT"
:value="item.value"
:key="item.value"
:title="$t(item.label)"
>
{{ $t(item.label) }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
</template>
<script>
import { BUILT_IN_TYPE, DISPLAY_VALUE_SELECT } from './constants.js'
import UserFilterComp from './userFilterComp/index.vue'
export default {
name: 'PreValueBuiltIn',
components: { UserFilterComp },
data() {
return {
BUILT_IN_TYPE,
DISPLAY_VALUE_SELECT,
activeKey: BUILT_IN_TYPE.DEPARTMENT,
tabList: [
{
key: BUILT_IN_TYPE.DEPARTMENT,
title: 'cmdb.ciType.department',
icon: 'veops-department'
},
{
key: BUILT_IN_TYPE.USER,
title: 'cmdb.ciType.user',
icon: 'icon-shidi-yonghu'
},
{
key: BUILT_IN_TYPE.USER_GROUP,
title: 'cmdb.ciType.userGroup',
icon: 'ops-setting-group'
}
],
userGroupList: [],
allFlatEmployees: [],
form: this.$form.createForm(this),
formLayout: {
labelCol: { span: 5 },
wrapperCol: { span: 16 },
}
}
},
methods: {
setData(data) {
this.activeKey = data?.builtin_type || BUILT_IN_TYPE.DEPARTMENT
this.$nextTick(() => {
this.form.setFieldsValue({
cascade_display: data?.cascade_display ?? false,
display_value: data?.display_value ?? undefined,
user_group_key: data?.user_group_key ?? undefined
})
this.$refs.userFilterRef.setRuleList(data?.filter_rule_list || [])
})
},
getData() {
let params = {}
this.form.validateFields({ force: true }, (err, values) => {
if (err) {
params.isError = true
return
}
params = {
...values,
builtin_type: this.activeKey,
}
if (this.activeKey === BUILT_IN_TYPE.USER) {
params.filter_rule_list = this.$refs.userFilterRef.getRuleList()
}
})
return params
},
async clickTab(key) {
this.activeKey = key
},
}
}
</script>
<style lang="less" scoped>
.builtin {
width: 100%;
&-tab {
padding: 4px 80px 20px;
display: flex;
align-items: center;
gap: 60px;
&-item {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border: solid 1px #E4E7ED;
border-radius: 2px;
padding: 0 20px;
min-width: 72px;
height: 64px;
cursor: pointer;
&-icon {
font-size: 20px;
color: #A5A9BC;
}
&-title {
font-size: 14px;
font-weight: 400;
line-height: 14px;
margin-top: 4px;
}
&_active {
border-color: #B1C9FF;
.builtin-tab-item-icon {
color: #7F97FA;
}
.builtin-tab-item-title {
color: #2F54EB;
}
}
}
}
&-department {
display: flex;
align-items: center;
margin-left: 60px;
&-icon {
font-size: 12px;
color: #A5A9BC;
}
&-tip {
color: #4E5969;
font-size: 14px;
font-weight: 400;
margin-left: 4px;
}
}
&-select {
width: 240px;
}
}
</style>

View File

@ -0,0 +1,16 @@
export const ruleTypeList = [
{ value: '&', label: 'cs.components.and' },
{ value: '|', label: 'cs.components.or' },
]
export const expList = [
{ value: 1, label: 'cs.components.equal' },
{ value: 2, label: 'cs.components.notEqual' },
{ value: 7, label: 'cs.components.isEmpty' },
{ value: 8, label: 'cs.components.isNotEmpty' },
]
export const compareTypeList = [
{ value: 5, label: 'cs.components.moreThan' },
{ value: 6, label: 'cs.components.lessThan' },
]

View File

@ -0,0 +1,143 @@
<template>
<treeselect
class="employee-tree-select"
:style="{ width: '100px' }"
:disable-branch-nodes="!multiple"
:multiple="multiple"
:options="employeeTreeSelectOption"
:placeholder="readOnly ? '' : placeholder || $t('cs.components.selectEmployee')"
v-model="treeValue"
:max-height="150"
:noChildrenText="$t('cs.components.empty')"
:noOptionsText="$t('cs.components.empty')"
:class="className ? className : 'ops-setting-treeselect'"
value-consists-of="LEAF_PRIORITY"
:limit="limit"
:limitText="(count) => `+ ${count}`"
v-bind="$attrs"
:zIndex="1050"
:flat="flat"
>
<div
:title="node.label"
slot="option-label"
slot-scope="{ node }"
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
>
{{ node.label }}
</div>
</treeselect>
</template>
<script>
import Treeselect from '@riophae/vue-treeselect'
import { formatOption } from '@/utils/util'
export default {
name: 'EmployeeTreeSelect',
components: {
Treeselect,
},
model: {
prop: 'value',
event: 'change',
},
props: {
value: {
type: [String, Array, Number, null],
default: null,
},
multiple: {
type: Boolean,
default: false,
},
className: {
type: String,
default: 'ops-setting-treeselect',
},
placeholder: {
type: String,
default: '',
},
idType: {
type: Number,
default: 3,
},
departmentKey: {
type: String,
default: 'department_id',
},
employeeKey: {
type: String,
default: 'employee_id',
},
limit: {
type: Number,
default: 20,
},
flat: {
type: Boolean,
default: false,
},
},
data() {
return {}
},
inject: {
provide_allTreeDepAndEmp: {
from: 'provide_allTreeDepAndEmp',
default: () => {
return () => {
return []
}
}
},
readOnly: {
from: 'readOnly',
default: false,
},
},
computed: {
treeValue: {
get() {
return this.value
},
set(val) {
this.$emit('change', val)
return val
},
},
allTreeDepAndEmp() {
return this.provide_allTreeDepAndEmp()
},
employeeTreeSelectOption() {
const option = formatOption(this.allTreeDepAndEmp, this.idType, false, this.departmentKey, this.employeeKey)
return option
},
},
methods: {},
}
</script>
<style lang="less" scoped>
.employee-tree-select {
/deep/ .vue-treeselect__menu {
width: 100px;
overflow-x: auto;
& > .vue-treeselect__list {
width: fit-content;
min-width: 100%;
}
.vue-treeselect__label-container {
width: max-content;
}
.vue-treeselect__option {
width: max-content;
min-width: 100%;
}
}
}
</style>

View File

@ -0,0 +1,347 @@
<template>
<div class="user-filter">
<a-button
v-if="!ruleList.length"
type="primary"
ghost
size="small"
class="add-btn"
@click="handleAddRule"
>
<a-icon type="plus" />
{{ $t('add') }}
</a-button>
<template v-else>
<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': '36px', position: 'absolute', top: '-30px', left: 0 }"
v-model="item.relation"
:multiple="false"
:clearable="false"
searchable
:options="ruleTypeList"
:normalizer="
(node) => {
return {
id: node.value,
label: $t(node.label),
children: node.children,
}
}
"
>
</treeselect>
</div>
<treeselect
class="custom-treeselect"
:style="{ width: '140px', '--custom-height': '36px' }"
v-model="item.column"
:multiple="false"
:clearable="false"
searchable
:options="userFilterSelect"
:maxHeight="150"
:normalizer="
(node) => {
return {
id: node.value,
label: $t(node.label),
children: node.children,
}
}
"
>
<div
slot="option-label"
slot-scope="{ node }"
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
>
<a-tooltip :title="$t(node.label)">
{{ $t(node.label) }}
</a-tooltip>
</div>
<div
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
slot="value-label"
slot-scope="{ node }"
>
<a-tooltip :title="$t(node.label)">
{{ $t(node.label) }}
</a-tooltip>
</div>
</treeselect>
<treeselect
class="custom-treeselect"
:style="{ width: '90px', '--custom-height': '36px' }"
v-model="item.operator"
:multiple="false"
:clearable="false"
searchable
:options="[...expList, ...compareTypeList]"
:maxHeight="150"
:normalizer="
(node) => {
return {
id: node.value,
label: $t(node.label),
children: node.children,
}
}
"
@select="(value) => handleChangeExp(value, item, index)"
>
<div
slot="option-label"
slot-scope="{ node }"
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
>
<a-tooltip :title="$t(node.label)">
{{ $t(node.label) }}
</a-tooltip>
</div>
<div
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
slot="value-label"
slot-scope="{ node }"
>
<a-tooltip :title="$t(node.label)">
{{ $t(node.label) }}
</a-tooltip>
</div>
</treeselect>
<treeselect
class="custom-treeselect"
:style="{ width: '100px', '--custom-height': '36px' }"
v-model="item.value"
:multiple="false"
:clearable="false"
searchable
v-if="isChoiceByProperty(item.column) && (item.operator === 1 || item.operator === 2)"
:options="getChoiceValueByProperty(item.column)"
:placeholder="$t('cs.components.selectPlaceholder')"
:normalizer="
(node) => {
return {
id: node.value,
label: $t(node.label),
children: node.children,
}
}
"
>
<div
:title="$t(node.label)"
slot="option-label"
slot-scope="{ node }"
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
>
{{ $t(node.label) }}
</div>
</treeselect>
<div v-else-if="item.column === 'direct_supervisor_id' && (item.operator === 1 || item.operator === 2)">
<EmployeeTreeSelect v-model="item.value" className="custom-treeselect"/>
</div>
<a-input
v-else-if="item.operator !== 7 && item.operator !== 8"
size="small"
v-model="item.value"
:placeholder="item.exp === 'in' || item.exp === '~in' ? $t('cs.components.operatorInPlaceholder') : ''"
class="ops-input"
></a-input>
<a-tooltip :title="$t('cs.components.copy')">
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="veops-copy"/></a>
</a-tooltip>
<a-tooltip :title="$t('delete')">
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="veops-reduce"/></a>
</a-tooltip>
<a-tooltip :title="$t('add')">
<a class="operation" @click="handleAddRule"><ops-icon type="veops-increase"/></a>
</a-tooltip>
</a-space>
</template>
</div>
</template>
<script>
import { mapState } from 'vuex'
import _ from 'lodash'
import { v4 as uuidv4 } from 'uuid'
import Treeselect from '@riophae/vue-treeselect'
import { ruleTypeList, expList, compareTypeList } from './constants'
import { USER_FILTER_SELECT } from '../constants'
import EmployeeTreeSelect from './employeeTreeSelect.vue'
export default {
name: 'UserFilterComp',
components: {
Treeselect,
EmployeeTreeSelect
},
data() {
return {
USER_FILTER_SELECT,
ruleTypeList,
expList,
compareTypeList,
ruleList: [],
filterExp: '',
userFilterSelect: [],
}
},
computed: {
...mapState(['user'])
},
created() {
this.initUserFilterSelect()
},
methods: {
initUserFilterSelect() {
const permissions = this.user?.detailPermissions?.backend?.find((item) => item.name === 'Employee_Fields')?.permissions
const userFilterSelect = USER_FILTER_SELECT.filter((item) => {
return permissions.includes(item.value)
})
this.userFilterSelect = userFilterSelect
},
async setRuleList(ruleList) {
this.ruleList = ruleList?.length ? ruleList : []
},
handleAddRule() {
this.ruleList.push({
id: uuidv4(),
relation: '&',
column: this.userFilterSelect?.[0]?.value ?? null,
operator: 1,
value: null,
})
},
handleCopyRule(item) {
this.ruleList.push({ ...item, id: uuidv4() })
},
handleDeleteRule(item) {
const idx = this.ruleList.findIndex((r) => r.id === item.id)
if (idx > -1) {
this.ruleList.splice(idx, 1)
}
},
getRuleList() {
if (this.ruleList && this.ruleList.length) {
const ruleList = _.cloneDeep(this.ruleList)
ruleList.forEach((item, index) => {
if (item.column === 'direct_supervisor_id') {
ruleList[index].value = item.value ? (item.value + '').includes('-') ? +item.value.split('-')[1] : +item.value : 0
}
})
if (ruleList?.length > 0) {
ruleList[0].relation = '&'
}
return ruleList.map((rule) => {
return _.omit(rule, 'id')
})
}
return []
},
handleChangeExp({ value }, item, index) {
const _ruleList = _.cloneDeep(this.ruleList)
if (value === 7 || value === 8) {
_ruleList[index] = {
..._ruleList[index],
value: null,
operator: value
}
} else {
_ruleList[index] = {
..._ruleList[index],
operator: value,
}
}
this.ruleList = _ruleList
},
filterOption(input, option) {
return option.componentOptions.children[1].children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
isChoiceByProperty(column) {
const _find = this.userFilterSelect.find((item) => item.value === column)
if (_find) {
return _find.is_choice
}
return false
},
getChoiceValueByProperty(column) {
const _find = this.userFilterSelect.find((item) => item.value === column)
if (_find) {
return _find.choice_value
}
return []
},
},
}
</script>
<style lang="less" scoped>
.user-filter {
display: flex;
flex-direction: column;
line-height: 36px;
.ops-input {
height: 36px;
width: 100px;
}
}
.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;
}
}
}
.add-btn {
font-size: 12px;
width: 80px;
margin-top: 10px;
}
</style>
<style lang="less" scoped>
.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>

View File

@ -39,3 +39,10 @@ export const compareTypeList = [
{ value: '3', label: '<' }, { value: '3', label: '<' },
{ value: '4', label: '<=' }, { value: '4', label: '<=' },
] ]
export const ENUM_VALUE_TYPE = {
INPUT: 'input',
DATE: 'date',
DATE_TIME: 'dateTIme',
NUMBER: 'number'
}

View File

@ -0,0 +1,310 @@
<template>
<div>
<a-popover
:visible="popoverVisible"
placement="bottom"
trigger="focus"
overlayClassName="pre-value-edit-popover"
@visibleChange="handleVisibleChange"
>
<div @click="popoverVisible = true" ref="popoverLabelRef">
<a-input
v-show="!labelData.label || popoverVisible"
type="text"
:style="{ width: '210px' }"
:value="labelData.label"
@change="changeLabel"
>
</a-input>
<div
class="pre-value-tag"
:style="labelData.style ? labelData.style : {}"
v-show="!popoverVisible && labelData.label"
>
<span>
<img
v-if="labelData.icon.id && labelData.icon.url"
:src="`/api/common-setting/v1/file/${labelData.icon.url}`"
:style="{ maxHeight: '12px', maxWidth: '12px', marginRight: '5px' }"
/>
<ops-icon
v-else-if="labelData.icon.name"
:type="labelData.icon.name"
:style="{ marginRight: '5px', color: labelData.icon.color || '#595959' }"
/>
<a-tooltip :title="labelData.label">
<span class="pre-value-tag-text">{{ labelData.label }}</span>
</a-tooltip>
</span>
</div>
</div>
<div ref="preValueEdit" slot="content">
<a-divider orientation="left" style="margin:8px 0;color:gray;font-size:10px;">{{ $t('icon') }}</a-divider>
<CustomIconSelect
:style="{ marginLeft: '10px' }"
:value="labelData.icon"
@change="changeIcon"
/>
<a-divider orientation="left" style="margin:8px 0;color:gray;font-size:10px;">{{ $t('cmdb.ciType.font') }}</a-divider>
<div :style="{ display: 'flex', justifyContent: 'space-around' }">
<div
@click="changeFontStyle('fontWeight', 'bold')"
:class="`attributes-font-icon ${labelData.style.fontWeight === 'bold' ? 'attributes-font-icon-selected' : ''}`"
>
<a-icon type="bold" />
</div>
<div
@click="changeFontStyle('fontStyle', 'italic')"
:class="`attributes-font-icon ${labelData.style.fontStyle === 'italic' ? 'attributes-font-icon-selected' : ''}`"
>
<a-icon type="italic" />
</div>
<div
@click="changeFontStyle('textDecoration', 'underline')"
:class="
`attributes-font-icon ${labelData.style.textDecoration === 'underline' ? 'attributes-font-icon-selected' : ''}`
"
>
<a-icon type="underline" />
</div>
</div>
<a-divider orientation="left" style="margin:8px 0;color:gray;font-size:10px;">{{ $t('cmdb.ciType.color') }}</a-divider>
<div :style="{ display: 'flex', justifyContent: 'space-around' }">
<div class="attributes-font-color">
<a-icon type="font-colors" />
<el-color-picker
size="mini"
:value="labelData.style.color"
@change="(v) => changeFontStyle('color', v)"
:predefine="defaultBGColors"
>
</el-color-picker>
</div>
<div class="attributes-font-color">
<a-icon type="bg-colors" />
<el-color-picker
size="mini"
:value="labelData.style.backgroundColor"
@change="(v) => changeFontStyle('backgroundColor', v)"
:predefine="defaultBGColors"
>
</el-color-picker>
</div>
</div>
<a-divider orientation="left" style="margin:8px 0;color:gray;font-size:10px;">{{ $t('operation') }}</a-divider>
<div style="text-align:right;">
<a-tooltip
:title="$t('delete')"
>
<a>
<a-icon
@click="handleDelete"
style="margin-right:10px;color:red;"
type="delete"
/>
</a>
</a-tooltip>
<a-tooltip
:title="$t('confirm')"
>
<a>
<a-icon @click="popoverVisible = false" style="margin-right:10px;color:green;" type="check"/>
</a>
</a-tooltip>
</div>
</div>
</a-popover>
</div>
</template>
<script>
import _ from 'lodash'
import { ColorPicker } from 'element-ui'
import CustomIconSelect from '@/components/CustomIconSelect'
import { defautValueColor, defaultBGColors } from '@/modules/cmdb/utils/const.js'
import lang from 'element-ui/lib/locale/lang/en'
import locale from 'element-ui/lib/locale'
locale.use(lang)
export default {
name: 'DefineLabel',
components: { ElColorPicker: ColorPicker, CustomIconSelect },
props: {
labelData: {
type: Object,
default: () => {},
},
},
data() {
return {
defautValueColor,
defaultBGColors,
popoverVisible: false,
}
},
mounted() {
document.addEventListener('click', this.eventListener)
},
beforeDestroy() {
document.removeEventListener('click', this.eventListener)
},
methods: {
eventListener(e) {
if (this.popoverVisible) {
const dom = this.$refs.preValueEdit
const dom_label = this.$refs.popoverLabelRef
const dom_icon = document.getElementById(`custom-icon-select-popover`)
e.stopPropagation()
e.preventDefault()
if (dom) {
const isSelf =
dom.contains(e.target) || (dom_label && dom_label.contains(e.target)) || (dom_icon && dom_icon.contains(e.target))
if (!isSelf) {
this.popoverVisible = false
}
}
}
},
handleDelete() {
this.popoverVisible = false
this.$emit('deleteData')
},
changeFontStyle(key, value) {
const style = {
...(this.labelData.style || {}),
[key]: this.labelData.style[key] === value ? 'initial' : value,
}
this.$emit('change', 'style', style)
},
changeLabel(e) {
const value = e.target.value
this.$emit('change', 'label', value)
},
changeIcon(value) {
this.$emit('change', 'icon', value)
},
handleVisibleChange(v) {
if (!v) {
this.popoverVisible = false
}
}
},
}
</script>
<style lang="less" scoped>
.pre-value-edit-color {
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
.pre-value-edit-color-item {
cursor: pointer;
display: inline-block;
width: 25px;
height: 20px;
margin: 5px;
}
}
.pre-value-tag {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
position: relative;
cursor: pointer;
max-width: 100%;
&-text {
overflow: hidden;
text-wrap: nowrap;
text-overflow: ellipsis;
max-width: 100%;
}
> span {
display: flex;
align-items: center;
}
&:hover .pre-value-tag-dropdown-icon {
display: inline !important;
}
.pre-value-tag-dropdown {
font-size: 10px;
color: #999999;
&:hover {
color: #2f54eb;
}
.pre-value-tag-dropdown-icon {
display: none;
position: absolute;
right: -10px;
top: 8px;
}
}
}
</style>
<style lang="less">
.pre-value-tag-input {
border: none;
border-bottom: 1px solid #d9d9d9;
font-size: 12px;
&:focus {
box-shadow: none;
}
}
.pre-value-edit-popover.ant-popover-placement-top .ant-popover-content {
margin-bottom: -10px;
}
.pre-value-edit-popover.ant-popover-placement-bottom .ant-popover-content {
margin-top: -10px;
}
.pre-value-edit-popover {
.ant-popover-content {
width: 150px;
.ant-popover-arrow {
display: none;
}
.ant-popover-inner-content {
padding: 3px 4px;
}
}
.attributes-font-icon {
cursor: pointer;
display: inline-block;
width: 30px;
height: 30px;
position: relative;
border: 1px solid #fff;
&:hover {
background-color: #eeeeee;
border-color: #606266;
}
> i {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.attributes-font-icon-selected {
background-color: #eeeeee;
}
.attributes-font-color {
display: inline-flex;
align-items: center;
width: 50%;
justify-content: center;
}
}
</style>

View File

@ -0,0 +1,245 @@
<template>
<div class="define-wrap">
<a-button
v-if="!defineList.length"
type="primary"
ghost
:disabled="disabled"
size="small"
class="add-btn"
@click="addData"
>
<a-icon type="plus" />
{{ $t('add') }}
</a-button>
<vxe-table
v-else
ref="xTable"
:data="defineList"
size="mini"
show-header-overflow
:row-config="{ height: 46 }"
:min-height="75"
border="outer"
class="define-wrap-table"
>
<vxe-column field="value" width="230" :title="$t('cmdb.ciType.enumValue')">
<template #header="{ column }">
<span class="table-header-required">*</span>
{{ column.title }}
</template>
<template #default="{ row, rowIndex }">
<a-input
v-if="enumValueType === ENUM_VALUE_TYPE.INPUT"
:value="row.value"
:placeholder="$t('cmdb.ciType.valueInputTip')"
@change="(e) => changeValue(rowIndex, e.target.value)"
></a-input>
<a-input-number
v-else-if="enumValueType === ENUM_VALUE_TYPE.NUMBER"
:value="row.value"
@change="(v) => changeValue(rowIndex, v)"
>
</a-input-number>
<a-date-picker
v-else
style="width: 100%"
:value="row.value"
:format="enumValueType === ENUM_VALUE_TYPE.DATE ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
:showTime="enumValueType === ENUM_VALUE_TYPE.DATE ? false : { format: 'HH:mm:ss' }"
@change="(e) => changeDate(rowIndex, e)"
/>
</template>
</vxe-column>
<vxe-column width="230" :title="$t('cmdb.ciType.label')">
<template #default="{ row, rowIndex }">
<DefineLabel
:labelData="row"
@change="(key, value) => changeStyle(rowIndex, key, value)"
@deleteData="handleClear(rowIndex)"
/>
</template>
</vxe-column>
</vxe-table>
<div class="define-wrap-action">
<div
v-for="(item, index) in defineList"
:key="item.id"
class="define-wrap-action-item"
>
<a-icon
type="plus-circle"
class="define-wrap-action-item-icon"
@click="addData(index)"
/>
<a-icon
type="minus-circle"
class="define-wrap-action-item-icon"
@click="deleteData(index)"
/>
</div>
</div>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import _ from 'lodash'
import DefineLabel from './defineLabel.vue'
import { ENUM_VALUE_TYPE } from '../constants.js'
export default {
name: 'PreValueDefine',
components: {
DefineLabel
},
props: {
value: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
// 枚举值控件类型
enumValueType: {
type: String,
default: ENUM_VALUE_TYPE.INPUT
}
},
model: {
prop: 'value',
event: 'change',
},
data() {
return {
ENUM_VALUE_TYPE
}
},
computed: {
defineList: {
get() {
return this.value.map((item) => {
return {
value: item?.[0] ?? '',
...(item?.[1] ?? {}),
id: uuidv4()
}
})
},
set(val) {
this.$emit('change', val.map((item) => {
return [
item?.value ?? '',
{
style: item?.style ?? {},
icon: item?.icon ?? {},
label: item?.label ?? '',
}
]
}))
return val
},
},
},
methods: {
addData(index) {
const list = _.cloneDeep(this.value)
list.splice(index + 1, 0, [
'',
{
style: {},
icon: {},
label: ''
}
])
this.$emit('change', list)
},
deleteData(index) {
if (this.value.length <= 1) {
this.$message.error(this.$t('cmdb.ad.deleteTip'))
return
}
const list = _.cloneDeep(this.value)
list.splice(index, 1)
this.$emit('change', list)
},
changeValue(rowIndex, value) {
const list = _.cloneDeep(this.value)
list[rowIndex][0] = value
this.$emit('change', list)
},
changeDate(rowIndex, e) {
const format = this.enumValueType === ENUM_VALUE_TYPE.DATE ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'
const value = e.format(format)
const list = _.cloneDeep(this.value)
list[rowIndex][0] = value
this.$emit('change', list)
},
changeStyle(rowIndex, key, value) {
const list = _.cloneDeep(this.value)
list[rowIndex][1] = {
...list[rowIndex][1],
[key]: value
}
this.$emit('change', list)
},
handleClear(rowIndex) {
const list = _.cloneDeep(this.value)
list[rowIndex][1] = {
style: {},
icon: {},
label: ''
}
this.$emit('change', list)
}
}
}
</script>
<style lang="less" scoped>
.define-wrap {
display: flex;
.add-btn {
font-size: 12px;
padding: 1px 7px;
}
&-table {
flex-shrink: 0;
.table-header-required {
color: #FD4C6A;
}
/deep/ .ant-input-number {
width: 100%;
}
}
&-action {
flex-shrink: 0;
margin-left: 11px;
padding-top: 36px;
&-item {
display: flex;
align-items: center;
height: 46px;
gap: 12px;
&-icon {
cursor: pointer;
color: #2F54EB;
}
}
}
}
</style>

View File

@ -179,6 +179,7 @@
> >
<a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in CITypes"> <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in CITypes">
{{ CIType.alias || CIType.name }} {{ CIType.alias || CIType.name }}
<span class="model-select-name">({{ CIType.name }})</span>
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -674,6 +675,11 @@ export default {
.modal-attribute-action { .modal-attribute-action {
margin-left: 5px; margin-left: 5px;
} }
.model-select-name {
font-size: 12px;
color: #A5A9BC;
}
</style> </style>
<style lang="less"> <style lang="less">

View File

@ -24,9 +24,10 @@
@change="handleSourceTypeChange" @change="handleSourceTypeChange"
:filterOption="filterOption" :filterOption="filterOption"
> >
<a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayCITypes">{{ <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayCITypes">
CIType.alias || CIType.name {{ CIType.alias || CIType.name }}
}}</a-select-option> <span class="model-select-name">({{ CIType.name }})</span>
</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item :label="$t('cmdb.ciType.dstCIType')"> <a-form-item :label="$t('cmdb.ciType.dstCIType')">
@ -39,6 +40,7 @@
> >
<a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayTargetCITypes"> <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayTargetCITypes">
{{ CIType.alias || CIType.name }} {{ CIType.alias || CIType.name }}
<span class="model-select-name">({{ CIType.name }})</span>
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -408,4 +410,9 @@ export default {
.modal-attribute-action { .modal-attribute-action {
margin-left: 5px; margin-left: 5px;
} }
.model-select-name {
font-size: 12px;
color: #A5A9BC;
}
</style> </style>

View File

@ -63,6 +63,14 @@
{{ id }} {{ id }}
</a> </a>
</template> </template>
<template #default="{row}" v-else-if="col.is_choice">
<span
v-for="value in (col.is_list ? row[col.field] : [row[col.field]])"
:key="value"
>
{{ getChoiceValueLabel(col, value) || value }}
</span>
</template>
<template #default="{row}" v-else-if="col.value_type == '6'"> <template #default="{row}" v-else-if="col.value_type == '6'">
<span v-if="col.value_type == '6' && row[col.field]">{{ JSON.stringify(row[col.field]) }}</span> <span v-if="col.value_type == '6' && row[col.field]">{{ JSON.stringify(row[col.field]) }}</span>
</template> </template>
@ -253,6 +261,14 @@ export default {
this.currentPage = page this.currentPage = page
this.fetchData() this.fetchData()
}, },
getChoiceValueLabel(col, colValue) {
const _find = col.filters.find((item) => String(item[0]) === String(colValue))
if (_find) {
return _find[1]?.label || ''
}
return ''
},
}, },
} }
</script> </script>

View File

@ -124,7 +124,7 @@
" "
target="_blank" target="_blank"
> >
{{ item }} {{ getChoiceValueLabel(col, item) || item }}
</a> </a>
</template> </template>
<PasswordField <PasswordField
@ -143,11 +143,13 @@
margin: '2px', margin: '2px',
...getChoiceValueStyle(col, value), ...getChoiceValueStyle(col, value),
}" }"
><ops-icon
:style="{ color: getChoiceValueIcon(col, value).color }"
:type="getChoiceValueIcon(col, value).name"
/>{{ value }}</span
> >
<ops-icon
:style="{ color: getChoiceValueIcon(col, value).color }"
:type="getChoiceValueIcon(col, value).name"
/>
{{ getChoiceValueLabel(col, value) || value }}
</span>
</template> </template>
<span <span
v-else v-else
@ -162,8 +164,8 @@
:style="{ color: getChoiceValueIcon(col, row[col.field]).color }" :style="{ color: getChoiceValueIcon(col, row[col.field]).color }"
:type="getChoiceValueIcon(col, row[col.field]).name" :type="getChoiceValueIcon(col, row[col.field]).name"
/> />
{{ row[col.field] }}</span {{ getChoiceValueLabel(col, row[col.field]) || row[col.field] }}
> </span>
</template> </template>
</template> </template>
</vxe-column> </vxe-column>
@ -582,6 +584,13 @@ export default {
} }
return {} return {}
}, },
getChoiceValueLabel(col, colValue) {
const _find = col?.filters?.find((item) => String(item[0]) === String(colValue))
if (_find) {
return _find[1]?.label || ''
}
return ''
},
handleExport() { handleExport() {
this.$refs.batchDownload.open({ this.$refs.batchDownload.open({
preferenceAttrList: [ preferenceAttrList: [
@ -611,6 +620,7 @@ export default {
return { ...item } return { ...item }
}), }),
], ],
original: true,
download: false, download: false,
}) })
this.selectedRowKeys = [] this.selectedRowKeys = []

View File

@ -1185,7 +1185,23 @@ export default {
if (!res.result.length) { if (!res.result.length) {
this.handleNullNodeTips(this.$t('cmdb.topo.noInstancePerm')) this.handleNullNodeTips(this.$t('cmdb.topo.noInstancePerm'))
} else { } else {
this.currentNodeValues = res.result[0] const currentNodeValues = res.result[0]
Object.keys(currentNodeValues).forEach((key) => {
const attr = this.currentNodeAttributes.find((attr) => attr.name === key)
if (attr?.choice_value?.length) {
if (Array.isArray(currentNodeValues[key])) {
currentNodeValues[key] = currentNodeValues[key].map((value) => {
const choice = attr.choice_value.find((choice) => value === choice?.[0])
return choice?.[1]?.label || value
})
} else {
const choice = attr.choice_value.find((choice) => currentNodeValues[key] === choice?.[0])
currentNodeValues[key] = choice?.[1]?.label || currentNodeValues[key]
}
}
})
this.currentNodeValues = currentNodeValues
} }
}).catch(error => { }).catch(error => {
this.handleNullNodeTips(((error.response || {}).data || {}).message) this.handleNullNodeTips(((error.response || {}).data || {}).message)

View File

@ -375,10 +375,9 @@ export default {
}, },
methods: { methods: {
async getAttributeList() { async getAttributeList() {
await getCITypeAttributesById(Number(this.typeId)).then((res) => { const res = await getCITypeAttributesById(Number(this.typeId))
this.attrList = res.attributes this.attrList = res.attributes
this.attributes = res this.attributes = res
})
}, },
async getTreeViews() { async getTreeViews() {
this.subscribeTreeViewCiTypesLoading = true this.subscribeTreeViewCiTypesLoading = true
@ -519,10 +518,18 @@ export default {
wrapTreeData(facet) { wrapTreeData(facet) {
// 切面 // 切面
console.log('facet', facet)
const _treeData = Object.values(facet)[0].map((item) => { const _treeData = Object.values(facet)[0].map((item) => {
let title = item[0]
const attr = this.attrList.find((attr) => attr.name === item[2])
if (attr?.choice_value?.length) {
const choice = attr.choice_value.find((choice) => item[0] === choice?.[0])
if (choice?.[1]?.label) {
title = choice[1].label
}
}
return { return {
title: item[0], title: title,
childLength: item[1], childLength: item[1],
key: this.treeKeys.join(this.keySplit) + this.keySplit + item[0], key: this.treeKeys.join(this.keySplit) + this.keySplit + item[0],
isLeaf: this.levels.length - 1 === this.treeKeys.length, isLeaf: this.levels.length - 1 === this.treeKeys.length,