diff --git a/src/api/sessions.js b/src/api/sessions.js index 3451b0f9d..e53880518 100644 --- a/src/api/sessions.js +++ b/src/api/sessions.js @@ -7,3 +7,17 @@ export function terminateSession(data) { data: data }) } + +export function getSessionDetail(id) { + return request({ + url: `/api/v1/terminal/sessions/${id}/`, + method: 'get' + }) +} + +export function getSessionCommands(id) { + return request({ + url: `/api/v1/terminal/commands/?session_id=${id}`, + method: 'get' + }) +} diff --git a/src/components/AutoDataForm/index.vue b/src/components/AutoDataForm/index.vue index 7fcb0f762..7350eafc0 100644 --- a/src/components/AutoDataForm/index.vue +++ b/src/components/AutoDataForm/index.vue @@ -1,5 +1,5 @@ @@ -48,7 +48,6 @@ export default { }, mounted() { this.optionUrlMeta() - console.log('auto data form', this.$attrs) }, methods: { optionUrlMeta() { @@ -76,47 +75,65 @@ export default { type = '' field.component = Select2 break + case 'string': + type = 'input' + if (!fieldMeta.max_length) { + field.el.type = 'textarea' + } + break default: type = 'input' break } + if (type === 'radio-group') { + const options = fieldMeta.choices.map(v => { + return { label: v.display_name, value: v.value } + }) + if (options.length > 4) { + type = 'select' + field.el.filterable = true + } + } field.type = type return field }, generateFieldByName(name, field) { switch (name) { case 'email': - field.el = { type: 'email' } + field.el.type = 'email' break case 'password': - field.el = { type: 'password' } + field.el.type = 'password' break case 'comment': - field.el = { type: 'textarea' } + field.el.type = 'textarea' break } return field }, generateFieldByOther(field, fieldMeta) { + const filedRules = field.rules || [] if (fieldMeta.required) { if (field.type === 'input') { - field.rules = [rules.Required] + filedRules.push(rules.Required) } else { - field.rules = [rules.RequiredChange] + filedRules.push(rules.RequiredChange) } } + field.rules = filedRules return field }, generateField(name) { - let field = {} + let field = { id: name, prop: name, el: {}} const fieldMeta = this.meta[name] || {} - field.id = name - field.prop = name field.label = fieldMeta.label field = this.generateFieldByType(fieldMeta.type, field, fieldMeta) field = this.generateFieldByName(name, field) field = this.generateFieldByOther(field, fieldMeta) field = Object.assign(field, this.fieldsMeta[name] || {}) + if (name === 'name') { + console.log(field) + } return field }, generateFieldGroup(data) { @@ -132,9 +149,6 @@ export default { generateFields(data) { let fields = [] for (let field of data) { - console.log('is array', field instanceof Array) - console.log('is string', typeof field === 'string') - console.log('is object', field instanceof Object) if (field instanceof Array) { const items = this.generateFieldGroup(field) fields = [...fields, ...items] diff --git a/src/components/AutoDataTable/index.vue b/src/components/AutoDataTable/index.vue index ef96e3b6a..dc0efc269 100644 --- a/src/components/AutoDataTable/index.vue +++ b/src/components/AutoDataTable/index.vue @@ -4,7 +4,7 @@ + + diff --git a/src/components/ListTable/formatters/ExpandPreFormatter.vue b/src/components/ListTable/formatters/ExpandPreFormatter.vue new file mode 100644 index 000000000..d94821140 --- /dev/null +++ b/src/components/ListTable/formatters/ExpandPreFormatter.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/src/components/ListTable/formatters/OutputExpandFormatter.vue b/src/components/ListTable/formatters/OutputExpandFormatter.vue new file mode 100644 index 000000000..1594ff04f --- /dev/null +++ b/src/components/ListTable/formatters/OutputExpandFormatter.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/ListTable/formatters/RouterFormatter.vue b/src/components/ListTable/formatters/RouterFormatter.vue new file mode 100644 index 000000000..062ab04fa --- /dev/null +++ b/src/components/ListTable/formatters/RouterFormatter.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/src/components/ListTable/formatters/index.js b/src/components/ListTable/formatters/index.js index 515c422e2..50bc0e617 100644 --- a/src/components/ListTable/formatters/index.js +++ b/src/components/ListTable/formatters/index.js @@ -1,6 +1,33 @@ -export { default as DetailFormatter } from './DetailFormatter' -export { default as DisplayFormatter } from './DisplayFormatter' -export { default as BooleanFormatter } from './ChoicesFormatter' -export { default as ActionsFormatter } from './ActionsFormatter' -export { default as LengthFormatter } from './LengthFormatter' +import DetailFormatter from './DetailFormatter' +import DisplayFormatter from './DisplayFormatter' +import BooleanFormatter from './ChoicesFormatter' +import ActionsFormatter from './ActionsFormatter' +import ExpandPreFormatter from './ExpandPreFormatter' +import LengthFormatter from './LengthFormatter' +import RouterFormatter from './RouterFormatter' +import OutputExpandFormatter from './OutputExpandFormatter' +import ExpandAssetPermissionFormatter from './ExpandAssetPermissionFormatter' +export default { + DetailFormatter, + DisplayFormatter, + BooleanFormatter, + ActionsFormatter, + ExpandPreFormatter, + LengthFormatter, + RouterFormatter, + OutputExpandFormatter, + ExpandAssetPermissionFormatter +} + +export { + DetailFormatter, + DisplayFormatter, + BooleanFormatter, + ActionsFormatter, + ExpandPreFormatter, + LengthFormatter, + RouterFormatter, + OutputExpandFormatter, + ExpandAssetPermissionFormatter +} diff --git a/src/i18n/langs/cn.js b/src/i18n/langs/cn.js index d3fb8b69f..64300f7c2 100644 --- a/src/i18n/langs/cn.js +++ b/src/i18n/langs/cn.js @@ -67,7 +67,10 @@ const cn = { 'cancel': '取消', 'Import': '导入', 'Export': '导出', - 'Other': '其它' + 'Other': '其它', + 'Create success': '创建成功', + 'Deactive selected': '禁用所选', + 'Active selected': '激活所选' }, route: { 'dashboard': '仪表盘', @@ -96,6 +99,7 @@ const cn = { 'Sessions': '会话管理', 'SessionOnline': '在线会话', 'SessionOffline': '历史会话', + 'SessionDetail': '会话详情', 'Commands': '命令记录', 'WebTerminal': 'Web终端', 'FileManager': '文件管理', @@ -112,7 +116,8 @@ const cn = { 'PasswordChangeLog': '改密日志', 'Settings': '系统设置', 'UserCreate': '创建用户', - 'UserGroupCreate': '创建用户组' + 'UserGroupCreate': '创建用户组', + 'UserUpdate': '更新用户' }, // 用户模块翻译 users: { @@ -326,41 +331,22 @@ const cn = { 'app_path': '应用路径' }, perms: { - 'asset_permission': '资产授权', - 'asset_permission_list': '资产授权列表', - 'asset_permission_detail': '资产授权详情', - 'create_asset_permission': '创建资产授权规则', - 'update_asset_permission': '更新资产授权规则', 'Asset permissions': '资产授权', - 'name': '名称', - 'user': '用户', - 'userGroup': '用户组', - 'asset': '资产', - 'node': '节点', - 'systemUser': '系统用户', - 'validity': '有效', - 'action': '动作', - 'update': '更新', - 'delete': '删除', - 'search': '搜索', - 'user_count': '用户数量', - 'user_group_count': '用户组数量', - 'asset_count': '资产数量', - 'node_count': '节点数量', - 'system_user_count': '系统用户数量', - 'date_start': '开始日期', - 'date_expired': '失效日期', - 'date_created': '创建日期', - 'created_by': '创建者', - 'comment': '备注', - 'quick_update': '快速更新', - 'active': '激活中', - 'users_and_user_groups': '用户或用户组', - 'assets_and_node': '资产或节点', + 'RefreshPermissionCache': '刷新授权缓存', + 'ReFreshSuccess': '刷新成功', + 'ReFreshFail': '刷新失败', + 'All': '全部', + 'Connect': '连接', + 'UpDownload': '上传下载', + 'UploadFile': '上传文件', + 'DownloadFile': '下载文件', 'Basic': '基本', 'User': '用户', 'Asset': '资产', 'Actions': '动作', + 'UserGroups': '用户组', + 'Node': '节点', + 'SystemUser': '系统用户', // 'RemoteApp': '远程应用', // @@ -373,30 +359,33 @@ const cn = { 'systemUser': '系统用户', 'remoteAddr': '远端地址', 'protocol': '协议', - 'loginForm': '登录来源', + 'loginFrom': '登录来源', 'command': '命令', 'dateStart': '开始日期', 'duration': '时长', 'terminate': '终断', - 'date_end': '结束日期', + 'dateEnd': '结束日期', 'commands': '命令记录', 'replay': '回放', 'download': '下载', - 'RiskLevel': '风险等级', + 'riskLevel': '风险等级', 'session': '会话', 'date': '日期', 'addr': '地址', 'active': '激活中', 'alive': '在线', 'StorageConfiguration': '存储配置', - 'join': '加入' + 'join': '加入', + 'goto': '转到', + 'sessionDetail': '会话详情', + 'quickModify': '快速修改' }, jobcenter: { 'RunTimes': '执行次数', - 'hosts': '主机', - 'success': '成功', - 'date': '日期', - 'time': '时间', + 'Hosts': '主机', + 'Success': '成功', + 'Date': '日期', + 'Time': '时间', 'run': '执行' }, tickets: { diff --git a/src/i18n/langs/en.js b/src/i18n/langs/en.js index 1c8d7d64c..a248365fc 100644 --- a/src/i18n/langs/en.js +++ b/src/i18n/langs/en.js @@ -18,7 +18,11 @@ const en = { 'assets': 'Assets', 'applications': 'Applications', 'perms': 'Perms', - 'sessions': 'Sessions', + 'Sessions': 'Sessions', + 'SessionOnline': 'Session Online', + 'SessionOffline': 'Session Offline', + 'SessionDetail': 'Session Detail', + 'Goto': 'Goto', 'jobcenter': 'Job Center' }, users: { @@ -265,18 +269,16 @@ const en = { 'database_app_count': 'DatabaseApp count' }, sessions: { - 'session_online_list': '', - 'session_detail': ' Session detail', 'id': '', 'user': 'Use', 'asset': 'Asset', - 'system_user': 'System user', - 'remote_addr': 'Remote addr', + 'systemUser': 'System user', + 'remoteAddr': 'Remote addr', 'protocol': 'Protocol', - 'login_form': 'Login from', + 'loginFrom': 'Login from', 'command': 'Command', - 'date_start': 'Date start', - 'duration': 'Druation', + 'dateStart': 'Date start', + 'duration': 'Duration', 'action': 'Action', 'search': 'Search', 'terminate_selected': 'Terminate selected', @@ -284,12 +286,15 @@ const en = { 'submit': 'Submit', 'terminate': 'Terminate', 'command_list': 'Command list', - 'date_end': 'Date end', + 'dateEnd': 'Date end', 'quick_modify': 'Quick modify', 'terminate_session': 'Terminate session', 'confirm': 'Confirm', 'commands': 'Commands', - 'join': 'join' + 'join': 'join', + 'goto': 'Goto', + 'sessionDetail': 'Session Detail', + 'quickModify': 'Quick Modify' }, setting: { 'setting': 'System Setting', diff --git a/src/layout/components/GenericCreateUpdatePage/index.vue b/src/layout/components/GenericCreateUpdatePage/index.vue index 35498a75b..02d906a4f 100644 --- a/src/layout/components/GenericCreateUpdatePage/index.vue +++ b/src/layout/components/GenericCreateUpdatePage/index.vue @@ -1,10 +1,17 @@ @@ -21,46 +28,105 @@ export default { type: String, required: true }, - method: { - type: String, - default: 'post' - }, fields: { type: Array, default: () => { return [] } }, - form: { + object: { type: Object, - default: () => { return {} } + default: () => ({}) + }, + initial: { + type: Object, + default: () => ({}) }, onSubmit: { type: Function, default: null + }, + getMethod: { + type: Function, + default: function() { + const params = this.$route.params + if (params.id) { + return 'put' + } else { + return 'post' + } + } + }, + getUrl: { + type: Function, + default: function() { + const params = this.$route.params + let url = this.url + if (params.id) { + url = `${url}/${params.id}/` + } + return url + } + } + }, + data() { + return { + form: {}, + loadding: true + } + }, + computed: { + method() { + const method = this.getMethod(this) + return method + }, + totalUrl() { + return this.getUrl() } }, mounted() { - console.log('generic', this.$attrs) - console.log(this.fields) + if (this.method === 'put') { + this.getObjectDetail() + } else { + this.form = Object.assign(this.form, this.initial) + this.loadding = false + } }, methods: { - handleSubmit(values) { + handleSubmit(values, form) { let handler = this.onSubmit || this.defaultOnSubmit handler = handler.bind(this) + const fields = form.$refs.elForm.fields console.log('submit', values) - return handler(values) + console.log('form.fields', fields) + return handler(values, form) }, - defaultOnSubmit(validValues) { - this.$axios.post(this.url, validValues).then( - () => { - const msg = this.$tc('Create success') - this.$message.success(msg) - setTimeout(() => { - this.$router.push({ name: 'UserList' }) - }, 500) + defaultPerformSubmit(validValues) { + return this.$axios[this.method](this.totalUrl, validValues) + }, + defaultOnSubmit(validValues, form) { + this.defaultPerformSubmit(validValues).then(() => { + const msg = this.$tc('Create success') + this.$message.success(msg) + this.$router.push({ name: 'UserList' }) + }).catch(error => { + console.log(form) + const response = error.response + const data = response.data + if (response.status === 400) { + this.errors.name = '你报错了滴滴滴' + console.log(data) } - ) + }) + }, + getObjectDetail() { + this.$axios.get(this.totalUrl).then(data => { + this.form = data + }).catch(error => { + console.log(error) + }).finally(() => { + this.loadding = false + }) } } } diff --git a/src/layout/components/BaseDetailPage/index.vue b/src/layout/components/GenericDetailPage/index.vue similarity index 98% rename from src/layout/components/BaseDetailPage/index.vue rename to src/layout/components/GenericDetailPage/index.vue index f47f12c5d..58d61f83f 100644 --- a/src/layout/components/BaseDetailPage/index.vue +++ b/src/layout/components/GenericDetailPage/index.vue @@ -25,7 +25,7 @@ import Page from '../Page/' import ActionsGroup from '@/components/ActionsGroup' export default { - name: 'BaseDetailPage', + name: 'GenericDetailPage', components: { Page, ActionsGroup diff --git a/src/layout/components/index.js b/src/layout/components/index.js index 62c228747..3d96c0b63 100644 --- a/src/layout/components/index.js +++ b/src/layout/components/index.js @@ -3,7 +3,7 @@ export { default as NavHeader } from './NavHeader' export { default as AppMain } from './AppMain' export { default as Page } from './Page' export { default as TagsView } from './TagsView' -export { default as BaseDetailPage } from './BaseDetailPage' +export { default as GenericDetailPage } from './GenericDetailPage' export { default as SubMenuPage } from './SubMenuPage' export { default as Footer } from './Footer' export { default as IBox } from './IBox' diff --git a/src/router/index.js b/src/router/index.js index f2d37a8be..7f99f0113 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -70,14 +70,14 @@ export const constantRoutes = [ component: () => import('@/views/users/UserCreateUpdate.vue'), // Parent router-view name: 'UserCreate', hidden: true, - meta: { title: 'UserCreate', activeMenu: '/users/users' } + meta: { title: 'UserCreate', activeMenu: '/users/users', action: 'create' } }, { - path: 'users/update/:id', + path: 'users/:id/update', component: () => import('@/views/users/UserCreateUpdate.vue'), // Parent router-view - name: 'UserEdit', + name: 'UserUpdate', hidden: true, - meta: { title: 'UserEdit' } + meta: { title: 'UserUpdate', activeMenu: '/users/users', action: 'update' } }, { path: 'users/detail/:id', @@ -274,6 +274,13 @@ export const constantRoutes = [ component: () => import('@/views/sessions/CommandList'), meta: { title: 'Commands' } }, + { + path: 'sessions/:id', + name: 'SessionDetail', + component: () => import('@/views/sessions/SessionDetail'), + meta: { title: 'SessionDetail' }, + hidden: true + }, { path: 'luna', name: 'WebTerminal', diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss index f29b19d40..55f646897 100644 --- a/src/styles/element-ui.scss +++ b/src/styles/element-ui.scss @@ -235,3 +235,29 @@ td .el-button.el-button--mini { .el-radio__input.is-checked+.el-radio__label { color: inherit; } + +.el-radio__input.is-checked .el-radio__inner { + border-color: #409EFF; + background-color: #409EFF; +} + +.el-checkbox__input.is-checked .el-checkbox__inner { + border-color: #409EFF; + background-color: #409EFF; +} + +.el-radio__inner:hover { + border-color: #409EFF; +} + +.el-textarea__inner { + border-radius: 0; +} + +.el-checkbox__input.is-checked .el-checkbox__inner, .el-checkbox__input.is-indeterminate .el-checkbox__inner { + border-color: #409EFF; + background-color: #409EFF; +} +.el-checkbox__inner:hover { + border-color: #409EFF; +} diff --git a/src/styles/index.scss b/src/styles/index.scss index 43ed2788a..eeb83163e 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -5,7 +5,6 @@ @import './sidebar.scss'; @import './element-index.css'; @import './menu.scss'; -@import 'vue-select/src/scss/vue-select.scss'; @import "./font-awesome/font-awesome.min.css"; body { diff --git a/src/utils/request.js b/src/utils/request.js index e2261db4a..cc3000b44 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -53,37 +53,34 @@ service.interceptors.response.use( const res = response.data // if the custom code is not 20000, it is judged as an error. - if (response.status < 200 || response.status > 300) { + if (response.status >= 200 && response.status < 400) { + if (response.config.raw === 1) { + return response + } + return res + } else if (response.status === 50008 || response.status === 50012 || response.status === 50014) { + MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { + confirmButtonText: 'Re-Login', + cancelButtonText: 'Cancel', + type: 'warning' + }).then(() => { + store.dispatch('user/resetToken').then(() => { + location.reload() + }) + }) + } else if (response.status === 400) { + console.log('status is 400') + return Promise.reject(res || 'Error') + } else { Message({ message: res.message || res.error || 'Error', type: 'error', duration: 5 * 1000 }) - - // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; - // 自定义错误码 - if (response.status === 50008 || response.status === 50012 || response.status === 50014) { - // to re-login - MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { - confirmButtonText: 'Re-Login', - cancelButtonText: 'Cancel', - type: 'warning' - }).then(() => { - store.dispatch('user/resetToken').then(() => { - location.reload() - }) - }) - } return Promise.reject(new Error(res.message || 'Error')) - } else { - if (response.config.raw === 1) { - return response - } - return res } }, error => { - console.log('err' + error) // for debug Message({ message: error.message, type: 'error', diff --git a/src/views/jobcenter/TaskList.vue b/src/views/jobcenter/TaskList.vue index 432e9bdaf..1c08ea5bc 100644 --- a/src/views/jobcenter/TaskList.vue +++ b/src/views/jobcenter/TaskList.vue @@ -3,8 +3,9 @@ diff --git a/src/views/sessions/CommandList.vue b/src/views/sessions/CommandList.vue index c3544ff95..323b30d4a 100644 --- a/src/views/sessions/CommandList.vue +++ b/src/views/sessions/CommandList.vue @@ -4,6 +4,8 @@ + + diff --git a/src/views/sessions/SessionOfflineList.vue b/src/views/sessions/SessionOfflineList.vue index a95a88080..3e9ead1fc 100644 --- a/src/views/sessions/SessionOfflineList.vue +++ b/src/views/sessions/SessionOfflineList.vue @@ -22,14 +22,16 @@ export default { ], columnsMeta: { index: { - type: 'index', - label: this.$t('sessions.id') + label: this.$t('sessions.id'), + formatter: function(row, column, cellValue, index) { + return { index + 1} + } }, command_amount: { label: this.$t('sessions.command') }, login_from: { - label: this.$t('sessions.loginForm') + label: this.$t('sessions.loginFrom') }, protocol: { label: this.$t('sessions.protocol'), diff --git a/src/views/sessions/SessionOnlineList.vue b/src/views/sessions/SessionOnlineList.vue index 9facf05a0..fc12c2ef4 100644 --- a/src/views/sessions/SessionOnlineList.vue +++ b/src/views/sessions/SessionOnlineList.vue @@ -22,8 +22,10 @@ export default { ], columnsMeta: { index: { - type: 'index', - label: this.$t('sessions.id') + label: this.$t('sessions.id'), + formatter: function(row, column, cellValue, index) { + return { index + 1} + } }, command_amount: { label: this.$t('sessions.command') diff --git a/src/views/users/UserCreateUpdate.vue b/src/views/users/UserCreateUpdate.vue index 17c8dce4b..d5c8e3673 100644 --- a/src/views/users/UserCreateUpdate.vue +++ b/src/views/users/UserCreateUpdate.vue @@ -1,10 +1,5 @@ diff --git a/src/views/users/UserGroupDetail/index.vue b/src/views/users/UserGroupDetail/index.vue index 8dfa51667..da789c9eb 100644 --- a/src/views/users/UserGroupDetail/index.vue +++ b/src/views/users/UserGroupDetail/index.vue @@ -1,5 +1,5 @@