perf: 拆分为多个文件

This commit is contained in:
ibuler
2023-05-23 15:28:20 +08:00
parent 1b2c85d86d
commit 08c16aae72
12 changed files with 341 additions and 194 deletions

View File

@@ -0,0 +1,148 @@
<template>
<Dialog
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.SelectAttrs')"
v-bind="$attrs"
v-on="$listeners"
>
<div v-if="!loading">
<DataForm
:form="form"
class="attr-form"
v-bind="formConfig"
@submit="onAttrDialogConfirm"
/>
</div>
</Dialog>
</template>
<script>
import DataForm from '@/components/DataForm/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import ValueField from '@/components/FormFields/JSONManyToManySelect/ValueField.vue'
import { attrMatchOptions, typeMatchMapper } from './const'
export default {
name: 'AttrFormDialog',
components: { Dialog, DataForm },
props: {
attrs: {
type: Array,
default: () => ([])
},
attrsAdded: {
type: Array,
default: () => ([])
},
form: {
type: Object,
default: () => ({})
}
},
data() {
return {
loading: true,
formConfig: {
// 为了方便更新,避免去取 fields 的索引
hasSaveContinue: false,
fields: [
{
id: 'name',
label: this.$t('common.AttrName'),
type: 'select',
options: this.attrs.map(attr => {
const disabled = this.attrsAdded.includes(attr.name) && this.form.name !== attr.name
return { label: attr.label, value: attr.name, disabled: disabled }
}),
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)
})
let defaultValue = ''
if (['m2m', 'select'].includes(attrType)) {
defaultValue = []
} else if (['bool'].includes(attrType)) {
defaultValue = false
}
setTimeout(() => {
updateForm({ match: matchSupports[0], value: defaultValue })
}, 0.1)
}
}
},
{
id: 'match',
label: this.$t('common.Match'),
type: 'select',
options: attrMatchOptions,
on: {
change: ([value], updateForm) => {
let defaultValue = ''
if (['in', 'ip_in'].includes(value)) {
defaultValue = []
}
updateForm({ value: defaultValue })
this.formConfig.fields[2].el.match = value
}
}
},
{
id: 'value',
label: this.$t('common.AttrValue'),
component: ValueField,
el: {
match: attrMatchOptions[0].value,
attr: this.attrs[0]
}
}
]
}
}
},
mounted() {
if (this.form.index === undefined || this.form.index === -1) {
Object.assign(this.form, this.getDefaultAttrForm())
}
this.formConfig.fields[2].el.attr = this.attrs.find(attr => attr.name === this.form.name)
this.formConfig.fields[2].el.match = this.form.match
this.$log.debug('Form config: ', this.formConfig)
this.loading = false
},
methods: {
getDefaultAttrForm() {
const attrKeys = this.attrs.map(attr => attr.name)
const diff = attrKeys.filter(attr => !this.attrsAdded.includes(attr))
let name = this.attrs[0].name
if (diff.length > 0) {
name = diff[0]
}
return {
name: name,
match: 'exact',
value: '',
rel: 'and'
}
},
onAttrDialogConfirm(form) {
this.$emit('confirm', form)
}
}
}
</script>
<style lang="scss" scoped>
.attr-form {
>>> .el-select {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,75 @@
<template>
<Dialog
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.MatchResult')"
:v-bind="$attrs"
:v-on="$listeners"
:visible.sync="iVisible"
>
<ListTable v-bind="attrMatchTableConfig" />
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import ListTable from '@/components/ListTable/index.vue'
export default {
name: 'AttrMatchResultDialog',
components: { ListTable, Dialog },
props: {
url: {
type: String,
default: ''
},
attrs: {
type: Array,
default: () => ([])
},
visible: {
type: Boolean,
default: false
}
},
data() {
return {
attrMatchTableConfig: {
headerActions: {
hasCreate: false,
hasImport: false,
hasExport: false,
hasMoreActions: false
},
tableConfig: {
url: this.url,
columns: this.attrs.filter(item => item.inTable).map(item => {
return {
prop: item.name,
label: item.label,
formatter: item.formatter
}
})
}
}
}
},
computed: {
iVisible: {
set(val) {
this.$emit('update:visible', val)
},
get() {
return this.visible
}
}
},
mounted() {
console.log('attrs', this.$attrs)
}
}
</script>
<style scoped>
</style>

View File

@@ -2,7 +2,7 @@
<div v-if="!loading">
<TagInput v-if="type === 'array'" :value="value" @input="handleInput" />
<Select2 v-else-if="type === 'select'" :value="value" v-bind="attr.el" @change="handleInput" @input="handleInput" />
<Switcher v-else-if="type === 'bool'" :value="value" @change="handleInput" />
<Switcher v-else-if="type === 'bool'" :value="value" @change="handleInput" @input="handleInput" />
<el-input v-else :value="value" @input="handleInput" />
</div>
</template>
@@ -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') {

View File

@@ -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
}

View File

@@ -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' }
]

