feat:citype regex check & pref:edit is_list (#380)

This commit is contained in:
wang-liang0615 2024-01-12 17:09:44 +08:00 committed by GitHub
parent 9bc0ab6009
commit 9141c06530
16 changed files with 482 additions and 194 deletions

View File

@ -0,0 +1,19 @@
/* eslint-disable no-useless-escape */
import i18n from '@/lang'
export const regList = () => {
return [
{ id: 'letter', label: i18n.t('regexSelect.letter'), value: '^[A-Za-z]+$', message: '请输入字母' },
{ id: 'number', label: i18n.t('regexSelect.number'), value: '^-?(?!0\\d+)\\d+(\\.\\d+)?$', message: '请输入数字' },
{ id: 'letterAndNumber', label: i18n.t('regexSelect.letterAndNumber'), value: '^[A-Za-z0-9.]+$', message: '请输入字母和数字' },
{ id: 'phone', label: i18n.t('regexSelect.phone'), value: '^1[3-9]\\d{9}$', message: '请输入正确手机号码' },
{ id: 'landline', label: i18n.t('regexSelect.landline'), value: '^(?:(?:\\d{3}-)?\\d{8}|^(?:\\d{4}-)?\\d{7,8})(?:-\\d+)?$', message: '请输入正确座机' },
{ id: 'zipCode', label: i18n.t('regexSelect.zipCode'), value: '^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\\d{4}$', message: '请输入正确邮政编码' },
{ id: 'IDCard', label: i18n.t('regexSelect.IDCard'), value: '(^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)', message: '请输入正确身份证号' },
{ id: 'ip', label: i18n.t('regexSelect.ip'), value: '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', message: '请输入正确IP地址' },
{ id: 'email', label: i18n.t('regexSelect.email'), value: '^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\.\\w+([-.]\\w+)*$', message: '请输入正确邮箱' },
{ id: 'link', label: i18n.t('regexSelect.link'), value: '^(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})(\.[a-zA-Z0-9]{2,})?$', message: '请输入链接' },
{ id: 'monetaryAmount', label: i18n.t('regexSelect.monetaryAmount'), value: '^-?\\d+(,\\d{3})*(\.\\d{1,2})?$', message: '请输入货币金额' },
{ id: 'custom', label: i18n.t('regexSelect.custom'), value: '', message: '' }
]
}

View File

@ -0,0 +1,2 @@
import RegexSelect from './regexSelect.vue'
export default RegexSelect

View File

@ -0,0 +1,208 @@
<template>
<a-popover
trigger="click"
placement="bottom"
ref="regexSelect"
overlayClassName="regex-select-wrapper"
:overlayStyle="{ '--overlay-width': `${width}px` }"
@visibleChange="visibleChange"
>
<div class="regex-select" slot="content">
<div class="regex-select-left">
<div class="regex-select-left-header">{{ $t('regexSelect.limitedFormat') }}</div>
<div
@click="
() => {
current = reg
testInput = ''
showMessage = false
}
"
:class="{
'regex-select-left-reg': true,
'regex-select-left-reg-selected': current && current.label === reg.label,
}"
v-for="(reg, index) in regList"
:key="reg.label"
>
<a-divider :style="{ margin: '2px -12px', width: 'calc(100% + 24px)' }" v-if="index === regList.length - 1" />
{{ reg.label }}
</div>
</div>
<div class="regex-select-right">
<template v-if="current">
<div class="regex-select-right-header">{{ $t('regexSelect.regExp') }}</div>
<div
v-if="current.label !== $t('regexSelect.custom')"
:style="{ color: '#000', fontSize: '12px', margin: '12px 0' }"
>
{{ current.value }}
</div>
<a-input
:style="{ margin: '12px 0' }"
size="small"
v-else
v-model="current.value"
@change="
() => {
this.$emit('change', current)
}
"
/>
<template v-if="isShowErrorMsg">
<div class="regex-select-right-header">{{ $t('regexSelect.errMsg') }}</div>
<a-input :style="{ margin: '12px 0' }" size="small" v-model="current.message" />
</template>
<div class="regex-select-right-header">{{ $t('regexSelect.test') }}</div>
<a-input v-model="testInput" :style="{ margin: '12px 0 4px' }" size="small" @change="validate" />
<span :style="{ color: 'red', fontSize: '12px' }" v-if="showMessage">{{
locale === 'zh' ? current.message || '错误' : $t('regexSelect.error')
}}</span>
</template>
</div>
</div>
<a-input ref="regInput" :placeholder="$t('regexSelect.placeholder')" :value="current.label" @change="changeLabel">
</a-input>
</a-popover>
</template>
<script>
import { mapState } from 'vuex'
import { regList } from './constants'
export default {
name: 'RegexSelect',
model: {
prop: 'value',
event: 'change',
},
props: {
value: {
type: Object,
default: () => {},
},
isShowErrorMsg: {
type: Boolean,
default: true,
},
limitedFormat: {
type: Array,
default: () => [],
},
},
data() {
return {
showMessage: false,
width: 370,
testInput: '',
}
},
computed: {
...mapState(['locale']),
regList() {
if (this.limitedFormat.length) {
return regList().filter((item) => this.limitedFormat.includes(item.id))
}
return regList()
},
current: {
get() {
if (this.value?.value && !this.value?.label) {
const _find = this.regList.find((reg) => reg.value === this.value?.value)
return { ...this.value, label: _find?.label ?? this.$t('regexSelect.custom') }
}
return this.value ?? {}
},
set(val) {
this.showMessage = false
this.$emit('change', val)
return val
},
},
},
mounted() {
this.$nextTick(() => {
const regInput = this.$refs.regInput.$refs.input
this.width = regInput.offsetWidth || 370
})
},
methods: {
validate(e) {
const reg = RegExp(this.current.value, 'g')
this.showMessage = !reg.test(e.target.value)
},
changeLabel(e) {
this.current = {}
},
visibleChange(visible) {
if (visible) {
this.$nextTick(() => {
this.testInput = ''
this.showMessage = false
})
}
},
},
}
</script>
<style lang="less" scoped>
@import '~@/style/static.less';
.regex-select {
width: 100%;
height: 300px;
display: flex;
.regex-select-left {
width: 40%;
height: 100%;
border: 1px solid #cacdd9;
border-radius: 4px;
padding: 12px;
&-reg {
padding-left: 2px;
cursor: pointer;
&-selected,
&:hover {
color: #custom_colors[color_1];
}
}
}
&-right {
flex: 1;
height: 100%;
border: 1px solid #cacdd9;
border-radius: 4px;
margin-left: 8px;
padding: 12px;
}
&-left,
&-right {
&-header {
font-weight: 400;
font-size: 14px;
color: #000000;
border-left: 2px solid #custom_colors[color_1];
padding-left: 6px;
margin-left: -6px;
}
}
}
</style>
<style lang="less">
.regex-select-wrapper {
.ant-popover-arrow {
display: none;
}
.ant-popover-inner-content {
padding: 0;
min-width: 370px;
width: var(--overlay-width);
}
}
.regex-select-wrapper.ant-popover-placement-bottom .ant-popover-content {
margin-top: -8px;
}
.regex-select-wrapper.ant-popover-placement-top .ant-popover-content {
margin-bottom: -8px;
}
</style>

