mirror of
https://github.com/veops/oneterm.git
synced 2025-10-04 15:02:45 +08:00
feat(ui): add access control
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
"lodash.pick": "^4.4.0",
|
"lodash.pick": "^4.4.0",
|
||||||
"md5": "^2.2.1",
|
"md5": "^2.2.1",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
|
"moment-timezone": "^0.6.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"snabbdom": "^3.5.1",
|
"snabbdom": "^3.5.1",
|
||||||
"sortablejs": "1.9.0",
|
"sortablejs": "1.9.0",
|
||||||
|
@@ -30,3 +30,11 @@ export function deleteAssetById(id) {
|
|||||||
method: 'delete',
|
method: 'delete',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAssetPermissions(id, params) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/asset/${id}/permissions`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
32
oneterm-ui/src/modules/oneterm/api/authorizationV2.js
Normal file
32
oneterm-ui/src/modules/oneterm/api/authorizationV2.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
export function getAuthList(params) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/authorization_v2',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postAuth(data) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/authorization_v2',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putAuthById(id, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/authorization_v2/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteAuthById(id) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/authorization_v2/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
32
oneterm-ui/src/modules/oneterm/api/commandTemplate.js
Normal file
32
oneterm-ui/src/modules/oneterm/api/commandTemplate.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
export function getCommandTemplateList(params) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/command_template',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postCommandTemplate(data) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/command_template',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putCommandTemplateById(id, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/command_template/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCommandTemplateById(id) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/command_template/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
32
oneterm-ui/src/modules/oneterm/api/timeTemplate.js
Normal file
32
oneterm-ui/src/modules/oneterm/api/timeTemplate.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
export function getTimeTemplateList(params) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/time_template',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postTimeTemplate(data) {
|
||||||
|
return axios({
|
||||||
|
url: '/oneterm/v1/time_template',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putTimeTemplateById(id, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/time_template/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteTimeTemplateById(id) {
|
||||||
|
return axios({
|
||||||
|
url: `/oneterm/v1/time_template/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
@@ -6,7 +6,7 @@
|
|||||||
<table :class="{ 'c-min-table': colspan < 2 }" class="c-weektime-table">
|
<table :class="{ 'c-min-table': colspan < 2 }" class="c-weektime-table">
|
||||||
<thead class="c-weektime-head">
|
<thead class="c-weektime-head">
|
||||||
<tr>
|
<tr>
|
||||||
<th rowspan="8" class="week-td">{{ $t('oneterm.assetList.weektime') }}</th>
|
<th rowspan="8" class="week-td">{{ $t('oneterm.timeTemplate.weektime') }}</th>
|
||||||
<th :colspan="12 * colspan">00:00 - 12:00</th>
|
<th :colspan="12 * colspan">00:00 - 12:00</th>
|
||||||
<th :colspan="12 * colspan">12:00 - 24:00</th>
|
<th :colspan="12 * colspan">12:00 - 24:00</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="c-weektime-body">
|
<tbody class="c-weektime-body">
|
||||||
<tr v-for="t in data" :key="t.row">
|
<tr v-for="t in data" :key="t.row">
|
||||||
<td>{{ $t(t.week) }}</td>
|
<td>{{ $t(`oneterm.timeTemplate.day${t.day}`) }}</td>
|
||||||
<td
|
<td
|
||||||
v-for="n in t.child"
|
v-for="n in t.child"
|
||||||
:key="`${n.row}-${n.col}`"
|
:key="`${n.row}-${n.col}`"
|
||||||
@@ -32,15 +32,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="49" class="c-weektime-preview">
|
<td colspan="49" class="c-weektime-preview">
|
||||||
<div class="g-clearfix c-weektime-con">
|
<div class="g-clearfix c-weektime-con">
|
||||||
<span class="g-pull-left">{{
|
<span class="g-pull-left">
|
||||||
selectState ? $t(`oneterm.assetList.selectedTime`) : $t(`oneterm.assetList.drag`)
|
{{ selectState ? $t('oneterm.timeTemplate.selectedTime') : $t('oneterm.timeTemplate.drag') }}
|
||||||
}}</span>
|
</span>
|
||||||
<a @click.prevent="$emit('onClear')" class="g-pull-right">{{ $t(`clear`) }}</a>
|
<a @click.prevent="$emit('onClear')" class="g-pull-right">{{ $t(`clear`) }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectState" class="c-weektime-time">
|
<div v-if="selectState" class="c-weektime-time">
|
||||||
<div v-for="t in selectValue" :key="t.id">
|
<div v-for="t in selectValue" :key="t.id">
|
||||||
<p v-if="t.value && t.value.length">
|
<p v-if="t.value && t.value.length">
|
||||||
<span class="g-tip-text">{{ $t(t.week) }}:</span>
|
<span class="g-tip-text">{{ $t(`oneterm.timeTemplate.day${t.day}`) }}:</span>
|
||||||
<span>{{ formatSelectValue(t.value) }}</span>
|
<span>{{ formatSelectValue(t.value) }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,7 +178,7 @@ export default {
|
|||||||
const _selectValue = this.data.map((item) => {
|
const _selectValue = this.data.map((item) => {
|
||||||
return {
|
return {
|
||||||
id: item.row,
|
id: item.row,
|
||||||
week: item.week,
|
day: item.day,
|
||||||
value: item.child.filter((c) => c.check).map((c) => c.value),
|
value: item.child.filter((c) => c.check).map((c) => c.value),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -1,70 +1,39 @@
|
|||||||
const formatDate = (date, fmt) => {
|
// Format a Date object to 'HH:mm'
|
||||||
const o = {
|
const formatTime = (date) => {
|
||||||
'M+': date.getMonth() + 1,
|
const h = String(date.getHours()).padStart(2, '0')
|
||||||
'd+': date.getDate(),
|
const m = String(date.getMinutes()).padStart(2, '0')
|
||||||
'h+': date.getHours(),
|
return `${h}:${m}`
|
||||||
'm+': date.getMinutes(),
|
|
||||||
's+': date.getSeconds(),
|
|
||||||
'q+': Math.floor((date.getMonth() + 3) / 3),
|
|
||||||
S: date.getMilliseconds()
|
|
||||||
}
|
|
||||||
if (/(y+)/.test(fmt)) {
|
|
||||||
fmt = fmt.replace(
|
|
||||||
RegExp.$1,
|
|
||||||
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (const k in o) {
|
|
||||||
if (new RegExp('(' + k + ')').test(fmt)) {
|
|
||||||
fmt = fmt.replace(
|
|
||||||
RegExp.$1,
|
|
||||||
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createArr = (len) => {
|
// Generate half-hour intervals for one day, last end is 23:59
|
||||||
return Array.from(Array(len)).map((ret, id) => id)
|
const generateDayIntervals = (day, row, total = 48) => {
|
||||||
}
|
const intervals = []
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
const formatWeektime = (col) => {
|
const startMinutes = i * 30
|
||||||
const timestamp = 1542384000000 // '2018-11-17 00:00:00'
|
const endMinutes = (i + 1) * 30
|
||||||
const beginstamp = timestamp + col * 1800000 // col * 30 * 60 * 1000
|
const begin = formatTime(new Date(2000, 0, 1, 0, startMinutes))
|
||||||
const endstamp = beginstamp + 1800000
|
// Last interval ends at 23:59, others at next half hour
|
||||||
|
const end =
|
||||||
const begin = formatDate(new Date(beginstamp), 'hh:mm')
|
i === total - 1
|
||||||
const end = formatDate(new Date(endstamp), 'hh:mm')
|
? '23:59'
|
||||||
return `${begin}~${end}`
|
: formatTime(new Date(2000, 0, 1, 0, endMinutes))
|
||||||
}
|
intervals.push({
|
||||||
|
day,
|
||||||
const data = [
|
value: `${begin}~${end}`,
|
||||||
'星期一',
|
begin,
|
||||||
'星期二',
|
end,
|
||||||
'星期三',
|
|
||||||
'星期四',
|
|
||||||
'星期五',
|
|
||||||
'星期六',
|
|
||||||
'星期日'
|
|
||||||
].map((ret, index) => {
|
|
||||||
const children = (ret, row, max) => {
|
|
||||||
return createArr(max).map((t, col) => {
|
|
||||||
return {
|
|
||||||
week: ret,
|
|
||||||
value: formatWeektime(col),
|
|
||||||
begin: formatWeektime(col).split('~')[0],
|
|
||||||
end: formatWeektime(col).split('~')[1],
|
|
||||||
row,
|
row,
|
||||||
col
|
col: i
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return {
|
return intervals
|
||||||
week: ret,
|
}
|
||||||
row: index,
|
|
||||||
child: children(ret, index, 48)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default data
|
// Generate week data: 7 days, each with 48 half-hour intervals
|
||||||
|
const weekTimeData = Array.from({ length: 7 }, (_, i) => ({
|
||||||
|
day: i + 1,
|
||||||
|
row: i,
|
||||||
|
child: generateDayIntervals(i + 1, i, 48)
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default weekTimeData
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<div class="enabled-status">
|
||||||
|
<div class="enabled-status-display">
|
||||||
|
<span
|
||||||
|
class="enabled-status-dot"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: statusColor + '22',
|
||||||
|
'--innerBackgroundColor': statusColor
|
||||||
|
}"
|
||||||
|
></span>
|
||||||
|
<span>
|
||||||
|
{{ $t(statusText) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a-popconfirm
|
||||||
|
:title="$t('oneterm.confirmEnable')"
|
||||||
|
@confirm="$emit('change', !status)"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:checked="status"
|
||||||
|
:checked-children="$t('oneterm.enabled')"
|
||||||
|
:un-checked-children="$t('oneterm.disabled')"
|
||||||
|
/>
|
||||||
|
</a-popconfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'EnabledStatus',
|
||||||
|
props: {
|
||||||
|
status: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
statusColor() {
|
||||||
|
return this.status ? '#52c41a' : '#A5A9BC'
|
||||||
|
},
|
||||||
|
statusText() {
|
||||||
|
return this.status ? 'oneterm.enabled' : 'oneterm.disabled'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.enabled-status {
|
||||||
|
width: min-content;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
/deep/ .ant-switch {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dot {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-top: -4px;
|
||||||
|
margin-left: -4px;
|
||||||
|
background-color: var(--innerBackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.enabled-status-display {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .ant-switch {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -2,13 +2,23 @@
|
|||||||
<a-modal
|
<a-modal
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="800"
|
:width="800"
|
||||||
|
:footer="null"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
@ok="handleOk"
|
|
||||||
>
|
>
|
||||||
<template v-if="showACLConfig">
|
<a-tabs v-model="tabKey">
|
||||||
<div class="auth-node-title">
|
<a-tab-pane key="accessPermission" :tab="$t('oneterm.assetList.accessPermission')">
|
||||||
{{ $t(grantTitle) }}
|
<PermissionForm
|
||||||
</div>
|
ref="permissionFormRef"
|
||||||
|
:dataType="dataType"
|
||||||
|
:ids="ids"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
v-if="showACLConfig"
|
||||||
|
key="operationPermissions"
|
||||||
|
:tab="$t(grantTitle)"
|
||||||
|
>
|
||||||
<ACLTable
|
<ACLTable
|
||||||
:tableData="aclTableData"
|
:tableData="aclTableData"
|
||||||
:resourceId="resourceId"
|
:resourceId="resourceId"
|
||||||
@@ -17,27 +27,8 @@
|
|||||||
<span class="grant-button" @click="openGrantUserModal('depart')">{{ $t('oneterm.assetList.grantUserOrDep') }}</span>
|
<span class="grant-button" @click="openGrantUserModal('depart')">{{ $t('oneterm.assetList.grantUserOrDep') }}</span>
|
||||||
<span class="grant-button" @click="openGrantUserModal('role')">{{ $t('oneterm.assetList.grantRole') }}</span>
|
<span class="grant-button" @click="openGrantUserModal('role')">{{ $t('oneterm.assetList.grantRole') }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
<div class="auth-node-title">
|
|
||||||
{{ $t('oneterm.assetList.grantLogin') }}
|
|
||||||
</div>
|
|
||||||
<EmployeeTreeSelect
|
|
||||||
v-model="rids"
|
|
||||||
multiple
|
|
||||||
:idType="2"
|
|
||||||
departmentKey="acl_rid"
|
|
||||||
employeeKey="acl_rid"
|
|
||||||
:placeholder="`${$t(`placeholder2`)}`"
|
|
||||||
class="custom-treeselect custom-treeselect-white"
|
|
||||||
:style="{
|
|
||||||
'--custom-height': '32px',
|
|
||||||
lineHeight: '32px',
|
|
||||||
'--custom-multiple-lineHeight': '18px',
|
|
||||||
}"
|
|
||||||
:limit="1"
|
|
||||||
:otherOptions="visualRoleList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GrantUserModal
|
<GrantUserModal
|
||||||
ref="grantUserModalRef"
|
ref="grantUserModalRef"
|
||||||
@@ -49,30 +40,28 @@
|
|||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { getAuth, postAuth } from '@/modules/oneterm/api/authorization.js'
|
|
||||||
import { getResourcePerms } from '@/modules/acl/api/permission'
|
import { getResourcePerms } from '@/modules/acl/api/permission'
|
||||||
import { searchRole } from '@/modules/acl/api/role'
|
|
||||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
import PermissionForm from './permissionForm.vue'
|
||||||
import ACLTable from './aclTable.vue'
|
import ACLTable from './aclTable.vue'
|
||||||
import GrantUserModal from './grantUserModal.vue'
|
import GrantUserModal from './grantUserModal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GrantModal',
|
name: 'GrantModal',
|
||||||
components: {
|
components: {
|
||||||
EmployeeTreeSelect,
|
PermissionForm,
|
||||||
ACLTable,
|
ACLTable,
|
||||||
GrantUserModal
|
GrantUserModal
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
tabKey: 'accessPermission',
|
||||||
visible: false,
|
visible: false,
|
||||||
rids: [], // 登录权限
|
|
||||||
resourceId: '', // acl resource id
|
resourceId: '', // acl resource id
|
||||||
aclTableData: [], // acl data
|
aclTableData: [], // acl data
|
||||||
ids: [], // id 列表
|
ids: [], // id list
|
||||||
dataType: 'node',
|
dataType: 'node',
|
||||||
showACLConfig: true, // 是否展示acl 配置
|
showACLConfig: true,
|
||||||
visualRoleList: [], // 虚拟角色
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -83,11 +72,11 @@ export default {
|
|||||||
grantTitle() {
|
grantTitle() {
|
||||||
switch (this.dataType) {
|
switch (this.dataType) {
|
||||||
case 'node':
|
case 'node':
|
||||||
return 'oneterm.assetList.grantCatalog'
|
return 'oneterm.assetList.folderOperationPermissions'
|
||||||
case 'asset':
|
case 'asset':
|
||||||
return 'oneterm.assetList.grantAsset'
|
return 'oneterm.assetList.assetOperationPermissions'
|
||||||
case 'account':
|
case 'account':
|
||||||
return 'oneterm.assetList.grantAccount'
|
return 'oneterm.assetList.accountOperationPermissions'
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -99,12 +88,11 @@ export default {
|
|||||||
ids,
|
ids,
|
||||||
resourceId,
|
resourceId,
|
||||||
}) {
|
}) {
|
||||||
|
this.tabKey = 'accessPermission'
|
||||||
this.showACLConfig = type === 'node' || (type !== 'node' && ids.length === 1)
|
this.showACLConfig = type === 'node' || (type !== 'node' && ids.length === 1)
|
||||||
this.ids = ids
|
this.ids = ids
|
||||||
this.dataType = type
|
this.dataType = type
|
||||||
this.resourceId = resourceId || ''
|
this.resourceId = resourceId ?? ''
|
||||||
|
|
||||||
this.loadRoles()
|
|
||||||
|
|
||||||
let aclTableData = []
|
let aclTableData = []
|
||||||
if (this.showACLConfig) {
|
if (this.showACLConfig) {
|
||||||
@@ -134,79 +122,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.aclTableData = aclTableData
|
this.aclTableData = aclTableData
|
||||||
|
|
||||||
const getAuthParams = {
|
|
||||||
page_index: 1,
|
|
||||||
page_size: 9999,
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case 'node':
|
|
||||||
getAuthParams.node_id = ids[0]
|
|
||||||
break
|
|
||||||
case 'asset':
|
|
||||||
if (ids.length === 1) {
|
|
||||||
getAuthParams.asset_id = ids[0]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'account':
|
|
||||||
if (ids.length === 1) {
|
|
||||||
getAuthParams.account_id = ids[0]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
let rids = []
|
|
||||||
if (
|
|
||||||
getAuthParams.node_id ||
|
|
||||||
getAuthParams.asset_id ||
|
|
||||||
getAuthParams.account_id
|
|
||||||
) {
|
|
||||||
const res = await getAuth(getAuthParams)
|
|
||||||
|
|
||||||
let newRids = []
|
|
||||||
if (res?.data?.list?.length) {
|
|
||||||
res.data.list.forEach((item) => {
|
|
||||||
newRids.push(...(item?.rids || []))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newRids = _.uniq(newRids)
|
|
||||||
if (newRids?.length) {
|
|
||||||
rids = newRids.map((id) => `employee-${id}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rids = rids
|
|
||||||
this.visible = true
|
this.visible = true
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadRoles() {
|
|
||||||
const res = await searchRole({ page_size: 9999, page: 1, app_id: 'oneterm', user_role: 0, user_only: 0, is_all: true })
|
|
||||||
|
|
||||||
const visualRoleList = []
|
|
||||||
const roleList = (res?.roles || []).filter((item) => !/_virtual$/.test(item.name))
|
|
||||||
|
|
||||||
if (roleList.length) {
|
|
||||||
visualRoleList.push({
|
|
||||||
acl_rid: -100,
|
|
||||||
department_name: this.$t('acl.visualRole'),
|
|
||||||
sub_departments: [],
|
|
||||||
employees: roleList.map((item) => {
|
|
||||||
return {
|
|
||||||
nickname: item.name,
|
|
||||||
acl_rid: item.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$set(this, 'visualRoleList', visualRoleList)
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
this.rids = []
|
this.rids = []
|
||||||
this.visible = false
|
this.visible = false
|
||||||
|
if (this.$refs.permissionFormRef) {
|
||||||
|
this.$refs.permissionFormRef.resetFields()
|
||||||
|
}
|
||||||
this.resourceId = ''
|
this.resourceId = ''
|
||||||
this.aclTableData = []
|
this.aclTableData = []
|
||||||
this.ids = []
|
this.ids = []
|
||||||
@@ -214,52 +138,6 @@ export default {
|
|||||||
this.showACLConfig = true
|
this.showACLConfig = true
|
||||||
},
|
},
|
||||||
|
|
||||||
handleOk() {
|
|
||||||
const rids = this.rids.map((rid) => {
|
|
||||||
return Number(rid?.split?.('-')?.[1])
|
|
||||||
})
|
|
||||||
|
|
||||||
switch (this.dataType) {
|
|
||||||
case 'node':
|
|
||||||
postAuth({
|
|
||||||
rids,
|
|
||||||
node_id: this.ids[0]
|
|
||||||
}).then(() => {
|
|
||||||
this.$message.success(this.$t('updateSuccess'))
|
|
||||||
this.handleCancel()
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'asset':
|
|
||||||
Promise.all(
|
|
||||||
this.ids.map((id) => {
|
|
||||||
return postAuth({
|
|
||||||
rids,
|
|
||||||
asset_id: id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
).then(() => {
|
|
||||||
this.$message.success(this.$t('updateSuccess'))
|
|
||||||
this.handleCancel()
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'account':
|
|
||||||
Promise.all(
|
|
||||||
this.ids.map((id) => {
|
|
||||||
return postAuth({
|
|
||||||
rids,
|
|
||||||
account_id: id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
).then(() => {
|
|
||||||
this.$message.success(this.$t('updateSuccess'))
|
|
||||||
this.handleCancel()
|
|
||||||
})
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
openGrantUserModal(type) {
|
openGrantUserModal(type) {
|
||||||
this.$refs.grantUserModalRef.open(type)
|
this.$refs.grantUserModalRef.open(type)
|
||||||
},
|
},
|
||||||
|
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model
|
||||||
|
ref="permissionFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :label="$t('oneterm.assetList.grantRole')" prop="rids">
|
||||||
|
<EmployeeTreeSelect
|
||||||
|
v-model="form.rids"
|
||||||
|
multiple
|
||||||
|
:idType="2"
|
||||||
|
departmentKey="acl_rid"
|
||||||
|
employeeKey="acl_rid"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
class="custom-treeselect custom-treeselect-white"
|
||||||
|
:style="{
|
||||||
|
'--custom-height': '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
'--custom-multiple-lineHeight': '18px',
|
||||||
|
}"
|
||||||
|
:limit="1"
|
||||||
|
:otherOptions="visualRoleList"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.assetList.operationPermissions')" prop="permissions">
|
||||||
|
<PermissionCheckbox
|
||||||
|
:value="form.permissions"
|
||||||
|
@change="(key, checked) => form.permissions[key] = checked"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<div class="permission-form-operation">
|
||||||
|
<a-button @click="handleCancel">
|
||||||
|
{{ $t('cancel') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleOk"
|
||||||
|
>
|
||||||
|
{{ $t('confirm') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form-model>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { postAuth } from '@/modules/oneterm/api/authorizationV2.js'
|
||||||
|
import { searchRole } from '@/modules/acl/api/role'
|
||||||
|
import { getConfig } from '@/modules/oneterm/api/config'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
import { TARGET_SELECT_TYPE } from '@/modules/oneterm/views/access/auth/constants.js'
|
||||||
|
|
||||||
|
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||||
|
import PermissionCheckbox from '@/modules/oneterm/views/systemSettings/accessControl/permissionCheckbox.vue'
|
||||||
|
|
||||||
|
const DEFAULT_PERMISSIONS = Object.values(PERMISSION_TYPE).reduce((config, key) => {
|
||||||
|
config[key] = false
|
||||||
|
return config
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PermissionForm',
|
||||||
|
components: {
|
||||||
|
EmployeeTreeSelect,
|
||||||
|
PermissionCheckbox
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
dataType: {
|
||||||
|
type: String,
|
||||||
|
default: 'node'
|
||||||
|
},
|
||||||
|
ids: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
rids: [],
|
||||||
|
permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
rids: [{ required: true, message: this.$t('placeholder2') }],
|
||||||
|
},
|
||||||
|
visualRoleList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initDefaultPermissions()
|
||||||
|
this.loadRoles()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initDefaultPermissions() {
|
||||||
|
getConfig({
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
const default_permissions = res?.data?.default_permissions
|
||||||
|
Object.keys(DEFAULT_PERMISSIONS).forEach((key) => {
|
||||||
|
DEFAULT_PERMISSIONS[key] = default_permissions?.[key] ?? DEFAULT_PERMISSIONS[key]
|
||||||
|
})
|
||||||
|
this.form.permissions = { ...DEFAULT_PERMISSIONS }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async loadRoles() {
|
||||||
|
const res = await searchRole({ page_size: 9999, page: 1, app_id: 'oneterm', user_role: 0, user_only: 0, is_all: true })
|
||||||
|
|
||||||
|
const visualRoleList = []
|
||||||
|
const roleList = (res?.roles || []).filter((item) => !/_virtual$/.test(item.name))
|
||||||
|
|
||||||
|
if (roleList.length) {
|
||||||
|
visualRoleList.push({
|
||||||
|
acl_rid: -100,
|
||||||
|
department_name: this.$t('acl.visualRole'),
|
||||||
|
sub_departments: [],
|
||||||
|
employees: roleList.map((item) => {
|
||||||
|
return {
|
||||||
|
nickname: item.name,
|
||||||
|
acl_rid: item.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.visualRoleList = visualRoleList
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.resetFields()
|
||||||
|
this.$emit('cancel')
|
||||||
|
},
|
||||||
|
resetFields() {
|
||||||
|
this.form = {
|
||||||
|
rids: [],
|
||||||
|
permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
|
}
|
||||||
|
this.$refs.permissionFormRef.resetFields()
|
||||||
|
},
|
||||||
|
handleOk() {
|
||||||
|
this.$refs.permissionFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
const params = this.handleParams()
|
||||||
|
postAuth(params).then(() => {
|
||||||
|
this.$message.success(this.$t('updateSuccess'))
|
||||||
|
this.handleCancel()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleParams() {
|
||||||
|
const rids = this.form.rids.map((rid) => {
|
||||||
|
return Number(rid?.split?.('-')?.[1])
|
||||||
|
})
|
||||||
|
const params = {
|
||||||
|
name: `${this.dataType}-${this.ids.join(',')}-${uuidv4()}`,
|
||||||
|
rids,
|
||||||
|
permissions: this.form.permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
;['node', 'account', 'asset'].forEach((key) => {
|
||||||
|
params[`${key}_selector`] = {
|
||||||
|
type: TARGET_SELECT_TYPE.ID,
|
||||||
|
values: key === this.dataType ? this.ids.map((id) => String(id)) : []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.permission-form-operation {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
column-gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -16,7 +16,7 @@ const oneterm_en = {
|
|||||||
accessRestrictions: 'Access Restrictions',
|
accessRestrictions: 'Access Restrictions',
|
||||||
comment: 'Comment',
|
comment: 'Comment',
|
||||||
node: 'Node',
|
node: 'Node',
|
||||||
catalog: 'Catalog',
|
folder: 'Folder',
|
||||||
cmdbType: 'CMDB Type',
|
cmdbType: 'CMDB Type',
|
||||||
fieldMap: 'Field Map',
|
fieldMap: 'Field Map',
|
||||||
field: 'Field',
|
field: 'Field',
|
||||||
@@ -33,13 +33,25 @@ const oneterm_en = {
|
|||||||
editPublicKey: 'Edit Public Key',
|
editPublicKey: 'Edit Public Key',
|
||||||
assetCount: 'Asset Count',
|
assetCount: 'Asset Count',
|
||||||
macAddress: 'Mac Address',
|
macAddress: 'Mac Address',
|
||||||
|
enabled: 'Enabled',
|
||||||
|
disabled: 'Disabled',
|
||||||
|
isEnable: 'Is Enable',
|
||||||
|
confirmEnable: 'Confirm switch enabled?',
|
||||||
|
switching: 'Switching',
|
||||||
|
switchingTip: 'Switching, total of {total}, {successNum} succeeded, {errorNum} failed',
|
||||||
|
permissions: 'Permissions',
|
||||||
menu: {
|
menu: {
|
||||||
oneterm: 'OneTerm',
|
oneterm: 'OneTerm',
|
||||||
workStation: 'Work Station',
|
workStation: 'Work Station',
|
||||||
|
resourceControl: 'Resource Control',
|
||||||
|
basicResource: 'Basic Resource',
|
||||||
assetManagement: 'Asset Management',
|
assetManagement: 'Asset Management',
|
||||||
assets: 'Assets',
|
gatewayManagement: 'Gateway Management',
|
||||||
gateways: 'Gateways',
|
accountManagement: 'Account Management',
|
||||||
accounts: 'Accounts',
|
accessControl: 'Access Control',
|
||||||
|
accessAuthorization: 'Access Authorization',
|
||||||
|
commandFilter: 'Command Filter',
|
||||||
|
accessTime: 'Access Time',
|
||||||
security: 'Security',
|
security: 'Security',
|
||||||
sessionAuditing: 'Session Auditing',
|
sessionAuditing: 'Session Auditing',
|
||||||
onlineSession: 'Online Session',
|
onlineSession: 'Online Session',
|
||||||
@@ -81,7 +93,8 @@ const oneterm_en = {
|
|||||||
pageUnloadMessage: 'Are you sure you want to leave the page?',
|
pageUnloadMessage: 'Are you sure you want to leave the page?',
|
||||||
batchExecution: 'Batch Execution',
|
batchExecution: 'Batch Execution',
|
||||||
chooseAssets: 'Choose Assets',
|
chooseAssets: 'Choose Assets',
|
||||||
batchExecutionPlaceholder: 'Please input the command, press Enter to execute'
|
batchExecutionPlaceholder: 'Please input the command, press Enter to execute',
|
||||||
|
assetShare: 'Asset Share'
|
||||||
},
|
},
|
||||||
sessionTable: {
|
sessionTable: {
|
||||||
target: 'Target',
|
target: 'Target',
|
||||||
@@ -105,12 +118,12 @@ const oneterm_en = {
|
|||||||
assetList: {
|
assetList: {
|
||||||
grantUser: 'Granted User',
|
grantUser: 'Granted User',
|
||||||
assetTree: 'Asset Tree',
|
assetTree: 'Asset Tree',
|
||||||
createCatalog: 'Create Catalog',
|
createFolder: 'Create Folder',
|
||||||
editCatalog: 'Edit Catalog',
|
editFolder: 'Edit Folder',
|
||||||
deleteCatalog: 'Delete Catalog',
|
deleteFolder: 'Delete Folder',
|
||||||
grantCatalog: 'Grant Catalog',
|
grantFolder: 'Grant Folder',
|
||||||
ip: 'IP',
|
ip: 'IP',
|
||||||
catalogName: 'Catalog Name',
|
folderName: 'Folder Name',
|
||||||
connectable: 'Connectable',
|
connectable: 'Connectable',
|
||||||
connected: 'Connected',
|
connected: 'Connected',
|
||||||
error: 'Error',
|
error: 'Error',
|
||||||
@@ -125,9 +138,6 @@ const oneterm_en = {
|
|||||||
commandIntercept: 'Command Intercept',
|
commandIntercept: 'Command Intercept',
|
||||||
allowAccess: 'Allow Access',
|
allowAccess: 'Allow Access',
|
||||||
prohibitAccess: 'Prohibit Access',
|
prohibitAccess: 'Prohibit Access',
|
||||||
weektime: 'week/time',
|
|
||||||
selectedTime: 'Selected Time',
|
|
||||||
drag: 'Drag the mouse to select time',
|
|
||||||
assetList: 'Asset List',
|
assetList: 'Asset List',
|
||||||
createAsset: 'Create Asset',
|
createAsset: 'Create Asset',
|
||||||
editAsset: 'Edit Asset',
|
editAsset: 'Edit Asset',
|
||||||
@@ -161,10 +171,15 @@ const oneterm_en = {
|
|||||||
grantLogin: 'Grant Login',
|
grantLogin: 'Grant Login',
|
||||||
grantUserOrDep: 'Grant User/Department',
|
grantUserOrDep: 'Grant User/Department',
|
||||||
grantRole: 'Grant Role',
|
grantRole: 'Grant Role',
|
||||||
createSubCatalog: 'Create Sub Catalog',
|
createSubFolder: 'Create Sub Folder',
|
||||||
assetSearchTip: 'Search in Asset',
|
assetSearchTip: 'Search in Asset',
|
||||||
basic: 'Basic',
|
basic: 'Basic',
|
||||||
database: 'Database'
|
database: 'Database',
|
||||||
|
folderOperationPermissions: 'Folder Operation Permissions',
|
||||||
|
assetOperationPermissions: 'Asset Operation Permissions',
|
||||||
|
accountOperationPermissions: 'Account Operation Permissions',
|
||||||
|
accessPermission: 'Access Permission',
|
||||||
|
operationPermissions: 'Operation Permissions'
|
||||||
},
|
},
|
||||||
log: {
|
log: {
|
||||||
time: 'Time',
|
time: 'Time',
|
||||||
@@ -193,26 +208,51 @@ const oneterm_en = {
|
|||||||
clipboard: 'Clipboard'
|
clipboard: 'Clipboard'
|
||||||
},
|
},
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
commandIntercept: 'Command Intercept',
|
accessControl: 'Access Control',
|
||||||
terminalControl: 'Terminal Control',
|
|
||||||
quickCommand: 'Quick Command',
|
quickCommand: 'Quick Command',
|
||||||
terminalDisplay: 'Terminal Display',
|
terminalDisplay: 'Terminal Display',
|
||||||
publicKey: 'Public Key',
|
publicKey: 'Public Key',
|
||||||
publicKeyTip: 'Password-free authentication for command terminals to log into the SSH service of the bastion',
|
publicKeyTip: 'Password-free authentication for command terminals to log into the SSH service of the bastion',
|
||||||
storageConfig: 'Storage Config'
|
storageConfig: 'Storage Config'
|
||||||
},
|
},
|
||||||
commandIntercept: {
|
commandFilter: {
|
||||||
enable: 'Enable',
|
name: 'Command Filter',
|
||||||
|
commandManagement: 'Command Management',
|
||||||
|
commandTemplateManagement: 'Command Template Management',
|
||||||
regexp: 'RegExp',
|
regexp: 'RegExp',
|
||||||
createCommand: 'Create Command',
|
createCommand: 'Create Command',
|
||||||
editCommand: 'Edit Command',
|
editCommand: 'Edit Command',
|
||||||
|
category: 'Category',
|
||||||
|
securityRelated: 'Security Related',
|
||||||
|
systemOperations: 'System Operations',
|
||||||
|
databaseOperations: 'Database Operations',
|
||||||
|
networkOperations: 'Network Operations',
|
||||||
|
fileOperations: 'File Operations',
|
||||||
|
developmentRelated: 'DevelopmentRelated',
|
||||||
|
riskLevel: 'Risk Level',
|
||||||
|
safe: 'Safe',
|
||||||
|
warning: 'Warning',
|
||||||
|
dangerous: 'Dangerous',
|
||||||
|
criticalDanger: 'Critical Danger',
|
||||||
|
commandCount: 'Command Count',
|
||||||
|
createCommandTemplate: 'Create Command Template',
|
||||||
|
editCommandTemplate: 'Edit Command Template',
|
||||||
|
selectCommand: 'Select Command',
|
||||||
|
unselectCommand: 'Unselect',
|
||||||
|
selectedCommand: 'Selected',
|
||||||
},
|
},
|
||||||
terminalControl: {
|
accessControl: {
|
||||||
timeout: 'Maximum session free time',
|
timeout: 'Maximum session free time',
|
||||||
allowCopy: 'Allow Copy',
|
copyTip: 'The local device can get the contents of the clipboard text in the remote desktop in real time.',
|
||||||
allowPaste: 'Allow Paste',
|
pasteTip: 'Remote Desktop can get the content of the text currently copied by the local device via the clipboard.',
|
||||||
copyTip: 'Copy: The local device can get the contents of the clipboard text in the remote desktop in real time.',
|
permissionConfig: 'Permission Config',
|
||||||
pasteTip: 'Paste: Remote Desktop can get the content of the text currently copied by the local device via the clipboard.'
|
permissionConfigTip: 'Uploading and downloading are for SSH and RDP protocols, copying and pasting are for RDP protocols',
|
||||||
|
connect: 'Connect',
|
||||||
|
upload: 'Upload',
|
||||||
|
download: 'Download',
|
||||||
|
copy: 'Copy',
|
||||||
|
paste: 'Paste',
|
||||||
|
share: 'Share'
|
||||||
},
|
},
|
||||||
terminalDisplay: {
|
terminalDisplay: {
|
||||||
fontSetting: 'Font Setting',
|
fontSetting: 'Font Setting',
|
||||||
@@ -268,7 +308,6 @@ const oneterm_en = {
|
|||||||
createConfig: 'Create Config',
|
createConfig: 'Create Config',
|
||||||
editConfig: 'Edit Config',
|
editConfig: 'Edit Config',
|
||||||
local: 'Local',
|
local: 'Local',
|
||||||
isEnable: 'Is Enable',
|
|
||||||
retentionDays: 'Retention Days',
|
retentionDays: 'Retention Days',
|
||||||
archiveDays: 'Archive Days',
|
archiveDays: 'Archive Days',
|
||||||
isCleanupEnabled: 'Is Cleanup Enabled',
|
isCleanupEnabled: 'Is Cleanup Enabled',
|
||||||
@@ -302,8 +341,76 @@ const oneterm_en = {
|
|||||||
basicConfig: 'Basic Config',
|
basicConfig: 'Basic Config',
|
||||||
advancedConfig: 'Advanced Config',
|
advancedConfig: 'Advanced Config',
|
||||||
advancedConfigTip: 'Configure the detailed parameters and strategies for storage',
|
advancedConfigTip: 'Configure the detailed parameters and strategies for storage',
|
||||||
confirmEnable: 'Confirm toggle enable?',
|
|
||||||
confirmPrimaryStorage: 'Confirm switching primary storage'
|
confirmPrimaryStorage: 'Confirm switching primary storage'
|
||||||
|
},
|
||||||
|
timeTemplate: {
|
||||||
|
createTimeTemplate: 'Create Time Template',
|
||||||
|
editTimeTemplate: 'Edit Time Template',
|
||||||
|
timeZone: 'Time Zone',
|
||||||
|
category: 'Category',
|
||||||
|
workTime: 'Work Time',
|
||||||
|
dutyTime: 'Duty Time',
|
||||||
|
maintenanceTime: 'Maintenance Time',
|
||||||
|
emergencyResponse: 'Emergency Response',
|
||||||
|
allTime: '24/7 Access',
|
||||||
|
timeRange: 'Time Range',
|
||||||
|
weektime: 'week/time',
|
||||||
|
selectedTime: 'Selected Time',
|
||||||
|
drag: 'Drag the mouse to select time',
|
||||||
|
day1: 'Monday',
|
||||||
|
day2: 'Tuesday',
|
||||||
|
day3: 'Wednesday',
|
||||||
|
day4: 'Thursday',
|
||||||
|
day5: 'Friday',
|
||||||
|
day6: 'Saturday',
|
||||||
|
day7: 'Sunday',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
createAuth: 'Create Authorization',
|
||||||
|
editAuth: 'Edit Authorization',
|
||||||
|
basicInfo: 'Basic Info',
|
||||||
|
priority: 'Priority',
|
||||||
|
emergency: 'Emergency',
|
||||||
|
high: 'High',
|
||||||
|
medium: 'Medium',
|
||||||
|
low: 'Low',
|
||||||
|
default: 'Default',
|
||||||
|
targetSelect: 'Target Select',
|
||||||
|
folderSelect: 'Folder Select',
|
||||||
|
assetSelect: 'Asset Select',
|
||||||
|
accountSelect: 'Account Select',
|
||||||
|
all: 'All',
|
||||||
|
selectItem: 'Select {name}',
|
||||||
|
selectItemTip: 'Please select the {name}',
|
||||||
|
regex: 'Regex',
|
||||||
|
regexTip: 'Please input the regular expression',
|
||||||
|
tag: 'Tag',
|
||||||
|
tags: 'Select Tag',
|
||||||
|
tagsTip: 'Please select the tag',
|
||||||
|
excludeItem: 'Exclude {name}',
|
||||||
|
excludeItemTip: 'Please select the {name} to be excluded',
|
||||||
|
authorizationRole: 'Authorization Role',
|
||||||
|
authorizationRoleTip: 'Please select an authorization role',
|
||||||
|
accessControl: 'Access Control',
|
||||||
|
accessTime: 'Access Time',
|
||||||
|
customTime: 'Custom Time',
|
||||||
|
timeTemplate: 'Time Template',
|
||||||
|
timeTemplateTip: 'Please select the time template',
|
||||||
|
editCustomTime: 'Edit Custom Time',
|
||||||
|
customTimeTip: 'No customized time set',
|
||||||
|
customTimeTip2: 'Please set a custom time',
|
||||||
|
timeZone: 'Time Zone',
|
||||||
|
time: 'Time',
|
||||||
|
commandFilter: 'Command Filter',
|
||||||
|
commandTip1: 'Please select the command',
|
||||||
|
commandTip2: 'Please select the command template',
|
||||||
|
ipWhiteList: 'IP White List',
|
||||||
|
ipWhiteListTip: 'Please select the IP White List',
|
||||||
|
validTime: 'Valid Time',
|
||||||
|
permanentValidity: 'Permanent Validity',
|
||||||
|
select: 'Select ({count} items)',
|
||||||
|
start: 'Start',
|
||||||
|
end: 'End'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default oneterm_en
|
export default oneterm_en
|
||||||
|
@@ -16,7 +16,7 @@ const oneterm_zh = {
|
|||||||
accessRestrictions: '接入限制',
|
accessRestrictions: '接入限制',
|
||||||
comment: '备注',
|
comment: '备注',
|
||||||
node: '节点',
|
node: '节点',
|
||||||
catalog: '目录',
|
folder: '目录',
|
||||||
cmdbType: '模型',
|
cmdbType: '模型',
|
||||||
fieldMap: '字段映射',
|
fieldMap: '字段映射',
|
||||||
field: '字段',
|
field: '字段',
|
||||||
@@ -33,13 +33,24 @@ const oneterm_zh = {
|
|||||||
editPublicKey: '编辑公钥',
|
editPublicKey: '编辑公钥',
|
||||||
assetCount: '资产数',
|
assetCount: '资产数',
|
||||||
macAddress: 'Mac地址',
|
macAddress: 'Mac地址',
|
||||||
|
enabled: '启用',
|
||||||
|
disabled: '禁用',
|
||||||
|
isEnable: '是否启用',
|
||||||
|
confirmEnable: '确认切换启用状态?',
|
||||||
|
switching: '正在切换',
|
||||||
|
switchingTip: '正在切换,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||||
menu: {
|
menu: {
|
||||||
oneterm: '堡垒机',
|
oneterm: '堡垒机',
|
||||||
workStation: '工作台',
|
workStation: '工作台',
|
||||||
|
resourceControl: '资源管控',
|
||||||
|
basicResource: '基础资源',
|
||||||
assetManagement: '资产管理',
|
assetManagement: '资产管理',
|
||||||
assets: '资产列表',
|
gatewayManagement: '网关管理',
|
||||||
gateways: '网关列表',
|
accountManagement: '帐号管理',
|
||||||
accounts: '账号列表',
|
accessControl: '访问控制',
|
||||||
|
accessAuthorization: '访问授权',
|
||||||
|
commandFilter: '命令拦截',
|
||||||
|
accessTime: '访问时段',
|
||||||
security: '安全设置',
|
security: '安全设置',
|
||||||
sessionAuditing: '会话审计',
|
sessionAuditing: '会话审计',
|
||||||
onlineSession: '在线会话',
|
onlineSession: '在线会话',
|
||||||
@@ -82,6 +93,7 @@ const oneterm_zh = {
|
|||||||
batchExecution: '批量执行',
|
batchExecution: '批量执行',
|
||||||
chooseAssets: '选择资产',
|
chooseAssets: '选择资产',
|
||||||
batchExecutionPlaceholder: '请输入命令, 按 Enter 执行',
|
batchExecutionPlaceholder: '请输入命令, 按 Enter 执行',
|
||||||
|
assetShare: '资产分享'
|
||||||
},
|
},
|
||||||
sessionTable: {
|
sessionTable: {
|
||||||
target: '目标',
|
target: '目标',
|
||||||
@@ -105,12 +117,12 @@ const oneterm_zh = {
|
|||||||
assetList: {
|
assetList: {
|
||||||
grantUser: '授权用户',
|
grantUser: '授权用户',
|
||||||
assetTree: '资产树',
|
assetTree: '资产树',
|
||||||
createCatalog: '新建目录',
|
createFolder: '新建目录',
|
||||||
editCatalog: '编辑目录',
|
editFolder: '编辑目录',
|
||||||
deleteCatalog: '删除目录',
|
deleteFolder: '删除目录',
|
||||||
grantCatalog: '授权目录',
|
grantFolder: '授权目录',
|
||||||
ip: '地址',
|
ip: '地址',
|
||||||
catalogName: '目录名称',
|
folderName: '目录名称',
|
||||||
connectable: '可连接',
|
connectable: '可连接',
|
||||||
connected: '连接',
|
connected: '连接',
|
||||||
error: '错误',
|
error: '错误',
|
||||||
@@ -125,9 +137,6 @@ const oneterm_zh = {
|
|||||||
commandIntercept: '命令拦截',
|
commandIntercept: '命令拦截',
|
||||||
allowAccess: '允许接入',
|
allowAccess: '允许接入',
|
||||||
prohibitAccess: '禁止接入',
|
prohibitAccess: '禁止接入',
|
||||||
weektime: '星期/时间',
|
|
||||||
selectedTime: '已选择时间段',
|
|
||||||
drag: '可拖动鼠标选择时间段',
|
|
||||||
assetList: '资产列表',
|
assetList: '资产列表',
|
||||||
createAsset: '创建资产',
|
createAsset: '创建资产',
|
||||||
editAsset: '编辑资产',
|
editAsset: '编辑资产',
|
||||||
@@ -161,10 +170,15 @@ const oneterm_zh = {
|
|||||||
grantLogin: '登录权限',
|
grantLogin: '登录权限',
|
||||||
grantUserOrDep: '授权用户/部门',
|
grantUserOrDep: '授权用户/部门',
|
||||||
grantRole: '授权角色',
|
grantRole: '授权角色',
|
||||||
createSubCatalog: '创建子目录',
|
createSubFolder: '创建子目录',
|
||||||
assetSearchTip: '在资产中搜索',
|
assetSearchTip: '在资产中搜索',
|
||||||
basic: '基础',
|
basic: '基础',
|
||||||
database: '数据库',
|
database: '数据库',
|
||||||
|
folderOperationPermissions: '目录操作权限',
|
||||||
|
assetOperationPermissions: '资产操作权限',
|
||||||
|
accountOperationPermissions: '帐号操作权限',
|
||||||
|
accessPermission: '访问权限',
|
||||||
|
operationPermissions: '操作权限'
|
||||||
},
|
},
|
||||||
log: {
|
log: {
|
||||||
time: '时间',
|
time: '时间',
|
||||||
@@ -193,26 +207,51 @@ const oneterm_zh = {
|
|||||||
clipboard: '剪贴板'
|
clipboard: '剪贴板'
|
||||||
},
|
},
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
commandIntercept: '命令拦截',
|
accessControl: '访问控制',
|
||||||
terminalControl: '终端控制',
|
|
||||||
quickCommand: '快捷命令',
|
quickCommand: '快捷命令',
|
||||||
terminalDisplay: '终端显示',
|
terminalDisplay: '终端显示',
|
||||||
publicKey: '我的公钥',
|
publicKey: '我的公钥',
|
||||||
publicKeyTip: '用于终端登录堡垒机SSH服务的免密认证',
|
publicKeyTip: '用于终端登录堡垒机SSH服务的免密认证',
|
||||||
storageConfig: '存储配置'
|
storageConfig: '存储配置'
|
||||||
},
|
},
|
||||||
commandIntercept: {
|
commandFilter: {
|
||||||
enable: '是否激活',
|
name: '命令拦截',
|
||||||
|
commandManagement: '命令管理',
|
||||||
|
commandTemplateManagement: '命令模板管理',
|
||||||
regexp: '是否正则',
|
regexp: '是否正则',
|
||||||
createCommand: '创建命令',
|
createCommand: '创建命令',
|
||||||
editCommand: '编辑命令',
|
editCommand: '编辑命令',
|
||||||
|
category: '分类',
|
||||||
|
securityRelated: '安全相关',
|
||||||
|
systemOperations: '系统操作',
|
||||||
|
databaseOperations: '数据库操作',
|
||||||
|
networkOperations: '网络操作',
|
||||||
|
fileOperations: '文件操作',
|
||||||
|
developmentRelated: '开发相关',
|
||||||
|
riskLevel: '风险等级',
|
||||||
|
safe: '安全',
|
||||||
|
warning: '警告',
|
||||||
|
dangerous: '危险',
|
||||||
|
criticalDanger: '严重危险',
|
||||||
|
commandCount: '命令数量',
|
||||||
|
createCommandTemplate: '新建命令模板',
|
||||||
|
editCommandTemplate: '编辑命令模板',
|
||||||
|
selectCommand: '选择命令',
|
||||||
|
unselectCommand: '未选命令',
|
||||||
|
selectedCommand: '已选命令',
|
||||||
},
|
},
|
||||||
terminalControl: {
|
accessControl: {
|
||||||
timeout: '会话最大空闲时间',
|
timeout: '会话最大空闲时间',
|
||||||
allowCopy: '允许复制',
|
copyTip: '本地设备可以实时获取远程桌面中的剪贴板文本内容。',
|
||||||
allowPaste: '允许粘贴',
|
pasteTip: '远程桌面可以通过剪贴板获取本地设备当前复制的文本内容。',
|
||||||
copyTip: '复制:本地设备可以实时获取远程桌面中的剪贴板文本内容。',
|
permissionConfig: '权限配置',
|
||||||
pasteTip: '粘贴:远程桌面可以通过剪贴板获取本地设备当前复制的文本内容。'
|
permissionConfigTip: '上传、下载是针对SSH、RDP协议,复制、粘贴是针对RDP协议',
|
||||||
|
connect: '连接',
|
||||||
|
upload: '上传',
|
||||||
|
download: '下载',
|
||||||
|
copy: '复制',
|
||||||
|
paste: '粘贴',
|
||||||
|
share: '分享'
|
||||||
},
|
},
|
||||||
terminalDisplay: {
|
terminalDisplay: {
|
||||||
fontSetting: '字体设置',
|
fontSetting: '字体设置',
|
||||||
@@ -269,7 +308,6 @@ const oneterm_zh = {
|
|||||||
createConfig: '创建配置',
|
createConfig: '创建配置',
|
||||||
editConfig: '编辑配置',
|
editConfig: '编辑配置',
|
||||||
local: '本地',
|
local: '本地',
|
||||||
isEnable: '是否启用',
|
|
||||||
retentionDays: '保留天数',
|
retentionDays: '保留天数',
|
||||||
archiveDays: '归档天数',
|
archiveDays: '归档天数',
|
||||||
isCleanupEnabled: '是否启用清理',
|
isCleanupEnabled: '是否启用清理',
|
||||||
@@ -303,8 +341,76 @@ const oneterm_zh = {
|
|||||||
basicConfig: '基础配置',
|
basicConfig: '基础配置',
|
||||||
advancedConfig: '高级配置',
|
advancedConfig: '高级配置',
|
||||||
advancedConfigTip: '配置存储的详细参数和策略',
|
advancedConfigTip: '配置存储的详细参数和策略',
|
||||||
confirmEnable: '确认切换启用状态?',
|
|
||||||
confirmPrimaryStorage: '确认切换主存储?'
|
confirmPrimaryStorage: '确认切换主存储?'
|
||||||
|
},
|
||||||
|
timeTemplate: {
|
||||||
|
createTimeTemplate: '创建时间模板',
|
||||||
|
editTimeTemplate: '编辑时间模板',
|
||||||
|
timeZone: '时区',
|
||||||
|
category: '分类',
|
||||||
|
workTime: '工作时间',
|
||||||
|
dutyTime: '值班时间',
|
||||||
|
maintenanceTime: '维护时间',
|
||||||
|
emergencyResponse: '紧急响应',
|
||||||
|
allTime: '全天候响应',
|
||||||
|
timeRange: '时间段',
|
||||||
|
weektime: '星期/时间',
|
||||||
|
selectedTime: '已选择时间段',
|
||||||
|
drag: '可拖动鼠标选择时间段',
|
||||||
|
day1: '星期一',
|
||||||
|
day2: '星期二',
|
||||||
|
day3: '星期三',
|
||||||
|
day4: '星期四',
|
||||||
|
day5: '星期五',
|
||||||
|
day6: '星期六',
|
||||||
|
day7: '星期日',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
createAuth: '创建授权',
|
||||||
|
editAuth: '编辑授权',
|
||||||
|
basicInfo: '基础信息',
|
||||||
|
priority: '优先级',
|
||||||
|
emergency: '紧急',
|
||||||
|
high: '高优先级',
|
||||||
|
medium: '中优先级',
|
||||||
|
low: '低优先级',
|
||||||
|
default: '默认',
|
||||||
|
targetSelect: '目标选择',
|
||||||
|
folderSelect: '目录选择',
|
||||||
|
assetSelect: '资产选择',
|
||||||
|
accountSelect: '帐号选择',
|
||||||
|
all: '全部',
|
||||||
|
selectItem: '选择{name}',
|
||||||
|
selectItemTip: '请选择{name}',
|
||||||
|
regex: '正则表达式',
|
||||||
|
regexTip: '请输入正则表达式',
|
||||||
|
tag: '标签',
|
||||||
|
tags: '选择标签',
|
||||||
|
tagsTip: '请选择标签',
|
||||||
|
excludeItem: '排除{name}',
|
||||||
|
excludeItemTip: '请选择需要排除的{name}',
|
||||||
|
accessControl: '访问控制',
|
||||||
|
authorizationRole: '授权角色',
|
||||||
|
authorizationRoleTip: '请选择授权角色',
|
||||||
|
accessTime: '访问时间',
|
||||||
|
customTime: '自定义时间',
|
||||||
|
timeTemplate: '时间模板',
|
||||||
|
timeTemplateTip: '请选择时间模板',
|
||||||
|
editCustomTime: '编辑自定义时间',
|
||||||
|
customTimeTip: '未设置自定义时间',
|
||||||
|
customTimeTip2: '请设置自定义时间',
|
||||||
|
timeZone: '时区',
|
||||||
|
time: '时段',
|
||||||
|
commandFilter: '命令拦截',
|
||||||
|
commandTip1: '请选择命令',
|
||||||
|
commandTip2: '请选择命令模板',
|
||||||
|
ipWhiteList: 'IP白名单',
|
||||||
|
ipWhiteListTip: '请选择IP白名单',
|
||||||
|
validTime: '有效时间',
|
||||||
|
permanentValidity: '永久有效',
|
||||||
|
select: '选择 ({count}个)',
|
||||||
|
start: '开始',
|
||||||
|
end: '结束'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default oneterm_zh
|
export default oneterm_zh
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
// @ts-ignore
|
|
||||||
import { BasicLayout, RouteView } from '@/layouts'
|
import { BasicLayout, RouteView } from '@/layouts'
|
||||||
import user from '@/store/global/user'
|
import user from '@/store/global/user'
|
||||||
|
|
||||||
@@ -36,27 +35,59 @@ const genOnetermRoutes = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/oneterm/assets',
|
path: '/oneterm/resource',
|
||||||
name: 'oneterm_assets',
|
name: 'oneterm_resource',
|
||||||
component: RouteView,
|
component: RouteView,
|
||||||
meta: { title: 'oneterm.menu.assetManagement', appName: 'oneterm', icon: 'ops-oneterm-asset-management', selectedIcon: 'ops-oneterm-asset-management', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.resourceControl', appName: 'oneterm', icon: 'ops-oneterm-asset-management', selectedIcon: 'ops-oneterm-asset-management', permission: ['oneterm_admin', 'admin'] },
|
||||||
redirect: '/oneterm/assets/assets',
|
redirect: '/oneterm/assets/assets',
|
||||||
children: [{
|
children: [
|
||||||
path: '/oneterm/assetlist',
|
{
|
||||||
|
path: `/oneterm/basicresource`,
|
||||||
|
name: `oneterm_resource_management`,
|
||||||
|
meta: { title: 'oneterm.menu.basicResource', appName: 'oneterm', disabled: true, style: 'margin-left: 12px', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/oneterm/asset',
|
||||||
name: 'oneterm_asset_list',
|
name: 'oneterm_asset_list',
|
||||||
meta: { title: 'oneterm.menu.assets', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.assetManagement', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/assets/assets')
|
component: () => import('../views/assets/assets')
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/oneterm/account',
|
path: '/oneterm/account',
|
||||||
name: 'oneterm_account',
|
name: 'oneterm_account_management',
|
||||||
meta: { title: 'oneterm.menu.accounts', icon: 'ops-oneterm-account', selectedIcon: 'ops-oneterm-account-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.accountManagement', icon: 'ops-oneterm-account', selectedIcon: 'ops-oneterm-account-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/assets/account')
|
component: () => import('../views/assets/account')
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/oneterm/gateway',
|
path: '/oneterm/gateway',
|
||||||
name: 'oneterm_gateway',
|
name: 'oneterm_gateway_management',
|
||||||
meta: { title: 'oneterm.menu.gateways', icon: 'ops-oneterm-gateway', selectedIcon: 'ops-oneterm-gateway-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.gatewayManagement', icon: 'ops-oneterm-gateway', selectedIcon: 'ops-oneterm-gateway-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/assets/gateway')
|
component: () => import('../views/assets/gateway')
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
path: `/oneterm/access`,
|
||||||
|
name: `oneterm_access`,
|
||||||
|
meta: { title: 'oneterm.menu.accessControl', appName: 'oneterm', disabled: true, style: 'margin-left: 12px', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/oneterm/access/auth',
|
||||||
|
name: 'oneterm_access_auth',
|
||||||
|
meta: { title: 'oneterm.menu.accessAuthorization', appName: 'oneterm', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
component: () => import('../views/access/auth')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/oneterm/access/command',
|
||||||
|
name: 'oneterm_access_command',
|
||||||
|
meta: { title: 'oneterm.menu.commandFilter', appName: 'oneterm', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
component: () => import('../views/access/command')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/oneterm/access/time',
|
||||||
|
name: 'oneterm_access_time',
|
||||||
|
meta: { title: 'oneterm.menu.accessTime', appName: 'oneterm', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
component: () => import('../views/access/time')
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/oneterm/audit',
|
path: '/oneterm/audit',
|
||||||
@@ -76,7 +107,8 @@ const genOnetermRoutes = () => {
|
|||||||
name: 'oneterm_session_online',
|
name: 'oneterm_session_online',
|
||||||
meta: { title: 'oneterm.menu.onlineSession', icon: 'ops-oneterm-sessiononline', selectedIcon: 'ops-oneterm-sessiononline-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.onlineSession', icon: 'ops-oneterm-sessiononline', selectedIcon: 'ops-oneterm-sessiononline-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/session/online.vue')
|
component: () => import('../views/session/online.vue')
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/oneterm/session/history',
|
path: '/oneterm/session/history',
|
||||||
name: 'oneterm_session_history',
|
name: 'oneterm_session_history',
|
||||||
meta: { title: 'oneterm.menu.offlineSession', icon: 'ops-oneterm-sessionhistory', selectedIcon: 'ops-oneterm-sessionhistory-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.offlineSession', icon: 'ops-oneterm-sessionhistory', selectedIcon: 'ops-oneterm-sessionhistory-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
@@ -92,12 +124,14 @@ const genOnetermRoutes = () => {
|
|||||||
name: 'oneterm_log_login',
|
name: 'oneterm_log_login',
|
||||||
meta: { title: 'oneterm.menu.loginLog', icon: 'ops-oneterm-login', selectedIcon: 'ops-oneterm-login-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.loginLog', icon: 'ops-oneterm-login', selectedIcon: 'ops-oneterm-login-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/log/login')
|
component: () => import('../views/log/login')
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/oneterm/log/operation',
|
path: '/oneterm/log/operation',
|
||||||
name: 'oneterm_log_operation',
|
name: 'oneterm_log_operation',
|
||||||
meta: { title: 'oneterm.menu.operationLog', icon: 'ops-oneterm-operation', selectedIcon: 'ops-oneterm-operation-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.operationLog', icon: 'ops-oneterm-operation', selectedIcon: 'ops-oneterm-operation-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||||
component: () => import('../views/log/operation')
|
component: () => import('../views/log/operation')
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/oneterm/log/file',
|
path: '/oneterm/log/file',
|
||||||
name: 'oneterm_log_file',
|
name: 'oneterm_log_file',
|
||||||
meta: { title: 'oneterm.menu.fileLog', appName: 'oneterm', icon: 'ops-oneterm-file_log', selectedIcon: 'ops-oneterm-file_log-selected', permission: ['oneterm_admin', 'admin'] },
|
meta: { title: 'oneterm.menu.fileLog', appName: 'oneterm', icon: 'ops-oneterm-file_log', selectedIcon: 'ops-oneterm-file_log-selected', permission: ['oneterm_admin', 'admin'] },
|
||||||
|
@@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.accessTime')" :prop="formItemProp">
|
||||||
|
<a-radio-group
|
||||||
|
:value="accessTimeType"
|
||||||
|
@change="handleRadioChange"
|
||||||
|
>
|
||||||
|
<a-radio :value="ACCESS_TIME_TYPE.TIME_TEMPLATE">
|
||||||
|
{{ $t('oneterm.auth.timeTemplate') }}
|
||||||
|
</a-radio>
|
||||||
|
<a-radio :value="ACCESS_TIME_TYPE.CUSTOM_TIME">
|
||||||
|
{{ $t('oneterm.auth.customTime') }}
|
||||||
|
<a
|
||||||
|
v-if="accessTimeType === ACCESS_TIME_TYPE.CUSTOM_TIME"
|
||||||
|
@click="openCustomTimeModal"
|
||||||
|
>
|
||||||
|
<ops-icon type="veops-edit"/>
|
||||||
|
</a>
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
v-if="accessTimeType === ACCESS_TIME_TYPE.TIME_TEMPLATE"
|
||||||
|
:value="form.access_control.time_template.template_id"
|
||||||
|
:options="timeTemplateSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.timeTemplateTip')"
|
||||||
|
@change="handleTimeTemplateChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="custom-time" v-else-if="accessTimeType === ACCESS_TIME_TYPE.CUSTOM_TIME">
|
||||||
|
<template v-if="!form.access_control.custom_time_ranges.length">
|
||||||
|
<a-icon type="exclamation-circle" />
|
||||||
|
{{ $t('oneterm.auth.customTimeTip') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="custom-time-row">
|
||||||
|
<span class="custom-time-label">{{ $t('oneterm.auth.timeZone') }}:</span>
|
||||||
|
{{ form.access_control.timezone }}
|
||||||
|
</div>
|
||||||
|
<div class="custom-time-row">
|
||||||
|
<span class="custom-time-label">{{ $t('oneterm.auth.time') }}:</span>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in customTimeText"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<span>{{ item.start_time }}~{{ item.end_time }}</span>
|
||||||
|
<span class="custom-time-week">{{ item.weekText }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CustomTimeModal
|
||||||
|
ref="customTimeModalRef"
|
||||||
|
@ok="handleCustomTimeModalOk"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getTimeTemplateList } from '@/modules/oneterm/api/timeTemplate.js'
|
||||||
|
import { ACCESS_TIME_TYPE } from './constants.js'
|
||||||
|
|
||||||
|
import CustomTimeModal from './customTimeModal.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccessTime',
|
||||||
|
components: {
|
||||||
|
CustomTimeModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
accessTimeType: {
|
||||||
|
type: String,
|
||||||
|
default: ACCESS_TIME_TYPE.TIME_TEMPLATE
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ACCESS_TIME_TYPE,
|
||||||
|
timeTemplateSelectOptions: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formItemProp() {
|
||||||
|
return this.accessTimeType === ACCESS_TIME_TYPE.TIME_TEMPLATE ? 'access_control.time_template.template_id' : 'access_control.custom_time_ranges'
|
||||||
|
},
|
||||||
|
customTimeText() {
|
||||||
|
const custom_time_ranges = this.form?.access_control?.custom_time_ranges || []
|
||||||
|
|
||||||
|
return custom_time_ranges.map((item) => {
|
||||||
|
return {
|
||||||
|
start_time: item.start_time,
|
||||||
|
end_time: item.end_time,
|
||||||
|
weekText: item.weekdays.map((day) => this.$t(`oneterm.timeTemplate.day${day}`)).join(', ')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getTimeTemplateList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTimeTemplateList() {
|
||||||
|
getTimeTemplateList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999,
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.timeTemplateSelectOptions = list.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleRadioChange(e) {
|
||||||
|
const value = e?.target?.value
|
||||||
|
if (value !== this.accessTimeType) {
|
||||||
|
this.$emit('update:accessTimeType', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTimeTemplateChange(value) {
|
||||||
|
this.$emit('change', ['access_control', 'time_template', 'template_id'], value)
|
||||||
|
},
|
||||||
|
openCustomTimeModal() {
|
||||||
|
const { custom_time_ranges = [], timezone } = this.form?.access_control || {}
|
||||||
|
|
||||||
|
this.$refs.customTimeModalRef.open({
|
||||||
|
custom_time_ranges,
|
||||||
|
timezone
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleCustomTimeModalOk(data) {
|
||||||
|
const { custom_time_ranges, timezone } = data
|
||||||
|
|
||||||
|
this.$emit('change', ['access_control', 'custom_time_ranges'], custom_time_ranges)
|
||||||
|
this.$emit('change', ['access_control', 'timezone'], timezone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.custom-time {
|
||||||
|
&-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-week {
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-edit {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.authorizationRole')" prop="rids">
|
||||||
|
<EmployeeTreeSelect
|
||||||
|
:value="rids"
|
||||||
|
multiple
|
||||||
|
:idType="2"
|
||||||
|
departmentKey="acl_rid"
|
||||||
|
employeeKey="acl_rid"
|
||||||
|
:placeholder="`${$t(`placeholder2`)}`"
|
||||||
|
class="custom-treeselect custom-treeselect-white"
|
||||||
|
:style="{
|
||||||
|
'--custom-height': '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
'--custom-multiple-lineHeight': '18px',
|
||||||
|
}"
|
||||||
|
:limit="1"
|
||||||
|
:otherOptions="visualRoleList"
|
||||||
|
@change="handleRoleChange"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { searchRole } from '@/modules/acl/api/role'
|
||||||
|
|
||||||
|
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AuthorizationRole',
|
||||||
|
components: {
|
||||||
|
EmployeeTreeSelect
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visualRoleList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rids() {
|
||||||
|
return (this.form?.rids || []).map((r) => `employee-${r}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getRoleList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getRoleList() {
|
||||||
|
const res = await searchRole({ page_size: 9999, page: 1, app_id: 'oneterm', user_role: 0, user_only: 0, is_all: true })
|
||||||
|
|
||||||
|
const visualRoleList = []
|
||||||
|
const roleList = (res?.roles || []).filter((item) => !/_virtual$/.test(item.name))
|
||||||
|
|
||||||
|
if (roleList.length) {
|
||||||
|
visualRoleList.push({
|
||||||
|
acl_rid: -100,
|
||||||
|
department_name: this.$t('acl.visualRole'),
|
||||||
|
sub_departments: [],
|
||||||
|
employees: roleList.map((item) => {
|
||||||
|
return {
|
||||||
|
nickname: item.name,
|
||||||
|
acl_rid: item.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$set(this, 'visualRoleList', visualRoleList)
|
||||||
|
},
|
||||||
|
handleRoleChange(value) {
|
||||||
|
const rids = (value || []).map((r) => Number(r.split('-')[1]))
|
||||||
|
this.$emit('change', ['rids'], rids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.commandFilter')" prop="access_control.cmd_ids">
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.access_control.cmd_ids"
|
||||||
|
:options="commandSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.commandTip1')"
|
||||||
|
@change="(value) => $emit('change', ['access_control', 'cmd_ids'], value)"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.access_control.template_ids"
|
||||||
|
:options="commandTemplateSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.commandTip2')"
|
||||||
|
@change="(value) => $emit('change', ['access_control', 'template_ids'], value)"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getCommandList } from '@/modules/oneterm/api/command.js'
|
||||||
|
import { getCommandTemplateList } from '@/modules/oneterm/api/commandTemplate.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Command',
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
commandSelectOptions: [],
|
||||||
|
commandTemplateSelectOptions: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getCommandList()
|
||||||
|
this.getCommandTemplateList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCommandList() {
|
||||||
|
getCommandList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.commandSelectOptions = list.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getCommandTemplateList() {
|
||||||
|
getCommandTemplateList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.commandTemplateSelectOptions = list.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,4 @@
|
|||||||
|
export const ACCESS_TIME_TYPE = {
|
||||||
|
TIME_TEMPLATE: 'timeTemplate',
|
||||||
|
CUSTOM_TIME: 'customTime'
|
||||||
|
}
|
@@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="$t('oneterm.auth.editCustomTime')"
|
||||||
|
:visible="visible"
|
||||||
|
:width="1000"
|
||||||
|
:bodyStyle="{
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="customTimeFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :wrapper-col="{ span: 16 }" :label="$t('oneterm.timeTemplate.timeZone')" prop="timezone">
|
||||||
|
<a-select
|
||||||
|
v-model="form.timezone"
|
||||||
|
showSearch
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="timezoneSelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :wrapper-col="{ span: 16 }" :label="$t('oneterm.timeTemplate.timeRange')" prop="custom_time_ranges">
|
||||||
|
<DragWeekTime
|
||||||
|
v-model="form.custom_time_ranges"
|
||||||
|
:data="weekTimeData"
|
||||||
|
@onClear="clearWeektime"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import momentTimezone from 'moment-timezone'
|
||||||
|
import { mergeTimeRange } from '@/modules/oneterm/views/access/time/mergeTimeRange.js'
|
||||||
|
import { splitTimeRange } from '@/modules/oneterm/views/access/time/splitTimeRange.js'
|
||||||
|
|
||||||
|
import DragWeekTime from '@/modules/oneterm/components/dragWeektime'
|
||||||
|
import weekTimeData from '@/modules/oneterm/components/dragWeektime/weektimeData'
|
||||||
|
|
||||||
|
const DEFAULT_FORM = {
|
||||||
|
timezone: momentTimezone.tz.guess(),
|
||||||
|
custom_time_ranges: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustomTimeModal',
|
||||||
|
components: {
|
||||||
|
DragWeekTime
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
form: { ...DEFAULT_FORM },
|
||||||
|
rules: {
|
||||||
|
timezone: [{ required: true, message: this.$t(`placeholder1`) }],
|
||||||
|
custom_time_ranges: [{ required: true, message: this.$t(`placeholder2`) }],
|
||||||
|
},
|
||||||
|
weekTimeData: _.cloneDeep(weekTimeData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
timezoneSelectOptions() {
|
||||||
|
const names = momentTimezone.tz.names()
|
||||||
|
return names.map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
let custom_time_ranges = []
|
||||||
|
if (data?.custom_time_ranges?.length) {
|
||||||
|
const timeRanges = splitTimeRange(data.custom_time_ranges)
|
||||||
|
custom_time_ranges = timeRanges.map((item) => {
|
||||||
|
const childData = this.weekTimeData?.[item?.day - 1]?.child
|
||||||
|
if (childData?.length) {
|
||||||
|
childData.forEach((t) => {
|
||||||
|
this.$set(t, 'check', Boolean(item?.value?.length) && item.value.includes(t.value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.day,
|
||||||
|
day: item.day,
|
||||||
|
value: item.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.form = {
|
||||||
|
timezone: data?.timezone ?? momentTimezone.tz.guess(),
|
||||||
|
custom_time_ranges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearWeektime() {
|
||||||
|
this.weekTimeData.forEach((item) => {
|
||||||
|
item.child.forEach((t) => {
|
||||||
|
this.$set(t, 'check', false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.form.custom_time_ranges = []
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
async handleOk() {
|
||||||
|
this.$refs.customTimeFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
let custom_time_ranges = []
|
||||||
|
if (this?.form?.custom_time_ranges?.length) {
|
||||||
|
custom_time_ranges = mergeTimeRange(this.form.custom_time_ranges)
|
||||||
|
}
|
||||||
|
this.$emit('ok', {
|
||||||
|
custom_time_ranges,
|
||||||
|
timezone: this.form.timezone
|
||||||
|
})
|
||||||
|
this.handleCancel()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<AuthorizationRole
|
||||||
|
:form="form"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
<a-form-model-item
|
||||||
|
prop="permissions"
|
||||||
|
:label="$t('oneterm.accessControl.permissionConfig')"
|
||||||
|
:extra="$t('oneterm.accessControl.permissionConfigTip')"
|
||||||
|
>
|
||||||
|
<PermissionCheckbox
|
||||||
|
:value="form.permissions"
|
||||||
|
@change="(key, checked) => handleFormChange(['permissions', key], checked)"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<AccessTime
|
||||||
|
:accessTimeType="accessTimeType"
|
||||||
|
:form="form"
|
||||||
|
@update:accessTimeType="accessTimeType = $event"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
<Command
|
||||||
|
:form="form"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
<IPWhiteList
|
||||||
|
:form="form"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ACCESS_TIME_TYPE } from './constants.js'
|
||||||
|
|
||||||
|
import AuthorizationRole from './authorizationRole.vue'
|
||||||
|
import PermissionCheckbox from '@/modules/oneterm/views/systemSettings/accessControl/permissionCheckbox.vue'
|
||||||
|
import AccessTime from './accessTime.vue'
|
||||||
|
import Command from './command.vue'
|
||||||
|
import IPWhiteList from './ipWhiteList.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccessControl',
|
||||||
|
components: {
|
||||||
|
AuthorizationRole,
|
||||||
|
PermissionCheckbox,
|
||||||
|
AccessTime,
|
||||||
|
Command,
|
||||||
|
IPWhiteList
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
accessTimeType: ACCESS_TIME_TYPE.TIME_TEMPLATE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initAccessTimeType(form) {
|
||||||
|
let accessTimeType = ACCESS_TIME_TYPE.TIME_TEMPLATE
|
||||||
|
if (
|
||||||
|
form.access_control?.custom_time_ranges?.length &&
|
||||||
|
form.access_control?.timezone
|
||||||
|
) {
|
||||||
|
accessTimeType = ACCESS_TIME_TYPE.CUSTOM_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
this.accessTimeType = accessTimeType
|
||||||
|
},
|
||||||
|
getAccessTimeType() {
|
||||||
|
return this.accessTimeType
|
||||||
|
},
|
||||||
|
handleFormChange(keys, value) {
|
||||||
|
this.$emit(
|
||||||
|
'change',
|
||||||
|
{
|
||||||
|
keys,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.ipWhiteList')" prop="access_control.ip_whitelist">
|
||||||
|
<a-select
|
||||||
|
mode="tags"
|
||||||
|
:value="form.access_control.ip_whitelist"
|
||||||
|
:options="tagSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.ipWhiteListTip')"
|
||||||
|
@change="handleTagChange"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'IPWhiteList',
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tagSelectOptions: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleTagChange(value) {
|
||||||
|
this.$emit('change', ['access_control', 'ip_whitelist'], value)
|
||||||
|
const tagSelectOptions = this.tagSelectOptions.concat(value.map((item) => ({
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
})))
|
||||||
|
this.tagSelectOptions = _.uniqBy(tagSelectOptions, 'value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<div class="basic-form">
|
||||||
|
<a-form-model-item :label="$t('name')" prop="name">
|
||||||
|
<a-input
|
||||||
|
:value="form.name"
|
||||||
|
:placeholder="$t('placeholder1')"
|
||||||
|
@change="(e) => handleFormChange(['name'], e.target.value)"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('description')" prop="description">
|
||||||
|
<a-input
|
||||||
|
:value="form.description"
|
||||||
|
:placeholder="$t('placeholder1')"
|
||||||
|
@change="(e) => handleFormChange(['description'], e.target.value)"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<ValidTime
|
||||||
|
:valid_from="form.valid_from"
|
||||||
|
:valid_to="form.valid_to"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
<a-form-model-item :label="$t('oneterm.isEnable')" prop="enabled">
|
||||||
|
<a-switch
|
||||||
|
:checked="form.enabled"
|
||||||
|
@change="(checked) => handleFormChange(['enabled'], checked)"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ValidTime from './validTime.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BasicInfo',
|
||||||
|
components: {
|
||||||
|
ValidTime
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFormChange(keys, value) {
|
||||||
|
this.$emit(
|
||||||
|
'change',
|
||||||
|
{
|
||||||
|
keys,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.basic-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.validTime')" prop="valid_from">
|
||||||
|
<a-range-picker
|
||||||
|
v-model="validTime"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ValidTime',
|
||||||
|
props: {
|
||||||
|
valid_from: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
valid_to: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validTime: {
|
||||||
|
get() {
|
||||||
|
if (this.valid_from && this.valid_to) {
|
||||||
|
return [moment(this.valid_from), moment(this.valid_to)]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if (Array.isArray(val) && val.length === 2) {
|
||||||
|
this.$emit('change', ['valid_from'], val[0] ? val[0].format('YYYY-MM-DD HH:mm:ss') : undefined)
|
||||||
|
this.$emit('change', ['valid_to'], val[1] ? val[1].format('YYYY-MM-DD HH:mm:ss') : undefined)
|
||||||
|
} else {
|
||||||
|
this.$emit('change', ['valid_from'], undefined)
|
||||||
|
this.$emit('change', ['valid_to'], undefined)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,258 @@
|
|||||||
|
<template>
|
||||||
|
<CustomDrawer
|
||||||
|
width="850px"
|
||||||
|
:visible="visible"
|
||||||
|
:title="title"
|
||||||
|
:maskClosable="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="authFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
class="auth-form"
|
||||||
|
>
|
||||||
|
<div class="auth-form-title">{{ $t('oneterm.auth.basicInfo') }}</div>
|
||||||
|
<BasicInfo
|
||||||
|
:form="form"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="auth-form-title">{{ $t('oneterm.auth.targetSelect') }}</div>
|
||||||
|
<TargetSelect
|
||||||
|
:form="form"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="auth-form-title">{{ $t('oneterm.auth.accessControl') }}</div>
|
||||||
|
<AccessControl
|
||||||
|
:form="form"
|
||||||
|
ref="accessControlRef"
|
||||||
|
@change="handleFormChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="custom-drawer-bottom-action">
|
||||||
|
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
|
||||||
|
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form-model>
|
||||||
|
</CustomDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import momentTimezone from 'moment-timezone'
|
||||||
|
import { postAuth, putAuthById } from '@/modules/oneterm/api/authorizationV2.js'
|
||||||
|
import { getConfig } from '@/modules/oneterm/api/config'
|
||||||
|
import { TARGET_SELECT_TYPE } from '../constants.js'
|
||||||
|
import { ACCESS_TIME_TYPE } from './accessControl/constants.js'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
|
import BasicInfo from './basicInfo/index.vue'
|
||||||
|
import TargetSelect from './targetSelect/index.vue'
|
||||||
|
import AccessControl from './accessControl/index.vue'
|
||||||
|
|
||||||
|
const DEFAULT_PERMISSIONS = Object.values(PERMISSION_TYPE).reduce((config, key) => {
|
||||||
|
config[key] = false
|
||||||
|
return config
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const DEFAULT_FORM = {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
enabled: true,
|
||||||
|
node_selector: {
|
||||||
|
type: TARGET_SELECT_TYPE.ALL,
|
||||||
|
values: [],
|
||||||
|
exclude_ids: []
|
||||||
|
},
|
||||||
|
asset_selector: {
|
||||||
|
type: TARGET_SELECT_TYPE.ALL,
|
||||||
|
values: [],
|
||||||
|
exclude_ids: []
|
||||||
|
},
|
||||||
|
account_selector: {
|
||||||
|
type: TARGET_SELECT_TYPE.ALL,
|
||||||
|
values: [],
|
||||||
|
exclude_ids: []
|
||||||
|
},
|
||||||
|
rids: [],
|
||||||
|
permissions: DEFAULT_PERMISSIONS,
|
||||||
|
access_control: {
|
||||||
|
time_template: {
|
||||||
|
template_id: undefined
|
||||||
|
},
|
||||||
|
custom_time_ranges: [],
|
||||||
|
timezone: momentTimezone.tz.guess(),
|
||||||
|
cmd_ids: [],
|
||||||
|
template_ids: [],
|
||||||
|
ip_whitelist: [],
|
||||||
|
},
|
||||||
|
valid_from: undefined,
|
||||||
|
valid_to: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AuthDrawer',
|
||||||
|
components: {
|
||||||
|
BasicInfo,
|
||||||
|
TargetSelect,
|
||||||
|
AccessControl
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
authId: '',
|
||||||
|
form: _.cloneDeep(DEFAULT_FORM),
|
||||||
|
rules: {
|
||||||
|
name: [{ required: true, message: this.$t('placeholder1') }],
|
||||||
|
rids: [{ required: true, message: this.$t('oneterm.auth.authorizationRoleTip') }],
|
||||||
|
permissions: [{ required: true, message: this.$t('placeholder2') }]
|
||||||
|
},
|
||||||
|
confirmLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.authId) {
|
||||||
|
return this.$t('oneterm.auth.editAuth')
|
||||||
|
}
|
||||||
|
return this.$t('oneterm.auth.createAuth')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initDefaultPermissions()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initDefaultPermissions() {
|
||||||
|
getConfig({
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
const default_permissions = res?.data?.default_permissions
|
||||||
|
Object.keys(DEFAULT_FORM.permissions).forEach((key) => {
|
||||||
|
DEFAULT_FORM.permissions[key] = default_permissions?.[key] ?? DEFAULT_FORM.permissions[key]
|
||||||
|
})
|
||||||
|
this.form.permissions = DEFAULT_FORM.permissions
|
||||||
|
})
|
||||||
|
},
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
const editData = _.cloneDeep(data)
|
||||||
|
|
||||||
|
// merge initialization form data (editData || DEFAULT_FORM)
|
||||||
|
const form = {}
|
||||||
|
Object.keys(DEFAULT_FORM).forEach((key) => {
|
||||||
|
if (typeof DEFAULT_FORM[key] === 'object' && !Array.isArray(DEFAULT_FORM[key])) {
|
||||||
|
form[key] = {}
|
||||||
|
Object.keys(DEFAULT_FORM[key]).forEach((childKey) => {
|
||||||
|
form[key][childKey] = editData?.[key]?.[childKey] ?? DEFAULT_FORM[key][childKey]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
form[key] = editData?.[key] ?? DEFAULT_FORM[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.form = form
|
||||||
|
this.authId = editData?.id ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.accessControlRef.initAccessTimeType(this.form)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* update form data
|
||||||
|
* @param keys key list [root key, parent key, child key, ...]
|
||||||
|
* @param value updated value
|
||||||
|
*/
|
||||||
|
handleFormChange({
|
||||||
|
keys,
|
||||||
|
value,
|
||||||
|
}) {
|
||||||
|
let obj = this.form
|
||||||
|
if (keys.length > 1) {
|
||||||
|
obj = _.get(this.form, keys.slice(0, -1).join('.'))
|
||||||
|
}
|
||||||
|
this.$set(obj, keys.slice(-1), value)
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.form = _.cloneDeep(DEFAULT_FORM)
|
||||||
|
this.authId = ''
|
||||||
|
|
||||||
|
this.$refs.authFormRef.resetFields()
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
async handleSubmit() {
|
||||||
|
this.$refs.authFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.confirmLoading = true
|
||||||
|
try {
|
||||||
|
const { params, errorMessage } = this.handleSubmitParams()
|
||||||
|
if (errorMessage) {
|
||||||
|
this.$message.error(errorMessage)
|
||||||
|
this.confirmLoading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.authId) {
|
||||||
|
await putAuthById(this.authId, params)
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
} else {
|
||||||
|
await postAuth(params)
|
||||||
|
this.$message.success(this.$t('createSuccess'))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('submit')
|
||||||
|
this.handleClose()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('submit error:', e)
|
||||||
|
} finally {
|
||||||
|
this.confirmLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSubmitParams() {
|
||||||
|
const params = _.cloneDeep(this.form)
|
||||||
|
const errorMessage = ''
|
||||||
|
const accessTimeType = this.$refs.accessControlRef.getAccessTimeType()
|
||||||
|
|
||||||
|
switch (accessTimeType) {
|
||||||
|
case ACCESS_TIME_TYPE.TIME_TEMPLATE:
|
||||||
|
params.access_control.custom_time_ranges = undefined
|
||||||
|
params.access_control.timezone = undefined
|
||||||
|
if (!params?.access_control?.time_template?.template_id) {
|
||||||
|
params.access_control.time_template = undefined
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case ACCESS_TIME_TYPE.CUSTOM_TIME:
|
||||||
|
params.access_control.time_template = undefined
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.auth-form {
|
||||||
|
.auth-form-title {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .ant-input-number {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.folderSelect')" prop="node_selector">
|
||||||
|
<TypeRadio
|
||||||
|
idName="oneterm.folder"
|
||||||
|
:selectData="form.node_selector"
|
||||||
|
@change="(value) => handleFormChange(['node_selector'], value)"
|
||||||
|
>
|
||||||
|
<template #id>
|
||||||
|
<a-tree-select
|
||||||
|
:value="form.node_selector.values"
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="$t('oneterm.auth.selectItemTip', { name: $t('oneterm.folder') })"
|
||||||
|
:tree-data="nodeSelectTreeData"
|
||||||
|
:load-data="onLoadData"
|
||||||
|
@change="(value) => handleFormChange(['node_selector', 'values'], value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</TypeRadio>
|
||||||
|
|
||||||
|
<div class="exclude-row">
|
||||||
|
<div class="exclude-row-label">{{ $t('oneterm.auth.excludeItem', { name: $t('oneterm.folder') }) }}: </div>
|
||||||
|
<a-tree-select
|
||||||
|
:value="form.node_selector.exclude_ids"
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="$t('oneterm.auth.excludeItemTip', { name: $t('oneterm.folder')})"
|
||||||
|
:tree-data="nodeSelectTreeData"
|
||||||
|
:load-data="onLoadData"
|
||||||
|
@change="(value) => handleFormChange(['node_selector', 'exclude_ids'], value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.assetSelect')" prop="asset_selector">
|
||||||
|
<TypeRadio
|
||||||
|
idName="oneterm.asset"
|
||||||
|
:selectData="form.asset_selector"
|
||||||
|
@change="(value) => handleFormChange(['asset_selector'], value)"
|
||||||
|
>
|
||||||
|
<template #id>
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.asset_selector.values"
|
||||||
|
:options="assetSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.selectItemTip', { name: $t('oneterm.asset') })"
|
||||||
|
@change="(value) => handleFormChange(['asset_selector', 'values'], value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</TypeRadio>
|
||||||
|
|
||||||
|
<div class="exclude-row">
|
||||||
|
<div class="exclude-row-label">{{ $t('oneterm.auth.excludeItem', { name: $t('oneterm.asset')}) }}: </div>
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.asset_selector.exclude_ids"
|
||||||
|
:options="assetSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.excludeItemTip', { name: $t('oneterm.asset')})"
|
||||||
|
@change="(value) => handleFormChange(['asset_selector', 'exclude_ids'], value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.auth.accountSelect')" prop="account_selector">
|
||||||
|
<TypeRadio
|
||||||
|
idName="oneterm.account"
|
||||||
|
:selectData="form.account_selector"
|
||||||
|
@change="(value) => handleFormChange(['account_selector'], value)"
|
||||||
|
>
|
||||||
|
<template #id>
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.account_selector.values"
|
||||||
|
:options="accountSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.selectItemTip', { name: $t('oneterm.account') })"
|
||||||
|
@change="(value) => handleFormChange(['account_selector', 'values'], value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</TypeRadio>
|
||||||
|
<div class="exclude-row">
|
||||||
|
<div class="exclude-row-label">{{ $t('oneterm.auth.excludeItem', { name: $t('oneterm.account')}) }}: </div>
|
||||||
|
<a-select
|
||||||
|
mode="multiple"
|
||||||
|
:value="form.account_selector.exclude_ids"
|
||||||
|
:options="accountSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.excludeItemTip', { name: $t('oneterm.account')})"
|
||||||
|
@change="(value) => handleFormChange(['account_selector', 'exclude_ids'], value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-form-model-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getAccountList } from '@/modules/oneterm/api/account'
|
||||||
|
import { getAssetList } from '@/modules/oneterm/api/asset'
|
||||||
|
import { getNodeList } from '@/modules/oneterm/api/node'
|
||||||
|
|
||||||
|
import TypeRadio from './typeRadio.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TargetSelect',
|
||||||
|
components: {
|
||||||
|
TypeRadio
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodeSelectTreeData: [],
|
||||||
|
accountSelectOptions: [],
|
||||||
|
assetSelectOptions: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getNodeList()
|
||||||
|
this.getAccountList()
|
||||||
|
this.getAssetList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getNodeList() {
|
||||||
|
getNodeList({
|
||||||
|
info: true,
|
||||||
|
parent_id: 0
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.nodeSelectTreeData = list.map((item) => ({
|
||||||
|
id: String(item.id),
|
||||||
|
value: String(item.id),
|
||||||
|
label: item.name,
|
||||||
|
isLeaf: !item.has_child,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onLoadData(treeNode) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (treeNode.dataRef.children) {
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getNodeList({
|
||||||
|
parent_id: treeNode.dataRef.id,
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
treeNode.dataRef.children = (res?.data?.list ?? []).map((item) => ({
|
||||||
|
id: String(item.id),
|
||||||
|
value: String(item.id),
|
||||||
|
label: item.name,
|
||||||
|
isLeaf: !item.has_child,
|
||||||
|
}))
|
||||||
|
this.nodeSelectTreeData = [...this.nodeSelectTreeData]
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAccountList() {
|
||||||
|
getAccountList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.accountSelectOptions = list.map((item) => ({
|
||||||
|
value: String(item.id),
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAssetList() {
|
||||||
|
getAssetList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999,
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.assetSelectOptions = list.map((item) => ({
|
||||||
|
value: String(item.id),
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFormChange(keys, value) {
|
||||||
|
this.$emit(
|
||||||
|
'change',
|
||||||
|
{
|
||||||
|
keys,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.exclude-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
line-height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-radio-group
|
||||||
|
:value="selectData.type"
|
||||||
|
:options="radioOptions"
|
||||||
|
@change="handleRadioChange"
|
||||||
|
/>
|
||||||
|
<slot
|
||||||
|
v-if="selectData.type === TARGET_SELECT_TYPE.ID"
|
||||||
|
name="id"
|
||||||
|
></slot>
|
||||||
|
<a-input
|
||||||
|
v-else-if="selectData.type === TARGET_SELECT_TYPE.REGEX"
|
||||||
|
:value="selectData.values[0]"
|
||||||
|
:placeholder="$t('oneterm.auth.regexTip')"
|
||||||
|
@change="handleRegexInputChange"
|
||||||
|
></a-input>
|
||||||
|
<a-select
|
||||||
|
v-else-if="selectData.type === TARGET_SELECT_TYPE.TAG"
|
||||||
|
mode="tags"
|
||||||
|
:value="selectData.values"
|
||||||
|
:options="tagSelectOptions"
|
||||||
|
:placeholder="$t('oneterm.auth.tagsTip')"
|
||||||
|
@change="handleTagChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { TARGET_SELECT_TYPE, TARGET_SELECT_TYPE_NAME } from '../../constants.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TypeRadio',
|
||||||
|
props: {
|
||||||
|
idName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
selectData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
TARGET_SELECT_TYPE,
|
||||||
|
TARGET_SELECT_TYPE_NAME,
|
||||||
|
tagSelectOptions: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
radioOptions() {
|
||||||
|
return Object.values(TARGET_SELECT_TYPE).map((key) => {
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
label: key === TARGET_SELECT_TYPE.ID ? this.$t(TARGET_SELECT_TYPE_NAME[key], { name: this.$t(this.idName) }) : this.$t(TARGET_SELECT_TYPE_NAME[key])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRadioChange(e) {
|
||||||
|
const type = e?.target?.value || TARGET_SELECT_TYPE.ALL
|
||||||
|
const values = []
|
||||||
|
switch (type) {
|
||||||
|
case TARGET_SELECT_TYPE.REGEX:
|
||||||
|
values.push('')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('change', {
|
||||||
|
type,
|
||||||
|
values,
|
||||||
|
exclude_ids: this.selectData.exclude_ids
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleRegexInputChange(e) {
|
||||||
|
const value = e?.target?.value
|
||||||
|
this.$emit('change', {
|
||||||
|
type: this.selectData.type,
|
||||||
|
exclude_ids: this.selectData.exclude_ids,
|
||||||
|
values: [value],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTagChange(value) {
|
||||||
|
this.$emit('change', {
|
||||||
|
type: this.selectData.type,
|
||||||
|
exclude_ids: this.selectData.exclude_ids,
|
||||||
|
values: value,
|
||||||
|
})
|
||||||
|
const tagSelectOptions = this.tagSelectOptions.concat(value.map((item) => ({
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
})))
|
||||||
|
this.tagSelectOptions = _.uniqBy(tagSelectOptions, 'value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const TARGET_SELECT_TYPE = {
|
||||||
|
ALL: 'all',
|
||||||
|
ID: 'ids',
|
||||||
|
REGEX: 'regex',
|
||||||
|
TAG: 'tags'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TARGET_SELECT_TYPE_NAME = {
|
||||||
|
[TARGET_SELECT_TYPE.ALL]: 'oneterm.auth.all',
|
||||||
|
[TARGET_SELECT_TYPE.ID]: 'oneterm.auth.selectItem',
|
||||||
|
[TARGET_SELECT_TYPE.REGEX]: 'oneterm.auth.regex',
|
||||||
|
[TARGET_SELECT_TYPE.TAG]: 'oneterm.auth.tags',
|
||||||
|
}
|
405
oneterm-ui/src/modules/oneterm/views/access/auth/index.vue
Normal file
405
oneterm-ui/src/modules/oneterm/views/access/auth/index.vue
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
<template>
|
||||||
|
<div class="access-auth">
|
||||||
|
<a-spin :tip="loadTip" :spinning="loading">
|
||||||
|
<div class="access-auth-header">
|
||||||
|
<a-space>
|
||||||
|
<a-input-search
|
||||||
|
v-model="searchValue"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="$t('placeholderSearch')"
|
||||||
|
:style="{ width: '250px' }"
|
||||||
|
class="ops-input ops-input-radius"
|
||||||
|
@search="updateTableData()"
|
||||||
|
/>
|
||||||
|
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
||||||
|
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||||
|
<span @click="batchChangeEnabled(true)">{{ $t('oneterm.enabled') }}</span>
|
||||||
|
<span @click="batchChangeEnabled(false)">{{ $t('oneterm.disabled') }}</span>
|
||||||
|
<span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="openAuthDrawer(null)">{{ $t(`create`) }}</a-button>
|
||||||
|
<a-button @click="updateTableData()">{{ $t(`refresh`) }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<ops-table
|
||||||
|
size="small"
|
||||||
|
ref="opsTable"
|
||||||
|
class="ops-stripe-table"
|
||||||
|
stripe
|
||||||
|
show-overflow
|
||||||
|
show-header-overflow
|
||||||
|
resizable
|
||||||
|
:data="tableData"
|
||||||
|
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||||
|
:row-config="{ keyField: 'id', height: '80px' }"
|
||||||
|
:column-config="{ width: 200 }"
|
||||||
|
:height="tableHeight"
|
||||||
|
@checkbox-change="onSelectChange"
|
||||||
|
@checkbox-all="onSelectChange"
|
||||||
|
@checkbox-range-end="onSelectRangeEnd"
|
||||||
|
>
|
||||||
|
<vxe-column type="checkbox" width="60px"></vxe-column>
|
||||||
|
<vxe-column :title="$t('name')" field="name"></vxe-column>
|
||||||
|
<vxe-column :title="$t('description')" field="description" width="auto" min-width="200"></vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.isEnable')" field="enabled" width="100">
|
||||||
|
<template #default="{row}">
|
||||||
|
<EnabledStatus
|
||||||
|
:status="Boolean(row.enabled)"
|
||||||
|
@change="changeIsEnabled(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('created_at')" field="created_at">
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ row.createdTimeText }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.auth.targetSelect')" field="targetSelect">
|
||||||
|
<template #default="{row}">
|
||||||
|
<a-tooltip
|
||||||
|
v-for="(item, index) in row.targetSelect"
|
||||||
|
:key="index"
|
||||||
|
:title="item"
|
||||||
|
placement="topLeft"
|
||||||
|
>
|
||||||
|
<div class="access-auth-target-select">
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.accessControl.permissionConfig')" field="permissions" width="170">
|
||||||
|
<template #default="{row}">
|
||||||
|
<div class="access-auth-permisson">
|
||||||
|
<span
|
||||||
|
v-for="(item) in permissionConfigKeys"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ $t(PERMISSION_TYPE_NAME[item]) }}
|
||||||
|
<a-icon
|
||||||
|
v-if="row.permissions && row.permissions[item]"
|
||||||
|
type="check-square"
|
||||||
|
style="color: #00b42a"
|
||||||
|
/>
|
||||||
|
<a-icon
|
||||||
|
v-else
|
||||||
|
type="close-square"
|
||||||
|
style="color: #fd4c6a"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.auth.validTime')" field="validTime">
|
||||||
|
<template #default="{row}">
|
||||||
|
<div
|
||||||
|
v-for="(item) in row.validTime"
|
||||||
|
:key="item"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('operation')" width="100" fixed="right">
|
||||||
|
<template #default="{row}">
|
||||||
|
<a-space>
|
||||||
|
<a @click="openAuthDrawer(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
||||||
|
<a @click="copyAuth(row)"><ops-icon type="veops-copy"/></a>
|
||||||
|
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteAuth(row)">
|
||||||
|
<a style="color:red"><ops-icon type="icon-xianxing-delete"/></a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
</ops-table>
|
||||||
|
<div class="access-auth-pagination">
|
||||||
|
<a-pagination
|
||||||
|
size="small"
|
||||||
|
show-size-changer
|
||||||
|
:current="currentPage"
|
||||||
|
:total="totalResult"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:default-current="1"
|
||||||
|
@change="pageOrSizeChange"
|
||||||
|
@showSizeChange="pageOrSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
<AuthDrawer ref="authDrawerRef" @submit="updateTableData()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { getAuthList, deleteAuthById, putAuthById } from '@/modules/oneterm/api/authorizationV2.js'
|
||||||
|
import { TARGET_SELECT_TYPE } from './constants.js'
|
||||||
|
import { PERMISSION_TYPE_NAME, PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
|
import AuthDrawer from './authDrawer/index.vue'
|
||||||
|
import EnabledStatus from '@/modules/oneterm/components/enabledStatus/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccessAuth',
|
||||||
|
components: {
|
||||||
|
AuthDrawer,
|
||||||
|
EnabledStatus
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
PERMISSION_TYPE_NAME,
|
||||||
|
searchValue: '',
|
||||||
|
|
||||||
|
tableData: [],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
totalResult: 0,
|
||||||
|
selectedRowKeys: [],
|
||||||
|
|
||||||
|
loading: false,
|
||||||
|
loadTip: '',
|
||||||
|
permissionConfigKeys: Object.values(PERMISSION_TYPE),
|
||||||
|
authRawKeys: [
|
||||||
|
'access_control',
|
||||||
|
'account_selector',
|
||||||
|
'node_selector',
|
||||||
|
'asset_selector',
|
||||||
|
'description',
|
||||||
|
'enabled',
|
||||||
|
'name',
|
||||||
|
'permissions',
|
||||||
|
'rids',
|
||||||
|
'valid_from',
|
||||||
|
'valid_to'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return this.windowHeight - 204
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateTableData() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
getAuthList({
|
||||||
|
page_index: this.currentPage,
|
||||||
|
page_size: this.pageSize,
|
||||||
|
search: this.searchValue
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const tableData = res?.data?.list || []
|
||||||
|
tableData.forEach((row) => {
|
||||||
|
row.createdTimeText = moment(row.created_at).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
|
||||||
|
if (row.valid_from && row.valid_to) {
|
||||||
|
row.validTime = [
|
||||||
|
`${this.$t('oneterm.auth.start')}: ${row.valid_from}`,
|
||||||
|
`${this.$t('oneterm.auth.end')}: ${row.valid_to}`
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
row.validTime = [
|
||||||
|
this.$t('oneterm.auth.permanentValidity')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
row.targetSelect = [
|
||||||
|
`${this.$t('oneterm.node')}: ${this.handleTargetSelectText(row.node_selector)}`,
|
||||||
|
`${this.$t('oneterm.asset')}: ${this.handleTargetSelectText(row.asset_selector)}`,
|
||||||
|
`${this.$t('oneterm.account')}: ${this.handleTargetSelectText(row.account_selector)}`
|
||||||
|
]
|
||||||
|
})
|
||||||
|
this.tableData = tableData
|
||||||
|
this.totalResult = res?.data?.count ?? 0
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleTargetSelectText(data) {
|
||||||
|
const values = data?.values || []
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case TARGET_SELECT_TYPE.ALL:
|
||||||
|
return this.$t('oneterm.auth.all')
|
||||||
|
case TARGET_SELECT_TYPE.ID:
|
||||||
|
return this.$t('oneterm.auth.select', { count: values.length })
|
||||||
|
case TARGET_SELECT_TYPE.REGEX:
|
||||||
|
return `${this.$t('oneterm.auth.regex')} (${values.join(', ')})`
|
||||||
|
case TARGET_SELECT_TYPE.TAG:
|
||||||
|
return `${this.$t('oneterm.auth.tag')} (${values.join(', ')})`
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSelectChange() {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
onSelectRangeEnd({ records }) {
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
pageOrSizeChange(currentPage, pageSize) {
|
||||||
|
this.currentPage = currentPage
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
openAuthDrawer(data) {
|
||||||
|
this.$refs.authDrawerRef.open(data)
|
||||||
|
},
|
||||||
|
deleteAuth(row) {
|
||||||
|
this.loading = true
|
||||||
|
deleteAuthById(row.id)
|
||||||
|
.then((res) => {
|
||||||
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
copyAuth(row) {
|
||||||
|
const data = _.omit(_.cloneDeep(row), 'id')
|
||||||
|
data.name += '-copy'
|
||||||
|
this.$refs.authDrawerRef.open(data)
|
||||||
|
},
|
||||||
|
async batchDelete() {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('confirmDelete'),
|
||||||
|
onOk: async () => {
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('deleting')}...`
|
||||||
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
|
await deleteAuthById(this.selectedRowKeys[i])
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('deletingTip', { total: this.selectedRowKeys.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchChangeEnabled(enabled) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('oneterm.confirmEnable'),
|
||||||
|
onOk: async () => {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('oneterm.switching')}...`
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i++) {
|
||||||
|
const params = _.pick(_.cloneDeep(records[i]), this.authRawKeys)
|
||||||
|
params.enabled = enabled
|
||||||
|
|
||||||
|
await putAuthById(records[i].id, params)
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('oneterm.switchingTip', { total: records.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
afterBatch() {
|
||||||
|
this.loading = false
|
||||||
|
this.loadTip = ''
|
||||||
|
this.selectedRowKeys = []
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeIsEnabled(row) {
|
||||||
|
const params = _.pick(_.cloneDeep(row), this.authRawKeys)
|
||||||
|
params.enabled = !params.enabled
|
||||||
|
putAuthById(row.id, params).then(() => {
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.access-auth {
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
/deep/ .vxe-body--row {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .vxe-cell {
|
||||||
|
max-height: max-content !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-target-select {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-permisson {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 8px;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="title"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="commandFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :label="$t('name')" prop="name">
|
||||||
|
<a-input v-model="form.name" :placeholder="$t('placeholder1')" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('description')" prop="description">
|
||||||
|
<a-input v-model="form.description" :placeholder="$t('placeholder1')" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.command')" prop="cmd">
|
||||||
|
<a-input v-model="form.cmd" :placeholder="$t('placeholder1')" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.commandFilter.riskLevel')" prop="risk_level">
|
||||||
|
<a-select
|
||||||
|
v-model="form.risk_level"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="rishLevelSelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.commandFilter.category')" prop="category">
|
||||||
|
<a-select
|
||||||
|
v-model="form.category"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="categorySelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.isEnable')" prop="enable">
|
||||||
|
<a-switch v-model="form.enable" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.commandFilter.regexp')" prop="is_re">
|
||||||
|
<a-switch v-model="form.is_re" />
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { COMMAND_CATEGORY, COMMAND_CATEGORY_NAME, COMMAND_RISK_NAME } from '../constants.js'
|
||||||
|
import { postCommand, putCommandById } from '@/modules/oneterm/api/command'
|
||||||
|
|
||||||
|
const DEFAULT_FORM = {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
cmd: '',
|
||||||
|
risk_level: 0,
|
||||||
|
category: COMMAND_CATEGORY.SECURITY,
|
||||||
|
enable: true,
|
||||||
|
is_re: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommandModal',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
commandId: '',
|
||||||
|
form: { ...DEFAULT_FORM },
|
||||||
|
rules: {
|
||||||
|
name: [{ required: true, message: this.$t('placeholder1') }],
|
||||||
|
},
|
||||||
|
confirmLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.commandId) {
|
||||||
|
return this.$t('oneterm.commandFilter.editCommand')
|
||||||
|
}
|
||||||
|
return this.$t('oneterm.commandFilter.createCommand')
|
||||||
|
},
|
||||||
|
categorySelectOptions() {
|
||||||
|
return Object.values(COMMAND_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rishLevelSelectOptions() {
|
||||||
|
return [0, 1, 2, 3].map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_RISK_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
if (data) {
|
||||||
|
this.form = {
|
||||||
|
name: data?.name ?? '',
|
||||||
|
description: data?.description ?? '',
|
||||||
|
cmd: data.cmd ?? '',
|
||||||
|
category: data?.category ?? COMMAND_CATEGORY.SECURITY,
|
||||||
|
risk_level: data?.risk_level ?? 0,
|
||||||
|
enable: Boolean(data.enable),
|
||||||
|
is_re: Boolean(data.is_re)
|
||||||
|
}
|
||||||
|
this.commandId = data?.id ?? ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.form = { ...DEFAULT_FORM }
|
||||||
|
this.commandId = ''
|
||||||
|
|
||||||
|
this.$refs.commandFormRef.resetFields()
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
async handleOk() {
|
||||||
|
this.$refs.commandFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.confirmLoading = true
|
||||||
|
try {
|
||||||
|
if (this.commandId) {
|
||||||
|
await putCommandById(this.commandId, { ...this.form })
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
} else {
|
||||||
|
await postCommand({ ...this.form })
|
||||||
|
this.$message.success(this.$t('createSuccess'))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('submit')
|
||||||
|
this.handleCancel()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('submit error:', e)
|
||||||
|
} finally {
|
||||||
|
this.confirmLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -0,0 +1,343 @@
|
|||||||
|
<template>
|
||||||
|
<div class="command-management">
|
||||||
|
<a-spin :tip="loadTip" :spinning="loading">
|
||||||
|
<div class="command-management-header">
|
||||||
|
<a-space>
|
||||||
|
<a-input-search
|
||||||
|
v-model="searchValue"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="$t('placeholderSearch')"
|
||||||
|
:style="{ width: '250px' }"
|
||||||
|
class="ops-input ops-input-radius"
|
||||||
|
@search="updateTableData()"
|
||||||
|
/>
|
||||||
|
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
||||||
|
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||||
|
<span @click="batchChangeEnabled(true)">{{ $t('oneterm.enabled') }}</span>
|
||||||
|
<span @click="batchChangeEnabled(false)">{{ $t('oneterm.disabled') }}</span>
|
||||||
|
<span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="openModal(null)">{{ $t('create') }}</a-button>
|
||||||
|
<a-button @click="updateTableData()">{{ $t('refresh') }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<ops-table
|
||||||
|
size="small"
|
||||||
|
ref="opsTable"
|
||||||
|
class="ops-stripe-table"
|
||||||
|
stripe
|
||||||
|
show-overflow
|
||||||
|
show-header-overflow
|
||||||
|
resizable
|
||||||
|
:data="tableData"
|
||||||
|
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||||
|
:row-config="{ keyField: 'id' }"
|
||||||
|
:height="tableHeight"
|
||||||
|
:filter-config="{ remote: true }"
|
||||||
|
@filter-change="handleFilterChange"
|
||||||
|
@checkbox-change="onSelectChange"
|
||||||
|
@checkbox-all="onSelectChange"
|
||||||
|
@checkbox-range-end="onSelectRangeEnd"
|
||||||
|
>
|
||||||
|
<vxe-column type="checkbox" width="60px"></vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.name')" field="name"></vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.command')" field="cmd"></vxe-column>
|
||||||
|
<vxe-column
|
||||||
|
:title="$t('oneterm.commandFilter.riskLevel')"
|
||||||
|
field="risk_level"
|
||||||
|
:filters="riskLevelFilters"
|
||||||
|
:filter-multiple="false"
|
||||||
|
>
|
||||||
|
<template #default="{row}">
|
||||||
|
<RiskDisplay :type="row.risk_level" />
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column
|
||||||
|
:title="$t('oneterm.commandFilter.category')"
|
||||||
|
field="category"
|
||||||
|
:filters="categoryFilters"
|
||||||
|
:filter-multiple="false"
|
||||||
|
>
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ $t(row.categoryName) }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.isEnable')" field="enable">
|
||||||
|
<template #default="{row}">
|
||||||
|
<EnabledStatus
|
||||||
|
:status="Boolean(row.enable)"
|
||||||
|
@change="changeEnable(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('created_at')" width="170">
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ row.createdTimeText }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('operation')" width="100">
|
||||||
|
<template #default="{row}">
|
||||||
|
<a-space>
|
||||||
|
<a @click="openModal(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
||||||
|
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteCommand(row)">
|
||||||
|
<a style="color:red"><ops-icon type="icon-xianxing-delete"/></a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
</ops-table>
|
||||||
|
<div class="command-management-pagination">
|
||||||
|
<a-pagination
|
||||||
|
size="small"
|
||||||
|
show-size-changer
|
||||||
|
:current="currentPage"
|
||||||
|
:total="totalResult"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:default-current="1"
|
||||||
|
@change="pageOrSizeChange"
|
||||||
|
@showSizeChange="pageOrSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
<CommandModal ref="commandModal" @submit="updateTableData()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { getCommandList, deleteCommandById, putCommandById } from '@/modules/oneterm/api/command.js'
|
||||||
|
import { COMMAND_CATEGORY, COMMAND_CATEGORY_NAME, COMMAND_RISK_NAME } from '../constants.js'
|
||||||
|
|
||||||
|
import CommandModal from './commandModal.vue'
|
||||||
|
import RiskDisplay from './riskDisplay.vue'
|
||||||
|
import EnabledStatus from '@/modules/oneterm/components/enabledStatus/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommandManagement',
|
||||||
|
components: {
|
||||||
|
CommandModal,
|
||||||
|
RiskDisplay,
|
||||||
|
EnabledStatus
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchValue: '',
|
||||||
|
currentRiskLevel: [],
|
||||||
|
currentCategory: [],
|
||||||
|
|
||||||
|
tableData: [],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
totalResult: 0,
|
||||||
|
selectedRowKeys: [],
|
||||||
|
loading: false,
|
||||||
|
loadTip: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return this.windowHeight - 254
|
||||||
|
},
|
||||||
|
riskLevelFilters() {
|
||||||
|
return [0, 1, 2, 3].map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_RISK_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
categoryFilters() {
|
||||||
|
return Object.values(COMMAND_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateTableData() {
|
||||||
|
this.loading = true
|
||||||
|
const risk_level = this?.currentRiskLevel?.length ? this.currentRiskLevel.join(',') : undefined
|
||||||
|
const category = this?.currentCategory?.length ? this.currentCategory.join(',') : undefined
|
||||||
|
|
||||||
|
getCommandList({
|
||||||
|
page_index: this.currentPage,
|
||||||
|
page_size: this.pageSize,
|
||||||
|
search: this.searchValue,
|
||||||
|
risk_level,
|
||||||
|
category
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const tableData = res?.data?.list || []
|
||||||
|
tableData.forEach((row) => {
|
||||||
|
row.categoryName = COMMAND_CATEGORY_NAME?.[row.category] ?? '-'
|
||||||
|
row.createdTimeText = moment(row.created_at).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
})
|
||||||
|
this.tableData = tableData
|
||||||
|
this.totalResult = res?.data?.count ?? 0
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onSelectChange() {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
onSelectRangeEnd({ records }) {
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
pageOrSizeChange(currentPage, pageSize) {
|
||||||
|
this.currentPage = currentPage
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
openModal(data) {
|
||||||
|
this.$refs.commandModal.open(data)
|
||||||
|
},
|
||||||
|
deleteCommand(row) {
|
||||||
|
this.loading = true
|
||||||
|
deleteCommandById(row.id)
|
||||||
|
.then((res) => {
|
||||||
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async batchDelete() {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('confirmDelete'),
|
||||||
|
onOk: async () => {
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('deleting')}...`
|
||||||
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
|
await deleteCommandById(this.selectedRowKeys[i])
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('deletingTip', { total: this.selectedRowKeys.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchChangeEnabled(enabled) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('oneterm.confirmEnable'),
|
||||||
|
onOk: async () => {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('oneterm.switching')}...`
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i++) {
|
||||||
|
const params = _.omit(_.cloneDeep(records[i]), ['categoryName', 'createdTimeText', 'id'])
|
||||||
|
params.enable = enabled
|
||||||
|
|
||||||
|
await putCommandById(records[i].id, params)
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('oneterm.switchingTip', { total: records.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
afterBatch() {
|
||||||
|
this.loading = false
|
||||||
|
this.loadTip = ''
|
||||||
|
this.selectedRowKeys = []
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeEnable(row) {
|
||||||
|
const params = _.omit(_.cloneDeep(row), ['categoryName', 'createdTimeText', 'id'])
|
||||||
|
params.enable = !params.enable
|
||||||
|
|
||||||
|
putCommandById(
|
||||||
|
row.id,
|
||||||
|
params
|
||||||
|
).then(() => {
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFilterChange(e) {
|
||||||
|
switch (e.field) {
|
||||||
|
case 'risk_level':
|
||||||
|
this.currentRiskLevel = e?.values
|
||||||
|
this.updateTableData()
|
||||||
|
break
|
||||||
|
case 'category':
|
||||||
|
this.currentCategory = e?.values
|
||||||
|
this.updateTableData()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.command-management {
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<span class="risk">
|
||||||
|
<span
|
||||||
|
class="risk-status"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: color + '22',
|
||||||
|
'--innerBackgroundColor': color
|
||||||
|
}"
|
||||||
|
></span>
|
||||||
|
<span class="risk-text">
|
||||||
|
{{ $t(text) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { COMMAND_RISK_NAME } from '../constants.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RiskDisplay',
|
||||||
|
props: {
|
||||||
|
type: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
COMMAND_RISK_COLOR: {
|
||||||
|
0: '#52c41a',
|
||||||
|
1: '#faad14',
|
||||||
|
2: '#ff4d4f',
|
||||||
|
3: '#722323'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
text() {
|
||||||
|
return COMMAND_RISK_NAME?.[this.type]
|
||||||
|
},
|
||||||
|
color() {
|
||||||
|
return this.COMMAND_RISK_COLOR?.[this.type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.risk {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-status {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-top: -4px;
|
||||||
|
margin-left: -4px;
|
||||||
|
background-color: var(--innerBackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="title"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
:width="800"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="commandTemplateForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :label="$t('name')" prop="name">
|
||||||
|
<a-input v-model="form.name" :placeholder="$t(`placeholder1`)"/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('description')" prop="description">
|
||||||
|
<a-input v-model="form.description" :placeholder="$t('placeholder1')"/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.commandFilter.category')" prop="category">
|
||||||
|
<a-select
|
||||||
|
v-model="form.category"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="categorySelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.commandFilter.selectCommand')" prop="cmd_ids">
|
||||||
|
<a-transfer
|
||||||
|
:data-source="allCommand"
|
||||||
|
:target-keys="form.cmd_ids"
|
||||||
|
:selected-keys="transferSelectedKeys"
|
||||||
|
:render="item => item.title"
|
||||||
|
:titles="[$t('oneterm.commandFilter.unselectCommand'), $t('oneterm.commandFilter.selectedCommand')]"
|
||||||
|
:listStyle="{
|
||||||
|
width: 'calc((100% - 40px) / 2)',
|
||||||
|
height: '300px'
|
||||||
|
}"
|
||||||
|
@change="handleTransferChange"
|
||||||
|
@selectChange="handleTransferSelectChange"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { COMMAND_CATEGORY, COMMAND_CATEGORY_NAME } from '../constants.js'
|
||||||
|
import { postCommandTemplate, putCommandTemplateById } from '@/modules/oneterm/api/commandTemplate.js'
|
||||||
|
import { getCommandList } from '@/modules/oneterm/api/command.js'
|
||||||
|
|
||||||
|
const DEFAULT_FORM = {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
category: COMMAND_CATEGORY.SECURITY,
|
||||||
|
cmd_ids: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommandTemplateModal',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
commandTemplateId: '',
|
||||||
|
form: { ...DEFAULT_FORM },
|
||||||
|
rules: {
|
||||||
|
name: [{ required: true, message: this.$t(`placeholder1`) }],
|
||||||
|
cmd_ids: [{ required: true, message: this.$t(`placeholder2`) }],
|
||||||
|
},
|
||||||
|
allCommand: [],
|
||||||
|
transferSelectedKeys: [],
|
||||||
|
confirmLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.commandTemplateId) {
|
||||||
|
return this.$t('oneterm.commandFilter.editCommandTemplate')
|
||||||
|
}
|
||||||
|
return this.$t('oneterm.commandFilter.createCommandTemplate')
|
||||||
|
},
|
||||||
|
categorySelectOptions() {
|
||||||
|
return Object.values(COMMAND_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
if (data) {
|
||||||
|
this.form = {
|
||||||
|
name: data?.name ?? '',
|
||||||
|
description: data?.description ?? '',
|
||||||
|
category: data?.category ?? COMMAND_CATEGORY.SECURITY,
|
||||||
|
cmd_ids: (data?.cmd_ids ?? []).map((id) => String(id))
|
||||||
|
}
|
||||||
|
this.commandTemplateId = data?.id ?? ''
|
||||||
|
}
|
||||||
|
this.getAllCommand()
|
||||||
|
},
|
||||||
|
async getAllCommand() {
|
||||||
|
const res = await getCommandList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999,
|
||||||
|
})
|
||||||
|
const allCommand = res?.data?.list || []
|
||||||
|
this.allCommand = allCommand.map((item) => {
|
||||||
|
return {
|
||||||
|
key: String(item.id),
|
||||||
|
title: item.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.form.cmd_ids = this.form.cmd_ids.filter((id) => this.allCommand.some((command) => command?.key === id))
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.form = { ...DEFAULT_FORM }
|
||||||
|
this.allCommand = []
|
||||||
|
this.transferSelectedKeys = []
|
||||||
|
this.commandTemplateId = ''
|
||||||
|
|
||||||
|
this.$refs.commandTemplateForm.resetFields()
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
handleTransferChange(nextTargetKeys) {
|
||||||
|
this.form.cmd_ids = nextTargetKeys
|
||||||
|
},
|
||||||
|
handleTransferSelectChange(sourceSelectedKeys, targetSelectedKeys) {
|
||||||
|
this.transferSelectedKeys = [...sourceSelectedKeys, ...targetSelectedKeys]
|
||||||
|
},
|
||||||
|
async handleOk() {
|
||||||
|
this.$refs.commandTemplateForm.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.confirmLoading = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
...this.form,
|
||||||
|
cmd_ids: this.form.cmd_ids.map((id) => Number(id))
|
||||||
|
}
|
||||||
|
if (this.commandTemplateId) {
|
||||||
|
await putCommandTemplateById(this.commandTemplateId, params)
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
} else {
|
||||||
|
await postCommandTemplate(params)
|
||||||
|
this.$message.success(this.$t('createSuccess'))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('submit')
|
||||||
|
this.handleCancel()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('submit error:', e)
|
||||||
|
} finally {
|
||||||
|
this.confirmLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -1,67 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="command-intercept">
|
<div class="command-template-management">
|
||||||
<a-spin :tip="loadTip" :spinning="loading">
|
<a-spin :tip="loadTip" :spinning="loading">
|
||||||
<div class="command-intercept-header">
|
<div class="command-template-management-header">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
allow-clear
|
v-model="searchValue"
|
||||||
v-model="filterName"
|
:placeholder="$t('placeholderSearch')"
|
||||||
:style="{ width: '250px' }"
|
:style="{ width: '250px' }"
|
||||||
class="ops-input ops-input-radius"
|
class="ops-input ops-input-radius"
|
||||||
:placeholder="$t('placeholderSearch')"
|
allow-clear
|
||||||
@search="updateTableData()"
|
@search="updateTableData()"
|
||||||
/>
|
/>
|
||||||
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
||||||
<span @click="batchDelete">{{ $t(`delete`) }}</span>
|
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||||
<span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
|
<span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button type="primary" @click="openModal(null)">{{ $t(`create`) }}</a-button>
|
<a-button type="primary" @click="openModal(null)">{{ $t('create') }}</a-button>
|
||||||
<a-button @click="updateTableData()">{{ $t(`refresh`) }}</a-button>
|
<a-button @click="updateTableData()">{{ $t('refresh') }}</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<ops-table
|
<ops-table
|
||||||
size="small"
|
size="small"
|
||||||
ref="opsTable"
|
ref="opsTable"
|
||||||
stripe
|
|
||||||
class="ops-stripe-table"
|
class="ops-stripe-table"
|
||||||
:data="tableData"
|
stripe
|
||||||
show-overflow
|
show-overflow
|
||||||
show-header-overflow
|
show-header-overflow
|
||||||
@checkbox-change="onSelectChange"
|
resizable
|
||||||
@checkbox-all="onSelectChange"
|
:data="tableData"
|
||||||
@checkbox-range-end="onSelectRangeEnd"
|
|
||||||
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||||
:row-config="{ keyField: 'id' }"
|
:row-config="{ keyField: 'id' }"
|
||||||
:height="tableHeight"
|
:height="tableHeight"
|
||||||
resizable
|
:filter-config="{ remote: true }"
|
||||||
|
@filter-change="handleFilterChange"
|
||||||
|
@checkbox-change="onSelectChange"
|
||||||
|
@checkbox-all="onSelectChange"
|
||||||
|
@checkbox-range-end="onSelectRangeEnd"
|
||||||
>
|
>
|
||||||
<vxe-column type="checkbox" width="60px"></vxe-column>
|
<vxe-column type="checkbox" width="60px"></vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.name`)" field="name"></vxe-column>
|
<vxe-column :title="$t('name')" field="name"></vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.command`)" field="cmd"></vxe-column>
|
<vxe-column :title="$t('description')" field="description"></vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.commandIntercept.enable`)" field="enable">
|
<vxe-column :title="$t('oneterm.commandFilter.commandCount')" field="description">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
<a-switch :checked="Boolean(row.enable)" @change="changeEnable(row)" />
|
{{ row.cmd_ids.length }}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column :title="$t(`created_at`)" width="120">
|
<vxe-column
|
||||||
|
:title="$t('oneterm.commandFilter.category')"
|
||||||
|
field="category"
|
||||||
|
:filters="categoryFilters"
|
||||||
|
:filter-multiple="false"
|
||||||
|
>
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
{{ moment(row.created_at).format('YYYY-MM-DD') }}
|
{{ $t(row.categoryName) }}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column :title="$t(`operation`)" width="100">
|
<vxe-column :title="$t('created_at')" width="170">
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ row.createdTimeText }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('operation')" width="100">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a @click="openModal(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
<a @click="openModal(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
||||||
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteCommand(row)">
|
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteCommandTemplate(row)">
|
||||||
<a style="color:red"><ops-icon type="icon-xianxing-delete"/></a>
|
<a style="color:red"><ops-icon type="icon-xianxing-delete"/></a>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
</ops-table>
|
</ops-table>
|
||||||
<div class="command-intercept-pagination">
|
<div class="command-template-management-pagination">
|
||||||
<a-pagination
|
<a-pagination
|
||||||
size="small"
|
size="small"
|
||||||
show-size-changer
|
show-size-changer
|
||||||
@@ -82,23 +94,26 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
<CommandModal ref="commandModal" @submit="updateTableData()" />
|
<CommandTemplateModal ref="commandTemplateModal" @submit="updateTableData()" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { getCommandList, deleteCommandById, putCommandById } from '@/modules/oneterm/api/command.js'
|
import { getCommandTemplateList, deleteCommandTemplateById } from '@/modules/oneterm/api/commandTemplate.js'
|
||||||
|
import { COMMAND_CATEGORY, COMMAND_CATEGORY_NAME } from '../constants.js'
|
||||||
|
|
||||||
import CommandModal from './commandModal.vue'
|
import CommandTemplateModal from './commandTemplateModal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CommandIntercept',
|
name: 'CommandTemplateManagement',
|
||||||
components: { CommandModal },
|
components: { CommandTemplateModal },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filterName: '',
|
searchValue: '',
|
||||||
|
currentCategory: [],
|
||||||
|
|
||||||
tableData: [],
|
tableData: [],
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
@@ -113,23 +128,38 @@ export default {
|
|||||||
windowHeight: (state) => state.windowHeight,
|
windowHeight: (state) => state.windowHeight,
|
||||||
}),
|
}),
|
||||||
tableHeight() {
|
tableHeight() {
|
||||||
return this.windowHeight - 207
|
return this.windowHeight - 254
|
||||||
|
},
|
||||||
|
categoryFilters() {
|
||||||
|
return Object.values(COMMAND_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(COMMAND_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.updateTableData()
|
this.updateTableData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
moment,
|
|
||||||
updateTableData() {
|
updateTableData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
getCommandList({
|
const category = this?.currentCategory?.length ? this.currentCategory.join(',') : undefined
|
||||||
|
|
||||||
|
getCommandTemplateList({
|
||||||
page_index: this.currentPage,
|
page_index: this.currentPage,
|
||||||
page_size: this.pageSize,
|
page_size: this.pageSize,
|
||||||
search: this.filterName,
|
search: this.searchValue,
|
||||||
|
category
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.tableData = res?.data?.list || []
|
const tableData = res?.data?.list || []
|
||||||
|
tableData.forEach((row) => {
|
||||||
|
row.categoryName = COMMAND_CATEGORY_NAME?.[row.category] ?? '-'
|
||||||
|
row.createdTimeText = moment(row.created_at).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
})
|
||||||
|
this.tableData = tableData
|
||||||
this.totalResult = res?.data?.count ?? 0
|
this.totalResult = res?.data?.count ?? 0
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -150,11 +180,11 @@ export default {
|
|||||||
this.updateTableData()
|
this.updateTableData()
|
||||||
},
|
},
|
||||||
openModal(data) {
|
openModal(data) {
|
||||||
this.$refs.commandModal.open(data)
|
this.$refs.commandTemplateModal.open(data)
|
||||||
},
|
},
|
||||||
deleteCommand(row) {
|
deleteCommandTemplate(row) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
deleteCommandById(row.id)
|
deleteCommandTemplateById(row.id)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.$message.success(this.$t('deleteSuccess'))
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
this.updateTableData()
|
this.updateTableData()
|
||||||
@@ -164,17 +194,16 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async batchDelete() {
|
async batchDelete() {
|
||||||
const that = this
|
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: that.$t('warning'),
|
title: this.$t('warning'),
|
||||||
content: that.$t('confirmDelete'),
|
content: this.$t('confirmDelete'),
|
||||||
async onOk() {
|
onOk: async () => {
|
||||||
let successNum = 0
|
let successNum = 0
|
||||||
let errorNum = 0
|
let errorNum = 0
|
||||||
that.loading = true
|
this.loading = true
|
||||||
that.loadTip = `${that.$t('deleting')}...`
|
this.loadTip = `${this.$t('deleting')}...`
|
||||||
for (let i = 0; i < that.selectedRowKeys.length; i++) {
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
await deleteCommandById(that.selectedRowKeys[i], false)
|
await deleteCommandTemplateById(this.selectedRowKeys[i])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
successNum += 1
|
successNum += 1
|
||||||
})
|
})
|
||||||
@@ -182,32 +211,36 @@ export default {
|
|||||||
errorNum += 1
|
errorNum += 1
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
that.loadTip = that.$t('deletingTip', { total: that.selectedRowKeys.length, successNum, errorNum })
|
this.loadTip = this.$t('deletingTip', { total: this.selectedRowKeys.length, successNum, errorNum })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
that.loading = false
|
this.loading = false
|
||||||
that.loadTip = ''
|
this.loadTip = ''
|
||||||
that.selectedRowKeys = []
|
this.selectedRowKeys = []
|
||||||
that.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
this.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||||
that.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
this.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||||
that.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
that.updateTableData()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
changeEnable(row) {
|
|
||||||
putCommandById(row.id, { ...row, enable: Boolean(!row.enable) }).then(() => {
|
|
||||||
this.$message.success(this.$t('editSuccess'))
|
|
||||||
this.updateTableData()
|
this.updateTableData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFilterChange(e) {
|
||||||
|
switch (e.field) {
|
||||||
|
case 'category':
|
||||||
|
this.currentCategory = e?.values
|
||||||
|
this.updateTableData()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.command-intercept {
|
.command-template-management {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
@@ -0,0 +1,26 @@
|
|||||||
|
export const COMMAND_CATEGORY = {
|
||||||
|
SECURITY: 'security',
|
||||||
|
SYSTEM: 'system',
|
||||||
|
DATABASE: 'database',
|
||||||
|
NETWORK: 'network',
|
||||||
|
FILE: 'file',
|
||||||
|
DEVELOPER: 'developer',
|
||||||
|
CUSTOM: 'custom'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const COMMAND_CATEGORY_NAME = {
|
||||||
|
[COMMAND_CATEGORY.SECURITY]: 'oneterm.commandFilter.securityRelated',
|
||||||
|
[COMMAND_CATEGORY.SYSTEM]: 'oneterm.commandFilter.systemOperations',
|
||||||
|
[COMMAND_CATEGORY.DATABASE]: 'oneterm.commandFilter.databaseOperations',
|
||||||
|
[COMMAND_CATEGORY.NETWORK]: 'oneterm.commandFilter.networkOperations',
|
||||||
|
[COMMAND_CATEGORY.FILE]: 'oneterm.commandFilter.fileOperations',
|
||||||
|
[COMMAND_CATEGORY.DEVELOPER]: 'oneterm.commandFilter.developmentRelated',
|
||||||
|
[COMMAND_CATEGORY.CUSTOM]: 'other'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const COMMAND_RISK_NAME = {
|
||||||
|
0: 'oneterm.commandFilter.safe',
|
||||||
|
1: 'oneterm.commandFilter.warning',
|
||||||
|
2: 'oneterm.commandFilter.dangerous',
|
||||||
|
3: 'oneterm.commandFilter.criticalDanger'
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<a-tabs
|
||||||
|
type="card"
|
||||||
|
class="ops-tab command-tab"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
key="commandManagement"
|
||||||
|
:tab="$t('oneterm.commandFilter.commandManagement')"
|
||||||
|
:forceRender="true"
|
||||||
|
>
|
||||||
|
<CommandManagement />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
key="commandTemplateManagement"
|
||||||
|
:tab="$t('oneterm.commandFilter.commandTemplateManagement')"
|
||||||
|
:forceRender="true"
|
||||||
|
>
|
||||||
|
<CommandTemplateManagement />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CommandManagement from './commandManagement/index.vue'
|
||||||
|
import CommandTemplateManagement from './commandTemplateManagement/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccessCommand',
|
||||||
|
components: {
|
||||||
|
CommandManagement,
|
||||||
|
CommandTemplateManagement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.command-tab {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,17 @@
|
|||||||
|
export const TIME_TEMPLATE_CATEGORY = {
|
||||||
|
WORK: 'work',
|
||||||
|
DUTY: 'duty',
|
||||||
|
MAINTENENCE: 'maintenance',
|
||||||
|
EMERGENCY: 'emergency',
|
||||||
|
ALWAYS: 'always',
|
||||||
|
CUSTOM: 'custom'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TIME_TEMPLATE_CATEGORY_NAME = {
|
||||||
|
[TIME_TEMPLATE_CATEGORY.WORK]: 'oneterm.timeTemplate.workTime',
|
||||||
|
[TIME_TEMPLATE_CATEGORY.DUTY]: 'oneterm.timeTemplate.dutyTime',
|
||||||
|
[TIME_TEMPLATE_CATEGORY.MAINTENENCE]: 'oneterm.timeTemplate.maintenanceTime',
|
||||||
|
[TIME_TEMPLATE_CATEGORY.EMERGENCY]: 'oneterm.timeTemplate.emergencyResponse',
|
||||||
|
[TIME_TEMPLATE_CATEGORY.ALWAYS]: 'oneterm.timeTemplate.allTime',
|
||||||
|
[TIME_TEMPLATE_CATEGORY.CUSTOM]: 'other'
|
||||||
|
}
|
314
oneterm-ui/src/modules/oneterm/views/access/time/index.vue
Normal file
314
oneterm-ui/src/modules/oneterm/views/access/time/index.vue
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<div class="time-template">
|
||||||
|
<a-spin :tip="loadTip" :spinning="loading">
|
||||||
|
<div class="time-template-header">
|
||||||
|
<a-space>
|
||||||
|
<a-input-search
|
||||||
|
v-model="searchValue"
|
||||||
|
:placeholder="$t('placeholderSearch')"
|
||||||
|
:style="{ width: '250px' }"
|
||||||
|
class="ops-input ops-input-radius"
|
||||||
|
allow-clear
|
||||||
|
@search="updateTableData()"
|
||||||
|
/>
|
||||||
|
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
|
||||||
|
<span @click="batchDelete">{{ $t('delete') }}</span>
|
||||||
|
<span @click="batchChangeActive(true)">{{ $t('oneterm.enabled') }}</span>
|
||||||
|
<span @click="batchChangeActive(false)">{{ $t('oneterm.disabled') }}</span>
|
||||||
|
<span>{{ $t('selectRows', { rows: selectedRowKeys.length }) }}</span>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="openModal(null)">{{ $t(`create`) }}</a-button>
|
||||||
|
<a-button @click="updateTableData()">{{ $t(`refresh`) }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<ops-table
|
||||||
|
size="small"
|
||||||
|
ref="opsTable"
|
||||||
|
class="ops-stripe-table"
|
||||||
|
stripe
|
||||||
|
show-overflow
|
||||||
|
show-header-overflow
|
||||||
|
resizable
|
||||||
|
:data="tableData"
|
||||||
|
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||||
|
:row-config="{ keyField: 'id' }"
|
||||||
|
:height="tableHeight"
|
||||||
|
:filter-config="{ remote: true }"
|
||||||
|
@filter-change="handleFilterChange"
|
||||||
|
@checkbox-change="onSelectChange"
|
||||||
|
@checkbox-all="onSelectChange"
|
||||||
|
@checkbox-range-end="onSelectRangeEnd"
|
||||||
|
>
|
||||||
|
<vxe-column type="checkbox" width="60px"></vxe-column>
|
||||||
|
<vxe-column :title="$t('name')" field="name"></vxe-column>
|
||||||
|
<vxe-column :title="$t('description')" field="description"></vxe-column>
|
||||||
|
<vxe-column
|
||||||
|
field="category"
|
||||||
|
:title="$t(`oneterm.timeTemplate.category`)"
|
||||||
|
:filters="categoryFilters"
|
||||||
|
:filter-multiple="false"
|
||||||
|
>
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ $t(row.categoryName) }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.timeTemplate.timeZone')" field="timezone"></vxe-column>
|
||||||
|
<vxe-column :title="$t('oneterm.isEnable')" field="is_active">
|
||||||
|
<template #default="{row}">
|
||||||
|
<EnabledStatus
|
||||||
|
:status="Boolean(row.is_active)"
|
||||||
|
@change="changeIsActive(row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('created_at')" width="170">
|
||||||
|
<template #default="{row}">
|
||||||
|
{{ row.createdTimeText }}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column :title="$t('operation')" width="100">
|
||||||
|
<template #default="{row}">
|
||||||
|
<a-space>
|
||||||
|
<a @click="openModal(row)"><ops-icon type="icon-xianxing-edit"/></a>
|
||||||
|
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteTimeTemplate(row)">
|
||||||
|
<a style="color:red"><ops-icon type="icon-xianxing-delete"/></a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
</ops-table>
|
||||||
|
<div class="time-template-pagination">
|
||||||
|
<a-pagination
|
||||||
|
size="small"
|
||||||
|
show-size-changer
|
||||||
|
:current="currentPage"
|
||||||
|
:total="totalResult"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:default-current="1"
|
||||||
|
@change="pageOrSizeChange"
|
||||||
|
@showSizeChange="pageOrSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
<TimeTemplateModal ref="timeTemplateModalRef" @submit="updateTableData()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { getTimeTemplateList, deleteTimeTemplateById, putTimeTemplateById } from '@/modules/oneterm/api/timeTemplate.js'
|
||||||
|
import { TIME_TEMPLATE_CATEGORY, TIME_TEMPLATE_CATEGORY_NAME } from './constants.js'
|
||||||
|
|
||||||
|
import TimeTemplateModal from './timeTemplateModal.vue'
|
||||||
|
import EnabledStatus from '@/modules/oneterm/components/enabledStatus/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TimeTemplate',
|
||||||
|
components: {
|
||||||
|
TimeTemplateModal,
|
||||||
|
EnabledStatus
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchValue: '',
|
||||||
|
currentCategory: [],
|
||||||
|
|
||||||
|
tableData: [],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
totalResult: 0,
|
||||||
|
selectedRowKeys: [],
|
||||||
|
loading: false,
|
||||||
|
loadTip: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return this.windowHeight - 254
|
||||||
|
},
|
||||||
|
categoryFilters() {
|
||||||
|
return Object.values(TIME_TEMPLATE_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(TIME_TEMPLATE_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateTableData() {
|
||||||
|
this.loading = true
|
||||||
|
const category = this?.currentCategory?.length ? this.currentCategory.join(',') : undefined
|
||||||
|
|
||||||
|
getTimeTemplateList({
|
||||||
|
page_index: this.currentPage,
|
||||||
|
page_size: this.pageSize,
|
||||||
|
search: this.searchValue,
|
||||||
|
category
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const tableData = res?.data?.list || []
|
||||||
|
tableData.forEach((row) => {
|
||||||
|
row.categoryName = TIME_TEMPLATE_CATEGORY_NAME?.[row.category] ?? '-'
|
||||||
|
row.createdTimeText = moment(row.created_at).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
})
|
||||||
|
this.tableData = tableData
|
||||||
|
this.totalResult = res?.data?.count ?? 0
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onSelectChange() {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
onSelectRangeEnd({ records }) {
|
||||||
|
this.selectedRowKeys = records.map((i) => i.id)
|
||||||
|
},
|
||||||
|
pageOrSizeChange(currentPage, pageSize) {
|
||||||
|
this.currentPage = currentPage
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.updateTableData()
|
||||||
|
},
|
||||||
|
openModal(data) {
|
||||||
|
this.$refs.timeTemplateModalRef.open(data)
|
||||||
|
},
|
||||||
|
deleteTimeTemplate(row) {
|
||||||
|
this.loading = true
|
||||||
|
deleteTimeTemplateById(row.id)
|
||||||
|
.then((res) => {
|
||||||
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async batchDelete() {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('confirmDelete'),
|
||||||
|
onOk: async () => {
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('deleting')}...`
|
||||||
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
|
await deleteTimeTemplateById(this.selectedRowKeys[i])
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('deletingTip', { total: this.selectedRowKeys.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchChangeActive(active) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('oneterm.confirmEnable'),
|
||||||
|
onOk: async () => {
|
||||||
|
const opsTable = this.$refs.opsTable.getVxetableRef()
|
||||||
|
const records = [...opsTable.getCheckboxRecords(), ...opsTable.getCheckboxReserveRecords()]
|
||||||
|
|
||||||
|
let successNum = 0
|
||||||
|
let errorNum = 0
|
||||||
|
this.loading = true
|
||||||
|
this.loadTip = `${this.$t('oneterm.switching')}...`
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i++) {
|
||||||
|
const params = _.omit(_.cloneDeep(records[i]), ['categoryName', 'createdTimeText', 'id', 'resource_id'])
|
||||||
|
params.is_active = active
|
||||||
|
|
||||||
|
await putTimeTemplateById(records[i].id, params)
|
||||||
|
.then(() => {
|
||||||
|
successNum += 1
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
errorNum += 1
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadTip = this.$t('oneterm.switchingTip', { total: records.length, successNum, errorNum })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.afterBatch()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
afterBatch() {
|
||||||
|
this.loading = false
|
||||||
|
this.loadTip = ''
|
||||||
|
this.selectedRowKeys = []
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||||
|
this.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeIsActive(row) {
|
||||||
|
const params = _.omit(_.cloneDeep(row), ['categoryName', 'createdTimeText', 'id', 'resource_id'])
|
||||||
|
params.is_active = !params.is_active
|
||||||
|
|
||||||
|
putTimeTemplateById(row.id, params).then(() => {
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
this.updateTableData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFilterChange(e) {
|
||||||
|
switch (e.field) {
|
||||||
|
case 'category':
|
||||||
|
this.currentCategory = e?.values
|
||||||
|
this.updateTableData()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.time-template {
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* merge time range
|
||||||
|
* @param {*} timeRanges Array<{ day: number, value: Array<'HH:mm'> }>
|
||||||
|
* @returns Array<{ weekdays: number[], start_time: 'HH:mm', end_time: 'HH:mm' }>
|
||||||
|
*/
|
||||||
|
export function mergeTimeRange(timeRanges) {
|
||||||
|
// 1. Count which weekdays each interval appears on
|
||||||
|
const intervalMap = {} // key: 'start~end', value: Set(day)
|
||||||
|
timeRanges.forEach(item => {
|
||||||
|
item.value.forEach(interval => {
|
||||||
|
if (!intervalMap[interval]) intervalMap[interval] = new Set()
|
||||||
|
intervalMap[interval].add(item.day)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2. Group by interval and sort by weekday
|
||||||
|
const intervalArr = Object.entries(intervalMap).map(([interval, weekSet]) => {
|
||||||
|
const [start_time, end_time] = interval.split('~')
|
||||||
|
return {
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
weekdays: Array.from(weekSet).sort((a, b) => a - b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. Merge consecutive intervals with exactly the same weekdays
|
||||||
|
intervalArr.sort((a, b) => {
|
||||||
|
// First by weekdays, then by start_time
|
||||||
|
const w1 = a.weekdays.join(',')
|
||||||
|
const w2 = b.weekdays.join(',')
|
||||||
|
if (w1 !== w2) return w1.localeCompare(w2)
|
||||||
|
return a.start_time.localeCompare(b.start_time)
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < intervalArr.length; i++) {
|
||||||
|
const cur = intervalArr[i]
|
||||||
|
if (
|
||||||
|
result.length &&
|
||||||
|
// Same weekdays as previous, and previous end_time equals current start_time
|
||||||
|
JSON.stringify(result[result.length - 1].weekdays) === JSON.stringify(cur.weekdays) &&
|
||||||
|
result[result.length - 1].end_time === cur.start_time
|
||||||
|
) {
|
||||||
|
// Merge
|
||||||
|
result[result.length - 1].end_time = cur.end_time
|
||||||
|
} else {
|
||||||
|
result.push({ ...cur })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
// Convert time string to minutes
|
||||||
|
function timeToMinutes(str) {
|
||||||
|
const [h, m] = str.split(':').map(Number)
|
||||||
|
return h * 60 + m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert minutes to time string
|
||||||
|
function minutesToTime(mins) {
|
||||||
|
const h = String(Math.floor(mins / 60)).padStart(2, '0')
|
||||||
|
const m = String(mins % 60).padStart(2, '0')
|
||||||
|
return `${h}:${m}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into half-hour intervals
|
||||||
|
function splitHalfHour(start, end) {
|
||||||
|
const res = []
|
||||||
|
let s = timeToMinutes(start)
|
||||||
|
const e = timeToMinutes(end)
|
||||||
|
while (s < e) {
|
||||||
|
const next = Math.min(s + 30, e)
|
||||||
|
res.push(`${minutesToTime(s)}~${minutesToTime(next)}`)
|
||||||
|
s = next
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* split time range
|
||||||
|
* @param {*} mergeData Array<{ weekdays: number[], start_time: 'HH:mm', end_time: 'HH:mm' }>
|
||||||
|
* @returns Array<{ day: number, value: Array<'HH:mm'> }>
|
||||||
|
*/
|
||||||
|
export function splitTimeRange(timeRanges) {
|
||||||
|
const weekMap = {}
|
||||||
|
|
||||||
|
timeRanges.forEach(item => {
|
||||||
|
item.weekdays.forEach(weekNum => {
|
||||||
|
if (!weekMap[weekNum]) weekMap[weekNum] = []
|
||||||
|
weekMap[weekNum].push(...splitHalfHour(item.start_time, item.end_time))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove duplicates and sort
|
||||||
|
return Object.keys(weekMap)
|
||||||
|
.sort((a, b) => a - b)
|
||||||
|
.map(weekNum => {
|
||||||
|
// Remove duplicates
|
||||||
|
const valueSet = new Set(weekMap[weekNum])
|
||||||
|
// Sort
|
||||||
|
const value = Array.from(valueSet).sort((a, b) => {
|
||||||
|
const [aStart] = a.split('~')
|
||||||
|
const [bStart] = b.split('~')
|
||||||
|
return timeToMinutes(aStart) - timeToMinutes(bStart)
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
day: Number(weekNum),
|
||||||
|
value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="title"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
:width="1000"
|
||||||
|
:bodyStyle="{
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="timeTemplateFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :label="$t('name')" prop="name">
|
||||||
|
<a-input v-model="form.name" :placeholder="$t(`placeholder1`)" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('description')" prop="description">
|
||||||
|
<a-input v-model="form.description" :placeholder="$t(`placeholder1`)" />
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.timeTemplate.category')" prop="category">
|
||||||
|
<a-select
|
||||||
|
v-model="form.category"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="categorySelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.timeTemplate.timeZone')" prop="timezone">
|
||||||
|
<a-select
|
||||||
|
v-model="form.timezone"
|
||||||
|
showSearch
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="timezoneSelectOptions"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t('oneterm.timeTemplate.timeRange')" prop="time_ranges">
|
||||||
|
<DragWeekTime
|
||||||
|
v-model="form.time_ranges"
|
||||||
|
:data="weekTimeData"
|
||||||
|
@onClear="clearWeektime"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import momentTimezone from 'moment-timezone'
|
||||||
|
import { TIME_TEMPLATE_CATEGORY, TIME_TEMPLATE_CATEGORY_NAME } from './constants.js'
|
||||||
|
import { postTimeTemplate, putTimeTemplateById } from '@/modules/oneterm/api/timeTemplate.js'
|
||||||
|
import { mergeTimeRange } from './mergeTimeRange.js'
|
||||||
|
import { splitTimeRange } from './splitTimeRange.js'
|
||||||
|
|
||||||
|
import DragWeekTime from '@/modules/oneterm/components/dragWeektime'
|
||||||
|
import weekTimeData from '@/modules/oneterm/components/dragWeektime/weektimeData'
|
||||||
|
|
||||||
|
const DEFAULT_FORM = {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
category: TIME_TEMPLATE_CATEGORY.WORK,
|
||||||
|
timezone: momentTimezone.tz.guess(),
|
||||||
|
time_ranges: [],
|
||||||
|
is_active: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TimeTemplateModal',
|
||||||
|
components: {
|
||||||
|
DragWeekTime
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
timeTemplateId: '',
|
||||||
|
form: { ...DEFAULT_FORM },
|
||||||
|
rules: {
|
||||||
|
name: [{ required: true, message: this.$t(`placeholder1`) }],
|
||||||
|
timezone: [{ required: true, message: this.$t(`placeholder1`) }],
|
||||||
|
time_ranges: [{ required: true, message: this.$t(`placeholder2`) }],
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
weekTimeData: _.cloneDeep(weekTimeData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.timeTemplateId) {
|
||||||
|
return this.$t('oneterm.timeTemplate.editTimeTemplate')
|
||||||
|
}
|
||||||
|
return this.$t('oneterm.timeTemplate.createTimeTemplate')
|
||||||
|
},
|
||||||
|
categorySelectOptions() {
|
||||||
|
return Object.values(TIME_TEMPLATE_CATEGORY).map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.$t(TIME_TEMPLATE_CATEGORY_NAME[value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
timezoneSelectOptions() {
|
||||||
|
const names = momentTimezone.tz.names()
|
||||||
|
return names.map((value) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
let time_ranges = []
|
||||||
|
if (data?.time_ranges?.length) {
|
||||||
|
const timeRanges = splitTimeRange(data.time_ranges)
|
||||||
|
time_ranges = timeRanges.map((item) => {
|
||||||
|
const childData = this.weekTimeData?.[item?.day - 1]?.child
|
||||||
|
if (childData?.length) {
|
||||||
|
childData.forEach((t) => {
|
||||||
|
this.$set(t, 'check', Boolean(item?.value?.length) && item.value.includes(t.value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.day,
|
||||||
|
day: item.day,
|
||||||
|
value: item.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.form = {
|
||||||
|
name: data?.name ?? '',
|
||||||
|
description: data?.description ?? '',
|
||||||
|
category: data?.category ?? TIME_TEMPLATE_CATEGORY.WORK,
|
||||||
|
timezone: data?.timezone ?? momentTimezone.tz.guess(),
|
||||||
|
is_active: Boolean(data.is_active),
|
||||||
|
time_ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeTemplateId = data?.id ?? ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearWeektime() {
|
||||||
|
this.weekTimeData.forEach((item) => {
|
||||||
|
item.child.forEach((t) => {
|
||||||
|
this.$set(t, 'check', false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.form.time_ranges = []
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.form = { ...DEFAULT_FORM }
|
||||||
|
this.clearWeektime()
|
||||||
|
this.timeTemplateId = ''
|
||||||
|
|
||||||
|
this.$refs.timeTemplateFormRef.resetFields()
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
async handleOk() {
|
||||||
|
this.$refs.timeTemplateFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.confirmLoading = true
|
||||||
|
try {
|
||||||
|
let time_ranges = []
|
||||||
|
if (this?.form?.time_ranges?.length) {
|
||||||
|
time_ranges = mergeTimeRange(this.form.time_ranges)
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
...this.form,
|
||||||
|
time_ranges
|
||||||
|
}
|
||||||
|
if (this.timeTemplateId) {
|
||||||
|
await putTimeTemplateById(this.timeTemplateId, params)
|
||||||
|
this.$message.success(this.$t('editSuccess'))
|
||||||
|
} else {
|
||||||
|
await postTimeTemplate(params)
|
||||||
|
this.$message.success(this.$t('createSuccess'))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('submit')
|
||||||
|
this.handleCancel()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('submit error:', e)
|
||||||
|
} finally {
|
||||||
|
this.confirmLoading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="oneterm-layout">
|
<div class="oneterm-layout">
|
||||||
<div class="oneterm-header">{{ $t('oneterm.menu.accounts') }}</div>
|
<div class="oneterm-header">{{ $t('oneterm.menu.accountManagement') }}</div>
|
||||||
<a-spin :tip="loadTip" :spinning="loading">
|
<a-spin :tip="loadTip" :spinning="loading">
|
||||||
<div class="oneterm-layout-container">
|
<div class="oneterm-layout-container">
|
||||||
<div class="oneterm-layout-container-header">
|
<div class="oneterm-layout-container-header">
|
||||||
|
@@ -1,160 +1,162 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-form-model ref="form" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
|
<a-form-model ref="form" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
|
||||||
<a-form-model-item>
|
<a-form-model-item :label="$t('oneterm.assetList.time')">
|
||||||
<span slot="label">
|
<DragWeektime v-model="form.ranges" :data="weekTimeData" @onClear="clearWeektime" />
|
||||||
<a-tooltip placement="right" :title="$t('oneterm.assetList.timeTip')">
|
|
||||||
<a><a-icon type="question-circle"/></a>
|
|
||||||
</a-tooltip>
|
|
||||||
{{ $t(`oneterm.assetList.time`) }}
|
|
||||||
</span>
|
|
||||||
<a-radio-group v-model="form.allow" style="display:block;margin:8px 0;">
|
|
||||||
<a-radio :value="true">
|
|
||||||
{{ $t('oneterm.assetList.allowAccess') }}
|
|
||||||
</a-radio>
|
|
||||||
<a-radio :value="false">
|
|
||||||
{{ $t('oneterm.assetList.prohibitAccess') }}
|
|
||||||
</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
<DragWeektime v-model="form.ranges" :data="weektimeData" @onClear="clearWeektime" />
|
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item :label="$t(`oneterm.assetList.effectiveDate`)" prop="startAndEnd">
|
<a-form-model-item :wrapper-col="{ span: 16 }" :label="$t('oneterm.timeTemplate.timeZone')" prop="timezone">
|
||||||
<a-range-picker v-model="form.startAndEnd" />
|
<a-select
|
||||||
|
v-model="form.timezone"
|
||||||
|
showSearch
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:options="timezoneSelectOptions"
|
||||||
|
/>
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item :label="$t(`oneterm.assetList.commandIntercept`)" prop="cmd_ids">
|
<a-form-model-item :label="$t(`oneterm.assetList.commandIntercept`)" prop="cmd_ids">
|
||||||
<treeselect
|
<a-select
|
||||||
class="custom-treeselect custom-treeselect-white"
|
|
||||||
:style="{
|
|
||||||
'--custom-height': '32px',
|
|
||||||
lineHeight: '32px',
|
|
||||||
'--custom-multiple-lineHeight': '18px',
|
|
||||||
}"
|
|
||||||
v-model="form.cmd_ids"
|
v-model="form.cmd_ids"
|
||||||
:multiple="true"
|
mode="multiple"
|
||||||
:clearable="true"
|
:options="commandSelectOptions"
|
||||||
searchable
|
:placeholder="$t('oneterm.auth.commandTip1')"
|
||||||
:options="cmdList"
|
/>
|
||||||
:placeholder="`${$t(`placeholder2`)}`"
|
<a-select
|
||||||
:normalizer="
|
mode="multiple"
|
||||||
(node) => {
|
v-model="form.template_ids"
|
||||||
return {
|
:options="commandTemplateSelectOptions"
|
||||||
id: node.id,
|
:placeholder="$t('oneterm.auth.commandTip2')"
|
||||||
label: node.name,
|
/>
|
||||||
}
|
|
||||||
}
|
|
||||||
"
|
|
||||||
appendToBody
|
|
||||||
:z-index="1056"
|
|
||||||
>
|
|
||||||
</treeselect>
|
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment'
|
import _ from 'lodash'
|
||||||
import { getCommandList } from '../../../api/command'
|
import momentTimezone from 'moment-timezone'
|
||||||
import DragWeektime from '../../../components/dragWeektime'
|
import { getCommandList } from '@/modules/oneterm/api/command'
|
||||||
import weektimeData from '../../../components/dragWeektime/weektimeData'
|
import { getCommandTemplateList } from '@/modules/oneterm/api/commandTemplate.js'
|
||||||
|
import { mergeTimeRange } from '@/modules/oneterm/views/access/time/mergeTimeRange.js'
|
||||||
|
import { splitTimeRange } from '@/modules/oneterm/views/access/time/splitTimeRange.js'
|
||||||
|
|
||||||
|
import DragWeektime from '@/modules/oneterm/components/dragWeektime'
|
||||||
|
import weekTimeData from '@/modules/oneterm/components/dragWeektime/weektimeData'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AccessAuth',
|
name: 'AccessAuth',
|
||||||
components: { DragWeektime },
|
components: { DragWeektime },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
weektimeData,
|
weekTimeData: _.cloneDeep(weekTimeData),
|
||||||
weekMap: {
|
|
||||||
0: '一',
|
|
||||||
1: '二',
|
|
||||||
2: '三',
|
|
||||||
3: '四',
|
|
||||||
4: '五',
|
|
||||||
5: '六',
|
|
||||||
6: '七',
|
|
||||||
},
|
|
||||||
form: {
|
form: {
|
||||||
cmd_ids: undefined,
|
|
||||||
startAndEnd: [],
|
|
||||||
ranges: [],
|
ranges: [],
|
||||||
allow: true,
|
timezone: momentTimezone.tz.guess(),
|
||||||
|
cmd_ids: undefined,
|
||||||
|
template_ids: undefined
|
||||||
},
|
},
|
||||||
rules: {},
|
rules: {},
|
||||||
allRoleList: [],
|
commandSelectOptions: [],
|
||||||
roleList: [],
|
commandTemplateSelectOptions: [],
|
||||||
cmdList: [],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
computed: {
|
||||||
getCommandList({ page_index: 1 }).then((res) => {
|
timezoneSelectOptions() {
|
||||||
this.cmdList = res?.data?.list || []
|
const names = momentTimezone.tz.names()
|
||||||
})
|
return names.map((value) => {
|
||||||
this.form.ranges = this.weektimeData.map((item) => {
|
|
||||||
return {
|
return {
|
||||||
id: item.row,
|
value,
|
||||||
week: item.week,
|
label: value
|
||||||
value: [],
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getCommandList()
|
||||||
|
this.getCommandTemplateList()
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.clearWeektime()
|
this.clearWeektime()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getCommandList() {
|
||||||
|
getCommandList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.commandSelectOptions = list.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getCommandTemplateList() {
|
||||||
|
getCommandTemplateList({
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 9999
|
||||||
|
}).then((res) => {
|
||||||
|
const list = res?.data?.list || []
|
||||||
|
this.commandTemplateSelectOptions = list.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
},
|
||||||
clearWeektime() {
|
clearWeektime() {
|
||||||
this.weektimeData.forEach((item) => {
|
this.weekTimeData.forEach((item) => {
|
||||||
item.child.forEach((t) => {
|
item.child.forEach((t) => {
|
||||||
this.$set(t, 'check', false)
|
this.$set(t, 'check', false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.form.ranges = this.weektimeData.map((item) => {
|
this.form.ranges = []
|
||||||
return {
|
|
||||||
id: item.row,
|
|
||||||
week: item.week,
|
|
||||||
value: [],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
getValues() {
|
getValues() {
|
||||||
const { cmd_ids, startAndEnd, ranges, allow } = this.form
|
const { ranges, timezone, cmd_ids, template_ids } = this.form
|
||||||
|
let time_ranges = []
|
||||||
|
if (ranges.length) {
|
||||||
|
time_ranges = mergeTimeRange(ranges)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cmd_ids,
|
cmd_ids,
|
||||||
start: startAndEnd[0]
|
template_ids,
|
||||||
? moment(startAndEnd[0])
|
time_ranges,
|
||||||
.startOf('day')
|
timezone
|
||||||
.format()
|
|
||||||
: null,
|
|
||||||
end: startAndEnd[1]
|
|
||||||
? moment(startAndEnd[1])
|
|
||||||
.endOf('day')
|
|
||||||
.format()
|
|
||||||
: null,
|
|
||||||
ranges: ranges.map((r) => ({
|
|
||||||
week: r.id,
|
|
||||||
times: r.value,
|
|
||||||
})),
|
|
||||||
allow,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async setValues(access_auth) {
|
async setValues({
|
||||||
const { cmd_ids = undefined, start, end, ranges = [], allow = true } = access_auth
|
access_time_control,
|
||||||
|
asset_command_control
|
||||||
|
}) {
|
||||||
|
const { time_ranges = [], timezone = momentTimezone.tz.guess() } = access_time_control
|
||||||
|
const { cmd_ids = undefined, template_ids = undefined } = asset_command_control
|
||||||
|
|
||||||
|
let ranges = []
|
||||||
|
if (time_ranges?.length) {
|
||||||
|
const timeRanges = splitTimeRange(time_ranges)
|
||||||
|
ranges = timeRanges.map((item) => {
|
||||||
|
const childData = this.weekTimeData?.[item?.day - 1]?.child
|
||||||
|
if (childData?.length) {
|
||||||
|
childData.forEach((t) => {
|
||||||
|
this.$set(t, 'check', Boolean(item?.value?.length) && item.value.includes(t.value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.day,
|
||||||
|
day: item.day,
|
||||||
|
value: item.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.form = {
|
this.form = {
|
||||||
cmd_ids,
|
cmd_ids,
|
||||||
allow,
|
template_ids,
|
||||||
startAndEnd: [start ? moment(start) : null, end ? moment(end) : null],
|
ranges,
|
||||||
ranges: ranges.map((r, index) => {
|
timezone
|
||||||
this.weektimeData[index].child.forEach((t) => {
|
|
||||||
this.$set(t, 'check', !!r.times && r.times.includes(t.value))
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
id: index,
|
|
||||||
week: `星期${this.weekMap[index]}`,
|
|
||||||
value: r.times,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
if (!ranges.length) {
|
if (!ranges.length) {
|
||||||
this.clearWeektime()
|
this.clearWeektime()
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-row>
|
<a-row class="form-account">
|
||||||
<a-col v-bind="colSpan">
|
<a-col v-bind="colSpan">
|
||||||
<table class="account-table">
|
<vxe-table
|
||||||
<tr>
|
ref="xTable"
|
||||||
<th>{{ $t(`oneterm.name`) }}</th>
|
size="mini"
|
||||||
<th>{{ $t(`oneterm.account`) }}</th>
|
:data="authList"
|
||||||
<th>{{ $t(`oneterm.assetList.grantUser`) }}</th>
|
:column-config="{ width: 200 }"
|
||||||
<th>{{ $t(`operation`) }}</th>
|
:min-height="110"
|
||||||
</tr>
|
>
|
||||||
<tr v-for="(item, index) in countList" :key="item.id">
|
<vxe-column field="account" :title="$t('oneterm.account')" width="190">
|
||||||
<td>
|
<template #default="{ row }">
|
||||||
<a-select
|
<a-select
|
||||||
v-model="item.name"
|
v-model="row.account"
|
||||||
showSearch
|
showSearch
|
||||||
:style="{
|
:style="{
|
||||||
width: '180px',
|
width: '180px',
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
:placeholder="$t('placeholder2')"
|
:placeholder="$t('placeholder2')"
|
||||||
optionFilterProp="title"
|
optionFilterProp="title"
|
||||||
allowClear
|
allowClear
|
||||||
@change="(value) => selectAccount(value, index)"
|
|
||||||
>
|
>
|
||||||
<a-select-option
|
<a-select-option
|
||||||
v-for="(node, nodeIndex) in accountList"
|
v-for="(node, nodeIndex) in accountList"
|
||||||
@@ -27,35 +26,18 @@
|
|||||||
:value="node.id"
|
:value="node.id"
|
||||||
:title="node.name"
|
:title="node.name"
|
||||||
>
|
>
|
||||||
|
<a-tooltip :title="node.toolTip">
|
||||||
{{ node.name }}
|
{{ node.name }}
|
||||||
|
<span v-if="node.account" class="select-option-name">({{ node.account }})</span>
|
||||||
|
</a-tooltip>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
</vxe-column>
|
||||||
<a-select
|
<vxe-column field="grantUser" :title="$t('oneterm.assetList.grantRole')" width="190">
|
||||||
v-model="item.account"
|
<template #default="{ row }">
|
||||||
:style="{
|
|
||||||
width: '180px',
|
|
||||||
}"
|
|
||||||
showSearch
|
|
||||||
:placeholder="$t('placeholder2')"
|
|
||||||
optionFilterProp="title"
|
|
||||||
allowClear
|
|
||||||
@change="(value) => selectAccount(value, index)"
|
|
||||||
>
|
|
||||||
<a-select-option
|
|
||||||
v-for="(node, nodeIndex) in accountList"
|
|
||||||
:key="node.id + nodeIndex"
|
|
||||||
:value="node.id"
|
|
||||||
:title="node.account"
|
|
||||||
>
|
|
||||||
{{ node.account }}
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<EmployeeTreeSelect
|
<EmployeeTreeSelect
|
||||||
v-model="item.rids"
|
v-model="row.rids"
|
||||||
multiple
|
multiple
|
||||||
:idType="2"
|
:idType="2"
|
||||||
departmentKey="acl_rid"
|
departmentKey="acl_rid"
|
||||||
@@ -70,28 +52,50 @@
|
|||||||
:limit="1"
|
:limit="1"
|
||||||
:otherOptions="visualRoleList"
|
:otherOptions="visualRoleList"
|
||||||
/>
|
/>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
</vxe-column>
|
||||||
<a-space :style="{ width: '60px' }">
|
<vxe-column field="permissions" :title="$t('oneterm.assetList.operationPermissions')" width="230">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<PermissionCheckbox
|
||||||
|
:value="row.permissions"
|
||||||
|
@change="(key, checked) => row.permissions[key] = checked"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="operation" :title="$t('operation')" width="55" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<a-space>
|
||||||
<a @click="addCount"><a-icon type="plus-circle"/></a>
|
<a @click="addCount"><a-icon type="plus-circle"/></a>
|
||||||
<a v-if="countList && countList.length > 1" @click="deleteCount(index)"><a-icon type="minus-circle"/></a>
|
<a v-if="authList && authList.length > 1" @click="deleteCount(row.id)"><a-icon type="minus-circle"/></a>
|
||||||
</a-space>
|
</a-space>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
</vxe-column>
|
||||||
</table>
|
</vxe-table>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { getAccountList } from '../../../api/account'
|
import { getAccountList } from '@/modules/oneterm/api/account'
|
||||||
import { searchRole } from '@/modules/acl/api/role'
|
import { searchRole } from '@/modules/acl/api/role'
|
||||||
|
import { getConfig } from '@/modules/oneterm/api/config'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
|
||||||
|
import PermissionCheckbox from '@/modules/oneterm/views/systemSettings/accessControl/permissionCheckbox.vue'
|
||||||
|
|
||||||
|
const DEFAULT_PERMISSIONS = Object.values(PERMISSION_TYPE).reduce((config, key) => {
|
||||||
|
config[key] = false
|
||||||
|
return config
|
||||||
|
}, {})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Account',
|
name: 'Account',
|
||||||
components: { EmployeeTreeSelect },
|
components: {
|
||||||
|
EmployeeTreeSelect,
|
||||||
|
PermissionCheckbox
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
colSpan: {
|
colSpan: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -104,17 +108,36 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
accountList: [],
|
accountList: [],
|
||||||
countList: [{ id: uuidv4(), name: undefined, account: undefined, rids: undefined }],
|
authList: [{
|
||||||
|
id: uuidv4(),
|
||||||
|
account: undefined,
|
||||||
|
rids: undefined,
|
||||||
|
permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
|
}],
|
||||||
|
visualRoleList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadRoles()
|
this.initDefaultPermissions()
|
||||||
getAccountList({ page_index: 1 }).then((res) => {
|
this.getRoleList()
|
||||||
this.accountList = res?.data?.list || []
|
this.getAccountList()
|
||||||
})
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadRoles() {
|
initDefaultPermissions() {
|
||||||
|
getConfig({
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
const default_permissions = res?.data?.default_permissions
|
||||||
|
Object.keys(DEFAULT_PERMISSIONS).forEach((key) => {
|
||||||
|
DEFAULT_PERMISSIONS[key] = default_permissions?.[key] ?? DEFAULT_PERMISSIONS[key]
|
||||||
|
})
|
||||||
|
this.authList.forEach((item) => {
|
||||||
|
item.permissions = { ...DEFAULT_PERMISSIONS }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRoleList() {
|
||||||
const res = await searchRole({ page_size: 9999, page: 1, app_id: 'oneterm', user_role: 0, user_only: 0, is_all: true })
|
const res = await searchRole({ page_size: 9999, page: 1, app_id: 'oneterm', user_role: 0, user_only: 0, is_all: true })
|
||||||
|
|
||||||
const visualRoleList = []
|
const visualRoleList = []
|
||||||
@@ -137,35 +160,63 @@ export default {
|
|||||||
this.$set(this, 'visualRoleList', visualRoleList)
|
this.$set(this, 'visualRoleList', visualRoleList)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getAccountList() {
|
||||||
|
const res = await getAccountList({ page_index: 1 })
|
||||||
|
const accountList = res?.data?.list || []
|
||||||
|
accountList.forEach((item) => {
|
||||||
|
item.toolTip = item.name + (item.account ? '(item.account)' : '')
|
||||||
|
})
|
||||||
|
this.accountList = accountList
|
||||||
|
},
|
||||||
|
|
||||||
addCount() {
|
addCount() {
|
||||||
this.countList.push({ id: uuidv4(), name: undefined, account: undefined, rids: undefined })
|
this.authList.push({
|
||||||
},
|
id: uuidv4(),
|
||||||
deleteCount(index) {
|
account: undefined,
|
||||||
this.countList.splice(index, 1)
|
rids: undefined,
|
||||||
},
|
permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
selectAccount(id, index) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$set(this.countList[index], 'name', id)
|
|
||||||
this.$set(this.countList[index], 'account', id)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
deleteCount(id) {
|
||||||
|
const index = this.authList.findIndex((item) => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.authList.splice(index, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getValues() {
|
getValues() {
|
||||||
const authorization = {}
|
const authorization = {}
|
||||||
this.countList
|
|
||||||
.filter((count) => count.name)
|
this.authList
|
||||||
.forEach((count) => {
|
.filter((auth) => auth.account)
|
||||||
authorization[count.name] = count?.rids?.length ? count.rids.map((r) => Number(r.split('-')[1])) : []
|
.forEach((auth) => {
|
||||||
|
const rids = (auth?.rids || []).map((r) => Number(r.split('-')[1]))
|
||||||
|
authorization[auth.account] = {
|
||||||
|
rids,
|
||||||
|
permissions: auth.permissions
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return { authorization }
|
return { authorization }
|
||||||
},
|
},
|
||||||
|
|
||||||
setValues({ authorization = {} }) {
|
setValues({ authorization = {} }) {
|
||||||
const authorizationList = Object.entries(authorization)
|
const authorizationList = Object.entries(authorization || {})
|
||||||
if (authorizationList.length) {
|
if (authorizationList.length) {
|
||||||
this.countList = authorizationList.map(([acc, rids]) => {
|
this.authList = authorizationList.map(([key, value]) => {
|
||||||
return { id: uuidv4(), name: Number(acc), account: Number(acc), rids: rids.map((r) => `employee-${r}`) }
|
return {
|
||||||
|
id: uuidv4(),
|
||||||
|
account: Number(key),
|
||||||
|
rids: (value?.rids || []).map((r) => `employee-${r}`),
|
||||||
|
permissions: value.permissions
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.countList = [{ id: uuidv4(), name: undefined, account: undefined, rids: undefined }]
|
this.authList = [{
|
||||||
|
id: uuidv4(),
|
||||||
|
account: undefined,
|
||||||
|
rids: undefined,
|
||||||
|
permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -173,16 +224,14 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.account-table {
|
.form-account {
|
||||||
border-collapse: collapse;
|
/deep/ .ant-checkbox-wrapper {
|
||||||
border: 1px solid #e4e7ed;
|
margin-right: 0px;
|
||||||
border-spacing: 20px;
|
width: 48%;
|
||||||
th {
|
|
||||||
background-color: #f0f5ff;
|
|
||||||
}
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
padding: 5px 8px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.select-option-name {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #A5A9BC;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -43,21 +43,21 @@
|
|||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item key="1" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', { parent_id: node.dataRef.id })">
|
<a-menu-item key="1" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', { parent_id: node.dataRef.id })">
|
||||||
<a-icon type="plus-circle" />
|
<a-icon type="plus-circle" />
|
||||||
{{ $t(`oneterm.assetList.createSubCatalog`) }}
|
{{ $t(`oneterm.assetList.createSubFolder`) }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="2" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', node.dataRef)">
|
<a-menu-item key="2" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', node.dataRef)">
|
||||||
<ops-icon type="icon-xianxing-edit" />
|
<ops-icon type="icon-xianxing-edit" />
|
||||||
{{ $t(`oneterm.assetList.editCatalog`) }}
|
{{ $t(`oneterm.assetList.editFolder`) }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="3" v-if="showNodeOperation(node.dataRef, ['delete'])" @click="deleteNode(node.dataRef)">
|
<a-menu-item key="3" v-if="showNodeOperation(node.dataRef, ['delete'])" @click="deleteNode(node.dataRef)">
|
||||||
<ops-icon type="veops-delete" />
|
<ops-icon type="veops-delete" />
|
||||||
{{ $t(`oneterm.assetList.deleteCatalog`) }}
|
{{ $t(`oneterm.assetList.deleteFolder`) }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<template v-if="showNodeOperation(node.dataRef, ['grant'])">
|
<template v-if="showNodeOperation(node.dataRef, ['grant'])">
|
||||||
<a-divider style="margin: 4px 0" />
|
<a-divider style="margin: 4px 0" />
|
||||||
<a-menu-item key="4" @click="openGrantModal(node.dataRef)">
|
<a-menu-item key="4" @click="openGrantModal(node.dataRef)">
|
||||||
<a-icon type="user-add" />
|
<a-icon type="user-add" />
|
||||||
{{ $t(`oneterm.assetList.grantCatalog`) }}
|
{{ $t(`oneterm.assetList.grantFolder`) }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</template>
|
</template>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<vxe-column type="checkbox" width="60px"></vxe-column>
|
<vxe-column type="checkbox" width="60px"></vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.name`)" field="name"></vxe-column>
|
<vxe-column :title="$t(`oneterm.name`)" field="name"></vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.assetList.ip`)" field="ip"> </vxe-column>
|
<vxe-column :title="$t(`oneterm.assetList.ip`)" field="ip"> </vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.assetList.catalogName`)" field="node_chain"> </vxe-column>
|
<vxe-column :title="$t(`oneterm.assetList.folderName`)" field="node_chain"> </vxe-column>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
:title="$t(`status`)"
|
:title="$t(`status`)"
|
||||||
field="connectable"
|
field="connectable"
|
||||||
@@ -242,7 +242,7 @@ export default {
|
|||||||
searchValue: '',
|
searchValue: '',
|
||||||
|
|
||||||
getRequestParams: {
|
getRequestParams: {
|
||||||
info: false
|
info: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -544,7 +544,7 @@ export default {
|
|||||||
authAsset() {
|
authAsset() {
|
||||||
const assetIds = this.selectedRowKeys.map((item) => item.id)
|
const assetIds = this.selectedRowKeys.map((item) => item.id)
|
||||||
this.$refs.grantModalRef.open({
|
this.$refs.grantModalRef.open({
|
||||||
resourceId: this.selectedRowKeys?.[0]?.resource_id || '',
|
resourceId: this.selectedRowKeys?.[0]?.resource_id ?? '',
|
||||||
type: 'asset',
|
type: 'asset',
|
||||||
ids: assetIds
|
ids: assetIds
|
||||||
})
|
})
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
<a-form-model-item :label="$t('oneterm.assetList.ip')" prop="ip">
|
<a-form-model-item :label="$t('oneterm.assetList.ip')" prop="ip">
|
||||||
<a-input v-model="baseForm.ip" :placeholder="`${$t(`placeholder1`)}`" />
|
<a-input v-model="baseForm.ip" :placeholder="`${$t(`placeholder1`)}`" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item :label="$t(`oneterm.catalog`)" prop="parent_id">
|
<a-form-model-item :label="$t(`oneterm.folder`)" prop="parent_id">
|
||||||
<treeselect
|
<treeselect
|
||||||
class="custom-treeselect custom-treeselect-white"
|
class="custom-treeselect custom-treeselect-white"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -90,8 +90,8 @@
|
|||||||
import Protocol from './protocol.vue'
|
import Protocol from './protocol.vue'
|
||||||
import Account from './account.vue'
|
import Account from './account.vue'
|
||||||
import AccessAuth from './accessAuth.vue'
|
import AccessAuth from './accessAuth.vue'
|
||||||
import { getNodeList } from '../../../api/node'
|
import { getNodeList } from '@/modules/oneterm/api/node'
|
||||||
import { postAsset, putAssetById } from '../../../api/asset'
|
import { postAsset, putAssetById } from '@/modules/oneterm/api/asset'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateAsset',
|
name: 'CreateAsset',
|
||||||
@@ -110,6 +110,7 @@ export default {
|
|||||||
},
|
},
|
||||||
baseRules: {
|
baseRules: {
|
||||||
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
||||||
|
ip: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
||||||
parent_id: [{ required: true, message: `${this.$t(`placeholder2`)}` }],
|
parent_id: [{ required: true, message: `${this.$t(`placeholder2`)}` }],
|
||||||
},
|
},
|
||||||
nodeList: [],
|
nodeList: [],
|
||||||
@@ -141,7 +142,8 @@ export default {
|
|||||||
gateway_id = undefined,
|
gateway_id = undefined,
|
||||||
protocols = [],
|
protocols = [],
|
||||||
authorization = {},
|
authorization = {},
|
||||||
access_auth = {},
|
access_time_control = {},
|
||||||
|
asset_command_control = {}
|
||||||
} = asset ?? {}
|
} = asset ?? {}
|
||||||
this.assetId = id
|
this.assetId = id
|
||||||
this.baseForm = {
|
this.baseForm = {
|
||||||
@@ -152,7 +154,10 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$refs.protocol.setValues({ gateway_id, protocols })
|
this.$refs.protocol.setValues({ gateway_id, protocols })
|
||||||
this.$refs.account.setValues({ authorization })
|
this.$refs.account.setValues({ authorization })
|
||||||
this.$refs.accessAuth.setValues(access_auth)
|
this.$refs.accessAuth.setValues({
|
||||||
|
access_time_control,
|
||||||
|
asset_command_control
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -181,7 +186,7 @@ export default {
|
|||||||
const { name, ip, parent_id, comment } = this.baseForm
|
const { name, ip, parent_id, comment } = this.baseForm
|
||||||
const { gateway_id, protocols } = this.$refs.protocol.getValues()
|
const { gateway_id, protocols } = this.$refs.protocol.getValues()
|
||||||
const { authorization } = this.$refs.account.getValues()
|
const { authorization } = this.$refs.account.getValues()
|
||||||
const access_auth = this.$refs.accessAuth.getValues()
|
const { cmd_ids, template_ids, time_ranges, timezone } = this.$refs.accessAuth.getValues()
|
||||||
const params = {
|
const params = {
|
||||||
name,
|
name,
|
||||||
ip: ip?.trim?.() ?? '',
|
ip: ip?.trim?.() ?? '',
|
||||||
@@ -190,8 +195,16 @@ export default {
|
|||||||
protocols,
|
protocols,
|
||||||
gateway_id,
|
gateway_id,
|
||||||
authorization,
|
authorization,
|
||||||
access_auth,
|
access_time_control: {
|
||||||
|
time_ranges,
|
||||||
|
timezone
|
||||||
|
},
|
||||||
|
asset_command_control: {
|
||||||
|
cmd_ids,
|
||||||
|
template_ids
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
if (this.assetId) {
|
if (this.assetId) {
|
||||||
putAssetById(this.assetId, { ...params, id: this.assetId })
|
putAssetById(this.assetId, { ...params, id: this.assetId })
|
||||||
|
@@ -16,10 +16,10 @@
|
|||||||
:label-col="{ span: 5 }"
|
:label-col="{ span: 5 }"
|
||||||
:wrapper-col="{ span: 16 }"
|
:wrapper-col="{ span: 16 }"
|
||||||
>
|
>
|
||||||
<a-form-model-item :label="$t('oneterm.assetList.catalogName')" prop="name">
|
<a-form-model-item :label="$t('oneterm.assetList.folderName')" prop="name">
|
||||||
<a-input v-model="baseForm.name" :placeholder="`${$t(`placeholder1`)}`" />
|
<a-input v-model="baseForm.name" :placeholder="`${$t(`placeholder1`)}`" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item :label="$t(`oneterm.catalog`)" prop="parent_id">
|
<a-form-model-item :label="$t(`oneterm.folder`)" prop="parent_id">
|
||||||
<treeselect
|
<treeselect
|
||||||
class="custom-treeselect custom-treeselect-white"
|
class="custom-treeselect custom-treeselect-white"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -55,77 +55,6 @@
|
|||||||
<a-textarea v-model="baseForm.comment" :placeholder="`${$t(`placeholder1`)}`" />
|
<a-textarea v-model="baseForm.comment" :placeholder="`${$t(`placeholder1`)}`" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
<!-- <p>
|
|
||||||
<strong>{{ $t(`oneterm.assetList.cmdbSync`) }}</strong>
|
|
||||||
</p>
|
|
||||||
<a-form-model ref="syncForm" :model="syncForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
|
|
||||||
<a-form-model-item
|
|
||||||
:label="$t('oneterm.cmdbType')"
|
|
||||||
prop="type_id"
|
|
||||||
:style="{ display: 'flex', alignItems: 'center' }"
|
|
||||||
>
|
|
||||||
<CMDBTypeSelect v-model="syncForm.type_id" selectType="ci_type" @change="changeTypeId" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t('oneterm.fieldMap')">
|
|
||||||
<div v-for="item in fieldMap" :key="item.id">
|
|
||||||
<div class="cmdb-radio-slot-field">
|
|
||||||
<div class="slot-field1">
|
|
||||||
<span>*</span>
|
|
||||||
<a-input disabled size="small" :style="{ width: '200px' }" v-model="item['attribute'].label" />
|
|
||||||
</div>
|
|
||||||
<div class="slot-field2" :style="{ marginLeft: '150px' }">
|
|
||||||
<treeselect
|
|
||||||
class="custom-treeselect custom-treeselect-white"
|
|
||||||
:style="{
|
|
||||||
'--custom-height': '24px',
|
|
||||||
lineHeight: '24px',
|
|
||||||
width: '250px',
|
|
||||||
}"
|
|
||||||
v-model="item.field_name"
|
|
||||||
:multiple="false"
|
|
||||||
:clearable="true"
|
|
||||||
searchable
|
|
||||||
:options="attributes"
|
|
||||||
:placeholder="`${$t(`placeholder2`)}`"
|
|
||||||
:normalizer="
|
|
||||||
(node) => {
|
|
||||||
return {
|
|
||||||
id: node.name,
|
|
||||||
label: node.alias || node.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
:title="node.label"
|
|
||||||
slot="option-label"
|
|
||||||
slot-scope="{ node }"
|
|
||||||
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
|
||||||
>
|
|
||||||
{{ node.label }}
|
|
||||||
</div>
|
|
||||||
</treeselect>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span v-if="item.error" style="color: red">{{ `${$t(`placeholder2`)}` }}</span>
|
|
||||||
</div>
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t('oneterm.filter')" class="cmdb-value-filter">
|
|
||||||
<FilterComp
|
|
||||||
ref="filterComp"
|
|
||||||
:isDropdown="false"
|
|
||||||
:canSearchPreferenceAttrList="attributes"
|
|
||||||
@setExpFromFilter="setExpFromFilter"
|
|
||||||
:expression="filterExp ? `q=${filterExp}` : ''"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t('oneterm.assetList.sync')" prop="enable">
|
|
||||||
<a-switch v-model="syncForm.enable" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t('oneterm.assetList.frequency')" prop="frequency">
|
|
||||||
<a-input-number :min="0" v-model="syncForm.frequency" />{{ $t('hour') }}
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model> -->
|
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ $t(`oneterm.protocol`) }}</strong>
|
<strong>{{ $t(`oneterm.protocol`) }}</strong>
|
||||||
</p>
|
</p>
|
||||||
@@ -134,10 +63,6 @@
|
|||||||
<strong>{{ $t(`oneterm.accountAuthorization`) }}</strong>
|
<strong>{{ $t(`oneterm.accountAuthorization`) }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<Account ref="account" />
|
<Account ref="account" />
|
||||||
<p>
|
|
||||||
<strong>{{ $t(`oneterm.accessRestrictions`) }}</strong>
|
|
||||||
</p>
|
|
||||||
<AccessAuth ref="accessAuth" />
|
|
||||||
<div class="custom-drawer-bottom-action">
|
<div class="custom-drawer-bottom-action">
|
||||||
<a-button
|
<a-button
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@@ -154,17 +79,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CMDBTypeSelect from '../../../components/cmdbTypeSelect'
|
import { getNodeList, postNode, putNodeById } from '@/modules/oneterm/api/node'
|
||||||
import { getCITypeAttributesById } from '../../../api/otherModules'
|
|
||||||
import FilterComp from '@/components/CMDBFilterComp'
|
|
||||||
import Protocol from './protocol.vue'
|
import Protocol from './protocol.vue'
|
||||||
import Account from './account.vue'
|
import Account from './account.vue'
|
||||||
import AccessAuth from './accessAuth.vue'
|
|
||||||
import { getNodeList, postNode, putNodeById } from '../../../api/node'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateNode',
|
name: 'CreateNode',
|
||||||
components: { CMDBTypeSelect, FilterComp, Protocol, Account, AccessAuth },
|
components: {
|
||||||
|
Protocol,
|
||||||
|
Account
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -179,37 +104,15 @@ export default {
|
|||||||
baseRules: {
|
baseRules: {
|
||||||
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
||||||
},
|
},
|
||||||
syncForm: {
|
|
||||||
type_id: undefined,
|
|
||||||
enable: true,
|
|
||||||
frequency: undefined,
|
|
||||||
},
|
|
||||||
// fieldMap
|
|
||||||
fieldMap: [
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'name', label: this.$t('oneterm.name') },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'ip', label: this.$t('oneterm.assetList.ip') },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldMapObj: {
|
|
||||||
ip: 'IP',
|
|
||||||
name: this.$t('oneterm.name'),
|
|
||||||
},
|
|
||||||
attributes: [],
|
|
||||||
filterExp: '',
|
|
||||||
nodeList: [],
|
nodeList: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
if (this.type === 'create') {
|
if (this.type === 'create') {
|
||||||
return this.$t(`oneterm.assetList.createCatalog`)
|
return this.$t(`oneterm.assetList.createFolder`)
|
||||||
}
|
}
|
||||||
return this.$t(`oneterm.assetList.editCatalog`)
|
return this.$t(`oneterm.assetList.editFolder`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {},
|
mounted() {},
|
||||||
@@ -225,128 +128,42 @@ export default {
|
|||||||
getNodeList(params).then((res) => {
|
getNodeList(params).then((res) => {
|
||||||
this.nodeList = res?.data?.list || []
|
this.nodeList = res?.data?.list || []
|
||||||
})
|
})
|
||||||
console.log(node)
|
|
||||||
const {
|
const {
|
||||||
id = null,
|
id = null,
|
||||||
name = '',
|
name = '',
|
||||||
comment = '',
|
comment = '',
|
||||||
parent_id,
|
parent_id,
|
||||||
sync = {},
|
|
||||||
gateway_id = undefined,
|
gateway_id = undefined,
|
||||||
protocols = [],
|
protocols = [],
|
||||||
authorization = {},
|
authorization = {}
|
||||||
access_auth = {},
|
|
||||||
} = node ?? {}
|
} = node ?? {}
|
||||||
const { type_id = undefined, enable = true, frequency = undefined, filters = '', mapping = {} } = sync
|
|
||||||
this.nodeId = id
|
this.nodeId = id
|
||||||
this.baseForm = {
|
this.baseForm = {
|
||||||
name,
|
name,
|
||||||
parent_id: parent_id || undefined,
|
parent_id: parent_id || undefined,
|
||||||
comment,
|
comment,
|
||||||
}
|
}
|
||||||
this.syncForm = {
|
|
||||||
type_id,
|
|
||||||
enable,
|
|
||||||
frequency,
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.fieldMap =
|
|
||||||
JSON.stringify(mapping) === '{}'
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'name', label: this.$t('oneterm.name') },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'ip', label: this.$t('oneterm.assetList.ip') },
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: Object.keys(mapping).map((key) => {
|
|
||||||
return {
|
|
||||||
field_name: mapping[key],
|
|
||||||
attribute: { value: key, label: this.fieldMapObj[key] },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.filterExp = filters
|
|
||||||
// this.$nextTick(() => {
|
|
||||||
// this.$refs.filterComp.visibleChange(true, false)
|
|
||||||
// })
|
|
||||||
this.$refs.protocol.setValues({ gateway_id, protocols })
|
this.$refs.protocol.setValues({ gateway_id, protocols })
|
||||||
this.$refs.account.setValues({ authorization })
|
this.$refs.account.setValues({ authorization })
|
||||||
this.$refs.accessAuth.setValues(access_auth)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async changeTypeId(id) {
|
|
||||||
this.fieldMap = [
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'name', label: this.$t('oneterm.name') },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field_name: undefined,
|
|
||||||
attribute: { value: 'ip', label: this.$t('oneterm.assetList.ip') },
|
|
||||||
},
|
|
||||||
]
|
|
||||||
if (id) {
|
|
||||||
await getCITypeAttributesById(id).then((res) => {
|
|
||||||
const { attributes } = res
|
|
||||||
this.attributes = attributes
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.attributes = []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// setExpFromFilter(filterExp) {
|
|
||||||
// if (filterExp) {
|
|
||||||
// this.filterExp = `${filterExp}`
|
|
||||||
// } else {
|
|
||||||
// this.filterExp = ''
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
this.$refs.baseForm.validate((valid) => {
|
this.$refs.baseForm.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const { name, parent_id, comment } = this.baseForm
|
const { name, parent_id, comment } = this.baseForm
|
||||||
// const { type_id, enable, frequency } = this.syncForm
|
|
||||||
// this.$refs.filterComp.handleSubmit()
|
|
||||||
// const mapping = {}
|
|
||||||
// let flag = true
|
|
||||||
// this.fieldMap.forEach((field) => {
|
|
||||||
// if (!field.field_name) {
|
|
||||||
// this.$set(field, 'error', true)
|
|
||||||
// field.error = true
|
|
||||||
// flag = false
|
|
||||||
// } else {
|
|
||||||
// this.$set(field, 'error', false)
|
|
||||||
// field.error = false
|
|
||||||
// mapping[field.attribute.value] = field.field_name
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// if (type_id && !flag) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
const { gateway_id, protocols } = this.$refs.protocol.getValues()
|
const { gateway_id, protocols } = this.$refs.protocol.getValues()
|
||||||
const { authorization } = this.$refs.account.getValues()
|
const { authorization } = this.$refs.account.getValues()
|
||||||
const access_auth = this.$refs.accessAuth.getValues()
|
|
||||||
const params = {
|
const params = {
|
||||||
name,
|
name,
|
||||||
comment,
|
comment,
|
||||||
parent_id: parent_id ?? 0,
|
parent_id: parent_id ?? 0,
|
||||||
// sync: {
|
|
||||||
// enable,
|
|
||||||
// filters: this.filterExp,
|
|
||||||
// frequency,
|
|
||||||
// mapping,
|
|
||||||
// type_id,
|
|
||||||
// },
|
|
||||||
protocols,
|
protocols,
|
||||||
gateway_id,
|
gateway_id,
|
||||||
authorization,
|
authorization
|
||||||
access_auth,
|
|
||||||
}
|
}
|
||||||
console.log(params)
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
if (this.nodeId) {
|
if (this.nodeId) {
|
||||||
putNodeById(this.nodeId, { ...params, id: this.nodeId })
|
putNodeById(this.nodeId, { ...params, id: this.nodeId })
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<template #title>
|
<template #title>
|
||||||
<div class="asset-list-title">
|
<div class="asset-list-title">
|
||||||
<div class="asset-list-title-text">
|
<div class="asset-list-title-text">
|
||||||
{{ $t('oneterm.assetList.assetList') }}
|
{{ $t('oneterm.menu.assetManagement') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<a-icon type="plus"/>
|
<a-icon type="plus"/>
|
||||||
</span>
|
</span>
|
||||||
<span class="asset-list-title-create-text">
|
<span class="asset-list-title-create-text">
|
||||||
{{ $t(`oneterm.assetList.createCatalog`) }}
|
{{ $t(`oneterm.assetList.createFolder`) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,11 +52,6 @@ export default {
|
|||||||
allTreeDepAndEmp: [],
|
allTreeDepAndEmp: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
title() {
|
|
||||||
return this.$t(`oneterm.assetList.assetList`)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||||
this.allTreeDepAndEmp = res
|
this.allTreeDepAndEmp = res
|
||||||
|
@@ -181,9 +181,9 @@ export default {
|
|||||||
const _protocols = this.protocols.map((pro) => `${pro.value}:${pro.label}`)
|
const _protocols = this.protocols.map((pro) => `${pro.value}:${pro.label}`)
|
||||||
return { gateway_id, protocols: _protocols }
|
return { gateway_id, protocols: _protocols }
|
||||||
},
|
},
|
||||||
setValues({ gateway_id = undefined, protocols = [] }) {
|
setValues({ gateway_id = undefined, protocols }) {
|
||||||
this.form = { gateway_id: gateway_id || undefined }
|
this.form = { gateway_id: gateway_id || undefined }
|
||||||
this.protocols = protocols.length
|
this.protocols = protocols?.length
|
||||||
? protocols.map((p) => ({
|
? protocols.map((p) => ({
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
value: p.split(':')[0],
|
value: p.split(':')[0],
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="oneterm-layout">
|
<div class="oneterm-layout">
|
||||||
<div class="oneterm-header">
|
<div class="oneterm-header">
|
||||||
<a-space>
|
<a-space>
|
||||||
<span>{{ $t('oneterm.menu.gateways') }}</span>
|
<span>{{ $t('oneterm.menu.gatewayManagement') }}</span>
|
||||||
<a-tooltip placement="right" :title="$t('oneterm.assetList.gatewayTip')">
|
<a-tooltip placement="right" :title="$t('oneterm.assetList.gatewayTip')">
|
||||||
<a><a-icon type="question-circle"/></a>
|
<a><a-icon type="question-circle"/></a>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="file-management-title-right">
|
<div class="file-management-title-right">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="selectedRows.length"
|
v-if="selectedRows.length && showDownload"
|
||||||
:title="$t('oneterm.fileManagement.batchDownloadFiles')"
|
:title="$t('oneterm.fileManagement.batchDownloadFiles')"
|
||||||
>
|
>
|
||||||
<a-icon
|
<a-icon
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<UploadFile
|
<UploadFile
|
||||||
|
v-if="showUpload"
|
||||||
:sessionId="sessionId"
|
:sessionId="sessionId"
|
||||||
:pathStr="pathStr"
|
:pathStr="pathStr"
|
||||||
:connectType="connectType"
|
:connectType="connectType"
|
||||||
@@ -87,7 +88,11 @@
|
|||||||
@checkbox-all="onSelectChange"
|
@checkbox-all="onSelectChange"
|
||||||
@checkbox-range-end="onSelectChange"
|
@checkbox-range-end="onSelectChange"
|
||||||
>
|
>
|
||||||
<vxe-column type="checkbox" width="40px"></vxe-column>
|
<vxe-column
|
||||||
|
v-if="showDownload"
|
||||||
|
type="checkbox"
|
||||||
|
width="40px"
|
||||||
|
></vxe-column>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
:title="$t('name')"
|
:title="$t('name')"
|
||||||
field="name"
|
field="name"
|
||||||
@@ -144,6 +149,7 @@ import moment from 'moment'
|
|||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { getFileListBySessionId } from '@/modules/oneterm/api/file.js'
|
import { getFileListBySessionId } from '@/modules/oneterm/api/file.js'
|
||||||
import { getRDPFileList } from '@/modules/oneterm/api/rdp.js'
|
import { getRDPFileList } from '@/modules/oneterm/api/rdp.js'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
import UploadFile from './uploadFile.vue'
|
import UploadFile from './uploadFile.vue'
|
||||||
|
|
||||||
@@ -160,6 +166,10 @@ export default {
|
|||||||
connectType: {
|
connectType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'ssh' // 'ssh' | 'rdp'
|
default: 'ssh' // 'ssh' | 'rdp'
|
||||||
|
},
|
||||||
|
assetPermissions: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -179,6 +189,12 @@ export default {
|
|||||||
}),
|
}),
|
||||||
pathStr() {
|
pathStr() {
|
||||||
return `/${this.pathList.join('/')}`
|
return `/${this.pathList.join('/')}`
|
||||||
|
},
|
||||||
|
showDownload() {
|
||||||
|
return this.assetPermissions?.[PERMISSION_TYPE.FILE_DOWNLOAD] || false
|
||||||
|
},
|
||||||
|
showUpload() {
|
||||||
|
return this.assetPermissions?.[PERMISSION_TYPE.FILE_UPLOAD] || false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
ref="fileManagementDrawerRef"
|
ref="fileManagementDrawerRef"
|
||||||
connectType="rdp"
|
connectType="rdp"
|
||||||
:sessionId="sessionId"
|
:sessionId="sessionId"
|
||||||
|
:assetPermissions="assetPermissions"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -32,6 +33,7 @@ import _ from 'lodash'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import Guacamole from 'guacamole-common-js'
|
import Guacamole from 'guacamole-common-js'
|
||||||
import { pageBeforeUnload } from '@/modules/oneterm/utils/index.js'
|
import { pageBeforeUnload } from '@/modules/oneterm/utils/index.js'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
import ClipboardModal from './clipboardModal.vue'
|
import ClipboardModal from './clipboardModal.vue'
|
||||||
import ResolutionModal from './resolutionModal.vue'
|
import ResolutionModal from './resolutionModal.vue'
|
||||||
@@ -76,7 +78,7 @@ export default {
|
|||||||
type: [Object, null],
|
type: [Object, null],
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
controlConfig: {
|
assetPermissions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
}
|
}
|
||||||
@@ -154,7 +156,7 @@ export default {
|
|||||||
// clipboard contents received by remote desktop
|
// clipboard contents received by remote desktop
|
||||||
client.onclipboard = this.handleClipboardReceived
|
client.onclipboard = this.handleClipboardReceived
|
||||||
|
|
||||||
if (this?.controlConfig?.[`${queryProtocol?.split?.(':')?.[0]}_config`]?.copy) {
|
if (this?.assetPermissions?.[PERMISSION_TYPE.COPY]) {
|
||||||
// handle the clipboard content received from the remote desktop.
|
// handle the clipboard content received from the remote desktop.
|
||||||
client.onclipboard = this.handleClipboardReceived
|
client.onclipboard = this.handleClipboardReceived
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
ref="fileManagementDrawerRef"
|
ref="fileManagementDrawerRef"
|
||||||
connectType="ssh"
|
connectType="ssh"
|
||||||
:sessionId="connectData.sessionId"
|
:sessionId="connectData.sessionId"
|
||||||
|
:assetPermissions="assetPermissions"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -71,6 +72,10 @@ export default {
|
|||||||
preferenceSetting: {
|
preferenceSetting: {
|
||||||
type: [Object, null],
|
type: [Object, null],
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
assetPermissions: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
export const PERMISSION_TYPE = {
|
||||||
|
CONNECT: 'connect',
|
||||||
|
SHARE: 'share',
|
||||||
|
FILE_UPLOAD: 'file_upload',
|
||||||
|
FILE_DOWNLOAD: 'file_download',
|
||||||
|
COPY: 'copy',
|
||||||
|
PASTE: 'paste'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PERMISSION_TYPE_NAME = {
|
||||||
|
[PERMISSION_TYPE.CONNECT]: 'oneterm.accessControl.connect',
|
||||||
|
[PERMISSION_TYPE.FILE_UPLOAD]: 'oneterm.accessControl.upload',
|
||||||
|
[PERMISSION_TYPE.FILE_DOWNLOAD]: 'oneterm.accessControl.download',
|
||||||
|
[PERMISSION_TYPE.COPY]: 'oneterm.accessControl.copy',
|
||||||
|
[PERMISSION_TYPE.PASTE]: 'oneterm.accessControl.paste',
|
||||||
|
[PERMISSION_TYPE.SHARE]: 'oneterm.accessControl.share',
|
||||||
|
}
|
@@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div class="access-control">
|
||||||
|
<a-form-model ref="configForm" :model="form" :rules="rules" :label-col="{ span: 7 }" :wrapper-col="{ span: 14 }">
|
||||||
|
<a-form-model-item :label="$t('oneterm.accessControl.timeout')" prop="timeout">
|
||||||
|
<a-input-number
|
||||||
|
:min="0"
|
||||||
|
:max="7200"
|
||||||
|
v-model="form.timeout"
|
||||||
|
:formatter="(value) => `${value}s`"
|
||||||
|
:parser="(value) => value.replace('s', '')"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item
|
||||||
|
prop="default_permissions"
|
||||||
|
:label="$t('oneterm.accessControl.permissionConfig')"
|
||||||
|
:extra="$t('oneterm.accessControl.permissionConfigTip')"
|
||||||
|
>
|
||||||
|
<PermissionCheckbox
|
||||||
|
:value="form.default_permissions"
|
||||||
|
@change="handlePermissionChange"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item label=" " :colon="false">
|
||||||
|
<a-space>
|
||||||
|
<a-button :loading="loading" @click="getConfig()">{{ $t('reset') }}</a-button>
|
||||||
|
<a-button :loading="loading" type="primary" @click="handleSave">{{ $t('save') }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getConfig, postConfig } from '@/modules/oneterm/api/config'
|
||||||
|
import { PERMISSION_TYPE } from './constants.js'
|
||||||
|
|
||||||
|
import PermissionCheckbox from './permissionCheckbox.vue'
|
||||||
|
|
||||||
|
const DEFAULT_PERMISSIONS = Object.values(PERMISSION_TYPE).reduce((config, key) => {
|
||||||
|
config[key] = false
|
||||||
|
return config
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccessControl',
|
||||||
|
components: {
|
||||||
|
PermissionCheckbox
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
rules: {},
|
||||||
|
form: {
|
||||||
|
timeout: 5,
|
||||||
|
default_permissions: { ...DEFAULT_PERMISSIONS }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getConfig()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getConfig() {
|
||||||
|
getConfig({
|
||||||
|
info: true
|
||||||
|
}).then((res) => {
|
||||||
|
const { timeout = 5, default_permissions } = res?.data
|
||||||
|
this.form = {
|
||||||
|
timeout,
|
||||||
|
default_permissions: default_permissions || { ...DEFAULT_PERMISSIONS }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handlePermissionChange(key, checked) {
|
||||||
|
this.form.default_permissions[key] = checked
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
this.loading = true
|
||||||
|
postConfig({ ...this.form })
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success(this.$t('saveSuccess'))
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
this.getConfig()
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.access-control {
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100%;
|
||||||
|
padding: 18px 0px;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
margin-left: 6px;
|
||||||
|
color: @text-color_3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-checkbox
|
||||||
|
v-for="(item) in permissionCheckboxOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:checked="value[item.value]"
|
||||||
|
@change="(e) => $emit('change', item.value, e.target.checked)"
|
||||||
|
>
|
||||||
|
{{ $t(item.label) }}
|
||||||
|
<a-tooltip v-if="item.value === PERMISSION_TYPE.COPY">
|
||||||
|
<p slot="title">{{ $t('oneterm.accessControl.copyTip') }}</p>
|
||||||
|
<a-icon type="info-circle" class="terminal-control-label-icon"/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip v-if="item.value === PERMISSION_TYPE.PASTE">
|
||||||
|
<p slot="title">{{ $t('oneterm.accessControl.pasteTip') }}</p>
|
||||||
|
<a-icon type="info-circle" class="terminal-control-label-icon"/>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { PERMISSION_TYPE, PERMISSION_TYPE_NAME } from './constants.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PermissionCheckbox',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
PERMISSION_TYPE,
|
||||||
|
permissionCheckboxOptions: Object.values(PERMISSION_TYPE).map((key) => ({
|
||||||
|
value: key,
|
||||||
|
label: PERMISSION_TYPE_NAME[key]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ant-checkbox-wrapper + .ant-checkbox-wrapper {
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,100 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-modal :title="title" :visible="visible" @cancel="handleCancel" @ok="handleOk" :confirmLoading="loading">
|
|
||||||
<a-form-model ref="commandForm" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
|
|
||||||
<a-form-model-item :label="$t(`oneterm.name`)" prop="name">
|
|
||||||
<a-input v-model="form.name" :placeholder="`${$t(`placeholder1`)}`" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t(`oneterm.command`)" prop="cmd">
|
|
||||||
<a-input v-model="form.cmd" :placeholder="`${$t(`placeholder1`)}`" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t(`oneterm.commandIntercept.enable`)" prop="enable">
|
|
||||||
<a-switch v-model="form.enable" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item :label="$t(`oneterm.commandIntercept.regexp`)" prop="enable">
|
|
||||||
<a-switch v-model="form.is_re" />
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model>
|
|
||||||
</a-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { postCommand, putCommandById } from '../../../api/command'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'CommandModal',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: false,
|
|
||||||
form: {
|
|
||||||
name: '',
|
|
||||||
cmd: '',
|
|
||||||
enable: true,
|
|
||||||
is_re: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
|
||||||
},
|
|
||||||
loading: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
title() {
|
|
||||||
if (this.form.id) {
|
|
||||||
return this.$t('oneterm.commandIntercept.editCommand')
|
|
||||||
}
|
|
||||||
return this.$t('oneterm.commandIntercept.createCommand')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
open(data) {
|
|
||||||
this.visible = true
|
|
||||||
if (data) {
|
|
||||||
this.form = {
|
|
||||||
...data,
|
|
||||||
enable: Boolean(data.enable),
|
|
||||||
is_re: Boolean(data.is_re),
|
|
||||||
cmd: data.cmd ?? ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleCancel() {
|
|
||||||
this.$refs.commandForm.resetFields()
|
|
||||||
this.form = {
|
|
||||||
name: '',
|
|
||||||
cmd: '',
|
|
||||||
enable: true,
|
|
||||||
is_re: true,
|
|
||||||
}
|
|
||||||
this.visible = false
|
|
||||||
},
|
|
||||||
async handleOk() {
|
|
||||||
this.$refs.commandForm.validate(async (valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.loading = true
|
|
||||||
if (this.form.id) {
|
|
||||||
await putCommandById(this.form.id, { ...this.form })
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success(this.$t('editSuccess'))
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await postCommand({ ...this.form })
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success(this.$t('createSuccess'))
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.$emit('submit')
|
|
||||||
this.handleCancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@@ -24,8 +24,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CommandIntercept from './commandIntercept/index.vue'
|
import AccessControl from './accessControl/index.vue'
|
||||||
import TerminalControl from './terminalControl/index.vue'
|
|
||||||
import PublicKey from './publicKey/index.vue'
|
import PublicKey from './publicKey/index.vue'
|
||||||
import QuickCommand from './quickCommand/index.vue'
|
import QuickCommand from './quickCommand/index.vue'
|
||||||
import TerminalDisplay from './terminalDisplay/index.vue'
|
import TerminalDisplay from './terminalDisplay/index.vue'
|
||||||
@@ -36,8 +35,7 @@ const systemSettingTabStorageKey = 'ops_oneterm_system_setting_tab_key'
|
|||||||
export default {
|
export default {
|
||||||
name: 'SystemSettings',
|
name: 'SystemSettings',
|
||||||
components: {
|
components: {
|
||||||
CommandIntercept,
|
AccessControl,
|
||||||
TerminalControl,
|
|
||||||
PublicKey,
|
PublicKey,
|
||||||
QuickCommand,
|
QuickCommand,
|
||||||
TerminalDisplay,
|
TerminalDisplay,
|
||||||
@@ -70,16 +68,10 @@ export default {
|
|||||||
component: 'TerminalDisplay'
|
component: 'TerminalDisplay'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'oneterm.systemSettings.terminalControl',
|
label: 'oneterm.systemSettings.accessControl',
|
||||||
icon: 'basic_settings',
|
icon: 'basic_settings',
|
||||||
key: 'terminalControl',
|
key: 'accessControl',
|
||||||
component: 'TerminalControl',
|
component: 'AccessControl',
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'oneterm.systemSettings.commandIntercept',
|
|
||||||
icon: 'a-command_interception1',
|
|
||||||
key: 'commandIntercept',
|
|
||||||
component: 'CommandIntercept',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'oneterm.systemSettings.storageConfig',
|
label: 'oneterm.systemSettings.storageConfig',
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
<a-form-model-item :label="$t('oneterm.storageConfig.isPrimary')" prop="is_primary">
|
<a-form-model-item :label="$t('oneterm.storageConfig.isPrimary')" prop="is_primary">
|
||||||
<a-switch v-model="form.is_primary" />
|
<a-switch v-model="form.is_primary" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
<a-form-model-item :label="$t('oneterm.storageConfig.isEnable')" prop="enabled">
|
<a-form-model-item :label="$t('oneterm.isEnable')" prop="enabled">
|
||||||
<a-switch v-model="form.enabled" />
|
<a-switch v-model="form.enabled" />
|
||||||
</a-form-model-item>
|
</a-form-model-item>
|
||||||
|
|
||||||
|
@@ -82,10 +82,10 @@
|
|||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column :title="$t('oneterm.storageConfig.isEnable')" field="enabled" min-width="70">
|
<vxe-column :title="$t('oneterm.isEnable')" field="enabled" min-width="70">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
:title="$t('oneterm.storageConfig.confirmEnable')"
|
:title="$t('oneterm.confirmEnable')"
|
||||||
@confirm="toggleEnabled(row)"
|
@confirm="toggleEnabled(row)"
|
||||||
>
|
>
|
||||||
<a-switch :checked="row.enabled" />
|
<a-switch :checked="row.enabled" />
|
||||||
|
@@ -1,143 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="terminal-control">
|
|
||||||
<a-form-model ref="configForm" :model="form" :rules="rules" :label-col="{ span: 7 }" :wrapper-col="{ span: 14 }">
|
|
||||||
<a-form-model-item :label="$t('oneterm.terminalControl.timeout')" prop="timeout">
|
|
||||||
<a-input-number
|
|
||||||
:min="0"
|
|
||||||
:max="7200"
|
|
||||||
v-model="form.timeout"
|
|
||||||
:formatter="(value) => `${value}s`"
|
|
||||||
:parser="(value) => value.replace('s', '')"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
|
|
||||||
<a-form-model-item>
|
|
||||||
<div class="terminal-control-label" slot="label">
|
|
||||||
<span>RDP</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<div slot="title">
|
|
||||||
<p>{{ $t('oneterm.terminalControl.copyTip') }}</p>
|
|
||||||
<p>{{ $t('oneterm.terminalControl.pasteTip') }}</p>
|
|
||||||
</div>
|
|
||||||
<a-icon
|
|
||||||
type="info-circle"
|
|
||||||
class="terminal-control-label-icon"
|
|
||||||
/>
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-checkbox v-model="form.rdp_config.copy">
|
|
||||||
{{ $t('oneterm.terminalControl.allowCopy') }}
|
|
||||||
</a-checkbox>
|
|
||||||
<a-checkbox v-model="form.rdp_config.paste">
|
|
||||||
{{ $t('oneterm.terminalControl.allowPaste') }}
|
|
||||||
</a-checkbox>
|
|
||||||
</a-form-model-item>
|
|
||||||
|
|
||||||
<a-form-model-item>
|
|
||||||
<div class="terminal-control-label" slot="label">
|
|
||||||
<span>VNC</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<div slot="title">
|
|
||||||
<p>{{ $t('oneterm.terminalControl.copyTip') }}</p>
|
|
||||||
<p>{{ $t('oneterm.terminalControl.pasteTip') }}</p>
|
|
||||||
</div>
|
|
||||||
<a-icon
|
|
||||||
type="info-circle"
|
|
||||||
class="terminal-control-label-icon"
|
|
||||||
/>
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-checkbox v-model="form.vnc_config.copy">
|
|
||||||
{{ $t('oneterm.terminalControl.allowCopy') }}
|
|
||||||
</a-checkbox>
|
|
||||||
<a-checkbox v-model="form.vnc_config.paste">
|
|
||||||
{{ $t('oneterm.terminalControl.allowPaste') }}
|
|
||||||
</a-checkbox>
|
|
||||||
</a-form-model-item>
|
|
||||||
|
|
||||||
<a-form-model-item label=" " :colon="false">
|
|
||||||
<a-space>
|
|
||||||
<a-button :loading="loading" @click="getConfig()">{{ $t('reset') }}</a-button>
|
|
||||||
<a-button :loading="loading" type="primary" @click="handleSave">{{ $t('save') }}</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getConfig, postConfig } from '@/modules/oneterm/api/config'
|
|
||||||
export default {
|
|
||||||
name: 'TerminalControl',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
form: {
|
|
||||||
timeout: 5,
|
|
||||||
rdp_config: {
|
|
||||||
copy: false,
|
|
||||||
paste: false,
|
|
||||||
},
|
|
||||||
vnc_config: {
|
|
||||||
copy: false,
|
|
||||||
paste: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getConfig()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getConfig() {
|
|
||||||
await getConfig({
|
|
||||||
info: true
|
|
||||||
}).then((res) => {
|
|
||||||
const { timeout = 5, rdp_config, vnc_config } = res?.data
|
|
||||||
this.form = {
|
|
||||||
timeout,
|
|
||||||
vnc_config: {
|
|
||||||
copy: vnc_config?.copy || false,
|
|
||||||
paste: vnc_config?.paste || false
|
|
||||||
},
|
|
||||||
rdp_config: {
|
|
||||||
copy: rdp_config?.copy || false,
|
|
||||||
paste: rdp_config?.paste || false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleSave() {
|
|
||||||
this.loading = true
|
|
||||||
postConfig({ ...this.form })
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success(this.$t('saveSuccess'))
|
|
||||||
})
|
|
||||||
.finally(async () => {
|
|
||||||
this.getConfig()
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.terminal-control {
|
|
||||||
background-color: #fff;
|
|
||||||
height: 100%;
|
|
||||||
padding: 18px 0px;
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
&-label {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
margin-left: 6px;
|
|
||||||
color: @text-color_3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -114,7 +114,7 @@ export default {
|
|||||||
searchValue: '',
|
searchValue: '',
|
||||||
|
|
||||||
getRequestParams: {
|
getRequestParams: {
|
||||||
info: true
|
info: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -53,7 +53,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.assetList.ip`)" field="ip"> </vxe-column>
|
<vxe-column :title="$t(`oneterm.assetList.ip`)" field="ip"> </vxe-column>
|
||||||
<vxe-column :title="$t(`oneterm.assetList.catalogName`)" field="node_chain"> </vxe-column>
|
<vxe-column :title="$t(`oneterm.assetList.folderName`)" field="node_chain"> </vxe-column>
|
||||||
<vxe-column
|
<vxe-column
|
||||||
:title="$t(`status`)"
|
:title="$t(`status`)"
|
||||||
field="connectable"
|
field="connectable"
|
||||||
|
@@ -89,6 +89,7 @@
|
|||||||
:assetId="item.assetId"
|
:assetId="item.assetId"
|
||||||
:accountId="item.accountId"
|
:accountId="item.accountId"
|
||||||
:protocol="item.protocol"
|
:protocol="item.protocol"
|
||||||
|
:assetPermissions="item.permissions"
|
||||||
:isFullScreen="false"
|
:isFullScreen="false"
|
||||||
:preferenceSetting="preferenceSetting"
|
:preferenceSetting="preferenceSetting"
|
||||||
@close="handleTerminalError(item)"
|
@close="handleTerminalError(item)"
|
||||||
@@ -103,9 +104,9 @@
|
|||||||
:assetId="item.assetId"
|
:assetId="item.assetId"
|
||||||
:accountId="item.accountId"
|
:accountId="item.accountId"
|
||||||
:protocol="item.protocol"
|
:protocol="item.protocol"
|
||||||
|
:assetPermissions="item.permissions"
|
||||||
:isFullScreen="false"
|
:isFullScreen="false"
|
||||||
:preferenceSetting="preferenceSetting"
|
:preferenceSetting="preferenceSetting"
|
||||||
:controlConfig="controlConfig"
|
|
||||||
@close="handleTerminalError(item)"
|
@close="handleTerminalError(item)"
|
||||||
@open="getOfUserStat()"
|
@open="getOfUserStat()"
|
||||||
@updatePreferenceSetting="getPreference"
|
@updatePreferenceSetting="getPreference"
|
||||||
@@ -128,7 +129,6 @@
|
|||||||
:openFullScreen="openFullScreen"
|
:openFullScreen="openFullScreen"
|
||||||
:accountList="accountList"
|
:accountList="accountList"
|
||||||
:currentTabData="currentTabData"
|
:currentTabData="currentTabData"
|
||||||
:controlConfig="controlConfig"
|
|
||||||
@toggleFullScreen="toggleFullScreen"
|
@toggleFullScreen="toggleFullScreen"
|
||||||
@openRecentSession="openRecentSession"
|
@openRecentSession="openRecentSession"
|
||||||
@openBatchExecution="openBatchExecution"
|
@openBatchExecution="openBatchExecution"
|
||||||
@@ -154,6 +154,7 @@ import { getOfUserStat } from '@/modules/oneterm/api/stat'
|
|||||||
import { getPreference } from '@/modules/oneterm/api/preference.js'
|
import { getPreference } from '@/modules/oneterm/api/preference.js'
|
||||||
import { getAccountList } from '@/modules/oneterm/api/account'
|
import { getAccountList } from '@/modules/oneterm/api/account'
|
||||||
import { getConfig } from '@/modules/oneterm/api/config'
|
import { getConfig } from '@/modules/oneterm/api/config'
|
||||||
|
import { getAssetPermissions } from '@/modules/oneterm/api/asset'
|
||||||
import { defaultPreferenceSetting } from '../systemSettings/terminalDisplay/constants.js'
|
import { defaultPreferenceSetting } from '../systemSettings/terminalDisplay/constants.js'
|
||||||
import { WORKSTATION_TAB_TYPE } from './constants.js'
|
import { WORKSTATION_TAB_TYPE } from './constants.js'
|
||||||
import FullScreenMixin from '@/modules/oneterm/mixins/fullScreenMixin'
|
import FullScreenMixin from '@/modules/oneterm/mixins/fullScreenMixin'
|
||||||
@@ -289,23 +290,27 @@ export default {
|
|||||||
this.selectedKeys = keys
|
this.selectedKeys = keys
|
||||||
},
|
},
|
||||||
|
|
||||||
openTerminal(data) {
|
async openTerminal(data) {
|
||||||
const id = uuidv4()
|
const id = uuidv4()
|
||||||
const accountName = this.getAccountName(data.accountId)
|
const accountName = this.getAccountName(data.accountId)
|
||||||
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
||||||
|
const permissions = await this.getAssetPermissions(data.assetId, data.accountId)
|
||||||
|
|
||||||
this.terminalList.push({
|
this.terminalList.push({
|
||||||
...data,
|
...data,
|
||||||
socketStatus: true,
|
socketStatus: true,
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
type: this.getConnectType(data.protocolType)
|
type: this.getConnectType(data.protocolType),
|
||||||
|
permissions: permissions?.[data.accountId] || {}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.tabActiveKey = id
|
this.tabActiveKey = id
|
||||||
},
|
},
|
||||||
|
|
||||||
openTerminalList(data) {
|
async openTerminalList(data) {
|
||||||
|
const permissions = await this.getAssetPermissions(data.assetId, data.accountList.map((id) => id).join(','))
|
||||||
|
|
||||||
const newList = data.accountList.map((id) => {
|
const newList = data.accountList.map((id) => {
|
||||||
const accountName = this.getAccountName(id)
|
const accountName = this.getAccountName(id)
|
||||||
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
||||||
@@ -318,13 +323,35 @@ export default {
|
|||||||
accountId: id,
|
accountId: id,
|
||||||
socketStatus: true,
|
socketStatus: true,
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
type: this.getConnectType(data.protocolType)
|
type: this.getConnectType(data.protocolType),
|
||||||
|
permissions: permissions?.[id] || {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.tabActiveKey = newList[0].id
|
this.tabActiveKey = newList[0].id
|
||||||
this.terminalList.push(...newList)
|
this.terminalList.push(...newList)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getAssetPermissions(assetId, account_ids) {
|
||||||
|
const defaultPermissions = this.controlConfig?.default_permissions
|
||||||
|
const permissions = {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getAssetPermissions(assetId, { account_ids })
|
||||||
|
const data = res?.data?.results || {}
|
||||||
|
Object.keys(data).forEach((accountId) => {
|
||||||
|
const permissionData = data?.[accountId]?.results || {}
|
||||||
|
permissions[accountId] = {}
|
||||||
|
Object.keys(defaultPermissions).forEach((permissionType) => {
|
||||||
|
permissions[accountId][permissionType] = permissionData?.[permissionType]?.allowed ?? defaultPermissions?.[permissionType] ?? false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('getAssetPermissions error', error)
|
||||||
|
}
|
||||||
|
return permissions
|
||||||
|
},
|
||||||
|
|
||||||
closeTerminal(item, index) {
|
closeTerminal(item, index) {
|
||||||
if (item.id === this.tabActiveKey) {
|
if (item.id === this.tabActiveKey) {
|
||||||
this.tabActiveKey = index === 0 ? WORKSTATION_TAB_TYPE.MY_ASSETS : this.terminalList[index - 1].id
|
this.tabActiveKey = index === 0 ? WORKSTATION_TAB_TYPE.MY_ASSETS : this.terminalList[index - 1].id
|
||||||
|
@@ -7,5 +7,6 @@ export const OPERATION_MENU_TYPE = {
|
|||||||
QUICK_COMMAND: 'quickCommand',
|
QUICK_COMMAND: 'quickCommand',
|
||||||
FILE_MANAGEMENT: 'fileManagement',
|
FILE_MANAGEMENT: 'fileManagement',
|
||||||
CLIPBOARD: 'clipboard',
|
CLIPBOARD: 'clipboard',
|
||||||
RESOLUTION: 'resolution'
|
RESOLUTION: 'resolution',
|
||||||
|
SHARE: 'share'
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,17 @@
|
|||||||
class="workstation-operation-menu-divider"
|
class="workstation-operation-menu-divider"
|
||||||
></a-divider>
|
></a-divider>
|
||||||
|
|
||||||
|
<a-tooltip
|
||||||
|
v-if="controlDisplayList.includes(OPERATION_MENU_TYPE.SHARE)"
|
||||||
|
:title="$t('oneterm.workStation.assetShare')"
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<ops-icon
|
||||||
|
type="veops-share"
|
||||||
|
@click="shareAsset"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="controlDisplayList.includes(OPERATION_MENU_TYPE.QUICK_COMMAND)"
|
v-if="controlDisplayList.includes(OPERATION_MENU_TYPE.QUICK_COMMAND)"
|
||||||
:title="$t('oneterm.quickCommand.name')"
|
:title="$t('oneterm.quickCommand.name')"
|
||||||
@@ -109,19 +120,26 @@
|
|||||||
:accountList="accountList"
|
:accountList="accountList"
|
||||||
@ok="openBatchExecution"
|
@ok="openBatchExecution"
|
||||||
/>
|
/>
|
||||||
|
<ShareAssetModal
|
||||||
|
ref="shareAssetModalRef"
|
||||||
|
:assetData="currentTabData"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { WORKSTATION_TAB_TYPE } from '@/modules/oneterm/views/workStation/constants.js'
|
import { WORKSTATION_TAB_TYPE } from '@/modules/oneterm/views/workStation/constants.js'
|
||||||
import { OPERATION_MENU_TYPE } from './constants.js'
|
import { OPERATION_MENU_TYPE } from './constants.js'
|
||||||
|
import { PERMISSION_TYPE } from '@/modules/oneterm/views/systemSettings/accessControl/constants.js'
|
||||||
|
|
||||||
import ChooseAssetsModal from '../batchExecution/chooseAssetsModal.vue'
|
import ChooseAssetsModal from '../batchExecution/chooseAssetsModal.vue'
|
||||||
|
import ShareAssetModal from './shareAssetModal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OperationMenu',
|
name: 'OperationMenu',
|
||||||
components: {
|
components: {
|
||||||
ChooseAssetsModal
|
ChooseAssetsModal,
|
||||||
|
ShareAssetModal
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
openFullScreen: {
|
openFullScreen: {
|
||||||
@@ -135,10 +153,6 @@ export default {
|
|||||||
currentTabData: {
|
currentTabData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
},
|
|
||||||
controlConfig: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -155,6 +169,8 @@ export default {
|
|||||||
return this.currentTabData?.type === WORKSTATION_TAB_TYPE.GUACAMOLE
|
return this.currentTabData?.type === WORKSTATION_TAB_TYPE.GUACAMOLE
|
||||||
},
|
},
|
||||||
controlDisplayList() {
|
controlDisplayList() {
|
||||||
|
const assetPermissions = this.currentTabData?.permissions || {}
|
||||||
|
|
||||||
const controlDisplayList = [
|
const controlDisplayList = [
|
||||||
OPERATION_MENU_TYPE.FULL_SCREEN,
|
OPERATION_MENU_TYPE.FULL_SCREEN,
|
||||||
OPERATION_MENU_TYPE.RECENT_SESSION,
|
OPERATION_MENU_TYPE.RECENT_SESSION,
|
||||||
@@ -167,15 +183,22 @@ export default {
|
|||||||
controlDisplayList.push(OPERATION_MENU_TYPE.RESOLUTION)
|
controlDisplayList.push(OPERATION_MENU_TYPE.RESOLUTION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showShare = assetPermissions?.[PERMISSION_TYPE.SHARE] || false
|
||||||
|
if (showShare && (this.isGuacamole || this.isTerminal)) {
|
||||||
|
controlDisplayList.push(OPERATION_MENU_TYPE.SHARE)
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isTerminal) {
|
if (this.isTerminal) {
|
||||||
controlDisplayList.push(OPERATION_MENU_TYPE.QUICK_COMMAND)
|
controlDisplayList.push(OPERATION_MENU_TYPE.QUICK_COMMAND)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['ssh', 'rdp'].includes(this.currentTabData?.protocolType)) {
|
const showUpload = assetPermissions?.[PERMISSION_TYPE.FILE_UPLOAD] || false
|
||||||
|
const showDownload = assetPermissions?.[PERMISSION_TYPE.FILE_DOWNLOAD] || false
|
||||||
|
if (['ssh', 'rdp'].includes(this.currentTabData?.protocolType) && (showUpload || showDownload)) {
|
||||||
controlDisplayList.push(OPERATION_MENU_TYPE.FILE_MANAGEMENT)
|
controlDisplayList.push(OPERATION_MENU_TYPE.FILE_MANAGEMENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
const showClipboard = this?.controlConfig?.[`${this.currentTabData?.protocolType}_config`]?.paste
|
const showClipboard = assetPermissions?.[PERMISSION_TYPE.PASTE] || false
|
||||||
if (this.isGuacamole && showClipboard) {
|
if (this.isGuacamole && showClipboard) {
|
||||||
controlDisplayList.push(OPERATION_MENU_TYPE.CLIPBOARD)
|
controlDisplayList.push(OPERATION_MENU_TYPE.CLIPBOARD)
|
||||||
}
|
}
|
||||||
@@ -211,6 +234,9 @@ export default {
|
|||||||
},
|
},
|
||||||
callComponentFn(name) {
|
callComponentFn(name) {
|
||||||
this.$emit('callComponentFn', name)
|
this.$emit('callComponentFn', name)
|
||||||
|
},
|
||||||
|
shareAsset() {
|
||||||
|
this.$refs.shareAssetModalRef.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="$t('oneterm.assetList.createTempLink')"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
:okText="linkText ? $t('confirm') : $t('create')"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="linkText"
|
||||||
|
class="temp-link-content"
|
||||||
|
>
|
||||||
|
<span class="temp-link-content-label">{{ $t('oneterm.assetList.tempLink') }}: </span>
|
||||||
|
<span class="temp-link-content-text">
|
||||||
|
{{ linkText }}
|
||||||
|
<a @click="copyLink">
|
||||||
|
<ops-icon type="veops-copy"/>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a-form-model
|
||||||
|
v-else
|
||||||
|
ref="createTempLinkForm"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 19 }"
|
||||||
|
>
|
||||||
|
<a-form-model-item :label="$t(`oneterm.assetList.validTime`)" prop="validTime">
|
||||||
|
<a-range-picker
|
||||||
|
v-model="form.validTime"
|
||||||
|
:show-time="{ format: 'HH:mm' }"
|
||||||
|
format="YYYY-MM-DD HH:mm"
|
||||||
|
>
|
||||||
|
<a-icon slot="suffixIcon" type="calendar" />
|
||||||
|
</a-range-picker>
|
||||||
|
</a-form-model-item>
|
||||||
|
<a-form-model-item :label="$t(`oneterm.assetList.times`)" prop="timesRadio">
|
||||||
|
<a-radio-group v-model="form.timesRadio">
|
||||||
|
<a-radio class="temp-link-times" value="fixed">
|
||||||
|
<div class="temp-link-times-fixed">
|
||||||
|
<span>{{ $t('oneterm.assetList.fixed') }}</span>
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.times"
|
||||||
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
|
/>
|
||||||
|
<span>{{ $t('oneterm.assetList.times2') }}</span>
|
||||||
|
</div>
|
||||||
|
</a-radio>
|
||||||
|
<a-radio class="temp-link-times" value="any">
|
||||||
|
{{ $t('oneterm.assetList.any') }}
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment'
|
||||||
|
import { postShareLink } from '@/modules/oneterm/api/connect'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ShareAssetModal',
|
||||||
|
props: {
|
||||||
|
assetData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
confirmLoading: false,
|
||||||
|
linkText: '',
|
||||||
|
form: {
|
||||||
|
validTime: [moment(), moment().add(1, 'day')],
|
||||||
|
times: 1,
|
||||||
|
timesRadio: 'fixed'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
validTime: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('placeholder2')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
timesRadio: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('placeholder2')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.visible = true
|
||||||
|
this.form = {
|
||||||
|
validTime: [moment(), moment().add(1, 'day')],
|
||||||
|
times: 1,
|
||||||
|
timesRadio: 'fixed'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyLink() {
|
||||||
|
this.$copyText(this.linkText)
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success(this.$t('copySuccess'))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.confirmLoading = false
|
||||||
|
this.linkText = ''
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
handleOk() {
|
||||||
|
if (this.linkText) {
|
||||||
|
this.handleCancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.createTempLinkForm.validate(async (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.confirmLoading = true
|
||||||
|
const start = this.form.validTime[0].format()
|
||||||
|
const end = this.form.validTime[1].format()
|
||||||
|
const times = this.form.timesRadio === 'fixed' ? this.form.times : 0
|
||||||
|
const protocol = this.assetData.protocolType
|
||||||
|
|
||||||
|
const params = [
|
||||||
|
{
|
||||||
|
account_id: Number(this.assetData.accountId),
|
||||||
|
asset_id: this.assetData.assetId,
|
||||||
|
protocol,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
times,
|
||||||
|
no_limit: this.form.timesRadio === 'any'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const res = await postShareLink(params)
|
||||||
|
const shareId = res?.list?.[0]
|
||||||
|
if (shareId) {
|
||||||
|
this.$message.success(this.$t('createSuccess'))
|
||||||
|
this.linkText = `${document.location.origin}/oneterm/share/${protocol}/${shareId}`
|
||||||
|
}
|
||||||
|
this.confirmLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.temp-link-content {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp-link-times {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-fixed {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Reference in New Issue
Block a user