View File

@@ -5,7 +5,7 @@
{{ tp.label }}
</el-radio>
</el-radio-group>
<Select2 v-if="iValue.type === 'ids'" v-model="ids" v-bind="select2" />
<Select2 v-if="iValue.type === 'ids'" v-model="ids" v-bind="select2" @change="onChangeEmit" />
<div v-if="iValue.type === 'attrs'">
<DataTable :config="tableConfig" class="attr-list" />
<div class="actions">
@@ -18,42 +18,36 @@
</span>
</div>
</div>
<Dialog
v-if="attrFormVisible"
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.SelectAttrs')"
:visible.sync="attrFormVisible"
@close="getAttrsCount"
>
<DataForm class="attr-form" v-bind="formConfig" @submit="onAttrDialogConfirm" />
</Dialog>
<Dialog
v-if="attrMatchTableVisible"
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.MatchResult')"
:visible.sync="attrMatchTableVisible"
>
<ListTable v-bind="attrMatchTableConfig" />
</Dialog>
<AttrFormDialog
v-if="attrFormVisible"
:attrs="attrs"
:attrs-added="attrsAdded"
:form="attrForm"
:visible.sync="attrFormVisible"
@confirm="handleAttrDialogConfirm"
/>
<AttrMatchResultDialog
v-if="attrMatchTableVisible"
:attrs="attrs"
:url="attrMatchTableUrl"
:visible.sync="attrMatchTableVisible"
/>
</div>
</template>
<script>
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'
import AttrFormDialog from './AttrFormDialog.vue'
import AttrMatchResultDialog from './AttrMatchResultDialog.vue'
import { setUrlParam } from '@/utils/common'
import ListTable from '@/components/ListTable/index.vue'
import { attrMatchOptions } from './const'
export default {
name: 'JSONManyToManySelect',
components: { DataTable, Select2, Dialog, DataForm, ListTable },
components: { AttrFormDialog, DataTable, Select2, AttrMatchResultDialog },
props: {
value: {
type: Object,
@@ -81,32 +75,6 @@ export default {
}
},
data() {
const attrMatchOptions = [
{ label: this.$t('common.Equal'), value: 'exact' },
{ label: this.$t('common.NotEqual'), value: 'not' },
{ label: this.$t('common.MatchIn'), value: 'in' },
{ label: this.$t('common.Contains'), value: 'contains' },
{ 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.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 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
@@ -124,28 +92,13 @@ export default {
}
return {
iValue: Object.assign({ type: 'all' }, this.value),
attrMatchTableConfig: {
headerActions: {
hasCreate: false,
hasImport: false,
hasExport: false,
hasMoreActions: false
},
tableConfig: {
url: this.select2.url,
columns: this.attrs.filter(item => item.inTable).map(item => {
return {
prop: item.name,
label: item.label,
formatter: item.formatter
}
})
}
},
attrFormVisible: false,
attrForm: {},
attrMatchCount: 0,
attrMatchTableVisible: false,
attrMatchTableUrl: '',
ids: this.value.ids || [],
editIndex: -1,
types: [
{ name: 'all', label: this.$t('common.All') + this.resource },
{ name: 'ids', label: this.$t('common.Spec') + this.resource },
@@ -179,56 +132,14 @@ export default {
],
totalData: this.value.attrs || [],
hasPagination: false
},
formConfig: {
// 为了方便更新,避免去取 fields 的索引
hasSaveContinue: false,
editRowIndex: -1,
form: {},
fields: [
{
id: 'name',
label: this.$t('common.AttrName'),
type: 'select',
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], value: '' }), 0.1)
}
}
},
{
id: 'match',
label: this.$t('common.Match'),
type: 'select',
options: attrMatchOptions,
on: {
change: ([value], updateForm) => {
this.formConfig.fields[2].el.match = value
}
}
},
{
id: 'value',
label: this.$t('common.AttrValue'),
component: ValueField,
el: {
match: attrMatchOptions[0].value,
attr: this.attrs[0]
}
}
]
}
}
},
computed: {
attrsAdded() {
return this.tableConfig.totalData.map(item => item.name)
}
},
watch: {
attrFormVisible(val) {
if (!val) {
@@ -237,7 +148,6 @@ export default {
}
},
mounted() {
this.formConfig.form = this.getDefaultAttrForm()
if (this.value.type === 'attrs') {
this.getAttrsCount()
}
@@ -245,9 +155,9 @@ export default {
},
methods: {
showAttrMatchTable() {
this.attrMatchTableVisible = true
const attrFilter = this.getAttrFilterKey()
this.attrMatchTableConfig.tableConfig.url = setUrlParam(this.select2.url, 'attr_rules', attrFilter)
this.attrMatchTableUrl = setUrlParam(this.select2.url, 'attr_rules', attrFilter)
this.attrMatchTableVisible = true
},
getAttrFilterKey() {
if (this.tableConfig.totalData.length === 0) return ''
@@ -267,68 +177,28 @@ export default {
this.attrMatchCount = res.count
})
},
onAttrDialogConfirm(form) {
const allAttrs = this.tableConfig.totalData
if (this.formConfig.editRowIndex !== -1) {
allAttrs.splice(this.formConfig.editRowIndex, 1)
this.formConfig.editRowIndex = -1
}
// 因为可能 attr 的 name 会重复,所以需要先删除再添加
const setIndex = allAttrs.findIndex(attr => attr.name === form.name)
if (setIndex === -1) {
allAttrs.push(Object.assign({}, form))
} else {
allAttrs.splice(setIndex, 1, Object.assign({}, form))
}
form = this.getDefaultAttrForm()
this.attrFormVisible = false
},
getDefaultAttrForm() {
const attrKeys = this.attrs.map(attr => attr.name)
const attrSet = this.tableConfig.totalData.map(attr => attr.name)
const diff = attrKeys.filter(attr => !attrSet.includes(attr))
let name = this.attrs[0].name
if (diff.length > 0) {
name = diff[0]
}
return {
name: name,
match: 'exact',
value: '',
rel: 'and'
}
},
setAttrNameOptionUsed() {
const options = this.formConfig.fields[0].options
const used = this.tableConfig.totalData.map(attr => attr.name)
options.forEach(opt => {
if (used.includes(opt.value) && opt.value !== this.formConfig.form.name) {
opt.disabled = true
} else {
delete opt.disabled
}
})
},
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.attrForm = Object.assign({ index }, row)
this.editIndex = index
this.attrFormVisible = true
}
},
handleAttrDelete({ index }) {
return () => {
this.tableConfig.totalData.splice(index, 1)
this.getAttrsCount()
}
},
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.attrForm = {}
this.editIndex = -1
this.attrFormVisible = true
},
onChangeEmit() {
const tp = this.iValue.type
this.handleTypeChange(tp)
},
handleTypeChange(val) {
switch (val) {
case 'ids':
@@ -341,6 +211,21 @@ export default {
this.$emit('input', { type: 'all' })
break
}
},
handleAttrDialogConfirm(form) {
if (this.editIndex > -1) {
this.tableConfig.totalData.splice(this.editIndex, 1)
}
const allAttrs = this.tableConfig.totalData
// 因为可能 attr 的 name 会重复,所以需要先删除再添加
const setIndex = allAttrs.findIndex(attr => attr.name === form.name)
if (setIndex === -1) {
allAttrs.push(Object.assign({}, form))
} else {
allAttrs.splice(setIndex, 1, Object.assign({}, form))
}
this.attrFormVisible = false
this.onChangeEmit()
}
}
}
@@ -350,9 +235,4 @@ export default {
.attr-list {
width: 99%;
}
.attr-form {
>>> .el-select {
width: 100%;
}
}
</style>

View File

@@ -1,8 +1,8 @@
<template>
<el-switch
v-model="iValue"
inactive-color="#dcdfe6"
:class="type"
inactive-color="#dcdfe6"
v-bind="$attrs"
v-on="$listeners"
/>
@@ -17,7 +17,7 @@ export default {
default: 'primary'
},
value: {
type: Boolean,
type: [Boolean, String],
default: true
}
},
@@ -31,9 +31,14 @@ export default {
this.$emit('input', newValue)
},
get: function() {
return this.value
return !!this.value
}
}
},
watch: {
value(val) {
this.$log.debug('Switcher Value changed: ', val)
}
}
}
</script>

View File

@@ -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",

View File

@@ -461,7 +461,7 @@
"ReLoginErr": "ログイン時間が 5 分を超えました。もう一度ログインしてください"
},
"common": {
"BatchProcessing": "一括処理(選択 {Number} 項目)",
"BatchProcessing": "選択 {Number} 項目",
"ServerError": "サーバーエラー",
"RestoreDefault": "デフォルトに戻す",
"DownloadCenter": "ダウンロードセンター",

View File

@@ -482,7 +482,7 @@
"RelAnd": "与",
"RelOr": "或",
"RelNot": "非",
"BatchProcessing": "批量处理(选中 {Number} 项)",
"BatchProcessing": "选中 {Number} 项",
"Created": "已创建",
"Updated": "已更新",
"Skipped": "已跳过",

View File

@@ -40,7 +40,7 @@ export default [
]
},
{
path: 'host-acls',
path: 'login-asset-acls',
component: empty,
redirect: '',
meta: {

View File

@@ -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 {