Skip to content

Commit d3c596d

Browse files
author
puhui999
committed
Merge remote-tracking branch 'refs/remotes/yudao/dev' into dev-crm
2 parents 334c024 + 5fd8177 commit d3c596d

File tree

17 files changed

+299
-219
lines changed

17 files changed

+299
-219
lines changed

src/api/ai/chat/conversation/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ export const ChatConversationApi = {
4343
},
4444

4545
// 删除【我的】所有对话,置顶除外
46-
deleteMyAllExceptPinned: async () => {
47-
return await request.delete({ url: `/ai/chat/conversation/delete-my-all-except-pinned` })
46+
deleteChatConversationMyByUnpinned: async () => {
47+
return await request.delete({ url: `/ai/chat/conversation/delete-by-unpinned` })
4848
},
4949

5050
// 获得【我的】聊天对话列表

src/api/ai/image/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface ImageVO {
1616
taskId: number // 任务编号
1717
buttons: ImageMjButtonsVO[] // mj 操作按钮
1818
createTime: string // 创建时间
19+
finishTime: string // 完成时间
1920
}
2021

2122
export interface ImagePageReqVO {

src/assets/ai/dall2.jpg

108 KB
Loading

src/assets/ai/dall3.jpg

87.2 KB
Loading

src/assets/ai/qingxi.jpg

86.3 KB
Loading

src/assets/ai/ziran.jpg

121 KB
Loading

src/utils/download.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,29 @@ const download = {
2929
html: (data: Blob, fileName: string) => {
3030
download0(data, fileName, 'text/html')
3131
},
32-
// 下载 MarkdownView 方法
32+
// 下载 Markdown 方法
3333
markdown: (data: Blob, fileName: string) => {
3434
download0(data, fileName, 'text/markdown')
3535
}
3636
}
3737

3838
export default download
39+
40+
/** 图片下载(通过浏览器图片下载) */
41+
export const downloadImage = async (imageUrl) => {
42+
const image = new Image()
43+
image.setAttribute('crossOrigin', 'anonymous')
44+
image.src = imageUrl
45+
image.onload = () => {
46+
const canvas = document.createElement('canvas')
47+
canvas.width = image.width
48+
canvas.height = image.height
49+
const ctx = canvas.getContext('2d') as CanvasDrawImage
50+
ctx.drawImage(image, 0, 0, image.width, image.height)
51+
const url = canvas.toDataURL('image/png')
52+
const a = document.createElement('a')
53+
a.href = url
54+
a.download = 'image.png'
55+
a.click()
56+
}
57+
}

src/views/ai/chat/Conversation.vue

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
<!-- AI 对话 -->
22
<template>
3-
<el-aside width="260px" class="conversation-container" style="height: 100%;">
4-
3+
<el-aside width="260px" class="conversation-container" style="height: 100%">
54
<!-- 左顶部:对话 -->
6-
<div style="height: 100%;">
5+
<div style="height: 100%">
76
<el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation">
8-
<Icon icon="ep:plus" class="mr-5px"/>
7+
<Icon icon="ep:plus" class="mr-5px" />
98
新建对话
109
</el-button>
1110

@@ -18,17 +17,19 @@
1817
@keyup="searchConversation"
1918
>
2019
<template #prefix>
21-
<Icon icon="ep:search"/>
20+
<Icon icon="ep:search" />
2221
</template>
2322
</el-input>
2423

2524
<!-- 左中间:对话列表 -->
2625
<div class="conversation-list">
27-
2826
<el-empty v-if="loading" description="." :v-loading="loading" />
2927

3028
<div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey">
31-
<div class="conversation-item classify-title" v-if="conversationMap[conversationKey].length">
29+
<div
30+
class="conversation-item classify-title"
31+
v-if="conversationMap[conversationKey].length"
32+
>
3233
<el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text>
3334
</div>
3435
<div
@@ -40,46 +41,47 @@
4041
@mouseout="hoverConversationId = ''"
4142
>
4243
<div
43-
:class="conversation.id === activeConversationId ? 'conversation active' : 'conversation'"
44+
:class="
45+
conversation.id === activeConversationId ? 'conversation active' : 'conversation'
46+
"
4447
>
4548
<div class="title-wrapper">
46-
<img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg"/>
49+
<img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" />
4750
<span class="title">{{ conversation.title }}</span>
4851
</div>
4952
<div class="button-wrapper" v-show="hoverConversationId === conversation.id">
50-
<el-button class="btn" link @click.stop="handlerTop(conversation)" >
53+
<el-button class="btn" link @click.stop="handlerTop(conversation)">
5154
<el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon>
5255
<el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon>
5356
</el-button>
5457
<el-button class="btn" link @click.stop="updateConversationTitle(conversation)">
55-
<el-icon title="编辑" >
56-
<Icon icon="ep:edit"/>
58+
<el-icon title="编辑">
59+
<Icon icon="ep:edit" />
5760
</el-icon>
5861
</el-button>
5962
<el-button class="btn" link @click.stop="deleteChatConversation(conversation)">
60-
<el-icon title="删除对话" >
61-
<Icon icon="ep:delete"/>
63+
<el-icon title="删除对话">
64+
<Icon icon="ep:delete" />
6265
</el-icon>
6366
</el-button>
6467
</div>
6568
</div>
6669
</div>
6770
</div>
6871
<!-- 底部站位 -->
69-
<div style="height: 160px; width: 100%;"></div>
72+
<div style="height: 160px; width: 100%"></div>
7073
</div>
71-
7274
</div>
7375

7476
<!-- 左底部:工具栏 -->
7577
<!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
7678
<div class="tool-box">
7779
<div @click="handleRoleRepository">
78-
<Icon icon="ep:user"/>
80+
<Icon icon="ep:user" />
7981
<el-text size="small">角色仓库</el-text>
8082
</div>
8183
<div @click="handleClearConversation">
82-
<Icon icon="ep:delete"/>
84+
<Icon icon="ep:delete" />
8385
<el-text size="small">清空未置顶对话</el-text>
8486
</div>
8587
</div>
@@ -88,17 +90,16 @@
8890

8991
<!-- 角色仓库抽屉 -->
9092
<el-drawer v-model="drawer" title="角色仓库" size="754px">
91-
<Role/>
93+
<Role />
9294
</el-drawer>
93-
9495
</el-aside>
9596
</template>
9697

9798
<script setup lang="ts">
98-
import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation'
99-
import {ref} from "vue";
100-
import Role from "@/views/ai/chat/role/index.vue";
101-
import {Bottom, Top} from "@element-plus/icons-vue";
99+
import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
100+
import { ref } from 'vue'
101+
import Role from '@/views/ai/chat/role/index.vue'
102+
import { Bottom, Top } from '@element-plus/icons-vue'
102103
import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
103104
104105
const message = useMessage() // 消息弹窗
@@ -107,8 +108,8 @@ const message = useMessage() // 消息弹窗
107108
const searchName = ref<string>('') // 对话搜索
108109
const activeConversationId = ref<string | null>(null) // 选中的对话,默认为 null
109110
const hoverConversationId = ref<string | null>(null) // 悬浮上去的对话
110-
const conversationList = ref([] as ChatConversationVO[]) // 对话列表
111-
const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
111+
const conversationList = ref([] as ChatConversationVO[]) // 对话列表
112+
const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
112113
const drawer = ref<boolean>(false) // 角色仓库抽屉 TODO @fan:roleDrawer 会不会好点哈
113114
const loading = ref<boolean>(false) // 加载中
114115
const loadingTime = ref<any>() // 加载中定时器
@@ -138,7 +139,7 @@ const searchConversation = async (e) => {
138139
conversationMap.value = await conversationTimeGroup(conversationList.value)
139140
} else {
140141
// 过滤
141-
const filterValues = conversationList.value.filter(item => {
142+
const filterValues = conversationList.value.filter((item) => {
142143
return item.title.includes(searchName.value.trim())
143144
})
144145
conversationMap.value = await conversationTimeGroup(filterValues)
@@ -150,7 +151,7 @@ const searchConversation = async (e) => {
150151
*/
151152
const handleConversationClick = async (id: string) => {
152153
// 过滤出选中的对话
153-
const filterConversation = conversationList.value.filter(item => {
154+
const filterConversation = conversationList.value.filter((item) => {
154155
return item.id === id
155156
})
156157
// 回调 onConversationClick
@@ -211,28 +212,28 @@ const getChatConversationList = async () => {
211212
const conversationTimeGroup = async (list: ChatConversationVO[]) => {
212213
// 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前)
213214
const groupMap = {
214-
'置顶': [],
215-
'今天': [],
216-
'一天前': [],
217-
'三天前': [],
218-
'七天前': [],
219-
'三十天前': []
215+
置顶: [],
216+
今天: [],
217+
一天前: [],
218+
三天前: [],
219+
七天前: [],
220+
三十天前: []
220221
}
221222
// 当前时间的时间戳
222-
const now = Date.now();
223+
const now = Date.now()
223224
// 定义时间间隔常量(单位:毫秒)
224-
const oneDay = 24 * 60 * 60 * 1000;
225-
const threeDays = 3 * oneDay;
226-
const sevenDays = 7 * oneDay;
227-
const thirtyDays = 30 * oneDay;
225+
const oneDay = 24 * 60 * 60 * 1000
226+
const threeDays = 3 * oneDay
227+
const sevenDays = 7 * oneDay
228+
const thirtyDays = 30 * oneDay
228229
for (const conversation: ChatConversationVO of list) {
229230
// 置顶
230231
if (conversation.pinned) {
231232
groupMap['置顶'].push(conversation)
232233
continue
233234
}
234235
// 计算时间差(单位:毫秒)
235-
const diff = now - conversation.updateTime;
236+
const diff = now - conversation.updateTime
236237
// 根据时间间隔判断
237238
if (diff < oneDay) {
238239
groupMap['今天'].push(conversation)
@@ -271,7 +272,7 @@ const createConversation = async () => {
271272
*/
272273
const updateConversationTitle = async (conversation: ChatConversationVO) => {
273274
// 1. 二次确认
274-
const {value} = await ElMessageBox.prompt('修改标题', {
275+
const { value } = await ElMessageBox.prompt('修改标题', {
275276
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
276277
inputErrorMessage: '标题不能为空',
277278
inputValue: conversation.title
@@ -285,7 +286,7 @@ const updateConversationTitle = async (conversation: ChatConversationVO) => {
285286
// 3. 刷新列表
286287
await getChatConversationList()
287288
// 4. 过滤当前切换的
288-
const filterConversationList = conversationList.value.filter(item => {
289+
const filterConversationList = conversationList.value.filter((item) => {
289290
return item.id === conversation.id
290291
})
291292
if (filterConversationList.length > 0) {
@@ -310,8 +311,7 @@ const deleteChatConversation = async (conversation: ChatConversationVO) => {
310311
await getChatConversationList()
311312
// 回调
312313
emits('onConversationDelete', conversation)
313-
} catch {
314-
}
314+
} catch {}
315315
}
316316
317317
/**
@@ -343,16 +343,13 @@ const handleRoleRepository = async () => {
343343
*/
344344
const handleClearConversation = async () => {
345345
// TODO @fan:可以使用 await message.confirm( 简化,然后使用 await 改成同步的逻辑,会更简洁
346-
ElMessageBox.confirm(
347-
'确认后对话会全部清空,置顶的对话除外。',
348-
'确认提示',
349-
{
350-
confirmButtonText: '确认',
351-
cancelButtonText: '取消',
352-
type: 'warning',
353-
})
346+
ElMessageBox.confirm('确认后对话会全部清空,置顶的对话除外。', '确认提示', {
347+
confirmButtonText: '确认',
348+
cancelButtonText: '取消',
349+
type: 'warning'
350+
})
354351
.then(async () => {
355-
await ChatConversationApi.deleteMyAllExceptPinned()
352+
await ChatConversationApi.deleteChatConversationMyByUnpinned()
356353
ElMessage({
357354
message: '操作成功!',
358355
type: 'success'
@@ -364,8 +361,7 @@ const handleClearConversation = async () => {
364361
// 回调 方法
365362
emits('onConversationClear')
366363
})
367-
.catch(() => {
368-
})
364+
.catch(() => {})
369365
}
370366
371367
// ============ 组件 onMounted
@@ -377,7 +373,7 @@ watch(activeId, async (newValue, oldValue) => {
377373
})
378374
379375
// 定义 public 方法
380-
defineExpose({createConversation})
376+
defineExpose({ createConversation })
381377
382378
onMounted(async () => {
383379
// 获取 对话列表
@@ -394,11 +390,9 @@ onMounted(async () => {
394390
}
395391
}
396392
})
397-
398393
</script>
399394
400395
<style scoped lang="scss">
401-
402396
.conversation-container {
403397
position: relative;
404398
display: flex;

src/views/ai/image/ImageDetailDrawer.vue

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<el-drawer
33
v-model="showDrawer"
44
title="图片详细"
5-
@close="handlerDrawerClose"
5+
@close="handleDrawerClose"
66
custom-class="drawer-class"
77
>
88
<!-- 图片 -->
@@ -22,8 +22,7 @@
2222
<div class="tip">时间</div>
2323
<div class="body">
2424
<div>提交时间:{{ imageDetail.createTime }}</div>
25-
<!-- TODO @fan:要不加个完成时间的字段 finishTime?updateTime 不算特别合理哈 -->
26-
<div>生成时间:{{ imageDetail.updateTime }}</div>
25+
<div>生成时间:{{ imageDetail.finishTime }}</div>
2726
</div>
2827
</div>
2928
<!-- 模型 -->
@@ -79,8 +78,8 @@ const props = defineProps({
7978
})
8079
8180
/** 抽屉 - close */
82-
const handlerDrawerClose = async () => {
83-
emits('handlerDrawerClose')
81+
const handleDrawerClose = async () => {
82+
emits('handleDrawerClose')
8483
}
8584
8685
/** 获取 - 图片 detail */
@@ -90,7 +89,7 @@ const getImageDetail = async (id) => {
9089
}
9190
9291
/** 任务 - detail */
93-
const handlerTaskDetail = async () => {
92+
const handleTaskDetail = async () => {
9493
showDrawer.value = true
9594
}
9695
@@ -107,7 +106,7 @@ watch(id, async (newVal, oldVal) => {
107106
}
108107
})
109108
//
110-
const emits = defineEmits(['handlerDrawerClose'])
109+
const emits = defineEmits(['handleDrawerClose'])
111110
//
112111
onMounted(async () => {})
113112
</script>

0 commit comments

Comments
 (0)