Compare commits

...

7 Commits

Author SHA1 Message Date
Ewall555
d79435a96c feat: Asset permossion add account selection component and its dialog and button 2025-06-27 06:09:10 +00:00
jiangweidong
8a74ce1d4b perf: Add a config to the cloud sync task: is always update. 2025-06-26 18:36:01 +08:00
zhaojisen
6715465dda Fixed: Tree icons 2025-06-26 15:20:53 +08:00
feng
aaca037e02 perf: Applet detail table 2025-06-25 11:07:02 +08:00
feng
d1552e67d1 perf: Translate 2025-06-23 11:05:47 +08:00
w940853815
926967111a fix: Delete gather account failed 2025-06-20 13:32:00 +08:00
w940853815
0b27a01013 fix: A scroll bar appears in the asset tree on the left side of the adhoc 2025-06-19 17:53:32 +08:00
10 changed files with 360 additions and 40 deletions

View File

@@ -0,0 +1,221 @@
<template>
<Dialog
:close-on-click-modal="false"
:title="$t('AssetAccount')"
custom-class="account-select-dialog"
top="2vh"
v-bind="$attrs"
width="1000px"
@cancel="handleCancel"
@close="handleClose"
@confirm="handleConfirm"
v-on="$listeners"
>
<AssetTreeTable
ref="ListPage"
:header-actions="headerActions"
:node-url="baseNodeUrl"
:sync-select-to-url="false"
:table-config="tableConfig"
:tree-setting="iTreeSetting"
:tree-url="`${baseNodeUrl}children/tree/`"
:url="baseUrl"
class="tree-table"
v-bind="$attrs"
@loaded="handleTableLoaded"
v-on="$listeners"
/>
</Dialog>
</template>
<script>
import AssetTreeTable from '@/components/Apps/AssetTreeTable/index.vue'
import Dialog from '@/components/Dialog/index.vue'
export default {
componentName: 'AssetSelectDialog',
components: { AssetTreeTable, Dialog },
props: {
baseUrl: {
type: String,
default: '/api/v1/accounts/accounts/'
},
baseNodeUrl: {
type: String,
default: '/api/v1/assets/nodes/'
},
value: {
type: Array,
default: () => []
},
canSelect: {
type: Function,
default(row, index) {
return true
}
},
disabled: {
type: [Boolean, Function],
default: false
},
treeSetting: {
type: Object,
default: () => ({})
}
},
data() {
const vm = this
return {
isLoaded: false,
dialogVisible: false,
rowSelected: _.cloneDeep(this.value) || [],
rowsAdd: [],
tableConfig: {
url: this.baseUrl,
hasTree: true,
canSelect: this.canSelect,
columns: [
{
prop: 'name',
label: this.$t('Name'),
sortable: true
},
{
prop: 'username',
label: this.$t('Username'),
sortable: 'custom'
},
{
prop: 'asset.name',
label: this.$t('Asset'),
sortable: 'custom',
formatter: function(row) {
return row.asset.name
}
},
{
prop: 'asset.platform.name',
label: this.$t('Platform'),
sortable: true,
formatter: function(row) {
return row.asset.platform.name
}
},
{
prop: 'secret_type.label',
label: this.$t('SecretType'),
sortable: true,
formatter: function(row) {
return row.secret_type.label
}
},
{
prop: 'actions',
has: false
}
],
listeners: {
'toggle-row-selection': (isSelected, row) => {
if (isSelected) {
vm.addRowToSelect(row)
} else {
vm.removeRowFromSelect(row)
}
}
}
// theRowDefaultIsSelected: (row) => {
// return this.value.indexOf(row.id) > -1
// }
},
headerActions: {
hasLeftActions: false,
hasRightActions: false,
hasLabelSearch: true,
searchConfig: {
getUrlQuery: false
}
}
}
},
computed: {
iTreeSetting() {
return { ...this.treeSetting, selectSyncToRoute: false }
}
},
methods: {
handleTableLoaded() {
this.isLoaded = true
},
handleClose() {
this.$refs.ListPage.$refs.TreeList.componentKey += 1
},
handleConfirm() {
this.$emit('confirm', this.rowSelected, this.rowsAdd)
if (this.rowSelected.length > 0) {
this.handleClose()
}
},
handleCancel() {
this.$emit('cancel')
this.handleClose()
},
addRowToSelect(row) {
this.rowSelected.push(row.username)
this.rowsAdd.push(row)
// const selectValueIndex = this.rowSelected.indexOf(row.id)
// if (selectValueIndex === -1) {
// this.rowSelected.push(row.id)
// this.rowsAdd.push(row)
// }
},
removeRowFromSelect(row) {
const selectValueIndex = this.rowSelected.indexOf(row.username)
this.rowSelected.splice(selectValueIndex, 1)
// const selectValueIndex = this.rowSelected.indexOf(row.id)
// if (selectValueIndex > -1) {
// this.rowSelected.splice(selectValueIndex, 1)
// }
}
}
}
</script>
<style lang="scss" scoped>
.page ::v-deep .page-heading {
display: none;
}
.el-dialog__wrapper ::v-deep .el-dialog__body {
padding: 0 0 0 3px;
.tree-table {
.search {
}
.left {
padding: 5px;
}
.right {
min-height: 500px;
overflow: auto;
}
.mini {
padding-top: 8px;
}
.transition-box {
padding: 10px 5px;
}
}
}
.page ::v-deep .treebox .ztree {
}
.account-select-dialog ::v-deep .el-icon-circle-check {
display: none;
}
</style>

