From 26d8154db87b4031eaa721e865d8aba258fb9092 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 27 Apr 2023 16:46:38 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20json=20m2m?= =?UTF-8?q?=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FormFields/JSONManyToManySelect.vue | 77 +++++++++++++++++++ src/components/FormFields/index.js | 7 +- .../CommandFilterAclCreateUpdate.vue | 30 ++++++-- .../components/AssetAccounts.vue | 65 ++++++++++------ 4 files changed, 150 insertions(+), 29 deletions(-) create mode 100644 src/components/FormFields/JSONManyToManySelect.vue diff --git a/src/components/FormFields/JSONManyToManySelect.vue b/src/components/FormFields/JSONManyToManySelect.vue new file mode 100644 index 000000000..15f783326 --- /dev/null +++ b/src/components/FormFields/JSONManyToManySelect.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/FormFields/index.js b/src/components/FormFields/index.js index eb69b3167..d9b5f540c 100644 --- a/src/components/FormFields/index.js +++ b/src/components/FormFields/index.js @@ -15,6 +15,7 @@ import UploadSecret from './UploadSecret' import WeekCronSelect from './WeekCronSelect' import NestedObjectSelect2 from './NestedObjectSelect2' import DatetimeRangePicker from './DatetimeRangePicker' +import JSONManyToManySelect from './JSONManyToManySelect' export default { Text, @@ -33,7 +34,8 @@ export default { UploadSecret, WeekCronSelect, NestedObjectSelect2, - DatetimeRangePicker + DatetimeRangePicker, + JSONManyToManySelect } export { @@ -53,5 +55,6 @@ export { UploadSecret, WeekCronSelect, NestedObjectSelect2, - DatetimeRangePicker + DatetimeRangePicker, + JSONManyToManySelect } diff --git a/src/views/acl/CommandAcl/CommandFilterAcl/CommandFilterAclCreateUpdate.vue b/src/views/acl/CommandAcl/CommandFilterAcl/CommandFilterAclCreateUpdate.vue index 63a82a308..4ad0e6eed 100644 --- a/src/views/acl/CommandAcl/CommandFilterAcl/CommandFilterAclCreateUpdate.vue +++ b/src/views/acl/CommandAcl/CommandFilterAcl/CommandFilterAclCreateUpdate.vue @@ -1,8 +1,8 @@ + + diff --git a/src/components/FormFields/JSONManyToManySelect/index.vue b/src/components/FormFields/JSONManyToManySelect/index.vue index dcedf2f5f..77f1586ef 100644 --- a/src/components/FormFields/JSONManyToManySelect/index.vue +++ b/src/components/FormFields/JSONManyToManySelect/index.vue @@ -14,7 +14,7 @@ - + @@ -25,6 +25,7 @@ import Select2 from '../Select2.vue' import DataTable from '@/components/DataTable/index.vue' import Dialog from '@/components/Dialog/index.vue' import DataForm from '@/components/DataForm/index.vue' +import ValueFormatter from './ValueFormatter.vue' import ValueField from './ValueField.vue' export default { @@ -61,14 +62,24 @@ export default { { label: this.$t('common.Startswith'), value: 'startswith' }, { label: this.$t('common.Endswith'), value: 'endswith' }, { label: this.$t('common.Regex'), value: 'regex' }, - { label: this.$t('common.IPMatch'), value: 'ip_in' } + { label: this.$t('common.BelongTo'), value: 'm2m' }, + { label: this.$t('common.IPMatch'), value: 'ip_in' }, + { label: this.$t('common.GreatEqualThan'), value: 'gte' }, + { label: this.$t('common.LessEqualThan'), value: 'lte' } ] - const attrRelOptions = [ - { label: this.$t('common.RelAnd'), value: 'and' }, - { label: this.$t('common.RelOr'), value: 'or' }, - { label: this.$t('common.RelNot'), value: 'not' } - ] - const attrNameOptions = this.attrs.map(attr => ({ label: attr.label, value: attr.name })) + + const strMatchValues = ['exact', 'not', 'in', 'contains', 'startswith', 'endswith', 'regex'] + const typeMatchMapper = { + str: strMatchValues, + bool: ['exact', 'not'], + m2m: ['m2m'], + ip: strMatchValues + ['ip_in'], + int: strMatchValues + ['gte', 'lte'], + select: ['in'] + } + attrMatchOptions.forEach((option) => { + option.hidden = !typeMatchMapper[this.attrs[0].type || 'str'].includes(option.value) + }) const tableFormatter = (colName) => { return (row, col, cellValue) => { const value = cellValue @@ -77,8 +88,6 @@ export default { return this.attrs.find(attr => attr.name === value)?.label || value case 'match': return attrMatchOptions.find(opt => opt.value === value).label || value - case 'rel': - return attrRelOptions.find(opt => opt.value === value)?.label || value case 'value': return Array.isArray(value) ? value.join(', ') : value default: @@ -98,8 +107,7 @@ export default { columns: [ { prop: 'name', label: this.$t('common.AttrName'), formatter: tableFormatter('name') }, { prop: 'match', label: this.$t('common.Match'), formatter: tableFormatter('match') }, - { prop: 'value', label: this.$t('common.AttrValue'), formatter: tableFormatter('value') }, - { prop: 'rel', label: this.$t('common.Relation'), formatter: tableFormatter('rel') }, + { prop: 'value', label: this.$t('common.AttrValue'), formatter: ValueFormatter, formatterArgs: { attrs: this.attrs }}, { prop: 'action', label: this.$t('common.Action'), formatter: (row, col, cellValue, index) => { return (
@@ -126,46 +134,48 @@ export default { }, formConfig: { // 为了方便更新,避免去取 fields 的索引 - attrNameOptions: attrNameOptions, hasSaveContinue: false, editRowIndex: -1, form: {}, fields: [ { id: 'name', - label: '属性', + label: this.$t('common.AttrName'), type: 'select', - options: attrNameOptions + options: this.attrs.map(attr => ({ label: attr.label, value: attr.name })), + on: { + change: ([val], updateForm) => { + const attr = this.attrs.find(attr => attr.name === val) + if (!attr) return + this.formConfig.fields[2].el.attr = attr + const attrType = attr.type || 'str' + const matchSupports = typeMatchMapper[attrType] + attrMatchOptions.forEach((option) => { + option.hidden = !matchSupports.includes(option.value) + }) + setTimeout(() => updateForm({ match: matchSupports[0] }), 0.1) + } + } }, { id: 'match', - label: '匹配方式', + label: this.$t('common.Match'), type: 'select', options: attrMatchOptions, on: { change: ([value], updateForm) => { this.formConfig.fields[2].el.match = value - if (['in', 'ip_in'].includes(value)) { - updateForm({ value: [] }) - } else { - updateForm({ value: '' }) - } } } }, { id: 'value', - label: '值', + label: this.$t('common.AttrValue'), component: ValueField, el: { - match: 'exact' + match: attrMatchOptions[0].value, + attr: this.attrs[0] } - }, - { - id: 'rel', - label: '关系', - type: 'radio-group', - options: attrRelOptions } ] }, @@ -209,9 +219,8 @@ export default { } }, setAttrNameOptionUsed() { - const options = this.formConfig.attrNameOptions + const options = this.formConfig.fields[0].options const used = this.tableConfig.totalData.map(attr => attr.name) - console.log('Used: ', used) options.forEach(opt => { if (used.includes(opt.value)) { opt.disabled = true @@ -219,11 +228,11 @@ export default { delete opt.disabled } }) - console.log('Options: ', options) }, handleAttrEdit({ row, index }) { return () => { this.formConfig.editRowIndex = index + this.formConfig.fields[2].el.attr = this.attrs.find(attr => attr.name === row.name) this.formConfig.form = Object.assign({ index }, row) this.setAttrNameOptionUsed() this.visible = true @@ -236,6 +245,7 @@ export default { }, handleAttrAdd() { this.formConfig.form = this.getDefaultAttrForm() + this.formConfig.fields[2].el.attr = this.attrs.find(attr => attr.name === this.formConfig.form.name) this.setAttrNameOptionUsed() this.visible = true }, diff --git a/src/i18n/langs/zh.json b/src/i18n/langs/zh.json index fa44d833b..a9fbeb85d 100644 --- a/src/i18n/langs/zh.json +++ b/src/i18n/langs/zh.json @@ -456,6 +456,11 @@ "ReLoginErr": "登录时长已超过 5 分钟,请重新登录" }, "common": { + "GreatEqualThan": "大于等于", + "LessEqualThan": "小于等于", + "BelongTo": "所属", + "Email": "邮箱", + "IsActive": "激活", "All": "所有", "Spec": "指定", "SelectByAttr": "属性筛选", diff --git a/src/views/assets/const.js b/src/views/assets/const.js index 5e83cedbd..88d3e58cc 100644 --- a/src/views/assets/const.js +++ b/src/views/assets/const.js @@ -140,6 +140,23 @@ export const assetFieldsMeta = (vm) => { } export const assetJSONSelectMeta = (vm) => { + const categories = [] + const types = [] + const protocols = [] + vm.$axios.get('/api/v1/assets/categories/').then((res) => { + const _types = [] + const _protocols = [] + for (const category of res) { + categories.push({ value: category.value, label: category.label }) + _types.push(...category.types.map(item => ({ value: item.value, label: item.label }))) + for (const type of category.types) { + _protocols.push(...type.constraints.protocols?.map(item => ({ value: item.name, label: item.name.toUpperCase() }))) + } + } + types.push(..._.uniqBy(_types, 'value')) + protocols.push(..._.uniqBy(_protocols, 'value')) + }) + return { component: JSONManyToManySelect, el: { @@ -160,7 +177,63 @@ export const assetJSONSelectMeta = (vm) => { }, { name: 'address', - label: vm.$t('assets.Address') + label: vm.$t('assets.Address'), + type: 'ip' + }, + { + name: 'nodes', + label: vm.$t('assets.Node'), + type: 'm2m', + el: { + url: '/api/v1/assets/nodes/', + ajax: { + transformOption: (item) => { + return { label: item.full_value, value: item.id } + } + } + } + }, + { + name: 'platform', + label: vm.$t('assets.Platform'), + type: 'm2m', + el: { + multiple: false, + url: '/api/v1/assets/platforms/' + } + }, + { + name: 'category', + label: vm.$t('assets.Category'), + type: 'select', + el: { + options: categories + } + }, + { + name: 'type', + label: vm.$t('assets.Type'), + type: 'select', + el: { + options: types + } + }, + { + name: 'protocols', + label: vm.$t('assets.Protocols'), + type: 'select', + el: { + options: protocols + } + }, + { + name: 'labels', + label: vm.$t('assets.Label'), + type: 'm2m', + el: { + multiple: true, + url: '/api/v1/assets/labels/' + } } ] } diff --git a/src/views/perms/AssetPermission/components/AccountFormatter.vue b/src/views/perms/AssetPermission/components/AccountFormatter.vue index 84cdd180c..77ad5b9b6 100644 --- a/src/views/perms/AssetPermission/components/AccountFormatter.vue +++ b/src/views/perms/AssetPermission/components/AccountFormatter.vue @@ -176,6 +176,7 @@ export default { }, mounted() { this.initDefaultChoice() + this.$emit('input', this.value) }, methods: { initDefaultChoice() { diff --git a/src/views/users/const.js b/src/views/users/const.js index 0a3a529f5..9c0fcb449 100644 --- a/src/views/users/const.js +++ b/src/views/users/const.js @@ -22,6 +22,55 @@ export const userJSONSelectMeta = (vm) => { { name: 'username', label: vm.$t('common.Username') + }, + { + name: 'email', + label: vm.$t('common.Email') + }, + { + name: 'comment', + label: vm.$t('common.Comment') + }, + { + name: 'is_active', + label: vm.$t('common.IsActive'), + type: 'bool' + }, + { + name: 'system_roles', + label: vm.$t('users.SystemRoles'), + type: 'm2m', + el: { + url: '/api/v1/rbac/system-roles/?fields_size=mini', + ajax: { + transformOption: (item) => { + return { label: item.display_name, value: item.id } + } + }, + displayField: 'display_name' + } + }, + { + name: 'org_roles', + label: vm.$t('users.OrgRoles'), + type: 'm2m', + el: { + url: '/api/v1/rbac/org-roles/', + ajax: { + transformOption: (item) => { + return { label: item.display_name, value: item.id } + } + }, + displayField: 'display_name' + } + }, + { + name: 'groups', + label: vm.$t('users.UserGroups'), + type: 'm2m', + el: { + url: '/api/v1/users/groups/?fields_size=mini' + } } ] } From 3a5aa7bf906a45595a463c726495acb2dde7e7fb Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 18 May 2023 17:31:56 +0800 Subject: [PATCH 08/12] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20Json=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Dialog/index.vue | 4 + .../FormFields/JSONManyToManySelect/index.vue | 80 +++++++++++++++++-- src/i18n/langs/zh.json | 3 + src/views/assets/const.js | 10 ++- src/views/users/const.js | 11 ++- 5 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/components/Dialog/index.vue b/src/components/Dialog/index.vue index b74c8284c..10d4ec13f 100644 --- a/src/components/Dialog/index.vue +++ b/src/components/Dialog/index.vue @@ -94,6 +94,10 @@ export default { border-radius: 0.3em; max-width: 1500px; + .el-icon-circle-check { + display: none; + } + &__header { box-sizing: border-box; padding: 15px 22px; diff --git a/src/components/FormFields/JSONManyToManySelect/index.vue b/src/components/FormFields/JSONManyToManySelect/index.vue index 77f1586ef..9c5015850 100644 --- a/src/components/FormFields/JSONManyToManySelect/index.vue +++ b/src/components/FormFields/JSONManyToManySelect/index.vue @@ -12,11 +12,31 @@ {{ $t('common.Add') }} + + {{ $t('common.MatchedCount') }}: + {{ attrMatchCount }} +
- + + + + + @@ -27,10 +47,12 @@ import Dialog from '@/components/Dialog/index.vue' import DataForm from '@/components/DataForm/index.vue' import ValueFormatter from './ValueFormatter.vue' import ValueField from './ValueField.vue' +import { setUrlParam } from '@/utils/common' +import ListTable from '@/components/ListTable/index.vue' export default { name: 'JSONManyToManySelect', - components: { DataTable, Select2, Dialog, DataForm }, + components: { DataTable, Select2, Dialog, DataForm, ListTable }, props: { value: { type: Object, @@ -51,6 +73,10 @@ export default { resource: { type: String, default: '' + }, + attrTableColumns: { + type: Array, + default: () => (['name']) } }, data() { @@ -97,6 +123,25 @@ export default { } return { iValue: Object.assign({ type: 'all' }, this.value), + attrMatchTableConfig: { + headerActions: { + hasRightActions: false, + hasCreate: false, + hasMoreActions: false + }, + tableConfig: { + url: this.select2.url, + columns: this.attrs.filter(item => item.inTable).map(item => { + return { + prop: item.name, + label: item.label + } + }) + } + }, + attrFormVisible: false, + attrMatchCount: 0, + attrMatchTableVisible: false, ids: this.value.ids || [], types: [ { name: 'all', label: this.$t('common.All') + this.resource }, @@ -178,15 +223,36 @@ export default { } } ] - }, - visible: false + } } }, mounted() { this.formConfig.form = this.getDefaultAttrForm() + if (this.value.type === 'attrs') { + this.getAttrsCount() + } this.$emit('input', this.iValue) }, methods: { + showAttrMatchTable() { + this.attrMatchTableVisible = true + const attrFilter = this.getAttrFilterKey() + this.attrMatchTableConfig.tableConfig.url = setUrlParam(this.select2.url, 'attr_rules', attrFilter) + }, + getAttrFilterKey() { + let attrFilter = { type: 'attrs', attrs: this.tableConfig.totalData } + attrFilter = encodeURIComponent(btoa(JSON.stringify(attrFilter))) + return attrFilter + }, + getAttrsCount() { + const attrFilter = this.getAttrFilterKey() + console.log('attrFilter', attrFilter) + let url = setUrlParam(this.select2.url, 'attr_rules', attrFilter) + url = setUrlParam(url, 'limit', 1) + return this.$axios.get(url).then(res => { + this.attrMatchCount = res.count + }) + }, onAttrDialogConfirm(form) { const allAttrs = this.tableConfig.totalData if (this.formConfig.editRowIndex !== -1) { @@ -201,7 +267,7 @@ export default { allAttrs.splice(setIndex, 1, Object.assign({}, form)) } form = this.getDefaultAttrForm() - this.visible = false + this.attrFormVisible = false }, getDefaultAttrForm() { const attrKeys = this.attrs.map(attr => attr.name) @@ -235,7 +301,7 @@ export default { this.formConfig.fields[2].el.attr = this.attrs.find(attr => attr.name === row.name) this.formConfig.form = Object.assign({ index }, row) this.setAttrNameOptionUsed() - this.visible = true + this.attrFormVisible = true } }, handleAttrDelete({ index }) { @@ -247,7 +313,7 @@ export default { this.formConfig.form = this.getDefaultAttrForm() this.formConfig.fields[2].el.attr = this.attrs.find(attr => attr.name === this.formConfig.form.name) this.setAttrNameOptionUsed() - this.visible = true + this.attrFormVisible = true }, handleTypeChange(val) { switch (val) { diff --git a/src/i18n/langs/zh.json b/src/i18n/langs/zh.json index a9fbeb85d..522413ee4 100644 --- a/src/i18n/langs/zh.json +++ b/src/i18n/langs/zh.json @@ -456,6 +456,9 @@ "ReLoginErr": "登录时长已超过 5 分钟,请重新登录" }, "common": { + "MatchedCount": "匹配条目", + "SelectAttrs": "选择属性", + "MatchResult": "匹配结果", "GreatEqualThan": "大于等于", "LessEqualThan": "小于等于", "BelongTo": "所属", diff --git a/src/views/assets/const.js b/src/views/assets/const.js index 88d3e58cc..9a60e5641 100644 --- a/src/views/assets/const.js +++ b/src/views/assets/const.js @@ -163,8 +163,8 @@ export const assetJSONSelectMeta = (vm) => { value: [], resource: vm.$t('assets.Asset'), select2: { + url: '/api/v1/assets/assets/', ajax: { - url: '/api/v1/assets/assets/?fields_size=mini', transformOption: (item) => { return { label: item.name + '(' + item.address + ')', value: item.id } } @@ -173,12 +173,14 @@ export const assetJSONSelectMeta = (vm) => { attrs: [ { name: 'name', - label: vm.$t('common.Name') + label: vm.$t('common.Name'), + inTable: true }, { name: 'address', label: vm.$t('assets.Address'), - type: 'ip' + type: 'ip', + inTable: true }, { name: 'nodes', @@ -206,6 +208,7 @@ export const assetJSONSelectMeta = (vm) => { name: 'category', label: vm.$t('assets.Category'), type: 'select', + inTable: true, el: { options: categories } @@ -214,6 +217,7 @@ export const assetJSONSelectMeta = (vm) => { name: 'type', label: vm.$t('assets.Type'), type: 'select', + inTable: true, el: { options: types } diff --git a/src/views/users/const.js b/src/views/users/const.js index 9c0fcb449..3bef6b77e 100644 --- a/src/views/users/const.js +++ b/src/views/users/const.js @@ -7,8 +7,8 @@ export const userJSONSelectMeta = (vm) => { value: [], resource: vm.$t('users.Users'), select2: { + url: '/api/v1/users/users/?fields_size=mini', ajax: { - url: '/api/v1/users/users/?fields_size=mini', transformOption: (item) => { return { label: item.name + '(' + item.username + ')', value: item.id } } @@ -17,15 +17,18 @@ export const userJSONSelectMeta = (vm) => { attrs: [ { name: 'name', - label: vm.$t('common.Name') + label: vm.$t('common.Name'), + inTable: true }, { name: 'username', - label: vm.$t('common.Username') + label: vm.$t('common.Username'), + inTable: true }, { name: 'email', - label: vm.$t('common.Email') + label: vm.$t('common.Email'), + inTable: true }, { name: 'comment', From 1b2c85d86d29d9d7a1bcef476d2f7922e517bc86 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 18 May 2023 21:33:58 +0800 Subject: [PATCH 09/12] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20m2m=20json=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JSONManyToManySelect/ValueField.vue | 55 ++++++++++++------- .../FormFields/JSONManyToManySelect/index.vue | 26 +++++++-- src/i18n/langs/zh.json | 2 +- .../components/AssetAccounts.vue | 2 +- src/views/assets/const.js | 2 + src/views/users/User/UserList.vue | 5 ++ src/views/users/const.js | 2 +- 7 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/components/FormFields/JSONManyToManySelect/ValueField.vue b/src/components/FormFields/JSONManyToManySelect/ValueField.vue index 29ade6513..d407625aa 100644 --- a/src/components/FormFields/JSONManyToManySelect/ValueField.vue +++ b/src/components/FormFields/JSONManyToManySelect/ValueField.vue @@ -31,11 +31,42 @@ export default { }, data() { return { - loading: false + loading: true, + type: 'string' } }, - computed: { - type() { + watch: { + match() { + this.getSetType() + }, + attr: { + handler() { + this.getSetType() + }, + deep: true + } + }, + mounted() { + this.getSetType() + this.loading = false + }, + methods: { + handleInput(value) { + this.$emit('input', value) + }, + getSetType() { + this.loading = true + this.type = this.getType() + if (['select', 'array'].includes(this.type) && typeof this.value === 'string') { + const value = this.value ? this.value.split(',') : [] + console.log('Type: ', this.type, 'Value: ', value) + this.handleInput(value) + } + this.$nextTick(() => { + this.loading = false + }) + }, + getType() { const attrType = this.attr.type if (attrType === 'm2m') { return 'select' @@ -44,28 +75,12 @@ export default { } else if (attrType === 'select') { return 'select' } - if (this.match in ['in', 'ip_in']) { + if (['in', 'ip_in'].includes(this.match)) { return 'array' } else { return 'string' } } - }, - watch: { - attr: { - handler() { - this.loading = true - this.$nextTick(() => { - this.loading = false - }) - }, - deep: true - } - }, - methods: { - handleInput(value) { - this.$emit('input', value) - } } } diff --git a/src/components/FormFields/JSONManyToManySelect/index.vue b/src/components/FormFields/JSONManyToManySelect/index.vue index 9c5015850..ea3e881ab 100644 --- a/src/components/FormFields/JSONManyToManySelect/index.vue +++ b/src/components/FormFields/JSONManyToManySelect/index.vue @@ -19,6 +19,7 @@ item.inTable).map(item => { return { prop: item.name, - label: item.label + label: item.label, + formatter: item.formatter } }) } @@ -153,7 +156,7 @@ export default { { prop: 'name', label: this.$t('common.AttrName'), formatter: tableFormatter('name') }, { prop: 'match', label: this.$t('common.Match'), formatter: tableFormatter('match') }, { prop: 'value', label: this.$t('common.AttrValue'), formatter: ValueFormatter, formatterArgs: { attrs: this.attrs }}, - { prop: 'action', label: this.$t('common.Action'), formatter: (row, col, cellValue, index) => { + { prop: 'action', label: this.$t('common.Action'), align: 'center', width: '120px', formatter: (row, col, cellValue, index) => { return (
{ option.hidden = !matchSupports.includes(option.value) }) - setTimeout(() => updateForm({ match: matchSupports[0] }), 0.1) + setTimeout(() => updateForm({ match: matchSupports[0], value: '' }), 0.1) } } }, @@ -226,6 +229,13 @@ export default { } } }, + watch: { + attrFormVisible(val) { + if (!val) { + this.getAttrsCount() + } + } + }, mounted() { this.formConfig.form = this.getDefaultAttrForm() if (this.value.type === 'attrs') { @@ -240,13 +250,17 @@ export default { this.attrMatchTableConfig.tableConfig.url = setUrlParam(this.select2.url, 'attr_rules', attrFilter) }, getAttrFilterKey() { + if (this.tableConfig.totalData.length === 0) return '' let attrFilter = { type: 'attrs', attrs: this.tableConfig.totalData } attrFilter = encodeURIComponent(btoa(JSON.stringify(attrFilter))) return attrFilter }, getAttrsCount() { const attrFilter = this.getAttrFilterKey() - console.log('attrFilter', attrFilter) + if (!attrFilter) { + this.attrMatchCount = 0 + return + } let url = setUrlParam(this.select2.url, 'attr_rules', attrFilter) url = setUrlParam(url, 'limit', 1) return this.$axios.get(url).then(res => { @@ -288,7 +302,7 @@ export default { const options = this.formConfig.fields[0].options const used = this.tableConfig.totalData.map(attr => attr.name) options.forEach(opt => { - if (used.includes(opt.value)) { + if (used.includes(opt.value) && opt.value !== this.formConfig.form.name) { opt.disabled = true } else { delete opt.disabled diff --git a/src/i18n/langs/zh.json b/src/i18n/langs/zh.json index 522413ee4..d060ba0b5 100644 --- a/src/i18n/langs/zh.json +++ b/src/i18n/langs/zh.json @@ -456,7 +456,7 @@ "ReLoginErr": "登录时长已超过 5 分钟,请重新登录" }, "common": { - "MatchedCount": "匹配条目", + "MatchedCount": "匹配结果", "SelectAttrs": "选择属性", "MatchResult": "匹配结果", "GreatEqualThan": "大于等于", diff --git a/src/views/assets/Asset/AssetCreateUpdate/components/AssetAccounts.vue b/src/views/assets/Asset/AssetCreateUpdate/components/AssetAccounts.vue index 3f2f41486..79bf4b0e8 100644 --- a/src/views/assets/Asset/AssetCreateUpdate/components/AssetAccounts.vue +++ b/src/views/assets/Asset/AssetCreateUpdate/components/AssetAccounts.vue @@ -19,7 +19,7 @@ - - + @@ -37,37 +37,41 @@ export default { }, watch: { match() { - this.getSetType() + this.setTypeAndValue() }, attr: { handler() { - this.getSetType() + this.setTypeAndValue() }, deep: true } }, mounted() { - this.getSetType() - this.loading = false + this.setTypeAndValue() }, methods: { handleInput(value) { this.$emit('input', value) }, - getSetType() { - this.loading = true + setTypeAndValue() { + this.loading = false this.type = this.getType() + console.log('Type: ', this.type, 'Value: ', this.value) if (['select', 'array'].includes(this.type) && typeof this.value === 'string') { const value = this.value ? this.value.split(',') : [] - console.log('Type: ', this.type, 'Value: ', value) this.handleInput(value) + } else if (this.type === 'bool') { + const value = !!this.value + this.handleInput(value) + console.log('This. vlaue: ', value) } this.$nextTick(() => { this.loading = false }) }, getType() { - const attrType = this.attr.type + const attrType = this.attr.type || 'str' + this.$log.debug('Value field attr type: ', attrType, this.attr, this.match) if (attrType === 'm2m') { return 'select' } else if (attrType === 'bool') { diff --git a/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue b/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue index e6a67d51d..0286c9d99 100644 --- a/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue +++ b/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue @@ -28,13 +28,11 @@ export default { return { formatterArgs: formatterArgs, loading: true, + attr: {}, value: '' } }, computed: { - attr() { - return this.formatterArgs.attrs.find(attr => attr.name === this.row.name) || {} - } }, watch: { cellValue: { @@ -50,6 +48,9 @@ export default { }, methods: { async getValue() { + this.attr = this.formatterArgs.attrs.find(attr => attr.name === this.row.name) + this.match = this.row.match + console.log('Attr: ', this.attr, this.row.name) if (this.attr.type === 'm2m') { const url = setUrlParam(this.attr.el.url, 'ids', this.cellValue.join(',')) const data = await this.$axios.get(url) @@ -61,6 +62,8 @@ export default { this.value = this.attr.el.options .filter(item => this.cellValue.includes(item.value)) .map(item => item.label).join(',') + } else if (['in', 'ip_in'].includes(this.match)) { + this.value = this.cellValue.join(', ') } else { this.value = this.cellValue } diff --git a/src/components/FormFields/JSONManyToManySelect/const.js b/src/components/FormFields/JSONManyToManySelect/const.js new file mode 100644 index 000000000..d70bebd11 --- /dev/null +++ b/src/components/FormFields/JSONManyToManySelect/const.js @@ -0,0 +1,25 @@ +import i18n from '@/i18n/i18n' + +export const strMatchValues = ['exact', 'not', 'in', 'contains', 'startswith', 'endswith', 'regex'] +export const typeMatchMapper = { + str: strMatchValues, + bool: ['exact', 'not'], + m2m: ['m2m'], + ip: strMatchValues + ['ip_in'], + int: strMatchValues + ['gte', 'lte'], + select: ['in'] +} + +export const attrMatchOptions = [ + { label: i18n.t('common.Equal'), value: 'exact' }, + { label: i18n.t('common.NotEqual'), value: 'not' }, + { label: i18n.t('common.MatchIn'), value: 'in' }, + { label: i18n.t('common.Contains'), value: 'contains' }, + { label: i18n.t('common.Startswith'), value: 'startswith' }, + { label: i18n.t('common.Endswith'), value: 'endswith' }, + { label: i18n.t('common.Regex'), value: 'regex' }, + { label: i18n.t('common.BelongTo'), value: 'm2m' }, + { label: i18n.t('common.IPMatch'), value: 'ip_in' }, + { label: i18n.t('common.GreatEqualThan'), value: 'gte' }, + { label: i18n.t('common.LessEqualThan'), value: 'lte' } +] diff --git a/src/components/FormFields/JSONManyToManySelect/index.vue b/src/components/FormFields/JSONManyToManySelect/index.vue index ea3e881ab..ad21201eb 100644 --- a/src/components/FormFields/JSONManyToManySelect/index.vue +++ b/src/components/FormFields/JSONManyToManySelect/index.vue @@ -5,7 +5,7 @@ {{ tp.label }} - +
@@ -18,42 +18,36 @@
- - - - - - + +
diff --git a/src/i18n/langs/en.json b/src/i18n/langs/en.json index c68e3a495..30b5e7f42 100644 --- a/src/i18n/langs/en.json +++ b/src/i18n/langs/en.json @@ -461,7 +461,7 @@ "ReLoginErr": "Login time has exceeded 5 minutes, please login again" }, "common": { - "BatchProcessing": "Batch processing(select {Number} items)", + "BatchProcessing": "Select {Number} items", "ServerError": "Server Error", "CommunityEdition": "Community Edition", "EnterpriseEdition": "Enterprise Edition", diff --git a/src/i18n/langs/ja.json b/src/i18n/langs/ja.json index e24d055f3..025e448bc 100644 --- a/src/i18n/langs/ja.json +++ b/src/i18n/langs/ja.json @@ -461,7 +461,7 @@ "ReLoginErr": "ログイン時間が 5 分を超えました。もう一度ログインしてください" }, "common": { - "BatchProcessing": "一括処理(選択 {Number} 項目)", + "BatchProcessing": "選択 {Number} 項目", "ServerError": "サーバーエラー", "RestoreDefault": "デフォルトに戻す", "DownloadCenter": "ダウンロードセンター", diff --git a/src/i18n/langs/zh.json b/src/i18n/langs/zh.json index d060ba0b5..d7332d2b1 100644 --- a/src/i18n/langs/zh.json +++ b/src/i18n/langs/zh.json @@ -482,7 +482,7 @@ "RelAnd": "与", "RelOr": "或", "RelNot": "非", - "BatchProcessing": "批量处理(选中 {Number} 项)", + "BatchProcessing": "选中 {Number} 项", "Created": "已创建", "Updated": "已更新", "Skipped": "已跳过", diff --git a/src/router/console/perms.js b/src/router/console/perms.js index 966a6c9a9..8c20d0616 100644 --- a/src/router/console/perms.js +++ b/src/router/console/perms.js @@ -40,7 +40,7 @@ export default [ ] }, { - path: 'host-acls', + path: 'login-asset-acls', component: empty, redirect: '', meta: { diff --git a/src/views/perms/AssetPermission/components/AccountFormatter.vue b/src/views/perms/AssetPermission/components/AccountFormatter.vue index 77ad5b9b6..9622e34dc 100644 --- a/src/views/perms/AssetPermission/components/AccountFormatter.vue +++ b/src/views/perms/AssetPermission/components/AccountFormatter.vue @@ -57,7 +57,7 @@ export default { }, props: { value: { - type: [Array], + type: [Array, String], default: () => [] }, assets: { @@ -176,7 +176,11 @@ export default { }, mounted() { this.initDefaultChoice() - this.$emit('input', this.value) + if (this.value === '') { + this.$emit('input', []) + } else { + this.$emit('input', this.value) + } }, methods: { initDefaultChoice() { @@ -245,6 +249,9 @@ export default { } .spec-accounts { + >>> .el-select { + width: 100%; + } } .help-text { From 4eaeff0a1865de81d82f9a347371426b6216c175 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 24 May 2023 14:30:52 +0800 Subject: [PATCH 11/12] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89=20console.log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AttrMatchResultDialog.vue | 3 -- .../JSONManyToManySelect/ValueField.vue | 3 +- .../JSONManyToManySelect/ValueFormatter.vue | 2 +- .../FormFields/JSONManyToManySelect/index.vue | 10 ++-- src/components/GrantedAssets/index.vue | 3 +- src/components/ManyJsonTabs/AssetJsonTab.vue | 53 +++++++++++++++++++ src/components/ManyJsonTabs/UserJsonTab.vue | 53 +++++++++++++++++++ .../TableFormatters/TagChoicesFormatter.vue | 1 - .../TreeTable/components/FileTree.vue | 1 - src/utils/jms.js | 4 ++ .../acl/AssetAcl/AssetAclDetail/Detail.vue | 16 +----- .../acl/AssetAcl/AssetAclDetail/index.vue | 17 +++++- .../CommandFilterAclDetail/Detail.vue | 18 +------ .../CommandFilterAclDetail/index.vue | 16 +++++- 14 files changed, 152 insertions(+), 48 deletions(-) create mode 100644 src/components/ManyJsonTabs/AssetJsonTab.vue create mode 100644 src/components/ManyJsonTabs/UserJsonTab.vue diff --git a/src/components/FormFields/JSONManyToManySelect/AttrMatchResultDialog.vue b/src/components/FormFields/JSONManyToManySelect/AttrMatchResultDialog.vue index af1894425..1b0bb5210 100644 --- a/src/components/FormFields/JSONManyToManySelect/AttrMatchResultDialog.vue +++ b/src/components/FormFields/JSONManyToManySelect/AttrMatchResultDialog.vue @@ -63,9 +63,6 @@ export default { return this.visible } } - }, - mounted() { - console.log('attrs', this.$attrs) } } diff --git a/src/components/FormFields/JSONManyToManySelect/ValueField.vue b/src/components/FormFields/JSONManyToManySelect/ValueField.vue index c764cbcf9..dfb87c1a4 100644 --- a/src/components/FormFields/JSONManyToManySelect/ValueField.vue +++ b/src/components/FormFields/JSONManyToManySelect/ValueField.vue @@ -56,14 +56,13 @@ export default { setTypeAndValue() { this.loading = false this.type = this.getType() - console.log('Type: ', this.type, 'Value: ', this.value) + this.$log.debug('ValueField: Type: ', this.type, 'Value: ', this.value) if (['select', 'array'].includes(this.type) && typeof this.value === 'string') { const value = this.value ? this.value.split(',') : [] this.handleInput(value) } else if (this.type === 'bool') { const value = !!this.value this.handleInput(value) - console.log('This. vlaue: ', value) } this.$nextTick(() => { this.loading = false diff --git a/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue b/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue index 0286c9d99..2634f59dc 100644 --- a/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue +++ b/src/components/FormFields/JSONManyToManySelect/ValueFormatter.vue @@ -50,7 +50,7 @@ export default { async getValue() { this.attr = this.formatterArgs.attrs.find(attr => attr.name === this.row.name) this.match = this.row.match - console.log('Attr: ', this.attr, this.row.name) + this.$log.debug('ValueFormatter: ', this.attr, this.row.name) if (this.attr.type === 'm2m') { const url = setUrlParam(this.attr.el.url, 'ids', this.cellValue.join(',')) const data = await this.$axios.get(url) diff --git a/src/components/FormFields/JSONManyToManySelect/index.vue b/src/components/FormFields/JSONManyToManySelect/index.vue index ad21201eb..84034e89c 100644 --- a/src/components/FormFields/JSONManyToManySelect/index.vue +++ b/src/components/FormFields/JSONManyToManySelect/index.vue @@ -44,6 +44,7 @@ import AttrFormDialog from './AttrFormDialog.vue' import AttrMatchResultDialog from './AttrMatchResultDialog.vue' import { setUrlParam } from '@/utils/common' import { attrMatchOptions } from './const' +import { toM2MJsonParams } from '@/utils/jms' export default { name: 'JSONManyToManySelect', @@ -155,14 +156,14 @@ export default { }, methods: { showAttrMatchTable() { - const attrFilter = this.getAttrFilterKey() - this.attrMatchTableUrl = setUrlParam(this.select2.url, 'attr_rules', attrFilter) + const [key, value] = this.getAttrFilterKey() + this.attrMatchTableUrl = setUrlParam(this.select2.url, key, value) this.attrMatchTableVisible = true }, getAttrFilterKey() { if (this.tableConfig.totalData.length === 0) return '' let attrFilter = { type: 'attrs', attrs: this.tableConfig.totalData } - attrFilter = encodeURIComponent(btoa(JSON.stringify(attrFilter))) + attrFilter = toM2MJsonParams(attrFilter) return attrFilter }, getAttrsCount() { @@ -171,7 +172,8 @@ export default { this.attrMatchCount = 0 return } - let url = setUrlParam(this.select2.url, 'attr_rules', attrFilter) + const [key, value] = attrFilter + let url = setUrlParam(this.select2.url, key, value) url = setUrlParam(url, 'limit', 1) return this.$axios.get(url).then(res => { this.attrMatchCount = res.count diff --git a/src/components/GrantedAssets/index.vue b/src/components/GrantedAssets/index.vue index 102d2abdf..7004892d6 100644 --- a/src/components/GrantedAssets/index.vue +++ b/src/components/GrantedAssets/index.vue @@ -59,7 +59,8 @@ export default { hasTree: true, columnsExclude: ['spec_info'], columnShow: { - min: ['name', 'address', 'accounts'] + min: ['name', 'address', 'accounts'], + default: ['name', 'address', 'accounts', 'actions'] }, columnsMeta: { name: { diff --git a/src/components/ManyJsonTabs/AssetJsonTab.vue b/src/components/ManyJsonTabs/AssetJsonTab.vue new file mode 100644 index 000000000..588c6696b --- /dev/null +++ b/src/components/ManyJsonTabs/AssetJsonTab.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/components/ManyJsonTabs/UserJsonTab.vue b/src/components/ManyJsonTabs/UserJsonTab.vue new file mode 100644 index 000000000..3452970a1 --- /dev/null +++ b/src/components/ManyJsonTabs/UserJsonTab.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/components/TableFormatters/TagChoicesFormatter.vue b/src/components/TableFormatters/TagChoicesFormatter.vue index 473c97bb6..1c2ea79b3 100644 --- a/src/components/TableFormatters/TagChoicesFormatter.vue +++ b/src/components/TableFormatters/TagChoicesFormatter.vue @@ -53,7 +53,6 @@ export default { iGetTag() { let tag = this.formatterArgs.getTag({ row: this.row, cellValue: this.cellValue }) if (tag) return tag - console.log('Tag: ', tag) tag = { size: this.formatterArgs.getTagSize({ row: this.row, cellValue: this.cellValue }), type: this.formatterArgs.getTagType({ row: this.row, cellValue: this.cellValue }), diff --git a/src/components/TreeTable/components/FileTree.vue b/src/components/TreeTable/components/FileTree.vue index ac0d433eb..04d6ebc5e 100644 --- a/src/components/TreeTable/components/FileTree.vue +++ b/src/components/TreeTable/components/FileTree.vue @@ -38,7 +38,6 @@ export default { }, methods: { handleNodeClick(data) { - console.log(data) } } } diff --git a/src/utils/jms.js b/src/utils/jms.js index 2ca819e3a..4f03efae9 100644 --- a/src/utils/jms.js +++ b/src/utils/jms.js @@ -144,3 +144,7 @@ export function getConstRouteName() { addRoutes(names, constRoutes) return names } + +export function toM2MJsonParams(attrFilter) { + return ['attr_rules', encodeURIComponent(btoa(JSON.stringify(attrFilter)))] +} diff --git a/src/views/acl/AssetAcl/AssetAclDetail/Detail.vue b/src/views/acl/AssetAcl/AssetAclDetail/Detail.vue index 82ec668e6..05c91a81f 100644 --- a/src/views/acl/AssetAcl/AssetAclDetail/Detail.vue +++ b/src/views/acl/AssetAcl/AssetAclDetail/Detail.vue @@ -1,7 +1,7 @@ @@ -23,21 +23,9 @@ export default { }, data() { return { - url: `/api/v1/acls/login-asset-acls/${this.object.id}`, + url: `/api/v1/acls/login-asset-acls/${this.object.id}/`, detailFields: [ 'name', - { - key: this.$t('acl.UserUsername'), - value: this.object.users.username_group.toString() - }, - { - key: this.$t('acl.AssetName'), - value: this.object.assets.name_group.toString() - }, - { - key: this.$t('acl.AssetAddress'), - value: this.object.accounts.username_group.toString() - }, { key: this.$t('acl.action'), value: this.object.action.label diff --git a/src/views/acl/AssetAcl/AssetAclDetail/index.vue b/src/views/acl/AssetAcl/AssetAclDetail/index.vue index 6111e2f96..d95ae1405 100644 --- a/src/views/acl/AssetAcl/AssetAclDetail/index.vue +++ b/src/views/acl/AssetAcl/AssetAclDetail/index.vue @@ -1,5 +1,5 @@