diff --git a/src/components/DataForm/components/el-form-renderer/components/render-form-item.vue b/src/components/DataForm/components/el-form-renderer/components/render-form-item.vue index 1c4376c7d..16379d707 100755 --- a/src/components/DataForm/components/el-form-renderer/components/render-form-item.vue +++ b/src/components/DataForm/components/el-form-renderer/components/render-form-item.vue @@ -6,6 +6,15 @@ :rules="_show && Array.isArray(data.rules) ? data.rules : []" v-bind="data.attrs" > + +
+ + diff --git a/src/components/ListTable/formatters/index.js b/src/components/ListTable/formatters/index.js index a2235da5b..66ca66727 100644 --- a/src/components/ListTable/formatters/index.js +++ b/src/components/ListTable/formatters/index.js @@ -8,6 +8,7 @@ import RouterFormatter from './RouterFormatter' import OutputExpandFormatter from './OutputExpandFormatter' import ExpandAssetPermissionFormatter from './ExpandAssetPermissionFormatter' import CustomActionsFormatter from './CustomActionsFormatter' +import DeleteActionFormatter from './DeleteActionFormatter' export default { DetailFormatter, @@ -19,7 +20,8 @@ export default { RouterFormatter, OutputExpandFormatter, ExpandAssetPermissionFormatter, - CustomActionsFormatter + CustomActionsFormatter, + DeleteActionFormatter } export { @@ -32,5 +34,6 @@ export { RouterFormatter, OutputExpandFormatter, ExpandAssetPermissionFormatter, - CustomActionsFormatter + CustomActionsFormatter, + DeleteActionFormatter } diff --git a/src/components/RelationCard/index.vue b/src/components/RelationCard/index.vue index 825686586..583f06990 100644 --- a/src/components/RelationCard/index.vue +++ b/src/components/RelationCard/index.vue @@ -24,6 +24,14 @@ + + + + + {{ $tc('More') }} ( {{ hasObjectLeftLength }} ) + + +
@@ -73,6 +81,7 @@ export default { data() { return { iHasObjects: this.hasObjects || [], + totalHasObjectsLength: 0, params: { page: 1, hasMore: false, @@ -88,22 +97,25 @@ export default { }, computed: { iAjax() { - this.$log.debug('iAjax', this.$refs.select2) return this.$refs.select2.iAjax + }, + safeMakeParams() { + return this.$refs.select2.safeMakeParams + }, + hasObjectLeftLength() { + this.$log.debug('Total', this.totalHasObjectsLength) + this.$log.debug(this.iHasObjects.length) + return this.totalHasObjectsLength - this.iHasObjects.length } }, mounted() { if (this.hasObjectsId.length !== 0) { setTimeout(() => { this.getHasObjectsByIds() - }, 500) + }, 50) } }, methods: { - safeMakeParams(params) { - this.$log.debug('safeMakeParams', this.$refs.select2) - return this.$refs.select2.safeMakeParams(params) - }, async loadMore() { if (this.loading) { return @@ -125,14 +137,18 @@ export default { let data = await this.$axios.get(this.iAjax.url, { params: params }) data = this.iAjax.processResults.bind(this)(data) data.results.forEach((v) => { - this.hasObjects.push(v) + if (!this.hasObjects.find((item) => item.value === v.value)) { + this.hasObjects.push(v) + } }) // 如果还有其它页,继续获取, 如果没有就停止 - if (!data.pagination) { - this.params.hasMore = false - } + this.params.hasMore = !!data.pagination + this.totalHasObjectsLength = data.total }, async getHasObjectsByIds() { + if (!this.$refs.select2 || !this.iAjax || !this.safeMakeParams) { + return + } const resp = await createSourceIdCache(this.hasObjectsId) this.params.spm = resp.spm await this.loadHasObjects() diff --git a/src/components/Select2/index.vue b/src/components/Select2/index.vue index 39fb8b359..d96cc64c6 100644 --- a/src/components/Select2/index.vue +++ b/src/components/Select2/index.vue @@ -7,9 +7,12 @@ :multiple="multiple" filterable remote + :reserve-keyword="true" popper-append-to-body class="select2" v-bind="$attrs" + @change="onChange" + @visible-change="onVisibleChange" v-on="$listeners" > @@ -42,7 +46,8 @@ const defaultProcessResults = (data) => { return { label: item.name, value: item.id } }) const more = !!data.next - return { results: results, pagination: more } + const total = data.count + return { results: results, pagination: more, total: total } } export const defaultAjax = { @@ -86,7 +91,7 @@ export default { }, ajax: { type: Object, - default: () => { return defaultAjax } + default: () => ({}) }, // 是否是多选 multiple: { @@ -99,6 +104,10 @@ export default { default() { return this.multiple ? [] : '' } + }, + disabledValues: { + type: Array, + default: () => [] } }, data() { @@ -110,10 +119,11 @@ export default { } return { loading: false, - iAjax: Object.assign(defaultAjax, this.ajax, this.url ? { url: this.url } : {}), + initialized: false, + iAjax: Object.assign(_.cloneDeep(defaultAjax), this.ajax, this.url ? { url: this.url } : {}), iValue: this.multiple ? [] : '', - defaultParams: defaultParams, - params: Object.assign({}, defaultParams), + defaultParams: _.cloneDeep(defaultParams), + params: _.cloneDeep(defaultParams), iOptions: this.options || [], initialOptions: [] } @@ -124,7 +134,13 @@ export default { } }, mounted() { - this.initialSelect() + this.$log.debug('Select is: ', this.iAjax.url) + this.$log.debug('Select url: ', this.url) + this.$log.debug('Default ajax: ', defaultAjax) + if (!this.initialized) { + this.initialSelect() + this.initialized = true + } }, methods: { async loadMore(load) { @@ -160,7 +176,17 @@ export default { this.params.search = query this.getOptions() }, - + reFresh() { + this.resetParams() + this.iOptions = [] + this.getOptions() + }, + addOption(option) { + if (this.disabledValues.indexOf(option.value) !== -1) { + option.disabled = true + } + this.iOptions.push(option) + }, async getInitialOptions() { const params = this.safeMakeParams(this.params) this.$log.debug('Get initial options: ', params) @@ -169,7 +195,7 @@ export default { data.results.forEach((v) => { this.initialOptions.push(v) if (this.optionsValues.indexOf(v.value) === -1) { - this.iOptions.push(v) + this.addOption(v) } }) // 如果还有其它页,继续获取, 如果没有就停止 @@ -190,7 +216,7 @@ export default { } data.results.forEach((v) => { if (this.optionsValues.indexOf(v.value) === -1) { - this.iOptions.push(v) + this.addOption(v) } }) }, @@ -207,6 +233,28 @@ export default { await this.getOptions() } this.iValue = this.value + }, + getOptionsByValues(values) { + return this.iOptions.filter((v) => { + return values.indexOf(v.value) !== -1 + }) + }, + getSelectedOptions() { + const values = this.iValue + return this.iOptions.filter((v) => { + return values.indexOf(v.value) !== -1 + }) + }, + onChange(values) { + const options = this.getSelectedOptions() + this.$log.debug('Current select options: ', options) + this.$emit('changeOptions', options) + }, + onVisibleChange(visible) { + if (!visible && this.params.search) { + this.reFresh() + this.$log.debug('Visible change, refresh select2') + } } } } diff --git a/src/components/index.js b/src/components/index.js index 07976b9d0..21bb26775 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -16,3 +16,4 @@ export { default as ActiveCard } from './ActiveCard' export { default as Select2 } from './Select2' export { default as SvgIcon } from './SvgIcon' export { default as TreeTable } from './TreeTable' + diff --git a/src/i18n/langs/cn.js b/src/i18n/langs/cn.js index fd19bed26..cfede97b2 100644 --- a/src/i18n/langs/cn.js +++ b/src/i18n/langs/cn.js @@ -131,7 +131,8 @@ const cn = { 'PlatformCreate': '创建系统平台', 'PlatformUpdate': '更新系统平台', 'CommandFilterCreate': '创建命令过滤器', - 'CommandFilterUpdate': '更新命令过滤器' + 'CommandFilterUpdate': '更新命令过滤器', + 'CreateReplyStorage': '创建录像存储' }, // 用户模块翻译 users: { @@ -349,6 +350,9 @@ const cn = { 'RefreshPermissionCache': '刷新授权缓存', 'ReFreshSuccess': '刷新成功', 'ReFreshFail': '刷新失败', + 'IsValid': '有效', + 'IP': 'IP', + 'Hostname': '主机名', 'All': '全部', 'Connect': '连接', 'UpDownload': '上传下载', @@ -433,7 +437,18 @@ const cn = { 'commandStorage': '命令存储', 'replayStorage': '录像存储', 'storage': '存储', - 'test': '测试' + 'test': '测试', + 'createReplyStorage': '创建录像存储', + 'endPoint': '端点', + 'bucket': '桶名称', + 'type': '类型', + 'containerName': '容器名称', + 'accountName': '账户名称', + 'accountKey': '账户密钥', + 'endpointSuffix': '端点后缀', + 'docType': '文档类型', + 'index': '索引', + 'hosts': '主机' }, jobcenter: { 'RunTimes': '执行次数', diff --git a/src/i18n/langs/en.js b/src/i18n/langs/en.js index 25aaf2e26..02a36e2cf 100644 --- a/src/i18n/langs/en.js +++ b/src/i18n/langs/en.js @@ -314,7 +314,18 @@ const en = { 'commandStorage': 'Command storage', 'replayStorage': 'Replay Storage', 'storage': 'Storage', - 'test': 'Test' + 'test': 'Test', + 'createReplyStorage': 'Create replay storage', + 'endPoint': 'Endpoint', + 'bucket': 'Bucket', + 'type': 'Type', + 'containerName': 'Container name', + 'accountName': 'Account name', + 'accountKey': 'Account key', + 'endpointSuffix': 'Endpoint suffix', + 'docType': 'Doc type', + 'index': 'Index', + 'hosts': 'Hosts' }, setting: { 'setting': 'System Setting', diff --git a/src/layout/components/GenericCreateUpdatePage/index.vue b/src/layout/components/GenericCreateUpdatePage/index.vue index 6b903bc4d..035bbac04 100644 --- a/src/layout/components/GenericCreateUpdatePage/index.vue +++ b/src/layout/components/GenericCreateUpdatePage/index.vue @@ -41,6 +41,10 @@ export default { type: Object, default: () => ({}) }, + cleanFormValue: { + type: Function, + default: (value) => value + }, onSubmit: { type: Function, default: null @@ -124,6 +128,7 @@ export default { handleSubmit(values) { let handler = this.onSubmit || this.defaultOnSubmit handler = handler.bind(this) + values = this.cleanFormValue(values) return handler(values) }, defaultPerformSubmit(validValues) { @@ -135,7 +140,6 @@ export default { const route = this.method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute performSubmit(validValues).then(() => { this.$message.success(msg) - console.log(route) this.$router.push(route) }).catch(error => { const response = error.response diff --git a/src/layout/components/GenericDetailPage/index.vue b/src/layout/components/GenericDetailPage/index.vue index 23deb9275..d2a2fbcc8 100644 --- a/src/layout/components/GenericDetailPage/index.vue +++ b/src/layout/components/GenericDetailPage/index.vue @@ -91,7 +91,7 @@ export default { detailApiUrl: getApiPath(this) } return { - loading: false, + loading: true, activeName: this.activeMenu || 'info', validActions: Object.assign(defaultActions, this.actions) } @@ -117,8 +117,13 @@ export default { return this.title || this.getTitle(this.object) } }, - mounted() { - this.getObject() + async mounted() { + try { + this.loading = true + await this.getObject() + } finally { + this.loading = false + } }, methods: { defaultDelete() { @@ -157,12 +162,9 @@ export default { this.$router.push({ name: routeName, params: { id: id }}) }, getObject() { - this.loading = true const url = this.validActions.detailApiUrl - this.$axios.get(url).then(data => { + return this.$axios.get(url).then(data => { this.$emit('update:object', data) - }).finally(() => { - this.loading = false }) }, handleTabClick(tab) { diff --git a/src/router/index.js b/src/router/index.js index 0c6c91530..80d99b242 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -416,6 +416,20 @@ export const constantRoutes = [ name: 'Storage', component: () => import('@/views/sessions/Storage'), hidden: true + }, + { + path: 'replay-storage/create', + name: 'CreateReplyStorage', + component: () => import('@/views/sessions/ReplyStorageCreate'), + meta: { title: 'CreateReplyStorage' }, + hidden: true + }, + { + path: 'command-storage/create', + name: 'CreateCommandStorage', + component: () => import('@/views/sessions/CommandStorageCreate'), + meta: { title: 'CreateCommandStorage' }, + hidden: true } ] }, diff --git a/src/views/perms/AssetPermissionAsset.vue b/src/views/perms/AssetPermissionAsset.vue index 9d2e1d01a..3e760df7a 100644 --- a/src/views/perms/AssetPermissionAsset.vue +++ b/src/views/perms/AssetPermissionAsset.vue @@ -14,6 +14,7 @@ + + diff --git a/src/views/sessions/ReplyStorageCreate.vue b/src/views/sessions/ReplyStorageCreate.vue new file mode 100644 index 000000000..dbd0aa7c9 --- /dev/null +++ b/src/views/sessions/ReplyStorageCreate.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/src/views/sessions/Storage.vue b/src/views/sessions/Storage.vue index c3ed50b69..a94283117 100644 --- a/src/views/sessions/Storage.vue +++ b/src/views/sessions/Storage.vue @@ -16,7 +16,6 @@ import { TabPage } from '@/layout/components' import { ListTable } from '@/components' import { TestCommandStorage, TestReplayStorage } from '@/api/sessions' -import { CustomActionsFormatter } from '@/components/ListTable/formatters' export default { name: 'Storage', @@ -49,48 +48,38 @@ export default { name: 'S3', title: 'S3', type: 'primary', - callback: function() { - console.log('S3===') - } + callback: this.createS3.bind(this) }, { name: 'Ceph', title: 'Ceph', type: 'primary', - callback: function() { - console.log('Ceph===') - } + callback: this.createCeph.bind(this) }, { name: 'Swift', title: 'Swift', type: 'primary', - callback: function() { - console.log('Swift===') - } + callback: this.createSwift.bind(this) }, { name: 'OSS', title: 'OSS', type: 'primary', - callback: function() { - console.log('OSS===') - } + callback: this.createOSS.bind(this) }, { name: 'Azure', title: 'Azure', type: 'primary', - callback: function() { - console.log('Azure===') - } + callback: this.createAzure.bind(this) } ] }, replayTableConfig: { url: '/api/v1/terminal/replay-storages/', - columns: ['name', 'type', 'comment', 'cusActions'], + columns: ['name', 'type', 'comment', 'actions'], columnsMeta: { name: { formatter: function(row) { @@ -105,36 +94,16 @@ export default { comment: { sortable: 'custom' }, - cusActions: { + actions: { prop: 'id', - formatter: CustomActionsFormatter, actions: { - actions: [ - { - name: 'update', - title: this.$tc('Update'), - type: 'primary', - can: function(row, cellValue,) { - return row.name !== 'null' && row.name !== 'default' - }, - callback: function({ row, col, cellValue, reload }) { - } - }, - { - name: 'delete', - title: this.$tc('Delete'), - type: 'danger', - can: function(row, cellValue) { - return row.name !== 'null' && row.name !== 'default' - }, - callback: function({ row, col, cellValue, reload }) { - const id = row.id - const url = `${this.url}${id}/` - this.$axios.delete(url).then(data => { - reload() - }) - } - }, + canUpdate: function(row, cellValue) { + return (row.name !== 'default' && row.name !== 'null') + }, + canDelete: function(row, cellValue) { + return (row.name !== 'default' && row.name !== 'null') + }, + extraActions: [ { name: 'test', title: this.$t('sessions.test'), @@ -170,16 +139,14 @@ export default { name: 'Elasticsearch', title: 'Elasticsearch', type: 'primary', - callback: function() { - console.log('Elasticsearch====') - } + callback: this.createEs.bind(this) } ] }, commandTableConfig: { title: 'command', url: '/api/v1/terminal/command-storages/', - columns: ['name', 'type', 'comment', 'cusActions'], + columns: ['name', 'type', 'comment', 'actions'], columnsMeta: { comment: { sortable: 'custom' @@ -194,36 +161,15 @@ export default { return row.type } }, - cusActions: { - prop: 'id', - formatter: CustomActionsFormatter, + actions: { actions: { - actions: [ - { - name: 'update', - title: this.$tc('Update'), - type: 'primary', - can: function(row, cellValue,) { - return row.name !== 'null' && row.name !== 'default' - }, - callback: function({ row, col, cellValue, reload }) { - } - }, - { - name: 'delete', - title: this.$tc('Delete'), - type: 'danger', - can: function(row, cellValue) { - return row.name !== 'null' && row.name !== 'default' - }, - callback: function({ row, col, cellValue, reload }) { - const id = row.id - const url = `${this.url}${id}/` - this.$axios.delete(url).then(data => { - reload() - }) - } - }, + canUpdate: function(row, cellValue) { + return (row.name !== 'default' && row.name !== 'null') + }, + canDelete: function(row, cellValue) { + return (row.name !== 'default' && row.name !== 'null') + }, + extraActions: [ { name: 'test', title: this.$t('sessions.test'), @@ -253,6 +199,26 @@ export default { Title() { return this.$t('sessions.storage') } + }, + methods: { + createS3() { + this.$router.push({ name: 'CreateReplyStorage', query: { type: 's3' }}) + }, + createCeph() { + this.$router.push({ name: 'CreateReplyStorage', query: { type: 'ceph' }}) + }, + createSwift() { + this.$router.push({ name: 'CreateReplyStorage', query: { type: 'swift' }}) + }, + createOSS() { + this.$router.push({ name: 'CreateReplyStorage', query: { type: 'oss' }}) + }, + createAzure() { + this.$router.push({ name: 'CreateReplyStorage', query: { type: 'azure' }}) + }, + createEs() { + this.$router.push({ name: 'CreateCommandStorage', query: { type: 'es' }}) + } } } diff --git a/src/views/users/UserGroupDetail/index.vue b/src/views/users/UserGroupDetail/index.vue index 40894afff..7a44e3711 100644 --- a/src/views/users/UserGroupDetail/index.vue +++ b/src/views/users/UserGroupDetail/index.vue @@ -50,9 +50,66 @@ export default { icon: 'fa-user', title: this.$tc('Members'), objectsAjax: { - url: '/api/v1/users/users/?fields_size=mini' + url: '/api/v1/users/users/?fields_size=mini&order=name', + processResults(data) { + let results = data.results + results = results.map((item) => { + return { label: item.name + '(' + item.username + ')', value: item.id } + }) + const more = !!data.next + return { results: results, pagination: more, total: data.count } + } }, - hasObjectsId: ['938b47a3-db34-40be-b732-ee0125a4857c', '3e3b75a9-11fd-4b97-ad04-17a50a72507c'], + hasObjectsId: ['ff0d0dc8-ef71-415f-9b83-60978c1971a8', + '9cb0e440-0340-408d-89d6-ce60eb5ab364', + '1ed4fb15-c526-45c9-83a8-0527f47b44f3', + '2c56fd37-db65-40ed-b787-b65a98635f12', + '938b47a3-db34-40be-b732-ee0125a4857c', + '6ece7c09-452f-4eac-ab20-9ccb1608680f', + 'e7f63eb0-1277-4dd9-87b2-0e347851bebc', + 'af84f59a-d70d-41ae-a30f-e92ceb4d84a1', + 'ce589bf7-f5ef-4eb1-b8ed-33c19a44566d', + '2a7359fc-1a8b-4f49-9bd0-e4b84c94428a', + '3e3b75a9-11fd-4b97-ad04-17a50a72507c', + 'fc175594-2895-4749-a350-0e9c3c89227a', + 'e34f1f38-eaf8-49ba-9229-44117833e9bb', + '87dc36ae-ad17-4f04-ae32-c884312f64e0', + 'b61768fa-3dfb-41cf-be1c-198402da881e', + 'c0c75f51-a78f-4623-89bc-7e4906d9eee6', + 'e48fb72e-ca52-441a-b6cc-d4aa5772d5c8', + '49aac9b3-4ff7-4ca1-a2fa-d8c577692f80', + '38f4d901-8283-499a-8b7e-f3d1377159a4', + '9c2e9073-5fa3-4a56-a5c5-3ef7b9e01fdb', + '88174bec-1ce7-40ef-a222-f8496a374812', + '12e835fc-978a-4229-b597-30a49232ee72', + '91fe7fad-706f-4fd4-b108-14373ff1b86c', + '8b63c190-0e3c-4a0c-9b7a-586a108f3239', + 'a442bfd9-117d-4604-b079-3ead15ebb4fd', + '445c998b-35f8-4345-b2d3-0c4eb947ea5b', + '55d09c56-09db-411a-93c4-eeeb080cf3c0', + '3d11bc66-ed17-402a-b32e-ae912e65999e', + '8a6387f0-0d8e-467d-8a57-957e2918e70e', + '0dd63aa5-2dfb-4499-ba01-042a4ef40189', + '47d63b4d-6a82-45ea-8194-744218b2c54f', + '26fd4116-4de7-4628-84ab-c158262e8782', + 'c335b2f9-ffd1-4b5c-aea6-1e534263b28f', + 'fb072f99-c352-414c-9deb-10480a9e943e', + 'ae441f3a-f53b-4232-9f14-88ce47fa37b7', + '579d21f7-ac3b-402b-87cd-258f788f95ef', + 'db20db9b-2d36-4db8-98c3-287e43a3454b', + '9add3233-8535-488c-82c8-bb1c5845c87e', + '647e467b-50da-4af3-bd71-6b8bca0ed4d6', + '21b7cb6f-bbf3-449c-8e91-a461a2b459b8', + '74c021e8-ae00-4433-9e48-99f76e975aa5', + '204b13b5-d0db-404b-8987-fe0953315c67', + '3a3728c0-5afb-43e3-a632-9338e1a7d591', + '6e91f74a-0558-48d6-ae47-ccc5afb97c84', + 'e3cb895f-3a07-4985-b6fb-7bfb23b4834e', + 'e9a5c087-9f29-4ff9-859f-6c36f87ee67a', + 'ada94cda-cf3b-4a93-ad4b-97b982dda627', + 'b2b449d0-f7bf-4874-a463-68f51ec98212', + '17dc5bc6-84b4-4814-9b96-d41bd12b5578', + '05ed8bfe-e8cb-49aa-86e3-756779ff7fac'], hasObjects: [ // { // id: '1',