View File

@@ -1,25 +1,23 @@
<template>
<tr>
<td>{{ getActionTitle(action) }}</td>
<td>
<el-popover
:content="action.attrs.tip"
:disabled="!action.attrs.showTip"
placement="left-end"
trigger="hover"
>
<span slot="reference">
<component
:is="iType"
v-model="action.attrs.model"
:title="label"
v-bind="action.attrs"
v-on="callbacks"
>
{{ label }}
</component>
</span>
</el-popover>
{{ getActionTitle(action) }}
<el-tooltip v-if="action.attrs.showTip" :content="action.attrs.tip" :open-delay="500" effect="dark">
<i class="fa fa-question-circle-o" />
</el-tooltip>
</td>
<td>
<span slot="reference">
<component
:is="iType"
v-model="action.attrs.model"
:title="label"
v-bind="action.attrs"
v-on="callbacks"
>
{{ label }}
</component>
</span>
</td>
</tr>
</template>

View File

@@ -196,18 +196,23 @@ export default {
}
let title = this.title
if (!title && this.resource) {
title = this.resource
}
if (!title) {
title = this.$route.meta?.title
title = title.replace('List', '').replace('列表', '')
title = _.trimEnd(title, 's')
}
if (!title) {
title = this.$t('NoTitle')
}
let actionLabel = ''
if (action === 'clone' || action === 'create') {
actionLabel = this.$t('Create')
} else if (action === 'update') {
@@ -215,7 +220,9 @@ export default {
} else if (action === 'detail') {
actionLabel = this.$t('Detail')
}
title = actionLabel + this.$t('WordSep') + toLowerCaseExcludeAbbr(title)
return title
},
getDefaultDrawer(action) {

View File

@@ -104,8 +104,9 @@ export default {
// 使用 dialog 的高度
const dialogs = [...document.getElementsByClassName('el-dialog__body')]
if (dialogs.length > 0) {
const dialog = dialogs.find((d) => d.innerHTML.indexOf(this.iZTreeID) !== -1) || dialogs[dialogs.length - 1]
const dialog = dialogs.find((d) => d.innerHTML.indexOf(this.iZTreeID) !== -1)
if (dialog) {
// 对话框内的 zTree 才需要重新计算高度
const dialogRect = dialog.getBoundingClientRect()
tree.style.height = `${dialogRect.height - 60}px`
return
@@ -439,21 +440,21 @@ div.rMenu li {
}
.menu-item {
font-size: 12px;
padding: 0 16px;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #606266;
height: 24px;
line-height: 24px;
box-sizing: border-box;
cursor: pointer;
font-size: 12px;
padding: 0 16px;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #606266;
height: 24px;
line-height: 24px;
box-sizing: border-box;
cursor: pointer;
i {
width: 15px;
}
i {
width: 15px;
}
}
.dropdown-menu {

View File

@@ -33,8 +33,8 @@ export default {
return this.$t('PasswordExpired')
}
if (securityPasswordExpirationTime - intervalTime <= 5) {
return this.$t('PasswordWillExpiredPrefixMsg') +
(securityPasswordExpirationTime - intervalTime) +
return this.$t('PasswordWillExpiredPrefixMsg') + ' ' +
(securityPasswordExpirationTime - intervalTime) + ' ' +
this.$t('PasswordWillExpiredSuffixMsg')
}
return false

View File

@@ -50,7 +50,8 @@
.ztree li span.button, .view_icon {
&.windows_ico_docu {
&.windows_ico_docu,
&.windows_ad_ico_docu {
background: url('./icons/windows.png') no-repeat center left transparent;
}

View File

@@ -131,7 +131,7 @@ export default {
})
},
handleConfirm() {
const url = '/api/v1/accounts/gathered-accounts/'
const url = `/api/v1/accounts/gathered-accounts/${this.account.id}/`
this.$axios.delete(url, {
params: {
username: this.account.username,

View File

@@ -98,6 +98,21 @@ export default {
this.updateTaskData({ 'release_assets': val })
}.bind(this)
}
},
{
title: this.$t('IsAlwaysUpdate'),
type: 'switch',
attrs: {
showTip: true,
tip: this.$t('IsAlwaysUpdateHelpTip'),
model: this.object.task.is_always_update,
disabled: !this.hasEditPerm()
},
callbacks: {
change: function(val) {
this.updateTaskData({ 'is_always_update': val })
}.bind(this)
}
}
],
quickExecuteActions: [

View File

@@ -27,8 +27,14 @@
<el-button size="mini" type="primary" @click="showTemplateDialog=true">
{{ $t('TemplateAdd') }}
</el-button>
<span v-if="showAddAccount">
<el-button size="mini" type="primary" @click="showAccountSelectDialog=true">
{{ $t('AssetAccount') }}
</el-button>
</span>
<span class="help-block">
{{ addTemplateHelpText }}
{{ addAccountSelectHelpText }}
</span>
</span>
</div>
@@ -73,6 +79,15 @@
>
<ListTable ref="templateTable" v-bind="accountTemplateTable" />
</Dialog>
<AccountSelectDialog
v-if="showAccountSelectDialog"
:value="value"
:visible.sync="showAccountSelectDialog"
v-bind="$attrs"
@cancel="handleCancel"
@confirm="handleConfirm"
v-on="$listeners"
/>
</el-form>
</template>
@@ -92,9 +107,11 @@ import {
} from '@/views/perms/const'
import ListTable from '@/components/Table/ListTable'
import Dialog from '@/components/Dialog'
import AccountSelectDialog from '@/components/Apps/AccountSelect/dialog.vue'
export default {
components: {
AccountSelectDialog,
TagInput,
ListTable,
Dialog
@@ -129,6 +146,16 @@ export default {
default() {
return this.$t('TemplateHelpText')
}
},
addAccountSelectHelpText: {
type: String,
default() {
return this.$t('AccountSelectHelpText')
}
},
showAddAccount: {
type: Boolean,
default: true
}
},
data() {
@@ -140,6 +167,7 @@ export default {
VIRTUAL: virtual,
EXCLUDE: NotAccount,
showTemplateDialog: false,
showAccountSelectDialog: false,
realRadioSelected: this.ALL,
realChoices: realChoices,
virtualChecked: false,
@@ -280,6 +308,19 @@ export default {
this.$emit('input', choicesSelected)
this.$emit('change', choicesSelected)
},
handleConfirm(valueSelected, rowsAdd) {
if (valueSelected === undefined) { return }
const newUsernames = [...new Set(rowsAdd.map(row => row.username))]
this.specAccountsInput = [...new Set([...this.specAccountsInput, ...newUsernames])]
this.outputValue()
setTimeout(() => {
this.showAccountSelectDialog = false
this.outputValue()
}, 100)
},
handleCancel() {
this.showAccountSelectDialog = false
}
}
}
@@ -333,4 +374,33 @@ export default {
}
}
.el-dialog__wrapper ::v-deep .el-dialog__body {
padding: 0 0 0 3px;
.tree-table {
.left {
padding: 5px 0;
.ztree {
height: 100%;
}
}
.right {
.transition-box {
padding-left: 0;
}
}
.mini {
padding-top: 8px;
width: 1px;
}
.transition-box {
padding: 10px 5px;
}
}
}
</style>

View File

@@ -42,9 +42,16 @@ export default {
'applet.display_name', 'applet.version',
'date_updated', 'status', 'actions'
],
columnsShow: {
min: ['name', 'actions'],
default: [
'applet.display_name', 'status', 'actions'
]
},
columnsMeta: {
'applet.display_name': {
label: this.$t('DisplayName'),
width: '160px',
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
@@ -61,7 +68,7 @@ export default {
label: this.$t('Version')
},
status: {
label: this.$t('PublishStatus'),
label: this.$t('Status'),
formatter: (row) => {
const typeMapper = {
'pending': 'success',
@@ -77,7 +84,7 @@ export default {
label: this.$t('Date')
},
actions: {
width: '160px',
width: '138px',
formatterArgs: {
hasUpdate: false,
hasDelete: false,