Compare commits

...

10 Commits

Author SHA1 Message Date
jiangweidong
6ea86c7efe feat: VMware automatically syncs folders to node 2025-01-03 18:52:06 +08:00
zhaojisen
dc0a0ae868 Fixed: Change Tree Icon Position 2025-01-02 14:58:43 +08:00
Bai
5b3b8f72cd fix: system org 2024-12-26 14:44:05 +08:00
feng
df26679166 perf: Firefox unable to download file 2024-12-26 13:59:08 +08:00
zhaojisen
9efccb8ada fixed: Fixed tissue switching issues 2024-12-25 16:22:56 +08:00
zhaojisen
d784530539 Fixed: Fixed an issue with the number of cross-pages in Select All reverse 2024-12-25 15:24:51 +08:00
zhaojisen
afcc60f29c fixed: Optimize select all and deselect all functionality 2024-12-24 17:00:01 +08:00
zhaojisen
f8d581e455 Fixed: Optimize select all and deselect all functionality 2024-12-24 17:00:01 +08:00
zhaojisen
dba1540953 fixed: Fixed remarks text wrapping issue 2024-12-24 16:07:21 +08:00
Bai
456227abcf fix: ticket approve 2024-12-18 16:35:58 +08:00
11 changed files with 137 additions and 44 deletions

View File

