mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-31 06:09:18 +00:00
Compare commits
34 Commits
v4.10.15-l
...
v5_refacto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65392f1d0c | ||
|
|
777b139fde | ||
|
|
cccd2cbb7a | ||
|
|
f75a4007f6 | ||
|
|
a9c1e06706 | ||
|
|
b66b779e79 | ||
|
|
841ba521f6 | ||
|
|
d2feaa021e | ||
|
|
c231f66e4a | ||
|
|
7fe6ab5f1b | ||
|
|
c037b52bb6 | ||
|
|
37a45cbc6e | ||
|
|
3d90d56373 | ||
|
|
587f49286f | ||
|
|
5e1b32aaf9 | ||
|
|
e1bc8245c9 | ||
|
|
6b6de2b2c5 | ||
|
|
7913979b4b | ||
|
|
16d8c7c9ac | ||
|
|
768210ef86 | ||
|
|
ce9d598683 | ||
|
|
2d75514aa1 | ||
|
|
669831c533 | ||
|
|
eb3f3ba441 | ||
|
|
46cf67e10f | ||
|
|
4e0b20b8e8 | ||
|
|
a6faad4b93 | ||
|
|
cb51ccae61 | ||
|
|
5a9d94aad0 | ||
|
|
69bdc7c0a2 | ||
|
|
2441c08da5 | ||
|
|
f9c244f006 | ||
|
|
9c5ff2b3a8 | ||
|
|
91bfb4d723 |
@@ -1,4 +1,4 @@
|
||||
FROM jumpserver/lina-base:20251105_092554 AS stage-build
|
||||
FROM jumpserver/lina-base:20251204_081759 AS stage-build
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
|
||||
@@ -59,7 +59,9 @@
|
||||
"npm": "^7.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"sortablejs": "^1.15.6",
|
||||
"uuid": "8.3.2",
|
||||
"v-sanitize": "^0.0.13",
|
||||
"vue": "2.7.16",
|
||||
"vue-codemirror": "4.0.6",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -62,7 +62,6 @@ import Dialog from '@/components/Dialog/index.vue'
|
||||
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
|
||||
import { SecretViewerFormatter } from '@/components/Table/TableFormatters'
|
||||
import { encryptPassword } from '@/utils/secure'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
@@ -112,9 +111,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
publicSettings: 'publicSettings'
|
||||
}),
|
||||
secretTypeLabel() {
|
||||
return this.account['secret_type'].label || 'Password'
|
||||
},
|
||||
@@ -150,11 +146,7 @@ export default {
|
||||
})
|
||||
},
|
||||
showSecretDialog() {
|
||||
if (!this.publicSettings.SECURITY_ACCOUNT_SECRET_READ) {
|
||||
this.$message.warning(this.$tc('AccountSecretReadDisabled'))
|
||||
return
|
||||
}
|
||||
return this.$axios.get(this.url).then((res) => {
|
||||
return this.$axios.get(this.url, { disableFlashErrorMsg: true }).then((res) => {
|
||||
this.secretInfo = res
|
||||
this.sshKeyFingerprint = res?.spec_info?.ssh_key_fingerprint || '-'
|
||||
this.showSecret = true
|
||||
@@ -175,54 +167,54 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-textarea ::v-deep .el-textarea__inner {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
.item-textarea ::v-deep .el-textarea__inner {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-right: 20px;
|
||||
line-height: 30px;
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
.el-form-item {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
::v-deep .el-form-item__content {
|
||||
line-height: 30px;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
::v-deep .el-form-item__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-right: 20px;
|
||||
line-height: 30px;
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__content {
|
||||
line-height: 30px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.title {
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.title {
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<script>
|
||||
import TreeTable from '../../Table/TreeTable/index.vue'
|
||||
import { getShowCurrentAssetValue, setRouterQuery, setUrlParam } from '@/utils/common/index'
|
||||
import { setRouterQuery, setUrlParam } from '@/utils/common/index'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
|
||||
export default {
|
||||
@@ -160,7 +160,7 @@ export default {
|
||||
return str
|
||||
},
|
||||
decorateRMenu() {
|
||||
const show_current_asset = getShowCurrentAssetValue(this.$cookie)
|
||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||
if (show_current_asset === '1') {
|
||||
$('#m_show_asset_all_children_node').css('color', '#606266')
|
||||
$('#m_show_asset_only_current_node').css('color', 'green')
|
||||
@@ -172,7 +172,6 @@ export default {
|
||||
|
||||
getAssetsUrl(treeNode) {
|
||||
let url = this.treeSetting?.url || this.url
|
||||
const showCurrentAsset = getShowCurrentAssetValue(this.$cookie)
|
||||
|
||||
const setParam = (param, value, delay) => {
|
||||
setTimeout(() => {
|
||||
@@ -184,12 +183,10 @@ export default {
|
||||
const nodeId = treeNode.meta.data.id
|
||||
setParam('node_id', nodeId)
|
||||
setParam('asset_id', '')
|
||||
setParam('show_current_asset', showCurrentAsset)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.data?.id || treeNode.id
|
||||
setParam('node_id', '')
|
||||
setParam('asset_id', assetId)
|
||||
setParam('show_current_asset', showCurrentAsset)
|
||||
} else if (treeNode.meta.type === 'category') {
|
||||
setParam('category', treeNode.meta.category)
|
||||
} else if (treeNode.meta.type === 'type') {
|
||||
|
||||
@@ -1,12 +1,60 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div v-if="hasPrompt" class="chat-action">
|
||||
<Select2
|
||||
v-model="select.value"
|
||||
:disabled="isLoading || isSelectDisabled"
|
||||
v-bind="select"
|
||||
@change="onSelectChange"
|
||||
/>
|
||||
<div class="chat-action">
|
||||
<div class="model-select">
|
||||
<Select2
|
||||
v-model="select.value"
|
||||
:disabled="isLoading || isSelectDisabled || loading || !options.length"
|
||||
v-bind="select"
|
||||
@change="onSelectChange"
|
||||
/>
|
||||
</div>
|
||||
<el-dropdown
|
||||
:hide-on-click="false"
|
||||
trigger="click"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
<i class="fa fa-plug" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<div class="menu-section">
|
||||
<div v-if="toolsLoading">
|
||||
<i class="el-icon-loading" /> {{ $t('Loading') }}
|
||||
</div>
|
||||
<div v-else class="menu-body">
|
||||
<div>
|
||||
<div
|
||||
v-for="item in toolOptions"
|
||||
:key="item.value"
|
||||
>
|
||||
<div style="padding: 0 10px">
|
||||
<i class="fa fa-wrench item-icon" />
|
||||
<span class="item-label">{{ item.label }}</span>
|
||||
|
||||
<el-switch
|
||||
:value="selectedToolsSet.has(item.value)"
|
||||
@change="() => toggleTool(item.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="item in toolServerOptions"
|
||||
:key="item.value"
|
||||
>
|
||||
<div>
|
||||
<i class="fa fa-server item-icon" />
|
||||
<span class="item-label">{{ item.label }}</span>
|
||||
<el-switch
|
||||
:value="selectedToolServersSet.has(item.value)"
|
||||
@change="() => toggleToolServer(item.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<el-input
|
||||
@@ -37,9 +85,37 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasPrompt: {
|
||||
modelOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedModel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: false
|
||||
},
|
||||
toolOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
toolServerOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedTools: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedToolServers: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
toolsLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -47,15 +123,10 @@ export default {
|
||||
isIM: false,
|
||||
inputValue: '',
|
||||
select: {
|
||||
url: '/api/v1/settings/chatai-prompts/',
|
||||
value: '',
|
||||
multiple: false,
|
||||
placeholder: this.$t('Role'),
|
||||
ajax: {
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.content }
|
||||
}
|
||||
}
|
||||
placeholder: this.$t('Model'),
|
||||
options: []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -64,7 +135,32 @@ export default {
|
||||
isLoading: state => state.chat.loading
|
||||
}),
|
||||
isSelectDisabled() {
|
||||
return !!this.select.value
|
||||
return false
|
||||
},
|
||||
options() {
|
||||
return (this.modelOptions || []).map(item => {
|
||||
return { label: item.name || item.id, value: item.id }
|
||||
})
|
||||
},
|
||||
selectedToolsSet() {
|
||||
return new Set(this.selectedTools || [])
|
||||
},
|
||||
selectedToolServersSet() {
|
||||
return new Set(this.selectedToolServers || [])
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelOptions: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.select.options = (val || []).map(item => ({ label: item.name || item.id, value: item.id }))
|
||||
}
|
||||
},
|
||||
selectedModel: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.select.value = val || ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -84,7 +180,25 @@ export default {
|
||||
this.inputValue = ''
|
||||
},
|
||||
onSelectChange(value) {
|
||||
this.$emit('select-prompt', value)
|
||||
this.$emit('select-model', value)
|
||||
},
|
||||
toggleTool(id) {
|
||||
const set = new Set(this.selectedTools || [])
|
||||
if (set.has(id)) {
|
||||
set.delete(id)
|
||||
} else {
|
||||
set.add(id)
|
||||
}
|
||||
this.$emit('select-tools', Array.from(set))
|
||||
},
|
||||
toggleToolServer(id) {
|
||||
const set = new Set(this.selectedToolServers || [])
|
||||
if (set.has(id)) {
|
||||
set.delete(id)
|
||||
} else {
|
||||
set.add(id)
|
||||
}
|
||||
this.$emit('select-tool-servers', Array.from(set))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,9 +213,18 @@ export default {
|
||||
.chat-action {
|
||||
width: 100%;
|
||||
margin: 6px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.model-select {
|
||||
flex: 0 0 48%;
|
||||
max-width: 240px;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
&::v-deep .el-select {
|
||||
width: 50%;
|
||||
width: 100%;
|
||||
|
||||
.el-input__inner {
|
||||
height: 28px;
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
<div class="chart-item-container">
|
||||
<div class="avatar">
|
||||
<el-avatar
|
||||
:src="isUserRole ? userUrl : chatUrl"
|
||||
v-if="isUserRole"
|
||||
:src="userUrl"
|
||||
class="header-avatar"
|
||||
/>
|
||||
<el-avatar v-else class="header-avatar" :style="{ backgroundColor: 'transparent' }">
|
||||
<ModelIcon :name="modelIconName" class-name="model-icon" />
|
||||
</el-avatar>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="operational">
|
||||
@@ -76,6 +80,7 @@
|
||||
|
||||
<script>
|
||||
import MessageText from './MessageText.vue'
|
||||
import ModelIcon from '../../models/ModelIcon.vue'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { copy } from '@/utils/common/index'
|
||||
import { useChat } from '../../useChat.js'
|
||||
@@ -85,7 +90,8 @@ const { setLoading, removeLoadingMessageInChat } = useChat()
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MessageText
|
||||
MessageText,
|
||||
ModelIcon
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
@@ -93,6 +99,10 @@ export default {
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
selectedModel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isTerminal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -129,10 +139,8 @@ export default {
|
||||
? this.$i18n.t('ServerBusyRetry')
|
||||
: ''
|
||||
},
|
||||
chatUrl() {
|
||||
return this.publicSettings.CHAT_AI_TYPE === 'gpt'
|
||||
? require('@/assets/img/chat.png')
|
||||
: require('@/assets/img/deepSeek.png')
|
||||
modelIconName() {
|
||||
return (this.item?.message?.model || this.selectedModel || this.publicSettings.CHAT_AI_TYPE || '').toString()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -171,11 +179,18 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
|
||||
&::v-deep img {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.model-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
text() {
|
||||
const value = this.message?.content || ''
|
||||
if (value && this.markdown) {
|
||||
return this.markdown?.render(value)
|
||||
return this.renderContentWithDetails(value)
|
||||
}
|
||||
return this.$xss.process(value)
|
||||
}
|
||||
@@ -120,6 +120,77 @@ export default {
|
||||
btn.addEventListener('click', callback)
|
||||
})
|
||||
},
|
||||
renderContentWithDetails(value) {
|
||||
// Kael responses may wrap reasoning/thinking in <details type="reasoning">; render them with a custom block.
|
||||
const detailRegex = /<details[^>]*>[\s\S]*?<\/details>/gi
|
||||
let result = ''
|
||||
let lastIndex = 0
|
||||
let match
|
||||
let hasDetails = false
|
||||
|
||||
while ((match = detailRegex.exec(value))) {
|
||||
hasDetails = true
|
||||
const preceding = value.slice(lastIndex, match.index)
|
||||
if (preceding.trim()) {
|
||||
result += this.markdown.render(preceding)
|
||||
}
|
||||
result += this.renderDetailBlock(match[0])
|
||||
lastIndex = match.index + match[0].length
|
||||
}
|
||||
|
||||
if (!hasDetails) {
|
||||
return this.markdown.render(value)
|
||||
}
|
||||
|
||||
const remaining = value.slice(lastIndex)
|
||||
if (remaining.trim()) {
|
||||
result += this.markdown.render(remaining)
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
renderDetailBlock(detailStr) {
|
||||
const attributes = this.extractAttributes(detailStr)
|
||||
const inner = detailStr.replace(/^<details[^>]*>/i, '').replace(/<\/details>$/i, '')
|
||||
const summaryMatch = inner.match(/<summary>([\s\S]*?)<\/summary>/i)
|
||||
const summary = summaryMatch ? this.decodeHtml(summaryMatch[1]) : ''
|
||||
const body = summaryMatch ? inner.replace(summaryMatch[0], '') : inner
|
||||
const bodyHtml = body.trim() ? this.markdown.render(this.decodeHtml(body.trim())) : ''
|
||||
|
||||
const baseClass = 'kael-detail'
|
||||
if (attributes.type === 'reasoning') {
|
||||
const statusClass = attributes.done === 'true' ? 'is-done' : 'is-pending'
|
||||
const title = summary || this.$t('DeeplyThoughtAbout')
|
||||
return `<div class="${baseClass} ${baseClass}--reasoning ${statusClass}">
|
||||
<div class="${baseClass}__header">
|
||||
<span class="${baseClass}__status-dot"></span>
|
||||
<span class="${baseClass}__title">${title}</span>
|
||||
</div>
|
||||
${bodyHtml ? `<div class="${baseClass}__body">${bodyHtml}</div>` : ''}
|
||||
</div>`
|
||||
}
|
||||
|
||||
return `<div class="${baseClass}">
|
||||
${summary ? `<div class="${baseClass}__header">${summary}</div>` : ''}
|
||||
${bodyHtml ? `<div class="${baseClass}__body">${bodyHtml}</div>` : ''}
|
||||
</div>`
|
||||
},
|
||||
extractAttributes(detailStr) {
|
||||
const attrs = {}
|
||||
const attrMatch = detailStr.match(/^<details([^>]*)>/i)
|
||||
const attrStr = (attrMatch && attrMatch[1]) || ''
|
||||
attrStr.replace(/(\w+)="(.*?)"/g, (all, key, val) => {
|
||||
attrs[key] = val
|
||||
return all
|
||||
})
|
||||
return attrs
|
||||
},
|
||||
decodeHtml(str) {
|
||||
if (!str) return ''
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.innerHTML = str
|
||||
return textArea.value
|
||||
},
|
||||
removeBtnClickEvent(selector) {
|
||||
const buttons = this.$refs.textRef.querySelectorAll(selector)
|
||||
buttons.forEach((btn) => {
|
||||
@@ -258,4 +329,64 @@ export default {
|
||||
.loading-box span:nth-child(3) {
|
||||
animation-delay: 0.49s;
|
||||
}
|
||||
|
||||
.kael-detail {
|
||||
margin: 8px 0;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e5e5;
|
||||
background: #f7f8fa;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__status-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-top: 6px;
|
||||
padding-left: 8px;
|
||||
border-left: 2px solid #e5e5e5;
|
||||
}
|
||||
|
||||
&--reasoning.is-pending {
|
||||
border-color: #f59e0b40;
|
||||
background: #fff8e6;
|
||||
|
||||
.kael-detail__status-dot {
|
||||
background: #f59e0b;
|
||||
animation: kael-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&--reasoning.is-done {
|
||||
border-color: #dbeafe;
|
||||
background: #f4f6ff;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kael-pulse {
|
||||
0% {
|
||||
opacity: 0.45;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.45;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,6 @@
|
||||
import Sidebar from './components/Sidebar/index.vue'
|
||||
import Chat from './components/ChitChat/index.vue'
|
||||
import { getInputFocus } from './useChat.js'
|
||||
import { ws } from '@/utils/request'
|
||||
import DrawerPanel from '@/components/Apps/DrawerPanel/index.vue'
|
||||
import { ObjectLocalStorage } from '@/utils/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
@@ -82,7 +81,8 @@ export default {
|
||||
height: '400px',
|
||||
expanded: false,
|
||||
clientOffset: {},
|
||||
currentTerminalContent: {}
|
||||
currentTerminalContent: {},
|
||||
initialized: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -123,11 +123,18 @@ export default {
|
||||
document.body.appendChild(script)
|
||||
}
|
||||
},
|
||||
initAssistant() {
|
||||
if (this.initialized) return
|
||||
this.initialized = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.component?.init()
|
||||
})
|
||||
},
|
||||
handlePostMessage() {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data === 'show-chat-panel') {
|
||||
this.$refs.drawer.show = true
|
||||
this.initWebSocket()
|
||||
this.initAssistant()
|
||||
return
|
||||
}
|
||||
const msg = event.data
|
||||
@@ -152,11 +159,6 @@ export default {
|
||||
}
|
||||
this.$refs.drawer.handleHeaderMoveUp(event)
|
||||
},
|
||||
initWebSocket() {
|
||||
if (!ws) {
|
||||
this.$refs.component?.init()
|
||||
}
|
||||
},
|
||||
onClose() {
|
||||
this.$refs.drawer.show = false
|
||||
},
|
||||
@@ -170,7 +172,6 @@ export default {
|
||||
},
|
||||
save_pannel_settings() {
|
||||
aiPannelLocalStorage.set('expanded', this.expanded)
|
||||
console.log('AI panel settings saved:', this.expanded)
|
||||
},
|
||||
updateExpandedState(expanded) {
|
||||
this.expanded = expanded
|
||||
@@ -184,8 +185,8 @@ export default {
|
||||
})
|
||||
},
|
||||
onToggle(status) {
|
||||
this.initWebSocket()
|
||||
if (status) {
|
||||
this.initAssistant()
|
||||
getInputFocus()
|
||||
}
|
||||
}
|
||||
|
||||
33
src/components/Apps/ChatAi/models/ChatGPT.vue
Normal file
33
src/components/Apps/ChatAi/models/ChatGPT.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<svg
|
||||
:stroke-width="strokeWidth"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:class="className"
|
||||
>
|
||||
<path
|
||||
fill="#6b7280"
|
||||
style="fill: #6b7280 !important"
|
||||
d="M11.2475 18.25C10.6975 18.25 10.175 18.1455 9.67999 17.9365C9.18499 17.7275 8.74499 17.436 8.35999 17.062C7.94199 17.205 7.50749 17.2765 7.05649 17.2765C6.31949 17.2765 5.63749 17.095 5.01049 16.732C4.38349 16.369 3.87749 15.874 3.49249 15.247C3.11849 14.62 2.93149 13.9215 2.93149 13.1515C2.93149 12.8325 2.97549 12.486 3.06349 12.112C2.62349 11.705 2.28249 11.2375 2.04049 10.7095C1.79849 10.1705 1.67749 9.6095 1.67749 9.0265C1.67749 8.4325 1.80399 7.8605 2.05699 7.3105C2.30999 6.7605 2.66199 6.2875 3.11299 5.8915C3.57499 5.4845 4.10849 5.204 4.71349 5.05C4.83449 4.423 5.08749 3.862 5.47249 3.367C5.86849 2.861 6.35249 2.465 6.92449 2.179C7.49649 1.893 8.10699 1.75 8.75599 1.75C9.30599 1.75 9.82849 1.8545 10.3235 2.0635C10.8185 2.2725 11.2585 2.564 11.6435 2.938C12.0615 2.795 12.496 2.7235 12.947 2.7235C13.684 2.7235 14.366 2.905 14.993 3.268C15.62 3.631 16.1205 4.126 16.4945 4.753C16.8795 5.38 17.072 6.0785 17.072 6.8485C17.072 7.1675 17.028 7.514 16.94 7.888C17.38 8.295 17.721 8.768 17.963 9.307C18.205 9.835 18.326 10.3905 18.326 10.9735C18.326 11.5675 18.1995 12.1395 17.9465 12.6895C17.6935 13.2395 17.336 13.718 16.874 14.125C16.423 14.521 15.895 14.796 15.29 14.95C15.169 15.577 14.9105 16.138 14.5145 16.633C14.1295 17.139 13.651 17.535 13.079 17.821C12.507 18.107 11.8965 18.25 11.2475 18.25ZM7.17199 16.1875C7.72199 16.1875 8.20049 16.072 8.60749 15.841L11.7095 14.059C11.8195 13.982 11.8745 13.8775 11.8745 13.7455V12.3265L7.88149 14.62C7.63949 14.763 7.39749 14.763 7.15549 14.62L4.03699 12.8215C4.03699 12.8545 4.03149 12.893 4.02049 12.937C4.02049 12.981 4.02049 13.047 4.02049 13.135C4.02049 13.696 4.15249 14.213 4.41649 14.686C4.69149 15.148 5.07099 15.511 5.55499 15.775C6.03899 16.05 6.57799 16.1875 7.17199 16.1875ZM7.33699 13.498C7.40299 13.531 7.46349 13.5475 7.51849 13.5475C7.57349 13.5475 7.62849 13.531 7.68349 13.498L8.92099 12.7885L4.94449 10.4785C4.70249 10.3355 4.58149 10.121 4.58149 9.835V6.2545C4.03149 6.4965 3.59149 6.8705 3.26149 7.3765C2.93149 7.8715 2.76649 8.4215 2.76649 9.0265C2.76649 9.5655 2.90399 10.0825 3.17899 10.5775C3.45399 11.0725 3.81149 11.4465 4.25149 11.6995L7.33699 13.498ZM11.2475 17.161C11.8305 17.161 12.3585 17.029 12.8315 16.765C13.3045 16.501 13.6785 16.138 13.9535 15.676C14.2285 15.214 14.366 14.697 14.366 14.125V10.561C14.366 10.429 14.311 10.33 14.201 10.264L12.947 9.538V14.1415C12.947 14.4275 12.826 14.642 12.584 14.785L9.46549 16.5835C10.0045 16.9685 10.5985 17.161 11.2475 17.161ZM11.8745 11.122V8.878L10.01 7.822L8.12899 8.878V11.122L10.01 12.178L11.8745 11.122ZM7.05649 5.8585C7.05649 5.5725 7.17749 5.358 7.41949 5.215L10.538 3.4165C9.99899 3.0315 9.40499 2.839 8.75599 2.839C8.17299 2.839 7.64499 2.971 7.17199 3.235C6.69899 3.499 6.32499 3.862 6.04999 4.324C5.78599 4.786 5.65399 5.303 5.65399 5.875V9.4225C5.65399 9.5545 5.70899 9.659 5.81899 9.736L7.05649 10.462V5.8585ZM15.4385 13.7455C15.9885 13.5035 16.423 13.1295 16.742 12.6235C17.072 12.1175 17.237 11.5675 17.237 10.9735C17.237 10.4345 17.0995 9.9175 16.8245 9.4225C16.5495 8.9275 16.192 8.5535 15.752 8.3005L12.6665 6.5185C12.6005 6.4745 12.54 6.458 12.485 6.469C12.43 6.469 12.375 6.4855 12.32 6.5185L11.0825 7.2115L15.0755 9.538C15.1965 9.604 15.2845 9.692 15.3395 9.802C15.4055 9.901 15.4385 10.022 15.4385 10.165V13.7455ZM12.122 5.3635C12.364 5.2095 12.606 5.2095 12.848 5.3635L15.983 7.195C15.983 7.118 15.983 7.019 15.983 6.898C15.983 6.37 15.851 5.8695 15.587 5.3965C15.334 4.9125 14.9655 4.5275 14.4815 4.2415C14.0085 3.9555 13.4585 3.8125 12.8315 3.8125C12.2815 3.8125 11.803 3.928 11.396 4.159L8.29399 5.941C8.18399 6.018 8.12899 6.1225 8.12899 6.2545V7.6735L12.122 5.3635Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ChatGPTIcon',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-8'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
45
src/components/Apps/ChatAi/models/Claude.vue
Normal file
45
src/components/Apps/ChatAi/models/Claude.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<svg
|
||||
:stroke-width="strokeWidth"
|
||||
viewBox="0 0 24 16"
|
||||
overflow="visible"
|
||||
width="20"
|
||||
height="20"
|
||||
:class="className"
|
||||
>
|
||||
<g style="transform: translateX(13px) rotateZ(0deg); transform-origin: 4.775px 7.73501px;">
|
||||
<path
|
||||
shape-rendering="geometricPrecision"
|
||||
fill-opacity="1"
|
||||
fill="#8c653f"
|
||||
style="fill: #8c653f !important"
|
||||
d=" M0,0 C0,0 6.1677093505859375,15.470022201538086 6.1677093505859375,15.470022201538086 C6.1677093505859375,15.470022201538086 9.550004005432129,15.470022201538086 9.550004005432129,15.470022201538086 C9.550004005432129,15.470022201538086 3.382294178009033,0 3.382294178009033,0 C3.382294178009033,0 0,0 0,0 C0,0 0,0 0,0z"
|
||||
/>
|
||||
</g>
|
||||
<g opacity="1" style="transform: none; transform-origin: 7.935px 7.73501px;">
|
||||
<path
|
||||
shape-rendering="geometricPrecision"
|
||||
fill-opacity="1"
|
||||
fill="#8c653f"
|
||||
style="fill: #8c653f !important"
|
||||
d=" M5.824605464935303,9.348296165466309 C5.824605464935303,9.348296165466309 7.93500280380249,3.911694288253784 7.93500280380249,3.911694288253784 C7.93500280380249,3.911694288253784 10.045400619506836,9.348296165466309 10.045400619506836,9.348296165466309 C10.045400619506836,9.348296165466309 5.824605464935303,9.348296165466309 5.824605464935303,9.348296165466309 C5.824605464935303,9.348296165466309 5.824605464935303,9.348296165466309 5.824605464935303,9.348296165466309z M6.166755199432373,0 C6.166755199432373,0 0,15.470022201538086 0,15.470022201538086 C0,15.470022201538086 3.4480772018432617,15.470022201538086 3.4480772018432617,15.470022201538086 C3.4480772018432617,15.470022201538086 4.709278583526611,12.22130012512207 4.709278583526611,12.22130012512207 C4.709278583526611,12.22130012512207 11.16093635559082,12.22130012512207 11.16093635559082,12.22130012512207 C11.16093635559082,12.22130012512207 12.421928405761719,15.470022201538086 12.421928405761719,15.470022201538086 C12.421928405761719,15.470022201538086 15.87000560760498,15.470022201538086 15.87000560760498,15.470022201538086 C15.87000560760498,15.470022201538086 9.703250885009766,0 9.703250885009766,0 C9.703250885009766,0 6.166755199432373,0 6.166755199432373,0 C6.166755199432373,0 6.166755199432373,0 6.166755199432373,0z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ClaudeIcon',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-4'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
34
src/components/Apps/ChatAi/models/DeepSeek.vue
Normal file
34
src/components/Apps/ChatAi/models/DeepSeek.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<svg
|
||||
id="图层_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-name="图层 1"
|
||||
viewBox="0 0 71.69 52.76"
|
||||
:class="className"
|
||||
:stroke-width="strokeWidth"
|
||||
>
|
||||
<path
|
||||
id="path"
|
||||
fill="#4d6bfe"
|
||||
style="fill: #4d6bfe !important"
|
||||
d="M523.77,276.34c-.76-.38-1.08.33-1.53.69a4,4,0,0,0-.41.41,5.07,5.07,0,0,1-4.1,1.87,8,8,0,0,0-6.46,2.53,5.82,5.82,0,0,0-3.72-4.62,6.39,6.39,0,0,1-2.85-1.94,7.76,7.76,0,0,1-.92-2.31c-.16-.48-.32-1-.87-1.05s-.83.41-1.07.82a11,11,0,0,0-1.26,5.5,11.9,11.9,0,0,0,5.49,10.14.75.75,0,0,1,.39,1c-.25.84-.54,1.65-.79,2.49-.17.53-.41.65-1,.42a16.63,16.63,0,0,1-5.18-3.52c-2.56-2.48-4.88-5.21-7.76-7.35-.68-.5-1.36-1-2.06-1.41-2.94-2.86.39-5.2,1.16-5.48s.28-1.29-2.33-1.28-5,.88-8,2a8.23,8.23,0,0,1-1.39.41,28.67,28.67,0,0,0-8.61-.3,18.57,18.57,0,0,0-13.44,7.83c-4,5.47-4.91,11.67-3.76,18.15a27.68,27.68,0,0,0,10,16.88,26.8,26.8,0,0,0,19.23,6.39c4.43-.25,9.36-.84,14.92-5.55a13.84,13.84,0,0,0,5.32,1.18,17.24,17.24,0,0,0,5.09-.38c2.2-.46,2.05-2.5,1.25-2.87-6.43-3-5-1.78-6.3-2.77,3.27-3.87,8.2-7.89,10.13-20.92a12.44,12.44,0,0,0,0-2.52c0-.51.1-.71.68-.76a12.55,12.55,0,0,0,4.62-1.42c4.17-2.28,5.85-6,6.25-10.51A1.57,1.57,0,0,0,523.77,276.34Zm-36.34,40.37c-6.24-4.9-9.27-6.52-10.52-6.45s-1,1.41-.7,2.28a8.49,8.49,0,0,0,1.11,2.21,1.14,1.14,0,0,1-.34,1.8c-2,1.24-5.5-.42-5.66-.5a26.08,26.08,0,0,1-9.87-9.88,30.15,30.15,0,0,1-3.87-13.39c-.06-1.15.28-1.56,1.42-1.77a14.31,14.31,0,0,1,4.57-.11,28.56,28.56,0,0,1,16.33,8.29,54.06,54.06,0,0,1,6.58,8.63,41.46,41.46,0,0,0,7.41,8.71,24.36,24.36,0,0,0,2.66,2C494.16,318.82,490.16,318.87,487.43,316.71Zm3-19.23a.92.92,0,0,1,.92-.92.83.83,0,0,1,.32.06.8.8,0,0,1,.34.22.9.9,0,0,1,.25.64.92.92,0,0,1-1.83,0Zm9.29,4.76a5.27,5.27,0,0,1-1.77.48,3.75,3.75,0,0,1-2.38-.76,3.57,3.57,0,0,1-1.65-2.26,5.16,5.16,0,0,1,0-1.76,2,2,0,0,0-.71-2.17,3.1,3.1,0,0,0-2.06-.59,1.63,1.63,0,0,1-.76-.24.75.75,0,0,1-.34-1.07,3.47,3.47,0,0,1,.57-.62,3.9,3.9,0,0,1,3.43,0,10,10,0,0,1,3,2.34,18.62,18.62,0,0,1,2,2.73,10.9,10.9,0,0,1,1.33,2.53C500.65,301.47,500.4,302,499.71,302.24Z"
|
||||
transform="translate(-452.83 -271.91)"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DeepSeekIcon',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-4'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
33
src/components/Apps/ChatAi/models/Gemini.vue
Normal file
33
src/components/Apps/ChatAi/models/Gemini.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 512 512"
|
||||
:class="className"
|
||||
:stroke-width="strokeWidth"
|
||||
>
|
||||
<path
|
||||
fill="#4285f4"
|
||||
style="fill: #4285f4 !important"
|
||||
d="M473.16,221.48l-2.26-9.59H262.46v88.22H387c-12.93,61.4-72.93,93.72-121.94,93.72-35.66,0-73.25-15-98.13-39.11a140.08,140.08,0,0,1-41.8-98.88c0-37.16,16.7-74.33,41-98.78s61-38.13,97.49-38.13c41.79,0,71.74,22.19,82.94,32.31l62.69-62.36C390.86,72.72,340.34,32,261.6,32h0c-60.75,0-119,23.27-161.58,65.71C58,139.5,36.25,199.93,36.25,256S56.83,369.48,97.55,411.6C141.06,456.52,202.68,480,266.13,480c57.73,0,112.45-22.62,151.45-63.66,38.34-40.4,58.17-96.3,58.17-154.9C475.75,236.77,473.27,222.12,473.16,221.48Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GeminiIcon',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-4'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
33
src/components/Apps/ChatAi/models/Grok.vue
Normal file
33
src/components/Apps/ChatAi/models/Grok.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
fill="currentColor"
|
||||
:class="className"
|
||||
:stroke-width="strokeWidth"
|
||||
>
|
||||
<path
|
||||
fill="#000000"
|
||||
style="fill: #000000 !important"
|
||||
d="m3.005 8.858 8.783 12.544h3.904L6.908 8.858zM6.905 15.825 3 21.402h3.907l1.951-2.788zM16.585 2l-6.75 9.64 1.953 2.79L20.492 2zM17.292 7.965v13.437h3.2V3.395z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GrokIcon',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-4'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
50
src/components/Apps/ChatAi/models/ModelIcon.vue
Normal file
50
src/components/Apps/ChatAi/models/ModelIcon.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<component
|
||||
:is="resolvedIcon"
|
||||
v-if="resolvedIcon"
|
||||
:class-name="className"
|
||||
:stroke-width="strokeWidth"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ChatGPTIcon from './ChatGPT.vue'
|
||||
import DeepSeekIcon from './DeepSeek.vue'
|
||||
import GrokIcon from './Grok.vue'
|
||||
import ClaudeIcon from './Claude.vue'
|
||||
import GeminiIcon from './Gemini.vue'
|
||||
|
||||
export default {
|
||||
name: 'ModelIcon',
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: 'size-5'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: [String, Number],
|
||||
default: '1.5'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
normalizedName() {
|
||||
return (this.name || '').toLowerCase()
|
||||
},
|
||||
resolvedIcon() {
|
||||
const name = this.normalizedName
|
||||
if (!name) return null
|
||||
if (name.includes('gpt')) return ChatGPTIcon
|
||||
if (name.includes('deep-seek')) return DeepSeekIcon
|
||||
if (name.includes('deepseek')) return DeepSeekIcon
|
||||
if (name.includes('grok')) return GrokIcon
|
||||
if (name.includes('claude')) return ClaudeIcon
|
||||
if (name.includes('gemini')) return GeminiIcon
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -11,6 +11,7 @@
|
||||
import AssetTreeTable from '@/components/Apps/AssetTreeTable'
|
||||
import { AccountInfoFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
|
||||
import { setUrlParam } from '@/utils/common/index'
|
||||
|
||||
export default {
|
||||
name: 'GrantedAssets',
|
||||
@@ -34,7 +35,7 @@ export default {
|
||||
}
|
||||
const initialUrl = vm.tableConfig.initialUrl
|
||||
const nodeId = node.meta.data.id
|
||||
const url = initialUrl.replace('/assets/', `/nodes/${nodeId}/assets/`)
|
||||
const url = setUrlParam(initialUrl, 'node_id', nodeId)
|
||||
vm.tableConfig.url = url
|
||||
}
|
||||
},
|
||||
@@ -70,7 +71,7 @@ export default {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
showSearch: false,
|
||||
showSearch: true,
|
||||
url: this.tableUrl,
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: this.treeUrl,
|
||||
@@ -78,6 +79,9 @@ export default {
|
||||
callback: {
|
||||
onSelected: (event, node) => vm.onSelected(node, vm),
|
||||
refresh: vm.refreshObjectAssetPermission
|
||||
},
|
||||
async: {
|
||||
enable: false
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
|
||||
@@ -149,7 +149,7 @@ export default {
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '25%'
|
||||
default: '18.2%'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
<div class="panel-title">
|
||||
<el-avatar :src="imageUrl" shape="square" />
|
||||
<div class="title-display">
|
||||
<p class="name" :title="object.name">{{ object.name }}</p>
|
||||
<span class="name">{{ object.name }}</span>
|
||||
<span class="comment">{{ object.provider.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="iActions.length !== 0" class="panel-actions" @click="handleClick($event)">
|
||||
<div
|
||||
v-if="iActions.length !== 0"
|
||||
class="panel-actions"
|
||||
@click="handleClick($event)"
|
||||
>
|
||||
<el-dropdown>
|
||||
<el-button size="mini">
|
||||
<i class="el-icon-more el-icon--right" />
|
||||
@@ -60,19 +64,21 @@ export default {
|
||||
},
|
||||
getImage: {
|
||||
type: Function,
|
||||
default: obj => ''
|
||||
default: (obj) => ''
|
||||
},
|
||||
getInfos: {
|
||||
type: Function,
|
||||
default: obj => []
|
||||
default: (obj) => []
|
||||
},
|
||||
handleUpdate: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
onView: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -146,13 +152,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
div.info-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
gap: unset;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
height: initial !important;
|
||||
|
||||
.panel-header {
|
||||
padding: 10px 20px;
|
||||
@@ -169,23 +175,12 @@ div.info-panel {
|
||||
|
||||
.title-display {
|
||||
display: flex;
|
||||
flex-basis: 225px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
max-width: 225px;
|
||||
min-width: 0;
|
||||
overflow-x: hidden;
|
||||
text-align: left;
|
||||
|
||||
.name {
|
||||
font-size: 1.1em;
|
||||
color: #555555;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
margin: unset;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.comment {
|
||||
@@ -198,7 +193,6 @@ div.info-panel {
|
||||
::v-deep {
|
||||
.el-avatar {
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export default {
|
||||
{
|
||||
name: 'actionFilter',
|
||||
icon: 'filter',
|
||||
tip: this.$t('QuickFilter'),
|
||||
tip: this.$t('Filter'),
|
||||
has: this.hasQuickFilter,
|
||||
callback: this.handleFilterClick.bind(this)
|
||||
},
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
<script>
|
||||
import { copy, downloadText } from '@/utils/common/index'
|
||||
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'SecretViewerFormatter',
|
||||
@@ -70,9 +69,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
publicSettings: 'publicSettings'
|
||||
}),
|
||||
hasShow: function() {
|
||||
return this.formatterArgs.hasShow
|
||||
},
|
||||
@@ -144,10 +140,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async getAccountSecret() {
|
||||
if (!this.publicSettings.SECURITY_ACCOUNT_SECRET_READ) {
|
||||
this.$message.warning(this.$tc('AccountSecretReadDisabled'))
|
||||
return
|
||||
}
|
||||
if (this.formatterArgs.secretFrom === 'cellValue' || this.getIt) {
|
||||
return
|
||||
}
|
||||
@@ -186,48 +178,48 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
//white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 13px;
|
||||
|
||||
.text {
|
||||
flex: 1;
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
.content {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
//white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.action {
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
margin-left: 1px;
|
||||
display: inline;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
.text {
|
||||
flex: 1;
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
.action {
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
margin-left: 1px;
|
||||
display: inline;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
&.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-input ::v-deep input {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
height: 30px;
|
||||
}
|
||||
.edit-input ::v-deep input {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -243,12 +243,10 @@ export default {
|
||||
delete routeFilter.search
|
||||
}
|
||||
const asFilterTags = _.cloneDeep(this.filterTags)
|
||||
setTimeout(() => {
|
||||
this.filterTags = {
|
||||
...asFilterTags,
|
||||
...routeFilter
|
||||
}
|
||||
}, 100)
|
||||
this.filterTags = {
|
||||
...asFilterTags,
|
||||
...routeFilter
|
||||
}
|
||||
},
|
||||
getValueLabel(key, value) {
|
||||
for (const field of this.options) {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
import DataZTree from '../DataZTree/index.vue'
|
||||
import Icon from '@/components/Widgets/Icon'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
import { getShowCurrentAssetValue } from '@/utils/common/index'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
@@ -200,7 +199,7 @@ export default {
|
||||
},
|
||||
// Request URL: http://localhost/api/v1/assets/assets/?node_id=ID&show_current_asset=null&draw=2&limit=15&offset=0&_=1587022917769
|
||||
onSelected: function(event, treeNode) {
|
||||
const show_current_asset = getShowCurrentAssetValue(this.$cookie)
|
||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||
if (!this.setting.url) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,16 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="treebox">
|
||||
<div v-if="treeSetting.showSearch">
|
||||
<div v-if="treeSetting.showSearch" @click="focusTreeSearchInput">
|
||||
<el-input
|
||||
v-show="showTreeSearch"
|
||||
ref="treeSearchInput"
|
||||
v-model="treeSearchValue"
|
||||
:placeholder="$tc('Search')"
|
||||
class="fixed-tree-search"
|
||||
prefix-icon="fa fa-search"
|
||||
:placeholder="treeSearchInputPlaceholder"
|
||||
size="mini"
|
||||
@input="treeSearchHandle"
|
||||
>
|
||||
<template #prepend>
|
||||
|
||||
<template v-if="!isSearchTypeDropdownEnabled">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="currentTreeSearchTypeTooltip"
|
||||
:open-delay="300"
|
||||
>
|
||||
<span style="cursor: pointer;" @click.stop="focusTreeSearchInput">
|
||||
<i class="fa fa-search" />
|
||||
<span class="search-label">{{ treeSearchTypeLabel }}</span>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-dropdown trigger="hover" @command="onSearchTypeChange">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="currentTreeSearchTypeTooltip"
|
||||
:open-delay="1000"
|
||||
>
|
||||
<span @click.stop="focusTreeSearchInput">
|
||||
<i class="fa fa-search" />
|
||||
<span class="search-label">{{ treeSearchTypeLabel }}</span>
|
||||
<i class="el-icon-arrow-down" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="(item, type) in treeSearchTypeOptions"
|
||||
:key="type"
|
||||
:command="type"
|
||||
:class="{ 'is-active': treeSearchType === type }"
|
||||
>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
placement="right"
|
||||
:content="item.tooltip"
|
||||
:open-delay="300"
|
||||
>
|
||||
<span>{{ item.label }}</span>
|
||||
</el-tooltip>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</template>
|
||||
<span slot="suffix">
|
||||
<i
|
||||
class="el-icon-close"
|
||||
@@ -47,6 +98,7 @@ import '@ztree/ztree_v3/js/jquery.ztree.exhide.min.js'
|
||||
import '@/styles/ztree.css'
|
||||
import '@/styles/ztree_icon.scss'
|
||||
import axiosRetry from 'axios-retry'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
|
||||
const defaultObject = {
|
||||
type: Object,
|
||||
@@ -66,18 +118,52 @@ export default {
|
||||
rMenu: '',
|
||||
init: false,
|
||||
loading: false,
|
||||
showTreeSearch: false,
|
||||
treeSearchValue: ''
|
||||
showTreeSearch: true,
|
||||
treeSearchValue: '',
|
||||
treeSearchType: 'asset',
|
||||
treeSearchTypeOptions: {},
|
||||
treeSearchTypeSupportOptions: {
|
||||
node: {
|
||||
label: this.$t('Node'),
|
||||
placeholder: this.$t('Search node'),
|
||||
tooltip: this.$t('Search by node name'),
|
||||
search_key: 'search_node'
|
||||
},
|
||||
asset: {
|
||||
label: this.$t('Asset'),
|
||||
placeholder: this.$t('Search asset'),
|
||||
tooltip: this.$t('Search by asset name or address'),
|
||||
search_key: 'search_asset'
|
||||
}
|
||||
},
|
||||
treeType: '' // asset | node
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
treeSetting() {
|
||||
return this.setting
|
||||
},
|
||||
isSearchTypeDropdownEnabled() {
|
||||
return Object.keys(this.treeSearchTypeOptions).length > 1
|
||||
},
|
||||
currentTreeSearchType() {
|
||||
return this.treeSearchTypeOptions[this.treeSearchType]
|
||||
},
|
||||
currentTreeSearchTypeTooltip() {
|
||||
return this.currentTreeSearchType?.tooltip || ''
|
||||
},
|
||||
treeSearchTypeLabel() {
|
||||
return this.currentTreeSearchType?.label || ''
|
||||
},
|
||||
treeSearchInputPlaceholder() {
|
||||
return this.currentTreeSearchType?.placeholder || this.$t('Search')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.refresh = this.refresh
|
||||
window.onSearch = this.onSearch
|
||||
this.initTreeType()
|
||||
this.initTreeSearchTypeOptions()
|
||||
this.initTree().then(() => {
|
||||
this.$nextTick(() => {
|
||||
this.updateTreeHeight()
|
||||
@@ -90,6 +176,36 @@ export default {
|
||||
window.removeEventListener('resize', this.updateTreeHeight)
|
||||
},
|
||||
methods: {
|
||||
initTreeType() {
|
||||
let treeType = this.treeSetting.treeType
|
||||
if (!treeType) {
|
||||
treeType = this.treeSetting.async?.enable ? 'asset' : 'node'
|
||||
}
|
||||
this.treeType = treeType
|
||||
},
|
||||
initTreeSearchTypeOptions() {
|
||||
if (this.treeType === 'asset') {
|
||||
// 资产树支持异步搜索节点和资产
|
||||
this.treeSearchTypeOptions = this.treeSearchTypeSupportOptions
|
||||
// 默认搜索资产
|
||||
this.treeSearchType = 'asset'
|
||||
} else {
|
||||
// 节点树只支持搜索节点
|
||||
this.treeSearchTypeOptions = Object.fromEntries(
|
||||
Object.entries(this.treeSearchTypeSupportOptions)
|
||||
.filter(([key]) => key === 'node')
|
||||
)
|
||||
// 默认搜索节点
|
||||
this.treeSearchType = 'node'
|
||||
}
|
||||
},
|
||||
onSearchTypeChange(type) {
|
||||
this.treeSearchType = type
|
||||
this.focusTreeSearchInput()
|
||||
},
|
||||
focusTreeSearchInput() {
|
||||
this.$refs.treeSearchInput.focus()
|
||||
},
|
||||
onMenuClick(menu) {
|
||||
if (menu.disabled) {
|
||||
return
|
||||
@@ -116,19 +232,14 @@ export default {
|
||||
const zTreeRect = tree.getBoundingClientRect()
|
||||
tree.style.height = `calc(100vh - ${zTreeRect.top}px - 30px - 25px)`
|
||||
}, 100),
|
||||
async initTree(refresh = false) {
|
||||
async initTree(refresh = false, iTreeUrl = '') {
|
||||
const vm = this
|
||||
let treeUrl
|
||||
this.loading = true
|
||||
if (refresh && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 &&
|
||||
this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1
|
||||
) {
|
||||
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1)
|
||||
? `${this.treeSetting.treeUrl}?rebuild_tree=1`
|
||||
: `${this.treeSetting.treeUrl}&rebuild_tree=1`
|
||||
} else {
|
||||
let treeUrl = iTreeUrl
|
||||
if (!treeUrl) {
|
||||
treeUrl = this.treeSetting.treeUrl
|
||||
}
|
||||
treeUrl = setUrlParam(treeUrl, 'tree_type', this.treeType)
|
||||
|
||||
if (refresh) {
|
||||
$.fn.zTree.destroy(this.iZTreeID)
|
||||
@@ -221,25 +332,31 @@ export default {
|
||||
searchInput.oninput = e => this.treeSearchHandle((e.target.value || ''))
|
||||
},
|
||||
treeSearchHandle: _.debounce(function(value) {
|
||||
if (this.treeSetting.async.enable) {
|
||||
this.filterAssetsServer(value)
|
||||
if (this.treeSetting.async?.enable) {
|
||||
this.searchFromServer(value)
|
||||
} else {
|
||||
this.filterTree(value)
|
||||
this.searchFromLocal(value)
|
||||
}
|
||||
}, 600),
|
||||
getCheckedNodes: function() {
|
||||
return this.zTree.getCheckedNodes(true)
|
||||
},
|
||||
|
||||
recurseParent(node) {
|
||||
const parentNode = node.getParentNode()
|
||||
if (parentNode && parentNode.pId) {
|
||||
return [parentNode, ...this.recurseParent(parentNode)]
|
||||
} else if (parentNode) {
|
||||
return [parentNode]
|
||||
} else {
|
||||
if (!parentNode) {
|
||||
return []
|
||||
}
|
||||
const allParents = []
|
||||
if (parentNode) {
|
||||
allParents.push(parentNode)
|
||||
if (parentNode.pId) {
|
||||
allParents.push(...this.recurseParent(parentNode))
|
||||
}
|
||||
}
|
||||
return allParents
|
||||
},
|
||||
|
||||
recurseChildren(node) {
|
||||
if (!node.isParent) {
|
||||
return []
|
||||
@@ -248,49 +365,27 @@ export default {
|
||||
if (!children) {
|
||||
return []
|
||||
}
|
||||
let allChildren = []
|
||||
const allChildren = []
|
||||
children.forEach((n) => {
|
||||
allChildren = [...children, ...this.recurseChildren(n)]
|
||||
allChildren.push(n)
|
||||
allChildren.push(...this.recurseChildren(n))
|
||||
})
|
||||
return allChildren
|
||||
},
|
||||
groupBy(array, filter) {
|
||||
const groups = {}
|
||||
array.forEach(function(o) {
|
||||
const group = JSON.stringify(filter(o))
|
||||
groups[group] = groups[group] || []
|
||||
groups[group].push(o)
|
||||
})
|
||||
return Object.keys(groups).map(function(group) {
|
||||
return groups[group]
|
||||
})
|
||||
},
|
||||
filterTree(keyword, tree = this.zTree) {
|
||||
|
||||
searchFromLocal(keyword, tree = this.zTree) {
|
||||
if (!this.zTree) return
|
||||
|
||||
const searchNode = tree.getNodesByFilter((node) => node.id === 'search')
|
||||
if (searchNode) tree.removeNode(searchNode[0])
|
||||
const nodes = tree.transformToArray(tree.getNodes())
|
||||
|
||||
const allNodes = tree.transformToArray(tree.getNodes())
|
||||
if (!keyword) {
|
||||
tree.showNodes(nodes)
|
||||
tree.showNodes(allNodes)
|
||||
tree.expandAll(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (!keyword) {
|
||||
if (tree.hiddenNodes) {
|
||||
tree.showNodes(tree.hiddenNodes)
|
||||
tree.hiddenNodes = null
|
||||
}
|
||||
if (tree.expandNodes) {
|
||||
tree.expandNodes.forEach((node) => {
|
||||
if (node.id !== nodes[0].id) {
|
||||
tree.expandNode(node, false)
|
||||
}
|
||||
})
|
||||
tree.expandNodes = null
|
||||
}
|
||||
return null
|
||||
}
|
||||
let shouldShow = []
|
||||
const matchedNodes = tree.getNodesByFilter((node) => {
|
||||
return node.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
||||
})
|
||||
@@ -300,67 +395,54 @@ export default {
|
||||
const assetsAmount = matchedNodes.length
|
||||
name = `${name} (${assetsAmount})`
|
||||
const newNode = { id: 'search', name: name, isParent: false, open: false }
|
||||
tree.addNodes(null, newNode)
|
||||
const addedNodes = tree.addNodes(null, newNode)
|
||||
// 隐藏所有节点,只显示搜索节点
|
||||
tree.hideNodes(allNodes)
|
||||
tree.showNodes(addedNodes)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取应该展示的节点,以及应该展开的节点
|
||||
let shouldShow = []
|
||||
let shouldExpandNodes = []
|
||||
let shouldCollapseNodes = []
|
||||
matchedNodes.forEach((node) => {
|
||||
const parents = this.recurseParent(node)
|
||||
const children = this.recurseChildren(node)
|
||||
shouldShow = [...shouldShow, ...parents, ...children, node]
|
||||
})
|
||||
// 应该显示匹配节点本身、其祖先节点和子孙节点
|
||||
shouldShow.push(node)
|
||||
shouldShow.push(...parents)
|
||||
shouldShow.push(...children)
|
||||
|
||||
tree.hiddenNodes = nodes
|
||||
tree.expandNodes = shouldShow
|
||||
tree.hideNodes(nodes)
|
||||
// 应该展开匹配节点的父节点,不展开匹配节点的子孙节点
|
||||
shouldExpandNodes.push(...parents)
|
||||
// 应该折叠匹配节点的子孙节点
|
||||
shouldCollapseNodes.push(node)
|
||||
shouldCollapseNodes.push(...children)
|
||||
})
|
||||
shouldShow = Array.from(new Set(shouldShow))
|
||||
shouldExpandNodes = Array.from(new Set(shouldExpandNodes))
|
||||
shouldCollapseNodes = Array.from(new Set(shouldCollapseNodes))
|
||||
|
||||
// 隐藏所有节点,显示应该显示的节点
|
||||
tree.hideNodes(allNodes)
|
||||
tree.showNodes(shouldShow)
|
||||
for (const node of shouldShow) {
|
||||
if (node.isParent) {
|
||||
tree.expandNode(node, true)
|
||||
}
|
||||
// 展开应该展开的节点
|
||||
for (const node of shouldExpandNodes) {
|
||||
tree.expandNode(node, true)
|
||||
}
|
||||
// 折叠应该折叠的节点
|
||||
for (const node of shouldCollapseNodes) {
|
||||
tree.expandNode(node, false)
|
||||
}
|
||||
},
|
||||
filterAssetsServer(keyword) {
|
||||
if (!this.zTree) return
|
||||
let searchNode = this.zTree.getNodesByFilter((node) => node.id === 'search')
|
||||
if (searchNode) {
|
||||
this.zTree.removeChildNodes(searchNode[0])
|
||||
this.zTree.removeNode(searchNode[0])
|
||||
}
|
||||
const treeNodes = this.zTree.getNodes()
|
||||
if (!keyword) {
|
||||
if (treeNodes.length !== 0) {
|
||||
this.zTree.showNodes(treeNodes)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (treeNodes.length !== 0) {
|
||||
this.zTree.hideNodes(treeNodes)
|
||||
}
|
||||
|
||||
let treeUrl = this.treeSetting.searchUrl ? this.treeSetting.searchUrl : this.treeSetting.treeUrl
|
||||
const filterField = treeUrl.includes('?') ? `&search=${keyword}` : `?search=${keyword}`
|
||||
if (treeUrl.indexOf('assets/nodes/children/tree') > -1) {
|
||||
treeUrl = treeUrl + '&all=all'
|
||||
}
|
||||
const searchUrl = `${treeUrl}${filterField}`
|
||||
this.$axios.get(searchUrl).then(nodes => {
|
||||
let name = this.$t('Search')
|
||||
const assetsAmount = nodes.length
|
||||
name = `${name} (${assetsAmount})`
|
||||
const newNode = { id: 'search', name: name, isParent: true, open: true, zAsync: true }
|
||||
searchNode = this.zTree.addNodes(null, newNode)[0]
|
||||
searchNode.zAsync = true
|
||||
this.rootNodeAddDom(searchNode)
|
||||
|
||||
const nodesGroupByOrg = this.groupBy(nodes, (node) => {
|
||||
return node.meta?.data?.org_name
|
||||
})
|
||||
|
||||
for (const item of nodesGroupByOrg) {
|
||||
this.zTree.addNodes(searchNode, item)
|
||||
}
|
||||
searchNode.open = true
|
||||
})
|
||||
searchFromServer(keyword) {
|
||||
// 直接用搜索 API 返回的数据重新初始化树
|
||||
const treeUrl = this.treeSetting.searchUrl ? this.treeSetting.searchUrl : this.treeSetting.treeUrl
|
||||
const searchTypeKey = this.treeSearchTypeOptions[this.treeSearchType]?.search_key || 'search'
|
||||
const searchUrl = setUrlParam(treeUrl, searchTypeKey, keyword)
|
||||
this.initTree(true, searchUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,12 +706,19 @@ div.rMenu li {
|
||||
|
||||
.fixed-tree-search {
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
& ::v-deep .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
background: #fafafa;
|
||||
padding-right: 32px;
|
||||
color: var(--color-text-primary)
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
& ::v-deep .el-input__suffix {
|
||||
@@ -653,6 +742,37 @@ div.rMenu li {
|
||||
& ::v-deep .el-input__suffix-inner {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
& ::v-deep .el-input-group__prepend {
|
||||
padding-left: 5px;
|
||||
padding-right: 3px;
|
||||
border: none;
|
||||
color: #999;
|
||||
* {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
background: #fafafa;
|
||||
.el-icon-arrow-down {
|
||||
display: inline-block;
|
||||
transition: transform 0.8s ease; /* 动画关键 */
|
||||
}
|
||||
:hover {
|
||||
.el-icon-arrow-down {
|
||||
transform: rotate(180deg); /* 顺时针 180° */
|
||||
}
|
||||
}
|
||||
.search-label {
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-dropdown-menu__item.is-active {
|
||||
color: var(--color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free v5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z"/></svg>
|
||||
|
Before Width: | Height: | Size: 691 B |
@@ -123,16 +123,6 @@ export default {
|
||||
permissions: ['authentication.view_connectiontoken']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/profile/access-token',
|
||||
component: () => import('@/views/profile/AccessToken'),
|
||||
name: 'AccessToken',
|
||||
meta: {
|
||||
title: i18n.t('AccessToken'),
|
||||
icon: 'access-token',
|
||||
permissions: ['oauth2_provider.view_accesstoken']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/profile/preferences',
|
||||
name: 'Preferences',
|
||||
|
||||
@@ -150,11 +150,10 @@ export function getErrorResponseMsg(error) {
|
||||
} else if (typeof data === 'string') {
|
||||
return data
|
||||
} else if (_.isPlainObject(data)) {
|
||||
const msg = Object.values(data)
|
||||
return Object.values(data)
|
||||
.map(item => getErrorResponseMsg(item))
|
||||
.filter(i => i)
|
||||
// 错误信息不要重复提示
|
||||
return [...new Set(msg)].join('; ')
|
||||
.join('; ')
|
||||
} else {
|
||||
msg = error.toString()
|
||||
}
|
||||
@@ -424,28 +423,6 @@ export function getDrawerWidth() {
|
||||
return '90%'
|
||||
}
|
||||
|
||||
export function getShowCurrentAssetValue(cookie, defaultValue = '0') {
|
||||
const stored = typeof window !== 'undefined'
|
||||
? window.localStorage.getItem('show_current_asset')
|
||||
: null
|
||||
if (stored === '0' || stored === '1') {
|
||||
return stored
|
||||
}
|
||||
if (cookie && typeof cookie.get === 'function') {
|
||||
return cookie.get('show_current_asset') || defaultValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
export function setShowCurrentAssetValue(cookie, value) {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem('show_current_asset', String(value))
|
||||
}
|
||||
if (cookie && typeof cookie.set === 'function') {
|
||||
cookie.set('show_current_asset', value, 1)
|
||||
}
|
||||
}
|
||||
|
||||
export class ObjectLocalStorage {
|
||||
constructor(key, attr) {
|
||||
this.key = key
|
||||
|
||||
@@ -92,7 +92,7 @@ export function encryptPassword(password) {
|
||||
rsaPublicKeyText = rsaPublicKeyText.replaceAll('"', '')
|
||||
const rsaPublicKey = atob(rsaPublicKeyText)
|
||||
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey)
|
||||
const passwordCipher = aesEncrypt(String(password), aesKey)
|
||||
const passwordCipher = aesEncrypt(password, aesKey)
|
||||
return `${keyCipher}:${passwordCipher}`
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ export default {
|
||||
value: this.object.snapshot.node_amount
|
||||
},
|
||||
'trigger_display', 'date_start', 'date_finished',
|
||||
{
|
||||
key: this.$t('MailRecipient'),
|
||||
value: this.object.recipients ? this.object.recipients.map(
|
||||
i => `${i[0]}` + `${i[1] ? ': ' + this.$t('ContainAttachment') : ''}`).join(', ') : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('Comment'),
|
||||
value: this.object.snapshot.common
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
</el-alert>
|
||||
<TwoCol>
|
||||
<template>
|
||||
<GenericListTable
|
||||
ref="listTable"
|
||||
:detail-drawer="detailDrawer"
|
||||
:header-actions="headerActions"
|
||||
:table-config="tableConfig"
|
||||
/>
|
||||
<GenericListTable ref="listTable" :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</template>
|
||||
<template #right>
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
@@ -26,13 +21,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { QuickActions } from '@/components'
|
||||
import { openTaskPage } from '@/utils/jms/index'
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
|
||||
import { openTaskPage } from '@/utils/jms/index'
|
||||
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateChangeSecret',
|
||||
@@ -52,7 +46,6 @@ export default {
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
detailDrawer: () => import('@/views/accounts/AccountDiscover/TaskDetail/index.vue'),
|
||||
visible: false,
|
||||
secretUrl: '',
|
||||
showViewSecretDialog: false,
|
||||
@@ -65,20 +58,20 @@ export default {
|
||||
},
|
||||
callbacks: Object.freeze({
|
||||
click: () => {
|
||||
this.$axios
|
||||
.patch(
|
||||
`/api/v1/accounts/account-templates/${this.object.id}/sync-related-accounts/`
|
||||
)
|
||||
.then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
this.$axios.patch(
|
||||
`/api/v1/accounts/account-templates/${this.object.id}/sync-related-accounts/`
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
tableConfig: {
|
||||
url: `/api/v1/accounts/accounts/?source_id=${this.object.id}`,
|
||||
columns: ['name', 'asset', 'secret_type', 'is_active', 'date_created'],
|
||||
columns: [
|
||||
'name', 'asset', 'secret_type', 'is_active', 'date_created'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
@@ -86,9 +79,8 @@ export default {
|
||||
drawer: true,
|
||||
can: vm.$hasPerm('accounts.view_account'),
|
||||
getRoute: ({ row }) => {
|
||||
this.detailDrawer = () => import('@/views/accounts/Account/AccountDetail/index.vue')
|
||||
return {
|
||||
name: 'AccountDetail',
|
||||
name: 'AssetAccountDetail',
|
||||
params: { id: row.id }
|
||||
}
|
||||
}
|
||||
@@ -102,7 +94,6 @@ export default {
|
||||
can: vm.$hasPerm('assets.view_asset'),
|
||||
getTitle: ({ row }) => row.asset.name,
|
||||
getRoute: ({ row }) => {
|
||||
this.detailDrawer = () => import('@/views/assets/Asset/AssetDetail')
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset.id }
|
||||
|
||||
@@ -137,8 +137,8 @@ export default {
|
||||
getUrlQuery: true,
|
||||
options: [
|
||||
{
|
||||
label: this.$t('ID'),
|
||||
value: 'id'
|
||||
label: this.$t('TaskID'),
|
||||
value: 'automation_id'
|
||||
},
|
||||
{
|
||||
label: this.$t('DisplayName'),
|
||||
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
updateSuccessNextRoute: this.updateSuccessNextRoute,
|
||||
hasDetailInMsg: false,
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'address', 'platform', 'nodes']],
|
||||
[this.$t('Basic'), ['name', 'address', 'platform', 'node']],
|
||||
[this.$t('Protocol'), ['protocols']],
|
||||
[this.$t('Account'), ['accounts']],
|
||||
[this.$t('Other'), ['directory_services', 'zone', 'labels', 'is_active', 'comment']]
|
||||
@@ -73,8 +73,8 @@ export default {
|
||||
const values = _.cloneDeep(validValues)
|
||||
const submitMethod = id ? 'put' : 'post'
|
||||
|
||||
if (values.nodes && values.nodes.length === 0) {
|
||||
delete values['nodes']
|
||||
if (!values.node) {
|
||||
delete values['node']
|
||||
}
|
||||
|
||||
if (submitMethod === 'put') {
|
||||
@@ -143,15 +143,14 @@ export default {
|
||||
},
|
||||
async setInitial() {
|
||||
const { defaultConfig } = this
|
||||
const { node } = this.$route.query
|
||||
const nodesInitial = node ? [node] : []
|
||||
const { node_id } = this.$route.query
|
||||
const platformId = this.platformID || 'Linux'
|
||||
const url = `/api/v1/assets/platforms/${platformId}/`
|
||||
this.platform = await this.$axios.get(url)
|
||||
const initial = {
|
||||
labels: [],
|
||||
is_active: true,
|
||||
nodes: nodesInitial,
|
||||
node: node_id,
|
||||
platform: parseInt(this.platform.id),
|
||||
protocols: []
|
||||
}
|
||||
|
||||
@@ -61,11 +61,8 @@ export default {
|
||||
},
|
||||
callbacks: {
|
||||
change: function(val) {
|
||||
const category = this.object.category.value
|
||||
const normalizedCategory =
|
||||
category === 'ds' ? 'directorie' : category
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/${normalizedCategory}s/${this.object.id}/`,
|
||||
`/api/v1/assets/assets/${this.object.id}/`,
|
||||
{ is_active: val }
|
||||
).then(res => {
|
||||
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
||||
|
||||
@@ -28,12 +28,7 @@ import { mapGetters } from 'vuex'
|
||||
import TreeMenu from './components/TreeMenu'
|
||||
import BaseList from './components/BaseList'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
import {
|
||||
getShowCurrentAssetValue,
|
||||
setShowCurrentAssetValue,
|
||||
setRouterQuery,
|
||||
setUrlParam
|
||||
} from '@/utils/common/index'
|
||||
import { setRouterQuery, setUrlParam } from '@/utils/common/index'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -51,6 +46,9 @@ export default {
|
||||
url: '/api/v1/assets/assets/',
|
||||
showMenu: !this.$store.getters.currentOrgIsRoot,
|
||||
showDefaultMenu: true,
|
||||
async: {
|
||||
enable: false
|
||||
},
|
||||
menu: [
|
||||
]
|
||||
},
|
||||
@@ -78,7 +76,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
decorateRMenu() {
|
||||
const show_current_asset = getShowCurrentAssetValue(this.$cookie)
|
||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||
if (show_current_asset === '1') {
|
||||
$('#m_show_asset_all_children_node').css('color', '#606266')
|
||||
$('#m_show_asset_only_current_node').css('color', 'green')
|
||||
@@ -88,7 +86,7 @@ export default {
|
||||
}
|
||||
},
|
||||
showAll({ node, showCurrentAsset }) {
|
||||
setShowCurrentAssetValue(this.$cookie, showCurrentAsset)
|
||||
this.$cookie.set('show_current_asset', showCurrentAsset, 1)
|
||||
this.decorateRMenu()
|
||||
const url = `${this.treeSetting.url}?node_id=${node.meta.data.id}&show_current_asset=${showCurrentAsset}`
|
||||
this.$refs.AssetTreeTable.$refs.TreeList.handleUrlChange(url)
|
||||
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
[this.$tc('Platform'), 'platform'],
|
||||
[this.$tc('Node'), 'node'],
|
||||
[this.$tc('Protocol'), 'protocols'],
|
||||
[this.$tc('Region'), 'region_name']
|
||||
[this.$tc('Region'), 'region_id']
|
||||
],
|
||||
data: []
|
||||
},
|
||||
@@ -124,7 +124,7 @@ export default {
|
||||
this.ws.onmessage = e => {
|
||||
const data = JSON.parse(e.data)
|
||||
if (data.action === 'sync_region') {
|
||||
this.addRegion(data.id, data.name)
|
||||
this.addRegion(data.region_id)
|
||||
} else if (data.action === 'import') {
|
||||
data['@status'] = 'pending'
|
||||
this.$refs.importTable.addTableItem(data)
|
||||
@@ -140,10 +140,10 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
addRegion(regionId, regionName) {
|
||||
if (!this.alreadySync.includes(regionId)) {
|
||||
this.alreadySync.push(regionId)
|
||||
this.tip = `${this.$t('SyncRegion')}: ${regionName}`
|
||||
addRegion(region) {
|
||||
if (!this.alreadySync.includes(region)) {
|
||||
this.alreadySync.push(region)
|
||||
this.tip = `${this.$t('SyncRegion')}: ${this.alreadySync.at(-1)}`
|
||||
}
|
||||
},
|
||||
showResult() {
|
||||
|
||||
@@ -17,8 +17,6 @@ export const ucloud = 'ucloud'
|
||||
|
||||
export const volcengine = 'volcengine'
|
||||
|
||||
export const ctyun = 'ctyun'
|
||||
|
||||
export const qingcloud_private = 'qingcloud_private'
|
||||
export const huaweicloud_private = 'huaweicloud_private'
|
||||
export const ctyun_private = 'ctyun_private'
|
||||
@@ -48,8 +46,7 @@ export const publicHostProviders = [
|
||||
gcp,
|
||||
ucloud,
|
||||
volcengine,
|
||||
smartx,
|
||||
ctyun
|
||||
smartx
|
||||
]
|
||||
|
||||
export const publicDBProviders = [aliyun]
|
||||
@@ -144,12 +141,6 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
attrs: ['access_key_id', 'access_key_secret'],
|
||||
image: require('@/assets/img/cloud/volcengine.svg')
|
||||
},
|
||||
[ctyun]: {
|
||||
name: ctyun,
|
||||
title: i18n.t('CTYun'),
|
||||
attrs: ['access_key_id', 'access_key_secret', 'project_id'],
|
||||
image: require('@/assets/img/cloud/state.svg')
|
||||
},
|
||||
[vmware]: {
|
||||
name: vmware,
|
||||
title: 'VMware',
|
||||
|
||||
@@ -148,9 +148,10 @@ export const assetFieldsMeta = (vm, category, type) => {
|
||||
return vm.platform.ds_enabled === false
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
node: {
|
||||
rules: [rules.RequiredChange],
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: item => {
|
||||
|
||||
@@ -8,6 +8,7 @@ export const UserAssetPermissionListPageSearchConfigOptions = [
|
||||
{ label: i18n.t('UserGroups'), value: 'user_group' },
|
||||
{ label: i18n.t('AssetName'), value: 'asset_name' },
|
||||
{ label: i18n.t('AssetAddress'), value: 'address' },
|
||||
{ label: i18n.t('Account'), value: 'accounts' },
|
||||
{
|
||||
label: i18n.t('Valid'),
|
||||
value: 'is_valid',
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<GenericListPage
|
||||
ref="GenericListTable"
|
||||
:header-actions="headerActions"
|
||||
:help-tip="helpMessage"
|
||||
:table-config="tableConfig"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListPage
|
||||
},
|
||||
data() {
|
||||
const ajaxUrl = '/api/v1/authentication/access-tokens/'
|
||||
return {
|
||||
helpMessage: this.$t('AccessTokenTip'),
|
||||
tableConfig: {
|
||||
hasSelection: false,
|
||||
url: ajaxUrl,
|
||||
columns: [
|
||||
'token_preview', 'scope', 'is_valid', 'expires', 'updated', 'created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
actions: {
|
||||
prop: '',
|
||||
formatterArgs: {
|
||||
hasUpdate: false,
|
||||
hasClone: false,
|
||||
hasDelete: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'Revoke',
|
||||
title: this.$t('Revoke'),
|
||||
can: ({ row }) => this.$hasPerm('oauth2_provider.delete_accesstoken'),
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
this.$axios.delete(`${ajaxUrl}${row.id}/revoke/`,
|
||||
).then(res => {
|
||||
this.reloadTable()
|
||||
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$tc('UpdateErrorMsg' + ' ' + error))
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasSearch: false,
|
||||
hasRightActions: true,
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasCreate: false,
|
||||
extraActions: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reloadTable() {
|
||||
this.$refs.GenericListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -96,6 +96,7 @@ export default {
|
||||
{
|
||||
title: this.$t('FacialFeatures'),
|
||||
has: this.$store.getters.publicSettings.FACE_RECOGNITION_ENABLED &&
|
||||
this.$store.getters.publicSettings.XPACK_LICENSE_EDITION_ULTIMATE &&
|
||||
!store.getters.publicSettings['PRIVACY_MODE'],
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
|
||||
@@ -1,41 +1,36 @@
|
||||
<template>
|
||||
<TwoCol>
|
||||
<template>
|
||||
<Account :url="url" :columns-meta="columnsMeta" :object.sync="object" />
|
||||
</template>
|
||||
<template #right>
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
</template>
|
||||
</TwoCol>
|
||||
<Account
|
||||
:url="url"
|
||||
:columns-meta="columnsMeta"
|
||||
:extra-quick-actions="quickActions"
|
||||
:object.sync="object"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { QuickActions } from '@/components'
|
||||
import Account from '@/views/assets/Asset/AssetDetail/Account'
|
||||
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||
|
||||
export default {
|
||||
name: 'Accounts',
|
||||
components: {
|
||||
TwoCol,
|
||||
Account,
|
||||
QuickActions
|
||||
Account
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: row => <span>{row.name}</span>
|
||||
formatter: (row) => <span>{row.name}</span>
|
||||
},
|
||||
asset: {
|
||||
label: this.$t('Asset'),
|
||||
formatter: row => <span>{row.asset.name}</span>
|
||||
formatter: (row) => <span>{row.asset.name}</span>
|
||||
}
|
||||
},
|
||||
quickActions: [
|
||||
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
'CAS_RENAME_ATTRIBUTES'
|
||||
]],
|
||||
[this.$t('Other'), [
|
||||
'CAS_ORG_IDS', 'CAS_LOGOUT_COMPLETELY'
|
||||
'CAS_ORG_IDS', 'CAS_CREATE_USER', 'CAS_LOGOUT_COMPLETELY'
|
||||
]]
|
||||
],
|
||||
fieldsMeta: {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { GenericCreateUpdateForm } from '@/layout/components'
|
||||
import IBox from '@/components/Common/IBox/index.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import ChatProvidersField from './components/ChatProvidersField.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -17,118 +18,54 @@ export default {
|
||||
GenericCreateUpdateForm
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const hasProviders = (formValue) => {
|
||||
const providers = formValue?.CHAT_AI_PROVIDERS?.providers || []
|
||||
return Array.isArray(providers) && providers.length > 0
|
||||
}
|
||||
return {
|
||||
url: '/api/v1/settings/setting/?category=chat',
|
||||
hasReset: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('Test'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
'/api/v1/settings/chatai/testing/',
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch(() => {
|
||||
vm.$log.error('err occur')
|
||||
}).finally(() => {
|
||||
btn.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
encryptedFields: ['VAULT_HCP_TOKEN'],
|
||||
fields: [
|
||||
'CHAT_AI_ENABLED',
|
||||
'CHAT_AI_METHOD',
|
||||
'CHAT_AI_EMBED_URL',
|
||||
'CHAT_AI_TYPE',
|
||||
'DEEPSEEK_BASE_URL',
|
||||
'DEEPSEEK_API_KEY',
|
||||
'DEEPSEEK_PROXY',
|
||||
'DEEPSEEK_MODEL',
|
||||
'GPT_BASE_URL',
|
||||
'GPT_API_KEY',
|
||||
'GPT_PROXY',
|
||||
'GPT_MODEL',
|
||||
'CUSTOM_GPT_MODEL',
|
||||
'CUSTOM_DEEPSEEK_MODEL'
|
||||
'CHAT_AI_PROVIDERS',
|
||||
'CHAT_AI_EMBED_URL'
|
||||
],
|
||||
fieldsMeta: {
|
||||
CHAT_AI_TYPE: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api'
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || hasProviders(formValue)
|
||||
}
|
||||
},
|
||||
GPT_BASE_URL: {
|
||||
el: {
|
||||
autocomplete: 'new-password'
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'gpt'
|
||||
}
|
||||
},
|
||||
GPT_API_KEY: {
|
||||
el: {
|
||||
autocomplete: 'new-password'
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'gpt'
|
||||
}
|
||||
},
|
||||
GPT_PROXY: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'gpt'
|
||||
}
|
||||
},
|
||||
GPT_MODEL: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'gpt'
|
||||
}
|
||||
},
|
||||
DEEPSEEK_BASE_URL: {
|
||||
el: {
|
||||
autocomplete: 'new-password'
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'deep-seek'
|
||||
}
|
||||
},
|
||||
DEEPSEEK_API_KEY: {
|
||||
el: {
|
||||
autocomplete: 'new-password'
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'deep-seek'
|
||||
}
|
||||
},
|
||||
DEEPSEEK_PROXY: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'deep-seek'
|
||||
}
|
||||
},
|
||||
DEEPSEEK_MODEL: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'deep-seek'
|
||||
}
|
||||
CHAT_AI_PROVIDERS: {
|
||||
component: ChatProvidersField,
|
||||
hidden: (formValue) => formValue.CHAT_AI_METHOD !== 'api'
|
||||
},
|
||||
CHAT_AI_EMBED_URL: {
|
||||
hidden: (formValue) => formValue.CHAT_AI_METHOD !== 'embed'
|
||||
},
|
||||
CUSTOM_GPT_MODEL: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'gpt' || formValue.GPT_MODEL !== 'custom'
|
||||
}
|
||||
},
|
||||
CUSTOM_DEEPSEEK_MODEL: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.CHAT_AI_METHOD !== 'api' || formValue.CHAT_AI_TYPE !== 'deep-seek' || formValue.DEEPSEEK_MODEL !== 'custom'
|
||||
}
|
||||
},
|
||||
afterGetFormValue(formValue) {
|
||||
const providers = Array.isArray(formValue.CHAT_AI_PROVIDERS) ? formValue.CHAT_AI_PROVIDERS : []
|
||||
return {
|
||||
...formValue,
|
||||
CHAT_AI_PROVIDERS: {
|
||||
providers: providers
|
||||
}
|
||||
}
|
||||
},
|
||||
cleanFormValue(values) {
|
||||
const config = values.CHAT_AI_PROVIDERS || {}
|
||||
const providers = Array.isArray(config.providers) ? config.providers : []
|
||||
return {
|
||||
...values,
|
||||
CHAT_AI_PROVIDERS: providers,
|
||||
CHAT_AI_TYPE: values.CHAT_AI_TYPE || providers[0]?.type || values.CHAT_AI_TYPE
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
}
|
||||
|
||||
186
src/views/settings/Feature/components/ChatProvidersField.vue
Normal file
186
src/views/settings/Feature/components/ChatProvidersField.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="providers-card">
|
||||
<div class="providers-header">
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="addProvider">
|
||||
{{ $t('Add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="localProviders" size="mini" border>
|
||||
<el-table-column :label="$t('Type')" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.type" size="mini" @change="emitChange">
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('Base URL')" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.base_url"
|
||||
size="mini"
|
||||
:placeholder="openAIBaseUrl"
|
||||
@input="emitChange"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('API Key')" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.api_key"
|
||||
size="mini"
|
||||
show-password
|
||||
autocomplete="new-password"
|
||||
@input="emitChange"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('Proxy')" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.proxy"
|
||||
size="mini"
|
||||
placeholder="http://ip:port"
|
||||
@input="emitChange"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('Actions')" width="100" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
class="danger-text"
|
||||
icon="el-icon-delete"
|
||||
@click="removeProvider($index)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
export default {
|
||||
name: 'ChatProvidersField',
|
||||
props: {
|
||||
value: {
|
||||
type: [Object, Array],
|
||||
default: () => ({ providers: [], defaultProvider: '' })
|
||||
},
|
||||
typeOptions: {
|
||||
type: Array,
|
||||
default: () => ([
|
||||
{ label: 'Ollama', value: 'ollama' },
|
||||
{ label: 'OpenAI', value: 'openai' }
|
||||
])
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const { providers, defaultProvider } = this.normalizeValue(this.value)
|
||||
return {
|
||||
localProviders: providers,
|
||||
defaultProvider: this.pickDefault(defaultProvider, providers),
|
||||
openAIBaseUrl: 'https://api.openai.com/v1'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(v) {
|
||||
const { providers, defaultProvider } = this.normalizeValue(v)
|
||||
this.localProviders = providers
|
||||
this.defaultProvider = this.pickDefault(defaultProvider, providers) || ''
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emptyProvider() {
|
||||
return {
|
||||
type: 'openai',
|
||||
base_url: this.openAIBaseUrl,
|
||||
api_key: '',
|
||||
proxy: ''
|
||||
}
|
||||
},
|
||||
normalizeValue(v) {
|
||||
if (!v) {
|
||||
return { providers: [], defaultProvider: '' }
|
||||
}
|
||||
// allow legacy array value
|
||||
if (Array.isArray(v)) {
|
||||
return { providers: cloneDeep(v), defaultProvider: '' }
|
||||
}
|
||||
const providers = Array.isArray(v.providers) ? cloneDeep(v.providers) : []
|
||||
return { providers, defaultProvider: v.defaultProvider || '' }
|
||||
},
|
||||
pickDefault(current, providers) {
|
||||
const enabledProviders = providers.filter(item => item?.enabled !== false)
|
||||
if (current && providers.some(item => item.name === current)) {
|
||||
return current
|
||||
}
|
||||
return enabledProviders[0]?.name || providers[0]?.name || ''
|
||||
},
|
||||
emitChange() {
|
||||
const providers = cloneDeep(this.localProviders || [])
|
||||
const defaultProvider = this.pickDefault(this.defaultProvider, providers)
|
||||
this.defaultProvider = defaultProvider
|
||||
this.$emit('input', { providers, defaultProvider })
|
||||
this.$emit('change', { providers, defaultProvider })
|
||||
},
|
||||
handleAssistantChange(current) {
|
||||
this.emitChange()
|
||||
},
|
||||
addProvider() {
|
||||
this.localProviders.push(this.emptyProvider())
|
||||
this.emitChange()
|
||||
},
|
||||
removeProvider(index) {
|
||||
this.localProviders.splice(index, 1)
|
||||
this.emitChange()
|
||||
},
|
||||
setDefault(name) {
|
||||
this.defaultProvider = name
|
||||
this.emitChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.providers-card {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.providers-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
color: #909399;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.danger-text {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
@@ -6,7 +6,6 @@
|
||||
:create-drawer="createDrawer"
|
||||
:detail-drawer="detailDrawer"
|
||||
:header-actions="iTicketAction"
|
||||
:quick-filters="quickFilters"
|
||||
:table-config="ticketTableConfig"
|
||||
/>
|
||||
</template>
|
||||
@@ -42,77 +41,11 @@ export default {
|
||||
loading: true,
|
||||
getDrawerTitle: () => ' ',
|
||||
createDrawer: () => import('@/views/tickets/RequestAssetPerm/CreateUpdate'),
|
||||
quickFilters: [
|
||||
{
|
||||
label: this.$t('Type'),
|
||||
options: [
|
||||
{
|
||||
label: this.$t('ApplyAsset'),
|
||||
filter: {
|
||||
type: 'apply_asset'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('LoginConfirm'),
|
||||
filter: {
|
||||
type: 'login_confirm'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('CommandConfirm'),
|
||||
filter: {
|
||||
type: 'command_confirm'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('LoginAssetConfirm'),
|
||||
filter: {
|
||||
type: 'login_asset_confirm'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('State'),
|
||||
options: [
|
||||
{
|
||||
label: this.$t('All'),
|
||||
filter: {
|
||||
state: 'all'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('Open'),
|
||||
filter: {
|
||||
state: 'pending'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('Cancel'),
|
||||
filter: {
|
||||
state: 'closed'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('Approved'),
|
||||
filter: {
|
||||
state: 'approved'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('Rejected'),
|
||||
filter: {
|
||||
state: 'rejected'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
detailDrawer: null,
|
||||
ticketTableConfig: {
|
||||
url: this.url,
|
||||
extraQuery: this.extraQuery,
|
||||
columnsExclude: ['process_map', 'rel_snapshot', 'status'],
|
||||
columnsExclude: ['process_map', 'rel_snapshot'],
|
||||
columnsShow: {
|
||||
min: ['title', 'serial_num', 'type', 'state', 'date_created'],
|
||||
default: ['title', 'serial_num', 'type', 'state', 'date_created']
|
||||
@@ -161,8 +94,25 @@ export default {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
status: {
|
||||
align: 'center',
|
||||
sortable: 'custom',
|
||||
formatter: TagChoicesFormatter,
|
||||
formatterArgs: {
|
||||
getTagLabel({ row }) {
|
||||
return row.status.label
|
||||
},
|
||||
getTagType({ row }) {
|
||||
if (row.status.value === 'open') {
|
||||
return 'primary'
|
||||
} else {
|
||||
return 'danger'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
label: this.$t('State'),
|
||||
label: this.$t('Action'),
|
||||
align: 'center',
|
||||
sortable: 'custom',
|
||||
formatter: TagChoicesFormatter,
|
||||
@@ -192,7 +142,7 @@ export default {
|
||||
}
|
||||
},
|
||||
defaultTicketActions: {
|
||||
hasImport: false,
|
||||
hasExport: false,
|
||||
hasMoreActions: false,
|
||||
hasLeftActions: true,
|
||||
canCreate: this.$hasPerm('tickets.view_ticket'),
|
||||
@@ -239,6 +189,10 @@ export default {
|
||||
value: 'relevant_asset',
|
||||
label: this.$t('RelevantAsset')
|
||||
},
|
||||
{
|
||||
value: 'relevant_system_user',
|
||||
label: this.$t('RelevantCommand')
|
||||
},
|
||||
{
|
||||
value: 'relevant_command',
|
||||
label: this.$t('ApplyRunCommand')
|
||||
|
||||
@@ -217,14 +217,12 @@ export default {
|
||||
const accounts = this.requestForm.accounts
|
||||
if (this.object.approval_step.value === this.object.process_map.length) {
|
||||
if (assets.length === 0 && nodes.length === 0) {
|
||||
this.$message.error(this.$tc('SelectAtLeastOneAssetOrNodeErrMsg'))
|
||||
return false
|
||||
return this.$message.error(this.$tc('SelectAtLeastOneAssetOrNodeErrMsg'))
|
||||
} else if (accounts.length === 0) {
|
||||
this.$message.error(this.$tc('RequiredSystemUserErrMsg'))
|
||||
return false
|
||||
return this.$message.error(this.$tc('RequiredSystemUserErrMsg'))
|
||||
}
|
||||
}
|
||||
return this.$axios.patch(`/api/v1/tickets/apply-asset-tickets/${this.object.id}/approve/`, {
|
||||
this.$axios.patch(`/api/v1/tickets/apply-asset-tickets/${this.object.id}/approve/`, {
|
||||
apply_nodes: nodes || [],
|
||||
apply_assets: assets || [],
|
||||
apply_accounts: accounts || [],
|
||||
@@ -241,11 +239,11 @@ export default {
|
||||
},
|
||||
handleClose() {
|
||||
const url = `/api/v1/tickets/apply-asset-tickets/${this.object.id}/close/`
|
||||
return this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
},
|
||||
handleReject() {
|
||||
const url = `/api/v1/tickets/apply-asset-tickets/${this.object.id}/reject/`
|
||||
return this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,16 +231,7 @@ export default {
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
const result = handler()
|
||||
if (result === false) {
|
||||
this.isDisabled = false
|
||||
return
|
||||
}
|
||||
if (result && typeof result.finally === 'function') {
|
||||
result.finally(() => {
|
||||
this.isDisabled = false
|
||||
})
|
||||
}
|
||||
handler()
|
||||
} else {
|
||||
this.$message.error('No handler for action')
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ export default {
|
||||
let mfa_level = null
|
||||
// SECURITY_MFA_AUTH 0 不开启 1 全局开启 2 管理员开启
|
||||
const securityMFAAuth = store.getters.publicSettings['SECURITY_MFA_AUTH']
|
||||
const adminUserIsNeed = (user?.is_superuser || user?.is_org_admin) && this.$route.params.action === 'update' &&
|
||||
const adminUserIsNeed = (user?.is_superuser || user?.is_org_admin) && this.$route.meta.action === 'update' &&
|
||||
securityMFAAuth === MFASystemSetting.onlyAdminUsers
|
||||
if (securityMFAAuth === MFASystemSetting.allUsers) {
|
||||
options = [{ 'value': MFALevel.allUsers, 'label': this.$t('MFAAllUsers') }]
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
treeUrl: `/api/v1/perms/users/${this.object.id}/nodes/children/tree/`,
|
||||
tableUrl: `/api/v1/perms/users/${this.object.id}/assets/?all=1`,
|
||||
tableUrl: `/api/v1/perms/users/${this.object.id}/assets/`,
|
||||
actions: {
|
||||
has: false
|
||||
},
|
||||
|
||||
@@ -20,8 +20,6 @@ import { QuickActions } from '@/components'
|
||||
import RelationCard from '@/components/Cards/RelationCard'
|
||||
import AutoDetailCard from '@/components/Cards/DetailCard/auto'
|
||||
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||
import store from '@/store'
|
||||
import { MFASystemSetting } from '@/views/users/const'
|
||||
|
||||
export default {
|
||||
name: 'UserInfo',
|
||||
@@ -228,22 +226,7 @@ export default {
|
||||
return <div>{doms}</div>
|
||||
}
|
||||
},
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id',
|
||||
{
|
||||
key: this.$t('MFA'),
|
||||
formatter: (item, val) => {
|
||||
const user = vm.object
|
||||
const securityMFAAuth = store.getters.publicSettings['SECURITY_MFA_AUTH']
|
||||
const adminUserIsNeed = (user?.is_superuser || user?.is_org_admin) && securityMFAAuth === MFASystemSetting.onlyAdminUsers
|
||||
if (securityMFAAuth === MFASystemSetting.allUsers) {
|
||||
return this.$t('MFAAllUsers')
|
||||
}
|
||||
if (securityMFAAuth === MFASystemSetting.onlyAdminUsers && adminUserIsNeed) {
|
||||
return this.$t('MFAOnlyAdminUsers')
|
||||
}
|
||||
return user?.mfa_level.label
|
||||
}
|
||||
},
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id', 'mfa_level',
|
||||
'source', 'labels',
|
||||
'created_by', 'date_joined', 'date_expired',
|
||||
'date_password_last_updated', 'last_login', 'comment'
|
||||
|
||||
66
yarn.lock
66
yarn.lock
@@ -1069,6 +1069,11 @@
|
||||
resolved "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@socket.io/component-emitter@~3.1.0":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2"
|
||||
integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==
|
||||
|
||||
"@soda/friendly-errors-webpack-plugin@^1.7.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz#4d4fbb1108993aaa362116247c3d18188a2c6c85"
|
||||
@@ -4767,6 +4772,13 @@ debug@^3.1.0, debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@~4.3.1, debug@~4.3.2:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||
@@ -5301,6 +5313,22 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
engine.io-client@~6.6.1:
|
||||
version "6.6.3"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.3.tgz#815393fa24f30b8e6afa8f77ccca2f28146be6de"
|
||||
integrity sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.1"
|
||||
engine.io-parser "~5.2.1"
|
||||
ws "~8.17.1"
|
||||
xmlhttprequest-ssl "~2.1.1"
|
||||
|
||||
engine.io-parser@~5.2.1:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f"
|
||||
integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==
|
||||
|
||||
enhanced-resolve@^4.1.0, enhanced-resolve@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
|
||||
@@ -12884,6 +12912,24 @@ snapdragon@^0.8.1:
|
||||
source-map-resolve "^0.5.0"
|
||||
use "^3.1.0"
|
||||
|
||||
socket.io-client@^4.8.1:
|
||||
version "4.8.1"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.1.tgz#1941eca135a5490b94281d0323fe2a35f6f291cb"
|
||||
integrity sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.2"
|
||||
engine.io-client "~6.6.1"
|
||||
socket.io-parser "~4.2.4"
|
||||
|
||||
socket.io-parser@~4.2.4:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
|
||||
integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.1"
|
||||
|
||||
sockjs-client@^1.5.0:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz#350b8eda42d6d52ddc030c39943364c11dcad806"
|
||||
@@ -14208,16 +14254,16 @@ utils-merge@1.0.1:
|
||||
resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
|
||||
uuid@8.3.2, uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
v-sanitize@^0.0.13:
|
||||
version "0.0.13"
|
||||
resolved "https://registry.npmmirror.com/v-sanitize/-/v-sanitize-0.0.13.tgz#d98e3eee3277a2b3a632bc2ba119c088967da0c4"
|
||||
@@ -14940,11 +14986,21 @@ ws@^6.0.0, ws@^6.2.1:
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@~8.17.1:
|
||||
version "8.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
|
||||
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
|
||||
|
||||
xml-name-validator@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
||||
|
||||
xmlhttprequest-ssl@~2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23"
|
||||
integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==
|
||||
|
||||
xss@^1.0.14, xss@^1.0.9:
|
||||
version "1.0.15"
|
||||
resolved "https://registry.npmmirror.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a"
|
||||
|
||||
Reference in New Issue
Block a user