feat(ui):i18n (#347)

* feat(acl-ui):i18n

* feat(base-ui):i18n

* feat(cmdb-ui):i18n
This commit is contained in:
wang-liang0615 2024-01-02 17:53:07 +08:00 committed by GitHub
parent ace160ae19
commit e429ad59ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
178 changed files with 21927 additions and 19433 deletions

View File

@ -1,5 +1,5 @@
<template>
<a-config-provider :locale="locale">
<a-config-provider :locale="antdLocale">
<div id="app" :class="{ 'ops-fullscreen': isOpsFullScreen, 'ops-only-topmenu': isOpsOnlyTopMenu }">
<router-view v-if="alive" />
</div>
@ -7,8 +7,9 @@
</template>
<script>
import { mapActions } from 'vuex'
import { mapState, mapActions, mapMutations } from 'vuex'
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import enUS from 'ant-design-vue/lib/locale-provider/en_US'
import { AppDeviceEnquire } from '@/utils/mixin'
import { debounce } from './utils/util'
@ -24,20 +25,28 @@ export default {
},
data() {
return {
locale: zhCN,
alive: true,
timer: null,
}
},
computed: {
...mapState(['locale']),
antdLocale() {
if (this.locale === 'zh') {
return zhCN
}
return enUS
},
isOpsFullScreen() {
return this.$route.name === 'cmdb_screen'
return ['cmdb_screen'].includes(this.$route.name)
},
isOpsOnlyTopMenu() {
return ['fullscreen_index', 'setting_person'].includes(this.$route.name)
return ['fullscreen_index', 'setting_person', 'notice_center'].includes(this.$route.name)
},
},
created() {
this.SET_LOCALE(localStorage.getItem('ops_locale') || 'zh')
this.$i18n.locale = localStorage.getItem('ops_locale') || 'zh'
this.timer = setInterval(() => {
this.setTime(new Date().getTime())
}, 1000)
@ -184,6 +193,7 @@ export default {
},
methods: {
...mapActions(['setTime']),
...mapMutations(['SET_LOCALE']),
reload() {
this.alive = false
this.$nextTick(() => {

View File

@ -1,29 +1,37 @@
export const ruleTypeList = [
{ value: 'and', label: '与' },
{ value: 'or', label: '或' },
import i18n from '@/lang'
export const ruleTypeList = () => {
return [
{ value: 'and', label: i18n.t('cmdbFilterComp.and') },
{ value: 'or', label: i18n.t('cmdbFilterComp.or') },
// { value: 'not', label: '非' },
]
]
}
export const expList = [
{ value: 'is', label: '等于' },
{ value: '~is', label: '不等于' },
{ value: 'contain', label: '包含' },
{ value: '~contain', label: '不包含' },
{ value: 'start_with', label: '以...开始' },
{ value: '~start_with', label: '不以...开始' },
{ value: 'end_with', label: '以...结束' },
{ value: '~end_with', label: '不以...结束' },
{ value: '~value', label: '为空' }, // 为空的定义有点绕
{ value: 'value', label: '不为空' },
]
export const expList = () => {
return [
{ value: 'is', label: i18n.t('cmdbFilterComp.is') },
{ value: '~is', label: i18n.t('cmdbFilterComp.~is') },
{ value: 'contain', label: i18n.t('cmdbFilterComp.contain') },
{ value: '~contain', label: i18n.t('cmdbFilterComp.~contain') },
{ value: 'start_with', label: i18n.t('cmdbFilterComp.start_with') },
{ value: '~start_with', label: i18n.t('cmdbFilterComp.~start_with') },
{ value: 'end_with', label: i18n.t('cmdbFilterComp.end_with') },
{ value: '~end_with', label: i18n.t('cmdbFilterComp.~end_with') },
{ value: '~value', label: i18n.t('cmdbFilterComp.~value') }, // 为空的定义有点绕
{ value: 'value', label: i18n.t('cmdbFilterComp.value') },
]
}
export const advancedExpList = [
{ value: 'in', label: 'in查询' },
{ value: '~in', label: '非in查询' },
{ value: 'range', label: '范围' },
{ value: '~range', label: '范围外' },
{ value: 'compare', label: '比较' },
]
export const advancedExpList = () => {
return [
{ value: 'in', label: i18n.t('cmdbFilterComp.in') },
{ value: '~in', label: i18n.t('cmdbFilterComp.~in') },
{ value: 'range', label: i18n.t('cmdbFilterComp.range') },
{ value: '~range', label: i18n.t('cmdbFilterComp.~range') },
{ value: 'compare', label: i18n.t('cmdbFilterComp.compare') },
]
}
export const compareTypeList = [
{ value: '1', label: '>' },

View File

@ -1,11 +1,11 @@
<template>
<div>
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
<div :style="{ width: '50px', height: '24px', position: 'relative' }">
<div :style="{ width: '70px', height: '24px', position: 'relative' }">
<treeselect
v-if="index"
class="custom-treeselect"
:style="{ width: '50px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
:style="{ width: '70px', '--custom-height': '24px', position: 'absolute', top: '-17px', left: 0 }"
v-model="item.type"
:multiple="false"
:clearable="false"
@ -91,7 +91,7 @@
searchable
v-if="isChoiceByProperty(item.property) && (item.exp === 'is' || item.exp === '~is')"
:options="getChoiceValueByProperty(item.property)"
placeholder="请选择"
:placeholder="$t('placeholder2')"
:normalizer="
(node) => {
return {
@ -119,9 +119,21 @@
v-else-if="item.exp === 'range' || item.exp === '~range'"
:style="{ width: '175px' }"
>
<a-input class="ops-input" size="small" v-model="item.min" :style="{ width: '78px' }" placeholder="最小值" />
<a-input
class="ops-input"
size="small"
v-model="item.min"
:style="{ width: '78px' }"
:placeholder="$t('min')"
/>
~
<a-input class="ops-input" size="small" v-model="item.max" :style="{ width: '78px' }" placeholder="最大值" />
<a-input
class="ops-input"
size="small"
v-model="item.max"
:style="{ width: '78px' }"
:placeholder="$t('max')"
/>
</a-input-group>
<a-input-group size="small" compact v-else-if="item.exp === 'compare'" :style="{ width: '175px' }">
<treeselect
@ -151,19 +163,23 @@
v-else-if="item.exp !== 'value' && item.exp !== '~value'"
size="small"
v-model="item.value"
:placeholder="item.exp === 'in' || item.exp === '~in' ? '以 ; 分隔' : ''"
:placeholder="item.exp === 'in' || item.exp === '~in' ? $t('cmdbFilterComp.split', { separator: ';' }) : ''"
class="ops-input"
:style="{ width: '175px' }"
></a-input>
<div v-else :style="{ width: '175px' }"></div>
<a-tooltip title="复制">
<a-tooltip :title="$t('copy')">
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="icon-xianxing-copy"/></a>
</a-tooltip>
<a-tooltip title="删除">
<a-tooltip :title="$t('delete')">
<a class="operation" @click="handleDeleteRule(item)"><ops-icon type="icon-xianxing-delete"/></a>
</a-tooltip>
<a-tooltip :title="$t('cmdbFilterComp.addHere')" :needAddHere="needAddHere">
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
</a-tooltip>
</a-space>
<div class="table-filter-add">
<a @click="handleAddRule">+ 新增</a>
<a @click="handleAddRule">+ {{ $t('new') }}</a>
</div>
</div>
</template>
@ -191,12 +207,13 @@ export default {
required: true,
default: () => [],
},
needAddHere: {
type: Boolean,
default: false,
},
},
data() {
return {
ruleTypeList,
expList,
advancedExpList,
compareTypeList,
}
},
@ -210,6 +227,15 @@ export default {
return val
},
},
ruleTypeList() {
return ruleTypeList()
},
expList() {
return expList()
},
advancedExpList() {
return advancedExpList()
},
},
methods: {
getExpListByProperty(property) {
@ -217,10 +243,10 @@ export default {
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
return [
{ value: 'is', label: '等于' },
{ value: '~is', label: '不等于' },
{ value: '~value', label: '为空' }, // 为空的定义有点绕
{ value: 'value', label: '不为空' },
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
{ value: '~value', label: this.$t('cmdbFilterComp.~value') }, // 为空的定义有点绕
{ value: 'value', label: this.$t('cmdbFilterComp.value') },
]
}
return this.expList
@ -255,6 +281,19 @@ export default {
}
this.$emit('change', this.ruleList)
},
handleAddRuleAt(item) {
const idx = this.ruleList.findIndex((r) => r.id === item.id)
if (idx > -1) {
this.ruleList.splice(idx, 0, {
id: uuidv4(),
type: 'and',
property: this.canSearchPreferenceAttrList[0]?.name,
exp: 'is',
value: null,
})
}
this.$emit('change', this.ruleList)
},
getChoiceValueByProperty(property) {
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
if (_find) {

View File

@ -9,23 +9,25 @@
@visibleChange="visibleChange"
>
<slot name="popover_item">
<a-button type="primary" ghost>条件过滤<a-icon type="filter"/></a-button>
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
</slot>
<template slot="content">
<Expression
:needAddHere="needAddHere"
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
/>
<a-divider :style="{ margin: '10px 0' }" />
<div style="width:534px">
<div style="width:554px">
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
<a-button type="primary" size="small" @click="handleSubmit">确定</a-button>
<a-button size="small" @click="handleClear">清空</a-button>
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
</a-space>
</div>
</template>
</a-popover>
<Expression
:needAddHere="needAddHere"
v-else
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
@ -63,6 +65,10 @@ export default {
type: Boolean,
default: true,
},
needAddHere: {
type: Boolean,
default: false,
},
},
data() {
return {

View File

@ -1,8 +1,10 @@
export const iconTypeList = [
import i18n from '@/lang'
export const iconTypeList = () => [
// { value: '0', label: '常用' },
{ value: '1', label: '线性' },
{ value: '2', label: '实底' },
{ value: '3', label: '多色' }
{ value: '1', label: i18n.t('customIconSelect.outlined') },
{ value: '2', label: i18n.t('customIconSelect.filled') },
{ value: '3', label: i18n.t('customIconSelect.multicolor') }
]
export const commonIconList = ['changyong-ubuntu',

View File

@ -16,7 +16,7 @@
{{ item.label }}
</div>
<div :class="`${currentIconType === '4' ? 'selected' : ''}`" @click="handleChangeIconType('4')">
自定义
{{ this.$t('customIconSelect.custom') }}
</div>
<a-upload
slot="description"
@ -26,7 +26,7 @@
accept=".svg,.png,.jpg,.jpeg"
v-if="currentIconType === '4'"
>
<a-button icon="plus" size="small" type="primary">添加</a-button>
<a-button icon="plus" size="small" type="primary">{{ $t('add') }}</a-button>
</a-upload>
</div>
<div class="custom-icon-select-popover-content">
@ -55,11 +55,11 @@
@click="clickCustomIcon(icon)"
>
<div class="custom-icon-select-popover-content-img-box">
<img :src="`/api/common-setting/v1/file/${icon.data.url}`" />
<img v-if="icon.data && icon.data.url" :src="`/api/common-setting/v1/file/${icon.data.url}`" />
<a-popconfirm
overlayClassName="custom-icon-select-confirm-popover"
:getPopupContainer="(trigger) => trigger.parentNode"
title="确认删除?"
:title="$t('confirmDelete')"
@confirm="(e) => deleteIcon(e, icon)"
@cancel="
(e) => {
@ -102,27 +102,27 @@
</template>
<a-form class="custom-icon-select-form" :form="form" v-show="currentIconType === '4' && formVisible">
<a-form-item
label="名称"
:label="$t('name')"
:labelCol="{ span: 4 }"
:wrapperCol="{ span: 16 }"
><a-input
v-decorator="['name', { rules: [{ required: true, message: '请输入名称' }] }]"
v-decorator="['name', { rules: [{ required: true, message: $t('placeholder1') }] }]"
/></a-form-item>
<a-form-item label="预览" :labelCol="{ span: 4 }">
<a-form-item :label="$t('customIconSelect.preview')" :labelCol="{ span: 4 }">
<div class="custom-icon-select-form-img">
<img :src="formImg" />
</div>
</a-form-item>
<a-form-item label=" " :colon="false" :labelCol="{ span: 16 }">
<a-space>
<a-button size="small" @click="handleCancel">取消</a-button>
<a-button size="small" type="primary" @click="handleOk">确定</a-button>
<a-button size="small" @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button size="small" type="primary" @click="handleOk">{{ $t('confirm') }}</a-button>
</a-space>
</a-form-item>
</a-form>
</div>
<div class="custom-icon-select-block" id="custom-icon-select-block" @click="showSelect">
<div class="custom-icon-select-block" :id="`custom-icon-select-block-${uuid}`" @click="showSelect">
<img v-if="value.id && value.url" :src="`/api/common-setting/v1/file/${value.url}`" />
<ops-icon
v-else
@ -134,6 +134,7 @@
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import { ColorPicker } from 'element-ui'
import {
iconTypeList,
@ -166,7 +167,6 @@ export default {
data() {
return {
form: this.$form.createForm(this),
iconTypeList,
commonIconList,
linearIconList,
fillIconList,
@ -177,6 +177,7 @@ export default {
formVisible: false,
formImg: null,
file: null,
uuid: uuidv4(),
}
},
computed: {
@ -200,6 +201,9 @@ export default {
const splitFileName = this.file.name.split('.')
return splitFileName.splice(0, splitFileName.length - 1).join('')
},
iconTypeList() {
return iconTypeList()
},
},
mounted() {
document.addEventListener('click', this.eventListener)
@ -217,7 +221,7 @@ export default {
eventListener(e) {
if (this.visible) {
const dom = document.getElementById(`custom-icon-select-popover`)
const dom_icon = document.getElementById(`custom-icon-select-block`)
const dom_icon = document.getElementById(`custom-icon-select-block-${this.uuid}`)
e.stopPropagation()
e.preventDefault()
if (dom) {
@ -249,12 +253,11 @@ export default {
color: '',
})
} else {
this.$emit('change', { name: icon.data.name, id: icon.id, url: icon.data.url })
this.$emit('change', { name: icon.data.name, id: icon.id, url: icon?.data?.url })
}
},
showSelect() {
this.visible = true
console.log(this.value)
if (!this.value.name) {
this.currentIconType = '3'
return
@ -278,7 +281,7 @@ export default {
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('图片大小不可超过2MB!')
this.$message.error(this.$t('customIconSelect.sizeLimit'))
return false
}
@ -306,7 +309,7 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
addFileData('ops-custom-icon', { data: { name: values.name, url: res.file_name } }).then(() => {
this.$message.success('上传成功!')
this.$message.success(this.$t('uploadSuccess'))
this.handleCancel()
this.getFileData()
})
@ -318,7 +321,7 @@ export default {
e.stopPropagation()
e.preventDefault()
deleteFileData('ops-custom-icon', icon.id).then(() => {
this.$message.success('删除成功!')
this.$message.success(this.$t('deleteSuccess'))
this.handleCancel()
this.getFileData()
})

View File

@ -6,17 +6,17 @@
:flat="true"
:multiple="true"
:options="employeeTreeSelectOption"
placeholder="请输入搜索内容"
:placeholder="$t('placeholderSearch')"
v-model="treeValue"
:max-height="height - 50"
noChildrenText=""
noOptionsText=""
:clearable="false"
:always-open="true"
:default-expand-level="1"
:default-expand-level="showInternship ? 0 : 1"
:class="{ 'employee-transfer': true, 'employee-transfer-has-input': !!inputValue }"
@search-change="changeInputValue"
noResultsText="暂无数据"
:noResultsText="$t('noData')"
openDirection="below"
>
</treeselect>
@ -85,6 +85,10 @@ export default {
type: Boolean,
default: false,
},
showInternship: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -99,13 +103,22 @@ export default {
},
computed: {
employeeTreeSelectOption() {
return formatOption(
const formatOptions = formatOption(
this.allTreeDepAndEmp,
2,
this.isDisabledAllCompany,
this.uniqueKey || 'department_id',
this.uniqueKey || 'employee_id'
)
if (this.showInternship) {
formatOptions.push(
...[
{ id: -2, label: '全职' },
{ id: -3, label: '实习生' },
]
)
}
return formatOptions
},
allTreeDepAndEmp() {
if (this.getDataBySelf) {
@ -148,12 +161,16 @@ export default {
const department = []
const user = []
this.rightData.forEach((item) => {
if (item === -2 || item === -3) {
department.push(item)
} else {
const _split = item.split('-')
if (_split[0] === 'department') {
department.push(Number(_split[1]))
} else {
user.push(Number(_split[1]))
}
}
})
const _idx = department.findIndex((item) => item === 0)
if (_idx > -1) {
@ -191,6 +208,12 @@ export default {
}
},
getLabel(id) {
if (id === -2) {
return '全职'
}
if (id === -3) {
return '实习生'
}
const _split = id.split('-')
const type = _split[0]
const _id = Number(_split[1])

View File

@ -161,6 +161,9 @@ export default {
}
return null
},
renderI18n(title) {
return this.$t(`${title}`)
},
renderMenuItem(menu) {
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
const isShowGrant = menu.path.substr(0, 20) === '/cmdb/relationviews/'
@ -183,7 +186,7 @@ export default {
<tag {...{ props, attrs }}>
{this.renderIcon({ icon: menu.meta.icon, customIcon: menu.meta.customIcon, name: menu.meta.name, typeId: menu.meta.typeId, routeName: menu.name, selectedIcon: menu.meta.selectedIcon, })}
<span>
<span class={menu.meta.title.length > 10 ? 'scroll' : ''}>{menu.meta.title}</span>
<span class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>{this.renderI18n(menu.meta.title)}</span>
{isShowDot &&
<a-popover
overlayClassName="custom-menu-extra-submenu"
@ -217,7 +220,7 @@ export default {
<SubMenu {...{ key: menu.path }}>
<span slot="title">
{this.renderIcon({ icon: menu.meta.icon, selectedIcon: menu.meta.selectedIcon, routeName: menu.name })}
<span>{menu.meta.title}</span>
<span>{this.renderI18n(menu.meta.title)}</span>
</span>
{itemArr}
</SubMenu>

View File

@ -3,9 +3,9 @@
<slot></slot>
<template #empty>
<slot name="empty">
<div>
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
<div>暂无数据</div>
<div :style="{ paddingTop: '10px' }">
<img :style="{ width: '100px', height: '90px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</slot>
</template>

View File

@ -3,12 +3,12 @@
<a-switch
class="role-transfer-switch"
v-model="isUserRole"
checked-children="用户"
un-checked-children="虚拟"
:checked-children="$t('user')"
:un-checked-children="$t('visual')"
@change="loadRoles"
/>
<div class="role-transfer-left">
<a-input placeholder="请输入搜索内容" v-model="searchValue" />
<a-input :placeholder="$t('placeholderSearch')" v-model="searchValue" />
<div v-for="item in filterAllRoles" :key="item.id" @click="handleSelectedLeft(item.id)">
<a-checkbox :checked="selectedLeft.includes(item.id)" />
<div :title="item.name" class="role-transfer-left-role">{{ item.name }}</div>

View File

@ -10,9 +10,10 @@
>
<a-icon type="setting" />
</span>
<span class="locale" @click="changeLang">{{ locale === 'zh' ? 'English' : '中文' }}</span>
<a-popover
trigger="click"
:overlayStyle="{ width: '120px' }"
:overlayStyle="{ width: '150px' }"
placement="bottomRight"
overlayClassName="custom-user"
>
@ -20,12 +21,12 @@
<router-link :to="{ name: 'setting_person' }" :style="{ color: '#000000a6' }">
<div class="custom-user-item">
<a-icon type="user" :style="{ marginRight: '10px' }" />
<span>个人中心</span>
<span>{{ $t('topMenu.personalCenter') }}</span>
</div>
</router-link>
<div @click="handleLogout" class="custom-user-item">
<a-icon type="logout" :style="{ marginRight: '10px' }" />
<span>退出登录</span>
<span>{{ $t('topMenu.logout') }}</span>
</div>
</template>
<span class="action ant-dropdown-link user-dropdown-menu">
@ -44,8 +45,9 @@
</template>
<script>
import { mapState, mapActions, mapGetters, mapMutations } from 'vuex'
import DocumentLink from './DocumentLink.vue'
import { mapState, mapActions, mapGetters } from 'vuex'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
export default {
name: 'UserMenu',
@ -53,21 +55,24 @@ export default {
DocumentLink,
},
computed: {
...mapState(['user']),
...mapState(['user', 'locale']),
hasBackendPermission() {
return this.user?.roles?.permissions.includes('acl_admin', 'backend_admin') || false
return this.user?.detailPermissions?.backend?.length
},
},
methods: {
...mapActions(['Logout']),
...mapGetters(['nickname', 'avatar']),
...mapMutations(['SET_LOCALE']),
handleLogout() {
const that = this
this.$confirm({
title: '提示',
content: '确认注销登录 ?',
content: '真的要注销登录吗 ?',
onOk() {
// localStorage.removeItem('ops_cityps_currentId')
localStorage.clear()
return that.Logout()
},
onCancel() {},
@ -76,9 +81,22 @@ export default {
handleClick() {
this.$router.push('/setting')
},
changeLang() {
if (this.locale === 'zh') {
this.SET_LOCALE('en')
this.$i18n.locale = 'en'
} else {
this.SET_LOCALE('zh')
this.$i18n.locale = 'zh'
}
this.$nextTick(() => {
setDocumentTitle(`${this.$t(this.$route.meta.title)} - ${domTitle}`)
})
},
},
}
</script>
<style lang="less">
@import '~@/style/static.less';
.color {
@ -98,4 +116,11 @@ export default {
color: #000000a6;
}
}
.locale {
cursor: pointer;
&:hover {
color: #custom_colors[color_1];
}
}
</style>

View File

@ -38,11 +38,18 @@ import CardTitle from '@/components/CardTitle'
import ElementUI from 'element-ui'
import Treeselect from '@riophae/vue-treeselect'
import OpsTable from '@/components/OpsTable'
import VueI18n from 'vue-i18n'
import i18n from '@/lang'
Vue.config.productionTip = false
Vue.prototype.$bus = EventBus
VXETable.setup({
i18n: (key, args) => i18n.t(key, args)
})
Vue.use(VXETable)
VXETable.use(VXETablePluginExportXLSX)
Vue.use(VueI18n)
Vue.config.productionTip = false
@ -75,4 +82,3 @@ Vue.component('CustomRadio', CustomRadio)
Vue.component('CardTitle', CardTitle)
Vue.component('Treeselect', Treeselect)
Vue.component('OpsTable', OpsTable)

View File

@ -7,6 +7,7 @@ import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from './store/global/mutation-types'
import i18n from '@/lang'
NProgress.configure({ showSpinner: false })
@ -17,7 +18,7 @@ const whitePath = ['/user/login', '/user/logout', '/user/register', '/api/sso/lo
// 登录页面处理处理 是否使用单点登录
router.beforeEach(async (to, from, next) => {
NProgress.start() // start progress bar
to.meta && (!!to.meta.title && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
to.meta && (!!to.meta.title && setDocumentTitle(`${i18n.t(to.meta.title)} - ${domTitle}`))
const authed = store.state.authed
const auth_type = localStorage.getItem('ops_auth_type')

140
cmdb-ui/src/lang/en.js Normal file
View File

@ -0,0 +1,140 @@
import cmdb_en from '@/modules/cmdb/lang/en.js'
import cs_en from '../views/setting/lang/en.js'
import acl_en from '@/modules/acl/lang/en.js'
export default {
screen: 'Big Screen',
dashboard: 'Dashboard',
admin: 'Admin',
user: 'User',
role: 'Role',
operation: 'Operation',
login: 'Login',
refresh: 'Refresh',
cancel: 'Cancel',
confirm: 'Confirm',
create: 'Create',
edit: 'Edit',
deleting: 'Deleting',
deletingTip: 'Deleting, total of {total}, {successNum} succeeded, {errorNum} failed',
grant: 'Grant',
login_at: 'Login At',
logout_at: 'Logout At',
createSuccess: 'Create Success',
editSuccess: 'edit Success',
warning: 'Warning',
export: 'Export',
placeholderSearch: 'Please Search',
success: 'Success',
fail: 'Fail',
browser: 'Browser',
status: 'Status',
type: 'Type',
description: 'Description',
new: 'New',
add: 'Add',
define: 'Define',
update: 'Update',
clear: 'Clear',
delete: 'Delete',
copy: 'Copy',
created_at: 'Created At',
updated_at: 'Updated At',
placeholder1: 'Please Input',
placeholder2: 'Please Select',
confirmDelete: 'Confirm delete?',
confirmDelete2: 'Confirm delete [{name}]?',
query: 'Query',
search: 'Search',
hide: 'Hide',
expand: 'Expand',
save: 'Save',
submit: 'Submit',
upload: 'Import',
download: 'Export',
name: 'Name',
alias: 'Alias',
desc: 'Description',
other: 'Other',
icon: 'Icon',
addSuccess: 'Added successfully',
uploadSuccess: 'Import successfully',
saveSuccess: 'Save successfully',
copySuccess: 'Copy successfully',
updateSuccess: 'Updated successfully',
deleteSuccess: 'Deleted successfully',
operateSuccess: 'The operation was successful',
noPermission: 'No Permission',
noData: 'No Data',
seconds: 'Seconds',
createdAt: 'Created At',
updatedAt: 'Updated At',
deletedAt: 'Deleted At',
required: 'required',
email: 'Email',
wechat: 'Wechat',
dingding: 'DingTalk',
feishu: 'Feishu',
bot: 'Robot',
checkAll: 'Select All',
loading: 'Loading...',
view: 'View',
reset: 'Reset',
yes: 'Yes',
no: 'No',
all: 'All',
selectRows: 'Selected: {rows} items',
itemsPerPage: '/page',
'星期一': 'Monday',
'星期二': 'Tuesday',
'星期三': 'Wednesday',
'星期四': 'Thursday',
'星期五': 'Friday',
'星期六': 'Saturday',
'星期日': 'Sunday',
hour: 'hour',
'items/page': '{items} items/page',
max: 'Max',
min: 'Min',
visual: 'Visual',
pagination: {
total: '{range0}-{range1} of {total} items'
},
topMenu: {
personalCenter: 'Personal Center',
logout: 'logout',
},
cmdbFilterComp: {
conditionFilter: 'Conditional filtering',
and: 'and',
or: 'or',
is: 'equal',
'~is': 'not equal',
contain: 'contain',
'~contain': 'not contain',
start_with: 'start_with',
'~start_with': 'not start_with',
end_with: 'end_with',
'~end_with': 'not end_with',
'~value': 'null',
value: 'not null',
in: 'in',
'~in': 'not in',
range: 'range',
'~range': 'out of range',
compare: 'compare',
addHere: 'Add Here',
split: 'split by {separator}'
},
customIconSelect: {
outlined: 'Outlined',
filled: 'Filled',
multicolor: 'Multicolor',
custom: 'Custom',
preview: 'Preview',
sizeLimit: 'The image size cannot exceed 2MB!'
},
cmdb: cmdb_en,
cs: cs_en,
acl: acl_en,
}

18
cmdb-ui/src/lang/index.js Normal file
View File

@ -0,0 +1,18 @@
import VueI18n from 'vue-i18n'
import zh from './zh'
import en from './en'
import Vue from 'vue'
import zhCN from 'vxe-table/lib/locale/lang/zh-CN'
import enUS from 'vxe-table/lib/locale/lang/en-US'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'zh', // 初始化中文
messages: {
'zh': { ...zh, ...zhCN },
'en': { ...en, ...enUS },
},
silentTranslationWarn: true
})
export default i18n

140
cmdb-ui/src/lang/zh.js Normal file
View File

@ -0,0 +1,140 @@
import cmdb_zh from '@/modules/cmdb/lang/zh.js'
import cs_zh from '../views/setting/lang/zh.js'
import acl_zh from '@/modules/acl/lang/zh.js'
export default {
screen: '大屏',
dashboard: '仪表盘',
admin: '管理员',
user: '用户',
role: '角色',
operation: '操作',
login: '登录',
refresh: '刷新',
cancel: '取消',
confirm: '确定',
create: '创建',
edit: '编辑',
deleting: '正在删除',
deletingTip: '正在删除,共{total}个,成功{successNum}个,失败{errorNum}个',
grant: '授权',
login_at: '登录时间',
logout_at: '登出时间',
createSuccess: '创建成功',
editSuccess: '修改成功',
warning: '警告',
export: '导出',
placeholderSearch: '请查找',
success: '成功',
fail: '失败',
browser: '浏览器',
status: '状态',
type: '类型',
description: '描述',
new: '新增',
add: '添加',
define: '定义',
update: '修改',
clear: '清空',
delete: '删除',
copy: '复制',
created_at: '创建日期',
updated_at: '更新日期',
placeholder1: '请输入',
placeholder2: '请选择',
confirmDelete: '确认删除?',
confirmDelete2: '确认删除【{name}】?',
query: '查询',
search: '搜索',
hide: '隐藏',
expand: '展开',
save: '保存',
submit: '提交',
upload: '导入',
download: '导出',
name: '名称',
alias: '别名',
desc: '描述',
other: '其他',
icon: '图标',
addSuccess: '新增成功',
uploadSuccess: '导入成功',
saveSuccess: '保存成功',
copySuccess: '复制成功',
updateSuccess: '更新成功',
deleteSuccess: '删除成功',
operateSuccess: '操作成功',
noPermission: '权限不足',
noData: '暂无数据',
seconds: '秒',
createdAt: '创建时间',
updatedAt: '更新时间',
deletedAt: '删除时间',
required: '必须',
email: '邮件',
wechat: '企业微信',
dingding: '钉钉',
feishu: '飞书',
bot: '机器人',
checkAll: '全选',
loading: '加载中...',
view: '查看',
reset: '重置',
yes: '是',
no: '否',
all: '全部',
selectRows: '选取:{rows} 项',
itemsPerPage: '/页',
'星期一': '星期一',
'星期二': '星期二',
'星期三': '星期三',
'星期四': '星期四',
'星期五': '星期五',
'星期六': '星期六',
'星期日': '星期日',
hour: '小时',
'items/page': '{items} 条/页',
max: '最大值',
min: '最小值',
visual: '虚拟',
pagination: {
total: '当前展示 {range0}-{range1} 条数据, 共 {total} 条'
},
topMenu: {
personalCenter: '个人中心',
logout: '退出登录',
},
cmdbFilterComp: {
conditionFilter: '条件过滤',
and: '与',
or: '或',
is: '等于',
'~is': '不等于',
contain: '包含',
'~contain': '不包含',
start_with: '以...开始',
'~start_with': '不以...开始',
end_with: '以...结束',
'~end_with': '不以...结束',
'~value': '为空',
value: '不为空',
in: 'in查询',
'~in': '非in查询',
range: '范围',
'~range': '范围外',
compare: '比较',
addHere: '在此处添加',
split: '以 {separator} 分隔'
},
customIconSelect: {
outlined: '线框',
filled: '实底',
multicolor: '多色',
custom: '自定义',
preview: '预览',
sizeLimit: '图片大小不可超过2MB'
},
cmdb: cmdb_zh,
cs: cs_zh,
acl: acl_zh,
}

View File

@ -10,6 +10,7 @@ import './guard' // guard permission control
import './utils/filter' // global filter
import Setting from './config/setting'
import { Icon } from 'ant-design-vue'
import i18n from './lang'
import iconFont from '../public/iconfont/iconfont'
@ -22,6 +23,7 @@ async function start() {
const _vue = new Vue({
router,
store,
i18n,
created: bootstrap,
render: h => h(App)
}).$mount('#app')

View File

@ -0,0 +1,125 @@
const acl_en = {
date: 'Date',
operator: 'Operator',
resource: 'Resource',
resourceType: 'Resource Type',
addResourceType: 'Add Resource Type',
app: 'App',
operateTime: 'Operate Time',
permission: 'Permission',
permission_placeholder: 'please select permission',
permissionList: 'Permission List',
summaryPermissions: 'Summary of permissions',
source: 'Source',
username: 'Username',
username_placeholder: 'please input username',
userList: 'User List',
groupUser: 'Group User',
addUser: 'Add User',
subordinateUsers: 'Subordinate Users',
nickname: 'Nickname',
nickname_placeholder: 'please input nickname',
password: 'Password',
password_placeholder: 'please input password',
department: 'Department',
group: 'Group',
email: 'Email',
email_placeholder: 'please input email',
mobile: 'Mobile',
isBlock: 'Is Block',
block: 'Block',
joined_at: 'Joined At',
role: 'Role',
role_placeholder1: 'please input role',
role_placeholder2: 'please select role',
role_placeholder3: 'please select a role name, multiple choices are allowed',
allRole: 'All Roles',
visualRole: 'Virtual Role',
addVisualRole: 'Add Virtual Role',
inheritedFrom: 'Inherited from',
heir: 'Inherit Roles',
permissionChange: 'Permissions',
roleChange: 'Roles',
resourceChange: 'Resources',
resourceTypeChange: 'Resource Type',
trigger: 'Triggers',
triggerNameInput: 'Please enter trigger name',
triggerChange: 'Triggers',
roleManage: 'Roles',
userManage: 'Users',
appManage: 'Applications',
resourceManage: 'Resources',
history: 'Audits',
userSecret: 'Secrets',
none: 'none',
danger: 'Dangerous',
confirmDeleteApp: 'Are you sure you want to delete this app?',
revoke: 'Revoke',
convenient: 'Quick Grant',
group2: 'Group',
groupName: 'Group Name',
resourceName: 'Resource Name',
creator: 'Creator',
member: 'Members',
viewAuth: 'view Auth',
addTypeTips: 'There is no type information yet, please add the resource type first!',
addResource: 'Add Resource',
resourceList: 'Resource List',
confirmResetSecret: 'Are you sure you want to reset the user secrets?',
addTrigger: 'Add Trigger',
deleteTrigger: 'Delete Trigger',
applyTrigger: 'Apply Trigger',
cancelTrigger: 'Cancel Trigger',
enable: 'Enable',
disable: 'Disable',
viewMatchResult: 'View regular matching results',
confirmDeleteTrigger: 'Are you sure you want to delete this trigger?',
ruleApply: 'Apply',
triggerTip1: 'Are you sure you want to apply this trigger?',
triggerTip2: 'Cancel applying this trigger?',
appNameInput: 'Please enter an application name',
descInput: 'Please enter a description',
addApp: 'Add',
updateApp: 'Update',
cancel: 'Cancel',
typeName: 'Name',
typeNameInput: 'Please enter a type name',
resourceNameInput: 'Please enter resource name',
pressEnter: 'Press Enter to confirm filtering',
groupMember: 'Group Members:',
isGroup: 'Group?',
errorTips: 'Error message',
roleList: 'Role List',
virtual: 'Virtual',
resourceBatchTips: 'Please enter the resource name, separated by newlines',
memberManage: 'Members: ',
newResource: 'New Resource: ',
deleteResource: 'Delete Resource: ',
deleteResourceType: 'Delete Resource Type: ',
noChange: 'No change',
batchOperate: 'Batch Operations',
batchGrant: 'Batch Grant',
batchRevoke: 'Batch Revoke',
editPerm: 'Add authorization: ',
permInput: 'Please enter permission name',
resourceTypeName: 'Resource Type Name',
selectedParents: 'Optionally inherit roles',
isAppAdmin: 'is app admin',
addRole: 'Add Role',
roleRelation: 'Role Relation',
roleRelationAdd: 'Add Role Relation',
roleRelationDelete: 'Delete Role Relation',
role2: 'Role',
admin: 'Admin',
involvingRP: 'Involving resources and permissions',
startAt: 'Start Time',
endAt: 'End Time',
triggerTips1: 'Priority regular pattern (secondary wildcard)',
pleaseSelectType: 'Please select resource type',
apply: 'Apply',
mobileTips: 'Please enter the correct phone number',
remove: 'Remove',
deleteUserConfirm: 'Are you sure you want to remove this user?',
copyResource: 'Copy resource name'
}
export default acl_en

View File

@ -0,0 +1,125 @@
const acl_zh = {
date: '日期',
operator: '操作员',
resource: '资源',
resourceType: '资源类型',
addResourceType: '新增资源类型',
app: '应用',
operateTime: '操作时间',
permission: '权限',
permission_placeholder: '请选择权限',
permissionList: '权限列表',
summaryPermissions: '权限汇总',
source: '来源',
username: '用户名',
username_placeholder: '请输入用户名',
userList: '用户列表',
groupUser: '组用户',
addUser: '新增用户',
subordinateUsers: '下属用户',
nickname: '中文名',
nickname_placeholder: '请输入中文名',
password: '密码',
password_placeholder: '请输入密码',
department: '部门',
group: '小组',
email: '邮箱',
email_placeholder: '请输入邮箱',
mobile: '手机号',
isBlock: '是否锁定',
block: '锁定',
joined_at: '加入时间',
role: '角色名',
role_placeholder1: '请输入角色名',
role_placeholder2: '请选择角色名称',
role_placeholder3: '请选择角色名称,可多选',
allRole: '所有角色',
visualRole: '虚拟角色',
addVisualRole: '新增虚拟角色',
inheritedFrom: '继承自',
heir: '继承者',
permissionChange: '权限变更',
roleChange: '角色变更',
resourceChange: '资源变更',
resourceTypeChange: '资源类型变更',
trigger: '触发器',
triggerNameInput: '请输入触发器名',
triggerChange: '触发器变更',
roleManage: '角色管理',
userManage: '用户管理',
appManage: '应用管理',
resourceManage: '资源管理',
history: '操作审计',
userSecret: '用户密钥',
none: '无',
danger: '危险操作',
confirmDeleteApp: '确定要删除该App吗',
revoke: '权限回收',
convenient: '便捷授权',
group2: '组',
groupName: '资源组名',
resourceName: '资源名',
creator: '创建者',
member: '成员',
viewAuth: '查看授权',
addTypeTips: '暂无类型信息,请先添加资源类型!',
addResource: '新增资源',
resourceList: '资源列表',
confirmResetSecret: '确定重置用户密钥?',
addTrigger: '新增触发器',
deleteTrigger: '删除触发器',
applyTrigger: '应用触发器',
cancelTrigger: '取消触发器',
enable: '启用',
disable: '禁用',
viewMatchResult: '查看正则匹配结果',
confirmDeleteTrigger: '确认删除该触发器吗?',
ruleApply: '规则应用',
triggerTip1: '是否确定应用该触发器?',
triggerTip2: '是否取消应用该触发器?',
appNameInput: '请输入应用名称',
descInput: '请输入描述',
addApp: '创建应用',
updateApp: '更新应用',
cancel: '撤销',
typeName: '类型名',
typeNameInput: '请输入类型名',
resourceNameInput: '请输入资源名',
pressEnter: '按回车确认筛选',
groupMember: '组成员:',
isGroup: '是否组',
errorTips: '错误提示',
roleList: '角色列表',
virtual: '虚拟',
resourceBatchTips: '请输入资源名,换行分隔',
memberManage: '成员管理:',
newResource: '新建资源:',
deleteResource: '删除资源:',
deleteResourceType: '删除资源类型:',
noChange: '没有修改',
batchOperate: '批量操作',
batchGrant: '批量授权',
batchRevoke: '批量权限回收',
editPerm: '添加授权:',
permInput: '请输入权限名',
resourceTypeName: '资源类型名',
selectedParents: '可选择继承角色',
isAppAdmin: '是否应用管理员',
addRole: '新增角色',
roleRelation: '角色关系',
roleRelationAdd: '添加角色关系',
roleRelationDelete: '删除角色关系',
role2: '角色',
admin: '管理员',
involvingRP: '涉及资源及权限',
startAt: '开始时间',
endAt: '结束时间',
triggerTips1: '优先正则模式(次通配符)',
pleaseSelectType: '请选择资源类型',
apply: '应用',
mobileTips: '请输入正确的手机号码',
remove: '移除',
deleteUserConfirm: '是否确定要移除该用户',
copyResource: '复制资源名'
}
export default acl_zh

View File

@ -12,35 +12,35 @@ const genAppRoute = ({ name }) => {
name: `${name}_roles_acl`,
hideChildrenInMenu: true,
component: () => import('../views/roles'),
meta: { title: '角色管理', icon: 'team', keepAlive: true }
meta: { title: 'acl.roleManage', icon: 'team', keepAlive: true }
},
{
path: `/acl/${name}/resources`,
name: `${name}_resources_acl`,
hideChildrenInMenu: true,
component: () => import('../views/resources'),
meta: { title: '资源管理', icon: 'credit-card', keepAlive: false }
meta: { title: 'acl.resourceManage', icon: 'credit-card', keepAlive: false }
},
{
path: `/acl/${name}/resource_types`,
name: `${name}_resource_types_acl`,
hideChildrenInMenu: true,
component: () => import('../views/resource_types'),
meta: { title: '资源类型', icon: 'file-text', keepAlive: true }
meta: { title: 'acl.resourceType', icon: 'file-text', keepAlive: true }
},
{
path: `/acl/${name}/trigger`,
name: `${name}_trigger_acl`,
hideChildrenInMenu: true,
component: () => import('../views/trigger'),
meta: { title: '触发器', icon: 'clock-circle', keepAlive: true }
meta: { title: 'acl.trigger', icon: 'clock-circle', keepAlive: true }
},
{
path: `/acl/${name}/history`,
name: `${name}_history_acl`,
hideChildrenInMenu: true,
component: () => import('../views/history'),
meta: { title: '操作审计', icon: 'search', keepAlive: false }
meta: { title: 'acl.history', icon: 'search', keepAlive: false }
}
]
}
@ -59,31 +59,31 @@ const genAclRoutes = async () => {
path: `/acl/secret_key`,
name: 'acl_secret_key',
component: () => import('../views/secretKey'),
meta: { title: '用户密钥', icon: 'key' }
meta: { title: 'acl.userSecret', icon: 'key' }
},
{
path: `/acl/operate_history`,
name: 'acl_operate_history',
component: () => import('../views/operation_history/index.vue'),
meta: { title: '操作审计', icon: 'search', permission: ['acl_admin'] },
meta: { title: 'acl.history', icon: 'search', permission: ['acl_admin'] }
},
{
path: `/acl/user`,
name: 'acl_user',
component: () => import('../views/users'),
meta: { title: '用户管理', icon: 'user', permission: ['acl_admin'] }
meta: { title: 'acl.userManage', icon: 'user', permission: ['acl_admin'] }
},
{
path: `/acl/roles`,
name: `acl_roles`,
component: () => import('../views/roles'),
meta: { title: '角色管理', icon: 'team', keepAlive: true, permission: ['acl_admin'] }
meta: { title: 'acl.roleManage', icon: 'team', keepAlive: true, permission: ['acl_admin'] }
},
{
path: `/acl/apps`,
name: 'acl_apps',
component: () => import('../views/apps'),
meta: { title: '应用管理', icon: 'appstore', permission: ['acl_admin'] }
meta: { title: 'acl.appManage', icon: 'appstore', permission: ['acl_admin'] }
}
]
}

View File

@ -11,7 +11,7 @@
:xs="24">
<a-card>
<a-card-meta :title="app.name">
<div slot="description" :title="app.description || ''">{{ app.description || '' }}</div>
<div slot="description" :title="app.description || ''">{{ app.description || $t('none') }}</div>
<a-avatar style="background-color: #5dc2f1" slot="avatar">{{ app.name[0].toUpperCase() }}</a-avatar>
</a-card-meta>
<template slot="actions">
@ -65,11 +65,11 @@ export default {
handleDeleteApp(app) {
const that = this
this.$confirm({
title: '危险操作',
content: '确定要删除该App吗',
title: that.$t('danger'),
content: that.$t('confirmDeleteApp'),
onOk() {
deleteApp(app.id).then((res) => {
that.$message.success(`删除成功${app.name}`)
that.$message.success(that.$t('deleteSuccess'))
that.loadApps()
})
},

View File

@ -1,7 +1,7 @@
<template>
<div class="acl-history">
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="权限变更">
<a-tab-pane key="1" :tab="$t('acl.permissionChange')">
<permisson-history-table
v-if="isloaded"
:allResourceTypes="allResourceTypes"
@ -17,7 +17,7 @@
@resourceClear="resourceClear"
></permisson-history-table>
</a-tab-pane>
<a-tab-pane key="2" tab="角色变更">
<a-tab-pane key="2" :tab="$t('acl.roleChange')">
<role-history-table
v-if="isloaded"
:allUsers="allUsers"
@ -26,7 +26,7 @@
:allUsersMap="allUsersMap"
></role-history-table>
</a-tab-pane>
<a-tab-pane key="3" tab="资源变更">
<a-tab-pane key="3" :tab="$t('acl.resourceChange')">
<resource-history-table
v-if="isloaded"
:allResources="allResources"
@ -41,7 +41,7 @@
@resourceClear="resourceClear"
></resource-history-table>
</a-tab-pane>
<a-tab-pane key="4" tab="资源类型变更">
<a-tab-pane key="4" :tab="$t('acl.resourceTypeChange')">
<resource-type-history-table
v-if="isloaded"
:allResourceTypes="allResourceTypes"
@ -52,7 +52,7 @@
:allResourceTypesMap="allResourceTypesMap"
></resource-type-history-table>
</a-tab-pane>
<a-tab-pane key="5" tab="触发器变更">
<a-tab-pane key="5" :tab="$t('acl.triggerChange')">
<trigger-history-table
v-if="isloaded"
:allTriggers="allTriggers"

View File

@ -1,11 +1,12 @@
<template>
<CustomDrawer @close="handleClose" width="500" :title="title" :visible="visible" :closable="false">
<a-form :form="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="应用名称">
<a-input v-decorator="['name', { rules: [{ required: true, message: '请输入应用名称' }] }]"> </a-input>
<a-form-item :label="$t('acl.app')">
<a-input v-decorator="['name', { rules: [{ required: true, message: $t('acl.appNameInput') }] }]"> </a-input>
</a-form-item>
<a-form-item label="描述">
<a-input v-decorator="['description', { rules: [{ required: true, message: '请输入描述' }] }]"> </a-input>
<a-form-item :label="$t('desc')">
<a-input v-decorator="['description', { rules: [{ required: true, message: $t('acl.descInput') }] }]">
</a-input>
</a-form-item>
<a-form-item label="AppId">
<a-input v-decorator="['app_id', { rules: [{ required: false }] }]" :disabled="mode === 'update'"> </a-input>
@ -19,8 +20,8 @@
</a-form-item>
</a-form>
<div class="custom-drawer-bottom-action">
<a-button @click="handleClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">提交</a-button>
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('submit') }}</a-button>
</div>
</CustomDrawer>
</template>
@ -32,10 +33,14 @@ export default {
data() {
return {
visible: false,
title: '创建应用',
mode: 'create',
}
},
computed: {
title() {
return this.$t('acl.addApp')
},
},
beforeCreate() {
this.form = this.$form.createForm(this)
},
@ -43,7 +48,7 @@ export default {
handleEdit(ele) {
this.visible = true
if (ele) {
this.title = '修改应用'
this.title = this.$t('updateApp')
this.mode = 'update'
console.log(ele)
const { name, description } = ele
@ -55,7 +60,7 @@ export default {
})
} else {
this.mode = 'create'
this.title = '创建应用'
this.title = this.$t('acl.addApp')
}
},
handleClose() {
@ -69,11 +74,11 @@ export default {
}
if (values.id) {
await updateApp(values.id, values).then((res) => {
this.$message.success('修改成功!')
this.$message.success(this.$t('updateSuccess'))
})
} else {
await addApp(values).then((res) => {
this.$message.success('创建成功!')
this.$message.success(this.$t('addSuccess'))
})
}
this.handleClose()

View File

@ -9,10 +9,10 @@
<a-dropdown class="dropdown" size="small" placement="topCenter" :trigger="['click']" :disabled="dropdownIsDisabled">
<a-menu slot="overlay">
<a-menu-item v-for="(size,index) in pageSizes" :key="index" @click="handleItemClick(size)">
{{ size }}/
{{ size }}{{ $t('itemsPerPage') }}
</a-menu-item>
</a-menu>
<a-button size="small"> {{ pageSize }}/ <a-icon type="down" /> </a-button>
<a-button size="small"> {{ pageSize }}{{ $t('itemsPerPage') }}<a-icon type="down" /> </a-button>
</a-dropdown>
</a-space>
</a-col>

View File

@ -1,6 +1,6 @@
<template>
<CustomDrawer
:title="`权限汇总: ${user.nickname}`"
:title="`${$t('acl.summaryPermissions')}: ${user.nickname}`"
:visible="visible"
placement="left"
width="100%"
@ -24,7 +24,7 @@
<vxe-table :max-height="`${windowHeight - 230}px`" :data="resources" ref="rTable">
<vxe-column
field="name"
title="资源名"
:title="$t('acl.resourceName')"
width="30%"
:filters="[{ data: '' }]"
:filter-method="filterNameMethod"
@ -39,62 +39,17 @@
v-model="option.data"
@input="$panel.changeOption($event, !!option.data, option)"
@keyup.enter="$panel.confirmFilter()"
placeholder="按回车确认筛选"
:placeholder="$t('acl.pressEnter')"
/>
</template>
</template>
</vxe-column>
<vxe-column field="permissions" title="权限列表" width="70%">
<vxe-column field="permissions" :title="$t('acl.permissionList')" width="70%">
<template #default="{row}">
<a-tag color="cyan" v-for="(r, index) in row.permissions" :key="index">{{ r }}</a-tag>
</template>
</vxe-column>
</vxe-table>
<!-- <a-table
:columns="tableColumns"
:dataSource="resources"
:rowKey="record=>record.id"
:pagination="{ showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条记录` }"
showPagination="auto"
ref="rTable"
size="middle">
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class="custom-filter-dropdown">
<a-input
v-ant-ref="c => searchInput = c"
:placeholder="` ${column.title}`"
:value="selectedKeys[0]"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="() => handleSearch(selectedKeys, confirm, column)"
style="width: 188px; margin-bottom: 8px; display: block;"
/>
<a-button
type="primary"
@click="() => handleSearch(selectedKeys, confirm, column)"
icon="search"
size="small"
style="width: 90px; margin-right: 8px"
>搜索</a-button>
<a-button
@click="() => handleReset(clearFilters, column)"
size="small"
style="width: 90px"
>重置</a-button>
</div>
<a-icon slot="filterIcon" slot-scope="filtered" type="search" :style="{ color: filtered ? '#108ee9' : undefined }" />
<template slot="nameSearchRender" slot-scope="text">
<span v-if="columnSearchText.name">
<template v-for="(fragment, i) in text.toString().split(new RegExp(`(?<=${columnSearchText.name})|(?=${columnSearchText.name})`, 'i'))">
<mark v-if="fragment.toLowerCase() === columnSearchText.name.toLowerCase()" :key="i" class="highlight">{{ fragment }}</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
<template v-else>{{ text }}</template>
</template>
<template slot="permissions" slot-scope="record">
<a-tag color="cyan" v-for="(r, index) in record" :key="index">{{ r }}</a-tag>
</template>
</a-table> -->
</a-spin>
</a-tab-pane>
</a-tabs>
@ -122,33 +77,6 @@ export default {
resourceTypes: [],
resourceTypePerms: [],
resources: [],
// tableColumns: [
// {
// title: '资源名',
// dataIndex: 'name',
// sorter: false,
// width: 150,
// // scopedSlots: {
// // customRender: 'nameSearchRender',
// // filterDropdown: 'filterDropdown',
// // filterIcon: 'filterIcon'
// // },
// // onFilter: (value, record) => record.name && record.name.toLowerCase().includes(value.toLowerCase()),
// // onFilterDropdownVisibleChange: (visible) => {
// // if (visible) {
// // setTimeout(() => {
// // this.searchInput.focus()
// // }, 0)
// // }
// // }
// },
// {
// title: '权限列表',
// dataIndex: 'permissions',
// width: 300,
// scopedSlots: { customRender: 'permissions' },
// },
// ],
columnSearchText: {
name: '',
},
@ -156,14 +84,14 @@ export default {
},
computed: {
...mapState({
windowHeight: state => state.windowHeight,
windowHeight: (state) => state.windowHeight,
}),
displayApps() {
const roles = this.$store.state.user.roles.permissions
if (roles.includes('acl_admin')) {
return this.apps
}
return this.apps.filter(item => {
return this.apps.filter((item) => {
if (roles.includes(`${item.name}_admin`)) {
return true
}
@ -192,7 +120,7 @@ export default {
},
async loadRoles(_appId) {
const res = await searchRole({ app_id: _appId, page_size: 9999, is_all: true })
this.roles = res.roles.filter(item => item.uid)
this.roles = res.roles.filter((item) => item.uid)
},
async handleSwitchApp(appId) {
this.currentAppId = appId
@ -218,7 +146,7 @@ export default {
},
async loadResource() {
this.spinning = true
const fil = this.roles.filter(role => role.uid === this.user.uid)
const fil = this.roles.filter((role) => role.uid === this.user.uid)
if (!fil[0]) {
return
}

View File

@ -1,29 +1,33 @@
<template>
<CustomDrawer
:closable="false"
:title="drawerTitle"
:title="$t('acl.addReourceType')"
:visible="drawerVisible"
@close="onClose"
placement="right"
width="30%"
>
<a-form :form="form" :layout="formLayout" @submit="handleSubmit">
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" label="类型名">
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" :label="$t('acl.typeName')">
<a-input
name="name"
placeholder=""
v-decorator="['name', { rules: [{ required: true, message: '请输入资源名' }] }]"
v-decorator="['name', { rules: [{ required: true, message: $t('acl.resourceNameInput') }] }]"
/>
</a-form-item>
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" label="描述">
<a-textarea placeholder="请输入描述信息..." name="description" :rows="4" />
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" :label="$t('desc')">
<a-textarea :placeholder="$t('acl.descInput')" name="description" :rows="4" />
</a-form-item>
<a-form-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" label="权限">
<a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
:label="$t('acl.permission')"
>
<div :style="{ borderBottom: '1px solid #E9E9E9' }">
<a-checkbox :indeterminate="indeterminate" @change="onCheckAllChange" :checked="checkAll">
全选
{{ $t('checkAll') }}
</a-checkbox>
</div>
<br />
@ -35,8 +39,8 @@
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="onClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -49,7 +53,6 @@ export default {
name: 'ResourceForm',
data() {
return {
drawerTitle: '新增资源类型',
drawerVisible: false,
formLayout: 'vertical',
perms: ['1'],
@ -142,8 +145,8 @@ export default {
})
},
updateResourceType(id, data) {
updateResourceTypeById(id, data).then(res => {
this.$message.success(`更新成功`)
updateResourceTypeById(id, data).then((res) => {
this.$message.success(this.$t('updateSuccess'))
this.handleOk()
this.onClose()
})
@ -151,18 +154,12 @@ export default {
},
createResourceType(data) {
addResourceType(data).then(res => {
this.$message.success(`添加成功`)
addResourceType(data).then((res) => {
this.$message.success(this.$t('addSuccess'))
this.handleOk()
this.onClose()
})
// .catch(err => this.requestFailed(err))
},
// requestFailed (err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// }
},
watch: {},
props: {

View File

@ -21,22 +21,22 @@
:height="`${windowHeight - windowHeightMinus}px`"
:scroll-y="{ enabled: false }"
>
<vxe-column field="created_at" width="144px" title="操作时间">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')">
<template #default="{ row }">
<span>{{ row.deleted_at || row.updated_at || row.created_at }}</span>
</template>
</vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="row.operate_type === 'grant' ? 'green' : 'red'">{{
operateTypeMap.get(row.operate_type)
}}</a-tag>
</template>
</vxe-column>
<vxe-column field="rid" title="用户"></vxe-column>
<vxe-column field="resource_type_id" title="资源类型"></vxe-column>
<vxe-column field="resources" title="资源">
<vxe-column field="rid" :title="$t('user')"></vxe-column>
<vxe-column field="resource_type_id" :title="$t('acl.resourceType')"></vxe-column>
<vxe-column field="resources" :title="$t('acl.resource')">
<template #default="{ row }">
<template v-if="row.resource_ids.length > 0">
<a-tooltip placement="top">
@ -55,14 +55,14 @@
</template>
</template>
</vxe-column>
<vxe-column title="权限">
<vxe-column :title="$t('acl.permission')">
<template #default="{ row }">
<a-tag v-for="(perm, index) in row.permission_ids" :key="'perms_' + perm + index">
{{ perm }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -122,10 +122,6 @@ export default {
loading: true,
app_id: this.$route.name.split('_')[0],
tableData: [],
operateTypeMap: new Map([
['grant', '授权'],
['revoke', '撤销'],
]),
queryParams: {
page: 1,
page_size: 50,
@ -133,49 +129,6 @@ export default {
start: '',
end: '',
},
permissionTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: '用户',
is_choice: true,
name: 'rid',
value_type: '2',
choice_value: this.allRoles,
},
{
alias: '资源类型',
is_choice: true,
name: 'resource_type_id',
value_type: '2',
choice_value: this.allResourceTypes,
},
{
alias: '资源',
is_choice: true,
name: 'resource_id',
value_type: '2',
choice_value: this.allResources,
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 授权: 'grant' }, { 撤销: 'revoke' }],
},
],
}
},
async created() {
@ -198,6 +151,12 @@ export default {
},
},
computed: {
operateTypeMap() {
return new Map([
['grant', this.$t('grant')],
['revoke', this.$t('acl.cancel')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
@ -207,6 +166,51 @@ export default {
tableDataLength() {
return this.tableData.length
},
permissionTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: this.$t('user'),
is_choice: true,
name: 'rid',
value_type: '2',
choice_value: this.allRoles,
},
{
alias: this.$t('acl.resourceType'),
is_choice: true,
name: 'resource_type_id',
value_type: '2',
choice_value: this.allResourceTypes,
},
{
alias: this.$t('acl.resource'),
is_choice: true,
name: 'resource_id',
value_type: '2',
choice_value: this.allResources,
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ [this.$t('grant')]: 'grant' }, { [this.$t('acl.cancel')]: 'revoke' }],
},
]
},
},
methods: {
// 获取数据

View File

@ -1,6 +1,6 @@
<template>
<CustomDrawer
title="便捷授权"
:title="$t('acl.convenient')"
width="500px"
:maskClosable="false"
:closable="true"
@ -10,12 +10,12 @@
<a-form :form="form">
<a-form-item>
<div slot="label" style="display: inline-block">
<span>角色列表</span>
<span>{{ $t('acl.roleList') }}</span>
<a-divider type="vertical" />
<a-switch
style="display: inline-block"
checked-children="用户"
un-checked-children="虚拟"
:checked-children="$t('user')"
:un-checked-children="$t('acl.virtual')"
@change="handleRoleTypeChange"
v-model="roleType"
/>
@ -23,37 +23,37 @@
<el-select
:style="{ width: '100%' }"
size="small"
v-decorator="['roleIdList', { rules: [{ required: true, message: '请选择角色名称' }] }]"
v-decorator="['roleIdList', { rules: [{ required: true, message: $t('acl.role_placeholder2') }] }]"
multiple
filterable
placeholder="请选择角色名称,可多选!"
:placeholder="$t('acl.role_placeholder3')"
>
<el-option v-for="role in allRoles" :key="role.id" :value="role.id" :label="role.name"></el-option>
</el-select>
</a-form-item>
<a-form-item label="权限列表">
<a-form-item :label="$t('acl.permissionList')">
<el-select
:style="{ width: '100%' }"
size="small"
name="permName"
v-decorator="['permName', { rules: [{ required: true, message: '请选择权限' }] }]"
v-decorator="['permName', { rules: [{ required: true, message: this.$t('acl.permission_placeholder') }] }]"
multiple
placeholder="请选择权限,可多选!"
:placeholder="this.$t('acl.permission_placeholder')"
>
<el-option v-for="perm in allPerms" :key="perm.name" :value="perm.name" :label="perm.name"></el-option>
</el-select>
</a-form-item>
<a-form-item label="资源名">
<a-form-item :label="$t('acl.resourceName')">
<a-textarea
v-decorator="['resource_names', { rules: [{ required: true, message: '请输入资源名,换行分隔' }] }]"
v-decorator="['resource_names', { rules: [{ required: true, message: $t('acl.resourceBatchTips') }] }]"
:autoSize="{ minRows: 4 }"
placeholder="请输入资源名,换行分隔"
:placeholder="$t('acl.resourceBatchTips')"
/>
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="handleRevoke" type="danger" ghost>权限回收</a-button>
<a-button @click="handleSubmit" type="primary">授权</a-button>
<a-button @click="handleRevoke" type="danger" ghost>{{ $t('acl.revoke') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('grant') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -98,12 +98,12 @@ export default {
this.loadRoles(Number(target))
},
loadRoles(isUserRole) {
searchRole({ page_size: 9999, app_id: this.$route.name.split('_')[0], user_role: isUserRole }).then(res => {
searchRole({ page_size: 9999, app_id: this.$route.name.split('_')[0], user_role: isUserRole }).then((res) => {
this.allRoles = res.roles
})
},
loadPerm(resourceTypeId) {
getResourceTypePerms(resourceTypeId).then(res => {
getResourceTypePerms(resourceTypeId).then((res) => {
this.allPerms = res
})
},
@ -111,13 +111,13 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
console.log(values)
values.roleIdList.forEach(roleId => {
values.roleIdList.forEach((roleId) => {
setBatchRoleResourceByResourceName(roleId, {
resource_names: values.resource_names.split('\n'),
perms: values.permName,
resource_type_id: this.resource_type_id,
}).then(res => {
this.$message.success('授权成功')
}).then((res) => {
this.$message.success(this.$t('operateSuccess'))
this.form.resetFields()
})
})
@ -128,13 +128,13 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
console.log(values)
values.roleIdList.forEach(roleId => {
values.roleIdList.forEach((roleId) => {
setBatchRoleResourceRevokeByResourceName(roleId, {
resource_names: values.resource_names.split('\n'),
perms: values.permName,
resource_type_id: this.resource_type_id,
}).then(res => {
this.$message.success('权限回收成功')
}).then((res) => {
this.$message.success(this.$t('operateSuccess'))
this.form.resetFields()
})
})

View File

@ -8,27 +8,27 @@
width="30%"
>
<a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="资源名">
<a-form-item :label="$t('acl.resourceName')">
<a-input
name="name"
placeholder=""
v-decorator="['name', { rules: [{ required: true, message: '请输入资源名' }] }]"
v-decorator="['name', { rules: [{ required: true, message: $t('acl.resourceNameInput') }] }]"
/>
</a-form-item>
<a-form-item label="资源类型">
<a-form-item :label="$t('acl.resourceType')">
<a-select v-model="selectedTypeId">
<a-select-option v-for="type in allTypes" :key="type.id">{{ type.name }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="是否组">
<a-form-item :label="$t('acl.isGroup')">
<a-radio-group v-model="isGroup">
<a-radio :value="true">
{{ $t('yes') }}
</a-radio>
<a-radio :value="false">
{{ $t('no') }}
</a-radio>
</a-radio-group>
</a-form-item>
@ -36,8 +36,8 @@
<a-input name="id" type="hidden" v-decorator="['id', { rules: [] }]" />
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="onClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -52,7 +52,6 @@ export default {
name: 'ResourceForm',
data() {
return {
drawerTitle: '新增资源',
drawerVisible: false,
allTypes: [],
isGroup: false,
@ -68,7 +67,11 @@ export default {
this.getAllResourceTypes()
},
computed: {},
computed: {
drawerTitle() {
return this.$t('acl.addResource')
},
},
mounted() {},
methods: {
getAllResourceTypes() {
@ -95,7 +98,7 @@ export default {
values.type_id = this.selectedTypeId
values.app_id = this.$route.name.split('_')[0]
if (values.id) {
this.$message.error('错误提示')
this.$message.error(this.$t('acl.errorTips'))
} else {
this.createResource(values)
}
@ -105,23 +108,17 @@ export default {
createResource(data) {
if (!this.isGroup) {
addResource(data).then((res) => {
this.$message.success(`添加成功`)
this.$message.success(this.$t('addSuccess'))
this.onClose()
})
// .catch(err => this.requestFailed(err))
} else {
addResourceGroup(data).then((res) => {
this.$message.success(`添加成功`)
this.$message.success(this.$t('addSuccess'))
this.onClose()
})
// .catch(err => this.requestFailed(err))
}
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
},
watch: {
'$route.name': function(newValue, oldValue) {

View File

@ -1,5 +1,5 @@
<template>
<a-modal v-model="visible" :title="`组成员:${editRecord.name}`" :width="800" :footer="null">
<a-modal v-model="visible" :title="`${$t('acl.groupMember')}${editRecord.name}`" :width="800" :footer="null">
<div :style="{ maxHeight: '500px', overflow: 'auto' }">
<a-tag :style="{ marginBottom: '5px' }" v-for="mem in members" :key="mem.name">
{{ mem.name }}

View File

@ -1,5 +1,5 @@
<template>
<a-modal v-model="visible" :title="`成员管理:${editRecord.name}`" @ok="handleSubmit" :width="690">
<a-modal v-model="visible" :title="`${$t('acl.memberManage')}${editRecord.name}`" @ok="handleSubmit" :width="690">
<!-- <CustomTransfer
ref="customTransfer"
:show-search="true"
@ -26,7 +26,7 @@
@selectChange="selectChange"
:selectedKeys="selectedKeys"
>
<span slot="notFoundContent">暂无数据</span>
<span slot="notFoundContent">{{ $t('noData') }}</span>
<template slot="children" slot-scope="{ props: { direction, filteredItems } }">
<div class="ant-transfer-list-content" v-if="direction === 'right'">
<div
@ -104,7 +104,7 @@ export default {
})
updateResourceGroup(this.editRecord['id'], { items: items.join(',') }).then(() => {
this.visible = false
this.$message.success('更新成功!')
this.$message.success(this.$t('updateSuccess'))
})
// .catch(err => this.$httpError(err))
},

View File

@ -4,7 +4,7 @@
ref="child"
:attrList="resourceTableAttrList"
:hasSwitch="true"
switchValue=""
:switchValue="$t('acl.group2')"
@onSwitchChange="onSwitchChange"
@search="handleSearch"
@searchFormReset="searchFormReset"
@ -22,30 +22,30 @@
resizable
:height="`${windowHeight - 310}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="link_id" title="资源名">
<vxe-column field="link_id" :title="$t('acl.resourceName')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.description }}
</p>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -99,40 +99,6 @@ export default {
checked: false,
tableData: [],
app_id: this.$route.name.split('_')[0],
resourceTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 新建: 'create' }, { 修改: 'update' }, { 删除: 'delete' }],
},
{
alias: '资源名',
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: this.allResources,
},
],
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
]),
colorMap: new Map([
['create', 'green'],
['update', 'orange'],
@ -170,9 +136,47 @@ export default {
windowHeight() {
return this.$store.state.windowHeight
},
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
])
},
tableDataLength() {
return this.tableData.length
},
resourceTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ [this.$t('create')]: 'create' }, { [this.$t('update')]: 'update' }, { [this.$t('delete')]: 'delete' }],
},
{
alias: this.$t('acl.resourceName'),
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: this.allResources,
},
]
},
},
methods: {
async getTable(queryParams) {
@ -280,7 +284,7 @@ export default {
switch (operate_type) {
// create
case 'create': {
item.description = `新建资源${item.current.name}`
item.description = `${this.$t('acl.newResource')}${item.current.name}`
break
}
case 'update': {
@ -290,9 +294,9 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
item.description += ` ${key} : 改为 ${newVal} `
item.description += ` ${key} : -> ${newVal} `
} else {
item.description += ` ${key} : ${oldVal} 改为 ${newVal} `
item.description += ` ${key} : ${oldVal} -> ${newVal} `
}
}
}
@ -300,18 +304,18 @@ export default {
const currentResource_ids = item.currentResource_ids
if (!_.isEqual(originResource_ids, currentResource_ids)) {
if (originResource_ids.length === 0) {
const str = ` resource_ids : 新增 ${currentResource_ids} `
const str = ` resource_ids : ${this.$t('new')} ${currentResource_ids} `
item.description += str
} else {
const str = ` resource_ids : ${originResource_ids} 改为 ${currentResource_ids} `
const str = ` resource_ids : ${originResource_ids} -> ${currentResource_ids} `
item.description += str
}
}
if (!item.description) item.description = '没有修改'
if (!item.description) item.description = this.$t('acl.noChange')
break
}
case 'delete': {
item.description = `删除资源${item.origin.name}`
item.description = `${this.$t('acl.deleteResource')}${item.origin.name}`
break
}
}

View File

@ -17,7 +17,7 @@
>
<vxe-column
field="name"
title="角色名"
:title="$t('acl.role')"
width="20%"
:filters="[{ data: '' }]"
:filter-method="filterNameMethod"
@ -32,19 +32,19 @@
v-model="option.data"
@input="$panel.changeOption($event, !!option.data, option)"
@keyup.enter="$panel.confirmFilter()"
placeholder="按回车确认筛选"
:placeholder="$t('acl.pressEnter')"
/>
</template>
</template>
</vxe-column>
<vxe-column field="users" title="下属用户" width="35%">
<vxe-column field="users" :title="$t('acl.subordinateUsers')" width="35%">
<template #default="{row}">
<a-tag color="green" v-for="user in row.users" :key="user.nickname">
{{ user.nickname }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="perms" title="权限列表" width="35%">
<vxe-column field="perms" :title="$t('acl.permissionList')" width="35%">
<template #default="{row}">
<a-tag
closable
@ -57,43 +57,18 @@
</a-tag>
</template>
</vxe-column>
<vxe-column field="operate" title="批量操作">
<vxe-column field="operate" :title="$t('batchOperate')">
<template #default="{row}">
<a-button size="small" type="danger" @click="handleClearAll(row)">
清空
{{ $t('clear') }}
</a-button>
</template>
</vxe-column>
<template slot="empty">
<img :src="require(`@/assets/data_empty.png`)" />
<p style="font-size: 14px; line-height: 17px; color: rgba(0, 0, 0, 0.6)">暂无数据</p>
<p style="font-size: 14px; line-height: 17px; color: rgba(0, 0, 0, 0.6)">{{ $t('noData') }}</p>
</template>
</vxe-table>
<!-- <a-table
:columns="columns"
:dataSource="resPerms"
:rowKey="record => record.name"
:pagination="{ showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条记录` }"
showPagination="auto"
ref="rTable"
size="middle"
>
<div slot="perms" slot-scope="text">
<a-tag closable color="cyan" v-for="perm in text" :key="perm.name" @close="deletePerm(perm.rid, perm.name)">
{{ perm.name }}
</a-tag>
</div>
<div slot="users" slot-scope="text">
<a-tag color="green" v-for="user in text" :key="user.nickname">
{{ user.nickname }}
</a-tag>
</div>
<div slot="operate" slot-scope="text">
<a-button size="small" type="danger" @click="handleClearAll(text)">
清空
</a-button>
</div>
</a-table> -->
</CustomDrawer>
</template>
<script>
@ -112,40 +87,11 @@ export default {
data() {
return {
isGroup: false,
drawerTitle: '权限列表',
drawerVisible: false,
record: null,
allPerms: [],
resPerms: [],
roleID: null,
// columns: [
// {
// title: '角色名',
// dataIndex: 'name',
// sorter: false,
// width: 50,
// },
// {
// title: '下属用户',
// dataIndex: 'users',
// sorter: false,
// width: 150,
// scopedSlots: { customRender: 'users' },
// },
// {
// title: '权限列表',
// dataIndex: 'perms',
// sorter: false,
// width: 150,
// scopedSlots: { customRender: 'perms' },
// },
// {
// title: '批量操作',
// sorter: false,
// width: 50,
// scopedSlots: { customRender: 'operate' },
// },
// ],
childrenDrawer: false,
allRoles: [],
}
@ -154,6 +100,9 @@ export default {
...mapState({
windowHeight: (state) => state.windowHeight,
}),
drawerTitle() {
return this.$t('acl.permissionList')
}
},
beforeCreate() {
this.form = this.$form.createForm(this)
@ -175,7 +124,7 @@ export default {
perms: [],
app_id: this.$route.name.split('_')[0],
}).then((res) => {
this.$message.success('删除成功')
this.$message.success(this.$t('deleteSuccess'))
this.$nextTick(() => {
this.getResPerms(this.record.id)
})
@ -185,7 +134,7 @@ export default {
perms: [],
app_id: this.$route.name.split('_')[0],
}).then((res) => {
this.$message.success('删除成功')
this.$message.success(this.$t('deleteSuccess'))
// for (let i = 0; i < this.resPerms.length; i++) {
// if (this.resPerms[i].name === text.name) {
// this.resPerms[i].perms = []
@ -227,7 +176,7 @@ export default {
perms: [permName],
app_id: this.$route.name.split('_')[0],
}).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
})
// .catch(err => this.requestFailed(err))
} else {
@ -235,18 +184,13 @@ export default {
perms: [permName],
app_id: this.$route.name.split('_')[0],
}).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
})
// .catch(err => this.requestFailed(err))
}
},
handleCancel(e) {
this.drawerVisible = false
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
filterNameMethod({ option, row }) {
return row.name.toLowerCase().includes(option.data.toLowerCase())
},

View File

@ -3,42 +3,42 @@
<a-form :form="form">
<a-form-item>
<div slot="label" style="display: inline-block">
<span>角色列表</span>
<span>{{ $t('acl.roleList') }}</span>
<a-divider type="vertical" />
<a-switch
style="display: inline-block"
checked-children="用户"
un-checked-children="虚拟"
:checked-children="$t('user')"
:un-checked-children="$t('acl.virtual')"
@change="handleRoleTypeChange"
/>
</div>
<el-select
:style="{ width: '100%' }"
size="small"
v-decorator="['roleIdList', { rules: [{ required: true, message: '请选择角色名称' }] }]"
v-decorator="['roleIdList', { rules: [{ required: true, message: $t('acl.role_placeholder2') }] }]"
multiple
filterable
placeholder="请选择角色名称,可多选!"
:placeholder="$t('acl.role_placeholder3')"
>
<el-option v-for="role in allRoles" :key="role.id" :value="role.id" :label="role.name"></el-option>
</el-select>
</a-form-item>
<a-form-item label="权限列表">
<a-form-item :label="$t('acl.permissionList')">
<el-select
:style="{ width: '100%' }"
size="small"
name="permName"
v-decorator="['permName', { rules: [{ required: true, message: '请选择权限' }] }]"
v-decorator="['permName', { rules: [{ required: true, message: $t('acl.permission_placeholder') }] }]"
multiple
placeholder="请选择权限,可多选!"
:placeholder="$t('acl.permission_placeholder') "
>
<el-option v-for="perm in allPerms" :key="perm.name" :value="perm.name" :label="perm.name"></el-option>
</el-select>
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="closeForm">取消</a-button>
<a-button @click="handleSubmit" type="primary" :loading="loading">确定</a-button>
<a-button @click="closeForm">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary" :loading="loading">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -117,16 +117,12 @@ export default {
this.type = type
if (Array.isArray(record)) {
this.loadPerm(record[0]['resource_type_id'])
this.title = `${type === 'grant' ? '批量授权' : '批量权限回收'}`
this.title = `${type === 'grant' ? this.$t('acl.batchGrant') : this.$t('acl.batchRevoke')}`
} else {
this.title = `添加授权${record.name}`
this.title = `${this.$t('acl.editPerm')}${record.name}`
this.loadPerm(record['resource_type_id'])
}
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
handleSubmit(e) {
e.preventDefault()
this.form.validateFields((err, values) => {
@ -136,16 +132,10 @@ export default {
this.loading = true
if (!this.isGroup) {
if (Array.isArray(this.instance)) {
// const promises = this.instance.map(item => {
// return setRoleResourcePerm(roleId, item.id, params)
// })
// Promise.all(promises).then(() => {
// this.$message.success('添加授权成功')
// })
if (this.type === 'grant') {
setBatchRoleResourcePerm(roleId, { ...params, resource_ids: this.instance.map((a) => a.id) })
.then((res) => {
this.$message.success('添加授权成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false
@ -153,7 +143,7 @@ export default {
} else {
setBatchRoleResourceRevoke(roleId, { ...params, resource_ids: this.instance.map((a) => a.id) })
.then((res) => {
this.$message.success('批量权限回收成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false
@ -162,7 +152,7 @@ export default {
} else {
setRoleResourcePerm(roleId, this.instance.id, params)
.then((res) => {
this.$message.success('添加授权成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false
@ -170,16 +160,10 @@ export default {
}
} else {
if (Array.isArray(this.instance)) {
// const promises = this.instance.map(item => {
// return setRoleResourceGroupPerm(roleId, item.id, params)
// })
// Promise.all(promises).then(() => {
// this.$message.success('添加授权成功')
// })
if (this.type === 'grant') {
setBatchRoleResourceGroupPerm(roleId, { ...params, group_ids: this.instance.map((a) => a.id) })
.then((res) => {
this.$message.success('添加授权成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false
@ -190,7 +174,7 @@ export default {
group_ids: this.instance.map((a) => a.id),
})
.then((res) => {
this.$message.success('批量权限回收成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false
@ -199,7 +183,7 @@ export default {
} else {
setRoleResourceGroupPerm(roleId, this.instance.id, params)
.then((res) => {
this.$message.success('添加授权成功')
this.$message.success(this.$t('operateSuccess'))
})
.finally(() => {
this.loading = false

View File

@ -1,32 +1,32 @@
<template>
<CustomDrawer
:closable="false"
:title="drawerTitle"
:title="$t('acl.addReourceType')"
:visible="drawerVisible"
@close="onClose"
placement="right"
width="500px"
>
<a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="类型名">
<a-form-item :label="$t('acl.typeName')">
<a-input
name="name"
placeholder="类型名称"
v-decorator="['name', { rules: [{ required: true, message: '请输入类型名' }] }]"
:placeholder="$t('acl.typeName')"
v-decorator="['name', { rules: [{ required: true, message: $t('acl.typeNameInput') }] }]"
/>
</a-form-item>
<a-form-item label="描述">
<a-form-item :label="$t('desc')">
<a-textarea
placeholder="请输入描述信息..."
:placeholder="$t('acl.descInput')"
name="description"
:rows="4"
v-decorator="['description', { rules: [] }]"
/>
</a-form-item>
<a-form-item label="权限">
<a-select mode="tags" v-model="perms" style="width: 100%" placeholder="请输入权限名..."> </a-select>
<a-form-item :label="$t('acl.permission')">
<a-select mode="tags" v-model="perms" style="width: 100%" :placeholder="$t('acl.permInput')"> </a-select>
</a-form-item>
<a-form-item>
@ -34,8 +34,8 @@
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="onClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -48,7 +48,6 @@ export default {
name: 'ResourceForm',
data() {
return {
drawerTitle: '新增资源类型',
drawerVisible: false,
perms: [],
}
@ -104,7 +103,7 @@ export default {
},
updateResourceType(id, data) {
updateResourceTypeById(id, data).then((res) => {
this.$message.success(`更新成功`)
this.$message.success(this.$t('updateSuccess'))
this.handleOk()
this.onClose()
})
@ -113,17 +112,11 @@ export default {
createResourceType(data) {
addResourceType(data).then((res) => {
this.$message.success(`添加成功`)
this.$message.success(this.$t('addSuccess'))
this.handleOk()
this.onClose()
})
// .catch(err => this.requestFailed(err))
},
// requestFailed (err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// }
},
watch: {},
props: {

View File

@ -16,30 +16,30 @@
:loading="loading"
:height="`${windowHeight - 310}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="link_id" title="资源类型名">
<vxe-column field="link_id" :title="$t('acl.resourceTypeName')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.changeDescription }}
</p>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -93,40 +93,6 @@ export default {
checked: false,
tableData: [],
app_id: this.$route.name.split('_')[0],
resourceTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 新建: 'create' }, { 修改: 'update' }, { 删除: 'delete' }],
},
{
alias: '资源类型',
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: this.allResourceTypes,
},
],
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
]),
colorMap: new Map([
['create', 'green'],
['update', 'orange'],
@ -155,12 +121,50 @@ export default {
},
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
},
resourceTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ [this.$t('create')]: 'create' }, { [this.$t('update')]: 'update' }, { [this.$t('delete')]: 'delete' }],
},
{
alias: this.$t('acl.resourceType'),
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: this.allResourceTypes,
},
]
},
},
methods: {
async getTable(queryParams) {
@ -236,7 +240,9 @@ export default {
switch (operate_type) {
// create
case 'create': {
item.changeDescription = `新增资源类型${item.current.name}\n描述${item.current.description}\n权限${item.extra.permission_ids.current}`
item.changeDescription = `${this.$t('acl.addReourceType')}${item.current.name}\n${this.$t('desc')}${
item.current.description
}\n${this.$t('acl.permission')}: ${item.extra.permission_ids.current}`
break
}
case 'update': {
@ -246,10 +252,10 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null || oldVal === '') {
const str = ` ${key} : 改为 ${newVal} \n`
const str = ` ${key} : -> ${newVal} \n`
item.changeDescription += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} \n`
const str = ` ${key} : ${oldVal} -> ${newVal} \n`
item.changeDescription += str
}
}
@ -257,13 +263,13 @@ export default {
const currentPerms = item.extra.permission_ids.current
const originPerms = item.extra.permission_ids.origin
if (!_.isEqual(currentPerms, originPerms)) {
item.changeDescription += ` permission_ids : ${originPerms} 改为 ${currentPerms} `
item.changeDescription += ` permission_ids : ${originPerms} -> ${currentPerms} `
}
if (!item.changeDescription) item.changeDescription = '没有修改'
if (!item.changeDescription) item.changeDescription = this.$t('acl.noChange')
break
}
case 'delete': {
item.changeDescription = `删除资源类型${item.origin.name}\n描述${item.origin.description}\n权限${item.extra.permission_ids.origin}`
item.changeDescription = `${this.$t('acl.deleteResourceType')}${item.origin.name}\n${this.$t('desc')}: ${item.origin.description}\n${this.$t('acl.permission')}: ${item.extra.permission_ids.origin}`
break
}
}

View File

@ -2,12 +2,12 @@
<CustomDrawer
width="800px"
placement="left"
title="资源列表"
:title="$t('acl.resourceList')"
@close="handleCancel"
:visible="visible"
:hasFooter="false"
>
<a-form-item label="资源类型" :label-col="{ span: 2 }" :wrapper-col="{ span: 14 }">
<a-form-item :label="$t('acl.resourceType')" :label-col="{ span: 4 }" :wrapper-col="{ span: 14 }">
<a-select v-model="typeSelected" style="width:100%" @change="refresh">
<a-select-option v-for="type in resourceTypes" :value="type.id" :key="type.id">{{ type.name }}</a-select-option>
</a-select>
@ -22,7 +22,7 @@
>
<vxe-column
field="name"
title="资源名"
:title="$t('acl.resourceName')"
width="30%"
:filters="[{ data: '' }]"
:filter-method="filterNameMethod"
@ -30,7 +30,7 @@
>
<template #header="{ column }">
<span>{{ column.title }}</span>
<a-tooltip title="复制资源名">
<a-tooltip :title="$t('acl.copyResource')">
<a-icon @click="copyResourceName" class="resource-user-form-copy" theme="filled" type="copy" />
</a-tooltip>
</template>
@ -48,7 +48,7 @@
</template>
</template>
</vxe-column>
<vxe-column field="permissions" title="权限列表" width="70%">
<vxe-column field="permissions" :title="$t('acl.permissionList')" width="70%">
<template #default="{row}">
<a-tag color="cyan" v-for="(r, index) in row.permissions" :key="index">{{ r }}</a-tag>
</template>
@ -58,69 +58,6 @@
<p style="font-size: 14px; line-height: 17px; color: rgba(0, 0, 0, 0.6)">暂无数据</p>
</template>
</vxe-table>
<!-- <a-table
:columns="columns"
:dataSource="records"
:rowKey="record => record.id"
:pagination="false"
ref="rTable"
size="middle"
:scroll="{ y: 300 }"
> -->
<!-- <div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class="custom-filter-dropdown">
<a-input
v-ant-ref="c => searchInput = c"
:placeholder="` ${column.title}`"
:value="selectedKeys[0]"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="() => handleSearch(selectedKeys, confirm, column)"
style="width: 188px; margin-bottom: 8px; display: block;"
/>
<a-button
type="primary"
@click="() => handleSearch(selectedKeys, confirm, column)"
icon="search"
size="small"
style="width: 90px; margin-right: 8px"
>搜索</a-button>
<a-button
@click="() => handleReset(clearFilters, column)"
size="small"
style="width: 90px"
>重置</a-button>
</div>
<a-icon slot="filterIcon" slot-scope="filtered" type="search" :style="{ color: filtered ? '#108ee9' : undefined }" />
<template slot="nameSearchRender" slot-scope="text">
<span v-if="columnSearchText.name">
<template v-for="(fragment, i) in text.toString().split(new RegExp(`(?<=${columnSearchText.name})|(?=${columnSearchText.name})`, 'i'))">
<mark v-if="fragment.toLowerCase() === columnSearchText.name.toLowerCase()" :key="i" class="highlight">{{ fragment }}</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
<template v-else>{{ text }}</template>
</template> -->
<!-- <template slot="permissions" slot-scope="record">
<a-tag color="cyan" v-for="(r, index) in record" :key="index">{{ r }}</a-tag>
</template>
</a-table> -->
<!-- <div
:style="{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}"
>
<a-button :style="{marginRight: '8px'}" @click="handleCancel">
取消
</a-button>
<a-button @click="handleOk" type="primary">确定</a-button>
</div> -->
</CustomDrawer>
</template>
<script>
@ -141,33 +78,6 @@ export default {
name: '',
},
filterName: '',
// columns: [
// {
// title: '资源名',
// field: 'name',
// sorter: false,
// width: '30%',
// // scopedSlots: {
// // customRender: 'nameSearchRender',
// // filterDropdown: 'filterDropdown',
// // filterIcon: 'filterIcon'
// // },
// // onFilter: (value, record) => record.name && record.name.toLowerCase().includes(value.toLowerCase()),
// // onFilterDropdownVisibleChange: (visible) => {
// // if (visible) {
// // setTimeout(() => {
// // this.searchInput.focus()
// // }, 0)
// // }
// // }
// },
// {
// title: '权限列表',
// field: 'permissions',
// width: '70%',
// slots: { default: 'permissions_default' },
// },
// ],
}
},
computed: {
@ -184,14 +94,6 @@ export default {
this.rid = record.id
this.refresh()
},
// handleSearch(selectedKeys, confirm, column) {
// confirm()
// this.columnSearchText[column.dataIndex] = selectedKeys[0]
// },
// handleReset(clearFilters, column) {
// clearFilters()
// this.columnSearchText[column.dataIndex] = ''
// },
loadResourceTypes() {
this.resourceTypes = []
const appId = this.$route.name.split('_')[0]
@ -204,7 +106,6 @@ export default {
this.typeSelected = null
}
})
// .catch(err => this.$httpError(err))
},
handleOk() {
this.visible = false
@ -217,7 +118,6 @@ export default {
}).then(res => {
this.records = res.resources
})
// .catch(err=>this.$httpError(err))
}
},
handleCancel() {
@ -238,7 +138,7 @@ export default {
.join('\n')
this.copy(val, () => {
this.$message.success('复制成功')
this.$message.success(this.$t('copySuccess'))
})
},
copy(value, cb) {

View File

@ -8,40 +8,29 @@
width="500px"
>
<a-form :form="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }" @submit="handleSubmit">
<a-form-item label="角色名">
<a-form-item :label="$t('acl.role')">
<a-input
name="name"
placeholder="角色名"
v-decorator="['name', { rules: [{ required: true, message: '请输入角色名' }] }]"
:placeholder="$t('acl.role_placeholder1')"
v-decorator="['name', { rules: [{ required: true, message: $t('acl.role_placeholder1') }] }]"
/>
</a-form-item>
<a-form-item v-if="$route.name.split('_')[0] !== 'acl'" label="密码">
<a-input name="password" placeholder="密码" v-decorator="['password', { rules: [{ required: false }] }]" />
<a-form-item v-if="$route.name.split('_')[0] !== 'acl'" :label="$t('acl.password')">
<a-input name="password" :placeholder="$t('acl.password')" v-decorator="['password', { rules: [{ required: false }] }]" />
</a-form-item>
<!-- <a-form-item label="继承自">
<a-select
showSearch
v-model="selectedParents"
:filterOption="false"
placeholder="可选择继承角色"
mode="multiple"
>
<a-select-option v-for="role in scrollData" :key="role.id">{{ role.name }}</a-select-option>
</a-select>
</a-form-item> -->
<a-form-item label="继承自">
<a-form-item :label="$t('acl.inheritedFrom')">
<el-select
:style="{ width: '100%' }"
size="small"
v-model="selectedParents"
multiple
filterable
placeholder="可选择继承角色"
:placeholder="$t('acl.selectedParents')"
>
<el-option v-for="role in allRoles" :key="role.id" :value="role.id" :label="role.name"></el-option>
</el-select>
</a-form-item>
<a-form-item label="是否应用管理员">
<a-form-item :label="$t('acl.isAppAdmin')">
<a-switch
@change="onChange"
name="is_app_admin"
@ -53,8 +42,8 @@
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="onClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -93,7 +82,7 @@ export default {
// return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
// },
handleCreate() {
this.drawerTitle = '新增角色'
this.drawerTitle = this.$t('acl.addRole')
this.drawerVisible = true
},
onClose() {
@ -107,7 +96,7 @@ export default {
},
handleEdit(record) {
this.drawerTitle = `编辑${record.name}`
this.drawerTitle = `${this.$t('edit')}: ${record.name}`
this.drawerVisible = true
this.current_id = record.id
const _parents = this.id2parents[record.id]
@ -142,7 +131,7 @@ export default {
updateRole(id, data) {
this.updateParents(id)
updateRoleById(id, { ...data, app_id: this.$route.name.split('_')[0] }).then((res) => {
this.$message.success(`更新成功`)
this.$message.success(this.$t('updateSuccess'))
this.handleOk()
this.onClose()
})
@ -151,7 +140,7 @@ export default {
createRole(data) {
addRole({ ...data, app_id: this.$route.name.split('_')[0] }).then((res) => {
this.$message.success(`添加成功`)
this.$message.success(this.$t('addSuccess'))
this.updateParents(res.id)
this.handleOk()
this.onClose()
@ -171,16 +160,9 @@ export default {
child_ids: [id],
app_id: this.$route.name.split('_')[0],
})
// addParentRole(id, item, { app_id: this.$route.name.split('_')[0] })
// .catch(err => this.requestFailed(err))
}
})
},
// requestFailed(err) {
// console.log(err)
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
},
watch: {},
props: {

View File

@ -4,7 +4,7 @@
ref="child"
:attrList="roleTableAttrList"
:hasSwitch="true"
switchValue="角色关系"
:switchValue="$t('acl.roleRelation')"
@onSwitchChange="onSwitchChange"
@search="handleSearch"
@searchFormReset="searchFormReset"
@ -19,16 +19,16 @@
resizable
:height="`${windowHeight - 310}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" title="操作员" width="130px"></vxe-column>
<vxe-column field="operate_type" title="操作" width="112px">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" :title="$t('acl.operator')" width="130px"></vxe-column>
<vxe-column field="operate_type" :title="$t('operation')" width="112px">
<template #default="{ row }">
<template>
<a-tag :color="handleTagColor(row.operate_type)">{{ operateTypeMap.get(row.operate_type) }}</a-tag>
</template>
</template>
</vxe-column>
<vxe-column :title="checked ? '角色' : '角色'">
<vxe-column :title="checked ? $t('acl.role2') : $t('acl.role2')">
<template #default="{ row }">
<template v-if="!checked">
<a-tag color="blue">{{ row.current.name || row.origin.name }}</a-tag>
@ -40,7 +40,7 @@
</template>
</template>
</vxe-column>
<vxe-column :title="checked ? '继承自' : '管理员'" :width="checked ? '350px' : '80px'">
<vxe-column :title="checked ? $t('acl.inheritedFrom') : $t('acl.admin')" :width="checked ? '350px' : '80px'">
<template #default="{ row }">
<template v-if="!checked">
<a-icon type="check" v-if="row.current.is_app_admin" />
@ -52,14 +52,14 @@
</template>
</template>
</vxe-column>
<vxe-column title="描述" v-if="!checked">
<vxe-column :title="$t('desc')" v-if="!checked">
<template #default="{ row }">
<p>
{{ row.description }}
</p>
</template>
</vxe-column>
<vxe-column field="source" title="来源" width="100px"></vxe-column>
<vxe-column field="source" :title="$t('acl.source')" width="100px"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -105,13 +105,6 @@ export default {
checked: false,
tableData: [],
app_id: this.$route.name.split('_')[0],
operateTypeMap: new Map([
['create', '新建'],
['delete', '删除'],
['update', '修改'],
['role_relation_add', '添加角色关系'],
['role_relation_delete', '删除角色关系'],
]),
colorMap: new Map([
['create', 'green'],
['delete', 'red'],
@ -127,42 +120,53 @@ export default {
start: '',
end: '',
},
roleTableAttrList: [
}
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
['role_relation_add', this.$t('acl.roleRelationAdd')],
['role_relation_delete', this.$t('acl.roleRelationDelete')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
},
roleTableAttrList() {
return [
{
alias: '日期',
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '操作员',
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: '操作',
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ 新建: 'create' },
{ 修改: 'update' },
{ 删除: 'delete' },
{ 添加角色关系: 'role_relation_add' },
{ 删除角色关系: 'role_relation_delete' },
{ [this.$t('create')]: 'create' },
{ [this.$t('update')]: 'update' },
{ [this.$t('delete')]: 'delete' },
{ [this.$t('acl.roleRelationAdd')]: 'role_relation_add' },
{ [this.$t('acl.roleRelationDelete')]: 'role_relation_delete' },
],
},
],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
]
},
},
async created() {
@ -273,7 +277,7 @@ export default {
switch (operate_type) {
// create
case 'create': {
item.description = `新建角色${item.current.name}`
item.description = `${this.$t('acl.addRole')}${item.current.name}`
break
}
case 'update': {
@ -283,15 +287,15 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
const str = ` ${key} : 改为 ${newVal} `
const str = ` ${key} : -> ${newVal} `
item.description += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} `
item.description += str
const str = ` ${key} : ${oldVal} -> ${newVal} `
item.description += ` ${key} : ${oldVal} -> ${newVal} `
}
}
}
if (!item.description) item.description = '没有修改'
if (!item.description) item.description = this.$t('acl.noChange')
break
}
case 'delete': {
@ -321,7 +325,7 @@ export default {
resourceMap.forEach((value, key) => {
permsArr.push(`${id2resources[key].name}${value}`)
})
item.description = `继承者${child_ids}\n继承自${parent_ids}\n涉及资源及权限\n${permsArr.join(`\n`)}`
item.description = `${this.$t('acl.heir')}${child_ids}\n${this.$t('acl.inheritedFrom')}${parent_ids}\n${this.$t('acl.involvingRP')}\n${permsArr.join(`\n`)}`
break
}
}

View File

@ -19,7 +19,7 @@
@popupScroll="loadMoreData(attr.name, $event)"
@search="(value) => fetchData(value, attr.name)"
v-model="queryParams[attr.name]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-if="attr.is_choice"
show-search
:filter-option="filterOption"
@ -38,7 +38,6 @@
@change="onChange"
:style="{ width: '100%' }"
format="YYYY-MM-DD HH:mm:ss"
:placeholder="['开始时间', '结束时间']"
:show-time="{
hideDisabledOptions: true,
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
@ -67,7 +66,7 @@
@popupScroll="loadMoreData(item.name, $event)"
@search="(value) => fetchData(value, item.name)"
v-model="queryParams[item.name]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-if="item.is_choice"
show-search
:filter-option="filterOption"
@ -85,7 +84,7 @@
:style="{ width: '100%' }"
@change="onChange"
format="YYYY-MM-DD HH:mm"
:placeholder="['开始时间', '结束时间']"
:placeholder="[$t('acl.startAt'), $t('acl.endAt')]"
v-else-if="valueTypeMap[item.value_type] == 'date' || valueTypeMap[item.value_type] == 'datetime'"
:show-time="{
hideDisabledOptions: true,
@ -107,13 +106,13 @@
v-model="checked"
/>
<a-button :style="{ marginLeft: '8px' }" type="primary" html-type="submit" @click="handleSearch">
查询
{{ $t('query') }}
</a-button>
<a-button :style="{ marginLeft: '8px' }" @click="handleReset">
重置
{{ $t('reset') }}
</a-button>
<a :style="{ marginLeft: '8px', fontSize: '12px' }" @click="toggle" v-if="attrList.length >= 5">
{{ expand ? '隐藏' : '展开' }} <a-icon :type="expand ? 'up' : 'down'" />
{{ expand ? $t('expand') : $t('expand') }} <a-icon :type="expand ? 'up' : 'down'" />
</a>
</a-col>
</a-row>

View File

@ -2,30 +2,30 @@
<CustomDrawer
@close="handleClose"
width="500"
:title="`${triggerId ? '修改' : '新建'}触发器`"
:title="`${triggerId ? $t('update') : $t('create')}${$t('acl.trigger')}`"
:visible="visible"
:maskClosable="false"
>
<a-form :form="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 15 }">
<a-form-item label="触发器名">
<a-input size="large" v-decorator="['name', { rules: [{ required: true, message: '请输入触发器名' }] }]">
<a-form-item :label="$t('name')">
<a-input size="large" v-decorator="['name', { rules: [{ required: true, message: $t('acl.triggerNameInput') }] }]">
</a-input>
</a-form-item>
<a-form-item label="资源名">
<a-input size="large" v-decorator="['wildcard']" placeholder="优先正则模式(次通配符)"> </a-input>
<a-form-item :label="$t('acl.resourceName')">
<a-input size="large" v-decorator="['wildcard']" :placeholder="$t('acl.triggerTips1')"> </a-input>
</a-form-item>
<a-form-item label="创建人">
<a-form-item :label="$t('acl.creator')">
<el-select :style="{ width: '100%' }" filterable multiple v-decorator="['uid']">
<template v-for="role in roles">
<el-option v-if="role.uid" :key="role.id" :value="role.uid" :label="role.name">{{ role.name }}</el-option>
</template>
</el-select>
</a-form-item>
<a-form-item label="资源类型">
<a-form-item :label="$t('acl.resourceType')">
<el-select
:style="{ width: '100%' }"
@change="handleRTChange"
v-decorator="['resource_type_id', { rules: [{ required: true, message: '请选择资源类型' }] }]"
v-decorator="['resource_type_id', { rules: [{ required: true, message: $t('acl.pleaseSelectType') }] }]"
>
<el-option
v-for="resourceType in resourceTypeList"
@ -34,25 +34,25 @@
:label="resourceType.name"
></el-option>
</el-select>
<a-tooltip title="查看正则匹配结果">
<a-tooltip :title="$t('acl.viewMatchResult')">
<a class="trigger-form-pattern" @click="handlePattern"><a-icon type="eye"/></a>
</a-tooltip>
</a-form-item>
<a-form-item label="角色">
<a-form-item :label="$t('acl.role2')">
<el-select
:style="{ width: '100%' }"
filterable
multiple
v-decorator="['roles', { rules: [{ required: true, message: '请选择角色' }] }]"
v-decorator="['roles', { rules: [{ required: true, message: $t('acl.role_placeholder2') }] }]"
>
<el-option v-for="role in roles" :key="role.id" :value="role.id" :label="role.name"></el-option>
</el-select>
</a-form-item>
<a-form-item label="权限">
<a-form-item :label="$t('acl.permission')">
<el-select
:style="{ width: '100%' }"
multiple
v-decorator="['permissions', { rules: [{ required: true, message: '请选择权限' }] }]"
v-decorator="['permissions', { rules: [{ required: true, message: $t('acl.permission_placeholder') }] }]"
>
<el-option
v-for="perm in selectResourceTypePerms"
@ -62,13 +62,13 @@
></el-option>
</el-select>
</a-form-item>
<a-form-item label="启用/禁用">
<a-form-item :label="$t('acl.enable')/$t('acl.disable')">
<a-switch v-decorator="['enabled', { rules: [], valuePropName: 'checked', initialValue: true }]" />
</a-form-item>
</a-form>
<div class="custom-drawer-bottom-action">
<a-button @click="handleClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">提交</a-button>
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('submit') }}</a-button>
</div>
<TriggerPattern ref="triggerPattern" :roles="roles" />
</CustomDrawer>
@ -159,13 +159,13 @@ export default {
if (this.triggerId) {
updateTrigger(this.triggerId, { ...values, app_id: this.app_id }).then((res) => {
this.visible = false
this.$message.success('修改成功!')
this.$message.success(this.$t('updateSuccess'))
this.$emit('refresh')
})
} else {
addTrigger({ ...values, app_id: this.app_id }).then((res) => {
this.visible = false
this.$message.success('创建成功!')
this.$message.success(this.$t('addSuccess'))
this.$emit('refresh')
})
}

View File

@ -16,23 +16,23 @@
resizable
:height="`${windowHeight - 310}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="trigger_id" width="250px" title="触发器">
<vxe-column field="trigger_id" width="250px" :title="$t('acl.trigger')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.changeDescription }}
@ -95,13 +95,6 @@ export default {
app_id: this.$route.name.split('_')[0],
loading: true,
tableData: [],
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
['trigger_apply', '应用'],
['trigger_cancel', '取消'],
]),
colorMap: new Map([
['create', 'green'],
['delete', 'red'],
@ -109,41 +102,6 @@ export default {
['trigger_apply', 'green'],
['trigger_cancel', 'red'],
]),
triggerTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ 新建: 'create' },
{ 修改: 'update' },
{ 删除: 'delete' },
{ 应用: 'trigger_apply' },
{ 取消: 'trigger_cancel' },
],
},
{
alias: '触发器',
is_choice: true,
name: 'trigger_id',
value_type: '2',
choice_value: this.allTriggers,
},
],
queryParams: {
page: 1,
page_size: 50,
@ -164,12 +122,58 @@ export default {
},
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
['trigger_apply', this.$t('acl.apply')],
['trigger_cancel', this.$t('cancel')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
},
triggerTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: this.allUsers,
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ [this.$t('create')]: 'create' },
{ [this.$t('update')]: 'update' },
{ [this.$t('delete')]: 'delete' },
{ [this.$t('acl.apply')]: 'trigger_apply' },
{ [this.$t('cancel')]: 'trigger_cancel' },
],
},
{
alias: this.$t('acl.trigger'),
is_choice: true,
name: 'trigger_id',
value_type: '2',
choice_value: this.allTriggers,
},
]
},
},
methods: {
async getTable(queryParams) {
@ -220,9 +224,9 @@ export default {
const str = item.current.roles
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => this.allRolesMap.get(Number(i))).join('')
item.changeDescription = `新增触发器${item.current.name}\n资源类型${this.allResourceTypesMap.get(
item.changeDescription = `${this.$t('acl.addTrigger')}: ${item.current.name}\n${this.$t('acl.resourceType')}: ${this.allResourceTypesMap.get(
item.current.resource_type_id
)}资源名${item.current.wildcard}角色[${newStr}]\n权限${item.current.permissions}\n状态${
)}this.$t('acl.resourceName')${item.current.wildcard}${this.$t('acl.role2')}: [${newStr}]\n${this.$t('acl.permission')}: ${item.current.permissions}\n${this.$t('status')}: ${
item.current.enabled
}`
break
@ -234,24 +238,24 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
const str = ` ${key} : 改为 ${newVal} `
const str = ` ${key} : -> ${newVal} `
item.changeDescription += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} `
item.changeDescription += str
const str = ` ${key} : ${oldVal} -> ${newVal} `
item.changeDescription += ` ${key} : ${oldVal} -> ${newVal} `
}
}
}
if (!item.changeDescription) item.changeDescription = '没有修改'
if (!item.changeDescription) item.changeDescription = this.$t('acl.noChange')
break
}
case 'delete': {
const str = item.origin.roles
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => this.allRolesMap.get(Number(i))).join('')
item.changeDescription = `删除触发器${item.origin.name}\n资源类型${this.allResourceTypesMap.get(
item.changeDescription = `${this.$t('acl.deleteTrigger')}: ${item.origin.name}\n${this.$t('acl.resourceType')}: ${this.allResourceTypesMap.get(
item.origin.resource_type_id
)}资源名${item.origin.wildcard}角色[${newStr}]\n权限${item.origin.permissions}\n状态${
)}this.$t('acl.resourceName')${item.origin.wildcard}${this.$t('acl.role2')}: [${newStr}]\n${this.$t('acl.permission')}: ${item.origin.permissions}\n${this.$t('status')}: ${
item.origin.enabled
}`
break
@ -260,9 +264,9 @@ export default {
const str = item.current.roles
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => this.allRolesMap.get(Number(i))).join('')
item.changeDescription = `应用触发器${item.current.name}\n资源类型${this.allResourceTypesMap.get(
item.changeDescription = `${this.$t('acl.applyTrigger')}: ${item.current.name}\n${this.$t('acl.resourceType')}: ${this.allResourceTypesMap.get(
item.current.resource_type_id
)}资源名${item.current.wildcard}角色[${newStr}]\n权限${item.current.permissions}\n状态${
)}this.$t('acl.resourceName')${item.current.wildcard}${this.$t('acl.role2')}: [${newStr}]\n${this.$t('acl.permission')}: ${item.current.permissions}\n${this.$t('status')}: ${
item.current.enabled
}`
break
@ -271,9 +275,9 @@ export default {
const str = item.current.roles
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => this.allRolesMap.get(Number(i))).join('')
item.changeDescription = `取消触发器${item.current.name}\n资源类型${this.allResourceTypesMap.get(
item.changeDescription = `${this.$t('acl.cancelTrigger')}: ${item.current.name}\n${this.$t('acl.resourceType')}: ${this.allResourceTypesMap.get(
item.current.resource_type_id
)}资源名${item.current.wildcard}角色[${newStr}]\n权限${item.current.permissions}\n状态${
)}this.$t('acl.resourceName')${item.current.wildcard}${this.$t('acl.role2')}: [${newStr}]\n${this.$t('acl.permission')}: ${item.current.permissions}\n${this.$t('status')}: ${
item.current.enabled
}`
break

View File

@ -1,7 +1,7 @@
<template>
<CustomDrawer
:hasFooter="false"
title="正则匹配结果"
:title="$t('acl.viewMatchResult')"
:visible="patternVisible"
width="500"
@close="
@ -16,16 +16,16 @@
class="ops-stripe-table"
:data="tableData"
:max-height="`${windowHeight - 110}px`">
<vxe-table-column field="name" title="资源名"></vxe-table-column>
<vxe-table-column field="uid" title="创建人">
<vxe-table-column field="name" :title="$t('acl.resourceName')"></vxe-table-column>
<vxe-table-column field="uid" :title="$t('acl.creator')">
<template #default="{row}">
{{ getRoleName(row.uid) }}
</template>
</vxe-table-column>
<vxe-table-column field="created_at" title="创建时间"></vxe-table-column>
<vxe-table-column field="created_at" :title="$t('created_at')"></vxe-table-column>
<template slot="empty">
<img :src="require(`@/assets/data_empty.png`)" />
<p style="font-size: 14px; line-height: 17px; color: rgba(0, 0, 0, 0.6)">暂无数据</p>
<p style="font-size: 14px; line-height: 17px; color: rgba(0, 0, 0, 0.6)">{{ $t('noData') }}</p>
</template>
</vxe-table>
</CustomDrawer>

View File

@ -8,33 +8,38 @@
width="500px"
>
<a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="用户名(英文)">
<a-form-item :label="$t('acl.username')">
<a-input
name="username"
placeholder="英文名"
v-decorator="['username', { rules: [{ required: true, message: '请输入用户名' }] }]"
:placeholder="$t('acl.username_placeholder')"
v-decorator="['username', { rules: [{ required: true, message: $t('acl.username_placeholder') }] }]"
/>
</a-form-item>
<a-form-item label="中文名">
<a-input name="nickname" v-decorator="['nickname', { rules: [] }]" />
<a-form-item :label="$t('acl.nickname')">
<a-input
name="nickname"
:placeholder="$t('acl.nickname_placeholder')"
v-decorator="['nickname', { rules: [] }]"
/>
</a-form-item>
<a-form-item label="密码">
<a-form-item :label="$t('acl.password')">
<a-input
type="password"
name="password"
v-decorator="['password', { rules: [{ required: true, message: '请输入密码' }] }]"
:placeholder="$t('acl.password_placeholder')"
v-decorator="['password', { rules: [{ required: true, message: $t('acl.password_placeholder') }] }]"
/>
</a-form-item>
<a-form-item label="部门">
<a-form-item :label="$t('acl.department')">
<a-input name="department" v-decorator="['department', { rules: [] }]" />
</a-form-item>
<a-form-item label="小组">
<a-form-item :label="$t('acl.group')">
<a-input name="catalog" v-decorator="['catalog', { rules: [] }]" />
</a-form-item>
<a-form-item label="邮箱">
<a-form-item :label="$t('acl.email')">
<a-input
name="email"
v-decorator="[
@ -43,11 +48,11 @@
rules: [
{
type: 'email',
message: '请输入正确的邮箱!',
message: $t('acl.email_placeholder'),
},
{
required: true,
message: '请输入邮箱',
message: $t('acl.email_placeholder'),
},
],
},
@ -55,14 +60,14 @@
/>
</a-form-item>
<a-form-item label="手机号码">
<a-form-item :label="$t('acl.mobile')">
<a-input
name="mobile"
v-decorator="['mobile', { rules: [{ message: '请输入正确的手机号码', pattern: /^1\d{10}$/ }] }]"
v-decorator="['mobile', { rules: [{ message: $t('acl.mobileTips'), pattern: /^1\d{10}$/ }] }]"
/>
</a-form-item>
<a-form-item label="是否锁定">
<a-form-item :label="$t('acl.isBlock')">
<a-switch @change="onChange" name="block" v-decorator="['block', { rules: [], valuePropName: 'checked' }]" />
</a-form-item>
@ -71,8 +76,8 @@
</a-form-item>
<div class="custom-drawer-bottom-action">
<a-button @click="onClose">取消</a-button>
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="onClose">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</a-form>
</CustomDrawer>
@ -85,7 +90,6 @@ export default {
name: 'AttributeForm',
data() {
return {
drawerTitle: '新增用户',
drawerVisible: false,
}
},
@ -94,7 +98,11 @@ export default {
this.form = this.$form.createForm(this)
},
computed: {},
computed: {
drawerTitle() {
return this.$t('acl.addUser')
},
},
mounted() {},
methods: {
handleCreate() {
@ -142,7 +150,7 @@ export default {
},
updateUser(attrId, data) {
updateUserById(attrId, data).then((res) => {
this.$message.success(`更新成功`)
this.$message.success(this.$t('updateSuccess'))
this.handleOk()
this.onClose()
})
@ -151,17 +159,11 @@ export default {
createUser(data) {
addUser(data).then((res) => {
this.$message.success(`添加成功`)
this.$message.success(this.$t('addSuccess'))
this.handleOk()
this.onClose()
})
// .catch(err => this.requestFailed(err))
},
// requestFailed (err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// }
},
watch: {},
props: {

View File

@ -1,6 +1,6 @@
<template>
<CustomDrawer :closable="true" :visible="visible" width="500px" @close="handleClose" title="组用户">
<a-form-item label="添加用户" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
<CustomDrawer :closable="true" :visible="visible" width="500px" @close="handleClose" :title="$t('acl.groupUser')">
<a-form-item :label="$t('acl.addUser')" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
<a-row>
<a-col span="15">
<el-select v-model="selectedChildrenRole" multiple collapse-tags size="small" filterable>
@ -14,14 +14,14 @@
</el-select>
</a-col>
<a-col span="5" offset="1">
<a-button style="display: inline-block" @click="handleAddRole">确定</a-button>
<a-button style="display: inline-block" @click="handleAddRole">{{ $t('confirm') }}</a-button>
</a-col>
</a-row>
</a-form-item>
<a-card>
<a-row :gutter="24" v-for="(record, index) in records" :key="record.id" :style="{ marginBottom: '5px' }">
<a-col :span="20">{{ index + 1 }}{{ record.nickname }}</a-col>
<a-col :span="4"><a-button type="danger" size="small" @click="handleRevokeUser(record)">移除</a-button></a-col>
<a-col :span="4"><a-button type="danger" size="small" @click="handleRevokeUser(record)">{{ $t('acl.remove') }}</a-button></a-col>
</a-row>
</a-card>
</CustomDrawer>
@ -63,34 +63,22 @@ export default {
this.visible = true
this.loadRecords(roleId)
},
// filterOption(input, option) {
// return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
// },
async handleAddRole() {
// await this.selectedChildrenRole.forEach(item => {
// addParentRole(item, this.roleId, { app_id: this.$route.name.split('_')[0] }).then(res => {
// this.$message.success('添加成功')
// })
// // .catch(err=>{
// // this.$httpError(err)
// // })
// })
await addBatchParentRole(this.roleId, {
child_ids: this.selectedChildrenRole,
app_id: this.$route.name.split('_')[0],
})
this.loadRecords(this.roleId)
this.$message.success('添加完成')
this.$message.success(this.$t('addSuccess'))
this.selectedChildrenRole = []
},
handleRevokeUser(record) {
const that = this
this.$confirm({
content: '是否确定要移除该用户',
content: that.$t('acl.deleteUserConfirm'),
onOk() {
delParentRole(record.role.id, that.roleId, { app_id: that.$route.name.split('_')[0] }).then((res) => {
that.$message.success('删除成功!')
that.$message.success(that.$t('deleteSuccess'))
that.loadRecords(that.roleId)
})
// .catch(err=>that.$httpError(err))

View File

@ -1,19 +1,19 @@
<template>
<div class="acl-operation-history">
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="权限变更">
<a-tab-pane key="1" :tab="$t('acl.permissionChange')">
<permisson-table></permisson-table>
</a-tab-pane>
<a-tab-pane key="2" tab="角色变更">
<a-tab-pane key="2" :tab="$t('acl.roleChange')">
<role-history-table></role-history-table>
</a-tab-pane>
<a-tab-pane key="3" tab="资源变更">
<a-tab-pane key="3" :tab="$t('acl.resourceChange')">
<resource-history-table></resource-history-table>
</a-tab-pane>
<a-tab-pane key="4" tab="资源类型变更">
<a-tab-pane key="4" :tab="$t('acl.resourceTypeChange')">
<resource-type-history-table></resource-type-history-table>
</a-tab-pane>
<a-tab-pane key="5" tab="触发器变更">
<a-tab-pane key="5" :tab="$t('acl.triggerChange')">
<trigger-history-table></trigger-history-table>
</a-tab-pane>
</a-tabs>

View File

@ -22,22 +22,22 @@
:height="`${windowHeight - windowHeightMinus}px`"
:scroll-y="{ enabled: false }"
>
<vxe-column field="created_at" width="144px" title="操作时间">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')">
<template #default="{ row }">
<span>{{ row.deleted_at || row.updated_at || row.created_at }}</span>
</template>
</vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="row.operate_type === 'grant' ? 'green' : 'red'">{{
operateTypeMap.get(row.operate_type)
}}</a-tag>
</template>
</vxe-column>
<vxe-column field="rid" title="用户"></vxe-column>
<vxe-column field="resource_type_id" title="资源类型"></vxe-column>
<vxe-column field="resources" title="资源">
<vxe-column field="rid" :title="$t('user')"></vxe-column>
<vxe-column field="resource_type_id" :title="$t('acl.resourceType')"></vxe-column>
<vxe-column field="resources" :title="$t('acl.resource')">
<template #default="{ row }">
<template v-if="row.resource_ids.length > 0">
<a-tooltip placement="top">
@ -56,14 +56,14 @@
</template>
</template>
</vxe-column>
<vxe-column title="权限">
<vxe-column :title="$t('acl.permission')">
<template #default="{ row }">
<a-tag v-for="(perm, index) in row.permission_ids" :key="'perms_' + perm + index">
{{ perm }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -108,10 +108,6 @@ export default {
allRolesMap: new Map(),
allUsersMap: new Map(),
allResourceTypesMap: new Map(),
operateTypeMap: new Map([
['grant', '授权'],
['revoke', '撤销'],
]),
queryParams: {
page: 1,
page_size: 50,
@ -120,57 +116,63 @@ export default {
},
permissionTableAttrList: [
{
alias: '日期',
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '应用',
alias: this.$t('acl.app'),
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作员',
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: '用户',
alias: this.$t('user'),
is_choice: true,
name: 'rid',
value_type: '2',
choice_value: [],
},
{
alias: '资源类型',
alias: this.$t('acl.resourceType'),
is_choice: true,
name: 'resource_type_id',
value_type: '2',
choice_value: [],
},
{
alias: '资源',
alias: this.$t('acl.resource'),
is_choice: true,
name: 'resource_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作',
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 授权: 'grant' }, { 撤销: 'revoke' }],
choice_value: [{ [this.$t('grant')]: 'grant' }, { [this.$t('acl.cancel')]: 'revoke' }],
},
],
}
},
computed: {
operateTypeMap() {
return new Map([
['grant', this.$t('grant')],
['revoke', this.$t('acl.cancel')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
@ -183,10 +185,10 @@ export default {
},
async created() {
this.$watch(
function() {
function () {
return this.permissionTableAttrList[3].choice_value
},
function() {
function () {
delete this.$refs.child.queryParams.rid
delete this.$refs.child.queryParams.resource_type_id
delete this.$refs.child.queryParams.resource_id

View File

@ -4,7 +4,7 @@
ref="child"
:attrList="resourceTableAttrList"
:hasSwitch="true"
switchValue=""
:switchValue="$t('acl.group2')"
@onSwitchChange="onSwitchChange"
@expandChange="handleExpandChange"
@search="handleSearch"
@ -24,30 +24,30 @@
:data="tableData"
:height="`${windowHeight - windowHeightMinus}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="link_id" title="资源名">
<vxe-column field="link_id" :title="$t('acl.resouceName')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.description }}
</p>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -87,47 +87,6 @@ export default {
allApps: [],
allUsersMap: new Map(),
allResourcesMap: new Map(),
resourceTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '应用',
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: '资源名',
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 新建: 'create' }, { 修改: 'update' }, { 删除: 'delete' }],
},
],
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
]),
colorMap: new Map([
['create', 'green'],
['update', 'orange'],
@ -144,10 +103,10 @@ export default {
},
async created() {
this.$watch(
function() {
function () {
return this.resourceTableAttrList[3].choice_value
},
function() {
function () {
delete this.$refs.child.queryParams.link_id
}
)
@ -158,6 +117,13 @@ export default {
this.$refs.xTable.$el.querySelector('.vxe-table--body-wrapper').scrollTop = 0
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
@ -167,6 +133,44 @@ export default {
tableDataLength() {
return this.tableData.length
},
resourceTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.app'),
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.resourceName'),
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ [this.$t('create')]: 'create' }, { [this.$t('update')]: 'update' }, { [this.$t('delete')]: 'delete' }],
},
]
},
},
methods: {
async getTable(queryParams) {
@ -347,7 +351,7 @@ export default {
switch (operate_type) {
// create
case 'create': {
item.description = `新建资源${item.current.name}`
item.description = `${this.$t('acl.newResource')}${item.current.name}`
break
}
case 'update': {
@ -357,10 +361,10 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
const str = ` ${key} : 改为 ${newVal} `
const str = ` ${key} : -> ${newVal} `
item.description += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} `
const str = ` ${key} : ${oldVal} -> ${newVal} `
item.description += str
}
}
@ -369,18 +373,18 @@ export default {
const currentResource_ids = item.currentResource_ids
if (!_.isEqual(originResource_ids, currentResource_ids)) {
if (originResource_ids.length === 0) {
const str = ` resource_ids : 新增 ${currentResource_ids} `
const str = ` resource_ids : ${this.$t('new')} ${currentResource_ids} `
item.description += str
} else {
const str = ` resource_ids : ${originResource_ids} 改为 ${currentResource_ids} `
const str = ` resource_ids : ${originResource_ids} -> ${currentResource_ids} `
item.description += str
}
}
if (!item.description) item.description = '没有修改'
if (!item.description) item.description = this.$t('acl.noChange')
break
}
case 'delete': {
item.description = `删除资源${item.origin.name}`
item.description = `${this.$t('acl.deleteResource')}${item.origin.name}`
break
}
}

View File

@ -18,30 +18,30 @@
:loading="loading"
:height="`${windowHeight - windowHeightMinus}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="link_id" width="159px" title="资源类型名">
<vxe-column field="link_id" width="159px" :title="$t('acl.resourceTypeName')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.changeDescription }}
</p>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -78,47 +78,6 @@ export default {
allApps: [],
allUsersMap: new Map(),
allResourcesMap: new Map(),
resourceTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '应用',
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: '资源类型',
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ 新建: 'create' }, { 修改: 'update' }, { 删除: 'delete' }],
},
],
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
]),
colorMap: new Map([
['create', 'green'],
['update', 'orange'],
@ -135,10 +94,10 @@ export default {
},
async created() {
this.$watch(
function() {
function () {
return this.resourceTableAttrList[3].choice_value
},
function() {
function () {
delete this.$refs.child.queryParams.link_id
}
)
@ -149,6 +108,13 @@ export default {
this.$refs.xTable.$el.querySelector('.vxe-table--body-wrapper').scrollTop = 0
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
@ -158,6 +124,44 @@ export default {
tableDataLength() {
return this.tableData.length
},
resourceTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.app'),
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.resourceType'),
is_choice: true,
name: 'link_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [{ [this.$t('create')]: 'create' }, { [this.$t('update')]: 'update' }, { [this.$t('delete')]: 'delete' }],
},
]
},
},
methods: {
async getTable(queryParams) {
@ -283,10 +287,12 @@ export default {
switch (operate_type) {
// create
case 'create': {
const description = item.current?.description === undefined ? '' : item.current?.description
const description = item.current?.description === undefined ? this.$t('acl.none') : item.current?.description
const permission =
item.extra.permission_ids?.current === undefined ? '' : item.extra.permission_ids?.current
item.changeDescription = `新增资源类型${item.current.name}\n描述${description}\n权限${permission}`
item.extra.permission_ids?.current === undefined ? this.$t('acl.none') : item.extra.permission_ids?.current
item.changeDescription = `${this.$t('acl.addReourceType')}${item.current.name}\n${this.$t(
'desc'
)}${description}\n${this.$t('acl.permission')}${permission}`
break
}
case 'update': {
@ -296,27 +302,29 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null || oldVal === '') {
const str = ` ${key} : 改为 ${newVal} \n`
const str = ` ${key} : -> ${newVal} \n`
item.changeDescription += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} \n`
const str = ` ${key} : ${oldVal} -> ${newVal} \n`
item.changeDescription += str
}
}
}
const currentPerms =
item.extra.permission_ids?.current === undefined ? '' : item.extra.permission_ids?.current
const originPerms = item.extra.permission_ids?.origin === undefined ? '' : item.extra.permission_ids?.origin
item.extra.permission_ids?.current === undefined ? this.$t('acl.none') : item.extra.permission_ids?.current
const originPerms = item.extra.permission_ids?.origin === undefined ? this.$t('acl.none') : item.extra.permission_ids?.origin
if (!_.isEqual(currentPerms, originPerms)) {
item.changeDescription += ` permission_ids : ${originPerms} 改为 ${currentPerms} `
item.changeDescription += ` permission_ids : ${originPerms} -> ${currentPerms} `
}
if (!item.changeDescription) item.changeDescription = '没有修改'
if (!item.changeDescription) item.changeDescription = this.$t('acl.noChange')
break
}
case 'delete': {
const description = item.origin?.description === undefined ? '' : item.origin?.description
const permission = item.extra.permission_ids?.origin === undefined ? '' : item.extra.permission_ids?.origin
item.changeDescription = `删除资源类型${item.origin.name}\n描述${description}\n权限${permission}`
const description = item.origin?.description === undefined ? this.$t('acl.none') : item.origin?.description
const permission = item.extra.permission_ids?.origin === undefined ? this.$t('acl.none') : item.extra.permission_ids?.origin
item.changeDescription = `${this.$t('acl.deleteResourceType')}: ${item.origin.name}\n${this.$t(
'desc'
)}${description}\n${this.$t('acl.permission')}: ${permission}`
break
}
}

View File

@ -4,7 +4,7 @@
ref="child"
:attrList="roleTableAttrList"
:hasSwitch="true"
switchValue="角色关系"
:switchValue="$t('acl.roleRelation')"
@onSwitchChange="onSwitchChange"
@search="handleSearch"
@searchFormReset="searchFormReset"
@ -19,16 +19,16 @@
:loading="loading"
:height="`${windowHeight - 310}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="112px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="112px" :title="$t('operation')">
<template #default="{ row }">
<template>
<a-tag :color="handleTagColor(row.operate_type)">{{ operateTypeMap.get(row.operate_type) }}</a-tag>
</template>
</template>
</vxe-column>
<vxe-column :title="checked ? '角色' : '角色'">
<vxe-column :title="checked ? $t('acl.role2') : $t('acl.role2')">
<template #default="{ row }">
<template v-if="!checked">
<a-tag color="blue">{{ row.current.name || row.origin.name }}</a-tag>
@ -40,7 +40,7 @@
</template>
</template>
</vxe-column>
<vxe-column :title="checked ? '继承自' : '管理员'" :width="checked ? '350px' : '80px'">
<vxe-column :title="checked ? $t('acl.inheritedFrom') : $t('acl.admin')" :width="checked ? '350px' : '80px'">
<template #default="{ row }">
<template v-if="!checked">
<a-icon type="check" v-if="row.current.is_app_admin" />
@ -52,14 +52,14 @@
</template>
</template>
</vxe-column>
<vxe-column title="描述" v-if="!checked">
<vxe-column :title="$t('desc')" v-if="!checked">
<template #default="{ row }">
<p>
{{ row.description }}
</p>
</template>
</vxe-column>
<vxe-column field="source" width="100px" title="来源"></vxe-column>
<vxe-column field="source" width="100px" :title="$t('acl.source')"></vxe-column>
</vxe-table>
<pager
:current-page.sync="queryParams.page"
@ -94,13 +94,6 @@ export default {
allRoles: [],
allRolesMap: new Map(),
allUsersMap: new Map(),
operateTypeMap: new Map([
['create', '新建'],
['delete', '删除'],
['update', '修改'],
['role_relation_add', '添加角色关系'],
['role_relation_delete', '删除角色关系'],
]),
colorMap: new Map([
['create', 'green'],
['delete', 'red'],
@ -115,49 +108,60 @@ export default {
start: '',
end: '',
},
roleTableAttrList: [
}
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
['role_relation_add', this.$t('acl.roleRelationAdd')],
['role_relation_delete', this.$t('acl.roleRelationDelete')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
},
roleTableAttrList() {
return [
{
alias: '日期',
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '应用',
alias: this.$t('acl.app'),
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作员',
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: '操作',
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ 新建: 'create' },
{ 修改: 'update' },
{ 删除: 'delete' },
{ 添加角色关系: 'role_relation_add' },
{ 删除角色关系: 'role_relation_delete' },
{ [this.$t('create')]: 'create' },
{ [this.$t('update')]: 'update' },
{ [this.$t('delete')]: 'delete' },
{ [this.$t('acl.roleRelationAdd')]: 'role_relation_add' },
{ [this.$t('acl.roleRelationDelete')]: 'role_relation_delete' },
],
},
],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableDataLength() {
return this.tableData.length
]
},
},
async created() {
@ -277,7 +281,7 @@ export default {
switch (operate_type) {
// create
case 'create': {
item.description = `新建角色${item.current.name}`
item.description = `${this.$t('acl.addRole')}${item.current.name}`
break
}
case 'update': {
@ -287,15 +291,15 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
const str = ` ${key} : 改为 ${newVal} `
const str = ` ${key} : -> ${newVal} `
item.description += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} `
item.description += str
const str = ` ${key} : ${oldVal} -> ${newVal} `
item.description += ` ${key} : ${oldVal} -> ${newVal} `
}
}
}
if (!item.description) item.description = '没有修改'
if (!item.description) item.description = this.$t('acl.noChange')
break
}
case 'delete': {
@ -325,7 +329,7 @@ export default {
resourceMap.forEach((value, key) => {
permsArr.push(`${id2resources[key].name}${value}`)
})
item.description = `继承者${child_ids}\n继承自${parent_ids}\n涉及资源及权限\n${permsArr.join(`\n`)}`
item.description = `${this.$t('acl.heir')}${child_ids}\n${this.$t('acl.inheritedFrom')}${parent_ids}\n${this.$t('acl.involvingRP')}\n${permsArr.join(`\n`)}`
break
}
}

View File

@ -18,23 +18,23 @@
:loading="loading"
:height="`${windowHeight - windowHeightMinus}px`"
>
<vxe-column field="created_at" width="144px" title="操作时间"></vxe-column>
<vxe-column field="operate_uid" width="130px" title="操作员"></vxe-column>
<vxe-column field="operate_type" width="80px" title="操作">
<vxe-column field="created_at" width="144px" :title="$t('acl.operateTime')"></vxe-column>
<vxe-column field="operate_uid" width="130px" :title="$t('acl.operator')"></vxe-column>
<vxe-column field="operate_type" width="100px" :title="$t('operation')">
<template #default="{ row }">
<a-tag :color="handleTagColor(row.operate_type)">
{{ operateTypeMap.get(row.operate_type) }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="trigger_id" width="250px" title="触发器">
<vxe-column field="trigger_id" width="250px" :title="$t('acl.trigger')">
<template #default="{ row }">
<span>
{{ row.current.name || row.origin.name }}
</span>
</template>
</vxe-column>
<vxe-column title="描述">
<vxe-column :title="$t('desc')">
<template #default="{ row }">
<p>
{{ row.changeDescription }}
@ -64,6 +64,7 @@ import { getTriggers } from '@/modules/acl/api/trigger'
import { searchUser } from '@/modules/acl/api/user'
import { searchApp } from '@/modules/acl/api/app'
export default {
name: 'TriggerHistoryTable',
components: { SearchForm, Pager },
data() {
return {
@ -82,13 +83,6 @@ export default {
allResourceTypesMap: new Map(),
allResourcesMap: new Map(),
allTriggersMap: new Map(),
operateTypeMap: new Map([
['create', '新建'],
['update', '修改'],
['delete', '删除'],
['trigger_apply', '应用'],
['trigger_cancel', '取消'],
]),
colorMap: new Map([
['create', 'green'],
['delete', 'red'],
@ -96,48 +90,6 @@ export default {
['trigger_apply', 'green'],
['trigger_cancel', 'red'],
]),
triggerTableAttrList: [
{
alias: '日期',
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: '应用',
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作员',
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: '触发器',
is_choice: true,
name: 'trigger_id',
value_type: '2',
choice_value: [],
},
{
alias: '操作',
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ 新建: 'create' },
{ 修改: 'update' },
{ 删除: 'delete' },
{ 应用: 'trigger_apply' },
{ 取消: 'trigger_cancel' },
],
},
],
queryParams: {
page: 1,
page_size: 50,
@ -162,6 +114,15 @@ export default {
this.$refs.xTable.$el.querySelector('.vxe-table--body-wrapper').scrollTop = 0
},
computed: {
operateTypeMap() {
return new Map([
['create', this.$t('create')],
['update', this.$t('update')],
['delete', this.$t('delete')],
['trigger_apply', this.$t('acl.apply')],
['trigger_cancel', this.$t('cancel')],
])
},
windowHeight() {
return this.$store.state.windowHeight
},
@ -171,6 +132,50 @@ export default {
tableDataLength() {
return this.tableData.length
},
triggerTableAttrList() {
return [
{
alias: this.$t('acl.date'),
is_choice: false,
name: 'datetime',
value_type: '3',
},
{
alias: this.$t('acl.app'),
is_choice: true,
name: 'app_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.operator'),
is_choice: true,
name: 'operate_uid',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('acl.trigger'),
is_choice: true,
name: 'trigger_id',
value_type: '2',
choice_value: [],
},
{
alias: this.$t('operation'),
is_choice: true,
name: 'operate_type',
value_type: '2',
choice_value: [
{ [this.$t('create')]: 'create' },
{ [this.$t('update')]: 'update' },
{ [this.$t('delete')]: 'delete' },
{ [this.$t('acl.apply')]: 'trigger_apply' },
{ [this.$t('cancel')]: 'trigger_cancel' },
],
},
]
},
},
methods: {
async getTable(queryParams) {
@ -270,9 +275,11 @@ export default {
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => id2roles[i].name).join('')
const { name, resource_type_id, wildcard, permissions, enabled } = item.current
item.changeDescription = `新增触发器${name}\n资源类型${
item.changeDescription = `${this.$t('acl.addTrigger')}:${name}\n${this.$t('acl.resourceType')}: ${
id2resource_types[resource_type_id].name
}资源名${wildcard || ''}角色[${newStr}]\n权限${permissions}\n状态${enabled}`
}this.$t('acl.resourceName')${wildcard || ''}${this.$t(
'acl.role2'
)}:[${newStr}]\nthis.$t('acl.permssion')}: ${permissions}\n${this.$t('status')}: ${enabled}`
break
}
case 'update': {
@ -282,15 +289,15 @@ export default {
const oldVal = item.origin[key]
if (!_.isEqual(newVal, oldVal) && key !== 'updated_at' && key !== 'deleted_at' && key !== 'created_at') {
if (oldVal === null) {
const str = ` ${key} : 改为 ${newVal} `
const str = ` ${key} : -> ${newVal} `
item.changeDescription += str
} else {
const str = ` ${key} : ${oldVal} 改为 ${newVal} `
item.changeDescription += str
const str = ` ${key} : ${oldVal} -> ${newVal} `
item.changeDescription += ` ${key} :${oldVal} -> ${newVal} `
}
}
}
if (!item.changeDescription) item.changeDescription = '没有修改'
if (!item.changeDescription) item.changeDescription = this.$t('acl.noChange')
break
}
case 'delete': {
@ -298,9 +305,11 @@ export default {
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => id2roles[i].name).join('')
const { name, resource_type_id, wildcard, permissions, enabled } = item.origin
item.changeDescription = `删除触发器${name}\n资源类型${
item.changeDescription = `${this.$t('acl.deleteTrigger')}: ${name}\n${this.$t('acl.resourceType')}: ${
id2resource_types[resource_type_id].name
}资源名${wildcard || ''}角色[${newStr}]\n权限${permissions}\n状态${enabled}`
}${this.$t('acl.resourceName')}: ${wildcard || ''}${this.$t(
'acl.role2'
)}:[${newStr}]\nthis.$t('acl.permssion')}: ${permissions}\n${this.$t('status')}: ${enabled}`
break
}
case 'trigger_apply': {
@ -308,9 +317,11 @@ export default {
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => id2roles[i].name).join('')
const { name, resource_type_id, wildcard, permissions, enabled } = item.current
item.changeDescription = `应用触发器${name}\n资源类型${
item.changeDescription = `${this.$t('acl.applyTrigger')}: ${name}\n${this.$t('acl.resourceType')}: ${
id2resource_types[resource_type_id].name
}资源名${wildcard || ''}角色[${newStr}]\n权限${permissions}\n状态${enabled}`
}${this.$t('acl.resourceName')}: ${wildcard || ''}${this.$t(
'acl.role2'
)}:[${newStr}]\nthis.$t('acl.permssion')}: ${permissions}\n${this.$t('status')}: ${enabled}`
break
}
case 'trigger_cancel': {
@ -318,9 +329,11 @@ export default {
const newArr = str.slice(1, str.length - 1).split(', ')
const newStr = newArr.map((i) => id2roles[i].name).join('')
const { name, resource_type_id, wildcard, permissions, enabled } = item.current
item.changeDescription = `取消触发器${name}\n资源类型${
item.changeDescription = `${this.$t('acl.cancelTrigger')}: ${name}\n${this.$t('acl.resourceType')}: ${
id2resource_types[resource_type_id].name
}资源名${wildcard || ''}角色[${newStr}]\n权限${permissions}\n状态${enabled}`
}${this.$t('acl.resourceName')}: ${wildcard || ''}${this.$t(
'acl.role2'
)}:[${newStr}]\nthis.$t('acl.permssion')}: ${permissions}\n${this.$t('status')}: ${enabled}`
break
}
}

View File

@ -1,11 +1,13 @@
<template>
<div class="acl-resource-types">
<div class="acl-resource-types-header">
<a-button @click="handleCreate" type="primary" style="margin-right: 0.3rem">{{ btnName }}</a-button>
<a-button @click="handleCreate" type="primary" style="margin-right: 0.3rem">{{
$t('acl.addReourceType')
}}</a-button>
<a-input-search
class="ops-input"
:style="{ display: 'inline', marginLeft: '10px', width: '200px' }"
placeholder="搜索 | 资源类型名"
:placeholder="`${$t('search')} | ${$t('acl.resourceType')}`"
v-model="searchName"
allowClear
@search="
@ -28,28 +30,28 @@
<!-- 1 -->
<vxe-table-column
field="name"
title="资源类型名"
:title="$t('acl.resoureType')"
:min-width="175"
fixed="left"
show-overflow
></vxe-table-column>
<!-- 2 -->
<vxe-table-column field="description" title="描述" :min-width="175"></vxe-table-column>
<vxe-table-column field="description" :title="$t('desc')" :min-width="175"></vxe-table-column>
<!-- 3 -->
<vxe-table-column field="id" title="权限" :min-width="300">
<vxe-table-column field="id" :title="$t('acl.permission')" :min-width="300">
<template #default="{ row }">
<a-tag color="cyan" v-for="perm in id2perms[row.id]" :key="perm.id">{{ perm.name }}</a-tag>
</template>
</vxe-table-column>
<!-- 4 -->
<vxe-table-column field="action" title="操作" :width="100" fixed="right">
<vxe-table-column field="action" :title="$t('operation')" :width="100" fixed="right">
<template #default="{ row }">
<a @click="handleEdit(row)"><a-icon type="edit"/></a>
<a-divider type="vertical" />
<a-popconfirm title="确认删除?" @confirm="handleDelete(row)" okText="" cancelText="">
<a-popconfirm :title="$t('confirmDelete')" @confirm="handleDelete(row)">
<a style="color: red"><a-icon type="delete"/></a>
</a-popconfirm>
</template>
@ -87,42 +89,12 @@ export default {
loading: false,
groups: [],
id2perms: {},
btnName: '新增资源类型',
pageSizeOptions: [10, 25, 50, 100],
tablePage: {
total: 0,
currentPage: 1,
pageSize: 50,
},
tableColumns: [
{
title: '资源类型名',
field: 'name',
minWidth: '175px',
fixed: 'left',
showOverflow: 'tooltip',
},
{
title: '描述',
field: 'description',
minWidth: '175px',
},
{
title: '权限',
field: 'id',
minWidth: '300px',
slots: {
default: 'id_default',
},
},
{
title: '操作',
field: 'action',
minWidth: '175px',
slots: { default: 'action_default' },
fixed: 'right',
},
],
searchName: '',
}
},
@ -200,15 +172,10 @@ export default {
},
deleteResourceType(id) {
deleteResourceTypeById(id).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
this.handleOk()
})
// .catch(err => this.requestFailed(err))
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
handlePageChange({ currentPage, pageSize }) {
this.tablePage.currentPage = currentPage
this.tablePage.pageSize = pageSize

View File

@ -6,10 +6,10 @@
</a-tabs>
<div class="acl-resources-header">
<a-space>
<a-button @click="handleCreate" type="primary">{{ btnName }}</a-button>
<a-button @click="handleCreate" type="primary">{{ $t('acl.addResource') }}</a-button>
<a-input-search
class="ops-input"
placeholder="搜索 | 资源名"
:placeholder="`${$t('search')} | ${$t('acl.resource')}`"
v-model="searchName"
@search="
() => {
@ -20,10 +20,10 @@
></a-input-search>
<div v-if="!!selectedRows.length" class="ops-list-batch-action">
<span @click="handleBatchPerm">授权</span>
<span @click="handleBatchPerm">{{ $t('grant') }}</span>
<a-divider type="vertical" />
<span @click="handleBatchRevoke">权限回收</span>
<span>选取: {{ selectedRows.length }} </span>
<span @click="handleBatchRevoke">{{ $t('acl.revoke') }}</span>
<span>{{ $t('selectRows', { rows: selectedRows.length }) }}</span>
</div>
</a-space>
@ -36,7 +36,7 @@
$refs.resourceBatchPerm.open(currentType.id)
}
"
>便捷授权</a-button
>{{ $t('acl.convenient') }}</a-button
>
<a-switch
v-model="isGroup"
@ -50,7 +50,7 @@
$refs.xTable && $refs.xTable.getVxetableRef().clearCheckboxReserve()
}
"
un-checked-children=""
:un-checked-children="$t('acl.group2')"
></a-switch>
</a-space>
</div>
@ -62,62 +62,63 @@
:data="tableData"
highlight-hover-row
:height="`${windowHeight - 250}px`"
:checkbox-config="{ reserve: true }"
:checkbox-config="{ reserve: true, highlight: true, range: true }"
@checkbox-change="changeCheckbox"
@checkbox-all="changeCheckbox"
@checkbox-range-end="onSelectRangeEnd"
ref="xTable"
row-id="id"
show-overflow
resizable
>
<!-- 1 -->
<vxe-table-column type="checkbox" fixed="left" :width="45"></vxe-table-column>
<vxe-table-column type="checkbox" fixed="left" :width="60"></vxe-table-column>
<!-- 2 -->
<vxe-table-column field="name" title="资源名" :min-widh="150" fixed="left" show-overflow>
<template #title="{ row }">
{{ row.isGroup ? '资源组名' : '资源名' }}
<vxe-table-column field="name" :title="$t('acl.resourceName')" :min-widh="150" fixed="left" show-overflow>
<template #title="{row}">
{{ row.isGroup ? $t('acl.groupName') : $t('acl.resourceName') }}
</template>
</vxe-table-column>
<!-- 3 -->
<vxe-table-column field="user" title="创建者" :min-widh="100"> </vxe-table-column>
<vxe-table-column field="user" :title="$t('acl.creator')" :min-widh="100"> </vxe-table-column>
<!-- 4 -->
<vxe-table-column field="created_at" title="创建时间" :min-widh="220" align="center"> </vxe-table-column>
<vxe-table-column field="created_at" :title="$t('created_at')" :min-widh="220" align="center"> </vxe-table-column>
<!-- 5 -->
<vxe-table-column field="updated_at" title="最后修改时间" :min-widh="220" fixed="center"> </vxe-table-column>
<vxe-table-column field="updated_at" :title="$t('updated_at')" :min-widh="220" fixed="center"> </vxe-table-column>
<!-- 6 -->
<vxe-table-column
field="action"
title="操作"
:title="$t('operation')"
:min-widh="200"
fixed="right"
align="center"
show-overflow>
<template #default="{ row }">
<template #default="{row}">
<span v-show="isGroup">
<a @click="handleDisplayMember(row)">成员</a>
<a @click="handleDisplayMember(row)">{{ $t('acl.member') }}</a>
<a-divider type="vertical" />
<a @click="handleGroupEdit(row)">编辑</a>
<a @click="handleGroupEdit(row)">{{ $t('edit') }}</a>
<a-divider type="vertical" />
</span>
<a-tooltip title="查看授权">
<a @click="handlePerm(row)"><a-icon type="eye" /></a>
<a-tooltip :title="$t('acl.viewAuth')">
<a @click="handlePerm(row)"><a-icon type="eye"/></a>
</a-tooltip>
<a-divider type="vertical" />
<a-tooltip title="授权">
<a-tooltip :title="$t('grant')">
<a :style="{ color: '#4bbb13' }" @click="handlePermManage(row)">
<a-icon type="usergroup-add" />
</a>
</a-tooltip>
<a-divider type="vertical" />
<a-popconfirm title="确认删除?" @confirm="handleDelete(row)" @cancel="cancel" okText="" cancelText="">
<a style="color: red"><a-icon type="delete" /></a>
<a-popconfirm :title="$t('confirmDelete')" @confirm="handleDelete(row)" @cancel="cancel" :okText="$t('yes')" :cancelText="$t('no')">
<a style="color: red"><a-icon type="delete"/></a>
</a-popconfirm>
</template>
</vxe-table-column>
@ -135,9 +136,9 @@
</vxe-pager>
</a-spin>
</div>
<div v-else style="text-align: center; margin-top: 20%">
<a-icon style="font-size: 50px; margin-bottom: 20px; color: orange" type="info-circle" />
<h3>暂无类型信息请先添加资源类型</h3>
<div v-else style="text-align: center;margin-top:20%">
<a-icon style="font-size:50px; margin-bottom: 20px; color: orange" type="info-circle" />
<h3>{{ $t('acl.addTypeTips') }}</h3>
</div>
<resourceForm ref="resourceForm" @fresh="handleOk"> </resourceForm>
<resourcePermForm ref="resourcePermForm"> </resourcePermForm>
@ -187,7 +188,6 @@ export default {
pageSize: 50,
},
tableData: [],
btnName: '新增资源',
isGroup: false,
allResourceTypes: [],
currentType: { id: 0 },
@ -196,7 +196,6 @@ export default {
selectedRows: [],
}
},
beforeCreate() {
this.form = this.$form.createForm(this)
},
@ -281,22 +280,16 @@ export default {
deleteResource(id) {
if (!this.isGroup) {
deleteResourceById(id, { app_id: this.$route.name.split('_')[0] }).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
this.handleOk()
})
// .catch(err => this.requestFailed(err))
} else {
deleteResourceGroup(id).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
this.handleOk()
})
// .catch(err => this.requestFailed(err))
}
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
cancel() {},
handlePageChange({ currentPage, pageSize }) {
this.tablePage.currentPage = currentPage
@ -309,6 +302,10 @@ export default {
.getVxetableRef()
.getCheckboxRecords()
.concat(this.$refs.xTable.getVxetableRef().getCheckboxReserveRecords())
console.log(this.selectedRows)
},
onSelectRangeEnd({ records }) {
this.selectedRows = records
},
handleBatchPerm() {
this.$refs['resourcePermManageForm'].editPerm(this.selectedRows, this.isGroup)
@ -322,7 +319,7 @@ export default {
},
},
watch: {
'$route.name': function (newName, oldName) {
'$route.name': function(newName, oldName) {
this.isGroup = false
this.tablePage = {
total: 0,

View File

@ -1,12 +1,12 @@
<template>
<div class="acl-roles">
<div class="acl-roles-header">
<a-button @click="handleCreate" type="primary">{{ btnName }}</a-button>
<a-button @click="handleCreate" type="primary">{{ $t('acl.addVisualRole') }}</a-button>
<a-input-search
class="ops-input"
allowClear
:style="{ display: 'inline', marginLeft: '10px', width: '200px' }"
placeholder="搜索 | 角色名"
:placeholder="`${$t('search')} | ${$t('acl.role')}`"
v-model="searchName"
@search="
() => {
@ -15,7 +15,7 @@
}
"
></a-input-search>
<a-checkbox :checked="is_all" @click="handleClickBoxChange">所有角色</a-checkbox>
<a-checkbox :checked="is_all" @click="handleClickBoxChange">{{ $t('acl.allRole') }}</a-checkbox>
</div>
<a-spin :spinning="loading">
<ops-table
@ -31,22 +31,23 @@
>
<vxe-table-column
field="name"
title="角色名"
:title="$t('acl.role')"
:min-width="150"
align="left"
fixed="left"
sortable
show-overflow>
show-overflow
>
</vxe-table-column>
<!-- 2 -->
<vxe-table-column field="is_app_admin" title="管理员" :min-width="100" align="center">
<vxe-table-column field="is_app_admin" :title="$t('admin')" :min-width="100" align="center">
<template #default="{row}">
<a-icon type="check" v-if="row.is_app_admin" />
</template>
</vxe-table-column>
<vxe-table-column field="id" title="继承自" :min-width="150">
<vxe-table-column field="id" :title="$t('acl.inheritedFrom')" :min-width="150">
<template #default="{row}">
<a-tag color="cyan" v-for="role in id2parents[row.id]" :key="role.id">{{ role.name }}</a-tag>
</template>
@ -54,12 +55,12 @@
<vxe-table-column
field="uid"
title="虚拟角色"
:width="100"
:title="$t('acl.visualRole')"
:width="120"
align="center"
:filters="[
{ label: '', value: 1 },
{ label: '', value: 0 },
{ label: $t('yes'), value: 1 },
{ label: $t('no'), value: 0 },
]"
:filterMultiple="false"
:filter-method="
@ -69,14 +70,14 @@
"
>
<template #default="{row}">
{{ row.uid ? '' : '' }}
{{ row.uid ? $t('no') : $t('yes') }}
</template>
</vxe-table-column>
<vxe-table-column field="action" title="操作" :width="120" fixed="right">
<vxe-table-column field="action" :title="$t('operation')" :width="120" fixed="right">
<template #default="{row}">
<a-space>
<a-tooltip title="资源列表">
<a-tooltip :title="$t('acl.resourceList')">
<a
v-if="$route.name !== 'acl_roles'"
@click="handleDisplayUserResource(row)"
@ -85,12 +86,18 @@
/></a>
</a-tooltip>
<a-tooltip
title="用户列表"
:title="$t('acl.userList')"
v-if="!row.uid"
><a @click="handleDisplayUserUnderRole(row)"><a-icon type="team"/></a
></a-tooltip>
<a @click="handleEdit(row)"><a-icon type="edit"/></a>
<a-popconfirm title="确认删除?" @confirm="handleDelete(row)" @cancel="cancel" okText="" cancelText="">
<a-popconfirm
:title="$t('confirmDelete')"
@confirm="handleDelete(row)"
@cancel="cancel"
:okText="$t('yes')"
:cancelText="$t('no')"
>
<a style="color: red"><a-icon type="delete"/></a>
</a-popconfirm>
</a-space>
@ -138,52 +145,6 @@ export default {
currentPage: 1,
pageSize: 50,
},
tableColumns: [
{
title: '角色名',
field: 'name',
sortable: true,
minWidth: '150px',
fixed: 'left',
showOverflow: 'tooltip',
},
{
title: '管理员',
field: 'is_app_admin',
minWidth: '100px',
align: 'center',
slots: { default: 'is_app_admin_default' },
},
{
title: '继承自',
field: 'id',
minWidth: '150px',
slots: { default: 'inherit_default' },
},
{
title: '虚拟角色',
field: 'uid',
minWidth: '100px',
align: 'center',
filters: [
{ label: '', value: 1 },
{ label: '', value: 0 },
],
filterMultiple: false,
filterMethod: ({ value, row }) => {
return value === !row.uid
},
slots: { default: 'isVisualRole_default' },
},
{
title: '操作',
minWidth: '280px',
field: 'action',
fixed: 'right',
slots: { default: 'action_default' },
},
],
btnName: '新增虚拟角色',
is_all: this.$route.name === 'acl_roles',
tableData: [],
allRoles: [],
@ -286,15 +247,10 @@ export default {
deleteRole(id) {
deleteRoleById(id, { app_id: this.$route.name.split('_')[0] }).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
this.handleOk()
})
// .catch(err => this.requestFailed(err))
},
// requestFailed(err) {
// const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
// this.$message.error(`${msg}`)
// },
cancel(e) {
return false
},

View File

@ -15,9 +15,9 @@
</a-form-model-item>
<a-form-model-item label=" " :colon="false">
<a-space>
<a-button type="primary" @click="changeVisible">{{ !visible ? '查看' : '隐藏' }}</a-button>
<a-button type="danger" ghost @click="handleSumbit">重置</a-button>
<!-- <a-button @click="handleCancel">取消</a-button> -->
<a-button type="primary" @click="changeVisible">{{ !visible ? $t('view') : $t('hide') }}</a-button>
<a-button type="danger" ghost @click="handleSumbit">{{ $t('reset') }}</a-button>
<!-- <a-button @click="handleCancel">{{ $t('cancel') }}</a-button> -->
</a-space>
</a-form-model-item>
</a-form-model>
@ -62,13 +62,13 @@ export default {
handleSumbit() {
const that = this
this.$confirm({
title: '重置',
content: '确定重置用户密钥?',
title: that.$t('reset'),
content: that.$t('acl.confirmResetSecret'),
onOk() {
that.$refs.secretKeyForm.validate((valid) => {
if (valid) {
updateSecret().then((res) => {
that.$message.success('重置成功')
that.$message.success(that.$t('operateSuccess'))
const { key, secret } = res
that.form = { key, secret }
})

View File

@ -1,11 +1,11 @@
<template>
<div class="acl-trigger">
<div class="acl-trigger-header">
<a-button type="primary" @click="handleCreateTrigger">新增触发器</a-button>
<a-button type="primary" @click="handleCreateTrigger">{{ $t('acl.addTrigger') }}</a-button>
<a-input-search
class="ops-input"
:style="{ display: 'inline', marginLeft: '10px', width: '200px' }"
placeholder="搜索 | 名称"
:placeholder="`${$t('search')} | ${$t('name')}`"
v-model="searchName"
allowClear
@search="filter"
@ -46,18 +46,18 @@
<a-tag v-for="(p, index) in row.permissions" :key="index">{{ p }}</a-tag>
</template>
<template #enabled_default="{row}">
<a-tag v-if="row.enabled" color="#2db7f5">启用</a-tag>
<a-tag v-else color="grey">禁用</a-tag>
<a-tag v-if="row.enabled" color="#2db7f5">{{ $t('acl.enable') }}</a-tag>
<a-tag v-else color="grey">{{ $t('acl.disable') }}</a-tag>
</template>
<template #action_default="{row}">
<a-space>
<a-tooltip title="应用">
<a-tooltip :title="$t('acl.apply')">
<a @click="handleApplyTrigger(row)" :style="{ color: '#0f9d58' }"><a-icon type="appstore"/></a>
</a-tooltip>
<a-tooltip title="取消">
<a-tooltip :title="$t('cancel')">
<a @click="handleCancelTrigger(row)" :style="{ color: 'orange' }"><a-icon type="stop"/></a>
</a-tooltip>
<a-tooltip title="查看正则匹配结果">
<a-tooltip :title="$t('acl.viewMatchResult')">
<a @click="handlePattern(row)" :style="{ color: 'purple' }"><a-icon type="eye"/></a>
</a-tooltip>
<a @click="handleEditTrigger(row)"><a-icon type="edit"/></a>
@ -67,7 +67,7 @@
<template slot="empty">
<div>
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
<div>暂无数据</div>
<div>{{ $t('noData') }}</div>
</div>
</template>
</vxe-grid>
@ -102,9 +102,19 @@ export default {
triggers: [],
id2parents: [],
id2perms: {},
tableColumns: [
}
},
computed: {
...mapState({
windowHeight: (state) => state.windowHeight,
}),
app_id() {
return this.$route.name.split('_')[0]
},
tableColumns() {
return [
{
title: '名称',
title: this.$t('name'),
field: 'name',
sortable: true,
minWidth: '150px',
@ -112,7 +122,7 @@ export default {
showOverflow: 'tooltip',
},
{
title: '资源名',
title: this.$t('acl.resource'),
field: 'wildcard',
minWidth: '250px',
showOverflow: 'tooltip',
@ -121,15 +131,15 @@ export default {
},
},
{
title: '资源类型',
title: this.$t('acl.resourceType'),
field: 'resource_type_id',
minWidth: '100px',
minWidth: '120px',
slots: {
default: 'resourceTypeRender_default',
},
},
{
title: '创建人',
title: this.$t('acl.creator'),
field: 'users',
minWidth: '150px',
showOverflow: 'tooltip',
@ -138,7 +148,7 @@ export default {
},
},
{
title: '角色',
title: this.$t('acl.allRole'),
field: 'roles',
minWidth: '150px',
slots: {
@ -172,7 +182,7 @@ export default {
},
},
{
title: '权限',
title: this.$t('acl.permission'),
field: 'permissions',
minWidth: '250px',
slots: {
@ -180,7 +190,7 @@ export default {
},
},
{
title: '状态',
title: this.$t('status'),
field: 'enabled',
minWidth: '100px',
slots: {
@ -188,7 +198,7 @@ export default {
},
},
{
title: '操作',
title: this.$t('operation'),
field: 'action',
width: '120px',
fixed: 'right',
@ -196,8 +206,8 @@ export default {
default: 'action_default',
},
},
],
}
]
},
},
created() {
this.loadRoles()
@ -206,15 +216,6 @@ export default {
beforeMount() {
this.loadTriggers()
},
computed: {
...mapState({
windowHeight: (state) => state.windowHeight,
}),
app_id() {
return this.$route.name.split('_')[0]
},
},
methods: {
loadTriggers() {
this.searchName = ''
@ -244,11 +245,11 @@ export default {
handleDeleteTrigger(record) {
const that = this
this.$confirm({
title: '删除',
content: '确认删除该触发器吗?',
title: that.$t('warning'),
content: that.$t('acl.confirmDeleteTrigger'),
onOk() {
deleteTrigger(record.id).then((res) => {
that.$message.success('删除成功')
that.$message.success(that.$t('deleteSuccess'))
that.loadTriggers()
})
// .catch(err => that.$httpError(err))
@ -258,11 +259,11 @@ export default {
handleApplyTrigger(record) {
const that = this
this.$confirm({
title: '规则应用',
content: '是否确定应用该触发器?',
title: that.$t('acl.ruleApply'),
content: that.$t('acl.triggerTip1'),
onOk() {
applyTrigger(record.id).then((res) => {
that.$message.success('提交成功!')
that.$message.success(that.$t('operateSuccess'))
})
// .catch(err => that.$httpError(err))
},
@ -271,11 +272,11 @@ export default {
handleCancelTrigger(record) {
const that = this
this.$confirm({
title: '规则应用',
content: '是否取消应用该触发器?',
title: that.$t('acl.ruleApply'),
content: that.$t('acl.triggerTip2'),
onOk() {
cancelTrigger(record.id).then((res) => {
that.$message.success('提交成功!')
that.$message.success(that.$t('operateSuccess'))
})
// .catch(err => that.$httpError(err))
},

View File

@ -5,8 +5,8 @@
<a-input-search
class="ops-input"
allowClear
:style="{ display: 'inline', marginLeft: '10px' }"
placeholder="搜索 | 用户名、中文名"
:style="{ width: '300px', display: 'inline', marginLeft: '10px' }"
:placeholder="`${$t('search')} | ${$t('acl.nickname')} 、 ${$t('acl.username')}`"
v-model="searchName"
></a-input-search>
</div>
@ -29,10 +29,10 @@
<a :disabled="isAclAdmin ? false : true" @click="handleEdit(row)">
<a-icon type="edit" />
</a>
<a-tooltip title="权限汇总">
<a-tooltip :title="$t('acl.summaryPermissions')">
<a @click="handlePermCollect(row)"><a-icon type="solution"/></a>
</a-tooltip>
<a-popconfirm :title="`确认删除【${row.nickname || row.username}】?`" @confirm="deleteUser(row.uid)">
<a-popconfirm :title="$t('confirmDelete')" @confirm="deleteUser(row.uid)">
<a :style="{ color: 'red' }"><ops-icon type="icon-xianxing-delete"/></a>
</a-popconfirm>
</a-space>
@ -59,48 +59,7 @@ export default {
data() {
return {
loading: false,
tableColumns: [
{
title: '用户名',
field: 'username',
sortable: true,
minWidth: '100px',
fixed: 'left',
},
{
title: '中文名',
field: 'nickname',
minWidth: '100px',
},
{
title: '加入时间',
field: 'date_joined',
minWidth: '160px',
align: 'center',
sortable: true,
},
{
title: '锁定',
field: 'block',
width: '150px',
align: 'center',
slots: {
default: 'block_default',
},
},
{
title: '操作',
field: 'action',
width: '150px',
fixed: 'right',
align: 'center',
slots: {
default: 'action_default',
},
},
],
onDutuUids: [],
btnName: '新增用户',
allUsers: [],
tableData: [],
searchName: '',
@ -125,6 +84,51 @@ export default {
return false
}
},
tableColumns() {
return [
{
title: this.$t('acl.username'),
field: 'username',
sortable: true,
minWidth: '100px',
fixed: 'left',
},
{
title: this.$t('acl.nickname'),
field: 'nickname',
minWidth: '100px',
},
{
title: this.$t('acl.joined_at'),
field: 'date_joined',
minWidth: '160px',
align: 'center',
sortable: true,
},
{
title: this.$t('acl.block'),
field: 'block',
width: '150px',
align: 'center',
slots: {
default: 'block_default',
},
},
{
title: this.$t('operation'),
field: 'action',
width: '150px',
fixed: 'right',
align: 'center',
slots: {
default: 'action_default',
},
},
]
},
btnName() {
return this.$t('acl.addUser')
},
},
watch: {
searchName: {
@ -175,7 +179,7 @@ export default {
},
deleteUser(uid) {
deleteUserById(uid).then((res) => {
this.$message.success(`删除成功`)
this.$message.success(this.$t('deleteSuccess'))
this.handleOk()
})
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -63,7 +63,7 @@ export default {
updateCI(this.row.ci_id || this.row._id, {
[`${this.column.property}`]: this.default_value_json_right ? this.jsonData : {},
}).then(() => {
this.$message.success('保存成功!')
this.$message.success(this.$t('saveSuccess'))
this.handleCancel()
this.$emit('jsonEditorOk', this.row, this.column, this.default_value_json_right ? this.jsonData : {})
})

View File

@ -7,7 +7,7 @@
width: '200px',
height: `${height}px`,
}"
:titles="['未选属性', '已选属性']"
:titles="[$t('cmdb.components.unselectAttributes'), $t('cmdb.components.selectAttributes')]"
:render="(item) => item.title"
:targetKeys="targetKeys"
@change="handleChange"
@ -16,7 +16,7 @@
:filterOption="filterOption"
class="cmdb-transfer"
>
<span slot="notFoundContent">暂无数据</span>
<span slot="notFoundContent">{{ $t('noData') }}</span>
<template slot="children" slot-scope="{ props: { direction, filteredItems } }">
<div class="ant-transfer-list-content" v-if="direction === 'right'">
<draggable :value="targetKeys" animation="300" @end="dragEnd" :disabled="!isSortable">
@ -27,10 +27,11 @@
:style="{ height: '38px' }"
>
<li
:class="{
'ant-transfer-list-content-item': true,
'ant-transfer-list-content-item-selected': selectedKeys.includes(item.key),
}"
:class="
`ant-transfer-list-content-item ${
selectedKeys.includes(item.key) ? 'ant-transfer-list-content-item-selected' : ''
}`
"
@click="setSelectedKeys(item)"
>
<OpsMoveIcon class="move-icon" />
@ -62,9 +63,11 @@
:style="{ height: '38px' }"
>
<li
:class="`ant-transfer-list-content-item ${
:class="
`ant-transfer-list-content-item ${
selectedKeys.includes(item.key) ? 'ant-transfer-list-content-item-selected' : ''
}`"
}`
"
@click="setSelectedKeys(item)"
>
<div class="ant-transfer-list-content-item-text" style="display: inline">
@ -83,7 +86,7 @@
</template>
</a-transfer>
<div v-if="hasFooter" :style="{ marginTop: '5px', height: '20px' }">
<a-button :style="{ float: 'right' }" size="small" @click="handleSubmit" type="primary">确定</a-button>
<a-button :style="{ float: 'right' }" size="small" @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</div>
</template>
@ -110,12 +113,12 @@ export default {
default: true,
},
isSortable: {
// 右侧是否可排序
// Is the right side sortable?
type: Boolean,
default: true,
},
isFixable: {
// 右侧是否可固定
// Can the right side be fixed?
type: Boolean,
default: true,
},

View File

@ -1,21 +1,30 @@
<template>
<a-modal :visible="visible" title="导出数据" @cancel="handleCancel" okText="导出" @ok="handleOk">
<a-modal
:visible="visible"
:title="$t('cmdb.components.downloadCI')"
@cancel="handleCancel"
@ok="handleOk"
width="700px"
>
<a-form :form="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 15 }">
<a-form-item label="文件名">
<a-form-item :label="$t('cmdb.components.filename')">
<a-input
placeholder="请输入文件名"
v-decorator="['filename', { rules: [{ required: true, message: '请输入文件名' }] }]"
:placeholder="$t('cmdb.components.filenameInputTips')"
v-decorator="['filename', { rules: [{ required: true, message: $t('cmdb.components.filenameInputTips') }] }]"
/>
</a-form-item>
<a-form-item label="保存类型">
<a-form-item :label="$t('cmdb.components.saveType')">
<a-select
placeholder="请选择保存类型"
v-decorator="['type', { rules: [{ required: true, message: '请选择保存类型' }], initialValue: 'xlsx' }]"
:placeholder="$t('cmdb.components.saveTypeTips')"
v-decorator="[
'type',
{ rules: [{ required: true, message: $t('cmdb.components.saveTypeTips') }], initialValue: 'xlsx' },
]"
>
<a-select-option v-for="item in typeList" :key="item.id" :values="item.id">{{ item.label }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="选择字段">
<a-form-item :label="$t('cmdb.ciType.selectAttributes')">
<div
:style="{
paddingLeft: '26px',
@ -29,7 +38,7 @@
:checked="checkAll"
@change="onCheckAllChange"
:style="{ marginRight: '10px' }"
/>全选
/>{{ $t('checkAll') }}
</div>
<div
:style="{
@ -76,30 +85,7 @@ export default {
},
},
data() {
const typeList = [
{
id: 'xlsx',
label: 'Excel工作簿(*.xlsx)',
},
{
id: 'csv',
label: 'CSV(逗号分隔)(*.csv)',
},
{
id: 'html',
label: '网页(*.html)',
},
{
id: 'xml',
label: 'XML数据(*.xml)',
},
{
id: 'txt',
label: '文本文件(制表符分隔)(*.txt)',
},
]
return {
typeList,
visible: false,
form: this.$form.createForm(this),
preferenceAttrList: [],
@ -109,6 +95,32 @@ export default {
defaultChecked: [],
}
},
computed: {
typeList() {
return [
{
id: 'xlsx',
label: this.$t('cmdb.components.xlsx'),
},
{
id: 'csv',
label: this.$t('cmdb.components.csv'),
},
{
id: 'html',
label: this.$t('cmdb.components.html'),
},
{
id: 'xml',
label: this.$t('cmdb.components.xml'),
},
{
id: 'txt',
label: this.$t('cmdb.components.txt'),
},
]
},
},
methods: {
...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']),
open({ preferenceAttrList, ciTypeName = undefined }) {

View File

@ -25,17 +25,17 @@
</vxe-column>
<template #empty>
<div v-if="loading()" style="height: 200px; line-height: 200px;color:#2F54EB">
<a-icon type="loading" /> 加载中...
<a-icon type="loading" /> {{ $t('loading') }}
</div>
<div v-else>
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
<div>暂无数据</div>
<div>{{ $t('noData') }}</div>
</div>
</template>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">授权用户/部门</span>
<span class="grant-button" @click="grantRole">授权角色</span>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
@ -95,10 +95,8 @@ export default {
}
return (this.windowHeight - 104) / 2 - 116
},
},
data() {
return {
permMap,
permMap() {
return permMap()
}
},
methods: {
@ -131,8 +129,8 @@ export default {
} else {
const that = this
this.$confirm({
title: '警告',
content: `确认删除 ${row.name} 授权 权限`,
title: that.$t('warning'),
content: that.$t('cmdb.components.confirmRevoke', { name: `${row.name}` }),
onOk() {
that.handleChange({ target: { checked: false } }, col, row)
const _idx = that.tableData.findIndex((item) => item.rid === row.rid)

View File

@ -1,11 +1,15 @@
export const permMap = {
read: '查看',
add: '新增',
create: '新增',
update: '修改',
delete: '删除',
config: '配置',
grant: '授权',
'read_attr': '查看字段',
'read_ci': '查看实例'
import i18n from '@/lang'
export const permMap = () => {
return {
read: i18n.t('view'),
add: i18n.t('new'),
create: i18n.t('new'),
update: i18n.t('update'),
delete: i18n.t('delete'),
config: i18n.t('cmdb.components.config'),
grant: i18n.t('grant'),
'read_attr': i18n.t('cmdb.components.readAttribute'),
'read_ci': i18n.t('cmdb.components.readCI')
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="cmdb-grant" :style="{ maxHeight: `${windowHeight - 104}px` }">
<template v-if="cmdbGrantType.includes('ci_type')">
<div class="cmdb-grant-title">模型权限</div>
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
@ -18,7 +18,7 @@
cmdbGrantType.includes('ci_type,ci') || (cmdbGrantType.includes('ci') && !cmdbGrantType.includes('ci_type'))
"
>
<div class="cmdb-grant-title">实例权限</div>
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
@ -32,7 +32,7 @@
/>
</template>
<template v-if="cmdbGrantType.includes('type_relation')">
<div class="cmdb-grant-title">关系权限</div>
<div class="cmdb-grant-title">{{ $t('cmdb.components.relationGrant') }}</div>
<TypeRelationGrant
:typeRelationIds="typeRelationIds"
:tableData="tableData"
@ -45,7 +45,7 @@
/>
</template>
<template v-if="cmdbGrantType.includes('relation_view')">
<div class="cmdb-grant-title">{{ resourceTypeName }}权限</div>
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<RelationViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
@ -116,7 +116,7 @@ export default {
attrGroup: [],
filerPerimissions: {},
loading: false,
addedRids: [], // 本次新增的rid
addedRids: [], // added rid this time
}
},
computed: {
@ -203,12 +203,12 @@ export default {
this.tableData = perms
this.loading = false
},
// 授权common-setting中的部门 从中拿到roleid
// Grant the department in common-setting and get the roleid from it
grantDepart(grantType) {
this.$refs.grantModal.open('depart')
this.grantType = grantType
},
// 授权最古老的角色权限
// Grant the oldest role permissions
grantRole(grantType) {
this.$refs.grantModal.open('role')
this.grantType = grantType

View File

@ -26,9 +26,9 @@ export default {
computed: {
title() {
if (this.type === 'depart') {
return '授权用户/部门'
return this.$t('cmdb.components.grantUser')
}
return '授权角色'
return this.$t('cmdb.components.grantRole')
},
},
methods: {

View File

@ -2,9 +2,9 @@
<a-modal :width="680" :title="title" :visible="visible" @ok="handleOk" @cancel="handleCancel">
<CustomRadio
:radioList="[
{ value: 1, label: '全部' },
{ value: 2, label: '自定义', layout: 'vertical' },
{ value: 3, label: '' },
{ value: 1, label: $t('cmdb.components.all') },
{ value: 2, label: $t('cmdb.components.customize'), layout: 'vertical' },
{ value: 3, label: $t('cmdb.components.none') },
]"
v-model="radioValue"
>
@ -16,7 +16,7 @@
:clearable="true"
searchable
:options="attrGroup"
placeholder="请选择属性字段"
:placeholder="$t('cmdb.ciType.selectAttributes')"
value-consists-of="LEAF_PRIORITY"
:limit="10"
:limitText="(count) => `+ ${count}`"
@ -24,8 +24,8 @@
(node) => {
return {
id: node.name || -1,
label: node.alias || node.name || '其他',
title: node.alias || node.name || '其他',
label: node.alias || node.name || $t('other'),
title: node.alias || node.name || $t('other'),
children: node.attributes,
}
}
@ -42,7 +42,7 @@
:wrapperCol="{ span: 10 }"
ref="form"
>
<a-form-model-item label="名称" prop="name">
<a-form-model-item :label="$t('name')" prop="name">
<a-input v-model="form.name" />
</a-form-model-item>
<FilterComp
@ -99,16 +99,16 @@ export default {
name: '',
},
rules: {
name: [{ required: true, message: '请输入自定义筛选条件名' }],
name: [{ required: true, message: this.$t('cmdb.components.customizeFilterName') }],
},
}
},
computed: {
title() {
if (this.colType === 'read_attr') {
return '字段权限'
return this.$t('cmdb.components.attributeGrant')
}
return '实例权限'
return this.$t('cmdb.components.ciGrant')
},
attrGroup() {
return this.provide_attrGroup()

View File

@ -17,8 +17,8 @@
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">授权用户/部门</span>
<span class="grant-button" @click="grantRole">授权角色</span>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
@ -51,7 +51,6 @@ export default {
},
data() {
return {
permMap,
columns: ['read', 'grant'],
}
},
@ -65,6 +64,9 @@ export default {
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,

View File

@ -17,8 +17,8 @@
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">授权用户/部门</span>
<span class="grant-button" @click="grantRole">授权角色</span>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
@ -51,7 +51,6 @@ export default {
},
data() {
return {
permMap,
columns: ['create', 'grant', 'delete'],
}
},
@ -65,6 +64,9 @@ export default {
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,

View File

@ -132,7 +132,7 @@ export default {
} else if (color.indexOf('rgb') !== -1) {
hsvObj = this.rgbToHSV(color)
} else {
throw new Error('初始化颜色格式错误,使用#fff或rgb格式')
throw new Error(this.$t('cmdb.components.colorPickerError'))
// this.$message.error('颜色格式错误使用16进制格式')
}
if (hsvObj) {

View File

@ -15,19 +15,19 @@
:edit-config="isEdit ? { trigger: 'click', mode: 'cell' } : {}"
>
<template v-if="isEdit">
<vxe-colgroup title="自动发现">
<vxe-column field="name" title="名称" width="100"> </vxe-column>
<vxe-column field="type" title="类型" width="80"> </vxe-column>
<vxe-column field="example" title="示例值">
<vxe-colgroup :title="$t('cmdb.ciType.autoDiscovery')">
<vxe-column field="name" :title="$t('name')" width="100"> </vxe-column>
<vxe-column field="type" :title="$t('type')" width="80"> </vxe-column>
<vxe-column field="example" :title="$t('cmdb.components.example')">
<template #default="{row}">
<span v-if="row.type === 'json'">{{ JSON.stringify(row.example) }}</span>
<span v-else>{{ row.example }}</span>
</template>
</vxe-column>
<vxe-column field="desc" title="描述"> </vxe-column>
<vxe-column field="desc" :title="$t('desc')"> </vxe-column>
</vxe-colgroup>
<vxe-colgroup title="模型属性">
<vxe-column field="attr" title="名称" :edit-render="{}">
<vxe-colgroup :title="$t('cmdb.ciType.attributes')">
<vxe-column field="attr" :title="$t('name')" :edit-render="{}">
<template #default="{row}">
{{ row.attr }}
</template>
@ -45,15 +45,15 @@
</vxe-colgroup>
</template>
<template v-else>
<vxe-column field="name" title="名称" width="100"> </vxe-column>
<vxe-column field="type" title="类型" width="80"> </vxe-column>
<vxe-column field="example" title="示例值">
<vxe-column field="name" :title="$t('name')" width="100"> </vxe-column>
<vxe-column field="type" :title="$t('type')" width="80"> </vxe-column>
<vxe-column field="example" :title="$t('cmdb.components.example')">
<template #default="{row}">
<span v-if="row.type === 'object'">{{ JSON.stringify(row.example) }}</span>
<span v-else>{{ row.example }}</span>
</template>
</vxe-column>
<vxe-column field="desc" title="描述"> </vxe-column>
<vxe-column field="desc" :title="$t('desc')"> </vxe-column>
</template>
</vxe-table>
</div>
@ -61,12 +61,6 @@
<script>
import { getHttpCategories, getHttpAttributes, getSnmpAttributes } from '../../api/discovery'
const httpMap = {
阿里云: { name: 'aliyun' },
腾讯云: { name: 'tencentcloud' },
华为云: { name: 'huaweicloud' },
AWS: { name: 'aws' },
}
export default {
name: 'HttpSnmpAD',
props: {
@ -107,13 +101,21 @@ export default {
const { ruleType, ruleName } = this
return { ruleType, ruleName }
},
httpMap() {
return {
[this.$t('cmdb.components.aliyun')]: { name: 'aliyun' },
[this.$t('cmdb.components.tencentcloud')]: { name: 'tencentcloud' },
[this.$t('cmdb.components.huaweicloud')]: { name: 'huaweicloud' },
AWS: { name: 'aws' },
}
},
},
watch: {
currentCate: {
immediate: true,
handler(newVal) {
if (newVal) {
getHttpAttributes(httpMap[`${this.ruleName}`].name, { category: newVal }).then((res) => {
getHttpAttributes(this.httpMap[`${this.ruleName}`].name, { category: newVal }).then((res) => {
if (this.isEdit) {
this.formatTableData(res)
} else {
@ -139,7 +141,7 @@ export default {
})
}
if (ruleType === 'http' && ruleName) {
getHttpCategories(httpMap[`${this.ruleName}`].name).then((res) => {
getHttpCategories(this.httpMap[`${this.ruleName}`].name).then((res) => {
this.categories = res
if (res && res.length) {
this.currentCate = res[0]

View File

@ -19,7 +19,7 @@
<Editor class="notice-content-editor" :defaultConfig="editorConfig" mode="simple" @onCreated="onCreated" />
<div class="notice-content-sidebar">
<template v-if="needOld">
<div class="notice-content-sidebar-divider">变更前</div>
<div class="notice-content-sidebar-divider">{{ $t('cmdb.components.beforeChange') }}</div>
<div
@dblclick="dblclickSidebar(`old_${attr.name}`, attr.alias || attr.name)"
class="notice-content-sidebar-item"
@ -29,7 +29,7 @@
>
{{ attr.alias || attr.name }}
</div>
<div class="notice-content-sidebar-divider">变更后</div>
<div class="notice-content-sidebar-divider">{{ $t('cmdb.components.afterChange') }}</div>
</template>
<div
@dblclick="dblclickSidebar(attr.name, attr.alias || attr.name)"
@ -65,7 +65,7 @@ export default {
data() {
return {
editor: null,
editorConfig: { placeholder: '请输入通知内容', readOnly: this.readOnly },
editorConfig: { placeholder: this.$t('cmdb.components.noticeContentTips'), readOnly: this.readOnly },
content: '',
defaultParams: [],
value2LabelMap: {},
@ -74,11 +74,11 @@ export default {
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy() // 组件销毁时及时销毁编辑器
editor.destroy() // When the component is destroyed, destroy the editor in time
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor) // 一定要用 Object.seal() 否则会报错
this.editor = Object.seal(editor) // Be sure to use Object.seal(), otherwise an error will be reported
},
getContent() {
const html = _.cloneDeep(this.editor.getHtml())

View File

@ -11,7 +11,7 @@
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-button v-else type="primary" size="small" ghost @click="showInput">保存筛选条件</a-button>
<a-button v-else type="primary" size="small" ghost @click="showInput">{{ $t('cmdb.components.saveQuery') }}</a-button>
</span>
<template v-for="(item, index) in preferenceSearchList.slice(0, 3)">
<span
@ -26,7 +26,7 @@
<a-tooltip :title="item.name">
<span @click="clickPreferenceSearch(item)">{{ `${item.name.slice(0, 6)}...` }}</span>
</a-tooltip>
<a-popconfirm title="确认删除?" @confirm="deletePreferenceSearch(item)">
<a-popconfirm :title="$t('cmdb.ciType.confirmDelete2')" @confirm="deletePreferenceSearch(item)">
<a-icon type="close" />
</a-popconfirm>
</span>
@ -40,7 +40,7 @@
}"
>
<span @click="clickPreferenceSearch(item)">{{ item.name }}</span>
<a-popconfirm title="确认删除?" @confirm="deletePreferenceSearch(item)">
<a-popconfirm :title="$t('cmdb.ciType.confirmDelete2')" @confirm="deletePreferenceSearch(item)">
<a-icon type="close" />
</a-popconfirm>
</span>
@ -73,7 +73,7 @@
{{ item.name }}
</div>
<a-popconfirm
title="确认删除?"
:title="$t('cmdb.ciType.confirmDelete2')"
:getPopupContainer="(trigger) => trigger.parentElement"
placement="left"
@confirm="

View File

@ -15,7 +15,7 @@
:limit="1"
:limitText="(count) => `+ ${count}`"
value-consists-of="LEAF_PRIORITY"
placeholder="模型"
:placeholder="$t('cmdb.ciType.ciType')"
@close="closeCiTypeGroup"
@open="openCiTypeGroup"
@input="inputCiTypeGroup"
@ -23,8 +23,8 @@
(node) => {
return {
id: node.id || -1,
label: node.alias || node.name || '其他',
title: node.alias || node.name || '其他',
label: node.alias || node.name || $t('other'),
title: node.alias || node.name || $t('other'),
children: node.ci_types,
}
}
@ -42,7 +42,7 @@
<a-input
v-model="fuzzySearch"
:style="{ display: 'inline-block', width: '244px' }"
placeholder="请查找"
:placeholder="$t('cmdb.components.pleaseSearch')"
@pressEnter="emitRefresh"
class="ops-input ops-input-radius"
>
@ -54,7 +54,7 @@
/>
<a-tooltip slot="prefix" placement="bottom" :overlayStyle="{ maxWidth: '550px' }">
<template slot="title">
1json属性不能搜索<br />2搜索内容包括逗号则需转义 \,<br />3只搜索索引属性非索引属性使用条件过滤
{{ $t('cmdb.components.ciSearchTips') }}
</template>
<a><a-icon type="question-circle"/></a>
</a-tooltip>
@ -68,7 +68,7 @@
>
<div slot="popover_item" class="search-form-bar-filter">
<a-icon class="search-form-bar-filter-icon" type="filter" />
条件过滤
{{ $t('cmdb.components.conditionFilter') }}
<a-icon class="search-form-bar-filter-icon" type="down" />
</div>
</FilterComp>
@ -97,8 +97,8 @@
</a-space>
</div>
<a-space>
<a-button @click="reset" size="small">重置</a-button>
<a-tooltip title="属性说明" v-if="type === 'relationView'">
<a-button @click="reset" size="small">{{ $t('reset') }}</a-button>
<a-tooltip :title="$t('cmdb.components.attributeDesc')" v-if="type === 'relationView'">
<a
@click="
() => {
@ -149,7 +149,7 @@ export default {
},
data() {
return {
// 高级搜索 展开/关闭
// Advanced Search Expand/Close
advanced: false,
queryParam: {},
isFocusExpression: false,
@ -163,7 +163,7 @@ export default {
computed: {
placeholder() {
return this.isFocusExpression ? 'q=hostname:*0.0.0.0*' : '表达式'
return this.isFocusExpression ? this.$t('cmdb.components.ciSearchTips2') : this.$t('cmdb.ciType.expr')
},
width() {
return '200px'

View File

@ -11,13 +11,13 @@
>
<a-tabs v-model="activeKey">
<a-tab-pane key="1">
<span slot="tab"><ops-icon type="cmdb-ci" />资源数据</span>
<span slot="tab"><ops-icon type="cmdb-ci" />{{ $t('cmdb.menu.ciTable') }}</span>
<div class="cmdb-subscribe-drawer-container" :style="{ height: `${windowHeight - 60}px` }">
<div class="cmdb-subscribe-drawer-container-title">
<span>订阅模型{{ ciType.alias || ciType.name }}</span>
<span>{{ $t('cmdb.components.subCIType') }}: {{ ciType.alias || ciType.name }}</span>
<span :style="{ fontWeight: 500, color: instanceSubscribed ? 'green' : 'red' }">{{
`${instanceSubscribed ? '' : ''}订阅`
}}</span>
`${instanceSubscribed ? $t('cmdb.components.already') : $t('cmdb.components.not')}`
}}{{ $t('cmdb.components.sub') }})</span>
</div>
<template>
<AttributesTransfer
@ -31,22 +31,22 @@
:height="windowHeight - 170"
/>
<div class="custom-drawer-bottom-action">
<a-button @click="subInstanceSubmit" type="primary">订阅</a-button>
<a-button @click="subInstanceSubmit" type="primary">{{ $t('cmdb.preference.sub') }}</a-button>
</div>
</template>
</div>
</a-tab-pane>
<a-tab-pane key="2" force-render>
<span slot="tab"><ops-icon type="cmdb-tree" />资源层级</span>
<span slot="tab"><ops-icon type="cmdb-tree" />{{ $t('cmdb.menu.ciTree') }}</span>
<div class="cmdb-subscribe-drawer-container" :style="{ height: `${windowHeight - 60}px` }">
<div class="cmdb-subscribe-drawer-container-title">
<span>订阅模型{{ ciType.alias || ciType.name }}</span>
<span>{{ $t('cmdb.components.subCIType') }}: {{ ciType.alias || ciType.name }}</span>
<span :style="{ fontWeight: 500, color: treeSubscribed ? 'green' : 'red' }">{{
`${treeSubscribed ? '' : ''}订阅`
}}</span>
`${treeSubscribed ? $t('cmdb.components.already') : $t('cmdb.components.not')}`
}}{{ $t('cmdb.components.sub') }})</span>
</div>
<div class="cmdb-subscribe-drawer-tree-header" :style="{ maxHeight: `${(windowHeight - 170) / 3 - 20}px` }">
<span v-if="!treeViews.length">请在下方进行选择</span>
<span v-if="!treeViews.length">{{ $t('cmdb.components.selectBelow') }}</span>
<div
class="cmdb-subscribe-drawer-tree-header-selected"
:style="{ marginLeft: `${18 * index}px` }"
@ -70,7 +70,7 @@
</div>
</div>
<div class="custom-drawer-bottom-action">
<a-button @click="subTreeSubmit" type="primary">订阅</a-button>
<a-button @click="subTreeSubmit" type="primary">{{ $t('cmdb.preference.sub') }}</a-button>
</div>
</div>
</a-tab-pane>
@ -179,7 +179,7 @@ export default {
},
subTreeSubmit() {
subscribeTreeView(this.ciType.type_id, this.treeViews).then((res) => {
this.$message.success('订阅成功')
this.$message.success(this.$t('cmdb.components.subSuccess'))
if (this.treeViews.length > 0) {
this.treeSubscribed = true
} else {
@ -194,7 +194,7 @@ export default {
return [item, !!this.fixedList.includes(item)]
})
).then((res) => {
this.$message.success('订阅成功')
this.$message.success(this.$t('cmdb.components.subSuccess'))
this.resetRoute()
if (this.selectedAttrList.length > 0) {
this.instanceSubscribed = true

View File

@ -25,10 +25,10 @@
<div style="margin-top:10px">
<table v-if="authorizationType === 'BasicAuth'">
<tr>
<td><a-input class="authorization-input" v-model="BasicAuth.username" placeholder="用户名" /></td>
<td><a-input class="authorization-input" v-model="BasicAuth.username" :placeholder="$t('cmdb.ciType.username')" /></td>
</tr>
<tr>
<td><a-input class="authorization-input" v-model="BasicAuth.password" placeholder="密码" /></td>
<td><a-input class="authorization-input" v-model="BasicAuth.password" :placeholder="$t('cmdb.ciType.password')" /></td>
</tr>
</table>
@ -87,7 +87,7 @@
}"
>
<img slot="image" :src="require('@/assets/data_empty.png')" />
<span slot="description"> 暂无请求认证 </span>
<span slot="description"> {{ $t('cmdb.components.noAuthRequest') }} </span>
</a-empty>
</div>
</div>

View File

@ -1,9 +1,9 @@
<template>
<div>
<div class="headers-header">
<span>请求参数</span>
<span>{{ $t('cmdb.components.requestParam') }}</span>
<a-space>
<a-tooltip title="清空">
<a-tooltip :title="$t('cmdb.components.clear')">
<ops-icon
type="icon-xianxing-delete"
@click="
@ -19,7 +19,7 @@
"
/>
</a-tooltip>
<a-tooltip title="新增">
<a-tooltip :title="$t('new')">
<a-icon type="plus" @click="add" />
</a-tooltip>
</a-space>
@ -27,8 +27,8 @@
<div class="headers-box">
<table>
<tr v-for="(item, index) in headers" :key="item.id">
<td><a-input class="headers-input" v-model="item.key" :placeholder="`参数${index + 1}`" /></td>
<td><a-input class="headers-input" v-model="item.value" :placeholder="`${index + 1}`" /></td>
<td><a-input class="headers-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
<td><a-input class="headers-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
<td>
<a style="color:red">
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />

View File

@ -18,7 +18,7 @@
searchable
:options="methodList"
value-consists-of="LEAF_PRIORITY"
placeholder="请选择方式"
:placeholder="$t('cmdb.components.selectMethods')"
>
</treeselect>
<a-input :style="{ display: 'inline-block', width: 'calc(100% - 100px)' }" v-model="url" />

View File

@ -1,9 +1,9 @@
<template>
<div>
<div class="parameters-header">
<span>请求参数</span>
<span>{{ $t('cmdb.components.requestParam') }}</span>
<a-space>
<a-tooltip title="清空">
<a-tooltip :title="$t('cmdb.components.clear')">
<ops-icon
type="icon-xianxing-delete"
@click="
@ -13,7 +13,7 @@
"
/>
</a-tooltip>
<a-tooltip title="新增">
<a-tooltip :title="$t('new')">
<a-icon type="plus" @click="add" />
</a-tooltip>
</a-space>
@ -21,8 +21,8 @@
<div class="parameters-box" v-if="parameters && parameters.length">
<table>
<tr v-for="(item, index) in parameters" :key="item.id">
<td><a-input class="parameters-input" v-model="item.key" :placeholder="`参数${index + 1}`" /></td>
<td><a-input class="parameters-input" v-model="item.value" :placeholder="`${index + 1}`" /></td>
<td><a-input class="parameters-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
<td><a-input class="parameters-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
<td>
<a style="color:red">
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
@ -38,9 +38,9 @@
}"
>
<img slot="image" :src="require('@/assets/data_empty.png')" />
<span slot="description"> 暂无请求参数 </span>
<span slot="description"> {{ $t('cmdb.components.noParamRequest') }} </span>
<a-button @click="add" type="primary" size="small" icon="plus" class="ops-button-primary">
添加
{{ $t('add') }}
</a-button>
</a-empty>
</div>

View File

@ -0,0 +1,483 @@
const cmdb_en = {
relation: 'Relation',
attribute: 'Attributes',
menu: {
views: 'Views',
config: 'Configuration',
backend: 'Management',
ciTable: 'Resource Views',
ciTree: 'Tree Views',
ciSearch: 'Search',
adCIs: 'AutoDiscovery Pool',
preference: 'Preference',
batchUpload: 'Batch Import',
citypeManage: 'Modeling',
backendManage: 'Backend',
customDashboard: 'Custom Dashboard',
serviceTreeDefine: 'Service Tree',
citypeRelation: 'CIType Relation',
operationHistory: 'Operation Audit',
relationType: 'Relation Type',
ad: 'AutoDiscovery',
},
ciType: {
ciType: 'CIType',
attributes: 'Attributes',
relation: 'Relation',
trigger: 'Triggers',
attributeAD: 'Attributes AutoDiscovery',
relationAD: 'Relation AutoDiscovery',
grant: 'Grant',
addGroup: 'New Group',
editGroup: 'Edit Group',
group: 'Group',
attributeLibray: 'Attribute Library',
addCITypeInGroup: 'Add a new CIType to the group',
addCIType: 'Add CIType',
editGroupName: 'Edit group name',
deleteGroup: 'Delete this group',
CITypeName: 'Name(English)',
English: 'English',
inputAttributeName: 'Please enter the attribute name',
attributeNameTips: 'It cannot start with a number, it can be English numbers and underscores (_)',
editCIType: 'Edit CIType',
defaultSort: 'Default sort',
selectDefaultOrderAttr: 'Select default sorting attributes',
asec: 'Forward order',
desc: 'Reverse order',
uniqueKey: 'Uniquely Identifies',
uniqueKeySelect: 'Please select a unique identifier',
notfound: 'Can\'t find what you want?',
cannotDeleteGroupTips: 'There is data under this group and cannot be deleted!',
confirmDeleteGroup: 'Are you sure you want to delete group [{groupName}]?',
confirmDeleteCIType: 'Are you sure you want to delete model [{typeName}]?',
uploading: 'Uploading',
uploadFailed: 'Upload failed, please try again later',
addPlugin: 'New plugin',
deletePlugin: 'Delete plugin',
confirmDeleteADT: 'Do you confirm to delete [{pluginName}]',
attributeMap: 'Attribute mapping',
autoDiscovery: 'AutoDiscovery',
node: 'Node',
adExecConfig: 'Execute configuration',
adExecTarget: 'Execute targets',
oneagentIdTips: 'Please enter the hexadecimal OneAgent ID starting with 0x',
selectFromCMDBTips: 'Select from CMDB ',
adAutoInLib: 'Save as CI auto',
adInterval: 'Collection frequency',
byInterval: 'by interval',
allNodes: 'All nodes',
specifyNodes: 'Specify Node',
specifyNodesTips: 'Please fill in the specify node!',
username: 'Username',
password: 'Password',
link: 'Link',
list: 'List',
listTips: 'The value of the field is one or more, and the type of the value returned by the interface is list.',
computeForAllCITips: 'All CI trigger computes',
confirmcomputeForAllCITips: 'Confirm triggering computes for all CIs?',
isUnique: 'Is it unique',
unique: 'Unique',
isChoice: 'Choiced',
defaultShow: 'Default Display',
defaultShowTips: 'The CI instance table displays this field by default',
isSortable: 'Sortable',
isIndex: 'Indexed',
index: 'Index',
indexTips: 'Fields can be used for retrieval to speed up queries',
confirmDelete: 'Confirm to delete [{name}]?',
confirmDelete2: 'Confirm to delete?',
computeSuccess: 'Triggered successfully!',
basicConfig: 'Basic Settings',
AttributeName: 'Name(English)',
DataType: 'Data Type',
defaultValue: 'Default value',
autoIncID: 'Auto-increment ID',
customTime: 'Custom time',
advancedSettings: 'Advanced Settings',
font: 'Font',
color: 'Color',
choiceValue: 'Predefined value',
computedAttribute: 'Computed Attribute',
computedAttributeTips: 'The value of this attribute is calculated through an expression constructed from other attributes of the CIType or by executing a piece of code. The reference method of the attribute is: {{ attribute name }}',
addAttribute: 'New attribute',
existedAttributes: 'Already have attributes',
editAttribute: 'Edit attribute',
addAttributeTips1: 'If sorting is selected, it must also be selected!',
uniqueConstraint: 'Unique Constraint',
up: 'Move up',
down: 'Move down',
selectAttribute: 'Select Attribute',
groupExisted: 'Group name already exists',
attributeSortedTips: 'Attributes in other groups cannot be sorted. If you need to sort, please drag them to a custom group first!',
buildinAttribute: 'built-in attributes',
expr: 'Expression',
code: 'Code',
apply: 'apply',
continueAdd: 'Keep adding',
filter: 'Filter',
choiceOther: 'Other CIType Attributes',
choiceWebhookTips: 'The returned results are filtered by fields, and the hierarchical nesting is separated by ##, such as k1##k2. The web request returns {k1: [{k2: 1}, {k2: 2}]}, and the parsing result is [1, 2 ]',
selectCIType: 'Please select a CMDB CIType',
selectCITypeAttributes: 'Please select CIType attributes',
selectAttributes: 'Please select attributes',
choiceScriptDemo: 'class ChoiceValue(object):\n @staticmethod\n def values():\n """\n Execution entry, returns predefined value\n :return: Returns a list, the type of the value is the same as the type of the attribute\n For example:\n return ["online", "offline"]\n """\n return []',
valueExisted: 'The current value already exists!',
addRelation: 'Add Relation',
sourceCIType: 'Source CIType',
sourceCITypeTips: 'Please select Source CIType',
dstCIType: 'Target CIType',
dstCITypeTips: 'Please select target CIType',
relationType: 'Relation Type',
relationTypeTips: 'Please select relation type',
isParent: 'is parent',
relationConstraint: 'Constraints',
relationConstraintTips: 'please select a relationship constraint',
one2Many: 'One to Many',
one2One: 'One to One',
many2Many: 'Many to Many',
basicInfo: 'Basic Information',
nameInputTips: 'Please enter name',
triggerDataChange: 'Data changes',
triggerDate: 'Date attribute',
triggerEnable: 'Turn on',
descInput: 'Please enter remarks',
triggerCondition: 'Triggering conditions',
addInstance: 'Add new instance',
deleteInstance: 'Delete instance',
changeInstance: 'Instance changes',
selectMutipleAttributes: 'Please select attributes (multiple selections)',
selectSingleAttribute: 'Please select an attribute (single choice)',
beforeDays: 'ahead of time',
days: 'Days',
notifyAt: 'Send time',
notify: 'Notify',
triggerAction: 'Trigger action',
receivers: 'Recipients',
emailTips: 'Please enter your email address, separate multiple email addresses with ;',
customEmail: 'Custom recipients',
notifySubject: 'Notification title',
notifySubjectTips: 'Please enter notification title',
notifyContent: 'Content',
notifyMethod: 'Notify methods',
botSelect: 'Please select a robot',
refAttributeTips: 'The title and content can reference the attribute value of the CIType. The reference method is: {{ attr_name }}',
webhookRefAttributeTips: 'Request parameters can reference the attribute value of the model. The reference method is: {{ attr_name }}',
newTrigger: 'Add trigger',
editTriggerTitle: 'Edit trigger {name}',
newTriggerTitle: 'Add trigger {name}',
confirmDeleteTrigger: 'Are you sure to delete this trigger?',
int: 'Integer',
float: 'Float',
text: 'Text',
datetime: 'DateTime',
date: 'Date',
time: 'Time',
json: 'JSON',
event: 'Event'
},
components: {
unselectAttributes: 'Unselected',
selectAttributes: 'Selected',
downloadCI: 'Export data',
filename: 'Filename',
filenameInputTips: 'Please enter filename',
saveType: 'Save type',
saveTypeTips: 'Please select save type',
xlsx: 'Excel workbook (*.xlsx)',
csv: 'CSV (comma separated) (*.csv)',
html: 'Web page (*.html)',
xml: 'XML data (*.xml)',
txt: 'Text file (tab delimited) (*.txt)',
grantUser: 'Grant User/Department',
grantRole: 'Grant Role',
confirmRevoke: 'Confirm to delete the [Authorization] permission of [{name}]?',
readAttribute: 'View Attributes',
readCI: 'View CIs',
config: 'Configuration',
ciTypeGrant: 'Grant CIType',
ciGrant: 'Grant CI',
attributeGrant: 'Grant Attribute',
relationGrant: 'Grant Relation',
perm: 'Permissions',
all: 'All',
customize: 'Customize',
none: 'None',
customizeFilterName: 'Please enter a custom filter name',
colorPickerError: 'Initialization color format error, use #fff or rgb format',
example: 'Example value',
aliyun: 'aliyun',
tencentcloud: 'Tencent Cloud',
huaweicloud: 'Huawei Cloud',
beforeChange: 'Before change',
afterChange: 'After change',
noticeContentTips: 'Please enter notification content',
saveQuery: 'Save Filters',
pleaseSearch: 'Please search',
conditionFilter: 'Conditional filtering',
attributeDesc: 'Attribute Description',
ciSearchTips: '1. JSON attributes cannot be searched<br />2. If the search content includes commas, they need to be escaped,<br />3. Only index attributes are searched, non-index attributes use conditional filtering',
ciSearchTips2: 'For example: q=hostname:*0.0.0.0*',
subCIType: 'Subscription CIType',
already: 'already',
not: 'not',
sub: 'subscription',
selectBelow: 'Please select below',
subSuccess: 'Subscription successful',
selectMethods: 'Please select a method',
noAuthRequest: 'No certification requested yet',
noParamRequest: 'No parameter certification yet',
requestParam: 'Request parameters',
param: 'Parameter{param}',
value: 'Value{value}',
clear: 'Clear',
},
batch: {
downloadFailed: 'Download failed',
unselectCIType: 'No CIType selected yet',
pleaseUploadFile: 'Please upload files',
batchUploadCanceled: 'Batch upload canceled',
selectCITypeTips: 'Please select CIType',
downloadTemplate: 'Download Template',
drawTips: 'Click or drag files here to upload!',
supportFileTypes: 'Supported file types: xls, xlsx',
uploadResult: 'Upload results',
total: 'total',
successItems: 'items, succeeded',
failedItems: 'items, failed',
items: 'items',
errorTips: 'Error message',
requestFailedTips: 'An error occurred with the request, please try again later',
requestSuccessTips: 'Upload completed',
},
preference: {
mySub: 'My Subscription',
sub: 'Subscribe',
cancelSub: 'Unsubscribe',
editSub: 'Edit subscription',
peopleSub: ' people subscribed',
noSub: 'No subscribed',
cancelSubSuccess: 'Unsubscribe successfully',
confirmcancelSub: 'Are you sure to cancel your subscription?',
confirmcancelSub2: 'Are you sure you want to unsubscribe {name}?',
of: 'of',
hoursAgo: 'hours ago',
daysAgo: 'days ago',
monthsAgo: 'month ago',
yearsAgo: 'years ago',
just: 'just now',
},
custom_dashboard: {
charts: 'Chart',
newChart: 'Add Chart',
editChart: 'Edit Chart',
title: 'Title',
titleTips: 'Please enter a chart title',
calcIndicators: 'Counter',
dimensions: 'Dimensions',
selectDimensions: 'Please select a dimension',
quantity: 'Quantity',
childCIType: 'Relational CIType',
level: 'Level',
levelTips: 'Please enter the relationship level',
preview: 'Preview',
showIcon: 'Display icon',
chartType: 'Chart Type',
dataFilter: 'Data Filtering',
format: 'Formats',
fontColor: 'Font Color',
backgroundColor: 'Background',
chartColor: 'Chart Color',
chartLength: 'Length',
barType: 'Bar Type',
stackedBar: 'Stacked Bar',
multipleSeriesBar: 'Multiple Series Bar ',
axis: 'Axis',
direction: 'Direction',
lowerShadow: 'Lower Shadow',
count: 'Indicator',
bar: 'Bar',
line: 'Line',
pie: 'Pie',
table: 'Table',
default: 'default',
relation: 'Relation',
noCustomDashboard: 'The administrator has not customized the dashboard yet',
},
preference_relation: {
newServiceTree: 'Add ServiceTree',
serviceTreeName: 'Name',
public: 'Public',
saveLayout: 'Save Layout',
childNodesNotFound: 'There are no child nodes and no business relationship can be formed. Please select again!',
tips1: 'Cannot form a view with the currently selected node, please select again!',
tips2: 'Please enter the new serviceTree name!',
tips3: 'Please select at least two nodes!',
},
history: {
ciChange: 'CI',
relationChange: 'Relation',
ciTypeChange: 'CIType',
triggerHistory: 'Triggers',
opreateTime: 'Operate Time',
user: 'User',
userTips: 'Enter filter username',
filter: 'Search',
filterOperate: 'fitler operation',
attribute: 'Attribute',
old: 'Old',
new: 'New',
noUpdate: 'No update',
itemsPerPage: '/page',
triggerName: 'Name',
event: 'Event',
action: 'Actoin',
status: 'Status',
done: 'Done',
undone: 'Undone',
triggerTime: 'Trigger Time',
totalItems: '{total} records in total',
pleaseSelect: 'Please select',
startTime: 'Start Time',
endTime: 'End Time',
deleteCIType: 'Delete CIType',
addCIType: 'Add CIType',
updateCIType: 'Update CIType',
addAttribute: 'Add Attribute',
updateAttribute: 'Update Attribute',
deleteAttribute: 'Delete Attribute',
addTrigger: 'Add Trigger',
updateTrigger: 'Update Trigger',
deleteTrigger: 'Delete Trigger',
addUniqueConstraint: 'Add Unique Constraint',
updateUniqueConstraint: 'Update Unique Constraint',
deleteUniqueConstraint: 'Delete Unique Constraint',
addRelation: 'Add Relation',
deleteRelation: 'Delete Relation',
noModifications: 'No Modifications',
attr: 'attribute',
attrId: 'attribute id',
changeDescription: 'attribute id: {attr_id}, {before_days} day(s) in advance, Subject: {subject}\nContent: {body}\nNotify At: {notify_at}'
},
relation_type: {
addRelationType: 'New',
nameTips: 'Please enter a type name',
},
ad: {
upload: 'Import',
download: 'Export',
accpet: 'Accept',
accpetBy: 'Accept By',
acceptTime: 'Accept Time',
confirmAccept: 'Confirm Accept?',
accpetSuccess: 'Accept successfully',
isAccpet: 'Is accept',
deleteADC: 'Confirm to delete this data?',
batchDelete: 'Confirm to delete this data?',
agent: 'Built-in & Plug-ins',
snmp: 'Network Devices',
http: 'Public Clouds',
rule: 'AutoDiscovery Rules',
timeout: 'Timeout error',
mode: 'Mode',
collectSettings: 'Collection Settings',
updateFields: 'Update Field',
pluginScript: `# -*- coding:utf-8 -*-
import json
class AutoDiscovery(object):
@property
def unique_key(self):
"""
:return: Returns the name of a unique attribute
"""
return
@staticmethod
def attributes():
"""
Define attribute fields
:return: Returns a list of attribute fields. The list items are (name, type, description). The name must be in English.
type: String Integer Float Date DateTime Time JSON
For example:
return [
("ci_type", "String", "CIType name"),
("private_ip", "String", "Internal IP, multiple values separated by commas")
]
"""
return []
@staticmethod
def run():
"""
Execution entry, returns collected attribute values
:return:
Returns a list, the list item is a dictionary, the dictionary key is the attribute name, and the value is the attribute value
For example:
return [dict(ci_type="server", private_ip="192.168.1.1")]
"""
return []
if __name__ == "__main__":
result = AutoDiscovery().run()
if isinstance(result, list):
print("AutoDiscovery::Result::{}".format(json.dumps(result)))
else:
print("ERROR: The collection return must be a list")
`,
server: 'Server',
vserver: 'VServer',
nic: 'NIC',
disk: 'harddisk',
},
ci: {
attributeDesc: 'Attribute Description',
selectRows: 'Select: {rows} items',
addRelation: 'Add Relation',
all: 'All',
batchUpdate: 'Batch Update',
batchUpdateConfirm: 'Are you sure you want to make batch updates?',
batchUpdateInProgress: 'Currently being updated in batches',
batchUpdateInProgress2: 'Updating in batches, {total} in total, {successNum} successful, {errorNum} failed',
batchDeleting: 'Deleting...',
batchDeleting2: 'Deleting {total} items in total, {successNum} items successful, {errorNum} items failed',
copyFailed: 'Copy failed',
noLevel: 'No hierarchical relationship!',
batchAddRelation: 'Batch Add Relation',
history: 'History',
topo: 'Topology',
table: 'Table',
m2mTips: 'The current CIType relationship is many-to-many, please go to the SerivceTree(relation view) to add or delete',
confirmDeleteRelation: 'Confirm to delete the relationship?',
tips1: 'Use commas to separate multiple values',
tips2: 'The field can be modified as needed. When the value is empty, the field will be left empty.',
tips3: 'Please select the fields that need to be modified',
tips4: 'At least one field must be selected',
tips5: 'Search name | alias',
tips6: 'Speed up retrieval, full-text search possible, no need to use conditional filtering\n\n json currently does not support indexing \n\nText characters longer than 190 cannot be indexed',
tips7: 'The form of expression is a drop-down box, and the value must be in the predefined value',
tips8: 'Multiple values, such as intranet IP',
tips9: 'For front-end only',
tips10: 'Other attributes of the CIType are computed using expressions\n\nA code snippet computes the returned value.',
newUpdateField: 'Add a Attribute',
attributeSettings: 'Attribute Settings',
},
serviceTree: {
deleteNode: 'Delete Node',
tips1: 'For example: q=os_version:centos&sort=os_version',
tips2: 'Expression search',
alert1: 'The administrator has not configured the ServiceTree(relation view), or you do not have permission to access it!',
copyFailed: 'Copy failed',
deleteRelationConfirm: 'Confirm to remove selected {name} from current relationship?',
},
tree: {
tips1: 'Please go to Preference page first to complete your subscription!',
subSettings: 'Settings',
}
}
export default cmdb_en

View File

@ -0,0 +1,482 @@
const cmdb_zh = {
relation: '关系',
attribute: '属性',
menu: {
views: '视图',
config: '配置',
backend: '管理端',
ciTable: '资源数据',
ciTree: '资源层级',
ciSearch: '资源搜索',
adCIs: '自动发现池',
preference: '我的订阅',
batchUpload: '批量导入',
citypeManage: '模型配置',
backendManage: '后台管理',
customDashboard: '定制仪表盘',
serviceTreeDefine: '服务树定义',
citypeRelation: '模型关系',
operationHistory: '操作审计',
relationType: '关系类型',
ad: '自动发现',
},
ciType: {
ciType: '模型',
attributes: '模型属性',
relation: '模型关联',
trigger: '触发器',
attributeAD: '属性自动发现',
relationAD: '关系自动发现',
grant: '权限配置',
addGroup: '新增分组',
editGroup: '修改分组',
group: '分组',
attributeLibray: '属性库',
addCITypeInGroup: '在该组中新增CI模型',
addCIType: '新增CI模型',
editGroupName: '编辑组名称',
deleteGroup: '删除该组',
CITypeName: '模型名(英文)',
English: '英文',
inputAttributeName: '请输入属性名',
attributeNameTips: '不能以数字开头,可以是英文 数字以及下划线 (_)',
editCIType: '编辑模型',
defaultSort: '默认排序',
selectDefaultOrderAttr: '选择默认排序属性',
asec: '正序',
desc: '倒序',
uniqueKey: '唯一标识',
uniqueKeySelect: '请选择唯一标识',
notfound: '找不到想要的?',
cannotDeleteGroupTips: '该分组下有数据, 不能删除!',
confirmDeleteGroup: '确定要删除分组 【{groupName}】 吗?',
confirmDeleteCIType: '确定要删除模型 【{typeName}】 吗?',
uploading: '正在导入中',
uploadFailed: '导入失败,请稍后重试',
addPlugin: '新建plugin',
deletePlugin: '删除plugin',
confirmDeleteADT: '确认删除 【{pluginName}】',
attributeMap: '字段映射',
autoDiscovery: '自动发现',
node: '节点',
adExecConfig: '执行配置',
adExecTarget: '执行机器',
oneagentIdTips: '请输入以0x开头的16进制OneAgent ID',
selectFromCMDBTips: '从CMDB中选择 ',
adAutoInLib: '自动入库',
adInterval: '采集频率',
byInterval: '按间隔',
allNodes: '所有节点',
specifyNodes: '指定节点',
specifyNodesTips: '请填写指定节点!',
username: '用户名',
password: '密码',
link: '链接',
list: '多值',
listTips: '字段的值是1个或者多个接口返回的值的类型是list',
computeForAllCITips: '所有CI触发计算',
confirmcomputeForAllCITips: '确认触发所有CI的计算',
isUnique: '是否唯一',
unique: '唯一',
isChoice: '是否选择',
defaultShow: '默认显示',
defaultShowTips: 'CI实例表格默认展示该字段',
isSortable: '可排序',
isIndex: '是否索引',
index: '索引',
indexTips: '字段可被用于检索,加速查询',
confirmDelete: '确认删除【{name}】?',
confirmDelete2: '确认删除?',
computeSuccess: '触发成功!',
basicConfig: '基础设置',
AttributeName: '属性名(英文)',
DataType: '数据类型',
defaultValue: '默认值',
autoIncID: '自增ID',
customTime: '自定义时间',
advancedSettings: '高级设置',
font: '字体',
color: '颜色',
choiceValue: '预定义值',
computedAttribute: '计算属性',
computedAttributeTips: '该属性的值是通过模型的其它属性构建的表达式或者执行一段代码的方式计算而来,属性的引用方法为: {{ 属性名 }}',
addAttribute: '新增属性',
existedAttributes: '已有属性',
editAttribute: '编辑属性',
addAttributeTips1: '选中排序,则必须也要选中!',
uniqueConstraint: '唯一校验',
up: '上移',
down: '下移',
selectAttribute: '添加属性',
groupExisted: '分组名称已存在',
attributeSortedTips: '其他分组中的属性不能进行排序,如需排序请先拖至自定义的分组!',
buildinAttribute: '内置字段',
expr: '表达式',
code: '代码',
apply: '应用',
continueAdd: '继续添加',
filter: '过滤',
choiceOther: '其他模型属性',
choiceWebhookTips: '返回的结果按字段来过滤,层级嵌套用##分隔比如k1##k2web请求返回{k1: [{k2: 1}, {k2: 2}]}, 解析结果为[1, 2]',
selectCIType: '请选择CMDB模型',
selectCITypeAttributes: '请选择模型属性',
selectAttributes: '请选择属性',
choiceScriptDemo: 'class ChoiceValue(object):\n @staticmethod\n def values():\n """\n 执行入口, 返回预定义值\n :return: 返回一个列表, 值的类型同属性的类型\n 例如:\n return ["在线", "下线"]\n """\n return []',
valueExisted: '当前值已存在!',
addRelation: '新增关系',
sourceCIType: '源模型',
sourceCITypeTips: '请选择源模型',
dstCIType: '目标模型名',
dstCITypeTips: '请选择目标模型',
relationType: '关联类型',
relationTypeTips: '请选择关联类型',
isParent: '被',
relationConstraint: '关系约束',
relationConstraintTips: '请选择关系约束',
one2Many: '一对多',
one2One: '一对一',
many2Many: '多对多',
basicInfo: '基本信息',
nameInputTips: '请输入名称',
triggerDataChange: '数据变更',
triggerDate: '日期属性',
triggerEnable: '开启',
descInput: '请输入备注',
triggerCondition: '触发条件',
addInstance: '新增实例',
deleteInstance: '删除实例',
changeInstance: '实例变更',
selectMutipleAttributes: '请选择属性(多选)',
selectSingleAttribute: '请选择属性(单选)',
beforeDays: '提前',
days: '天',
notifyAt: '发送时间',
notify: '通知',
triggerAction: '触发动作',
receivers: '收件人',
emailTips: '请输入邮箱,多个邮箱用;分隔',
customEmail: '自定义收件人',
notifySubject: '通知标题',
notifySubjectTips: '请输入通知标题',
notifyContent: '内容',
notifyMethod: '通知方式',
botSelect: '请选择机器人',
refAttributeTips: '标题、内容可以引用该模型的属性值,引用方法为: {{ attr_name }}',
webhookRefAttributeTips: '请求参数可以引用该模型的属性值,引用方法为: {{ attr_name }}',
newTrigger: '新增触发器',
editTriggerTitle: '编辑触发器 {name}',
newTriggerTitle: '新增触发器 {name}',
confirmDeleteTrigger: '确认删除该触发器吗?',
int: '整数',
float: '浮点数',
text: '文本',
datetime: '日期时间',
date: '日期',
time: '时间',
json: 'JSON',
event: '事件'
},
components: {
unselectAttributes: '未选属性',
selectAttributes: '已选属性',
downloadCI: '导出数据',
filename: '文件名',
filenameInputTips: '请输入文件名',
saveType: '保存类型',
saveTypeTips: '请选择保存类型',
xlsx: 'Excel工作簿(*.xlsx)',
csv: 'CSV(逗号分隔)(*.csv)',
html: '网页(*.html)',
xml: 'XML数据(*.xml)',
txt: '文本文件(制表符分隔)(*.txt)',
grantUser: '授权用户/部门',
grantRole: '授权角色',
confirmRevoke: '确认删除 【{name}】 的 【授权】 权限?',
readAttribute: '查看字段',
readCI: '查看实例',
config: '配置',
ciTypeGrant: '模型权限',
ciGrant: '实例权限',
attributeGrant: '字段权限',
relationGrant: '关系权限',
perm: '权限',
all: '全部',
customize: '自定义',
none: '无',
customizeFilterName: '请输入自定义筛选条件名',
colorPickerError: '初始化颜色格式错误,使用#fff或rgb格式',
example: '示例值',
aliyun: '阿里云',
tencentcloud: '腾讯云',
huaweicloud: '华为云',
beforeChange: '变更前',
afterChange: '变更后',
noticeContentTips: '请输入通知内容',
saveQuery: '保存筛选条件',
pleaseSearch: '请查找',
conditionFilter: '条件过滤',
attributeDesc: '属性说明',
ciSearchTips: '1. json属性不能搜索<br />2. 搜索内容包括逗号, 则需转义 ,<br />3. 只搜索索引属性, 非索引属性使用条件过滤',
ciSearchTips2: '例: q=hostname:*0.0.0.0*',
subCIType: '订阅模型',
already: '已',
not: '未',
sub: '订阅',
selectBelow: '请在下方进行选择',
subSuccess: '订阅成功',
selectMethods: '请选择方式',
noAuthRequest: '暂无请求认证',
noParamRequest: '暂无参数认证',
requestParam: '请求参数',
param: '参数{param}',
value: '值{value}',
clear: '清空',
},
batch: {
downloadFailed: '失败下载',
unselectCIType: '尚未选择模板类型',
pleaseUploadFile: '请上传文件',
batchUploadCanceled: '批量上传已取消',
selectCITypeTips: '请选择模板类型',
downloadTemplate: '下载模板',
drawTips: '点击或拖拽文件至此上传!',
supportFileTypes: '支持文件类型xlsxlsx',
uploadResult: '上传结果',
total: '共',
successItems: '条,已成功',
failedItems: '条,失败',
items: '条',
errorTips: '错误信息',
requestFailedTips: '请求出现错误,请稍后再试',
requestSuccessTips: '批量上传已完成',
},
preference: {
mySub: '我的订阅',
sub: '订阅',
cancelSub: '取消订阅',
editSub: '编辑订阅',
peopleSub: '位同事已订阅',
noSub: '暂无同事订阅',
cancelSubSuccess: '取消订阅成功',
confirmcancelSub: '确认取消订阅',
confirmcancelSub2: '确认取消订阅 {name} 吗?',
of: '的',
hoursAgo: '小时前',
daysAgo: '天前',
monthsAgo: '月前',
yearsAgo: '年前',
just: '刚刚',
},
custom_dashboard: {
charts: '图表',
newChart: '新增图表',
editChart: '编辑图表',
title: '标题',
titleTips: '请输入图表标题',
calcIndicators: '计算指标',
dimensions: '维度',
selectDimensions: '请选择维度',
quantity: '数量',
childCIType: '关系模型',
level: '层级',
levelTips: '请输入关系层级',
preview: '预览',
showIcon: '是否显示icon',
chartType: '图表类型',
dataFilter: '数据筛选',
format: '格式',
fontColor: '字体颜色',
backgroundColor: '背景颜色',
chartColor: '图表颜色',
chartLength: '图表长度',
barType: '柱状图类型',
stackedBar: '堆积柱状图',
multipleSeriesBar: '多系列柱状图',
axis: '轴',
direction: '方向',
lowerShadow: '下方阴影',
count: '指标',
bar: '柱状图',
line: '折线图',
pie: '饼状图',
table: '表格',
default: '默认',
relation: '关系',
noCustomDashboard: '管理员暂未定制仪表盘',
},
preference_relation: {
newServiceTree: '新增服务树',
serviceTreeName: '服务树名',
public: '公开',
saveLayout: '保存布局',
childNodesNotFound: '不存在子节点,不能形成业务关系,请重新选择!',
tips1: '不能与当前选中节点形成视图,请重新选择!',
tips2: '请输入新增服务树名!',
tips3: '请选择至少两个节点!',
},
history: {
ciChange: 'CI变更',
relationChange: '关系变更',
ciTypeChange: '模型变更',
triggerHistory: '触发历史',
opreateTime: '操作时间',
user: '用户',
userTips: '输入筛选用户名',
filter: '筛选',
filterOperate: '筛选操作',
attribute: '属性',
old: '旧',
new: '新',
noUpdate: '没有修改',
itemsPerPage: '/页',
triggerName: '触发器名称',
event: '事件',
action: '动作',
status: '状态',
done: '已完成',
undone: '未完成',
triggerTime: '触发时间',
totalItems: '共 {total} 条记录',
pleaseSelect: '请选择',
startTime: '开始时间',
endTime: '结束时间',
deleteCIType: '删除模型',
addCIType: '新增模型',
updateCIType: '修改模型',
addAttribute: '新增属性',
updateAttribute: '修改属性',
deleteAttribute: '删除属性',
addTrigger: '新增触发器',
updateTrigger: '修改触发器',
deleteTrigger: '删除触发器',
addUniqueConstraint: '新增联合唯一',
updateUniqueConstraint: '修改联合唯一',
deleteUniqueConstraint: '删除联合唯一',
addRelation: '新增关系',
deleteRelation: '删除关系',
noModifications: '没有修改',
attr: '属性名',
attrId: '属性ID',
changeDescription: '属性ID{attr_id},提前:{before_days}天,主题:{subject}\n内容{body}\n通知时间{notify_at}'
},
relation_type: {
addRelationType: '新增关系类型',
nameTips: '请输入类型名',
},
ad: {
upload: '规则导入',
download: '规则导出',
accpet: '入库',
accpetBy: '入库人',
acceptTime: '入库时间',
confirmAccept: '确认入库?',
accpetSuccess: '入库成功',
isAccpet: '是否入库',
deleteADC: '确认删除该条数据?',
batchDelete: '确认删除这些数据?',
agent: '内置 & 插件',
snmp: '网络设备',
http: '公有云资源',
rule: '自动发现规则',
timeout: '超时错误',
mode: '模式',
collectSettings: '采集设置',
updateFields: '更新字段',
pluginScript: `# -*- coding:utf-8 -*-
import json
class AutoDiscovery(object):
@property
def unique_key(self):
"""
:return: 返回唯一属性的名字
"""
return
@staticmethod
def attributes():
"""
定义属性字段
:return: 返回属性字段列表, 列表项是(名称, 类型, 描述), 名称必须是英文
类型: String Integer Float Date DateTime Time JSON
例如:
return [
("ci_type", "String", "模型名称"),
("private_ip", "String", "内网IP, 多值逗号分隔")
]
"""
return []
@staticmethod
def run():
"""
执行入口, 返回采集的属性值
:return: 返回一个列表, 列表项是字典, 字典key是属性名称, value是属性值
例如:
return [dict(ci_type="server", private_ip="192.168.1.1")]
"""
return []
if __name__ == "__main__":
result = AutoDiscovery().run()
if isinstance(result, list):
print("AutoDiscovery::Result::{}".format(json.dumps(result)))
else:
print("ERROR: 采集返回必须是列表")
`,
server: '物理机',
vserver: '虚拟机',
nic: '网卡',
disk: '硬盘',
},
ci: {
attributeDesc: '属性说明',
selectRows: '选取:{rows} 项',
addRelation: '添加关系',
all: '全部',
batchUpdate: '批量修改',
batchUpdateConfirm: '确认要批量修改吗?',
batchUpdateInProgress: '正在批量修改',
batchUpdateInProgress2: '正在批量修改,共{total}个,成功{successNum}个,失败{errorNum}个',
batchDeleting: '正在删除...',
batchDeleting2: '正在删除,共{total}个,成功{successNum}个,失败{errorNum}个',
copyFailed: '复制失败!',
noLevel: '无层级关系!',
batchAddRelation: '批量添加关系',
history: '操作历史',
topo: '拓扑',
table: '表格',
m2mTips: '当前模型关系为多对多,请前往关系视图进行增删操作',
confirmDeleteRelation: '确认删除关系?',
tips1: '多个值使用,分割',
tips2: '可根据需要修改字段,当值为 空 时,则该字段 置空',
tips3: '请选择需要修改的字段',
tips4: '必须至少选择一个字段',
tips5: '搜索 名称 | 别名',
tips6: '加快检索, 可以全文搜索, 无需使用条件过滤\n\n json目前不支持建索引 \n\n文本字符长度超过190不能建索引',
tips7: '表现形式是下拉框, 值必须在预定义值里',
tips8: '多值, 比如内网IP',
tips9: '仅针对前端',
tips10: '模型的其他属性通过表达式的方式计算出来\n\n一个代码片段计算返回的值',
newUpdateField: '新增修改字段',
attributeSettings: '字段设置',
},
serviceTree: {
deleteNode: '删除节点',
tips1: '例q=os_version:centos&sort=os_version',
tips2: '表达式搜索',
alert1: '管理员 还未配置业务关系, 或者你无权限访问!',
copyFailed: '复制失败',
deleteRelationConfirm: '确认将选中的 {name} 从当前关系中删除?',
},
tree: {
tips1: '请先到 我的订阅 页面完成订阅!',
subSettings: '订阅设置',
}
}
export default cmdb_zh

View File

@ -13,19 +13,19 @@ const genCmdbRoutes = async () => {
{
path: '/cmdb/dashboard',
name: 'cmdb_dashboard',
meta: { title: '仪表盘', icon: 'ops-cmdb-dashboard', selectedIcon: 'ops-cmdb-dashboard-selected', keepAlive: false },
meta: { title: 'dashboard', icon: 'ops-cmdb-dashboard', selectedIcon: 'ops-cmdb-dashboard-selected', keepAlive: false },
component: () => import('../views/dashboard/index_v2.vue')
},
{
path: '/cmdb/disabled1',
name: 'cmdb_disabled1',
meta: { title: '视图', disabled: true },
meta: { title: 'cmdb.menu.views', disabled: true },
},
{
path: '/cmdb/resourceviews',
name: 'cmdb_resource_views',
component: RouteView,
meta: { title: '资源数据', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource-selected', keepAlive: true },
meta: { title: 'cmdb.menu.ciTable', icon: 'ops-cmdb-resource', selectedIcon: 'ops-cmdb-resource-selected', keepAlive: true },
hideChildrenInMenu: false,
children: []
},
@ -33,108 +33,108 @@ const genCmdbRoutes = async () => {
path: '/cmdb/tree_views',
component: () => import('../views/tree_views'),
name: 'cmdb_tree_views',
meta: { title: '资源层级', icon: 'ops-cmdb-tree', selectedIcon: 'ops-cmdb-tree-selected', keepAlive: false },
meta: { title: 'cmdb.menu.ciTree', icon: 'ops-cmdb-tree', selectedIcon: 'ops-cmdb-tree-selected', keepAlive: false },
hideChildrenInMenu: true,
children: [
{
path: '/cmdb/tree_views/:typeId',
name: 'cmdb_tree_views_item',
component: () => import('../views/tree_views'),
meta: { title: '资源层级', keepAlive: false },
meta: { title: 'cmdb.menu.ciTree', keepAlive: false },
hidden: true
}]
},
{
path: '/cmdb/resourcesearch',
name: 'cmdb_resource_search',
meta: { title: '资源搜索', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search-selected', keepAlive: false },
meta: { title: 'cmdb.menu.ciSearch', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search-selected', keepAlive: false },
component: () => import('../views/resource_search/index.vue')
},
{
path: '/cmdb/adc',
name: 'cmdb_auto_discovery_ci',
meta: { title: '自动发现池', icon: 'ops-cmdb-adc', selectedIcon: 'ops-cmdb-adc-selected', keepAlive: false },
meta: { title: 'cmdb.menu.adCIs', icon: 'ops-cmdb-adc', selectedIcon: 'ops-cmdb-adc-selected', keepAlive: false },
component: () => import('../views/discoveryCI/index.vue')
},
{
path: '/cmdb/disabled2',
name: 'cmdb_disabled2',
meta: { title: '配置', disabled: true, },
meta: { title: 'cmdb.menu.config', disabled: true, },
},
{
path: '/cmdb/preference',
component: () => import('../views/preference/index'),
name: 'cmdb_preference',
meta: { title: '我的订阅', icon: 'ops-cmdb-preference', selectedIcon: 'ops-cmdb-preference-selected', keepAlive: false }
meta: { title: 'cmdb.menu.preference', icon: 'ops-cmdb-preference', selectedIcon: 'ops-cmdb-preference-selected', keepAlive: false }
},
{
path: '/cmdb/batch',
component: () => import('../views/batch'),
name: 'cmdb_batch',
meta: { 'title': '批量导入', icon: 'ops-cmdb-batch', selectedIcon: 'ops-cmdb-batch-selected', keepAlive: false }
meta: { 'title': 'cmdb.menu.batchUpload', icon: 'ops-cmdb-batch', selectedIcon: 'ops-cmdb-batch-selected', keepAlive: false }
},
{
path: '/cmdb/ci_types',
name: 'ci_type',
component: () => import('../views/ci_types/index'),
meta: { title: '模型配置', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype-selected', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
meta: { title: 'cmdb.menu.citypeManage', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype-selected', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
},
{
path: '/cmdb/disabled3',
name: 'cmdb_disabled3',
meta: { title: '管理端', disabled: true, permission: ['cmdb_admin', 'OneOPS_Application_Admin', 'admin'], },
meta: { title: 'cmdb.menu.backend', disabled: true, permission: ['cmdb_admin', 'OneOPS_Application_Admin', 'admin'], },
},
{
path: '/cmdb/citypes',
name: 'cmdb_ci_type',
component: RouteView,
redirect: '/cmdb/ci_type',
meta: { title: '后台管理', icon: 'setting', permission: ['cmdb_admin', 'OneOPS_Application_Admin', 'admin'], },
meta: { title: 'cmdb.menu.backendManage', icon: 'setting', permission: ['cmdb_admin', 'OneOPS_Application_Admin', 'admin'], },
children: [
{
path: '/cmdb/customdashboard',
name: 'cmdb_custom_dashboard',
component: () => import('../views/custom_dashboard/index'),
meta: { title: '定制仪表盘', keepAlive: false, icon: 'ops-cmdb-customdashboard', selectedIcon: 'ops-cmdb-customdashboard-selected' }
meta: { title: 'cmdb.menu.customDashboard', keepAlive: false, icon: 'ops-cmdb-customdashboard', selectedIcon: 'ops-cmdb-customdashboard-selected' }
},
{
path: '/cmdb/preferencerelation',
name: 'preference_relation',
component: () => import('../views/preference_relation/index'),
meta: { title: '业务关系定义', keepAlive: false, icon: 'ops-cmdb-preferencerelation', selectedIcon: 'ops-cmdb-preferencerelation-selected' }
meta: { title: 'cmdb.menu.serviceTreeDefine', keepAlive: false, icon: 'ops-cmdb-preferencerelation', selectedIcon: 'ops-cmdb-preferencerelation-selected' }
},
{
path: '/cmdb/modelrelation',
name: 'model_relation',
hideChildrenInMenu: true,
component: () => import('../views/model_relation/index'),
meta: { title: '模型关系', keepAlive: false, icon: 'ops-cmdb-modelrelation', selectedIcon: 'ops-cmdb-modelrelation-selected' }
meta: { title: 'cmdb.menu.citypeRelation', keepAlive: false, icon: 'ops-cmdb-modelrelation', selectedIcon: 'ops-cmdb-modelrelation-selected' }
},
{
path: '/cmdb/operationhistory',
name: 'operation_history',
hideChildrenInMenu: true,
component: () => import('../views/operation_history/index'),
meta: { title: '操作审计', keepAlive: false, icon: 'ops-cmdb-operation', selectedIcon: 'ops-cmdb-operation-selected' }
meta: { title: 'cmdb.menu.operationHistory', keepAlive: false, icon: 'ops-cmdb-operation', selectedIcon: 'ops-cmdb-operation-selected' }
},
{
path: '/cmdb/relationtype',
name: 'relation_type',
hideChildrenInMenu: true,
component: () => import('../views/relation_type/index'),
meta: { title: '关系类型', keepAlive: false, icon: 'ops-cmdb-relationtype', selectedIcon: 'ops-cmdb-relationtype-selected' }
meta: { title: 'cmdb.menu.relationType', keepAlive: false, icon: 'ops-cmdb-relationtype', selectedIcon: 'ops-cmdb-relationtype-selected' }
},
{
path: '/cmdb/discovery',
name: 'discovery',
component: () => import('../views/discovery/index'),
meta: { title: '自动发现', keepAlive: false, icon: 'ops-cmdb-adr', selectedIcon: 'ops-cmdb-adr-selected' }
meta: { title: 'cmdb.menu.ad', keepAlive: false, icon: 'ops-cmdb-adr', selectedIcon: 'ops-cmdb-adr-selected' }
},
]
}
]
}
// 动态添加订阅的条目及业务关系
// Dynamically add subscription items and business relationships
const [preference, relation] = await Promise.all([getPreference(), getRelationView()])
preference.forEach(item => {
@ -143,7 +143,7 @@ const genCmdbRoutes = async () => {
component: () => import(`../views/ci/index`),
name: `cmdb_${item.id}`,
meta: { title: item.alias, keepAlive: false, typeId: item.id, name: item.name, customIcon: item.icon },
// hideChildrenInMenu: true // 强制显示 MenuItem 而不是 SubMenu
// hideChildrenInMenu: true // Force display of MenuItem instead of SubMenu
})
})
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined

View File

@ -1,13 +1,17 @@
export const valueTypeMap = {
'0': '整数',
'1': '浮点数',
'2': '文本',
'3': '日期时间',
'4': '日期',
'5': '时间',
import i18n from '@/lang'
export const valueTypeMap = () => {
return {
'0': i18n.t('cmdb.ciType.int'),
'1': i18n.t('cmdb.ciType.float'),
'2': i18n.t('cmdb.ciType.text'),
'3': i18n.t('cmdb.ciType.datetime'),
'4': i18n.t('cmdb.ciType.date'),
'5': i18n.t('cmdb.ciType.time'),
'6': 'JSON',
'7': '密码',
'8': '链接'
'7': i18n.t('cmdb.ciType.password'),
'8': i18n.t('cmdb.ciType.link')
}
}
export const defautValueColor = [

View File

@ -16,9 +16,9 @@
<CiUploadTable :ciTypeAttrs="ciTypeAttrs" ref="ciUploadTable" :uploadData="uploadData"></CiUploadTable>
<div class="cmdb-batch-upload-action">
<a-space size="large">
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
<a-button @click="handleUpload" type="primary">上传</a-button>
<a-button v-if="hasError && !isUploading" @click="downloadError" type="primary">失败下载</a-button>
<a-button type="primary" ghost @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button @click="handleUpload" type="primary">{{ $t('upload') }}</a-button>
<a-button v-if="hasError && !isUploading" @click="downloadError" type="primary">{{ $t('cmdb.batch.downloadFailed') }}</a-button>
</a-space>
</div>
</a-col>
@ -109,7 +109,7 @@ export default {
},
handleUpload() {
if (!this.ciType) {
this.$message.error('尚未选择模板类型')
this.$message.error(this.$t('cmdb.batch.unselectCIType'))
return
}
if (this.uploadData && this.uploadData.length > 0) {
@ -118,7 +118,7 @@ export default {
this.$refs.uploadResult.upload2Server()
})
} else {
this.$message.error('请上传文件')
this.$message.error(this.$t('cmdb.batch.pleaseUploadFile'))
}
},
handleCancel() {
@ -127,7 +127,7 @@ export default {
this.$refs.ciTypeChoice.selectNum = null
this.hasError = false
} else {
this.$message.warning('批量上传已取消')
this.$message.warning(this.$t('cmdb.batch.batchUploadCanceled'))
this.isUploading = false
}
},

View File

@ -1,9 +1,9 @@
<template>
<a-space>
<span>模板类型</span>
<span>{{ $t('cmdb.ciType.ciType') }}: </span>
<a-select
showSearch
placeholder="请选择模板类型"
:placeholder="$t('cmdb.batch.selectCITypeTips')"
@change="selectCiType"
:style="{ width: '300px' }"
class="ops-select"
@ -20,7 +20,7 @@
type="primary"
class="ops-button-primary"
icon="download"
>下载模板</a-button
>{{ $t('cmdb.batch.downloadTemplate') }}</a-button
>
<a-modal
:bodyStyle="{ paddingTop: 0 }"
@ -31,14 +31,14 @@
@ok="handleOk"
wrapClassName="ci-type-choice-modal"
>
<a-divider orientation="left">模型属性</a-divider>
<a-divider orientation="left">{{ $t('cmdb.ciType.attributes') }}</a-divider>
<a-checkbox
@change="changeCheckAll"
:style="{ marginBottom: '20px' }"
:indeterminate="indeterminate"
:checked="checkAll"
>
全选
{{ $t('checkAll') }}
</a-checkbox>
<br />
<a-checkbox-group style="width:100%" v-model="checkedAttrs">
@ -52,7 +52,7 @@
</a-row>
</a-checkbox-group>
<template v-if="parentsType && parentsType.length">
<a-divider orientation="left">模型关联</a-divider>
<a-divider orientation="left">{{ $t('cmdb.ciType.relation') }}</a-divider>
<a-row :gutter="[24, 24]" align="top" type="flex">
<a-col :style="{ display: 'inline-flex' }" :span="12" v-for="item in parentsType" :key="item.id">
<a-checkbox @click="clickParent(item)" :checked="checkedParents.includes(item.alias || item.name)">
@ -150,7 +150,7 @@ export default {
},
methods: {
selectCiType(el) {
// 当选择好模板类型时的回调函数
// Callback function when a template type is selected
getCITypeAttributesById(el).then((res) => {
this.$emit('getCiTypeAttr', res)
this.selectCiTypeAttrList = res

View File

@ -10,8 +10,8 @@
:disabled="!ciType || isUploading"
>
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
<p class="ant-upload-text">点击或拖拽文件至此上传</p>
<p class="ant-upload-hint">支持文件类型xlsxlsx</p>
<p class="ant-upload-text">{{ $t('cmdb.batch.drawTips') }}</p>
<p class="ant-upload-hint">{{ $t('cmdb.batch.supportFileTypes') }}</p>
</a-upload-dragger>
<div v-for="item in fileList" :key="item.name" class="cmdb-batch-upload-dragger-file">
<span><a-icon type="file" :style="{ color: '#2F54EB', marginRight: '5px' }" />{{ item.name }}</span>

View File

@ -1,13 +1,13 @@
<template>
<div class="cmdb-batch-upload-result" v-if="visible">
<h3 class="cmdb-batch-upload-result-title">上传结果</h3>
<h3 class="cmdb-batch-upload-result-title">{{ $t('cmdb.batch.uploadResult') }}</h3>
<div class="cmdb-batch-upload-result-content">
<h4>
&nbsp;<span style="color: blue">{{ total }}</span> 已成功
<span style="color: lightgreen">{{ success }}</span> 失败 <span style="color: red">{{ errorNum }} </span>
{{ $t('cmdb.batch.total') }}&nbsp;<span style="color: blue">{{ total }}</span> {{ $t('cmdb.batch.successItems') }}
<span style="color: lightgreen">{{ success }}</span> {{ $t('cmdb.batch.failedItems') }} <span style="color: red">{{ errorNum }} </span>{{ $t('cmdb.batch.items') }}
</h4>
<div>
<span>错误信息</span>
<span>{{ $t('cmdb.batch.errorTips') }}: </span>
<ol>
<li :key="item + index" v-for="(item, index) in errorItems">{{ item }}</li>
</ol>
@ -71,7 +71,7 @@ export default {
if (r.status === 'fulfilled') {
this.success += 1
} else {
this.errorItems.push(r?.reason?.response?.data.message ?? '请求出现错误,请稍后再试')
this.errorItems.push(r?.reason?.response?.data.message ?? this.$t('cmdb.batch.requestFailedTips'))
this.errorNum += 1
this.$emit('uploadResultError', 6 * i + j)
}
@ -86,7 +86,7 @@ export default {
}
if (this.isUploading) {
this.$emit('uploadResultDone')
this.$message.success('批量上传已完成')
this.$message.success(this.$t('cmdb.batch.requestSuccessTips'))
}
},
},

View File

@ -11,22 +11,24 @@
"
class="cmdb-views-header-metadata"
>
<a-icon type="info-circle" />属性说明
<a-icon type="info-circle" />{{ $t('cmdb.ci.attributeDesc') }}
</span>
</span>
<a-space>
<a-button size="small" icon="plus" type="primary" @click="$refs.create.handleOpen(true, 'create')">
新建
{{ $t('create') }}
</a-button>
<a-button size="small" icon="user-add" type="primary" ghost @click="handlePerm">授权</a-button>
<a-button size="small" icon="user-add" type="primary" ghost @click="handlePerm">{{ $t('grant') }}</a-button>
<a-popconfirm
:title="`确认取消订阅 ${this.$route.meta.title || this.$route.meta.name} 吗?`"
ok-text="确认"
cancel-text="取消"
:title="
$t('cmdb.preference.confirmcancelSub2', { name: `${this.$route.meta.title || this.$route.meta.name}` })
"
:ok-text="$t('confirm')"
:cancel-text="$t('cancel')"
@confirm="unsubscribe"
placement="bottomRight"
>
<a-button size="small" icon="star" type="primary" ghost>取消订阅</a-button>
<a-button size="small" icon="star" type="primary" ghost>{{ $t('cmdb.preference.cancelSub') }}</a-button>
</a-popconfirm>
</a-space>
</div>
@ -47,12 +49,12 @@
@setParamsFromPreferenceSearch="setParamsFromPreferenceSearch"
/>
<div class="ops-list-batch-action" v-show="!!selectedRowKeys.length">
<span @click="$refs.create.handleOpen(true, 'update')">修改</span>
<span @click="$refs.create.handleOpen(true, 'update')">{{ $t('update') }}</span>
<a-divider type="vertical" />
<span @click="openBatchDownload">下载</span>
<span @click="openBatchDownload">{{ $t('download') }}</span>
<a-divider type="vertical" />
<span @click="batchDelete">删除</span>
<span>选取{{ selectedRowKeys.length }} </span>
<span @click="batchDelete">{{ $t('delete') }}</span>
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedRowKeys.length }) }}</span>
</div>
</SearchForm>
<CiDetail ref="detail" :typeId="typeId" />
@ -114,7 +116,7 @@
:getPopupContainer="(trigger) => trigger.parentElement"
:style="{ width: '100%', height: '32px' }"
v-model="row[col.field]"
placeholder="请选择"
:v-bind="$t('placeholder2')"
v-if="col.is_choice"
:showArrow="false"
:mode="col.is_list ? 'multiple' : 'default'"
@ -149,7 +151,7 @@
:getPopupContainer="(trigger) => trigger.parentElement"
:style="{ width: '100%', height: '32px' }"
v-model="row[col.field]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-else-if="col.is_list"
:showArrow="false"
mode="tags"
@ -230,9 +232,9 @@
</template>
</template>
</vxe-table-column>
<vxe-column align="left" field="operate" fixed="right" width="80">
<vxe-column align="left" field="operate" fixed="right" width="120">
<template #header>
<span>操作</span>
<span>{{ $t('operation') }}</span>
<EditAttrsPopover :typeId="typeId" class="operation-icon" @refresh="refreshAfterEditAttrs" />
<!-- <a-icon class="operation-icon" type="control" /> -->
</template>
@ -241,7 +243,7 @@
<a @click="$refs.detail.create(row.ci_id || row._id)">
<a-icon type="unordered-list" />
</a>
<a-tooltip title="添加关系">
<a-tooltip :title="$t('cmdb.ci.addRelation')">
<a @click="$refs.detail.create(row.ci_id || row._id, 'tab_2', '2')">
<a-icon type="retweet" />
</a>
@ -253,10 +255,10 @@
</template>
</vxe-column>
<template #empty>
<div v-if="loading" style="height: 200px; line-height: 200px">加载中...</div>
<div v-if="loading" style="height: 200px; line-height: 200px">{{ $t('loading') }}</div>
<div v-else>
<img :style="{ width: '200px' }" :src="require('@/assets/data_empty.png')" />
<div>暂无数据</div>
<div>{{ $t('noData') }}</div>
</div>
</template>
</ops-table>
@ -270,7 +272,14 @@
:page-size="pageSize"
:page-size-options="pageSizeOptions"
@showSizeChange="onShowSizeChange"
:show-total="(total, range) => `当前${range[0]}-${range[1]} 共 ${total}条记录`"
:show-total="
(total, range) =>
$t('pagination.total', {
range0: range[0],
range1: range[1],
total,
})
"
@change="
(page) => {
currentPage = page
@ -278,12 +287,14 @@
"
>
<template slot="buildOptionText" slot-scope="props">
<span v-if="props.value !== '100000'">{{ props.value }}/</span>
<span v-if="props.value === '100000'">全部</span>
<span v-if="props.value !== '100000'">{{ props.value }}{{ $t('itemsPerPage') }}</span>
<span v-if="props.value === '100000'">{{ $t('cmdb.ci.all') }}</span>
</template>
</a-pagination>
</div>
<create-instance-form ref="create" @reload="reloadData" @submit="batchUpdate" />
<!-- <EditAttrsDrawer ref="editAttrsDrawer" @refresh="refreshAfterEditAttrs" /> -->
<!-- <batch-update-relation :typeId="typeId" ref="batchUpdateRelation" @submit="batchUpdateRelation" /> -->
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
<BatchDownload ref="batchDownload" @batchDownload="batchDownload" />
<MetadataDrawer ref="metadataDrawer" />
@ -363,17 +374,16 @@ export default {
preferenceAttrList: [],
instanceList: [],
// 表头
columns: [],
// custom table alert & rowSelection
selectedRowKeys: [],
// 对照是否编辑
// Check whether to edit
initialInstanceList: [],
sortByTable: undefined,
isEditActive: false,
attrList: [],
attributes: {},
// 表格拖拽的参数
// Table drag parameters
tableDragClassName: [],
resource_type: {},
@ -419,11 +429,15 @@ export default {
},
inject: ['reloadBoard'],
mounted() {
// window.onkeypress = (e) => {
// this.handleKeyPress(e)
// }
setTimeout(() => {
this.columnDrop()
}, 1000)
},
beforeDestroy() {
// window.onkeypress = null
if (this.sortable) {
this.sortable.destroy()
}
@ -449,14 +463,19 @@ export default {
async loadTableData(sortByTable = undefined) {
try {
this.loading = true
// 若模糊搜索可以 queryParam相关后期可删除
// If fuzzy search is possible, queryParam can be deleted later.
// const queryParams = this.$refs['search'].queryParam || {}
const fuzzySearch = this.$refs['search'].fuzzySearch
const expression = this.$refs['search'].expression || ''
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const regSort = /(?<=sort=).+/g
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
// 如果是表格点击的排序 以表格为准
// if (exp) {
// exp = exp.replace(/(\:)/g, '$1*')
// exp = exp.replace(/(\,)/g, '*$1')
// }
// If the sorting is done by clicking on the table, the table will prevail.
let sort
if (sortByTable) {
sort = sortByTable
@ -514,18 +533,55 @@ export default {
this.$refs['xTable'].getVxetableRef().setCheckboxRow(rows, true)
}
},
// mergeQ(params) {
// let q = `_type:${this.typeId}`
// Object.keys(params).forEach((key) => {
// if (!['pageNo', 'pageSize', 'sortField', 'sortOrder'].includes(key) && params[key] + '' !== '') {
// if (typeof params[key] === 'object' && params[key] && params[key].length > 1) {
// q += `,${key}:(${params[key].join(';')})`
// } else if (params[key]) {
// q += `,${key}:*${params[key]}*`
// }
// }
// })
// return q
// },
async loadPreferenceAttrList() {
const subscribed = await getSubscribeAttributes(this.typeId)
this.preferenceAttrList = subscribed.attributes // 已经订阅的全部列
this.preferenceAttrList = subscribed.attributes // All columns that have been subscribed
},
onSelectChange() {
// const current = records.map((i) => i.ci_id || i._id)
// const cached = new Set(this.selectedRowKeys)
// if (checked) {
// current.forEach((i) => {
// cached.add(i)
// })
// } else {
// if (row) {
// cached.delete(row.ci_id || row._id)
// } else {
// this.instanceList.map((row) => {
// cached.delete(row.ci_id || row._id)
// })
// }
// }
const xTable = this.$refs.xTable.getVxetableRef()
const records = [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()]
this.selectedRowKeys = records.map((i) => i.ci_id || i._id)
},
onSelectRangeEnd({ records }) {
// const current = records.map((i) => i.ci_id || i._id)
// const cached = new Set(this.selectedRowKeys)
// current.forEach((i) => {
// cached.add(i)
// })
this.selectedRowKeys = records.map((i) => i.ci_id || i._id)
// this.setSelectRows()
},
reloadData() {
this.loadTableData()
@ -538,7 +594,10 @@ export default {
const $table = this.$refs['xTable'].getVxetableRef()
const data = {}
this.columns.forEach((item) => {
if (!(item.field in this.initialPasswordValue) && !_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])) {
if (
!(item.field in this.initialPasswordValue) &&
!_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])
) {
data[item.field] = row[item.field] ?? null
}
})
@ -553,7 +612,7 @@ export default {
if (JSON.stringify(data) !== '{}') {
updateCI(row.ci_id || row._id, data)
.then(() => {
this.$message.success('保存成功!')
this.$message.success(this.$t('saveSuccess'))
$table.reloadRow(row, null)
const _initialInstanceList = _.cloneDeep(this.initialInstanceList)
_initialInstanceList[rowIndex] = {
@ -611,8 +670,8 @@ export default {
batchUpdate(values) {
const that = this
this.$confirm({
title: '警告',
content: '确认要批量修改吗?',
title: that.$t('warning'),
content: that.$t('cmdb.ci.batchUpdateConfirm'),
async onOk() {
that.batchUpdateAsync(values)
},
@ -622,14 +681,14 @@ export default {
let successNum = 0
let errorNum = 0
this.loading = true
this.loadTip = `正在批量修改...`
this.loadTip = this.$t('cmdb.ci.batchUpdateInProgress') + '...'
const payload = {}
Object.keys(values).forEach((key) => {
if (values[key] || values[key] === 0) {
payload[key] = values[key]
}
// 字段值支持置空
// 目前存在字段值不支持置空由后端返回
// Field values support blanking
// There are currently field values that do not support blanking and will be returned by the backend.
if (values[key] === undefined || values[key] === null) {
payload[key] = null
}
@ -644,7 +703,11 @@ export default {
errorNum += 1
})
.finally(() => {
this.loadTip = `正在批量修改${this.selectedRowKeys.length}成功${successNum}失败${errorNum}`
this.loadTip = this.$t('cmdb.ci.batchUpdateInProgress2', {
total: this.selectedRowKeys.length,
successNum: successNum,
errorNum: errorNum,
})
})
}
this.loading = false
@ -657,8 +720,8 @@ export default {
batchDelete() {
const that = this
this.$confirm({
title: '警告',
content: '确认删除?',
title: that.$t('warning'),
content: that.$t('confirmDelete'),
onOk() {
that.batchDeleteAsync()
},
@ -668,7 +731,7 @@ export default {
let successNum = 0
let errorNum = 0
this.loading = true
this.loadTip = `正在删除...`
this.loadTip = this.$t('cmdb.ci.batchDeleting')
const floor = Math.ceil(this.selectedRowKeys.length / 6)
for (let i = 0; i < floor; i++) {
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
@ -684,7 +747,11 @@ export default {
})
})
.finally(() => {
this.loadTip = `正在删除${this.selectedRowKeys.length}成功${successNum}失败${errorNum}`
this.loadTip = this.$t('cmdb.ci.batchDeleting2', {
total: this.selectedRowKeys.length,
successNum: successNum,
errorNum: errorNum,
})
})
}
this.loading = false
@ -703,12 +770,12 @@ export default {
deleteCI(record) {
const that = this
this.$confirm({
title: '警告',
content: '确认删除?',
title: that.$t('warning'),
content: that.$t('confirmDelete'),
onOk() {
deleteCI(record.ci_id || record._id).then((res) => {
// that.$refs.table.refresh(true)
that.$message.success('删除成功!')
that.$message.success(that.$t('deleteSuccess'))
that.reloadData()
})
},
@ -731,7 +798,7 @@ export default {
this.$refs.jsonEditor.open(column, row)
},
jsonEditorOk(row, column, jsonData) {
// 后端写数据有快慢不拉接口直接修改table的数据
// The backend writes data at different speeds. You can modify the table data directly without pulling the interface.
// this.reloadData()
this.instanceList.forEach((item) => {
if (item._id === row._id) {
@ -747,6 +814,9 @@ export default {
} else {
this.currentPage = 1
}
setTimeout(() => {
// this.setSelectRows()
}, 500)
},
handleSortCol({ column, property, order, sortBy, sortList, $event }) {
let sortByTable
@ -781,7 +851,7 @@ export default {
},
onEnd: (params) => {
// 由于开启了虚拟滚动newIndex和oldIndex是虚拟的
const { newIndex, oldIndex, from, to } = params
const { newIndex, oldIndex } = params
// 从tableDragClassName拿到colid
const fromColid = this.tableDragClassName[oldIndex]
const toColid = this.tableDragClassName[newIndex]
@ -798,6 +868,9 @@ export default {
)
})
},
// tableFilterChangeEvent({ column, property, values, datas, filterList, $event }) {
// console.log(111)
// },
getChoiceValueStyle(col, colValue) {
const _find = col.filters.find((item) => String(item[0]) === String(colValue))
if (_find) {
@ -885,10 +958,10 @@ export default {
const text = `q=_type:${this.typeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`
this.$copyText(text)
.then(() => {
this.$message.success('复制成功!')
this.$message.success(this.$t('copySuccess'))
})
.catch(() => {
this.$message.error('复制失败!')
this.$message.error(this.$t('cmdb.ci.copyFailed'))
})
},
unsubscribe(ciType, type = 'all') {
@ -898,7 +971,7 @@ export default {
if (Number(ciType) === Number(lastTypeId)) {
localStorage.setItem('ops_ci_typeid', '')
}
this.$message.success('取消订阅成功')
this.$message.success(this.$t('cmdb.preference.cancelSubSuccess'))
this.resetRoute()
this.$router.push('/cmdb/preference')
})
@ -929,7 +1002,7 @@ export default {
})
})
} else {
this.$message.error('权限不足!')
this.$message.error(this.$t('noPermission'))
}
})
},

View File

@ -1,6 +1,6 @@
<template>
<a-drawer
title="批量添加关系"
:title="$t('cmdb.ci.batchAddRelation')"
width="50%"
@close="() => { visible = false; $emit('refresh', true) }"
:visible="visible"

View File

@ -16,10 +16,10 @@
>
<a-tabs v-model="activeTabKey" @change="changeTab">
<a-tab-pane key="tab_1">
<span slot="tab"><a-icon type="book" />属性</span>
<span slot="tab"><a-icon type="book" />{{ $t('cmdb.attribute') }}</span>
<div :style="{ maxHeight: `${windowHeight - 44}px`, overflow: 'auto', padding: '24px' }" class="ci-detail-attr">
<el-descriptions
:title="group.name || '其他'"
:title="group.name || $t('other')"
:key="group.name"
v-for="group in attributeGroups"
border
@ -36,13 +36,13 @@
</div>
</a-tab-pane>
<a-tab-pane key="tab_2">
<span slot="tab"><a-icon type="branches" />关系</span>
<span slot="tab"><a-icon type="branches" />{{ $t('cmdb.relation') }}</span>
<div :style="{ padding: '24px' }">
<CiDetailRelation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" />
</div>
</a-tab-pane>
<a-tab-pane key="tab_3">
<span slot="tab"><a-icon type="clock-circle" />操作历史</span>
<span slot="tab"><a-icon type="clock-circle" />{{ $t('cmdb.ci.history') }}</span>
<div :style="{ padding: '24px', height: 'calc(100vh - 44px)' }">
<vxe-table
ref="xTable"
@ -54,22 +54,22 @@
:scroll-y="{ enabled: false }"
class="ops-stripe-table"
>
<vxe-table-column sortable field="created_at" title="时间"></vxe-table-column>
<vxe-table-column sortable field="created_at" :title="$t('created_at')"></vxe-table-column>
<vxe-table-column
field="username"
title="用户"
:title="$t('user')"
:filters="[]"
:filter-method="filterUsernameMethod"
></vxe-table-column>
<vxe-table-column
field="operate_type"
:filters="[
{ value: 0, label: '新增' },
{ value: 1, label: '删除' },
{ value: 3, label: '修改' },
{ value: 0, label: $t('new') },
{ value: 1, label: $t('delete') },
{ value: 3, label: $t('update') },
]"
:filter-method="filterOperateMethod"
title="操作"
:title="$t('operation')"
>
<template #default="{ row }">
{{ operateTypeMap[row.operate_type] }}
@ -77,17 +77,17 @@
</vxe-table-column>
<vxe-table-column
field="attr_alias"
title="属性"
:title="$t('cmdb.attribute')"
:filters="[]"
:filter-method="filterAttrMethod"
></vxe-table-column>
<vxe-table-column field="old" title=""></vxe-table-column>
<vxe-table-column field="new" title=""></vxe-table-column>
<vxe-table-column field="old" :title="$t('cmdb.history.old')"></vxe-table-column>
<vxe-table-column field="new" :title="$t('cmdb.history.new')"></vxe-table-column>
</vxe-table>
</div>
</a-tab-pane>
<a-tab-pane key="tab_4">
<span slot="tab"><ops-icon type="itsm_auto_trigger" />触发历史</span>
<span slot="tab"><ops-icon type="itsm_auto_trigger" />{{ $t('cmdb.history.triggerHistory') }}</span>
<div :style="{ padding: '24px', height: 'calc(100vh - 44px)' }">
<TriggerTable :ci_id="ci._id" />
</div>
@ -125,13 +125,7 @@ export default {
},
},
data() {
const operateTypeMap = {
0: '新增',
1: '删除',
2: '修改',
}
return {
operateTypeMap,
visible: false,
ci: {},
attributeGroups: [],
@ -146,6 +140,14 @@ export default {
windowHeight() {
return this.$store.state.windowHeight
},
operateTypeMap() {
return {
0: this.$t('new'),
1: this.$t('delete'),
2: this.$t('update'),
}
},
},
provide() {
return {

View File

@ -10,8 +10,8 @@
:headerStyle="{ borderBottom: 'none' }"
>
<div class="custom-drawer-bottom-action">
<a-button @click="handleClose">取消</a-button>
<a-button type="primary" @click="createInstance">提交</a-button>
<a-button @click="handleClose">{{ $t('cancel') }}</a-button>
<a-button type="primary" @click="createInstance">{{ $t('submit') }}</a-button>
</div>
<template v-if="action === 'create'">
<template v-for="group in attributesByGroup">
@ -24,7 +24,7 @@
/>
</template>
<template v-if="parentsType && parentsType.length">
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">模型关系</a-divider>
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{ $t('cmdb.menu.citypeRelation') }}</a-divider>
<a-form>
<a-row :gutter="24" align="top" type="flex">
<a-col :span="12" v-for="item in parentsType" :key="item.id">
@ -40,7 +40,7 @@
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<a-input placeholder="多个值使用,分割" v-model="parentsForm[item.name].value" style="width: 50%" />
<a-input :placeholder="$t('cmdb.ci.tips1')" v-model="parentsForm[item.name].value" style="width: 50%" />
</a-input-group>
</a-form-item>
</a-col>
@ -50,11 +50,11 @@
</template>
<template v-if="action === 'update'">
<a-form :form="form">
<p>可根据需要修改字段当值为<strong></strong>则该字段<strong>置空</strong></p>
<p>{{ $t('cmdb.ci.tips2') }}</p>
<a-row :gutter="24" v-for="list in batchUpdateLists" :key="list.name">
<a-col :span="11">
<a-form-item>
<el-select showSearch size="small" filterable v-model="list.name" placeholder="请选择需要修改的字段">
<el-select showSearch size="small" filterable v-model="list.name" :placeholder="$t('cmdb.ci.tips3')">
<el-option
v-for="attr in attributeList"
:key="attr.name"
@ -71,7 +71,7 @@
<a-select
:style="{ width: '100%' }"
v-decorator="[list.name, { rules: [{ required: false }] }]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-if="getFieldType(list.name).split('%%')[0] === 'select'"
:mode="getFieldType(list.name).split('%%')[1] === 'multiple' ? 'multiple' : 'default'"
showSearch
@ -119,7 +119,7 @@
</a-form-item>
</a-col>
</a-row>
<a-button type="primary" ghost icon="plus" @click="handleAdd">新增修改字段</a-button>
<a-button type="primary" ghost icon="plus" @click="handleAdd">{{ $t('cmdb.ci.newUpdateField') }}</a-button>
</a-form>
</template>
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
@ -153,7 +153,6 @@ export default {
},
data() {
return {
valueTypeMap,
action: '',
form: this.$form.createForm(this),
visible: false,
@ -171,7 +170,7 @@ export default {
},
computed: {
title() {
return this.action === 'create' ? '创建 ' : '批量修改 '
return this.action === 'create' ? this.$t('create') + ' ' : this.$t('cmdb.ci.batchUpdate') + ' '
},
typeId() {
if (this.typeIdFromRelation) {
@ -179,6 +178,9 @@ export default {
}
return this.$router.currentRoute.meta.typeId
},
valueTypeMap() {
return valueTypeMap()
},
},
provide() {
return {
@ -210,7 +212,7 @@ export default {
(attr) => !attrHasGroupIds.includes(attr.id) && !attr.is_computed
)
if (otherGroupAttr.length) {
_attributesByGroup.push({ id: -1, name: '其他', attributes: otherGroupAttr })
_attributesByGroup.push({ id: -1, name: this.$t('other'), attributes: otherGroupAttr })
}
this.attributesByGroup = _attributesByGroup
})
@ -283,7 +285,7 @@ export default {
}
})
addCI(values).then((res) => {
_this.$message.success('新增成功!')
_this.$message.success(this.$t('addSuccess'))
_this.visible = false
_this.$emit('reload', { ci_id: res.ci_id })
})
@ -332,7 +334,7 @@ export default {
} else if (_find.value_type === '0' || _find.value_type === '1') {
return 'input_number'
} else if (_find.value_type === '4' || _find.value_type === '3') {
return valueTypeMap[_find.value_type]
return this.valueTypeMap[_find.value_type]
} else {
return 'input'
}

View File

@ -7,7 +7,7 @@
visible = false
}
"
title="属性说明"
:title="$t('cmdb.ci.attributeDesc')"
width="72%"
:bodyStyle="{ height: '100vh' }"
>
@ -18,7 +18,7 @@
:style="{ display: 'inline-block', width: '244px' }"
class="ops-input ops-input-radius"
type="search"
placeholder="搜索 名称 | 别名"
:placeholder="$t('cmdb.ci.tips5')"
@keyup="searchAttributes"
>
<a-icon type="search" slot="suffix" />
@ -56,8 +56,8 @@
: index === 2
? valueTypeFilters
: [
{ label: '', value: true },
{ label: '', value: false },
{ label: $t('yes'), value: true },
{ label: $t('no'), value: false },
]
"
type="html"
@ -82,78 +82,11 @@ import { valueTypeMap } from '@/modules/cmdb/utils/const'
export default {
name: 'MetadataDrawer',
data() {
const columns = [
{
field: 'name',
title: '名称',
width: 150,
align: 'left',
help: null,
},
{
field: 'alias',
title: '别名',
width: 150,
align: 'left',
help: null,
},
{
field: 'value_type',
title: '类型',
width: 100,
align: 'left',
help: null,
},
{
field: 'is_index',
title: '是否索引',
width: 110,
help: '加快检索, 可以全文搜索, 无需使用条件过滤\n\n json目前不支持建索引 \n\n文本字符长度超过190不能建索引',
},
{
field: 'default_show',
title: '默认显示',
width: 110,
help: '订阅CI默认显示在table里的属性',
},
{
field: 'is_unique',
title: '是否唯一',
width: 110,
help: null,
},
{
field: 'is_choice',
title: '是否选择',
width: 110,
help: '表现形式是下拉框, 值必须在预定义值里',
},
{
field: 'is_list',
title: '是否列表',
width: 110,
help: '多值, 比如内网IP',
},
{
field: 'is_sortable',
title: '可排序',
width: 100,
help: '仅针对前端',
},
{
field: 'is_computed',
title: '计算属性',
width: 110,
help: '模型的其他属性通过表达式的方式计算出来\n\n一个代码片段计算返回的值',
},
]
return {
columns,
visible: false,
list: [],
tableData: [],
loading: false,
valueTypeMap,
valueTypeFilters: [],
searchKey: '',
}
@ -162,6 +95,76 @@ export default {
windowHeight() {
return this.$store.state.windowHeight
},
valueTypeMap() {
return valueTypeMap()
},
columns() {
return [
{
field: 'name',
title: this.$t('name'),
width: 150,
align: 'left',
help: null,
},
{
field: 'alias',
title: this.$t('alias'),
width: 150,
align: 'left',
help: null,
},
{
field: 'value_type',
title: this.$t('type'),
width: 100,
align: 'left',
help: null,
},
{
field: 'is_index',
title: this.$t('cmdb.ciType.isIndex'),
width: 110,
help: this.$t('cmdb.ci.tips6'),
},
{
field: 'default_show',
title: this.$t('cmdb.ciType.defaultShow'),
width: 110,
help: this.$t('cmdb.ciType.defaultShowTips'),
},
{
field: 'is_unique',
title: this.$t('cmdb.ciType.isUnique'),
width: 110,
help: null,
},
{
field: 'is_choice',
title: this.$t('cmdb.ciType.isChoice'),
width: 110,
help: this.$t('cmdb.ci.tips7'),
},
{
field: 'is_list',
title: this.$t('cmdb.ciType.list'),
width: 110,
help: this.$t('cmdb.ci.tips8'),
},
{
field: 'is_sortable',
title: this.$t('cmdb.ciType.isSortable'),
width: 100,
help: this.$t('cmdb.ci.tips9'),
},
{
field: 'is_computed',
title: this.$t('cmdb.ciType.computedAttribute'),
width: 110,
help: this.$t('cmdb.ci.tips10'),
},
]
},
},
created: function() {
this.valueTypeFilters = Object.keys(this.valueTypeMap).map((key) => {

View File

@ -75,7 +75,7 @@
rules: [{ required: attr.is_required }],
},
]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-if="attr.is_choice"
:mode="attr.is_list ? 'multiple' : 'default'"
showSearch
@ -113,7 +113,7 @@
rules: [{ required: attr.is_required }],
},
]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-else-if="attr.is_list"
mode="tags"
showSearch
@ -263,7 +263,7 @@ export default {
if (!_.isEqual(this.ci[this.attr.name], newData)) {
await updateCI(this.ci._id, { [`${this.attr.name}`]: newData })
.then(() => {
this.$message.success('更新成功!')
this.$message.success(this.$t('updateSuccess'))
this.$emit('updateCIByself', { [`${this.attr.name}`]: newData }, this.attr.name)
})
.catch(() => {
@ -286,7 +286,7 @@ export default {
if (!_.isEqual(this.ci[this.attr.name], jsonData)) {
updateCI(this.ci._id, { [`${this.attr.name}`]: jsonData })
.then(() => {
this.$message.success('更新成功!')
this.$message.success(this.$t('updateSuccess'))
this.$emit('updateCIByself', { [`${this.attr.name}`]: jsonData }, this.attr.name)
})
.catch(() => {

View File

@ -2,10 +2,10 @@
<div class="ci-detail-relation">
<a-radio-group v-model="activeKey" size="small" @change="handleChangeActiveKey">
<a-radio-button value="1">
拓扑
{{ $t('cmdb.ci.topo') }}
</a-radio-button>
<a-radio-button value="2">
表格
{{ $t('cmdb.ci.table') }}
</a-radio-button>
</a-radio-group>
<CiDetailRelationTopo ref="ciDetailRelationTopo" v-if="activeKey === '1'" />
@ -24,7 +24,7 @@
><a-icon
type="plus-square"
/></a>
<span v-if="!canEdit[parent.id]">当前模型关系为多对多请前往关系视图进行增删操作</span>
<span v-if="!canEdit[parent.id]">{{ $t('cmdb.ci.m2mTips') }}</span>
</div>
<vxe-grid
v-if="firstCIs[parent.name]"
@ -39,7 +39,7 @@
class="ops-stripe-table"
>
<template #operation_default="{ row }">
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(row._id, ciId)">
<a-popconfirm arrowPointAtCenter :title="$t('cmdb.ci.confirmDeleteRelation')" @confirm="deleteRelation(row._id, ciId)">
<a
:disabled="!canEdit[parent.id]"
:style="{
@ -68,7 +68,7 @@
><a-icon
type="plus-square"
/></a>
<span v-if="!canEdit[child.id]">当前模型关系为多对多请前往关系视图进行增删操作</span>
<span v-if="!canEdit[child.id]">{{ $t('cmdb.ci.m2mTips') }}</span>
</div>
<vxe-grid
v-if="secondCIs[child.name]"
@ -82,7 +82,7 @@
class="ops-stripe-table"
>
<template #operation_default="{ row }">
<a-popconfirm arrowPointAtCenter title="确认删除关系?" @confirm="deleteRelation(ciId, row._id)">
<a-popconfirm arrowPointAtCenter :title="$t('cmdb.ci.confirmDeleteRelation')" @confirm="deleteRelation(ciId, row._id)">
<a
:disabled="!canEdit[child.id]"
:style="{
@ -338,7 +338,7 @@ export default {
firstCIColumns[item.id].push({
key: 'p_operation',
field: 'operation',
title: '操作',
title: this.$t('operation'),
width: '60px',
fixed: 'right',
slots: {
@ -379,7 +379,7 @@ export default {
secondCIColumns[item.id].push({
key: 'c_operation',
field: 'operation',
title: '操作',
title: this.$t('operation'),
width: '60px',
fixed: 'right',
slots: {

View File

@ -91,7 +91,7 @@ export default {
const newNodes = []
const newEdges = []
if (!res.result.length) {
this.$message.info('无层级关系!')
this.$message.info(this.$t('cmdb.ci.noLevel'))
return
}
const ci_types_list = this.ci_types()

View File

@ -1,5 +1,6 @@
/* eslint-disable no-useless-constructor */
import { TreeNode } from 'butterfly-dag'
import i18n from '@/lang'
import $ from 'jquery'
@ -31,7 +32,7 @@ class BaseNode extends TreeNode {
container.append(uniqueDom)
if (opts.options.side && (!opts.options.children.length && !(opts.options.edges && opts.options.edges.length && opts.options.edges.find(e => e.source === opts.options.side && e.sourceNode === opts.options.id)))) {
const addIcon = $(`<i aria-label="图标: plus-square" class="anticon anticon-plus-square add-icon-${opts.options.side}"><svg viewBox="64 64 896 896" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></i>`)
const addIcon = $(`<i aria-label="${i18n.t('icon')}: plus-square" class="anticon anticon-plus-square add-icon-${opts.options.side}"><svg viewBox="64 64 896 896" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></i>`)
container.append(addIcon)
addIcon.on('click', () => {
if (opts.options.side === 'left') {

View File

@ -1,6 +1,6 @@
<template>
<a-form :form="form">
<a-divider style="font-size: 14px; margin: 14px 0; font-weight: 700">{{ group.name || '其他' }}</a-divider>
<a-divider style="font-size:14px;margin:14px 0;font-weight:700;">{{ group.name || $t('other') }}</a-divider>
<a-row :gutter="24" align="top" type="flex">
<a-col
:span="12"
@ -21,11 +21,11 @@
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required, message: `请选择${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : attr.is_list ? [] : null,
},
]"
placeholder="请选择"
:placeholder="$t('placeholder2')"
v-if="attr.is_choice"
:mode="attr.is_list ? 'multiple' : 'default'"
showSearch
@ -60,7 +60,7 @@
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required, message: `请选择${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : attr.is_list ? [] : null,
},
]"
@ -70,7 +70,7 @@
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required, message: `请输入${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder1') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : null,
},
]"
@ -81,7 +81,7 @@
v-decorator="[
attr.name,
{
rules: [{ required: attr.is_required, message: `请选择${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder2') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? moment(attr.default.default) : null,
},
]"
@ -96,7 +96,7 @@
attr.name,
{
validateTrigger: ['submit'],
rules: [{ required: attr.is_required, message: `请输入${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder1') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? JSON.stringify(attr.default.default) : '',
},
]"
@ -109,7 +109,7 @@
attr.name,
{
validateTrigger: ['submit'],
rules: [{ required: attr.is_required, message: `请输入${attr.alias || attr.name}` }],
rules: [{ required: attr.is_required, message: $t('placeholder1') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : null,
},
]"

View File

@ -7,7 +7,7 @@
visible = false
}
"
title="字段设置"
:title="$t('cmdb.ci.attributeSettings')"
>
<CustomTransfer
ref="customTransfer"
@ -17,16 +17,16 @@
width: '230px',
height: '500px',
}"
:titles="['未选属性', '已选属性']"
:titles="[$t('cmdb.components.unselectAttributes'), $t('cmdb.components.selectAttributes')]"
:render="item => item.title"
:targetKeys="selectedAttrList"
@change="handleChange"
@selectChange="selectChange"
>
<span slot="notFoundContent">没数据</span>
<span slot="notFoundContent">{{ $t('noData') }}</span>
</CustomTransfer>
<div class="custom-drawer-bottom-action">
<a-button @click="handleSubmit" type="primary">确定</a-button>
<a-button @click="handleSubmit" type="primary">{{ $t('confirm') }}</a-button>
</div>
</CustomDrawer>
</template>
@ -79,16 +79,17 @@ export default {
this.selectedAttrList = targetKeys
},
handleSubmit() {
const that = this
if (this.selectedAttrList.length) {
subscribeCIType(this.typeId, this.selectedAttrList).then(res => {
this.$message.success('订阅成功!')
this.$message.success(this.$t('cmdb.components.subSuccess'))
this.visible = false
this.$emit('refresh')
})
} else {
this.$confirm({
title: '警告',
content: '必须至少选择一个字段',
title: that.$t('warning'),
content: that.$t('cmdb.ci.tips4'),
})
}
},

View File

@ -75,12 +75,12 @@ export default {
return [item, !!this.fixedList.includes(item)]
})
).then((res) => {
this.$message.success('订阅成功!')
this.$message.success(this.$t('cmdb.components.subSuccess'))
this.visible = false
this.$emit('refresh')
})
} else {
this.$message.error('请至少选择一个字段!')
this.$message.error(this.$t('cmdb.ci.tips4'))
}
},
setTargetKeys(targetKeys) {

View File

@ -3,9 +3,9 @@
<Discovery :isSelected="true" :style="{ maxHeight: '75vh', overflow: 'auto' }" />
<template #footer>
<a-space>
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleOK">确认</a-button>
<a-button type="primary" @click="addPlugin">新建plugin</a-button>
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button type="primary" @click="handleOK">{{ $t('confirm') }}</a-button>
<a-button type="primary" @click="addPlugin">{{ $t('cmdb.ciType.addPlugin') }}</a-button>
</a-space>
</template>
</a-modal>
@ -55,7 +55,7 @@ export default {
await Promise.all(promises)
.then((res) => {
this.getCITypeDiscovery(res[0].id)
this.$message.success('添加成功')
this.$message.success(this.$t('addSuccess'))
})
.catch(() => {
this.getCITypeDiscovery()

Some files were not shown because too many files have changed in this diff Show More