mirror of
https://github.com/veops/oneterm.git
synced 2025-09-26 19:31:14 +08:00
feat(ui): add access control
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
"lodash.pick": "^4.4.0",
|
||||
"md5": "^2.2.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.6.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"snabbdom": "^3.5.1",
|
||||
"sortablejs": "1.9.0",
|
||||
|
@@ -1,32 +1,32 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getAccountList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/account',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/account',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postAccount(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/account',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/account',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function putAccountById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/account/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/account/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteAccountById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/account/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/account/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
@@ -1,32 +1,40 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getAssetList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/asset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/asset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postAsset(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/asset',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/asset',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function putAssetById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/asset/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/asset/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteAssetById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/asset/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/asset/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
export function getAssetPermissions(id, params) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/asset/${id}/permissions`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getAuth(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/authorization',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/authorization',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postAuth(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/authorization',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/authorization',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
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',
|
||||
})
|
||||
}
|
@@ -1,32 +1,32 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getCommandList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/command',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/command',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postCommand(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/command',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/command',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function putCommandById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/command/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/command/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteCommandById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/command/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/command/${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',
|
||||
})
|
||||
}
|
@@ -1,17 +1,17 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getConfig(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/config',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/config',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postConfig(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/config',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/config',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@@ -1,21 +1,21 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function closeConnect(session_id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/connect/close/${session_id}`,
|
||||
method: 'post',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/connect/close/${session_id}`,
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
export function postConnectIsRight(asset_id, account_id, protocol, query = null) {
|
||||
let url = `/oneterm/v1/connect/${asset_id}/${account_id}/${protocol}`
|
||||
if (query) {
|
||||
url = `${url}?${query}`
|
||||
}
|
||||
return axios({
|
||||
url,
|
||||
method: 'post',
|
||||
})
|
||||
let url = `/oneterm/v1/connect/${asset_id}/${account_id}/${protocol}`
|
||||
if (query) {
|
||||
url = `${url}?${query}`
|
||||
}
|
||||
return axios({
|
||||
url,
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
export function postShareLink(data) {
|
||||
|
@@ -1,32 +1,32 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getGatewayList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/gateway',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/gateway',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function postGateway(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/gateway',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/gateway',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function putGatewayById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/gateway/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/gateway/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteGatewayById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/gateway/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/gateway/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getLoginLogList(params) {
|
||||
return axios({
|
||||
url: `/v1/acl/audit_log/login`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
return axios({
|
||||
url: `/v1/acl/audit_log/login`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
@@ -1,39 +1,39 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getNodeList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/node',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/node',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getNodeById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/node?id=${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/node?id=${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function postNode(data) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/node',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/node',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function putNodeById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/node/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/node/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteNodeById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/node/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/node/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getOperationLogList(params) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/history`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/history`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getResourceType(params) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/history/type/mapping`,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/history/type/mapping`,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getCITypeGroups(params) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/groups`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/groups`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getCITypeAttributesById(CITypeId, parameter) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attributes`,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/${CITypeId}/attributes`,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
@@ -1,32 +1,32 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getPublicKeyList(params) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function addPublicKey(data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function putPublicKeyById(id, data) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deletePublicKeyById(id) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/public_key/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getSessionList(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/session',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/session',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getSessionCmdList(session_id, params) {
|
||||
return axios({
|
||||
url: `/oneterm/v1/session/${session_id}/cmd`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: `/oneterm/v1/session/${session_id}/cmd`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
@@ -1,49 +1,49 @@
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getCountStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/count',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/count',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getAssetTypeStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/assettype',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/assettype',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getAssetStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/asset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/asset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getAccountStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/account',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/account',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getOfUserStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/count/ofuser',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/count/ofuser',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getRankOfUserStat(params) {
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/rank/ofuser',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
return axios({
|
||||
url: '/oneterm/v1/stat/rank/ofuser',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
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">
|
||||
<thead class="c-weektime-head">
|
||||
<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">12:00 - 24:00</th>
|
||||
</tr>
|
||||
@@ -16,7 +16,7 @@
|
||||
</thead>
|
||||
<tbody class="c-weektime-body">
|
||||
<tr v-for="t in data" :key="t.row">
|
||||
<td>{{ $t(t.week) }}</td>
|
||||
<td>{{ $t(`oneterm.timeTemplate.day${t.day}`) }}</td>
|
||||
<td
|
||||
v-for="n in t.child"
|
||||
:key="`${n.row}-${n.col}`"
|
||||
@@ -32,15 +32,15 @@
|
||||
<tr>
|
||||
<td colspan="49" class="c-weektime-preview">
|
||||
<div class="g-clearfix c-weektime-con">
|
||||
<span class="g-pull-left">{{
|
||||
selectState ? $t(`oneterm.assetList.selectedTime`) : $t(`oneterm.assetList.drag`)
|
||||
}}</span>
|
||||
<span class="g-pull-left">
|
||||
{{ selectState ? $t('oneterm.timeTemplate.selectedTime') : $t('oneterm.timeTemplate.drag') }}
|
||||
</span>
|
||||
<a @click.prevent="$emit('onClear')" class="g-pull-right">{{ $t(`clear`) }}</a>
|
||||
</div>
|
||||
<div v-if="selectState" class="c-weektime-time">
|
||||
<div v-for="t in selectValue" :key="t.id">
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
@@ -178,7 +178,7 @@ export default {
|
||||
const _selectValue = this.data.map((item) => {
|
||||
return {
|
||||
id: item.row,
|
||||
week: item.week,
|
||||
day: item.day,
|
||||
value: item.child.filter((c) => c.check).map((c) => c.value),
|
||||
}
|
||||
})
|
||||
|
@@ -1,70 +1,39 @@
|
||||
const formatDate = (date, fmt) => {
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'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
|
||||
// Format a Date object to 'HH:mm'
|
||||
const formatTime = (date) => {
|
||||
const h = String(date.getHours()).padStart(2, '0')
|
||||
const m = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${h}:${m}`
|
||||
}
|
||||
|
||||
const createArr = (len) => {
|
||||
return Array.from(Array(len)).map((ret, id) => id)
|
||||
// Generate half-hour intervals for one day, last end is 23:59
|
||||
const generateDayIntervals = (day, row, total = 48) => {
|
||||
const intervals = []
|
||||
for (let i = 0; i < total; i++) {
|
||||
const startMinutes = i * 30
|
||||
const endMinutes = (i + 1) * 30
|
||||
const begin = formatTime(new Date(2000, 0, 1, 0, startMinutes))
|
||||
// Last interval ends at 23:59, others at next half hour
|
||||
const end =
|
||||
i === total - 1
|
||||
? '23:59'
|
||||
: formatTime(new Date(2000, 0, 1, 0, endMinutes))
|
||||
intervals.push({
|
||||
day,
|
||||
value: `${begin}~${end}`,
|
||||
begin,
|
||||
end,
|
||||
row,
|
||||
col: i
|
||||
})
|
||||
}
|
||||
return intervals
|
||||
}
|
||||
|
||||
const formatWeektime = (col) => {
|
||||
const timestamp = 1542384000000 // '2018-11-17 00:00:00'
|
||||
const beginstamp = timestamp + col * 1800000 // col * 30 * 60 * 1000
|
||||
const endstamp = beginstamp + 1800000
|
||||
// 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)
|
||||
}))
|
||||
|
||||
const begin = formatDate(new Date(beginstamp), 'hh:mm')
|
||||
const end = formatDate(new Date(endstamp), 'hh:mm')
|
||||
return `${begin}~${end}`
|
||||
}
|
||||
|
||||
const data = [
|
||||
'星期一',
|
||||
'星期二',
|
||||
'星期三',
|
||||
'星期四',
|
||||
'星期五',
|
||||
'星期六',
|
||||
'星期日'
|
||||
].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,
|
||||
col
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
week: ret,
|
||||
row: index,
|
||||
child: children(ret, index, 48)
|
||||
}
|
||||
})
|
||||
|
||||
export default data
|
||||
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,42 +2,33 @@
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:width="800"
|
||||
:footer="null"
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<template v-if="showACLConfig">
|
||||
<div class="auth-node-title">
|
||||
{{ $t(grantTitle) }}
|
||||
</div>
|
||||
<ACLTable
|
||||
:tableData="aclTableData"
|
||||
:resourceId="resourceId"
|
||||
/>
|
||||
<a-space>
|
||||
<span class="grant-button" @click="openGrantUserModal('depart')">{{ $t('oneterm.assetList.grantUserOrDep') }}</span>
|
||||
<span class="grant-button" @click="openGrantUserModal('role')">{{ $t('oneterm.assetList.grantRole') }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<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"
|
||||
/>
|
||||
<a-tabs v-model="tabKey">
|
||||
<a-tab-pane key="accessPermission" :tab="$t('oneterm.assetList.accessPermission')">
|
||||
<PermissionForm
|
||||
ref="permissionFormRef"
|
||||
:dataType="dataType"
|
||||
:ids="ids"
|
||||
@cancel="handleCancel"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane
|
||||
v-if="showACLConfig"
|
||||
key="operationPermissions"
|
||||
:tab="$t(grantTitle)"
|
||||
>
|
||||
<ACLTable
|
||||
:tableData="aclTableData"
|
||||
:resourceId="resourceId"
|
||||
/>
|
||||
<a-space>
|
||||
<span class="grant-button" @click="openGrantUserModal('depart')">{{ $t('oneterm.assetList.grantUserOrDep') }}</span>
|
||||
<span class="grant-button" @click="openGrantUserModal('role')">{{ $t('oneterm.assetList.grantRole') }}</span>
|
||||
</a-space>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<GrantUserModal
|
||||
ref="grantUserModalRef"
|
||||
@@ -49,30 +40,28 @@
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { mapState } from 'vuex'
|
||||
import { getAuth, postAuth } from '@/modules/oneterm/api/authorization.js'
|
||||
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 GrantUserModal from './grantUserModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'GrantModal',
|
||||
components: {
|
||||
EmployeeTreeSelect,
|
||||
PermissionForm,
|
||||
ACLTable,
|
||||
GrantUserModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabKey: 'accessPermission',
|
||||
visible: false,
|
||||
rids: [], // 登录权限
|
||||
resourceId: '', // acl resource id
|
||||
aclTableData: [], // acl data
|
||||
ids: [], // id 列表
|
||||
ids: [], // id list
|
||||
dataType: 'node',
|
||||
showACLConfig: true, // 是否展示acl 配置
|
||||
visualRoleList: [], // 虚拟角色
|
||||
showACLConfig: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -83,11 +72,11 @@ export default {
|
||||
grantTitle() {
|
||||
switch (this.dataType) {
|
||||
case 'node':
|
||||
return 'oneterm.assetList.grantCatalog'
|
||||
return 'oneterm.assetList.folderOperationPermissions'
|
||||
case 'asset':
|
||||
return 'oneterm.assetList.grantAsset'
|
||||
return 'oneterm.assetList.assetOperationPermissions'
|
||||
case 'account':
|
||||
return 'oneterm.assetList.grantAccount'
|
||||
return 'oneterm.assetList.accountOperationPermissions'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
@@ -99,12 +88,11 @@ export default {
|
||||
ids,
|
||||
resourceId,
|
||||
}) {
|
||||
this.tabKey = 'accessPermission'
|
||||
this.showACLConfig = type === 'node' || (type !== 'node' && ids.length === 1)
|
||||
this.ids = ids
|
||||
this.dataType = type
|
||||
this.resourceId = resourceId || ''
|
||||
|
||||
this.loadRoles()
|
||||
this.resourceId = resourceId ?? ''
|
||||
|
||||
let aclTableData = []
|
||||
if (this.showACLConfig) {
|
||||
@@ -134,79 +122,15 @@ export default {
|
||||
}
|
||||
}
|
||||
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
|
||||
},
|
||||
|
||||
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() {
|
||||
this.rids = []
|
||||
this.visible = false
|
||||
if (this.$refs.permissionFormRef) {
|
||||
this.$refs.permissionFormRef.resetFields()
|
||||
}
|
||||
this.resourceId = ''
|
||||
this.aclTableData = []
|
||||
this.ids = []
|
||||
@@ -214,52 +138,6 @@ export default {
|
||||
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) {
|
||||
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>
|
@@ -2,7 +2,7 @@ import genOnetermRoutes from './router/index'
|
||||
import onetermStore from './store'
|
||||
|
||||
export default {
|
||||
name: 'oneterm',
|
||||
route: genOnetermRoutes,
|
||||
store: onetermStore
|
||||
name: 'oneterm',
|
||||
route: genOnetermRoutes,
|
||||
store: onetermStore
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ const oneterm_en = {
|
||||
accessRestrictions: 'Access Restrictions',
|
||||
comment: 'Comment',
|
||||
node: 'Node',
|
||||
catalog: 'Catalog',
|
||||
folder: 'Folder',
|
||||
cmdbType: 'CMDB Type',
|
||||
fieldMap: 'Field Map',
|
||||
field: 'Field',
|
||||
@@ -33,13 +33,25 @@ const oneterm_en = {
|
||||
editPublicKey: 'Edit Public Key',
|
||||
assetCount: 'Asset Count',
|
||||
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: {
|
||||
oneterm: 'OneTerm',
|
||||
workStation: 'Work Station',
|
||||
resourceControl: 'Resource Control',
|
||||
basicResource: 'Basic Resource',
|
||||
assetManagement: 'Asset Management',
|
||||
assets: 'Assets',
|
||||
gateways: 'Gateways',
|
||||
accounts: 'Accounts',
|
||||
gatewayManagement: 'Gateway Management',
|
||||
accountManagement: 'Account Management',
|
||||
accessControl: 'Access Control',
|
||||
accessAuthorization: 'Access Authorization',
|
||||
commandFilter: 'Command Filter',
|
||||
accessTime: 'Access Time',
|
||||
security: 'Security',
|
||||
sessionAuditing: 'Session Auditing',
|
||||
onlineSession: 'Online Session',
|
||||
@@ -81,7 +93,8 @@ const oneterm_en = {
|
||||
pageUnloadMessage: 'Are you sure you want to leave the page?',
|
||||
batchExecution: 'Batch Execution',
|
||||
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: {
|
||||
target: 'Target',
|
||||
@@ -105,12 +118,12 @@ const oneterm_en = {
|
||||
assetList: {
|
||||
grantUser: 'Granted User',
|
||||
assetTree: 'Asset Tree',
|
||||
createCatalog: 'Create Catalog',
|
||||
editCatalog: 'Edit Catalog',
|
||||
deleteCatalog: 'Delete Catalog',
|
||||
grantCatalog: 'Grant Catalog',
|
||||
createFolder: 'Create Folder',
|
||||
editFolder: 'Edit Folder',
|
||||
deleteFolder: 'Delete Folder',
|
||||
grantFolder: 'Grant Folder',
|
||||
ip: 'IP',
|
||||
catalogName: 'Catalog Name',
|
||||
folderName: 'Folder Name',
|
||||
connectable: 'Connectable',
|
||||
connected: 'Connected',
|
||||
error: 'Error',
|
||||
@@ -125,9 +138,6 @@ const oneterm_en = {
|
||||
commandIntercept: 'Command Intercept',
|
||||
allowAccess: 'Allow Access',
|
||||
prohibitAccess: 'Prohibit Access',
|
||||
weektime: 'week/time',
|
||||
selectedTime: 'Selected Time',
|
||||
drag: 'Drag the mouse to select time',
|
||||
assetList: 'Asset List',
|
||||
createAsset: 'Create Asset',
|
||||
editAsset: 'Edit Asset',
|
||||
@@ -161,10 +171,15 @@ const oneterm_en = {
|
||||
grantLogin: 'Grant Login',
|
||||
grantUserOrDep: 'Grant User/Department',
|
||||
grantRole: 'Grant Role',
|
||||
createSubCatalog: 'Create Sub Catalog',
|
||||
createSubFolder: 'Create Sub Folder',
|
||||
assetSearchTip: 'Search in Asset',
|
||||
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: {
|
||||
time: 'Time',
|
||||
@@ -193,26 +208,51 @@ const oneterm_en = {
|
||||
clipboard: 'Clipboard'
|
||||
},
|
||||
systemSettings: {
|
||||
commandIntercept: 'Command Intercept',
|
||||
terminalControl: 'Terminal Control',
|
||||
accessControl: 'Access Control',
|
||||
quickCommand: 'Quick Command',
|
||||
terminalDisplay: 'Terminal Display',
|
||||
publicKey: 'Public Key',
|
||||
publicKeyTip: 'Password-free authentication for command terminals to log into the SSH service of the bastion',
|
||||
storageConfig: 'Storage Config'
|
||||
},
|
||||
commandIntercept: {
|
||||
enable: 'Enable',
|
||||
commandFilter: {
|
||||
name: 'Command Filter',
|
||||
commandManagement: 'Command Management',
|
||||
commandTemplateManagement: 'Command Template Management',
|
||||
regexp: 'RegExp',
|
||||
createCommand: 'Create 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',
|
||||
allowCopy: 'Allow Copy',
|
||||
allowPaste: 'Allow Paste',
|
||||
copyTip: 'Copy: The local device can get the contents of the clipboard text in the remote desktop in real time.',
|
||||
pasteTip: 'Paste: Remote Desktop can get the content of the text currently copied by the local device via the clipboard.'
|
||||
copyTip: 'The local device can get the contents of the clipboard text in the remote desktop in real time.',
|
||||
pasteTip: 'Remote Desktop can get the content of the text currently copied by the local device via the clipboard.',
|
||||
permissionConfig: 'Permission Config',
|
||||
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: {
|
||||
fontSetting: 'Font Setting',
|
||||
@@ -268,7 +308,6 @@ const oneterm_en = {
|
||||
createConfig: 'Create Config',
|
||||
editConfig: 'Edit Config',
|
||||
local: 'Local',
|
||||
isEnable: 'Is Enable',
|
||||
retentionDays: 'Retention Days',
|
||||
archiveDays: 'Archive Days',
|
||||
isCleanupEnabled: 'Is Cleanup Enabled',
|
||||
@@ -302,8 +341,76 @@ const oneterm_en = {
|
||||
basicConfig: 'Basic Config',
|
||||
advancedConfig: 'Advanced Config',
|
||||
advancedConfigTip: 'Configure the detailed parameters and strategies for storage',
|
||||
confirmEnable: 'Confirm toggle enable?',
|
||||
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
|
||||
|
@@ -16,7 +16,7 @@ const oneterm_zh = {
|
||||
accessRestrictions: '接入限制',
|
||||
comment: '备注',
|
||||
node: '节点',
|
||||
catalog: '目录',
|
||||
folder: '目录',
|
||||
cmdbType: '模型',
|
||||
fieldMap: '字段映射',
|
||||
field: '字段',
|
||||
@@ -33,13 +33,24 @@ const oneterm_zh = {
|
||||
editPublicKey: '编辑公钥',
|
||||
assetCount: '资产数',
|
||||
macAddress: 'Mac地址',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
isEnable: '是否启用',
|
||||
confirmEnable: '确认切换启用状态?',
|
||||
switching: '正在切换',
|
||||
switchingTip: '正在切换,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||
menu: {
|
||||
oneterm: '堡垒机',
|
||||
workStation: '工作台',
|
||||
resourceControl: '资源管控',
|
||||
basicResource: '基础资源',
|
||||
assetManagement: '资产管理',
|
||||
assets: '资产列表',
|
||||
gateways: '网关列表',
|
||||
accounts: '账号列表',
|
||||
gatewayManagement: '网关管理',
|
||||
accountManagement: '帐号管理',
|
||||
accessControl: '访问控制',
|
||||
accessAuthorization: '访问授权',
|
||||
commandFilter: '命令拦截',
|
||||
accessTime: '访问时段',
|
||||
security: '安全设置',
|
||||
sessionAuditing: '会话审计',
|
||||
onlineSession: '在线会话',
|
||||
@@ -82,6 +93,7 @@ const oneterm_zh = {
|
||||
batchExecution: '批量执行',
|
||||
chooseAssets: '选择资产',
|
||||
batchExecutionPlaceholder: '请输入命令, 按 Enter 执行',
|
||||
assetShare: '资产分享'
|
||||
},
|
||||
sessionTable: {
|
||||
target: '目标',
|
||||
@@ -105,12 +117,12 @@ const oneterm_zh = {
|
||||
assetList: {
|
||||
grantUser: '授权用户',
|
||||
assetTree: '资产树',
|
||||
createCatalog: '新建目录',
|
||||
editCatalog: '编辑目录',
|
||||
deleteCatalog: '删除目录',
|
||||
grantCatalog: '授权目录',
|
||||
createFolder: '新建目录',
|
||||
editFolder: '编辑目录',
|
||||
deleteFolder: '删除目录',
|
||||
grantFolder: '授权目录',
|
||||
ip: '地址',
|
||||
catalogName: '目录名称',
|
||||
folderName: '目录名称',
|
||||
connectable: '可连接',
|
||||
connected: '连接',
|
||||
error: '错误',
|
||||
@@ -125,9 +137,6 @@ const oneterm_zh = {
|
||||
commandIntercept: '命令拦截',
|
||||
allowAccess: '允许接入',
|
||||
prohibitAccess: '禁止接入',
|
||||
weektime: '星期/时间',
|
||||
selectedTime: '已选择时间段',
|
||||
drag: '可拖动鼠标选择时间段',
|
||||
assetList: '资产列表',
|
||||
createAsset: '创建资产',
|
||||
editAsset: '编辑资产',
|
||||
@@ -161,10 +170,15 @@ const oneterm_zh = {
|
||||
grantLogin: '登录权限',
|
||||
grantUserOrDep: '授权用户/部门',
|
||||
grantRole: '授权角色',
|
||||
createSubCatalog: '创建子目录',
|
||||
createSubFolder: '创建子目录',
|
||||
assetSearchTip: '在资产中搜索',
|
||||
basic: '基础',
|
||||
database: '数据库',
|
||||
folderOperationPermissions: '目录操作权限',
|
||||
assetOperationPermissions: '资产操作权限',
|
||||
accountOperationPermissions: '帐号操作权限',
|
||||
accessPermission: '访问权限',
|
||||
operationPermissions: '操作权限'
|
||||
},
|
||||
log: {
|
||||
time: '时间',
|
||||
@@ -193,26 +207,51 @@ const oneterm_zh = {
|
||||
clipboard: '剪贴板'
|
||||
},
|
||||
systemSettings: {
|
||||
commandIntercept: '命令拦截',
|
||||
terminalControl: '终端控制',
|
||||
accessControl: '访问控制',
|
||||
quickCommand: '快捷命令',
|
||||
terminalDisplay: '终端显示',
|
||||
publicKey: '我的公钥',
|
||||
publicKeyTip: '用于终端登录堡垒机SSH服务的免密认证',
|
||||
storageConfig: '存储配置'
|
||||
},
|
||||
commandIntercept: {
|
||||
enable: '是否激活',
|
||||
commandFilter: {
|
||||
name: '命令拦截',
|
||||
commandManagement: '命令管理',
|
||||
commandTemplateManagement: '命令模板管理',
|
||||
regexp: '是否正则',
|
||||
createCommand: '创建命令',
|
||||
editCommand: '编辑命令',
|
||||
category: '分类',
|
||||
securityRelated: '安全相关',
|
||||
systemOperations: '系统操作',
|
||||
databaseOperations: '数据库操作',
|
||||
networkOperations: '网络操作',
|
||||
fileOperations: '文件操作',
|
||||
developmentRelated: '开发相关',
|
||||
riskLevel: '风险等级',
|
||||
safe: '安全',
|
||||
warning: '警告',
|
||||
dangerous: '危险',
|
||||
criticalDanger: '严重危险',
|
||||
commandCount: '命令数量',
|
||||
createCommandTemplate: '新建命令模板',
|
||||
editCommandTemplate: '编辑命令模板',
|
||||
selectCommand: '选择命令',
|
||||
unselectCommand: '未选命令',
|
||||
selectedCommand: '已选命令',
|
||||
},
|
||||
terminalControl: {
|
||||
accessControl: {
|
||||
timeout: '会话最大空闲时间',
|
||||
allowCopy: '允许复制',
|
||||
allowPaste: '允许粘贴',
|
||||
copyTip: '复制:本地设备可以实时获取远程桌面中的剪贴板文本内容。',
|
||||
pasteTip: '粘贴:远程桌面可以通过剪贴板获取本地设备当前复制的文本内容。'
|
||||
copyTip: '本地设备可以实时获取远程桌面中的剪贴板文本内容。',
|
||||
pasteTip: '远程桌面可以通过剪贴板获取本地设备当前复制的文本内容。',
|
||||
permissionConfig: '权限配置',
|
||||
permissionConfigTip: '上传、下载是针对SSH、RDP协议,复制、粘贴是针对RDP协议',
|
||||
connect: '连接',
|
||||
upload: '上传',
|
||||
download: '下载',
|
||||
copy: '复制',
|
||||
paste: '粘贴',
|
||||
share: '分享'
|
||||
},
|
||||
terminalDisplay: {
|
||||
fontSetting: '字体设置',
|
||||
@@ -269,7 +308,6 @@ const oneterm_zh = {
|
||||
createConfig: '创建配置',
|
||||
editConfig: '编辑配置',
|
||||
local: '本地',
|
||||
isEnable: '是否启用',
|
||||
retentionDays: '保留天数',
|
||||
archiveDays: '归档天数',
|
||||
isCleanupEnabled: '是否启用清理',
|
||||
@@ -303,8 +341,76 @@ const oneterm_zh = {
|
||||
basicConfig: '基础配置',
|
||||
advancedConfig: '高级配置',
|
||||
advancedConfigTip: '配置存储的详细参数和策略',
|
||||
confirmEnable: '确认切换启用状态?',
|
||||
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
|
||||
|
@@ -1,146 +1,180 @@
|
||||
// @ts-ignore
|
||||
import { BasicLayout, RouteView } from '@/layouts'
|
||||
import user from '@/store/global/user'
|
||||
|
||||
const genOnetermRoutes = () => {
|
||||
return {
|
||||
path: '/oneterm',
|
||||
name: 'oneterm',
|
||||
component: BasicLayout,
|
||||
meta: { title: 'oneterm.menu.oneterm', keepAlive: false },
|
||||
redirect: () => {
|
||||
const { detailPermissions } = user.state
|
||||
return {
|
||||
path: '/oneterm',
|
||||
name: 'oneterm',
|
||||
component: BasicLayout,
|
||||
meta: { title: 'oneterm.menu.oneterm', keepAlive: false },
|
||||
redirect: () => {
|
||||
const { detailPermissions } = user.state
|
||||
|
||||
if (detailPermissions['oneterm'].some(item => item.name === 'WorkStation')) {
|
||||
return '/oneterm/workstation'
|
||||
}
|
||||
if (detailPermissions['oneterm'].some(item => item.name === 'Dashboard')) {
|
||||
return '/oneterm/dashboard'
|
||||
}
|
||||
if (detailPermissions['oneterm'].some(item => item.name === 'WorkStation')) {
|
||||
return '/oneterm/workstation'
|
||||
}
|
||||
if (detailPermissions['oneterm'].some(item => item.name === 'Dashboard')) {
|
||||
return '/oneterm/dashboard'
|
||||
}
|
||||
|
||||
return '/oneterm/workstation'
|
||||
},
|
||||
return '/oneterm/workstation'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/oneterm/dashboard',
|
||||
name: 'onterm_dashboard',
|
||||
component: () => import('../views/dashboard'),
|
||||
meta: { title: 'dashboard', appName: 'oneterm', icon: 'ops-oneterm-dashboard-selected', selectedIcon: 'ops-oneterm-dashboard-selected', keepAlive: false, permission: ['oneterm_admin', 'admin'] }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/workstation',
|
||||
name: 'onterm_work_station',
|
||||
component: () => import('../views/workStation'),
|
||||
meta: {
|
||||
title: 'oneterm.menu.workStation', icon: 'ops-oneterm-workstation-selected', selectedIcon: 'ops-oneterm-workstation-selected', keepAlive: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/oneterm/resource',
|
||||
name: 'oneterm_resource',
|
||||
component: RouteView,
|
||||
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',
|
||||
children: [
|
||||
{
|
||||
path: '/oneterm/dashboard',
|
||||
name: 'onterm_dashboard',
|
||||
component: () => import('../views/dashboard'),
|
||||
meta: { title: 'dashboard', appName: 'oneterm', icon: 'ops-oneterm-dashboard-selected', selectedIcon: 'ops-oneterm-dashboard-selected', keepAlive: false, permission: ['oneterm_admin', 'admin'] }
|
||||
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/workstation',
|
||||
name: 'onterm_work_station',
|
||||
component: () => import('../views/workStation'),
|
||||
meta: {
|
||||
title: 'oneterm.menu.workStation', icon: 'ops-oneterm-workstation-selected', selectedIcon: 'ops-oneterm-workstation-selected', keepAlive: false
|
||||
}
|
||||
path: '/oneterm/asset',
|
||||
name: 'oneterm_asset_list',
|
||||
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')
|
||||
},
|
||||
{
|
||||
path: '/oneterm/assets',
|
||||
name: 'oneterm_assets',
|
||||
component: RouteView,
|
||||
meta: { title: 'oneterm.menu.assetManagement', appName: 'oneterm', icon: 'ops-oneterm-asset-management', selectedIcon: 'ops-oneterm-asset-management', permission: ['oneterm_admin', 'admin'] },
|
||||
redirect: '/oneterm/assets/assets',
|
||||
children: [{
|
||||
path: '/oneterm/assetlist',
|
||||
name: 'oneterm_asset_list',
|
||||
meta: { title: 'oneterm.menu.assets', icon: 'ops-oneterm-assetlist', selectedIcon: 'ops-oneterm-assetlist-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
component: () => import('../views/assets/assets')
|
||||
}, {
|
||||
path: '/oneterm/account',
|
||||
name: 'oneterm_account',
|
||||
meta: { title: 'oneterm.menu.accounts', icon: 'ops-oneterm-account', selectedIcon: 'ops-oneterm-account-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
component: () => import('../views/assets/account')
|
||||
}, {
|
||||
path: '/oneterm/gateway',
|
||||
name: 'oneterm_gateway',
|
||||
meta: { title: 'oneterm.menu.gateways', icon: 'ops-oneterm-gateway', selectedIcon: 'ops-oneterm-gateway-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
component: () => import('../views/assets/gateway')
|
||||
}]
|
||||
path: '/oneterm/account',
|
||||
name: 'oneterm_account_management',
|
||||
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')
|
||||
},
|
||||
{
|
||||
path: '/oneterm/audit',
|
||||
name: 'oneterm_session',
|
||||
component: RouteView,
|
||||
meta: { title: 'oneterm.menu.auditCentre', icon: 'ops-oneterm-log-selected', selectedIcon: 'ops-oneterm-log-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
redirect: '/oneterm/session/online',
|
||||
hideChildrenInMenu: false,
|
||||
children: [
|
||||
{
|
||||
path: `/oneterm/session`,
|
||||
name: `oneterm_session`,
|
||||
meta: { title: 'oneterm.menu.sessionAuditing', disabled: true, style: 'margin-left: 12px', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/session/online.vue')
|
||||
}, {
|
||||
path: '/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'] },
|
||||
component: () => import('../views/session/history.vue')
|
||||
},
|
||||
{
|
||||
path: `/oneterm/log`,
|
||||
name: `oneterm_log`,
|
||||
meta: { title: 'oneterm.menu.logAuditing', disabled: true, style: 'margin-left: 12px', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/login')
|
||||
}, {
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/operation')
|
||||
}, {
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/file')
|
||||
}
|
||||
]
|
||||
path: '/oneterm/gateway',
|
||||
name: 'oneterm_gateway_management',
|
||||
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')
|
||||
},
|
||||
{
|
||||
path: '/oneterm/settings',
|
||||
name: 'onterm_settings',
|
||||
component: () => import('../views/systemSettings'),
|
||||
meta: { title: 'oneterm.menu.systemSettings', appName: 'oneterm', icon: 'veops-setting2', selectedIcon: 'veops-setting2', keepAlive: false }
|
||||
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/terminal',
|
||||
name: 'oneterm_terminal',
|
||||
hidden: true,
|
||||
component: () => import('../views/connect/terminal/index.vue'),
|
||||
meta: { title: 'oneterm.menu.terminal', keepAlive: false }
|
||||
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/guacamole/:asset_id/:account_id/:protocol',
|
||||
name: 'oneterm_guacamole',
|
||||
hidden: true,
|
||||
component: () => import('../views/connect/guacamoleClient/index.vue'),
|
||||
meta: { title: 'oneterm.menu.terminal', keepAlive: false }
|
||||
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/replay/:session_id',
|
||||
name: 'oneterm_replay',
|
||||
hidden: true,
|
||||
component: () => import('../views/replay'),
|
||||
meta: { title: 'oneterm.menu.replay', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/replay/guacamole/:session_id',
|
||||
name: 'oneterm_replay_guacamole',
|
||||
hidden: true,
|
||||
component: () => import('../views/replay/guacamoleReplay.vue'),
|
||||
meta: { title: 'oneterm.menu.replay', keepAlive: false }
|
||||
},
|
||||
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',
|
||||
name: 'oneterm_session',
|
||||
component: RouteView,
|
||||
meta: { title: 'oneterm.menu.auditCentre', icon: 'ops-oneterm-log-selected', selectedIcon: 'ops-oneterm-log-selected', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
redirect: '/oneterm/session/online',
|
||||
hideChildrenInMenu: false,
|
||||
children: [
|
||||
{
|
||||
path: `/oneterm/session`,
|
||||
name: `oneterm_session`,
|
||||
meta: { title: 'oneterm.menu.sessionAuditing', disabled: true, style: 'margin-left: 12px', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/session/online.vue')
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/session/history.vue')
|
||||
},
|
||||
{
|
||||
path: `/oneterm/log`,
|
||||
name: `oneterm_log`,
|
||||
meta: { title: 'oneterm.menu.logAuditing', disabled: true, style: 'margin-left: 12px', appName: 'oneterm', permission: ['oneterm_admin', 'admin'] },
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/login')
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/operation')
|
||||
},
|
||||
{
|
||||
path: '/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'] },
|
||||
component: () => import('../views/log/file')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/oneterm/settings',
|
||||
name: 'onterm_settings',
|
||||
component: () => import('../views/systemSettings'),
|
||||
meta: { title: 'oneterm.menu.systemSettings', appName: 'oneterm', icon: 'veops-setting2', selectedIcon: 'veops-setting2', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/terminal',
|
||||
name: 'oneterm_terminal',
|
||||
hidden: true,
|
||||
component: () => import('../views/connect/terminal/index.vue'),
|
||||
meta: { title: 'oneterm.menu.terminal', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/guacamole/:asset_id/:account_id/:protocol',
|
||||
name: 'oneterm_guacamole',
|
||||
hidden: true,
|
||||
component: () => import('../views/connect/guacamoleClient/index.vue'),
|
||||
meta: { title: 'oneterm.menu.terminal', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/replay/:session_id',
|
||||
name: 'oneterm_replay',
|
||||
hidden: true,
|
||||
component: () => import('../views/replay'),
|
||||
meta: { title: 'oneterm.menu.replay', keepAlive: false }
|
||||
},
|
||||
{
|
||||
path: '/oneterm/replay/guacamole/:session_id',
|
||||
name: 'oneterm_replay_guacamole',
|
||||
hidden: true,
|
||||
component: () => import('../views/replay/guacamoleReplay.vue'),
|
||||
meta: { title: 'oneterm.menu.replay', keepAlive: false }
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export default genOnetermRoutes
|
||||
|
@@ -1,8 +1,8 @@
|
||||
const onetermStore = {
|
||||
namespaced: true,
|
||||
name: 'onetermStore',
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {}
|
||||
namespaced: true,
|
||||
name: 'onetermStore',
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {}
|
||||
}
|
||||
export default onetermStore
|
||||
|
@@ -1,50 +1,50 @@
|
||||
import i18n from '@/lang'
|
||||
|
||||
export const getAllParentNodesLabel = (node, label) => {
|
||||
if (node.parentNode) {
|
||||
return getAllParentNodesLabel(node.parentNode, `${node.parentNode.label}-${label}`)
|
||||
}
|
||||
return label
|
||||
if (node.parentNode) {
|
||||
return getAllParentNodesLabel(node.parentNode, `${node.parentNode.label}-${label}`)
|
||||
}
|
||||
return label
|
||||
}
|
||||
export const getTreeSelectLabel = (node) => {
|
||||
return `${getAllParentNodesLabel(node, node.label)}`
|
||||
return `${getAllParentNodesLabel(node, node.label)}`
|
||||
}
|
||||
|
||||
export const setLocalStorage = (name, param) => {
|
||||
let storageData = JSON.parse(localStorage.getItem(name))
|
||||
if (storageData) {
|
||||
storageData = { ...storageData, ...param }
|
||||
} else {
|
||||
storageData = { ...param }
|
||||
}
|
||||
localStorage.setItem(name, JSON.stringify(storageData))
|
||||
let storageData = JSON.parse(localStorage.getItem(name))
|
||||
if (storageData) {
|
||||
storageData = { ...storageData, ...param }
|
||||
} else {
|
||||
storageData = { ...param }
|
||||
}
|
||||
localStorage.setItem(name, JSON.stringify(storageData))
|
||||
}
|
||||
|
||||
class Strings {
|
||||
hasText = function (text) {
|
||||
return !(text === undefined || text === null || text.length === 0)
|
||||
}
|
||||
hasText = function (text) {
|
||||
return !(text === undefined || text === null || text.length === 0)
|
||||
}
|
||||
|
||||
zeroPad = function zeroPad(num, minLength) {
|
||||
let str = num.toString()
|
||||
while (str.length < minLength) { str = '0' + str }
|
||||
return str
|
||||
};
|
||||
zeroPad = function zeroPad(num, minLength) {
|
||||
let str = num.toString()
|
||||
while (str.length < minLength) { str = '0' + str }
|
||||
return str
|
||||
};
|
||||
}
|
||||
|
||||
export const strings = new Strings()
|
||||
|
||||
class Times {
|
||||
formatTime = function formatTime(millis) {
|
||||
const totalSeconds = Math.floor(millis / 1000)
|
||||
formatTime = function formatTime(millis) {
|
||||
const totalSeconds = Math.floor(millis / 1000)
|
||||
|
||||
// Split into seconds and minutes
|
||||
const seconds = totalSeconds % 60
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
// Split into seconds and minutes
|
||||
const seconds = totalSeconds % 60
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
|
||||
// Format seconds and minutes as MM:SS
|
||||
return strings.zeroPad(minutes, 2) + ':' + strings.zeroPad(seconds, 2)
|
||||
};
|
||||
// Format seconds and minutes as MM:SS
|
||||
return strings.zeroPad(minutes, 2) + ':' + strings.zeroPad(seconds, 2)
|
||||
};
|
||||
}
|
||||
|
||||
export const times = new Times()
|
||||
|
@@ -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>
|
||||
<div class="command-intercept">
|
||||
<div class="command-template-management">
|
||||
<a-spin :tip="loadTip" :spinning="loading">
|
||||
<div class="command-intercept-header">
|
||||
<div class="command-template-management-header">
|
||||
<a-space>
|
||||
<a-input-search
|
||||
allow-clear
|
||||
v-model="filterName"
|
||||
v-model="searchValue"
|
||||
:placeholder="$t('placeholderSearch')"
|
||||
:style="{ width: '250px' }"
|
||||
class="ops-input ops-input-radius"
|
||||
:placeholder="$t('placeholderSearch')"
|
||||
allow-clear
|
||||
@search="updateTableData()"
|
||||
/>
|
||||
<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>
|
||||
</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-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"
|
||||
stripe
|
||||
class="ops-stripe-table"
|
||||
:data="tableData"
|
||||
stripe
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
@checkbox-change="onSelectChange"
|
||||
@checkbox-all="onSelectChange"
|
||||
@checkbox-range-end="onSelectRangeEnd"
|
||||
resizable
|
||||
:data="tableData"
|
||||
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||
:row-config="{ keyField: 'id' }"
|
||||
: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 :title="$t(`oneterm.name`)" field="name"></vxe-column>
|
||||
<vxe-column :title="$t(`oneterm.command`)" field="cmd"></vxe-column>
|
||||
<vxe-column :title="$t(`oneterm.commandIntercept.enable`)" field="enable">
|
||||
<vxe-column :title="$t('name')" field="name"></vxe-column>
|
||||
<vxe-column :title="$t('description')" field="description"></vxe-column>
|
||||
<vxe-column :title="$t('oneterm.commandFilter.commandCount')" field="description">
|
||||
<template #default="{row}">
|
||||
<a-switch :checked="Boolean(row.enable)" @change="changeEnable(row)" />
|
||||
{{ row.cmd_ids.length }}
|
||||
</template>
|
||||
</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}">
|
||||
{{ moment(row.created_at).format('YYYY-MM-DD') }}
|
||||
{{ $t(row.categoryName) }}
|
||||
</template>
|
||||
</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}">
|
||||
<a-space>
|
||||
<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-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</ops-table>
|
||||
<div class="command-intercept-pagination">
|
||||
<div class="command-template-management-pagination">
|
||||
<a-pagination
|
||||
size="small"
|
||||
show-size-changer
|
||||
@@ -82,23 +94,26 @@
|
||||
/>
|
||||
</div>
|
||||
</a-spin>
|
||||
<CommandModal ref="commandModal" @submit="updateTableData()" />
|
||||
<CommandTemplateModal ref="commandTemplateModal" @submit="updateTableData()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
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 {
|
||||
name: 'CommandIntercept',
|
||||
components: { CommandModal },
|
||||
name: 'CommandTemplateManagement',
|
||||
components: { CommandTemplateModal },
|
||||
data() {
|
||||
return {
|
||||
filterName: '',
|
||||
searchValue: '',
|
||||
currentCategory: [],
|
||||
|
||||
tableData: [],
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
@@ -113,23 +128,38 @@ export default {
|
||||
windowHeight: (state) => state.windowHeight,
|
||||
}),
|
||||
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() {
|
||||
this.updateTableData()
|
||||
},
|
||||
methods: {
|
||||
moment,
|
||||
updateTableData() {
|
||||
this.loading = true
|
||||
getCommandList({
|
||||
const category = this?.currentCategory?.length ? this.currentCategory.join(',') : undefined
|
||||
|
||||
getCommandTemplateList({
|
||||
page_index: this.currentPage,
|
||||
page_size: this.pageSize,
|
||||
search: this.filterName,
|
||||
search: this.searchValue,
|
||||
category
|
||||
})
|
||||
.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
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -150,11 +180,11 @@ export default {
|
||||
this.updateTableData()
|
||||
},
|
||||
openModal(data) {
|
||||
this.$refs.commandModal.open(data)
|
||||
this.$refs.commandTemplateModal.open(data)
|
||||
},
|
||||
deleteCommand(row) {
|
||||
deleteCommandTemplate(row) {
|
||||
this.loading = true
|
||||
deleteCommandById(row.id)
|
||||
deleteCommandTemplateById(row.id)
|
||||
.then((res) => {
|
||||
this.$message.success(this.$t('deleteSuccess'))
|
||||
this.updateTableData()
|
||||
@@ -164,17 +194,16 @@ export default {
|
||||
})
|
||||
},
|
||||
async batchDelete() {
|
||||
const that = this
|
||||
this.$confirm({
|
||||
title: that.$t('warning'),
|
||||
content: that.$t('confirmDelete'),
|
||||
async onOk() {
|
||||
title: this.$t('warning'),
|
||||
content: this.$t('confirmDelete'),
|
||||
onOk: async () => {
|
||||
let successNum = 0
|
||||
let errorNum = 0
|
||||
that.loading = true
|
||||
that.loadTip = `${that.$t('deleting')}...`
|
||||
for (let i = 0; i < that.selectedRowKeys.length; i++) {
|
||||
await deleteCommandById(that.selectedRowKeys[i], false)
|
||||
this.loading = true
|
||||
this.loadTip = `${this.$t('deleting')}...`
|
||||
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||
await deleteCommandTemplateById(this.selectedRowKeys[i])
|
||||
.then(() => {
|
||||
successNum += 1
|
||||
})
|
||||
@@ -182,32 +211,36 @@ export default {
|
||||
errorNum += 1
|
||||
})
|
||||
.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
|
||||
that.loadTip = ''
|
||||
that.selectedRowKeys = []
|
||||
that.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||
that.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||
that.$nextTick(() => {
|
||||
that.updateTableData()
|
||||
this.loading = false
|
||||
this.loadTip = ''
|
||||
this.selectedRowKeys = []
|
||||
this.$refs.opsTable.getVxetableRef().clearCheckboxRow()
|
||||
this.$refs.opsTable.getVxetableRef().clearCheckboxReserve()
|
||||
this.$nextTick(() => {
|
||||
this.updateTableData()
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
changeEnable(row) {
|
||||
putCommandById(row.id, { ...row, enable: Boolean(!row.enable) }).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>
|
||||
.command-intercept {
|
||||
.command-template-management {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
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>
|
||||
<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">
|
||||
<div class="oneterm-layout-container">
|
||||
<div class="oneterm-layout-container-header">
|
||||
|
@@ -1,160 +1,162 @@
|
||||
<template>
|
||||
<a-form-model ref="form" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-model-item>
|
||||
<span slot="label">
|
||||
<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 :label="$t('oneterm.assetList.time')">
|
||||
<DragWeektime v-model="form.ranges" :data="weekTimeData" @onClear="clearWeektime" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item :label="$t(`oneterm.assetList.effectiveDate`)" prop="startAndEnd">
|
||||
<a-range-picker v-model="form.startAndEnd" />
|
||||
<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 :label="$t(`oneterm.assetList.commandIntercept`)" prop="cmd_ids">
|
||||
<treeselect
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
'--custom-height': '32px',
|
||||
lineHeight: '32px',
|
||||
'--custom-multiple-lineHeight': '18px',
|
||||
}"
|
||||
<a-select
|
||||
v-model="form.cmd_ids"
|
||||
:multiple="true"
|
||||
:clearable="true"
|
||||
searchable
|
||||
:options="cmdList"
|
||||
:placeholder="`${$t(`placeholder2`)}`"
|
||||
:normalizer="
|
||||
(node) => {
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
}
|
||||
}
|
||||
"
|
||||
appendToBody
|
||||
:z-index="1056"
|
||||
>
|
||||
</treeselect>
|
||||
mode="multiple"
|
||||
:options="commandSelectOptions"
|
||||
:placeholder="$t('oneterm.auth.commandTip1')"
|
||||
/>
|
||||
<a-select
|
||||
mode="multiple"
|
||||
v-model="form.template_ids"
|
||||
:options="commandTemplateSelectOptions"
|
||||
:placeholder="$t('oneterm.auth.commandTip2')"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import { getCommandList } from '../../../api/command'
|
||||
import DragWeektime from '../../../components/dragWeektime'
|
||||
import weektimeData from '../../../components/dragWeektime/weektimeData'
|
||||
import _ from 'lodash'
|
||||
import momentTimezone from 'moment-timezone'
|
||||
import { getCommandList } from '@/modules/oneterm/api/command'
|
||||
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 {
|
||||
name: 'AccessAuth',
|
||||
components: { DragWeektime },
|
||||
data() {
|
||||
return {
|
||||
weektimeData,
|
||||
weekMap: {
|
||||
0: '一',
|
||||
1: '二',
|
||||
2: '三',
|
||||
3: '四',
|
||||
4: '五',
|
||||
5: '六',
|
||||
6: '七',
|
||||
},
|
||||
weekTimeData: _.cloneDeep(weekTimeData),
|
||||
form: {
|
||||
cmd_ids: undefined,
|
||||
startAndEnd: [],
|
||||
ranges: [],
|
||||
allow: true,
|
||||
timezone: momentTimezone.tz.guess(),
|
||||
cmd_ids: undefined,
|
||||
template_ids: undefined
|
||||
},
|
||||
rules: {},
|
||||
allRoleList: [],
|
||||
roleList: [],
|
||||
cmdList: [],
|
||||
commandSelectOptions: [],
|
||||
commandTemplateSelectOptions: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getCommandList({ page_index: 1 }).then((res) => {
|
||||
this.cmdList = res?.data?.list || []
|
||||
})
|
||||
this.form.ranges = this.weektimeData.map((item) => {
|
||||
return {
|
||||
id: item.row,
|
||||
week: item.week,
|
||||
value: [],
|
||||
}
|
||||
})
|
||||
computed: {
|
||||
timezoneSelectOptions() {
|
||||
const names = momentTimezone.tz.names()
|
||||
return names.map((value) => {
|
||||
return {
|
||||
value,
|
||||
label: value
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getCommandList()
|
||||
this.getCommandTemplateList()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearWeektime()
|
||||
},
|
||||
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() {
|
||||
this.weektimeData.forEach((item) => {
|
||||
this.weekTimeData.forEach((item) => {
|
||||
item.child.forEach((t) => {
|
||||
this.$set(t, 'check', false)
|
||||
})
|
||||
})
|
||||
this.form.ranges = this.weektimeData.map((item) => {
|
||||
return {
|
||||
id: item.row,
|
||||
week: item.week,
|
||||
value: [],
|
||||
}
|
||||
})
|
||||
this.form.ranges = []
|
||||
},
|
||||
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 {
|
||||
cmd_ids,
|
||||
start: startAndEnd[0]
|
||||
? moment(startAndEnd[0])
|
||||
.startOf('day')
|
||||
.format()
|
||||
: null,
|
||||
end: startAndEnd[1]
|
||||
? moment(startAndEnd[1])
|
||||
.endOf('day')
|
||||
.format()
|
||||
: null,
|
||||
ranges: ranges.map((r) => ({
|
||||
week: r.id,
|
||||
times: r.value,
|
||||
})),
|
||||
allow,
|
||||
template_ids,
|
||||
time_ranges,
|
||||
timezone
|
||||
}
|
||||
},
|
||||
async setValues(access_auth) {
|
||||
const { cmd_ids = undefined, start, end, ranges = [], allow = true } = access_auth
|
||||
async setValues({
|
||||
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 = {
|
||||
cmd_ids,
|
||||
allow,
|
||||
startAndEnd: [start ? moment(start) : null, end ? moment(end) : null],
|
||||
ranges: ranges.map((r, index) => {
|
||||
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,
|
||||
}
|
||||
}),
|
||||
template_ids,
|
||||
ranges,
|
||||
timezone
|
||||
}
|
||||
if (!ranges.length) {
|
||||
this.clearWeektime()
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<a-row>
|
||||
<a-row class="form-account">
|
||||
<a-col v-bind="colSpan">
|
||||
<table class="account-table">
|
||||
<tr>
|
||||
<th>{{ $t(`oneterm.name`) }}</th>
|
||||
<th>{{ $t(`oneterm.account`) }}</th>
|
||||
<th>{{ $t(`oneterm.assetList.grantUser`) }}</th>
|
||||
<th>{{ $t(`operation`) }}</th>
|
||||
</tr>
|
||||
<tr v-for="(item, index) in countList" :key="item.id">
|
||||
<td>
|
||||
<vxe-table
|
||||
ref="xTable"
|
||||
size="mini"
|
||||
:data="authList"
|
||||
:column-config="{ width: 200 }"
|
||||
:min-height="110"
|
||||
>
|
||||
<vxe-column field="account" :title="$t('oneterm.account')" width="190">
|
||||
<template #default="{ row }">
|
||||
<a-select
|
||||
v-model="item.name"
|
||||
v-model="row.account"
|
||||
showSearch
|
||||
:style="{
|
||||
width: '180px',
|
||||
@@ -19,7 +19,6 @@
|
||||
:placeholder="$t('placeholder2')"
|
||||
optionFilterProp="title"
|
||||
allowClear
|
||||
@change="(value) => selectAccount(value, index)"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(node, nodeIndex) in accountList"
|
||||
@@ -27,35 +26,18 @@
|
||||
:value="node.id"
|
||||
:title="node.name"
|
||||
>
|
||||
{{ node.name }}
|
||||
<a-tooltip :title="node.toolTip">
|
||||
{{ node.name }}
|
||||
<span v-if="node.account" class="select-option-name">({{ node.account }})</span>
|
||||
</a-tooltip>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</td>
|
||||
<td>
|
||||
<a-select
|
||||
v-model="item.account"
|
||||
: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>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="grantUser" :title="$t('oneterm.assetList.grantRole')" width="190">
|
||||
<template #default="{ row }">
|
||||
<EmployeeTreeSelect
|
||||
v-model="item.rids"
|
||||
v-model="row.rids"
|
||||
multiple
|
||||
:idType="2"
|
||||
departmentKey="acl_rid"
|
||||
@@ -70,28 +52,50 @@
|
||||
:limit="1"
|
||||
:otherOptions="visualRoleList"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<a-space :style="{ width: '60px' }">
|
||||
</template>
|
||||
</vxe-column>
|
||||
<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 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>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 { 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 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: 'Account',
|
||||
components: { EmployeeTreeSelect },
|
||||
components: {
|
||||
EmployeeTreeSelect,
|
||||
PermissionCheckbox
|
||||
},
|
||||
props: {
|
||||
colSpan: {
|
||||
type: Object,
|
||||
@@ -104,17 +108,36 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
accountList: [],
|
||||
countList: [{ id: uuidv4(), name: undefined, account: undefined, rids: undefined }],
|
||||
authList: [{
|
||||
id: uuidv4(),
|
||||
account: undefined,
|
||||
rids: undefined,
|
||||
permissions: { ...DEFAULT_PERMISSIONS }
|
||||
}],
|
||||
visualRoleList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadRoles()
|
||||
getAccountList({ page_index: 1 }).then((res) => {
|
||||
this.accountList = res?.data?.list || []
|
||||
})
|
||||
this.initDefaultPermissions()
|
||||
this.getRoleList()
|
||||
this.getAccountList()
|
||||
},
|
||||
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 visualRoleList = []
|
||||
@@ -137,35 +160,63 @@ export default {
|
||||
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() {
|
||||
this.countList.push({ id: uuidv4(), name: undefined, account: undefined, rids: undefined })
|
||||
},
|
||||
deleteCount(index) {
|
||||
this.countList.splice(index, 1)
|
||||
},
|
||||
selectAccount(id, index) {
|
||||
this.$nextTick(() => {
|
||||
this.$set(this.countList[index], 'name', id)
|
||||
this.$set(this.countList[index], 'account', id)
|
||||
this.authList.push({
|
||||
id: uuidv4(),
|
||||
account: undefined,
|
||||
rids: undefined,
|
||||
permissions: { ...DEFAULT_PERMISSIONS }
|
||||
})
|
||||
},
|
||||
deleteCount(id) {
|
||||
const index = this.authList.findIndex((item) => item.id === id)
|
||||
if (index !== -1) {
|
||||
this.authList.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
getValues() {
|
||||
const authorization = {}
|
||||
this.countList
|
||||
.filter((count) => count.name)
|
||||
.forEach((count) => {
|
||||
authorization[count.name] = count?.rids?.length ? count.rids.map((r) => Number(r.split('-')[1])) : []
|
||||
|
||||
this.authList
|
||||
.filter((auth) => auth.account)
|
||||
.forEach((auth) => {
|
||||
const rids = (auth?.rids || []).map((r) => Number(r.split('-')[1]))
|
||||
authorization[auth.account] = {
|
||||
rids,
|
||||
permissions: auth.permissions
|
||||
}
|
||||
})
|
||||
return { authorization }
|
||||
},
|
||||
|
||||
setValues({ authorization = {} }) {
|
||||
const authorizationList = Object.entries(authorization)
|
||||
const authorizationList = Object.entries(authorization || {})
|
||||
if (authorizationList.length) {
|
||||
this.countList = authorizationList.map(([acc, rids]) => {
|
||||
return { id: uuidv4(), name: Number(acc), account: Number(acc), rids: rids.map((r) => `employee-${r}`) }
|
||||
this.authList = authorizationList.map(([key, value]) => {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
account: Number(key),
|
||||
rids: (value?.rids || []).map((r) => `employee-${r}`),
|
||||
permissions: value.permissions
|
||||
}
|
||||
})
|
||||
} 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>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.account-table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-spacing: 20px;
|
||||
th {
|
||||
background-color: #f0f5ff;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 5px 8px;
|
||||
.form-account {
|
||||
/deep/ .ant-checkbox-wrapper {
|
||||
margin-right: 0px;
|
||||
width: 48%;
|
||||
}
|
||||
}
|
||||
.select-option-name {
|
||||
font-size: 12px;
|
||||
color: #A5A9BC;
|
||||
}
|
||||
</style>
|
||||
|
@@ -43,21 +43,21 @@
|
||||
<a-menu>
|
||||
<a-menu-item key="1" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', { parent_id: node.dataRef.id })">
|
||||
<a-icon type="plus-circle" />
|
||||
{{ $t(`oneterm.assetList.createSubCatalog`) }}
|
||||
{{ $t(`oneterm.assetList.createSubFolder`) }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" v-if="showNodeOperation(node.dataRef, ['write'])" @click="$emit('openNode', node.dataRef)">
|
||||
<ops-icon type="icon-xianxing-edit" />
|
||||
{{ $t(`oneterm.assetList.editCatalog`) }}
|
||||
{{ $t(`oneterm.assetList.editFolder`) }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="3" v-if="showNodeOperation(node.dataRef, ['delete'])" @click="deleteNode(node.dataRef)">
|
||||
<ops-icon type="veops-delete" />
|
||||
{{ $t(`oneterm.assetList.deleteCatalog`) }}
|
||||
{{ $t(`oneterm.assetList.deleteFolder`) }}
|
||||
</a-menu-item>
|
||||
<template v-if="showNodeOperation(node.dataRef, ['grant'])">
|
||||
<a-divider style="margin: 4px 0" />
|
||||
<a-menu-item key="4" @click="openGrantModal(node.dataRef)">
|
||||
<a-icon type="user-add" />
|
||||
{{ $t(`oneterm.assetList.grantCatalog`) }}
|
||||
{{ $t(`oneterm.assetList.grantFolder`) }}
|
||||
</a-menu-item>
|
||||
</template>
|
||||
</a-menu>
|
||||
@@ -125,7 +125,7 @@
|
||||
<vxe-column type="checkbox" width="60px"></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.catalogName`)" field="node_chain"> </vxe-column>
|
||||
<vxe-column :title="$t(`oneterm.assetList.folderName`)" field="node_chain"> </vxe-column>
|
||||
<vxe-column
|
||||
:title="$t(`status`)"
|
||||
field="connectable"
|
||||
@@ -242,7 +242,7 @@ export default {
|
||||
searchValue: '',
|
||||
|
||||
getRequestParams: {
|
||||
info: false
|
||||
info: true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -544,7 +544,7 @@ export default {
|
||||
authAsset() {
|
||||
const assetIds = this.selectedRowKeys.map((item) => item.id)
|
||||
this.$refs.grantModalRef.open({
|
||||
resourceId: this.selectedRowKeys?.[0]?.resource_id || '',
|
||||
resourceId: this.selectedRowKeys?.[0]?.resource_id ?? '',
|
||||
type: 'asset',
|
||||
ids: assetIds
|
||||
})
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<a-form-model-item :label="$t('oneterm.assetList.ip')" prop="ip">
|
||||
<a-input v-model="baseForm.ip" :placeholder="`${$t(`placeholder1`)}`" />
|
||||
</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
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
@@ -90,8 +90,8 @@
|
||||
import Protocol from './protocol.vue'
|
||||
import Account from './account.vue'
|
||||
import AccessAuth from './accessAuth.vue'
|
||||
import { getNodeList } from '../../../api/node'
|
||||
import { postAsset, putAssetById } from '../../../api/asset'
|
||||
import { getNodeList } from '@/modules/oneterm/api/node'
|
||||
import { postAsset, putAssetById } from '@/modules/oneterm/api/asset'
|
||||
|
||||
export default {
|
||||
name: 'CreateAsset',
|
||||
@@ -110,6 +110,7 @@ export default {
|
||||
},
|
||||
baseRules: {
|
||||
name: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
||||
ip: [{ required: true, message: `${this.$t(`placeholder1`)}` }],
|
||||
parent_id: [{ required: true, message: `${this.$t(`placeholder2`)}` }],
|
||||
},
|
||||
nodeList: [],
|
||||
@@ -141,7 +142,8 @@ export default {
|
||||
gateway_id = undefined,
|
||||
protocols = [],
|
||||
authorization = {},
|
||||
access_auth = {},
|
||||
access_time_control = {},
|
||||
asset_command_control = {}
|
||||
} = asset ?? {}
|
||||
this.assetId = id
|
||||
this.baseForm = {
|
||||
@@ -152,7 +154,10 @@ export default {
|
||||
}
|
||||
this.$refs.protocol.setValues({ gateway_id, protocols })
|
||||
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 { gateway_id, protocols } = this.$refs.protocol.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 = {
|
||||
name,
|
||||
ip: ip?.trim?.() ?? '',
|
||||
@@ -190,8 +195,16 @@ export default {
|
||||
protocols,
|
||||
gateway_id,
|
||||
authorization,
|
||||
access_auth,
|
||||
access_time_control: {
|
||||
time_ranges,
|
||||
timezone
|
||||
},
|
||||
asset_command_control: {
|
||||
cmd_ids,
|
||||
template_ids
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
if (this.assetId) {
|
||||
putAssetById(this.assetId, { ...params, id: this.assetId })
|
||||
|
@@ -16,10 +16,10 @@
|
||||
:label-col="{ span: 5 }"
|
||||
: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-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
|
||||
class="custom-treeselect custom-treeselect-white"
|
||||
:style="{
|
||||
@@ -55,77 +55,6 @@
|
||||
<a-textarea v-model="baseForm.comment" :placeholder="`${$t(`placeholder1`)}`" />
|
||||
</a-form-model-item>
|
||||
</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>
|
||||
<strong>{{ $t(`oneterm.protocol`) }}</strong>
|
||||
</p>
|
||||
@@ -134,10 +63,6 @@
|
||||
<strong>{{ $t(`oneterm.accountAuthorization`) }}</strong>
|
||||
</p>
|
||||
<Account ref="account" />
|
||||
<p>
|
||||
<strong>{{ $t(`oneterm.accessRestrictions`) }}</strong>
|
||||
</p>
|
||||
<AccessAuth ref="accessAuth" />
|
||||
<div class="custom-drawer-bottom-action">
|
||||
<a-button
|
||||
:loading="loading"
|
||||
@@ -154,17 +79,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CMDBTypeSelect from '../../../components/cmdbTypeSelect'
|
||||
import { getCITypeAttributesById } from '../../../api/otherModules'
|
||||
import FilterComp from '@/components/CMDBFilterComp'
|
||||
import { getNodeList, postNode, putNodeById } from '@/modules/oneterm/api/node'
|
||||
|
||||
import Protocol from './protocol.vue'
|
||||
import Account from './account.vue'
|
||||
import AccessAuth from './accessAuth.vue'
|
||||
import { getNodeList, postNode, putNodeById } from '../../../api/node'
|
||||
|
||||
export default {
|
||||
name: 'CreateNode',
|
||||
components: { CMDBTypeSelect, FilterComp, Protocol, Account, AccessAuth },
|
||||
components: {
|
||||
Protocol,
|
||||
Account
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
@@ -179,37 +104,15 @@ export default {
|
||||
baseRules: {
|
||||
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: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
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() {},
|
||||
@@ -225,128 +128,42 @@ export default {
|
||||
getNodeList(params).then((res) => {
|
||||
this.nodeList = res?.data?.list || []
|
||||
})
|
||||
console.log(node)
|
||||
|
||||
const {
|
||||
id = null,
|
||||
name = '',
|
||||
comment = '',
|
||||
parent_id,
|
||||
sync = {},
|
||||
gateway_id = undefined,
|
||||
protocols = [],
|
||||
authorization = {},
|
||||
access_auth = {},
|
||||
authorization = {}
|
||||
} = node ?? {}
|
||||
const { type_id = undefined, enable = true, frequency = undefined, filters = '', mapping = {} } = sync
|
||||
|
||||
this.nodeId = id
|
||||
this.baseForm = {
|
||||
name,
|
||||
parent_id: parent_id || undefined,
|
||||
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.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() {
|
||||
this.$refs.baseForm.validate((valid) => {
|
||||
if (valid) {
|
||||
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 { authorization } = this.$refs.account.getValues()
|
||||
const access_auth = this.$refs.accessAuth.getValues()
|
||||
const params = {
|
||||
name,
|
||||
comment,
|
||||
parent_id: parent_id ?? 0,
|
||||
// sync: {
|
||||
// enable,
|
||||
// filters: this.filterExp,
|
||||
// frequency,
|
||||
// mapping,
|
||||
// type_id,
|
||||
// },
|
||||
protocols,
|
||||
gateway_id,
|
||||
authorization,
|
||||
access_auth,
|
||||
authorization
|
||||
}
|
||||
console.log(params)
|
||||
|
||||
this.loading = true
|
||||
if (this.nodeId) {
|
||||
putNodeById(this.nodeId, { ...params, id: this.nodeId })
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<template #title>
|
||||
<div class="asset-list-title">
|
||||
<div class="asset-list-title-text">
|
||||
{{ $t('oneterm.assetList.assetList') }}
|
||||
{{ $t('oneterm.menu.assetManagement') }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -19,7 +19,7 @@
|
||||
<a-icon type="plus"/>
|
||||
</span>
|
||||
<span class="asset-list-title-create-text">
|
||||
{{ $t(`oneterm.assetList.createCatalog`) }}
|
||||
{{ $t(`oneterm.assetList.createFolder`) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,11 +52,6 @@ export default {
|
||||
allTreeDepAndEmp: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.$t(`oneterm.assetList.assetList`)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||
this.allTreeDepAndEmp = res
|
||||
|
@@ -181,9 +181,9 @@ export default {
|
||||
const _protocols = this.protocols.map((pro) => `${pro.value}:${pro.label}`)
|
||||
return { gateway_id, protocols: _protocols }
|
||||
},
|
||||
setValues({ gateway_id = undefined, protocols = [] }) {
|
||||
setValues({ gateway_id = undefined, protocols }) {
|
||||
this.form = { gateway_id: gateway_id || undefined }
|
||||
this.protocols = protocols.length
|
||||
this.protocols = protocols?.length
|
||||
? protocols.map((p) => ({
|
||||
id: uuidv4(),
|
||||
value: p.split(':')[0],
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="oneterm-layout">
|
||||
<div class="oneterm-header">
|
||||
<a-space>
|
||||
<span>{{ $t('oneterm.menu.gateways') }}</span>
|
||||
<span>{{ $t('oneterm.menu.gatewayManagement') }}</span>
|
||||
<a-tooltip placement="right" :title="$t('oneterm.assetList.gatewayTip')">
|
||||
<a><a-icon type="question-circle"/></a>
|
||||
</a-tooltip>
|
||||
|
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="file-management-title-right">
|
||||
<a-tooltip
|
||||
v-if="selectedRows.length"
|
||||
v-if="selectedRows.length && showDownload"
|
||||
:title="$t('oneterm.fileManagement.batchDownloadFiles')"
|
||||
>
|
||||
<a-icon
|
||||
@@ -24,6 +24,7 @@
|
||||
/>
|
||||
</a-tooltip>
|
||||
<UploadFile
|
||||
v-if="showUpload"
|
||||
:sessionId="sessionId"
|
||||
:pathStr="pathStr"
|
||||
:connectType="connectType"
|
||||
@@ -87,7 +88,11 @@
|
||||
@checkbox-all="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
|
||||
:title="$t('name')"
|
||||
field="name"
|
||||
@@ -144,6 +149,7 @@ import moment from 'moment'
|
||||
import { mapState } from 'vuex'
|
||||
import { getFileListBySessionId } from '@/modules/oneterm/api/file.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'
|
||||
|
||||
@@ -160,6 +166,10 @@ export default {
|
||||
connectType: {
|
||||
type: String,
|
||||
default: 'ssh' // 'ssh' | 'rdp'
|
||||
},
|
||||
assetPermissions: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -179,6 +189,12 @@ export default {
|
||||
}),
|
||||
pathStr() {
|
||||
return `/${this.pathList.join('/')}`
|
||||
},
|
||||
showDownload() {
|
||||
return this.assetPermissions?.[PERMISSION_TYPE.FILE_DOWNLOAD] || false
|
||||
},
|
||||
showUpload() {
|
||||
return this.assetPermissions?.[PERMISSION_TYPE.FILE_UPLOAD] || false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@@ -23,6 +23,7 @@
|
||||
ref="fileManagementDrawerRef"
|
||||
connectType="rdp"
|
||||
:sessionId="sessionId"
|
||||
:assetPermissions="assetPermissions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -32,6 +33,7 @@ import _ from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Guacamole from 'guacamole-common-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 ResolutionModal from './resolutionModal.vue'
|
||||
@@ -76,7 +78,7 @@ export default {
|
||||
type: [Object, null],
|
||||
default: null
|
||||
},
|
||||
controlConfig: {
|
||||
assetPermissions: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
@@ -154,7 +156,7 @@ export default {
|
||||
// clipboard contents received by remote desktop
|
||||
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.
|
||||
client.onclipboard = this.handleClipboardReceived
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
ref="fileManagementDrawerRef"
|
||||
connectType="ssh"
|
||||
:sessionId="connectData.sessionId"
|
||||
:assetPermissions="assetPermissions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -71,6 +72,10 @@ export default {
|
||||
preferenceSetting: {
|
||||
type: [Object, null],
|
||||
default: null
|
||||
},
|
||||
assetPermissions: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
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>
|
||||
|
||||
<script>
|
||||
import CommandIntercept from './commandIntercept/index.vue'
|
||||
import TerminalControl from './terminalControl/index.vue'
|
||||
import AccessControl from './accessControl/index.vue'
|
||||
import PublicKey from './publicKey/index.vue'
|
||||
import QuickCommand from './quickCommand/index.vue'
|
||||
import TerminalDisplay from './terminalDisplay/index.vue'
|
||||
@@ -36,8 +35,7 @@ const systemSettingTabStorageKey = 'ops_oneterm_system_setting_tab_key'
|
||||
export default {
|
||||
name: 'SystemSettings',
|
||||
components: {
|
||||
CommandIntercept,
|
||||
TerminalControl,
|
||||
AccessControl,
|
||||
PublicKey,
|
||||
QuickCommand,
|
||||
TerminalDisplay,
|
||||
@@ -70,16 +68,10 @@ export default {
|
||||
component: 'TerminalDisplay'
|
||||
},
|
||||
{
|
||||
label: 'oneterm.systemSettings.terminalControl',
|
||||
label: 'oneterm.systemSettings.accessControl',
|
||||
icon: 'basic_settings',
|
||||
key: 'terminalControl',
|
||||
component: 'TerminalControl',
|
||||
},
|
||||
{
|
||||
label: 'oneterm.systemSettings.commandIntercept',
|
||||
icon: 'a-command_interception1',
|
||||
key: 'commandIntercept',
|
||||
component: 'CommandIntercept',
|
||||
key: 'accessControl',
|
||||
component: 'AccessControl',
|
||||
},
|
||||
{
|
||||
label: 'oneterm.systemSettings.storageConfig',
|
||||
|
@@ -41,7 +41,7 @@
|
||||
<a-form-model-item :label="$t('oneterm.storageConfig.isPrimary')" prop="is_primary">
|
||||
<a-switch v-model="form.is_primary" />
|
||||
</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-form-model-item>
|
||||
|
||||
|
@@ -82,10 +82,10 @@
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</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}">
|
||||
<a-popconfirm
|
||||
:title="$t('oneterm.storageConfig.confirmEnable')"
|
||||
:title="$t('oneterm.confirmEnable')"
|
||||
@confirm="toggleEnabled(row)"
|
||||
>
|
||||
<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: '',
|
||||
|
||||
getRequestParams: {
|
||||
info: true
|
||||
info: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -53,7 +53,7 @@
|
||||
</template>
|
||||
</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
|
||||
:title="$t(`status`)"
|
||||
field="connectable"
|
||||
|
@@ -89,6 +89,7 @@
|
||||
:assetId="item.assetId"
|
||||
:accountId="item.accountId"
|
||||
:protocol="item.protocol"
|
||||
:assetPermissions="item.permissions"
|
||||
:isFullScreen="false"
|
||||
:preferenceSetting="preferenceSetting"
|
||||
@close="handleTerminalError(item)"
|
||||
@@ -103,9 +104,9 @@
|
||||
:assetId="item.assetId"
|
||||
:accountId="item.accountId"
|
||||
:protocol="item.protocol"
|
||||
:assetPermissions="item.permissions"
|
||||
:isFullScreen="false"
|
||||
:preferenceSetting="preferenceSetting"
|
||||
:controlConfig="controlConfig"
|
||||
@close="handleTerminalError(item)"
|
||||
@open="getOfUserStat()"
|
||||
@updatePreferenceSetting="getPreference"
|
||||
@@ -128,7 +129,6 @@
|
||||
:openFullScreen="openFullScreen"
|
||||
:accountList="accountList"
|
||||
:currentTabData="currentTabData"
|
||||
:controlConfig="controlConfig"
|
||||
@toggleFullScreen="toggleFullScreen"
|
||||
@openRecentSession="openRecentSession"
|
||||
@openBatchExecution="openBatchExecution"
|
||||
@@ -154,6 +154,7 @@ import { getOfUserStat } from '@/modules/oneterm/api/stat'
|
||||
import { getPreference } from '@/modules/oneterm/api/preference.js'
|
||||
import { getAccountList } from '@/modules/oneterm/api/account'
|
||||
import { getConfig } from '@/modules/oneterm/api/config'
|
||||
import { getAssetPermissions } from '@/modules/oneterm/api/asset'
|
||||
import { defaultPreferenceSetting } from '../systemSettings/terminalDisplay/constants.js'
|
||||
import { WORKSTATION_TAB_TYPE } from './constants.js'
|
||||
import FullScreenMixin from '@/modules/oneterm/mixins/fullScreenMixin'
|
||||
@@ -289,23 +290,27 @@ export default {
|
||||
this.selectedKeys = keys
|
||||
},
|
||||
|
||||
openTerminal(data) {
|
||||
async openTerminal(data) {
|
||||
const id = uuidv4()
|
||||
const accountName = this.getAccountName(data.accountId)
|
||||
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
||||
const permissions = await this.getAssetPermissions(data.assetId, data.accountId)
|
||||
|
||||
this.terminalList.push({
|
||||
...data,
|
||||
socketStatus: true,
|
||||
id,
|
||||
name,
|
||||
type: this.getConnectType(data.protocolType)
|
||||
type: this.getConnectType(data.protocolType),
|
||||
permissions: permissions?.[data.accountId] || {}
|
||||
})
|
||||
|
||||
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 accountName = this.getAccountName(id)
|
||||
const name = accountName ? `${accountName}@${data.assetName}` : data.assetName
|
||||
@@ -318,13 +323,35 @@ export default {
|
||||
accountId: id,
|
||||
socketStatus: true,
|
||||
id: uuidv4(),
|
||||
type: this.getConnectType(data.protocolType)
|
||||
type: this.getConnectType(data.protocolType),
|
||||
permissions: permissions?.[id] || {}
|
||||
}
|
||||
})
|
||||
|
||||
this.tabActiveKey = newList[0].id
|
||||
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) {
|
||||
if (item.id === this.tabActiveKey) {
|
||||
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',
|
||||
FILE_MANAGEMENT: 'fileManagement',
|
||||
CLIPBOARD: 'clipboard',
|
||||
RESOLUTION: 'resolution'
|
||||
RESOLUTION: 'resolution',
|
||||
SHARE: 'share'
|
||||
}
|
||||
|
@@ -60,6 +60,17 @@
|
||||
class="workstation-operation-menu-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
|
||||
v-if="controlDisplayList.includes(OPERATION_MENU_TYPE.QUICK_COMMAND)"
|
||||
:title="$t('oneterm.quickCommand.name')"
|
||||
@@ -109,19 +120,26 @@
|
||||
:accountList="accountList"
|
||||
@ok="openBatchExecution"
|
||||
/>
|
||||
<ShareAssetModal
|
||||
ref="shareAssetModalRef"
|
||||
:assetData="currentTabData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { WORKSTATION_TAB_TYPE } from '@/modules/oneterm/views/workStation/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 ShareAssetModal from './shareAssetModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'OperationMenu',
|
||||
components: {
|
||||
ChooseAssetsModal
|
||||
ChooseAssetsModal,
|
||||
ShareAssetModal
|
||||
},
|
||||
props: {
|
||||
openFullScreen: {
|
||||
@@ -135,10 +153,6 @@ export default {
|
||||
currentTabData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
controlConfig: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -155,6 +169,8 @@ export default {
|
||||
return this.currentTabData?.type === WORKSTATION_TAB_TYPE.GUACAMOLE
|
||||
},
|
||||
controlDisplayList() {
|
||||
const assetPermissions = this.currentTabData?.permissions || {}
|
||||
|
||||
const controlDisplayList = [
|
||||
OPERATION_MENU_TYPE.FULL_SCREEN,
|
||||
OPERATION_MENU_TYPE.RECENT_SESSION,
|
||||
@@ -167,15 +183,22 @@ export default {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
const showClipboard = this?.controlConfig?.[`${this.currentTabData?.protocolType}_config`]?.paste
|
||||
const showClipboard = assetPermissions?.[PERMISSION_TYPE.PASTE] || false
|
||||
if (this.isGuacamole && showClipboard) {
|
||||
controlDisplayList.push(OPERATION_MENU_TYPE.CLIPBOARD)
|
||||
}
|
||||
@@ -211,6 +234,9 @@ export default {
|
||||
},
|
||||
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