@@ -78,7 +78,7 @@ export default {
formatterData = data
}
return (
<span>{formatterData}</span>
<span style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', lineHeight: '1.2' }}>{formatterData}</span>
)
}
if (this.value instanceof Array) {

View File

@@ -69,12 +69,48 @@ class StrategyPersistSelection extends StrategyAbstract {
* 用户切换当前页的多选
*/
onSelectAll(selection, selectable = () => true) {
const isSelected = !!selection.length
this.elDataTable.data.forEach(r => {
if (selectable(r)) {
this.toggleRowSelection(r, isSelected)
}
})
const { id, selected, data } = this.elDataTable
const selectableRows = data.filter(selectable)
// const isSelected = !!selection.length
// 创建已选择项的 id 集合,用于快速查找
const selectedIds = new Set(selected.map(r => r[id]))
const currentPageIds = new Set(selectableRows.map(row => row[id]))
// 前页面的选中状态
const currentPageSelectedCount = selectableRows.filter(row =>
selectedIds.has(row[id])
).length
// 判断是全选还是取消全选
const shouldSelectAll = currentPageSelectedCount < selectableRows.length
this.elTable.clearSelection()
if (shouldSelectAll) {
selectableRows.forEach(row => {
if (!selectedIds.has(row[id])) selected.push(row)
this.elTable.toggleRowSelection(row, true)
// ! 这里需要触发事件,否则在 el-table 中无法触发 selection-change 事件
this.elDataTable.$emit('toggle-row-selection', true, row)
})
} else {
const newSelected = []
selected.forEach(row => {
if (!currentPageIds.has(row[id])) {
newSelected.push(row)
} else {
this.elDataTable.$emit('toggle-row-selection', false, row)
}
})
this.elDataTable.selected = newSelected
}
this.elDataTable.$emit('selection-change', this.elDataTable.selected)
}
/**
* toggleRowSelection和clearSelection管理elDataTable的selected数组
@@ -83,14 +119,17 @@ class StrategyPersistSelection extends StrategyAbstract {
toggleRowSelection(row, isSelected) {
const { id, selected } = this.elDataTable
const foundIndex = selected.findIndex(r => r[id] === row[id])
if (typeof isSelected === 'undefined') {
isSelected = foundIndex <= -1
}
if (isSelected && foundIndex === -1) {
selected.push(row)
} else if (!isSelected && foundIndex > -1) {
selected.splice(foundIndex, 1)
}
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
this.updateElTableSelection()
}
@@ -103,14 +142,17 @@ class StrategyPersistSelection extends StrategyAbstract {
*/
updateElTableSelection() {
const { data, id, selected } = this.elDataTable
// 历史勾选的行已经不在当前页了所以要将当前页的行数据和selected合并
const mergeData = _.uniqWith([...data, ...selected], _.isEqual)
mergeData.forEach(r => {
const isSelected = !!selected.find(r2 => r[id] === r2[id])
if (!this.elTable) {
return
const selectedIds = new Set(selected.map(r => r[id]))
this.elTable.clearSelection()
data.forEach(row => {
const shouldBeSelected = selectedIds.has(row[id])
if (!this.elTable) return
if (shouldBeSelected) {
this.elTable.toggleRowSelection(row, true)
}
this.elTable.toggleRowSelection(r, isSelected)
})
}
}

View File

@@ -157,7 +157,7 @@ export default {
</a>`
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
const icons = `
<span style="float: right; margin-right: 10px">
<span>
${treeActions}
</span>`
if (rootNode) {

View File

@@ -74,9 +74,11 @@ export default {
},
async logout() {
const currentOrg = this.$store.getters.currentOrg
if (currentOrg.autoEnter) {
if (currentOrg.autoEnter || currentOrg.is_system) {
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
}
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
}
}

View File

@@ -71,10 +71,12 @@ const mutations = {
state.consoleOrgs = state.consoleOrgs.filter(i => i.id !== org.id)
},
SET_CURRENT_ORG(state, org) {
// 系统组织和全局组织不设置成 Pre org
if (!state.currentOrg?.autoEnter && !state.currentOrg?.is_root) {
state.preOrg = state.currentOrg
setPreOrgLocal(state.username, state.currentOrg)
// 系统组织不设置成 Pre org
const currentOrg = state.currentOrg
if (currentOrg && !currentOrg.autoEnter && !currentOrg.is_system) {
state.preOrg = currentOrg
setPreOrgLocal(state.username, currentOrg)
}
state.currentOrg = org
saveCurrentOrgLocal(state.username, org)
@@ -144,6 +146,7 @@ const actions = {
const systemOrg = {
id: orgUtil.SYSTEM_ORG_ID,
name: 'SystemSetting',
is_system: true,
autoEnter: new Date().getTime()
}
commit('SET_CURRENT_ORG', systemOrg)

View File

@@ -42,8 +42,14 @@ export function getCurrentOrgLocal(username) {
export function saveCurrentOrgLocal(username, org) {
const key = CURRENT_ORG_KEY + '_' + username
localStorage.setItem(key, JSON.stringify(org))
VueCookie.set('X-JMS-ORG', org.id)
if (org) {
localStorage.setItem(key, JSON.stringify(org))
VueCookie.set('X-JMS-ORG', org.id)
} else {
localStorage.removeItem(key)
VueCookie.delete('X-JMS-ORG')
}
}
export function setPreOrgLocal(username, org) {

View File

@@ -366,16 +366,32 @@ export function download(downloadUrl, filename) {
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
document.body.appendChild(iframe)
const a = document.createElement('a')
a.href = downloadUrl
const timeout = 1000 * 60 * 30
if (filename) {
a.download = filename
fetch(downloadUrl)
.then(response => response.blob())
.then(blob => {
const url = URL.createObjectURL(blob)
const a = iframe.contentWindow.document.createElement('a')
a.href = url
a.download = filename
iframe.contentWindow.document.body.appendChild(a)
a.click()
setTimeout(() => {
URL.revokeObjectURL(url)
document.body.removeChild(iframe)
}, timeout) // If you can't download it in half an hour, don't download it.
})
.catch(() => {
document.body.removeChild(iframe)
})
} else {
iframe.src = downloadUrl
setTimeout(() => {
document.body.removeChild(iframe)
}, timeout) // If you can't download it in half an hour, don't download it.
}
iframe.contentWindow.document.body.appendChild(a)
a.click()
setTimeout(() => {
document.body.removeChild(iframe)
}, 1000 * 60 * 30) // If you can't download it in half an hour, don't download it.
}
export function diffObject(object, base) {

View File

@@ -17,7 +17,7 @@ function getPropOrg() {
if (defaultOrg) {
return defaultOrg
}
return orgs.filter(item => !item['is_root'] && item.id !== SYSTEM_ORG_ID)[0]
return orgs.filter(item => !item['is_root'] && !item['is_system'])[0]
}
async function change2PropOrg() {

View File

@@ -8,7 +8,10 @@ import orgs from '@/api/orgs'
import { getPropView, isViewHasOrgs } from '@/utils/jms'
const whiteList = ['/login', process.env.VUE_APP_LOGIN_PATH] // no redirect whitelist
const autoEnterOrgs = ['00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000000']
const autoEnterOrgs = [
'00000000-0000-0000-0000-000000000001',
'00000000-0000-0000-0000-000000000000'
]
function reject(msg) {
return new Promise((resolve, reject) => reject(msg))
@@ -23,6 +26,19 @@ async function checkLogin({ to, from, next }) {
return await store.dispatch('users/getProfile')
} catch (e) {
Vue.$log.error(e)
// remove currentOrg: System org item
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
if (!key.startsWith('jms_current_org_')) {
continue
}
let value = localStorage.getItem(key)
value = JSON.parse(value)
if (!value.is_system) {
continue
}
localStorage.removeItem(key)
}
const status = e.response.status
if (store.getters.currentOrg.autoEnter) {
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
@@ -154,18 +170,24 @@ export async function startup({ to, from, next }) {
if (store.getters.inited) {
return true
}
await store.dispatch('app/init')
// set page title
// await getOpenPublicSetting({ to, from, next })
await getPublicSetting({ to, from, next }, true)
await checkLogin({ to, from, next })
await getPublicSetting({ to, from, next }, false)
await changeCurrentViewIfNeed({ to, from, next })
await changeCurrentOrgIfNeed({ to, from, next })
await generatePageRoutes({ to, from, next })
await checkUserFirstLogin({ to, from, next })
await store.dispatch('assets/getAssetCategories')
try {
await store.dispatch('app/init')
// set page title
// await getOpenPublicSetting({ to, from, next })
await getPublicSetting({ to, from, next }, true)
await checkLogin({ to, from, next })
await getPublicSetting({ to, from, next }, false)
await changeCurrentViewIfNeed({ to, from, next })
await changeCurrentOrgIfNeed({ to, from, next })
await generatePageRoutes({ to, from, next })
await checkUserFirstLogin({ to, from, next })
await store.dispatch('assets/getAssetCategories')
} catch (e) {
Vue.$log.error('Startup error: ', e)
}
return true
}

View File

@@ -102,7 +102,7 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
[vmware]: {
name: vmware,
title: 'VMware',
attrs: ['host', 'port', 'username', 'password']
attrs: ['host', 'port', 'username', 'password', 'auto_sync_node']
},
[nutanix]: {
name: nutanix,

View File

@@ -24,6 +24,7 @@
v-model="requestForm.accounts"
:nodes="requestForm.nodes"
:assets="requestForm.assets"
:oid="requestForm.oid"
:show-add-template="false"
style="width: 50% !important"
/>
@@ -83,6 +84,7 @@ export default {
assets: this.object.apply_assets?.map(i => i.id),
accounts: this.object.apply_accounts,
actions: this.object.apply_actions,
oid: this.object.org_id,
apply_date_expired: this.object.apply_date_expired,
apply_date_start: this.object.apply_date_start
},