View File

@ -145,6 +145,26 @@ export default {
sizeLimit: 'The image size cannot exceed 2MB!',
nodata: 'There are currently no custom icons available. Click here to upload'
},
regexSelect: {
limitedFormat: 'Limited Format',
regExp: 'RegExp',
errMsg: 'Error Message',
test: 'Test',
placeholder: 'Please Select RegExp',
error: 'Error',
letter: 'letter',
number: 'number',
letterAndNumber: 'letter&number',
phone: 'phone',
landline: 'landline',
zipCode: 'zip code',
IDCard: 'ID card',
ip: 'IP',
email: 'email',
link: 'link',
monetaryAmount: 'monetary amount',
custom: 'custom',
},
cmdb: cmdb_en,
cs: cs_en,
acl: acl_en,

View File

@ -145,6 +145,26 @@ export default {
sizeLimit: '图片大小不可超过2MB',
nodata: '暂无自定义图标,点击此处上传'
},
regexSelect: {
limitedFormat: '限定格式',
regExp: '正则表达式',
errMsg: '错误时提示',
test: '测试',
placeholder: '请选择正则表达式',
error: '错误',
letter: '字母',
number: '数字',
letterAndNumber: '字母和数字',
phone: '手机号码',
landline: '座机',
zipCode: '邮政编码',
IDCard: '身份证号',
ip: 'IP地址',
email: '邮箱',
link: '链接',
monetaryAmount: '货币金额',
custom: '自定义',
},
cmdb: cmdb_zh,
cs: cs_zh,
acl: acl_zh,

View File

