agilebuilder-ui 1.1.45 → 1.1.46-sit2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{401-1bde8dc9.js → 401-75472dcd.js} +1 -1
- package/lib/{404-48d76996.js → 404-e5ce8455.js} +1 -1
- package/lib/{iframe-page-77e184a0.js → iframe-page-9d5ce6b0.js} +1 -1
- package/lib/index-789fbe10.js +92825 -0
- package/lib/super-ui.css +1 -1
- package/lib/super-ui.js +33 -32
- package/lib/super-ui.umd.cjs +172 -133
- package/lib/{tab-content-iframe-index-39745d49.js → tab-content-iframe-index-47fae8cf.js} +1 -1
- package/lib/{tab-content-index-65696e56.js → tab-content-index-6e5f5717.js} +1 -1
- package/lib/{tache-subprocess-history-ef943f95.js → tache-subprocess-history-1ed95cee.js} +1 -1
- package/package.json +7 -2
- package/packages/chat-embed/index.ts +6 -0
- package/packages/chat-embed/src/chat-embed-message.ts +79 -0
- package/packages/chat-embed/src/chat-embed.css +117 -0
- package/packages/chat-embed/src/chat-sender.vue +240 -0
- package/packages/chat-embed/src/header.vue +50 -0
- package/packages/chat-embed/src/index.vue +425 -0
- package/packages/chat-embed/src/recommendation-message.vue +37 -0
- package/packages/chat-embed/src/util.ts +33 -0
- package/packages/department-tree-inline/src/department-multi-tree-inline.vue +42 -34
- package/packages/department-tree-inline/src/department-single-tree-inline.vue +53 -42
- package/packages/department-tree-inline/src/search-result.vue +187 -219
- package/packages/department-user-tree-inline/src/department-user-multiple-tree-inline.vue +52 -43
- package/packages/department-user-tree-inline/src/department-user-single-tree-inline.vue +52 -42
- package/packages/department-user-tree-inline/src/search-result.vue +207 -220
- package/packages/dynamic-source-select/src/dynamic-source-select-service.js +7 -2
- package/packages/empty-state/index.vue +28 -0
- package/packages/fs-preview/src/fs-preview.vue +12 -3
- package/packages/fs-upload/src/fs-upload-multi.vue +6 -4
- package/packages/fs-upload/src/fs-upload-single.vue +7 -6
- package/packages/fs-upload/src/fs-upload.vue +3 -1
- package/packages/fs-upload/src/see-big-picture.vue +3 -0
- package/packages/fs-upload-list/src/fs-upload-list.vue +8 -2
- package/packages/fs-upload-new/src/fs-button-upload.vue +11 -4
- package/packages/fs-upload-new/src/fs-drag-upload.vue +11 -4
- package/packages/fs-upload-new/src/fs-preview-new.vue +8 -5
- package/packages/fs-upload-new/src/fs-upload-new.vue +17 -0
- package/packages/index.js +16 -13
- package/packages/json-view/index.ts +3 -0
- package/packages/json-view/json-view-dialog.vue +53 -0
- package/packages/json-view/json-view.vue +126 -0
- package/packages/plugins/export-data-new.js +2 -0
- package/packages/super-grid/src/apis.js +11 -0
- package/packages/super-grid/src/components/grid-icon.vue +6 -3
- package/packages/super-grid/src/custom-formatter.js +15 -2
- package/packages/super-grid/src/dynamic-input.vue +46 -4
- package/packages/super-grid/src/formatter.js +5 -1
- package/packages/super-grid/src/normal-column-content.vue +31 -38
- package/packages/super-grid/src/normal-column.vue +8 -1
- package/packages/super-grid/src/row-operation.vue +13 -9
- package/packages/super-grid/src/super-grid.vue +21 -8
- package/packages/utils/utils.js +26 -9
- package/packages/workgroup-tree-inline/src/workgroup-tree-inline.vue +50 -41
- package/packages/workgroup-user-tree-inline/src/workgroup-user-tree-inline.vue +45 -36
- package/src/assets/chat-embed/avatar.png +0 -0
- package/src/i18n/langs/cn.js +20 -6
- package/src/i18n/langs/en.js +19 -5
- package/src/store/modules/chat-ai-store.ts +78 -0
- package/src/store/modules/tab-content.js +9 -3
- package/src/styles/element-ui.scss +8 -7
- package/src/styles/index.scss +45 -0
- package/src/utils/chat-ai-util.ts +31 -0
- package/src/utils/common-util.js +78 -8
- package/src/utils/insert_css.js +14 -1
- package/src/utils/jump-page-utils.js +8 -4
- package/src/views/dsc-component/Sidebar/SidebarItem.vue +6 -0
- package/src/views/dsc-component/tabs/tab-content.vue +6 -0
- package/lib/index-465b0d69.js +0 -73558
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- <div v-permission="'mc.ragFlow.converseWithChat'"> -->
|
|
3
|
+
<div v-if="enableAiChat">
|
|
4
|
+
<div ref="chatIcon" :style="style" style="position: fixed; cursor: pointer; z-index: 100">
|
|
5
|
+
<img src="https://maxkb.fit2cloud.com/ui/MaxKB.gif" @click="showChatDialog" />
|
|
6
|
+
</div>
|
|
7
|
+
<transition name="slide-up">
|
|
8
|
+
<div v-show="showChatVisable" ref="chatContainerRef" class="chat-container">
|
|
9
|
+
<!-- 头部 -->
|
|
10
|
+
<Header
|
|
11
|
+
:aiAvatar="aiAvatar"
|
|
12
|
+
:dialogFullScreen="dialogFullScreen"
|
|
13
|
+
@expandDialog="expandDialog"
|
|
14
|
+
@showChatDialog="showChatDialog"
|
|
15
|
+
/>
|
|
16
|
+
<!-- 聊天内容展示 -->
|
|
17
|
+
<!-- <div ref="talkMainRef" :key="talkMainRefKey" class="chat-embed__main"> -->
|
|
18
|
+
<div ref="talkMainRef" class="chat-embed__main" :key="talkMainRefKey">
|
|
19
|
+
<div class="chat-embed__main_content">
|
|
20
|
+
<BubbleList :list="messageList" :max-height="bubbleMaxHeight" :is-fog="true">
|
|
21
|
+
<template #content="{ item }">
|
|
22
|
+
<template v-if="!item.isRecommend && item.thinkingContent !== null">
|
|
23
|
+
<!-- ai消息正在回复中 -->
|
|
24
|
+
<Thinking
|
|
25
|
+
style="margin-bottom: 5px"
|
|
26
|
+
v-model="item.showThinkingContent"
|
|
27
|
+
:status="item.thinkingStatus"
|
|
28
|
+
auto-collapse
|
|
29
|
+
:content="item.thinkingContent"
|
|
30
|
+
max-width="100%"
|
|
31
|
+
:typing="{ interval: 20 }"
|
|
32
|
+
duration=".3s"
|
|
33
|
+
class="thinking-chain-warp"
|
|
34
|
+
>
|
|
35
|
+
</Thinking>
|
|
36
|
+
</template>
|
|
37
|
+
<template v-if="item.content">
|
|
38
|
+
<!-- ai消息和用户消息 -->
|
|
39
|
+
<div
|
|
40
|
+
:class="item.role === 'ai' ? 'content-container' : 'content-borderless-container'"
|
|
41
|
+
:style="{ maxWidth: bubbleMaxWidth }"
|
|
42
|
+
>
|
|
43
|
+
<Typewriter
|
|
44
|
+
v-if="item.role === 'ai' && !item.isRecommend"
|
|
45
|
+
:content="item.content"
|
|
46
|
+
:typing="{ interval: 10 }"
|
|
47
|
+
:is-markdown="true"
|
|
48
|
+
/>
|
|
49
|
+
<template v-else-if="item.role === 'user'">
|
|
50
|
+
{{ item.content }}
|
|
51
|
+
<template v-if="item.additionalData?.length > 0">
|
|
52
|
+
<el-divider border-style="dashed" />
|
|
53
|
+
<el-tag type="primary">
|
|
54
|
+
<template #default>
|
|
55
|
+
<div
|
|
56
|
+
style="
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
min-width: max-content;
|
|
61
|
+
margin-left: auto;
|
|
62
|
+
gap: 10px;
|
|
63
|
+
"
|
|
64
|
+
>
|
|
65
|
+
<el-icon><Paperclip /></el-icon>
|
|
66
|
+
{{ item.additionalDataLabel }}
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
</el-tag>
|
|
70
|
+
<div
|
|
71
|
+
style="
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
margin-top: 10px;
|
|
76
|
+
gap: 10px;
|
|
77
|
+
"
|
|
78
|
+
>
|
|
79
|
+
<el-button plain round size="small" @click="viewJson(item.additionalData)">
|
|
80
|
+
<el-icon><View /></el-icon> <span>预览数据</span>
|
|
81
|
+
</el-button>
|
|
82
|
+
<el-button
|
|
83
|
+
plain
|
|
84
|
+
round
|
|
85
|
+
size="small"
|
|
86
|
+
@click="downloadJsonData(item.additionalData, item.menuName)"
|
|
87
|
+
>
|
|
88
|
+
<el-icon><Download /></el-icon> 下载数据
|
|
89
|
+
</el-button>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
</template>
|
|
93
|
+
<template v-else> {{ item.content }} </template>
|
|
94
|
+
</div>
|
|
95
|
+
<template v-if="item.recommendations && item.recommendations.length > 0">
|
|
96
|
+
<!-- 推荐内容的消息 -->
|
|
97
|
+
<RecommendationMessage :item="item" @clickRecommendation="clickRecommendation" />
|
|
98
|
+
</template>
|
|
99
|
+
</template>
|
|
100
|
+
</template>
|
|
101
|
+
<template #footer="{ item }">
|
|
102
|
+
<div
|
|
103
|
+
v-if="item.role === 'ai' && !item.isRecommend && item.thinkingStatus !== 'thinking'"
|
|
104
|
+
class="footer-container"
|
|
105
|
+
>
|
|
106
|
+
<el-tooltip class="box-item" effect="dark" :content="$t('chatEmbed.regenerate')">
|
|
107
|
+
<el-button type="info" :icon="Refresh" size="small" circle @click="regenerateQuestion(item)" />
|
|
108
|
+
</el-tooltip>
|
|
109
|
+
<el-tooltip class="box-item" effect="dark" :content="$t('chatEmbed.copy')">
|
|
110
|
+
<el-button
|
|
111
|
+
type="info"
|
|
112
|
+
color="#626aef"
|
|
113
|
+
:icon="DocumentCopy"
|
|
114
|
+
size="small"
|
|
115
|
+
circle
|
|
116
|
+
@click="cpoyContent(item)"
|
|
117
|
+
/>
|
|
118
|
+
</el-tooltip>
|
|
119
|
+
</div>
|
|
120
|
+
</template>
|
|
121
|
+
</BubbleList>
|
|
122
|
+
</div>
|
|
123
|
+
<!-- 对话输入框 -->
|
|
124
|
+
<div ref="talkInputRef" class="chat-embed__input_container">
|
|
125
|
+
<ChatSender
|
|
126
|
+
:loading="loading"
|
|
127
|
+
@submit-question="sendQuestion"
|
|
128
|
+
@new-chat-session="newChatSession"
|
|
129
|
+
@handle-cancel="handleCancel"
|
|
130
|
+
@view-json="viewJson"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</transition>
|
|
136
|
+
<json-view-dialog v-model="jsonDialogVisible" :json-object="jsonObject" :append-to-body="true" title="预览数据" />
|
|
137
|
+
</div>
|
|
138
|
+
</template>
|
|
139
|
+
<script setup lang="ts">
|
|
140
|
+
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
|
141
|
+
import { Refresh, DocumentCopy } from '@element-plus/icons-vue'
|
|
142
|
+
import { BubbleList, Thinking, useXStream, Typewriter } from 'vue-element-plus-x'
|
|
143
|
+
import { useDraggable } from '@vueuse/core'
|
|
144
|
+
import { ElMessage } from 'element-plus'
|
|
145
|
+
import { getMessageTemplate, defaultMessage } from './chat-embed-message'
|
|
146
|
+
import RecommendationMessage from './recommendation-message.vue'
|
|
147
|
+
import { BubbleProps } from 'vue-element-plus-x/types/Bubble'
|
|
148
|
+
import { useI18n } from 'vue-i18n'
|
|
149
|
+
import Header from './header.vue'
|
|
150
|
+
import ChatSender from './chat-sender.vue'
|
|
151
|
+
import { JsonViewDialog } from '../../json-view'
|
|
152
|
+
import { generateFileName, downloadJsonFile } from './util.ts'
|
|
153
|
+
|
|
154
|
+
const enableAiChat = ref(false)
|
|
155
|
+
if (window.$vueApp.config.globalProperties.enableAiChat) {
|
|
156
|
+
enableAiChat.value = true
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const { t } = useI18n()
|
|
160
|
+
const chatIcon = ref()
|
|
161
|
+
let isActualClick = ref(true)
|
|
162
|
+
const isActualClickTimer = ref(null)
|
|
163
|
+
|
|
164
|
+
const jsonDialogVisible = ref(false)
|
|
165
|
+
const jsonObject = ref({})
|
|
166
|
+
|
|
167
|
+
const initialX = document.documentElement.clientWidth - 100
|
|
168
|
+
const initialY = document.documentElement.clientHeight - 100
|
|
169
|
+
const { x, y, style } = useDraggable(chatIcon, {
|
|
170
|
+
// 聊天图标的初始位置
|
|
171
|
+
initialValue: { x: initialX, y: initialY },
|
|
172
|
+
preventDefault: true,
|
|
173
|
+
onStart() {
|
|
174
|
+
// 拖拽开始时重置状态
|
|
175
|
+
isActualClick.value = true
|
|
176
|
+
},
|
|
177
|
+
onMove(position) {
|
|
178
|
+
isActualClick.value = false
|
|
179
|
+
// 限制拖拽范围 防止拖出屏幕外
|
|
180
|
+
const maxX = window.innerWidth - chatIcon.value.offsetWidth
|
|
181
|
+
const maxY = window.innerHeight - chatIcon.value.offsetHeight
|
|
182
|
+
position.x = Math.max(0, Math.min(position.x, maxX))
|
|
183
|
+
position.y = Math.max(0, Math.min(position.y, maxY))
|
|
184
|
+
},
|
|
185
|
+
onEnd() {
|
|
186
|
+
adjustChatIconPosition()
|
|
187
|
+
// 延迟重置确保 click 事件先处理
|
|
188
|
+
setTimeout(() => (isActualClick.value = true), 50)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
function adjustChatIconPosition() {
|
|
193
|
+
if (!chatIcon.value) return
|
|
194
|
+
const maxX = window.innerWidth - chatIcon.value.offsetWidth
|
|
195
|
+
const maxY = window.innerHeight - chatIcon.value.offsetHeight
|
|
196
|
+
if (x.value > maxX) x.value = maxX
|
|
197
|
+
if (y.value > maxY) y.value = maxY
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const talkInputRef = ref()
|
|
201
|
+
// 显示聊天窗口的状态
|
|
202
|
+
const showChatVisable = ref(false)
|
|
203
|
+
// 聊天窗口的引用
|
|
204
|
+
const chatContainerRef = ref()
|
|
205
|
+
// 聊天窗口主区域的引用
|
|
206
|
+
const talkMainRef = ref()
|
|
207
|
+
// 聊天输入框状态
|
|
208
|
+
const loading = ref(false)
|
|
209
|
+
// 聊天窗口是否全屏
|
|
210
|
+
const dialogFullScreen = ref(false)
|
|
211
|
+
|
|
212
|
+
const talkMainRefKey = ref(0)
|
|
213
|
+
|
|
214
|
+
const bubbleMaxWidth = ref('')
|
|
215
|
+
const bubbleMaxHeight = ref('500px')
|
|
216
|
+
|
|
217
|
+
const aiAvatar = ref('https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png')
|
|
218
|
+
|
|
219
|
+
// 聊天内容
|
|
220
|
+
const messageList: any = ref([])
|
|
221
|
+
// 默认ai提示消息
|
|
222
|
+
messageList.value.push(defaultMessage)
|
|
223
|
+
// messageList.value.push(getMessageTemplate('user', '11111111111', true))
|
|
224
|
+
// 正在生成的ai消息
|
|
225
|
+
const answerContent: any = ref({})
|
|
226
|
+
// 后端会话session_id
|
|
227
|
+
const session_id = ref(null)
|
|
228
|
+
|
|
229
|
+
onMounted(() => {
|
|
230
|
+
window.addEventListener('resize', adjustChatIconPosition)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
onUnmounted(() => {
|
|
234
|
+
window.removeEventListener('resize', adjustChatIconPosition)
|
|
235
|
+
// 清除定时器
|
|
236
|
+
if (isActualClickTimer.value) {
|
|
237
|
+
clearTimeout(isActualClickTimer.value)
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// 处理聊天窗口的显示和隐藏
|
|
242
|
+
function showChatDialog() {
|
|
243
|
+
if (isActualClick.value) {
|
|
244
|
+
showChatVisable.value = !showChatVisable.value
|
|
245
|
+
talkMainRefKey.value++
|
|
246
|
+
nextTick(() => {
|
|
247
|
+
setHeightAndWidth()
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const { cancel, data } = useXStream()
|
|
253
|
+
|
|
254
|
+
// 发送聊天内容
|
|
255
|
+
function sendQuestion(submitData: any) {
|
|
256
|
+
const question = submitData.question
|
|
257
|
+
if (!question) {
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
data.value = []
|
|
261
|
+
const message = getMessageTemplate('user', question, true)
|
|
262
|
+
if (submitData.additionalData && submitData.additionalData.length > 0) {
|
|
263
|
+
message.additionalData = submitData.additionalData
|
|
264
|
+
message.additionalDataLabel = submitData.additionalDataLabel
|
|
265
|
+
message.menuName = submitData.menuName
|
|
266
|
+
message.menuCode = submitData.menuCode
|
|
267
|
+
}
|
|
268
|
+
messageList.value.push(message)
|
|
269
|
+
// console.log('submit', question)
|
|
270
|
+
loading.value = true
|
|
271
|
+
if (!session_id.value) {
|
|
272
|
+
session_id.value = new Date().getTime().toString()
|
|
273
|
+
}
|
|
274
|
+
// 创建一个新的消息对象
|
|
275
|
+
answerContent.value = getMessageTemplate('ai', '', false)
|
|
276
|
+
// 设置消息的状态为正在思考 并存下历史的 question信息
|
|
277
|
+
answerContent.value.thinkingStatus = 'thinking'
|
|
278
|
+
answerContent.value.question = question
|
|
279
|
+
answerContent.value.menuName = submitData.menuName
|
|
280
|
+
answerContent.value.menuCode = submitData.menuCode
|
|
281
|
+
answerContent.value.askAdditionalData = submitData.additionalData
|
|
282
|
+
messageList.value.push(answerContent.value)
|
|
283
|
+
try {
|
|
284
|
+
window.$vueApp.config.globalProperties.$http
|
|
285
|
+
.post(window.$vueApp.config.globalProperties.baseAPI + '/component/ai/multiple-fuck', {
|
|
286
|
+
question: question,
|
|
287
|
+
messageId: session_id.value,
|
|
288
|
+
data: JSON.stringify(submitData.additionalData)
|
|
289
|
+
})
|
|
290
|
+
.then((res) => {
|
|
291
|
+
// 创建一个新的消息对象
|
|
292
|
+
answerContent.value.thinkingStatus = 'end'
|
|
293
|
+
answerContent.value.content = res
|
|
294
|
+
answerContent.value = {}
|
|
295
|
+
loading.value = false
|
|
296
|
+
})
|
|
297
|
+
} catch (err) {
|
|
298
|
+
console.error('Fetch error:', err)
|
|
299
|
+
messageList.push(getMessageTemplate('ai', t('chatEmbed.requestFailed'), false))
|
|
300
|
+
loading.value = false
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 处理聊天内容的取消
|
|
305
|
+
function handleCancel() {
|
|
306
|
+
loading.value = false
|
|
307
|
+
messageList.value[messageList.value.length - 1].thinkingStatus = 'end'
|
|
308
|
+
cancel()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// 处理聊天窗口的全屏和缩小
|
|
312
|
+
function expandDialog() {
|
|
313
|
+
dialogFullScreen.value = !dialogFullScreen.value
|
|
314
|
+
setHeightAndWidth()
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function setHeightAndWidth() {
|
|
318
|
+
let heightVh = 100
|
|
319
|
+
let widthVw = 40
|
|
320
|
+
if (dialogFullScreen.value) {
|
|
321
|
+
heightVh = 100
|
|
322
|
+
widthVw = 100
|
|
323
|
+
}
|
|
324
|
+
chatContainerRef.value.style.height = `calc(${heightVh}vh - var(--chat-padding) * 2)`
|
|
325
|
+
chatContainerRef.value.style.width = `calc(${widthVw}vw - var(--chat-padding) * 2)`
|
|
326
|
+
// talkMainRef.value.style.height = `calc(${heightVh}vh - var(--header-height) - var(--chat-padding) * 2)`
|
|
327
|
+
const talkHeight = talkInputRef.value.offsetHeight
|
|
328
|
+
// alert(`talkHeight: ${talkHeight}`)
|
|
329
|
+
// bubbleMaxHeight.value = `calc(${heightVh}vh - var(--header-height) - var(--chat-padding) * 5 - 144px)`
|
|
330
|
+
nextTick(() => {
|
|
331
|
+
const talkHeight = talkInputRef.value?.offsetHeight || 80
|
|
332
|
+
// 定义具体的像素值
|
|
333
|
+
const headerHeight = 56
|
|
334
|
+
const chatPadding = 12
|
|
335
|
+
// 更清晰的计算:总高度 - 头部 - 输入框 - 各种padding和间距
|
|
336
|
+
bubbleMaxHeight.value = `calc(${heightVh}vh - ${headerHeight}px - ${talkHeight}px - ${chatPadding * 4}px)`
|
|
337
|
+
talkMainRefKey.value++
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 点击推荐内容的处理函数
|
|
342
|
+
// 这里可以根据需要进行处理,比如发送请求、更新状态等
|
|
343
|
+
function clickRecommendation(item: BubbleProps) {
|
|
344
|
+
console.log('clickRecommendation', item)
|
|
345
|
+
sendQuestion(item.content)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function regenerateQuestion(answerData) {
|
|
349
|
+
sendQuestion({
|
|
350
|
+
question: answerData.question,
|
|
351
|
+
additionalData: answerData.additionalData,
|
|
352
|
+
additionalDataLabel: answerData.additionalDataLabel,
|
|
353
|
+
menuName: answerData.menuName,
|
|
354
|
+
menuCode: answerData.menuCode
|
|
355
|
+
})
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function cpoyContent(item: BubbleProps) {
|
|
359
|
+
// 复制内容到剪贴板
|
|
360
|
+
if (!item.content) {
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
navigator.clipboard.writeText(item.content).then(() => {
|
|
364
|
+
ElMessage({
|
|
365
|
+
message: t('chatEmbed.copySuccess'),
|
|
366
|
+
type: 'success'
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function newChatSession() {
|
|
372
|
+
// 清空聊天记录
|
|
373
|
+
messageList.value = []
|
|
374
|
+
if (session_id.value) {
|
|
375
|
+
try {
|
|
376
|
+
window.$vueApp.config.globalProperties.$http
|
|
377
|
+
.post(window.$vueApp.config.globalProperties.baseAPI + '/component/ai/multiple-fuck/clear/' + session_id.value)
|
|
378
|
+
.then((res) => {
|
|
379
|
+
//
|
|
380
|
+
})
|
|
381
|
+
} catch (err) {
|
|
382
|
+
console.error('Fetch error:', err)
|
|
383
|
+
messageList.push(getMessageTemplate('ai', t('chatEmbed.requestFailed'), false))
|
|
384
|
+
loading.value = false
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
session_id.value = null
|
|
388
|
+
// 重置加载状态
|
|
389
|
+
loading.value = false
|
|
390
|
+
messageList.value.push(defaultMessage)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function viewJson(viewData: any) {
|
|
394
|
+
jsonObject.value = viewData
|
|
395
|
+
jsonDialogVisible.value = true
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 下载 JSON 数据功能
|
|
399
|
+
function downloadJsonData(data: any, fileName: string) {
|
|
400
|
+
try {
|
|
401
|
+
downloadJsonFile(data, generateFileName(fileName || 'data'))
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error('下载失败:', error)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
</script>
|
|
407
|
+
<style lang="scss" rel="stylesheet/scss" scoped>
|
|
408
|
+
@import url('./chat-embed.css');
|
|
409
|
+
:root {
|
|
410
|
+
--header-height: 56px;
|
|
411
|
+
--chat-padding: 12px;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
h4 {
|
|
415
|
+
font-size: 16px;
|
|
416
|
+
margin-top: 0px;
|
|
417
|
+
margin-bottom: 0px;
|
|
418
|
+
}
|
|
419
|
+
:deep(.el-bubble-content-wrapper .el-bubble-content) {
|
|
420
|
+
padding: 0 !important;
|
|
421
|
+
}
|
|
422
|
+
:deep(.el-divider--horizontal) {
|
|
423
|
+
margin: 5px 0;
|
|
424
|
+
}
|
|
425
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Bubble
|
|
3
|
+
v-for="(recommendation, index) in item.recommendations"
|
|
4
|
+
class="chat-embed__recommendation"
|
|
5
|
+
:no-style="true"
|
|
6
|
+
variant="borderless"
|
|
7
|
+
>
|
|
8
|
+
<template v-if="index === 0" #header>
|
|
9
|
+
<div class="content-container-header">推荐内容</div>
|
|
10
|
+
</template>
|
|
11
|
+
<template #content>
|
|
12
|
+
<div class="content-borderless-container" @click="clickRecommendation(recommendation)">
|
|
13
|
+
{{ recommendation.content }}
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
</Bubble>
|
|
17
|
+
</template>
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { Bubble } from 'vue-element-plus-x'
|
|
20
|
+
import { defineProps, defineEmits } from 'vue'
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
item: {
|
|
23
|
+
type: Object,
|
|
24
|
+
required: true
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits(['clickRecommendation'])
|
|
29
|
+
|
|
30
|
+
const clickRecommendation = (recommendation: any) => {
|
|
31
|
+
emit('clickRecommendation', recommendation)
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<style scoped>
|
|
36
|
+
@import url('./chat-embed.css');
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// 生成文件名
|
|
2
|
+
function generateFileName(fileName: string): string {
|
|
3
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
|
4
|
+
const safeName = fileName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_')
|
|
5
|
+
return `${safeName}_${timestamp}.json`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 核心下载方法
|
|
9
|
+
function downloadJsonFile(data: any, fileName: string) {
|
|
10
|
+
// 将数据转换为格式化的 JSON 字符串
|
|
11
|
+
const jsonString = JSON.stringify(data, null, 2)
|
|
12
|
+
|
|
13
|
+
// 创建 Blob 对象
|
|
14
|
+
const blob = new Blob([jsonString], {
|
|
15
|
+
type: 'application/json;charset=utf-8'
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// 创建下载链接
|
|
19
|
+
const url = URL.createObjectURL(blob)
|
|
20
|
+
const link = document.createElement('a')
|
|
21
|
+
link.href = url
|
|
22
|
+
link.download = fileName
|
|
23
|
+
|
|
24
|
+
// 触发下载
|
|
25
|
+
document.body.appendChild(link)
|
|
26
|
+
link.click()
|
|
27
|
+
|
|
28
|
+
// 清理
|
|
29
|
+
document.body.removeChild(link)
|
|
30
|
+
URL.revokeObjectURL(url)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { generateFileName, downloadJsonFile }
|
|
@@ -19,39 +19,42 @@
|
|
|
19
19
|
</el-autocomplete>
|
|
20
20
|
</el-header>
|
|
21
21
|
<el-main style="padding: 10px">
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<el-tree
|
|
27
|
-
ref="deparmentTree"
|
|
28
|
-
:filter-node-method="filterNode"
|
|
29
|
-
:load="loadNode"
|
|
30
|
-
:props="defaultProps"
|
|
31
|
-
:show-checkbox="true"
|
|
32
|
-
check-strictly
|
|
33
|
-
lazy
|
|
34
|
-
node-key="id"
|
|
35
|
-
@check="handleCheckNode"
|
|
36
|
-
@check-change="handleCheckChange"
|
|
37
|
-
@node-expand="handleNodeExpand"
|
|
38
|
-
@node-click="handleNodeClick"
|
|
22
|
+
<template v-if="!searchValue">
|
|
23
|
+
<div
|
|
24
|
+
v-if="canShowOrgTree"
|
|
25
|
+
style="padding-top: 5px; overflow: auto; width: 100%; display: inline-block !important"
|
|
39
26
|
>
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
<el-tree
|
|
28
|
+
ref="deparmentTree"
|
|
29
|
+
:filter-node-method="filterNode"
|
|
30
|
+
:load="loadNode"
|
|
31
|
+
:props="defaultProps"
|
|
32
|
+
:show-checkbox="true"
|
|
33
|
+
check-strictly
|
|
34
|
+
lazy
|
|
35
|
+
node-key="id"
|
|
36
|
+
@check="handleCheckNode"
|
|
37
|
+
@check-change="handleCheckChange"
|
|
38
|
+
@node-expand="handleNodeExpand"
|
|
39
|
+
@node-click="handleNodeClick"
|
|
40
|
+
>
|
|
41
|
+
<template #default="{ node, data }">
|
|
42
|
+
<span>
|
|
43
|
+
<el-icon>
|
|
44
|
+
<Menu v-if="node.data.id === -1 || node.data.branch" />
|
|
45
|
+
<Tickets v-else-if="node.data.id === -2 || node.data.id === -3" />
|
|
46
|
+
<Calendar v-else />
|
|
47
|
+
</el-icon>
|
|
48
|
+
<span :title="node.label">
|
|
49
|
+
{{ node.label }}
|
|
50
|
+
</span>
|
|
49
51
|
</span>
|
|
50
|
-
</
|
|
51
|
-
</
|
|
52
|
-
</
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
</template>
|
|
53
|
+
</el-tree>
|
|
54
|
+
</div>
|
|
55
|
+
<!-- 暂无信息 -->
|
|
56
|
+
<EmptyState v-else style="width: 100%; height: 100%" />
|
|
57
|
+
</template>
|
|
55
58
|
<div v-if="searchValue && searchValue.length > 0" style="height: 100%; overflow: hidden">
|
|
56
59
|
<user-result
|
|
57
60
|
:grid-data="searchResult"
|
|
@@ -96,7 +99,7 @@ import utils from '../../utils/utils'
|
|
|
96
99
|
import departmentTreeService from './department-tree-service'
|
|
97
100
|
import UserResult from './search-result.vue'
|
|
98
101
|
import memoryCacheUtils from '../../utils/memory-cache-utils'
|
|
99
|
-
|
|
102
|
+
import EmptyState from '../../empty-state/index.vue'
|
|
100
103
|
export default {
|
|
101
104
|
data() {
|
|
102
105
|
return {
|
|
@@ -139,12 +142,14 @@ export default {
|
|
|
139
142
|
memoryCacheKey: 'DEPARTMENT_MEMORY_KEY',
|
|
140
143
|
// 记忆选择数据, 搜索框获得焦点后,下拉显示最近选中的10个人,倒序排列,最后选中的在最上面显示。
|
|
141
144
|
memoryCacheData: [],
|
|
142
|
-
ElIconSearch
|
|
145
|
+
ElIconSearch,
|
|
146
|
+
canShowOrgTree: false
|
|
143
147
|
}
|
|
144
148
|
},
|
|
145
149
|
name: 'InlineDepartmentMultiTree',
|
|
146
150
|
components: {
|
|
147
|
-
UserResult
|
|
151
|
+
UserResult,
|
|
152
|
+
EmptyState
|
|
148
153
|
},
|
|
149
154
|
props: {
|
|
150
155
|
checkStrictly: {
|
|
@@ -199,6 +204,9 @@ export default {
|
|
|
199
204
|
}
|
|
200
205
|
},
|
|
201
206
|
created() {
|
|
207
|
+
utils.canShowOrgTree().then((showOrgTree) => {
|
|
208
|
+
this.canShowOrgTree = showOrgTree
|
|
209
|
+
})
|
|
202
210
|
this.initSelectDepts(this.searchField, this.selectDepartmentInfo, this.separator).then((selectUsers) => {
|
|
203
211
|
if (selectUsers) {
|
|
204
212
|
this.selectResult = selectUsers
|