diff --git a/src/components/AutoDataForm/index.vue b/src/components/AutoDataForm/index.vue index 8d443ed63..42011884b 100644 --- a/src/components/AutoDataForm/index.vue +++ b/src/components/AutoDataForm/index.vue @@ -44,7 +44,6 @@ export default { groups: [] } }, - mounted() { this.optionUrlMeta() }, @@ -54,7 +53,7 @@ export default { this.meta = data.actions[this.method.toUpperCase()] || {} this.generateColumns() }).catch(err => { - console.error(err) + this.$log.error(err) }).finally(() => { this.loading = false }) @@ -150,13 +149,12 @@ export default { }) return this.generateFields(fields) }, - genreateFieldAttrs(name) { + generateFieldAttrs(name) { const fields = [] Object.keys(this.meta[name]['children']).forEach((key, i) => { const filed = this.generateField(key) fields.push(filed) }) - return fields }, generateFields(data) { @@ -166,7 +164,7 @@ export default { const items = this.generateFieldGroup(field) fields = [...fields, ...items] } else if (field === 'attrs') { - const items = this.genreateFieldAttrs(field) + const items = this.generateFieldAttrs(field) fields = [...fields, ...items] // 修改title插入ID this.groups[this.groups.length - 1].name = items[0].id @@ -183,6 +181,7 @@ export default { }, generateColumns() { this.totalFields = this.generateFields(this.fields) + this.$log.debug('Total fields: ', this.totalFields) }, setFieldError(name, error) { const field = this.totalFields.find((v) => v.prop === name) diff --git a/src/components/ListTable/formatters/ActionsFormatter.vue b/src/components/ListTable/formatters/ActionsFormatter.vue index ef4ae906f..31fceb513 100644 --- a/src/components/ListTable/formatters/ActionsFormatter.vue +++ b/src/components/ListTable/formatters/ActionsFormatter.vue @@ -24,6 +24,19 @@ const defaultUpdateCallback = function({ row, col }) { this.$router.push(route) } +const defaultCloneCallback = function({ row, col }) { + const id = row.id + let route = { query: { clone_from: id }} + const cloneRoute = this.colActions.cloneRoute + + if (typeof cloneRoute === 'object') { + route = Object.assign(route, cloneRoute) + } else { + route.name = cloneRoute + } + this.$router.push(route) +} + const defaultDeleteCallback = function({ row, col, cellValue, reload }) { let msg = this.$t('common.deleteWarningMsg') const name = row.name || row.hostname @@ -71,10 +84,14 @@ export default { canUpdate: true, // can set function(row, value) hasDelete: true, // can set function(row, value) canDelete: true, + hasClone: true, + canClone: true, updateRoute: this.$route.name.replace('List', 'Update'), + cloneRoute: this.$route.name.replace('List', 'Create'), performDelete: defaultPerformDelete, onUpdate: defaultUpdateCallback, onDelete: defaultDeleteCallback, + onClone: defaultCloneCallback, extraActions: [] // format see defaultActions } } @@ -89,7 +106,8 @@ export default { type: 'primary', has: colActions.hasUpdate, can: colActions.canUpdate, - callback: colActions.onUpdate + callback: colActions.onUpdate, + order: 10 }, { name: 'delete', @@ -97,7 +115,17 @@ export default { type: 'danger', has: colActions.hasDelete, can: colActions.canDelete, - callback: colActions.onDelete + callback: colActions.onDelete, + order: 20 + }, + { + name: 'clone', + title: this.$t('common.Clone'), + type: 'info', + has: colActions.hasClone, + can: colActions.canClone, + callback: colActions.onClone, + order: 30 } ] return { @@ -115,9 +143,11 @@ export default { v.has = this.cleanBoolean(v, 'has') v.can = this.cleanBoolean(v, 'can') v.callback = this.cleanCallback(v) + v.order = v.order || 100 return v }) actions = actions.filter((v) => v.has) + actions.sort((a, b) => a.order - b.order) return actions }, actions() { diff --git a/src/i18n/langs/cn.json b/src/i18n/langs/cn.json index 7d1351aad..efae6a51e 100644 --- a/src/i18n/langs/cn.json +++ b/src/i18n/langs/cn.json @@ -245,6 +245,7 @@ "To": "至", "Update": "更新", "Upload": "上传", + "Clone": "克隆", "Username": "用户名", "Validity": "有效", "Invalidity": "无效", @@ -258,6 +259,7 @@ "NeedAssetsAndSystemUserErrMsg": "请先选择授权的系统用户和资产", "bulkRemoveSuccessMsg": "批量移除成功", "createBy": "创建者", + "cloneFrom": "克隆自", "createErrorMsg": "创建失败", "createSuccessMsg": "创建成功", "saveSuccessContinueMsg": "创建成功,更新内容后可以继续添加", diff --git a/src/i18n/langs/en.json b/src/i18n/langs/en.json index a544f5baf..7d4861f5e 100644 --- a/src/i18n/langs/en.json +++ b/src/i18n/langs/en.json @@ -244,6 +244,7 @@ "To": "To", "Update": "Update", "Upload": "Upload", + "Clone": "Clone", "Username": "Username", "Validity": "Validity", "Invalidity": "Invalidity", @@ -257,6 +258,7 @@ "bulkRemoveSuccessMsg": "Bulk remove success", "NeedAssetsAndSystemUserErrMsg": "Need assets and systemuser", "createBy": "Create by", + "cloneFrom": "Clone from", "createErrorMsg": "Create error", "createSuccessMsg": "Create success", "saveSuccessContinueMsg": "Create success, you may add another", diff --git a/src/layout/components/GenericCreateUpdateForm/index.vue b/src/layout/components/GenericCreateUpdateForm/index.vue index 8088f34e9..ece35dd97 100644 --- a/src/layout/components/GenericCreateUpdateForm/index.vue +++ b/src/layout/components/GenericCreateUpdateForm/index.vue @@ -22,38 +22,46 @@ export default { AutoDataForm }, props: { + // 创建对象的地址 url: { type: String, default: '' }, + // 更新的对象 object: { type: Object, default: null }, + // form的默认值 initial: { type: Object, default: () => ({}) }, + // 提交前,清理form的值 cleanFormValue: { type: Function, default: (value) => value }, + // 当提交的时候,怎么处理 onSubmit: { type: Function, default: null }, + // 如何提交数据 performSubmit: { type: Function, default(validValues) { return this.$axios[this.method](this.iUrl, validValues) } }, + // 创建成功的msg createSuccessMsg: { type: String, default: function() { return this.$t('common.createSuccessMsg') } }, + // 更新成功的msg saveSuccessContinueMsg: { type: String, default: function() { @@ -66,6 +74,7 @@ export default { return this.$t('common.updateSuccessMsg') } }, + // 创建成功的跳转路由 createSuccessNextRoute: { type: Object, default: function() { @@ -73,6 +82,7 @@ export default { return { name: routeName } } }, + // 更新成功的跳转路由 updateSuccessNextRoute: { type: Object, default: function() { @@ -80,12 +90,14 @@ export default { return { name: routeName } } }, + // 获取下一个路由 getNextRoute: { type: Function, default(res, method) { return method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute } }, + // 获取提交的方法 getMethod: { type: Function, default: function() { @@ -97,6 +109,7 @@ export default { } } }, + // 获取创建和更新的url function getUrl: { type: Function, default: function() { @@ -149,7 +162,8 @@ export default { return { form: {}, loading: true, - isSubmitting: false + isSubmitting: false, + clone: false } }, computed: { @@ -176,6 +190,7 @@ export default { this.loading = true try { const values = await this.getFormValue() + this.$log.debug(this.$attrs) this.form = Object.assign(this.form, values) } finally { this.loading = false @@ -196,21 +211,35 @@ export default { .finally(() => { this.isSubmitting = false }) }, async getFormValue() { - if (this.method !== 'put') { + const cloneFrom = this.$route.query['clone_from'] + this.$log.debug('Clone from: ', cloneFrom) + if (this.method !== 'put' && !cloneFrom) { return Object.assign(this.form, this.initial) } let object = this.object - if (object === null) { - object = await this.getObjectDetail() + if (cloneFrom) { + const url = `${this.url}${cloneFrom}/` + object = await this.getObjectDetail(url) + if (object['name']) { + object.name = this.$t('common.cloneFrom') + ' ' + object.name + } else if (object['hostname']) { + object.hostname = this.$t('common.cloneFrom') + ' ' + object.hostname + } + } else { + object = await this.getObjectDetail(this.iUrl) + } + if (object) { if (object['attrs']) { object = deepmerge(object, object['attrs']) } + this.$log.debug('Object is: ', object) this.$emit('update:object', object) } return object }, - async getObjectDetail() { - return this.$axios.get(this.iUrl) + async getObjectDetail(url) { + this.$log.debug('Get object detail: ', url) + return this.$axios.get(url) } } } diff --git a/vue.config.js b/vue.config.js index d5f3cebaa..24c4319e7 100644 --- a/vue.config.js +++ b/vue.config.js @@ -53,6 +53,20 @@ module.exports = { changeOrigin: true, ws: true }, + '/koko/': { + target: 'http://127.0.0.1:5000', + changeOrigin: true, + ws: true + }, + '/guacamole/': { + target: 'http://127.0.0.1:8081', + changeOrigin: true, + ws: true + }, + '/luna/': { + target: 'http://127.0.0.1:4200/luna/', + changeOrigin: true + }, '^/(core|static|media)/': { target: process.env.VUE_APP_CORE_HOST, changeOrigin: true