Compare commits
34 Commits
pr@dev@per
...
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:20251030_071727 AS stage-build
|
FROM jumpserver/lina-base:20251204_081759 AS stage-build
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV VERSION=$VERSION
|
ENV VERSION=$VERSION
|
||||||
|
|||||||
11
package.json
@@ -59,9 +59,11 @@
|
|||||||
"npm": "^7.8.0",
|
"npm": "^7.8.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"path-to-regexp": "3.3.0",
|
"path-to-regexp": "3.3.0",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
|
"uuid": "8.3.2",
|
||||||
"v-sanitize": "^0.0.13",
|
"v-sanitize": "^0.0.13",
|
||||||
"vue": "2.6.10",
|
"vue": "2.7.16",
|
||||||
"vue-codemirror": "4.0.6",
|
"vue-codemirror": "4.0.6",
|
||||||
"vue-cookie": "^1.1.4",
|
"vue-cookie": "^1.1.4",
|
||||||
"vue-echarts": "^5.0.0-beta.0",
|
"vue-echarts": "^5.0.0-beta.0",
|
||||||
@@ -119,11 +121,11 @@
|
|||||||
"svg-sprite-loader": "4.1.3",
|
"svg-sprite-loader": "4.1.3",
|
||||||
"svgo": "1.2.2",
|
"svgo": "1.2.2",
|
||||||
"vue-i18n-extract": "^1.1.1",
|
"vue-i18n-extract": "^1.1.1",
|
||||||
"vue-template-compiler": "2.6.10",
|
"vue-template-compiler": "2.7.16",
|
||||||
"webpack": "^4.28.4"
|
"webpack": "^4.28.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.9",
|
"node": ">=12",
|
||||||
"npm": ">= 3.0.0"
|
"npm": ">= 3.0.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
@@ -140,5 +142,6 @@
|
|||||||
"src/**/*.{js,vue}": [
|
"src/**/*.{js,vue}": [
|
||||||
"eslint --fix"
|
"eslint --fix"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 584 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -107,6 +107,10 @@ export default {
|
|||||||
this.$emit('add', true)
|
this.$emit('add', true)
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (error?.response?.data?.code === 'no_valid_assets') {
|
||||||
|
this.$message.error(error?.response?.data?.detail)
|
||||||
|
return
|
||||||
|
}
|
||||||
this.iVisible = true
|
this.iVisible = true
|
||||||
this.handleResult(null, error)
|
this.handleResult(null, error)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div v-if="hasPrompt" class="chat-action">
|
<div class="chat-action">
|
||||||
<Select2
|
<div class="model-select">
|
||||||
v-model="select.value"
|
<Select2
|
||||||
:disabled="isLoading || isSelectDisabled"
|
v-model="select.value"
|
||||||
v-bind="select"
|
:disabled="isLoading || isSelectDisabled || loading || !options.length"
|
||||||
@change="onSelectChange"
|
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>
|
||||||
<div class="chat-input">
|
<div class="chat-input">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -37,9 +85,37 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
hasPrompt: {
|
modelOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
selectedModel: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
type: Boolean,
|
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() {
|
data() {
|
||||||
@@ -47,15 +123,10 @@ export default {
|
|||||||
isIM: false,
|
isIM: false,
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
select: {
|
select: {
|
||||||
url: '/api/v1/settings/chatai-prompts/',
|
|
||||||
value: '',
|
value: '',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
placeholder: this.$t('Role'),
|
placeholder: this.$t('Model'),
|
||||||
ajax: {
|
options: []
|
||||||
transformOption: (item) => {
|
|
||||||
return { label: item.name, value: item.content }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -64,7 +135,32 @@ export default {
|
|||||||
isLoading: state => state.chat.loading
|
isLoading: state => state.chat.loading
|
||||||
}),
|
}),
|
||||||
isSelectDisabled() {
|
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: {
|
methods: {
|
||||||
@@ -84,7 +180,25 @@ export default {
|
|||||||
this.inputValue = ''
|
this.inputValue = ''
|
||||||
},
|
},
|
||||||
onSelectChange(value) {
|
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 {
|
.chat-action {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 6px 0;
|
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 {
|
&::v-deep .el-select {
|
||||||
width: 50%;
|
width: 100%;
|
||||||
|
|
||||||
.el-input__inner {
|
.el-input__inner {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
<div class="chart-item-container">
|
<div class="chart-item-container">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<el-avatar
|
<el-avatar
|
||||||
:src="isUserRole ? userUrl : chatUrl"
|
v-if="isUserRole"
|
||||||
|
:src="userUrl"
|
||||||
class="header-avatar"
|
class="header-avatar"
|
||||||
/>
|
/>
|
||||||
|
<el-avatar v-else class="header-avatar" :style="{ backgroundColor: 'transparent' }">
|
||||||
|
<ModelIcon :name="modelIconName" class-name="model-icon" />
|
||||||
|
</el-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="operational">
|
<div class="operational">
|
||||||
@@ -76,6 +80,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MessageText from './MessageText.vue'
|
import MessageText from './MessageText.vue'
|
||||||
|
import ModelIcon from '../../models/ModelIcon.vue'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { copy } from '@/utils/common/index'
|
import { copy } from '@/utils/common/index'
|
||||||
import { useChat } from '../../useChat.js'
|
import { useChat } from '../../useChat.js'
|
||||||
@@ -85,7 +90,8 @@ const { setLoading, removeLoadingMessageInChat } = useChat()
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MessageText
|
MessageText,
|
||||||
|
ModelIcon
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
@@ -93,6 +99,10 @@ export default {
|
|||||||
default: () => {
|
default: () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
selectedModel: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
isTerminal: {
|
isTerminal: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -129,10 +139,8 @@ export default {
|
|||||||
? this.$i18n.t('ServerBusyRetry')
|
? this.$i18n.t('ServerBusyRetry')
|
||||||
: ''
|
: ''
|
||||||
},
|
},
|
||||||
chatUrl() {
|
modelIconName() {
|
||||||
return this.publicSettings.CHAT_AI_TYPE === 'gpt'
|
return (this.item?.message?.model || this.selectedModel || this.publicSettings.CHAT_AI_TYPE || '').toString()
|
||||||
? require('@/assets/img/chat.png')
|
|
||||||
: require('@/assets/img/deepSeek.png')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -171,11 +179,18 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
&::v-deep img {
|
&::v-deep img {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-icon {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
text() {
|
text() {
|
||||||
const value = this.message?.content || ''
|
const value = this.message?.content || ''
|
||||||
if (value && this.markdown) {
|
if (value && this.markdown) {
|
||||||
return this.markdown?.render(value)
|
return this.renderContentWithDetails(value)
|
||||||
}
|
}
|
||||||
return this.$xss.process(value)
|
return this.$xss.process(value)
|
||||||
}
|
}
|
||||||
@@ -120,6 +120,77 @@ export default {
|
|||||||
btn.addEventListener('click', callback)
|
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) {
|
removeBtnClickEvent(selector) {
|
||||||
const buttons = this.$refs.textRef.querySelectorAll(selector)
|
const buttons = this.$refs.textRef.querySelectorAll(selector)
|
||||||
buttons.forEach((btn) => {
|
buttons.forEach((btn) => {
|
||||||
@@ -258,4 +329,64 @@ export default {
|
|||||||
.loading-box span:nth-child(3) {
|
.loading-box span:nth-child(3) {
|
||||||
animation-delay: 0.49s;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
import Sidebar from './components/Sidebar/index.vue'
|
import Sidebar from './components/Sidebar/index.vue'
|
||||||
import Chat from './components/ChitChat/index.vue'
|
import Chat from './components/ChitChat/index.vue'
|
||||||
import { getInputFocus } from './useChat.js'
|
import { getInputFocus } from './useChat.js'
|
||||||
import { ws } from '@/utils/request'
|
|
||||||
import DrawerPanel from '@/components/Apps/DrawerPanel/index.vue'
|
import DrawerPanel from '@/components/Apps/DrawerPanel/index.vue'
|
||||||
import { ObjectLocalStorage } from '@/utils/common'
|
import { ObjectLocalStorage } from '@/utils/common'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
@@ -82,7 +81,8 @@ export default {
|
|||||||
height: '400px',
|
height: '400px',
|
||||||
expanded: false,
|
expanded: false,
|
||||||
clientOffset: {},
|
clientOffset: {},
|
||||||
currentTerminalContent: {}
|
currentTerminalContent: {},
|
||||||
|
initialized: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -123,11 +123,18 @@ export default {
|
|||||||
document.body.appendChild(script)
|
document.body.appendChild(script)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
initAssistant() {
|
||||||
|
if (this.initialized) return
|
||||||
|
this.initialized = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.component?.init()
|
||||||
|
})
|
||||||
|
},
|
||||||
handlePostMessage() {
|
handlePostMessage() {
|
||||||
window.addEventListener('message', (event) => {
|
window.addEventListener('message', (event) => {
|
||||||
if (event.data === 'show-chat-panel') {
|
if (event.data === 'show-chat-panel') {
|
||||||
this.$refs.drawer.show = true
|
this.$refs.drawer.show = true
|
||||||
this.initWebSocket()
|
this.initAssistant()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const msg = event.data
|
const msg = event.data
|
||||||
@@ -152,11 +159,6 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$refs.drawer.handleHeaderMoveUp(event)
|
this.$refs.drawer.handleHeaderMoveUp(event)
|
||||||
},
|
},
|
||||||
initWebSocket() {
|
|
||||||
if (!ws) {
|
|
||||||
this.$refs.component?.init()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onClose() {
|
onClose() {
|
||||||
this.$refs.drawer.show = false
|
this.$refs.drawer.show = false
|
||||||
},
|
},
|
||||||
@@ -170,7 +172,6 @@ export default {
|
|||||||
},
|
},
|
||||||
save_pannel_settings() {
|
save_pannel_settings() {
|
||||||
aiPannelLocalStorage.set('expanded', this.expanded)
|
aiPannelLocalStorage.set('expanded', this.expanded)
|
||||||
console.log('AI panel settings saved:', this.expanded)
|
|
||||||
},
|
},
|
||||||
updateExpandedState(expanded) {
|
updateExpandedState(expanded) {
|
||||||
this.expanded = expanded
|
this.expanded = expanded
|
||||||
@@ -184,8 +185,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onToggle(status) {
|
onToggle(status) {
|
||||||
this.initWebSocket()
|
|
||||||
if (status) {
|
if (status) {
|
||||||
|
this.initAssistant()
|
||||||
getInputFocus()
|
getInputFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 AssetTreeTable from '@/components/Apps/AssetTreeTable'
|
||||||
import { AccountInfoFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
import { AccountInfoFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||||
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
|
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
|
||||||
|
import { setUrlParam } from '@/utils/common/index'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GrantedAssets',
|
name: 'GrantedAssets',
|
||||||
@@ -34,7 +35,7 @@ export default {
|
|||||||
}
|
}
|
||||||
const initialUrl = vm.tableConfig.initialUrl
|
const initialUrl = vm.tableConfig.initialUrl
|
||||||
const nodeId = node.meta.data.id
|
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
|
vm.tableConfig.url = url
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -70,7 +71,7 @@ export default {
|
|||||||
showMenu: false,
|
showMenu: false,
|
||||||
showRefresh: true,
|
showRefresh: true,
|
||||||
showAssets: false,
|
showAssets: false,
|
||||||
showSearch: false,
|
showSearch: true,
|
||||||
url: this.tableUrl,
|
url: this.tableUrl,
|
||||||
// ?assets=0不显示资产. =1显示资产
|
// ?assets=0不显示资产. =1显示资产
|
||||||
treeUrl: this.treeUrl,
|
treeUrl: this.treeUrl,
|
||||||
@@ -78,6 +79,9 @@ export default {
|
|||||||
callback: {
|
callback: {
|
||||||
onSelected: (event, node) => vm.onSelected(node, vm),
|
onSelected: (event, node) => vm.onSelected(node, vm),
|
||||||
refresh: vm.refreshObjectAssetPermission
|
refresh: vm.refreshObjectAssetPermission
|
||||||
|
},
|
||||||
|
async: {
|
||||||
|
enable: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
|
|||||||
@@ -37,8 +37,12 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
headerActions: {
|
headerActions: {
|
||||||
hasExport: false,
|
hasExport: true,
|
||||||
hasImport: false,
|
hasImport: true,
|
||||||
|
importOptions: {
|
||||||
|
encryptFields: [''], // 这里不加密 password,''只是为了保证数组有值
|
||||||
|
canImportUpdate: false
|
||||||
|
},
|
||||||
hasCreate: true,
|
hasCreate: true,
|
||||||
hasSearch: true,
|
hasSearch: true,
|
||||||
hasRefresh: true,
|
hasRefresh: true,
|
||||||
|
|||||||
@@ -73,28 +73,28 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.help-dialog.dialog .el-dialog__footer {
|
::v-deep .help-dialog.dialog .el-dialog__footer {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.help-table {
|
.help-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep th, td {
|
::v-deep .help-table th,
|
||||||
height: 40px;
|
::v-deep .help-table td {
|
||||||
padding: 0 8px;
|
height: 40px;
|
||||||
text-align: left;
|
padding: 0 8px;
|
||||||
}
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep .item-td, .item-label {
|
::v-deep .help-table .item-td,
|
||||||
cursor: pointer;
|
::v-deep .help-table .item-label {
|
||||||
color: var(--color-primary);
|
cursor: pointer;
|
||||||
}
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -223,8 +223,21 @@ export default {
|
|||||||
const mapped = {}
|
const mapped = {}
|
||||||
Object.entries(errors || {}).forEach(([k, v]) => {
|
Object.entries(errors || {}).forEach(([k, v]) => {
|
||||||
let msg = v
|
let msg = v
|
||||||
if (Array.isArray(v)) msg = v.join('; ')
|
console.log(k, v)
|
||||||
else if (typeof v === 'object' && v !== null) msg = JSON.stringify(v)
|
// v是数组并且数组都是字符串,则拼接为字符串
|
||||||
|
if (Array.isArray(v) && v.every(item => typeof item === 'string')) msg = v.join('; ')
|
||||||
|
// 处理 [{"port":["请确保该值小于或者等于 65535。"]},{},{}] 这种情况
|
||||||
|
else if (Array.isArray(v) && v.every(item => _.isPlainObject(item))) {
|
||||||
|
const subMsg = []
|
||||||
|
v.forEach((subItem) => {
|
||||||
|
Object.values(subItem).forEach((subMsgArr) => {
|
||||||
|
if (Array.isArray(subMsgArr)) {
|
||||||
|
subMsg.push(...subMsgArr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
msg = subMsg.join(' ')
|
||||||
|
} else if (typeof v === 'object' && v !== null) msg = JSON.stringify(v)
|
||||||
mapped[k] = String(msg || '')
|
mapped[k] = String(msg || '')
|
||||||
})
|
})
|
||||||
this.serverErrors = mapped
|
this.serverErrors = mapped
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export default {
|
|||||||
},
|
},
|
||||||
labelWidth: {
|
labelWidth: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '25%'
|
default: '18.2%'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
:import-option="importOption"
|
:import-option="importOption"
|
||||||
:json-data="jsonData"
|
:json-data="jsonData"
|
||||||
:url="url"
|
:url="url"
|
||||||
|
v-bind="$attrs"
|
||||||
@cancel="cancelUpload"
|
@cancel="cancelUpload"
|
||||||
@finish="closeDialog"
|
@finish="closeDialog"
|
||||||
/>
|
/>
|
||||||
@@ -247,46 +248,46 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@import "~@/styles/variables";
|
@import "~@/styles/variables";
|
||||||
|
|
||||||
.error-msg {
|
.error-msg {
|
||||||
color: $--color-danger;
|
color: $--color-danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg.error-results {
|
||||||
|
background-color: #f3f3f4;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader ::v-deep .el-upload {
|
||||||
|
width: 100%;
|
||||||
|
//padding-right: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader ::v-deep .el-upload-dragger {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.importTableZone {
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
.importTable {
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-msg.error-results {
|
.tableFilter {
|
||||||
background-color: #f3f3f4;
|
padding-bottom: 10px;
|
||||||
max-height: 200px;
|
|
||||||
overflow: auto
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.file-uploader ::v-deep .el-upload {
|
.importTable ::v-deep .el-dialog__body {
|
||||||
width: 100%;
|
padding-bottom: 20px;
|
||||||
//padding-right: 150px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.file-uploader ::v-deep .el-upload-dragger {
|
.export-item {
|
||||||
width: 100%;
|
margin-left: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.importTableZone {
|
|
||||||
padding: 0 20px;
|
|
||||||
|
|
||||||
.importTable {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableFilter {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.importTable ::v-deep .el-dialog__body {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.export-item {
|
|
||||||
margin-left: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.export-item:first-child {
|
.export-item:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ export default {
|
|||||||
origin: {
|
origin: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
encryptFields: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -273,11 +277,15 @@ export default {
|
|||||||
}
|
}
|
||||||
return columns
|
return columns
|
||||||
},
|
},
|
||||||
|
getEncryptFields() {
|
||||||
|
const fromProp = Array.isArray(this.encryptFields) && this.encryptFields.length ? this.encryptFields : null
|
||||||
|
return fromProp || ['password', 'secret', 'private_key']
|
||||||
|
},
|
||||||
generateTableData(tableTitles, tableData) {
|
generateTableData(tableTitles, tableData) {
|
||||||
const totalData = []
|
const totalData = []
|
||||||
tableData.forEach(item => {
|
tableData.forEach(item => {
|
||||||
this.$set(item, '@status', 'pending')
|
this.$set(item, '@status', 'pending')
|
||||||
const encryptFields = ['password', 'secret', 'private_key']
|
const encryptFields = this.getEncryptFields()
|
||||||
for (const field of encryptFields) {
|
for (const field of encryptFields) {
|
||||||
if (item[field]) {
|
if (item[field]) {
|
||||||
item[field] = encryptPassword(item[field])
|
item[field] = encryptPassword(item[field])
|
||||||
|
|||||||
@@ -421,12 +421,8 @@ export default {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-z-tree {
|
.data-z-tree ::v-deep .icon {
|
||||||
::v-deep {
|
width: 10px;
|
||||||
.icon {
|
margin-right: 3px;
|
||||||
width: 10px;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,16 +1,67 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="treebox">
|
<div class="treebox">
|
||||||
<div v-if="treeSetting.showSearch">
|
<div v-if="treeSetting.showSearch" @click="focusTreeSearchInput">
|
||||||
<el-input
|
<el-input
|
||||||
v-show="showTreeSearch"
|
v-show="showTreeSearch"
|
||||||
|
ref="treeSearchInput"
|
||||||
v-model="treeSearchValue"
|
v-model="treeSearchValue"
|
||||||
:placeholder="$tc('Search')"
|
|
||||||
class="fixed-tree-search"
|
class="fixed-tree-search"
|
||||||
prefix-icon="fa fa-search"
|
:placeholder="treeSearchInputPlaceholder"
|
||||||
size="mini"
|
size="mini"
|
||||||
@input="treeSearchHandle"
|
@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">
|
<span slot="suffix">
|
||||||
<i
|
<i
|
||||||
class="el-icon-close"
|
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.css'
|
||||||
import '@/styles/ztree_icon.scss'
|
import '@/styles/ztree_icon.scss'
|
||||||
import axiosRetry from 'axios-retry'
|
import axiosRetry from 'axios-retry'
|
||||||
|
import { setUrlParam } from '@/utils/common'
|
||||||
|
|
||||||
const defaultObject = {
|
const defaultObject = {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -66,18 +118,52 @@ export default {
|
|||||||
rMenu: '',
|
rMenu: '',
|
||||||
init: false,
|
init: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
showTreeSearch: false,
|
showTreeSearch: true,
|
||||||
treeSearchValue: ''
|
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: {
|
computed: {
|
||||||
treeSetting() {
|
treeSetting() {
|
||||||
return this.setting
|
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() {
|
mounted() {
|
||||||
window.refresh = this.refresh
|
window.refresh = this.refresh
|
||||||
window.onSearch = this.onSearch
|
window.onSearch = this.onSearch
|
||||||
|
this.initTreeType()
|
||||||
|
this.initTreeSearchTypeOptions()
|
||||||
this.initTree().then(() => {
|
this.initTree().then(() => {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.updateTreeHeight()
|
this.updateTreeHeight()
|
||||||
@@ -90,6 +176,36 @@ export default {
|
|||||||
window.removeEventListener('resize', this.updateTreeHeight)
|
window.removeEventListener('resize', this.updateTreeHeight)
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
onMenuClick(menu) {
|
||||||
if (menu.disabled) {
|
if (menu.disabled) {
|
||||||
return
|
return
|
||||||
@@ -116,19 +232,14 @@ export default {
|
|||||||
const zTreeRect = tree.getBoundingClientRect()
|
const zTreeRect = tree.getBoundingClientRect()
|
||||||
tree.style.height = `calc(100vh - ${zTreeRect.top}px - 30px - 25px)`
|
tree.style.height = `calc(100vh - ${zTreeRect.top}px - 30px - 25px)`
|
||||||
}, 100),
|
}, 100),
|
||||||
async initTree(refresh = false) {
|
async initTree(refresh = false, iTreeUrl = '') {
|
||||||
const vm = this
|
const vm = this
|
||||||
let treeUrl
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
if (refresh && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 &&
|
let treeUrl = iTreeUrl
|
||||||
this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1
|
if (!treeUrl) {
|
||||||
) {
|
|
||||||
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1)
|
|
||||||
? `${this.treeSetting.treeUrl}?rebuild_tree=1`
|
|
||||||
: `${this.treeSetting.treeUrl}&rebuild_tree=1`
|
|
||||||
} else {
|
|
||||||
treeUrl = this.treeSetting.treeUrl
|
treeUrl = this.treeSetting.treeUrl
|
||||||
}
|
}
|
||||||
|
treeUrl = setUrlParam(treeUrl, 'tree_type', this.treeType)
|
||||||
|
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
$.fn.zTree.destroy(this.iZTreeID)
|
$.fn.zTree.destroy(this.iZTreeID)
|
||||||
@@ -221,25 +332,31 @@ export default {
|
|||||||
searchInput.oninput = e => this.treeSearchHandle((e.target.value || ''))
|
searchInput.oninput = e => this.treeSearchHandle((e.target.value || ''))
|
||||||
},
|
},
|
||||||
treeSearchHandle: _.debounce(function(value) {
|
treeSearchHandle: _.debounce(function(value) {
|
||||||
if (this.treeSetting.async.enable) {
|
if (this.treeSetting.async?.enable) {
|
||||||
this.filterAssetsServer(value)
|
this.searchFromServer(value)
|
||||||
} else {
|
} else {
|
||||||
this.filterTree(value)
|
this.searchFromLocal(value)
|
||||||
}
|
}
|
||||||
}, 600),
|
}, 600),
|
||||||
getCheckedNodes: function() {
|
getCheckedNodes: function() {
|
||||||
return this.zTree.getCheckedNodes(true)
|
return this.zTree.getCheckedNodes(true)
|
||||||
},
|
},
|
||||||
|
|
||||||
recurseParent(node) {
|
recurseParent(node) {
|
||||||
const parentNode = node.getParentNode()
|
const parentNode = node.getParentNode()
|
||||||
if (parentNode && parentNode.pId) {
|
if (!parentNode) {
|
||||||
return [parentNode, ...this.recurseParent(parentNode)]
|
|
||||||
} else if (parentNode) {
|
|
||||||
return [parentNode]
|
|
||||||
} else {
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
const allParents = []
|
||||||
|
if (parentNode) {
|
||||||
|
allParents.push(parentNode)
|
||||||
|
if (parentNode.pId) {
|
||||||
|
allParents.push(...this.recurseParent(parentNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allParents
|
||||||
},
|
},
|
||||||
|
|
||||||
recurseChildren(node) {
|
recurseChildren(node) {
|
||||||
if (!node.isParent) {
|
if (!node.isParent) {
|
||||||
return []
|
return []
|
||||||
@@ -248,49 +365,27 @@ export default {
|
|||||||
if (!children) {
|
if (!children) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
let allChildren = []
|
const allChildren = []
|
||||||
children.forEach((n) => {
|
children.forEach((n) => {
|
||||||
allChildren = [...children, ...this.recurseChildren(n)]
|
allChildren.push(n)
|
||||||
|
allChildren.push(...this.recurseChildren(n))
|
||||||
})
|
})
|
||||||
return allChildren
|
return allChildren
|
||||||
},
|
},
|
||||||
groupBy(array, filter) {
|
|
||||||
const groups = {}
|
searchFromLocal(keyword, tree = this.zTree) {
|
||||||
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) {
|
|
||||||
if (!this.zTree) return
|
if (!this.zTree) return
|
||||||
|
|
||||||
const searchNode = tree.getNodesByFilter((node) => node.id === 'search')
|
const searchNode = tree.getNodesByFilter((node) => node.id === 'search')
|
||||||
if (searchNode) tree.removeNode(searchNode[0])
|
if (searchNode) tree.removeNode(searchNode[0])
|
||||||
const nodes = tree.transformToArray(tree.getNodes())
|
|
||||||
|
const allNodes = tree.transformToArray(tree.getNodes())
|
||||||
if (!keyword) {
|
if (!keyword) {
|
||||||
tree.showNodes(nodes)
|
tree.showNodes(allNodes)
|
||||||
|
tree.expandAll(false)
|
||||||
return
|
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) => {
|
const matchedNodes = tree.getNodesByFilter((node) => {
|
||||||
return node.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
return node.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
||||||
})
|
})
|
||||||
@@ -300,67 +395,54 @@ export default {
|
|||||||
const assetsAmount = matchedNodes.length
|
const assetsAmount = matchedNodes.length
|
||||||
name = `${name} (${assetsAmount})`
|
name = `${name} (${assetsAmount})`
|
||||||
const newNode = { id: 'search', name: name, isParent: false, open: false }
|
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) => {
|
matchedNodes.forEach((node) => {
|
||||||
const parents = this.recurseParent(node)
|
const parents = this.recurseParent(node)
|
||||||
const children = this.recurseChildren(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
|
shouldExpandNodes.push(...parents)
|
||||||
tree.hideNodes(nodes)
|
// 应该折叠匹配节点的子孙节点
|
||||||
|
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)
|
tree.showNodes(shouldShow)
|
||||||
for (const node of shouldShow) {
|
// 展开应该展开的节点
|
||||||
if (node.isParent) {
|
for (const node of shouldExpandNodes) {
|
||||||
tree.expandNode(node, true)
|
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
|
searchFromServer(keyword) {
|
||||||
const filterField = treeUrl.includes('?') ? `&search=${keyword}` : `?search=${keyword}`
|
// 直接用搜索 API 返回的数据重新初始化树
|
||||||
if (treeUrl.indexOf('assets/nodes/children/tree') > -1) {
|
const treeUrl = this.treeSetting.searchUrl ? this.treeSetting.searchUrl : this.treeSetting.treeUrl
|
||||||
treeUrl = treeUrl + '&all=all'
|
const searchTypeKey = this.treeSearchTypeOptions[this.treeSearchType]?.search_key || 'search'
|
||||||
}
|
const searchUrl = setUrlParam(treeUrl, searchTypeKey, keyword)
|
||||||
const searchUrl = `${treeUrl}${filterField}`
|
this.initTree(true, searchUrl)
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,12 +706,19 @@ div.rMenu li {
|
|||||||
|
|
||||||
.fixed-tree-search {
|
.fixed-tree-search {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-within {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
& ::v-deep .el-input__inner {
|
& ::v-deep .el-input__inner {
|
||||||
border-radius: 4px;
|
border: none;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
padding-right: 32px;
|
padding-right: 32px;
|
||||||
color: var(--color-text-primary)
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
& ::v-deep .el-input__suffix {
|
& ::v-deep .el-input__suffix {
|
||||||
@@ -653,6 +742,37 @@ div.rMenu li {
|
|||||||
& ::v-deep .el-input__suffix-inner {
|
& ::v-deep .el-input__suffix-inner {
|
||||||
line-height: 30px;
|
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 {
|
.icon-refresh {
|
||||||
|
|||||||
@@ -78,6 +78,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -65,6 +65,17 @@ export default {
|
|||||||
'usingOrgs',
|
'usingOrgs',
|
||||||
'currentViewRoute'
|
'currentViewRoute'
|
||||||
]),
|
]),
|
||||||
|
currentOrgDisplayName() {
|
||||||
|
const currentOrgId = this.currentOrg?.id
|
||||||
|
if (!currentOrgId) {
|
||||||
|
return this.$tc('Select')
|
||||||
|
}
|
||||||
|
const matchedOrg = this.usingOrgs.find(item => item.id === currentOrgId)
|
||||||
|
if (matchedOrg?.name) {
|
||||||
|
return matchedOrg.name
|
||||||
|
}
|
||||||
|
return this.currentOrg.name || this.$tc('Select')
|
||||||
|
},
|
||||||
orgActionsGroup() {
|
orgActionsGroup() {
|
||||||
const orgActions = {
|
const orgActions = {
|
||||||
label: this.$t('OrganizationList'),
|
label: this.$t('OrganizationList'),
|
||||||
@@ -110,11 +121,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentOrg: {
|
currentOrgDisplayName() {
|
||||||
handler() {
|
this.updateWidth()
|
||||||
this.updateWidth()
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -133,8 +141,9 @@ export default {
|
|||||||
tempSpan.style.fontWeight = 'normal'
|
tempSpan.style.fontWeight = 'normal'
|
||||||
tempSpan.style.letterSpacing = 'normal'
|
tempSpan.style.letterSpacing = 'normal'
|
||||||
|
|
||||||
// 获取当前组织名称
|
// 获取当前组织显示名称
|
||||||
const orgName = this.currentOrg.name || this.$tc('Select')
|
const orgName = this.currentOrgDisplayName
|
||||||
|
|
||||||
tempSpan.textContent = orgName
|
tempSpan.textContent = orgName
|
||||||
document.body.appendChild(tempSpan)
|
document.body.appendChild(tempSpan)
|
||||||
|
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-input__prefix .el-input__icon{
|
.el-input__prefix .el-input__icon {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
}
|
}
|
||||||
@@ -402,11 +402,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
/* 搜索模态框全局样式 */
|
/* 搜索模态框全局样式 */
|
||||||
.search-modal {
|
::v-deep .search-modal {
|
||||||
&.el-dialog {
|
&.el-dialog {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@@ -427,17 +425,17 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body .v-modal {
|
::v-deep body .v-modal {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-modal-content {
|
::v-deep .search-modal-content {
|
||||||
height: 70vh;
|
height: 70vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-wrapper {
|
::v-deep .search-input-wrapper {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
// background: #fff;
|
// background: #fff;
|
||||||
@@ -451,7 +449,7 @@ body .v-modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-results {
|
::v-deep .search-results {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -476,7 +474,7 @@ body .v-modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
::v-deep .section-title {
|
||||||
padding: 12px 24px 6px;
|
padding: 12px 24px 6px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -505,12 +503,12 @@ body .v-modal {
|
|||||||
|
|
||||||
.clear-icon {
|
.clear-icon {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list {
|
::v-deep .list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -581,15 +579,15 @@ body .v-modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading,
|
::v-deep .loading,
|
||||||
.empty {
|
::v-deep .empty {
|
||||||
padding: 32px 24px;
|
padding: 32px 24px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section.placeholder {
|
::v-deep .section.placeholder {
|
||||||
padding: 32px 24px;
|
padding: 32px 24px;
|
||||||
|
|
||||||
.placeholder-content {
|
.placeholder-content {
|
||||||
@@ -637,4 +635,3 @@ body .v-modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -167,8 +167,7 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
.page.no-title {
|
.page.no-title {
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.page-submenu .el-tabs__header {
|
.page-submenu .el-tabs__header {
|
||||||
@@ -199,6 +198,14 @@ export default {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tabs__nav-next {
|
.el-tabs__nav-next {
|
||||||
|
|||||||
@@ -276,10 +276,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss">
|
// reset element css of el-icon-close
|
||||||
//reset element css of el-icon-close
|
|
||||||
.tags-view-wrapper {
|
.tags-view-wrapper {
|
||||||
.tags-view-item {
|
.tags-view-item {
|
||||||
.el-icon-close {
|
.el-icon-close {
|
||||||
|
|||||||
@@ -149,6 +149,11 @@ export function getErrorResponseMsg(error) {
|
|||||||
.join('; ')
|
.join('; ')
|
||||||
} else if (typeof data === 'string') {
|
} else if (typeof data === 'string') {
|
||||||
return data
|
return data
|
||||||
|
} else if (_.isPlainObject(data)) {
|
||||||
|
return Object.values(data)
|
||||||
|
.map(item => getErrorResponseMsg(item))
|
||||||
|
.filter(i => i)
|
||||||
|
.join('; ')
|
||||||
} else {
|
} else {
|
||||||
msg = error.toString()
|
msg = error.toString()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,11 +144,6 @@ export default {
|
|||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
if (!this.$store.getters.hasValidLicense) {
|
|
||||||
delete this.fields[3]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
handleAfterGetRemoteMeta(meta) {
|
handleAfterGetRemoteMeta(meta) {
|
||||||
const needSetOptionFields = [
|
const needSetOptionFields = [
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ export default {
|
|||||||
asset: ''
|
asset: ''
|
||||||
},
|
},
|
||||||
treeSetting: {
|
treeSetting: {
|
||||||
showMenu: true,
|
showMenu: (node) => {
|
||||||
|
return node?.meta?.type === 'asset'
|
||||||
|
},
|
||||||
showRefresh: true,
|
showRefresh: true,
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
showAssets: true,
|
showAssets: true,
|
||||||
@@ -55,7 +57,7 @@ export default {
|
|||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
id: 'check',
|
id: 'check',
|
||||||
name: this.$t('Check'),
|
name: this.$t('RiskDetection'),
|
||||||
icon: 'scan',
|
icon: 'scan',
|
||||||
callback: (node) => {
|
callback: (node) => {
|
||||||
vm.detectDialog.asset = node.id
|
vm.detectDialog.asset = node.id
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
class="risk-review-drawer"
|
class="risk-review-drawer"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
direction="rtl"
|
direction="rtl"
|
||||||
style="z-index: 999"
|
|
||||||
>
|
>
|
||||||
<div class="drawer-container">
|
<div class="drawer-container">
|
||||||
<div class="drawer-body">
|
<div class="drawer-body">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default {
|
|||||||
updateSuccessNextRoute: this.updateSuccessNextRoute,
|
updateSuccessNextRoute: this.updateSuccessNextRoute,
|
||||||
hasDetailInMsg: false,
|
hasDetailInMsg: false,
|
||||||
fields: [
|
fields: [
|
||||||
[this.$t('Basic'), ['name', 'address', 'platform', 'nodes']],
|
[this.$t('Basic'), ['name', 'address', 'platform', 'node']],
|
||||||
[this.$t('Protocol'), ['protocols']],
|
[this.$t('Protocol'), ['protocols']],
|
||||||
[this.$t('Account'), ['accounts']],
|
[this.$t('Account'), ['accounts']],
|
||||||
[this.$t('Other'), ['directory_services', 'zone', 'labels', 'is_active', 'comment']]
|
[this.$t('Other'), ['directory_services', 'zone', 'labels', 'is_active', 'comment']]
|
||||||
@@ -73,8 +73,8 @@ export default {
|
|||||||
const values = _.cloneDeep(validValues)
|
const values = _.cloneDeep(validValues)
|
||||||
const submitMethod = id ? 'put' : 'post'
|
const submitMethod = id ? 'put' : 'post'
|
||||||
|
|
||||||
if (values.nodes && values.nodes.length === 0) {
|
if (!values.node) {
|
||||||
delete values['nodes']
|
delete values['node']
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitMethod === 'put') {
|
if (submitMethod === 'put') {
|
||||||
@@ -143,15 +143,14 @@ export default {
|
|||||||
},
|
},
|
||||||
async setInitial() {
|
async setInitial() {
|
||||||
const { defaultConfig } = this
|
const { defaultConfig } = this
|
||||||
const { node } = this.$route.query
|
const { node_id } = this.$route.query
|
||||||
const nodesInitial = node ? [node] : []
|
|
||||||
const platformId = this.platformID || 'Linux'
|
const platformId = this.platformID || 'Linux'
|
||||||
const url = `/api/v1/assets/platforms/${platformId}/`
|
const url = `/api/v1/assets/platforms/${platformId}/`
|
||||||
this.platform = await this.$axios.get(url)
|
this.platform = await this.$axios.get(url)
|
||||||
const initial = {
|
const initial = {
|
||||||
labels: [],
|
labels: [],
|
||||||
is_active: true,
|
is_active: true,
|
||||||
nodes: nodesInitial,
|
node: node_id,
|
||||||
platform: parseInt(this.platform.id),
|
platform: parseInt(this.platform.id),
|
||||||
protocols: []
|
protocols: []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export default {
|
|||||||
url: '/api/v1/assets/assets/',
|
url: '/api/v1/assets/assets/',
|
||||||
showMenu: !this.$store.getters.currentOrgIsRoot,
|
showMenu: !this.$store.getters.currentOrgIsRoot,
|
||||||
showDefaultMenu: true,
|
showDefaultMenu: true,
|
||||||
|
async: {
|
||||||
|
enable: false
|
||||||
|
},
|
||||||
menu: [
|
menu: [
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -143,7 +143,20 @@ export default {
|
|||||||
disabled: !this.canEdit
|
disabled: !this.canEdit
|
||||||
},
|
},
|
||||||
callbacks: Object.freeze({
|
callbacks: Object.freeze({
|
||||||
click: () => {
|
click: async () => {
|
||||||
|
try {
|
||||||
|
await this.$confirm(
|
||||||
|
this.$t('overwriteProtocolsAndPortsMsg'),
|
||||||
|
this.$t('Confirm'),
|
||||||
|
{
|
||||||
|
confirmButtonText: this.$t('Confirm'),
|
||||||
|
cancelButtonText: this.$t('Cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const data = { platform_id: this.object.id }
|
const data = { platform_id: this.object.id }
|
||||||
this.$axios.post(
|
this.$axios.post(
|
||||||
'/api/v1/assets/assets/sync-platform-protocols/', data).then(res => {
|
'/api/v1/assets/assets/sync-platform-protocols/', data).then(res => {
|
||||||
|
|||||||
@@ -148,9 +148,10 @@ export const assetFieldsMeta = (vm, category, type) => {
|
|||||||
return vm.platform.ds_enabled === false
|
return vm.platform.ds_enabled === false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
nodes: {
|
node: {
|
||||||
rules: [rules.RequiredChange],
|
rules: [rules.RequiredChange],
|
||||||
el: {
|
el: {
|
||||||
|
multiple: false,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/api/v1/assets/nodes/',
|
url: '/api/v1/assets/nodes/',
|
||||||
transformOption: item => {
|
transformOption: item => {
|
||||||
|
|||||||
@@ -125,17 +125,21 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
/* 修复input 背景不协调 和光标变色 */
|
/* 修复input 背景不协调 和光标变色 */
|
||||||
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
|
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
|
||||||
|
|
||||||
$bg:#283443;
|
$input-bg: #283443;
|
||||||
$light_gray:#fff;
|
$input-light-gray: #fff;
|
||||||
$cursor: #fff;
|
$input-cursor: #fff;
|
||||||
|
|
||||||
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
|
$login-bg: #2d3a4b;
|
||||||
|
$login-dark-gray: #889aa4;
|
||||||
|
$login-light-gray: #eee;
|
||||||
|
|
||||||
|
@supports (-webkit-mask: none) and (not (cater-color: $input-cursor)) {
|
||||||
.login-container .el-input input {
|
.login-container .el-input input {
|
||||||
color: $cursor;
|
color: $input-cursor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,17 +151,17 @@ $cursor: #fff;
|
|||||||
|
|
||||||
input {
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0px;
|
border: 0;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
border-radius: 0px;
|
border-radius: 0;
|
||||||
padding: 12px 5px 12px 15px;
|
padding: 12px 5px 12px 15px;
|
||||||
color: $light_gray;
|
color: $input-light-gray;
|
||||||
height: 47px;
|
height: 47px;
|
||||||
caret-color: $cursor;
|
caret-color: $input-cursor;
|
||||||
|
|
||||||
&:-webkit-autofill {
|
&:-webkit-autofill {
|
||||||
box-shadow: 0 0 0px 1000px $bg inset !important;
|
box-shadow: 0 0 0 1000px $input-bg inset !important;
|
||||||
-webkit-text-fill-color: $cursor !important;
|
-webkit-text-fill-color: $input-cursor !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,17 +173,11 @@ $cursor: #fff;
|
|||||||
color: #454545;
|
color: #454545;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$bg:#2d3a4b;
|
|
||||||
$dark_gray:#889aa4;
|
|
||||||
$light_gray:#eee;
|
|
||||||
|
|
||||||
.login-container {
|
.login-container {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: $bg;
|
background-color: $login-bg;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
@@ -205,7 +203,7 @@ $light_gray:#eee;
|
|||||||
|
|
||||||
.svg-container {
|
.svg-container {
|
||||||
padding: 6px 5px 6px 15px;
|
padding: 6px 5px 6px 15px;
|
||||||
color: $dark_gray;
|
color: $login-dark-gray;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -216,7 +214,7 @@ $light_gray:#eee;
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
color: $light_gray;
|
color: $login-light-gray;
|
||||||
margin: 0px auto 40px auto;
|
margin: 0px auto 40px auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -228,7 +226,7 @@ $light_gray:#eee;
|
|||||||
right: 10px;
|
right: 10px;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: $dark_gray;
|
color: $login-dark-gray;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -515,6 +515,8 @@ export default {
|
|||||||
this.setBtn()
|
this.setBtn()
|
||||||
this.selectAssets = assets
|
this.selectAssets = assets
|
||||||
this.selectNodes = nodes
|
this.selectNodes = nodes
|
||||||
|
}).catch(() => {
|
||||||
|
this.lastRequestPayload = null
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
viewConfirmRunAssets() {
|
viewConfirmRunAssets() {
|
||||||
@@ -525,8 +527,8 @@ export default {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
this.xterm.write(
|
this.xterm.write(
|
||||||
'\x1b[31m' +
|
'\x1b[31m' +
|
||||||
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) +
|
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) +
|
||||||
'\x1b[0m'
|
'\x1b[0m'
|
||||||
)
|
)
|
||||||
this.xterm.write(this.wrapperError(''))
|
this.xterm.write(this.wrapperError(''))
|
||||||
this.getTaskStatus()
|
this.getTaskStatus()
|
||||||
|
|||||||
@@ -67,28 +67,28 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.help-dialog.dialog .el-dialog__footer {
|
::v-deep .help-dialog.dialog .el-dialog__footer {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.help-table {
|
.help-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep th, td {
|
::v-deep .help-table th,
|
||||||
height: 40px;
|
::v-deep .help-table td {
|
||||||
padding: 0 8px;
|
height: 40px;
|
||||||
text-align: left;
|
padding: 0 8px;
|
||||||
}
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep .item-td, .item-label {
|
::v-deep .help-table .item-td,
|
||||||
cursor: pointer;
|
::v-deep .help-table .item-label {
|
||||||
color: var(--color-primary);
|
cursor: pointer;
|
||||||
}
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,66 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<TwoCol>
|
<TwoCol>
|
||||||
<template>
|
<template>
|
||||||
<AutoDetailCard
|
<AutoDetailCard :excludes="excludes" :fields="detailFields" :object="object" :url="url" />
|
||||||
:excludes="excludes"
|
|
||||||
:fields="detailFields"
|
|
||||||
:object="object"
|
|
||||||
:url="url"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="hasSummary" #right class="detail-right-quick-actions">
|
<template v-if="hasSummary" #right class="detail-right-quick-actions">
|
||||||
<IBox
|
<IBox
|
||||||
v-if="object.summary.ok"
|
v-if="object.summary.ok"
|
||||||
:title="`${$tc('SuccessAsset')} (${object.summary.ok.length})` "
|
:title="`${$tc('SuccessAsset')} (${object.summary.ok.length})`"
|
||||||
type="success"
|
type="success"
|
||||||
>
|
>
|
||||||
<el-collapse>
|
<el-collapse>
|
||||||
<el-collapse-item
|
<el-collapse-item
|
||||||
v-for="(item,index) in object.summary.ok"
|
v-for="(item, index) in object.summary.ok"
|
||||||
:key="index"
|
:key="index"
|
||||||
:name="index"
|
:name="index"
|
||||||
:title="item"
|
|
||||||
disabled
|
disabled
|
||||||
/>
|
>
|
||||||
|
<template #title>
|
||||||
|
<el-tooltip :content="item" placement="top" class="text-overflow">
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</IBox>
|
</IBox>
|
||||||
<IBox
|
<IBox
|
||||||
v-if="object.summary.excludes"
|
v-if="object.summary.excludes"
|
||||||
:title="`${$tc('ExcludeAsset')} (${Object.keys(object.summary.excludes).length})` "
|
:title="`${$tc('ExcludeAsset')} (${Object.keys(object.summary.excludes).length})`"
|
||||||
type="warning"
|
type="warning"
|
||||||
>
|
>
|
||||||
<el-collapse>
|
<el-collapse>
|
||||||
<el-collapse-item
|
<el-collapse-item
|
||||||
v-for="(val,key,index) in object.summary.excludes"
|
v-for="(val, key, index) in object.summary.excludes"
|
||||||
:key="index"
|
:key="index"
|
||||||
:name="index"
|
:name="index"
|
||||||
:title="key"
|
|
||||||
>
|
>
|
||||||
|
<template #title>
|
||||||
|
<el-tooltip :content="key" placement="top" class="text-overflow">
|
||||||
|
<span>{{ key }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div>{{ $tc('Reason') }}: {{ val }}</div>
|
<div>{{ $tc('Reason') }}: {{ val }}</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</IBox>
|
</IBox>
|
||||||
<IBox
|
<IBox
|
||||||
v-if="object.summary.failures"
|
v-if="object.summary.failures"
|
||||||
:title="`${$tc('FailedAsset')} (${Object.keys(Object.assign(object.summary.failures,object.summary.dark)).length})` "
|
:title="`${$tc('FailedAsset')} (${Object.keys(Object.assign(object.summary.failures, object.summary.dark)).length})`"
|
||||||
type="danger"
|
type="danger"
|
||||||
>
|
>
|
||||||
<el-collapse>
|
<el-collapse>
|
||||||
<el-collapse-item
|
<el-collapse-item
|
||||||
v-for="(val,key,index) in Object.assign(object.summary.failures,object.summary.dark)"
|
v-for="(val, key, index) in Object.assign(object.summary.failures, object.summary.dark)"
|
||||||
:key="index"
|
:key="index"
|
||||||
:name="index"
|
:name="index"
|
||||||
:title="key"
|
|
||||||
>
|
>
|
||||||
|
<template #title>
|
||||||
|
<el-tooltip :content="key" placement="top" class="text-overflow">
|
||||||
|
<span>{{ key }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div>{{ $tc('Reason') }}: {{ val }}</div>
|
<div>{{ $tc('Reason') }}: {{ val }}</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</IBox>
|
</IBox>
|
||||||
<IBox
|
<IBox v-if="object.summary.error" :title="$tc('SystemError')" type="danger">
|
||||||
v-if="object.summary.error"
|
|
||||||
:title="$tc('SystemError') "
|
|
||||||
type="danger"
|
|
||||||
>
|
|
||||||
{{ object.summary.error }}
|
{{ object.summary.error }}
|
||||||
</IBox>
|
</IBox>
|
||||||
</template>
|
</template>
|
||||||
@@ -86,11 +92,10 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
excludes: [
|
excludes: ['job', 'parameters', 'summary', 'task_id', 'timedelta'],
|
||||||
'job', 'parameters', 'summary', 'task_id', 'timedelta'
|
|
||||||
],
|
|
||||||
detailFields: [
|
detailFields: [
|
||||||
'task_id', 'time_cost',
|
'task_id',
|
||||||
|
'time_cost',
|
||||||
{
|
{
|
||||||
key: this.$t('IsFinished'),
|
key: this.$t('IsFinished'),
|
||||||
value: this.object.is_finished ? this.$t('Yes') : this.$t('No')
|
value: this.object.is_finished ? this.$t('Yes') : this.$t('No')
|
||||||
@@ -99,8 +104,12 @@ export default {
|
|||||||
key: this.$t('IsSuccess'),
|
key: this.$t('IsSuccess'),
|
||||||
value: this.object.is_success ? this.$t('Yes') : this.$t('No')
|
value: this.object.is_success ? this.$t('Yes') : this.$t('No')
|
||||||
},
|
},
|
||||||
'job_type', 'material', 'org_name',
|
'job_type',
|
||||||
'date_start', 'date_finished', 'date_created'
|
'material',
|
||||||
|
'org_name',
|
||||||
|
'date_start',
|
||||||
|
'date_finished',
|
||||||
|
'date_created'
|
||||||
],
|
],
|
||||||
url: `/api/v1/ops/job-executions/${this.object.id}/`
|
url: `/api/v1/ops/job-executions/${this.object.id}/`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -408,7 +408,6 @@ export default {
|
|||||||
createJob(data).then(res => {
|
createJob(data).then(res => {
|
||||||
this.progressLength = 0
|
this.progressLength = 0
|
||||||
this.executionInfo.timeCost = 0
|
this.executionInfo.timeCost = 0
|
||||||
this.showProgress = true
|
|
||||||
this.speedText = ''
|
this.speedText = ''
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
const start = Date.now()
|
const start = Date.now()
|
||||||
@@ -436,13 +435,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
this.showProgress = true
|
||||||
this.executionInfo.status = 'running'
|
this.executionInfo.status = 'running'
|
||||||
this.currentTaskId = res.task_id
|
this.currentTaskId = res.task_id
|
||||||
this.xtermConfig = { taskId: this.currentTaskId, type: 'shortcut_cmd' }
|
this.xtermConfig = { taskId: this.currentTaskId, type: 'shortcut_cmd' }
|
||||||
this.setCostTimeInterval()
|
this.setCostTimeInterval()
|
||||||
this.writeExecutionOutput()
|
this.writeExecutionOutput()
|
||||||
}).catch((error) => {
|
}).catch(() => {
|
||||||
this.$message.error(this.$tc('Error'), error)
|
|
||||||
this.execute_stop()
|
this.execute_stop()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -224,11 +224,12 @@ export default {
|
|||||||
title: this.$t('ExecuteAfterSaving'),
|
title: this.$t('ExecuteAfterSaving'),
|
||||||
callback: (value, form, btn) => {
|
callback: (value, form, btn) => {
|
||||||
form.value.run_after_save = true
|
form.value.run_after_save = true
|
||||||
const parameters = form.value.variable.reduce((acc, item) => {
|
if (form.value?.variable) {
|
||||||
acc[item.var_name] = item.default_value || ''
|
form.value['parameters'] = form.value.variable.reduce((acc, item) => {
|
||||||
return acc
|
acc[item.var_name] = item.default_value || ''
|
||||||
}, {})
|
return acc
|
||||||
form.value['parameters'] = parameters
|
}, {})
|
||||||
|
}
|
||||||
this.submitForm(form, btn)
|
this.submitForm(form, btn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ export default {
|
|||||||
type: 'input',
|
type: 'input',
|
||||||
component: AccountFormatter,
|
component: AccountFormatter,
|
||||||
el: {
|
el: {
|
||||||
|
enableExcludeAccounts: true,
|
||||||
|
enableNoneAccount: true,
|
||||||
assets: [],
|
assets: [],
|
||||||
nodes: []
|
nodes: []
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-radio-group v-model="realRadioSelected" @input="handleRadioChanged">
|
<el-radio-group v-model="realRadioSelected" @input="handleRadioChanged">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="(i) in realChoices"
|
v-for="(i) in iRealChoices"
|
||||||
:key="i.label"
|
:key="i.label"
|
||||||
:disabled="i.disabled"
|
:disabled="i.disabled"
|
||||||
:label="i.value"
|
:label="i.value"
|
||||||
@@ -125,6 +125,14 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
enableNoneAccount: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
enableExcludeAccounts: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
addTemplateHelpText: {
|
addTemplateHelpText: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
@@ -185,6 +193,18 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
virtualAccount() {
|
virtualAccount() {
|
||||||
return virtualAccount
|
return virtualAccount
|
||||||
|
},
|
||||||
|
iRealChoices: {
|
||||||
|
get() {
|
||||||
|
let choices = this.realChoices.slice()
|
||||||
|
if (!this.enableNoneAccount) {
|
||||||
|
choices = choices.filter(i => i.value !== NoneAccount)
|
||||||
|
}
|
||||||
|
if (!this.enableExcludeAccounts) {
|
||||||
|
choices = choices.filter(i => i.value !== ExcludeAccount)
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -32,6 +32,22 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
passwordMenuDisabled() {
|
||||||
|
return this.$store.state.users.profile.source.value !== 'local'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
passwordMenuDisabled: {
|
||||||
|
immediate: true,
|
||||||
|
handler(disabled) {
|
||||||
|
this.config.submenu = this.getSubmenu()
|
||||||
|
if (disabled && this.config.activeMenu === 'Password') {
|
||||||
|
this.config.activeMenu = 'SSHKeyList'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getSubmenu() {
|
getSubmenu() {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -222,13 +222,11 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.el-select-dropdown.select-org-dropdown {
|
::v-deep .el-select-dropdown.select-org-dropdown {
|
||||||
max-width: 300px !important;
|
max-width: 300px !important;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.org-select {
|
.org-select {
|
||||||
float: left;
|
float: left;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
@@ -245,5 +243,4 @@ export default {
|
|||||||
width: 180px !important;
|
width: 180px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import { GenericCreateUpdateForm } from '@/layout/components'
|
import { GenericCreateUpdateForm } from '@/layout/components'
|
||||||
import IBox from '@/components/Common/IBox/index.vue'
|
import IBox from '@/components/Common/IBox/index.vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import ChatProvidersField from './components/ChatProvidersField.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -17,106 +18,54 @@ export default {
|
|||||||
GenericCreateUpdateForm
|
GenericCreateUpdateForm
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const vm = this
|
const hasProviders = (formValue) => {
|
||||||
|
const providers = formValue?.CHAT_AI_PROVIDERS?.providers || []
|
||||||
|
return Array.isArray(providers) && providers.length > 0
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
url: '/api/v1/settings/setting/?category=chat',
|
url: '/api/v1/settings/setting/?category=chat',
|
||||||
hasReset: false,
|
hasReset: false,
|
||||||
moreButtons: [
|
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'],
|
encryptedFields: ['VAULT_HCP_TOKEN'],
|
||||||
fields: [
|
fields: [
|
||||||
'CHAT_AI_ENABLED',
|
'CHAT_AI_ENABLED',
|
||||||
'CHAT_AI_METHOD',
|
'CHAT_AI_METHOD',
|
||||||
'CHAT_AI_EMBED_URL',
|
'CHAT_AI_PROVIDERS',
|
||||||
'CHAT_AI_TYPE',
|
'CHAT_AI_EMBED_URL'
|
||||||
'DEEPSEEK_BASE_URL',
|
|
||||||
'DEEPSEEK_API_KEY',
|
|
||||||
'DEEPSEEK_PROXY',
|
|
||||||
'DEEPSEEK_MODEL',
|
|
||||||
'GPT_BASE_URL',
|
|
||||||
'GPT_API_KEY',
|
|
||||||
'GPT_PROXY',
|
|
||||||
'GPT_MODEL'
|
|
||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
CHAT_AI_TYPE: {
|
CHAT_AI_TYPE: {
|
||||||
hidden: (formValue) => {
|
hidden: (formValue) => {
|
||||||
return formValue.CHAT_AI_METHOD !== 'api'
|
return formValue.CHAT_AI_METHOD !== 'api' || hasProviders(formValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GPT_BASE_URL: {
|
CHAT_AI_PROVIDERS: {
|
||||||
el: {
|
component: ChatProvidersField,
|
||||||
autocomplete: 'new-password'
|
hidden: (formValue) => formValue.CHAT_AI_METHOD !== 'api'
|
||||||
},
|
|
||||||
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_EMBED_URL: {
|
CHAT_AI_EMBED_URL: {
|
||||||
hidden: (formValue) => formValue.CHAT_AI_METHOD !== 'embed'
|
hidden: (formValue) => formValue.CHAT_AI_METHOD !== 'embed'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
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() {
|
submitMethod() {
|
||||||
return 'patch'
|
return 'patch'
|
||||||
}
|
}
|
||||||
|
|||||||
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>
|
||||||
@@ -73,28 +73,28 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.help-dialog.dialog .el-dialog__footer {
|
::v-deep .help-dialog.dialog .el-dialog__footer {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.help-table {
|
.help-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep th, td {
|
::v-deep .help-table th,
|
||||||
height: 40px;
|
::v-deep .help-table td {
|
||||||
padding: 0 8px;
|
height: 40px;
|
||||||
text-align: left;
|
padding: 0 8px;
|
||||||
}
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
&::v-deep .item-td, .item-label {
|
::v-deep .help-table .item-td,
|
||||||
cursor: pointer;
|
::v-deep .help-table .item-label {
|
||||||
color: var(--color-primary);
|
cursor: pointer;
|
||||||
}
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -219,11 +219,12 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.el-popover.el-popper.monitor-popover {
|
:global(.el-popover.el-popper.monitor-popover) {
|
||||||
ul {
|
ul {
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
@@ -231,19 +232,19 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: opacity(65%)!important;
|
filter: opacity(65%) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.echarts {
|
.echarts {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
@@ -256,6 +257,7 @@ export default {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
.num {
|
.num {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -279,9 +281,9 @@ export default {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
-webkit-transition: width .6s ease;
|
-webkit-transition: width 0.6s ease;
|
||||||
-o-transition: width .6s ease;
|
-o-transition: width 0.6s ease;
|
||||||
transition: width .6s ease;
|
transition: width 0.6s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,9 +294,11 @@ export default {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
@@ -303,6 +307,7 @@ export default {
|
|||||||
.session {
|
.session {
|
||||||
margin-top: 13px;
|
margin-top: 13px;
|
||||||
border-left: 1px solid #f3f3f3;
|
border-left: 1px solid #f3f3f3;
|
||||||
|
|
||||||
.session-title {
|
.session-title {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: #a3a3a4;
|
color: #a3a3a4;
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default {
|
|||||||
this.loading = true
|
this.loading = true
|
||||||
const url = `/api/v1/tickets/comments/?ticket_id=${this.object.id}`
|
const url = `/api/v1/tickets/comments/?ticket_id=${this.object.id}`
|
||||||
this.$axios.get(url).then(res => {
|
this.$axios.get(url).then(res => {
|
||||||
this.comments = res.results
|
this.comments = res
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.$message.error(err)
|
this.$message.error(err)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
|||||||
@@ -115,14 +115,12 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.acceptance .el-message-box__content {
|
::v-deep .acceptance .el-message-box__content {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.box {
|
.box {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
treeUrl: `/api/v1/perms/users/${this.object.id}/nodes/children/tree/`,
|
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: {
|
actions: {
|
||||||
has: false
|
has: false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -161,8 +161,7 @@ module.exports = {
|
|||||||
target: process.env.VUE_APP_CORE_HOST || 'http://127.0.0.1:8080',
|
target: process.env.VUE_APP_CORE_HOST || 'http://127.0.0.1:8080',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
after: require('./mock/mock-server.js')
|
|
||||||
},
|
},
|
||||||
css: {},
|
css: {},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
|
|||||||
87
yarn.lock
@@ -1069,6 +1069,11 @@
|
|||||||
resolved "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
resolved "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
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":
|
"@soda/friendly-errors-webpack-plugin@^1.7.1":
|
||||||
version "1.8.1"
|
version "1.8.1"
|
||||||
resolved "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz#4d4fbb1108993aaa362116247c3d18188a2c6c85"
|
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:
|
dependencies:
|
||||||
ms "^2.1.1"
|
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:
|
debuglog@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmmirror.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
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:
|
dependencies:
|
||||||
once "^1.4.0"
|
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:
|
enhanced-resolve@^4.1.0, enhanced-resolve@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
|
resolved "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
|
||||||
@@ -6916,7 +6944,7 @@ hasown@^2.0.0, hasown@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
he@1.2.x, he@^1.1.0, he@^1.1.1:
|
he@1.2.x, he@^1.1.1, he@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||||
@@ -12884,6 +12912,24 @@ snapdragon@^0.8.1:
|
|||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.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:
|
sockjs-client@^1.5.0:
|
||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz#350b8eda42d6d52ddc030c39943364c11dcad806"
|
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"
|
resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
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:
|
uuid@^3.3.2:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
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:
|
v-sanitize@^0.0.13:
|
||||||
version "0.0.13"
|
version "0.0.13"
|
||||||
resolved "https://registry.npmmirror.com/v-sanitize/-/v-sanitize-0.0.13.tgz#d98e3eee3277a2b3a632bc2ba119c088967da0c4"
|
resolved "https://registry.npmmirror.com/v-sanitize/-/v-sanitize-0.0.13.tgz#d98e3eee3277a2b3a632bc2ba119c088967da0c4"
|
||||||
@@ -14423,32 +14469,27 @@ vue-style-loader@^4.1.0:
|
|||||||
hash-sum "^1.0.2"
|
hash-sum "^1.0.2"
|
||||||
loader-utils "^1.0.2"
|
loader-utils "^1.0.2"
|
||||||
|
|
||||||
vue-template-compiler@2.6.10:
|
vue-template-compiler@2.7.16:
|
||||||
version "2.6.10"
|
version "2.7.16"
|
||||||
resolved "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc"
|
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b"
|
||||||
integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==
|
integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
de-indent "^1.0.2"
|
de-indent "^1.0.2"
|
||||||
he "^1.1.0"
|
he "^1.2.0"
|
||||||
|
|
||||||
vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0:
|
vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||||
|
|
||||||
vue@2.6.10:
|
|
||||||
version "2.6.10"
|
|
||||||
resolved "https://registry.npmmirror.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
|
|
||||||
integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
|
|
||||||
|
|
||||||
vue@2.6.11:
|
vue@2.6.11:
|
||||||
version "2.6.11"
|
version "2.6.11"
|
||||||
resolved "https://registry.npmmirror.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
|
resolved "https://registry.npmmirror.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
|
||||||
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
|
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
|
||||||
|
|
||||||
vue@^2.2.6:
|
vue@2.7.16, vue@^2.2.6:
|
||||||
version "2.7.16"
|
version "2.7.16"
|
||||||
resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
|
||||||
integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
|
integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-sfc" "2.7.16"
|
"@vue/compiler-sfc" "2.7.16"
|
||||||
@@ -14945,11 +14986,21 @@ ws@^6.0.0, ws@^6.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
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:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
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:
|
xss@^1.0.14, xss@^1.0.9:
|
||||||
version "1.0.15"
|
version "1.0.15"
|
||||||
resolved "https://registry.npmmirror.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a"
|
resolved "https://registry.npmmirror.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a"
|
||||||
|
|||||||