@ -174,7 +174,8 @@ const cmdb_en = {
date: 'Date',
time: 'Time',
json: 'JSON',
event: 'Event'
event: 'Event',
reg: 'Regex'
},
components: {
unselectAttributes: 'Unselected',

View File

@ -174,7 +174,8 @@ const cmdb_zh = {
date: '日期',
time: '时间',
json: 'JSON',
event: '事件'
event: '事件',
reg: '正则校验'
},
components: {
unselectAttributes: '未选属性',

View File

@ -101,7 +101,6 @@
:cell-type="col.value_type === '2' ? 'string' : 'auto'"
:fixed="col.is_fixed ? 'left' : ''"
>
<!-- <template #edit="{row}"><a-input v-model="row[col.field]"></a-input></template> -->
<template #header>
<span class="vxe-handle">
<OpsMoveIcon
@ -110,7 +109,7 @@
<span>{{ col.title }}</span>
</span>
</template>
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
@ -147,18 +146,6 @@
</span>
</a-select-option>
</a-select>
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
:style="{ width: '100%', height: '32px' }"
v-model="row[col.field]"
:placeholder="$t('placeholder2')"
v-else-if="col.is_list"
:showArrow="false"
mode="tags"
class="ci-table-edit-select"
allowClear
>
</a-select>
</template>
<template
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
@ -293,7 +280,6 @@
</a-pagination>
</div>
<create-instance-form ref="create" @reload="reloadData" @submit="batchUpdate" />
<!-- <batch-update-relation :typeId="typeId" ref="batchUpdateRelation" @submit="batchUpdateRelation" /> -->
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
<BatchDownload ref="batchDownload" @batchDownload="batchDownload" />
<MetadataDrawer ref="metadataDrawer" />
@ -470,11 +456,6 @@ export default {
const regSort = /(?<=sort=).+/g
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
// if (exp) {
// exp = exp.replace(/(\:)/g, '$1*')
// exp = exp.replace(/(\,)/g, '*$1')
// }
// If the sorting is done by clicking on the table, the table will prevail.
let sort
if (sortByTable) {
sort = sortByTable
@ -483,7 +464,6 @@ export default {
}
const res = await searchCI({
q: `_type:${this.typeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
// q: `${this.mergeQ(queryParams)}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
count: this.pageSize,
page: this.currentPage,
sort,
@ -532,55 +512,17 @@ export default {
this.$refs['xTable'].getVxetableRef().setCheckboxRow(rows, true)
}
},
// mergeQ(params) {
// let q = `_type:${this.typeId}`
// Object.keys(params).forEach((key) => {
// if (!['pageNo', 'pageSize', 'sortField', 'sortOrder'].includes(key) && params[key] + '' !== '') {
// if (typeof params[key] === 'object' && params[key] && params[key].length > 1) {
// q += `,${key}:(${params[key].join(';')})`
// } else if (params[key]) {
// q += `,${key}:*${params[key]}*`
// }
// }
// })
// return q
// },
async loadPreferenceAttrList() {
const subscribed = await getSubscribeAttributes(this.typeId)
this.preferenceAttrList = subscribed.attributes // All columns that have been subscribed
},
onSelectChange() {
// const current = records.map((i) => i.ci_id || i._id)
// const cached = new Set(this.selectedRowKeys)
// if (checked) {
// current.forEach((i) => {
// cached.add(i)
// })
// } else {
// if (row) {
// cached.delete(row.ci_id || row._id)
// } else {
// this.instanceList.map((row) => {
// cached.delete(row.ci_id || row._id)
// })
// }
// }
const xTable = this.$refs.xTable.getVxetableRef()
const records = [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()]
this.selectedRowKeys = records.map((i) => i.ci_id || i._id)
},
onSelectRangeEnd({ records }) {
// const current = records.map((i) => i.ci_id || i._id)
// const cached = new Set(this.selectedRowKeys)
// current.forEach((i) => {
// cached.add(i)
// })
this.selectedRowKeys = records.map((i) => i.ci_id || i._id)
// this.setSelectRows()
},
reloadData() {
this.loadTableData()
@ -622,7 +564,7 @@ export default {
})
.catch((err) => {
console.log(err)
$table.revertData(row)
this.loadTableData()
})
}
this.columns.forEach((col) => {
@ -693,12 +635,22 @@ export default {
}
})
this.$refs.create.visible = false
const key = 'updatable'
let errorMsg = ''
for (let i = 0; i < this.selectedRowKeys.length; i++) {
await updateCI(this.selectedRowKeys[i], payload, false)
.then(() => {
successNum += 1
})
.catch(() => {
.catch((error) => {
errorMsg = errorMsg + '\n' + `${this.selectedRowKeys[i]}:${error.response?.data?.message ?? ''}`
this.$notification.warning({
key,
message: this.$t('warning'),
description: errorMsg,
duration: 0,
style: { whiteSpace: 'break-spaces' },
})
errorNum += 1
})
.finally(() => {

View File

@ -24,7 +24,9 @@
/>
</template>
<template v-if="parentsType && parentsType.length">
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{ $t('cmdb.menu.citypeRelation') }}</a-divider>
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{
$t('cmdb.menu.citypeRelation')
}}</a-divider>
<a-form>
<a-row :gutter="24" align="top" type="flex">
<a-col :span="12" v-for="item in parentsType" :key="item.id">
@ -40,7 +42,11 @@
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<a-input :placeholder="$t('cmdb.ci.tips1')" v-model="parentsForm[item.name].value" style="width: 50%" />
<a-input
:placeholder="$t('cmdb.ci.tips1')"
v-model="parentsForm[item.name].value"
style="width: 50%"
/>
</a-input-group>
</a-form-item>
</a-col>

View File

@ -60,7 +60,7 @@
</span>
</template>
<template v-else-if="attr.is_list">
<span> {{ ci[attr.name].join(',') }}</span>
<span> {{ ci[attr.name] && Array.isArray(ci[attr.name]) ? ci[attr.name].join(',') : ci[attr.name] }}</span>
</template>
<template v-else>{{ getName(ci[attr.name]) }}</template>
</span>
@ -105,23 +105,6 @@
</span>
</a-select-option>
</a-select>
<a-select
:style="{ width: '100%' }"
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required }],
},
]"
:placeholder="$t('placeholder2')"
v-else-if="attr.is_list"
mode="tags"
showSearch
allowClear
size="small"
:getPopupContainer="(trigger) => trigger.parentElement"
>
</a-select>
<a-input-number
size="small"
v-decorator="[
@ -131,7 +114,7 @@
},
]"
style="width: 100%"
v-else-if="attr.value_type === '0' || attr.value_type === '1'"
v-else-if="(attr.value_type === '0' || attr.value_type === '1') && !attr.is_list"
/>
<a-date-picker
size="small"
@ -144,22 +127,9 @@
style="width: 100%"
:format="attr.value_type === '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
:valueFormat="attr.value_type === '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
v-else-if="attr.value_type === '4' || attr.value_type === '3'"
v-else-if="(attr.value_type === '4' || attr.value_type === '3') && !attr.is_list"
:showTime="attr.value_type === '4' ? false : { format: 'HH:mm:ss' }"
/>
<!-- <a-input
size="small"
@focus="(e) => handleFocusInput(e, attr)"
v-decorator="[
attr.name,
{
validateTrigger: ['submit'],
rules: [{ required: attr.is_required }],
},
]"
style="width: 100%"
v-else-if="attr.value_type === '6'"
/> -->
<a-input
size="small"
v-decorator="[
@ -241,7 +211,9 @@ export default {
this.$nextTick(async () => {
if (this.attr.is_list && !this.attr.is_choice) {
this.form.setFieldsValue({
[`${this.attr.name}`]: this.ci[this.attr.name] || null,
[`${this.attr.name}`]: Array.isArray(this.ci[this.attr.name])
? this.ci[this.attr.name].join(',')
: this.ci[this.attr.name],
})
return
}

View File

@ -22,7 +22,12 @@
attr.name,
{
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : attr.is_list ? [] : null,
initialValue:
attr.default && attr.default.default
? attr.is_list
? attr.default.default.split(',')
: attr.default.default
: null,
},
]"
:placeholder="$t('placeholder2')"
@ -53,19 +58,18 @@
</span>
</a-select-option>
</a-select>
<a-select
<a-input
v-else-if="attr.is_list"
mode="tags"
:style="{ width: '100%' }"
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : attr.is_list ? [] : null,
initialValue: attr.default && attr.default.default ? attr.default.default : '',
},
]"
>
</a-select>
</a-input>
<a-input-number
v-decorator="[
attr.name,

View File

@ -13,7 +13,11 @@
<a-form :form="form" :layout="formLayout">
<a-divider style="font-size:14px;margin-top:6px;">{{ $t('cmdb.ciType.basicConfig') }}</a-divider>
<a-col :span="12">
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" :label="$t('cmdb.ciType.AttributeName')">
<a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:label="$t('cmdb.ciType.AttributeName')"
>
<a-input
:disabled="true"
name="name"
@ -35,12 +39,20 @@
</a-col>
<a-col
:span="12"
><a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" :label="$t('alias')">
><a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:label="$t('alias')"
>
<a-input name="alias" v-decorator="['alias', { rules: [] }]" /> </a-form-item
></a-col>
<a-col
:span="12"
><a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" :label="$t('cmdb.ciType.DataType')">
><a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:label="$t('cmdb.ciType.DataType')"
>
<a-select
:disabled="true"
name="value_type"
@ -59,13 +71,12 @@
:label="$t('cmdb.ciType.defaultValue')"
>
<template>
<a-select
<a-input
v-if="form.getFieldValue('is_list')"
mode="tags"
:style="{ width: '100%' }"
v-decorator="['default_value', { rules: [{ required: false }] }]"
>
</a-select>
</a-input>
<a-select
v-decorator="['default_value', { rules: [{ required: false }] }]"
mode="tags"
@ -160,7 +171,11 @@
</a-form-item>
</a-col>
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item :label-col="{ span: 8 }" :wrapper-col="horizontalFormItemLayout.wrapperCol" :label="$t('cmdb.ciType.unique')">
<a-form-item
:label-col="{ span: 8 }"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
@ -282,6 +297,11 @@
</a-col>
<a-divider style="font-size:14px;margin-top:6px;">{{ $t('cmdb.ciType.advancedSettings') }}</a-divider>
<a-row>
<a-col :span="24" v-if="!['6'].includes(currentValueType)">
<a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 12 }" :label="$t('cmdb.ciType.reg')">
<RegSelect :isShowErrorMsg="false" v-model="re_check" :limitedFormat="getLimitedFormat()" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }" :label="$t('cmdb.ciType.font')">
<FontArea ref="fontArea" />
@ -303,11 +323,7 @@
<span
style="position:relative;white-space:pre;"
>{{ $t('cmdb.ciType.computedAttribute') }}
<a-tooltip
:title="
$t('cmdb.ciType.computedAttributeTips')
"
>
<a-tooltip :title="$t('cmdb.ciType.computedAttributeTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
type="question-circle"
@ -355,6 +371,8 @@ import _ from 'lodash'
import moment from 'moment'
import vueJsonEditor from 'vue-json-editor'
import {
// createAttribute,
// createCITypeAttributes,
updateAttributeById,
updateCITypeAttributesById,
canDefineComputed,
@ -364,10 +382,11 @@ import { valueTypeMap } from '../../utils/const'
import ComputedArea from './computedArea.vue'
import PreValueArea from './preValueArea.vue'
import FontArea from './fontArea.vue'
import RegSelect from '@/components/RegexSelect'
export default {
name: 'AttributeEditForm',
components: { ComputedArea, PreValueArea, vueJsonEditor, FontArea },
components: { ComputedArea, PreValueArea, vueJsonEditor, FontArea, RegSelect },
props: {
CITypeId: {
type: Number,
@ -395,6 +414,7 @@ export default {
isShowComputedArea: false,
defaultForDatetime: '',
re_check: {},
}
},
@ -517,15 +537,30 @@ export default {
})
}
console.log(_record)
if (!['6'].includes(_record.value_type) && _record.re_check) {
this.re_check = {
value: _record.re_check,
}
} else {
this.re_check = {}
}
if (_record.default) {
this.$nextTick(() => {
if (_record.value_type === '0') {
this.form.setFieldsValue({
default_value: _record.default.default ? [_record.default.default] : [],
})
if (_record.is_list) {
this.$nextTick(() => {
this.form.setFieldsValue({
default_value: _record.default.default ? _record.default.default : '',
})
})
} else {
this.form.setFieldsValue({
default_value: _record.default.default ? [_record.default.default] : [],
})
}
} else if (_record.value_type === '6') {
this.default_value_json = _record?.default?.default || null
} else if (_record.value_type === '3' || _record.value_type === '4') {
} else if ((_record.value_type === '3' || _record.value_type === '4') && !_record.is_list) {
if (_record?.default?.default === '$created_at' || _record?.default?.default === '$updated_at') {
this.defaultForDatetime = _record.default.default
this.form.setFieldsValue({
@ -584,6 +619,9 @@ export default {
await this.form.validateFields(async (err, values) => {
if (!err) {
console.log('Received values of form: ', values)
// if (values.choice_value) {
// values.choice_value = values.choice_value.split('\n')
// }
if (this.record.is_required !== values.is_required || this.record.default_show !== values.default_show) {
console.log('changed is_required')
@ -598,7 +636,11 @@ export default {
delete values['is_required']
const { default_value } = values
if (values.value_type === '0' && default_value) {
values.default = { default: default_value[0] || null }
if (values.is_list) {
values.default = { default: default_value || null }
} else {
values.default = { default: default_value[0] || null }
}
} else if (values.value_type === '6') {
if (this.default_value_json_right) {
values.default = { default: this.default_value_json }
@ -606,13 +648,13 @@ export default {
values.default = { default: null }
}
} else if (default_value || default_value === 0) {
if (values.value_type === '3') {
if (values.value_type === '3' && !values.is_list) {
if (default_value === '$created_at' || default_value === '$updated_at') {
values.default = { default: default_value }
} else {
values.default = { default: moment(default_value).format('YYYY-MM-DD HH:mm:ss') }
}
} else if (values.value_type === '4') {
} else if (values.value_type === '4' && !values.is_list) {
values.default = { default: moment(default_value).format('YYYY-MM-DD') }
} else {
values.default = { default: default_value }
@ -641,6 +683,9 @@ export default {
values.value_type = '2'
values.is_link = true
}
if (values.value_type !== '6') {
values.re_check = this.re_check?.value ?? null
}
if (values.id) {
await this.updateAttribute(values.id, { ...values, option: { fontOptions } }, isCalcComputed)
} else {
@ -698,6 +743,21 @@ export default {
async handleCalcComputed() {
await this.handleSubmit(true)
},
getLimitedFormat() {
if (['0'].includes(this.currentValueType)) {
return ['number', 'phone', 'landline', 'zipCode', 'IDCard', 'monetaryAmount', 'custom']
}
if (['1'].includes(this.currentValueType)) {
return ['number', 'monetaryAmount', 'custom']
}
if (['3', '4', '5'].includes(this.currentValueType)) {
return ['custom']
}
if (this.currentValueType === '8') {
return ['link', 'custom']
}
return []
},
},
watch: {},
}

View File

@ -21,7 +21,10 @@
message: $t('cmdb.ciType.attributeNameTips'),
pattern: RegExp('^(?!\\d)[a-zA-Z_0-9]+$'),
},
{ message: $t('cmdb.ciType.buildinAttribute'), pattern: RegExp('^(?!(id|_id|ci_id|type|_type|ci_type)$).*$') },
{
message: $t('cmdb.ciType.buildinAttribute'),
pattern: RegExp('^(?!(id|_id|ci_id|type|_type|ci_type)$).*$'),
},
],
},
]"
@ -59,13 +62,12 @@
:label="$t('cmdb.ciType.defaultValue')"
>
<template>
<a-select
<a-input
v-if="form.getFieldValue('is_list')"
mode="tags"
:style="{ width: '100%' }"
v-decorator="['default_value', { rules: [{ required: false }] }]"
>
</a-select>
</a-input>
<a-input-number
style="width: 100%"
v-else-if="currentValueType === '1'"
@ -162,7 +164,11 @@
</a-form-item>
</a-col>
<a-col :span="6" v-if="currentValueType !== '6' && currentValueType !== '7'">
<a-form-item :label-col="{ span: 8 }" :wrapper-col="horizontalFormItemLayout.wrapperCol" :label="$t('cmdb.ciType.unique')">
<a-form-item
:label-col="{ span: 8 }"
:wrapper-col="horizontalFormItemLayout.wrapperCol"
:label="$t('cmdb.ciType.unique')"
>
<a-switch
:disabled="isShowComputedArea"
@change="onChange"
@ -280,6 +286,11 @@
</a-col>
<a-divider style="font-size:14px;margin-top:6px;">{{ $t('cmdb.ciType.advancedSettings') }}</a-divider>
<a-row>
<a-col :span="24" v-if="!['6'].includes(currentValueType)">
<a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 12 }" :label="$t('cmdb.ciType.reg')">
<RegSelect :isShowErrorMsg="false" v-model="re_check" :limitedFormat="getLimitedFormat()" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }" :label="$t('cmdb.ciType.font')">
<FontArea ref="fontArea" />
@ -296,11 +307,7 @@
<span
style="position:relative;white-space:pre;"
>{{ $t('cmdb.ciType.computedAttribute') }}
<a-tooltip
:title="
$t('cmdb.ciType.computedAttributeTips')
"
>
<a-tooltip :title="$t('cmdb.ciType.computedAttributeTips')">
<a-icon
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
type="question-circle"
@ -340,6 +347,7 @@ import { valueTypeMap } from '../../utils/const'
import ComputedArea from './computedArea.vue'
import PreValueArea from './preValueArea.vue'
import FontArea from './fontArea.vue'
import RegSelect from '@/components/RegexSelect'
export default {
name: 'CreateNewAttribute',
@ -348,6 +356,7 @@ export default {
PreValueArea,
vueJsonEditor,
FontArea,
RegSelect,
},
props: {
hasFooter: {
@ -374,6 +383,8 @@ export default {
isShowComputedArea: false,
defaultForDatetime: '',
re_check: {},
}
},
computed: {
@ -397,8 +408,12 @@ export default {
const data = { is_required, default_show }
delete values.is_required
delete values.default_show
if (values.value_type === '0' && default_value && default_value.length) {
values.default = { default: default_value[0] }
if (values.value_type === '0' && default_value) {
if (values.is_list) {
values.default = { default: default_value || null }
} else {
values.default = { default: default_value[0] || null }
}
} else if (values.value_type === '6') {
if (this.default_value_json_right) {
values.default = { default: this.default_value_json }
@ -406,13 +421,13 @@ export default {
values.default = { default: null }
}
} else if (default_value || default_value === 0) {
if (values.value_type === '3') {
if (values.value_type === '3' && !values.is_list) {
if (default_value === '$created_at' || default_value === '$updated_at') {
values.default = { default: default_value }
} else {
values.default = { default: moment(default_value).format('YYYY-MM-DD HH:mm:ss') }
}
} else if (values.value_type === '4') {
} else if (values.value_type === '4' && !values.is_list) {
values.default = { default: moment(default_value).format('YYYY-MM-DD') }
} else {
values.default = { default: default_value }
@ -449,6 +464,9 @@ export default {
values.value_type = '2'
values.is_link = true
}
if (values.value_type !== '6') {
values.re_check = this.re_check?.value ?? null
}
const { attr_id } = await createAttribute({ ...values, option: { fontOptions } })
this.form.resetFields()
@ -539,6 +557,21 @@ export default {
default_value: key,
})
},
getLimitedFormat() {
if (['0'].includes(this.currentValueType)) {
return ['number', 'phone', 'landline', 'zipCode', 'IDCard', 'monetaryAmount', 'custom']
}
if (['1'].includes(this.currentValueType)) {
return ['number', 'monetaryAmount', 'custom']
}
if (['3', '4', '5'].includes(this.currentValueType)) {
return ['custom']
}
if (this.currentValueType === '8') {
return ['link', 'custom']
}
return []
},
},
}
</script>

View File

@ -37,7 +37,7 @@
>{{ $t('cmdb.ciType.attributeLibray') }}</a
>
<a-dropdown v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')">
<a><ops-icon type="ops-menu" /></a>
<a><ops-icon type="ops-menu"/></a>
<a-menu slot="overlay">
<a-menu-item key="0">
<a-upload
@ -48,12 +48,15 @@
action="/api/v0.1/ci_types/template/import/file"
@change="changeUploadFile"
>
<a><a-icon type="upload" /></a><a> {{ $t('upload') }}</a>
<a><a-icon type="upload"/></a><a> {{ $t('upload') }}</a>
</a-upload>
</a-menu-item>
<a-menu-item key="1">
<a-space>
<a href="/api/v0.1/ci_types/template/export/file"><a-icon type="download" /> {{ $t('download') }}</a>
<a
href="/api/v0.1/ci_types/template/export/file"
><a-icon type="download" /> {{ $t('download') }}</a
>
</a-space>
</a-menu-item>
</a-menu>
@ -63,9 +66,11 @@
<draggable class="ci-types-left-content" :list="CITypeGroups" @end="handleChangeGroups" filter=".undraggable">
<div v-for="g in CITypeGroups" :key="g.id || g.name">
<div
:class="`${currentGId === g.id && !currentCId ? 'selected' : ''} ci-types-left-group ${
g.id === -1 ? 'undraggable' : ''
}`"
:class="
`${currentGId === g.id && !currentCId ? 'selected' : ''} ci-types-left-group ${
g.id === -1 ? 'undraggable' : ''
}`
"
@click="handleClickGroup(g.id)"
>
<div>
@ -78,16 +83,16 @@
<a-space>
<a-tooltip>
<template slot="title">{{ $t('cmdb.ciType.addCITypeInGroup') }}</template>
<a><a-icon type="plus" @click="handleCreate(g)" /></a>
<a><a-icon type="plus" @click="handleCreate(g)"/></a>
</a-tooltip>
<template v-if="g.id !== -1">
<a-tooltip>
<template slot="title">{{ $t('cmdb.ciType.editGroup') }}</template>
<a><a-icon type="edit" @click="handleEditGroup(g)" /></a>
<a><a-icon type="edit" @click="handleEditGroup(g)"/></a>
</a-tooltip>
<a-tooltip>
<template slot="title">{{ $t('cmdb.ciType.deleteGroup') }}</template>
<a style="color: red"><a-icon type="delete" @click="handleDeleteGroup(g)" /></a>
<a style="color: red"><a-icon type="delete" @click="handleDeleteGroup(g)"/></a>
</a-tooltip>
</template>
</a-space>
@ -130,14 +135,15 @@
</div>
<span class="ci-types-left-detail-title">{{ ci.alias || ci.name }}</span>
<a-space class="ci-types-left-detail-action">
<a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)" /></a>
<a><a-icon type="edit" @click="(e) => handleEdit(e, ci)" /></a>
<a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
<a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
<a
v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"
@click="(e) => handleDownloadCiType(e, ci)">
<a-icon type="download"/>
@click="(e) => handleDownloadCiType(e, ci)"
>
<a-icon type="download" />
</a>
<a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete" /></a>
<a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
</a-space>
</div>
</draggable>
@ -242,6 +248,11 @@
:filter-method="filterOption"
v-decorator="['unique_key', { rules: [{ required: true, message: $t('cmdb.ciType.uniqueKeySelect') }] }]"
:placeholder="$t('placeholder2')"
@visible-change="
() => {
filterInput = ''
}
"
>
<el-option
:key="item.id"
@ -549,6 +560,7 @@ export default {
})
},
onClose() {
this.filterInput = ''
this.form.resetFields()
this.drawerVisible = false
},
@ -739,7 +751,7 @@ export default {
const x = new XMLHttpRequest()
x.open('GET', `/api/v0.1/ci_types/${ci.id}/template/export`, true)
x.responseType = 'blob'
x.onload = function (e) {
x.onload = function(e) {
const url = window.URL.createObjectURL(x.response)
const a = document.createElement('a')
a.href = url

View File

@ -58,13 +58,9 @@
/>
<div class="relation-views-right-bar">
<a-space>
<a-button
v-if="isLeaf"
type="primary"
size="small"
@click="$refs.create.handleOpen(true, 'create')"
>{{ $t('create') }}</a-button
>
<a-button v-if="isLeaf" type="primary" size="small" @click="$refs.create.handleOpen(true, 'create')">{{
$t('create')
}}</a-button>
<div class="ops-list-batch-action" v-if="isLeaf && isShowBatchIcon">
<template v-if="selectedRowKeys.length">
@ -135,7 +131,7 @@
{{ col.title }}</span
>
</template>
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
@ -172,18 +168,6 @@
</span>
</a-select-option>
</a-select>
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
:style="{ width: '100%', height: '32px' }"
v-model="row[col.field]"
:placeholder="$t('placeholder2')"
v-else-if="col.is_list"
:showArrow="false"
mode="tags"
class="ci-table-edit-select"
allowClear
>
</a-select>
</template>
<template
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
@ -364,7 +348,7 @@ import {
} from '@/modules/cmdb/api/CIRelation'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { searchCI2, updateCI, deleteCI, searchCI } from '@/modules/cmdb/api/ci'
import { searchCI2, updateCI, deleteCI } from '@/modules/cmdb/api/ci'
import { getCITypes } from '../../api/CIType'
import { roleHasPermissionToGrant } from '@/modules/acl/api/permission'
import { searchResourceType } from '@/modules/acl/api/resource'
@ -1018,11 +1002,7 @@ export default {
this.$confirm({
title: that.$t('warning'),
content: (h) => (
<div>
{that.$t('confirmDelete2', { name: Object.values(firstCIObj)[0] })}
</div>
),
content: (h) => <div>{that.$t('confirmDelete2', { name: Object.values(firstCIObj)[0] })}</div>,
onOk() {
deleteCIRelationView(_tempTreeParent[0], _tempTree[0], { ancestor_ids }).then((res) => {
that.$message.success(that.$t('deleteSuccess'))
@ -1048,7 +1028,7 @@ export default {
title: that.$t('warning'),
content: (h) => (
<div>
{that.$t('cmdb.serviceTreedeleteRelationConfirm', { name: currentShowType.alias || currentShowType.name })}
{that.$t('cmdb.serviceTree.deleteRelationConfirm', { name: currentShowType.alias || currentShowType.name })}
</div>
),
onOk() {

View File

@ -182,7 +182,7 @@
{{ col.title }}</span
>
</template>
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
@ -219,18 +219,6 @@
</span>
</a-select-option>
</a-select>
<a-select
:getPopupContainer="(trigger) => trigger.parentElement"
:style="{ width: '100%', height: '32px' }"
v-model="row[col.field]"
:placeholder="$t('placeholder2')"
v-else-if="col.is_list"
:showArrow="false"
mode="tags"
class="ci-table-edit-select"
allowClear
>
</a-select>
</template>
<template
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
@ -1138,12 +1126,22 @@ export default {
}
})
this.$refs.create.visible = false
const key = 'updatable'
let errorMsg = ''
for (let i = 0; i < this.selectedRowKeys.length; i++) {
await updateCI(this.selectedRowKeys[i], payload, false)
.then(() => {
successNum += 1
})
.catch(() => {
.catch((error) => {
errorMsg = errorMsg + '\n' + `${this.selectedRowKeys[i]}:${error.response?.data?.message ?? ''}`
this.$notification.warning({
key,
message: this.$t('warning'),
description: errorMsg,
duration: 0,
style: { whiteSpace: 'break-spaces' },
})
errorNum += 1
})
.finally(() => {