Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ebbe002

Browse files
committed
feat: 优化布局
1 parent 35b88f9 commit ebbe002

File tree

19 files changed

+1025
-94
lines changed

19 files changed

+1025
-94
lines changed

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"devDependencies": {
1818
"@types/uuid": "^9.0.1",
1919
"@vitejs/plugin-vue": "^4.1.0",
20+
"@vitejs/plugin-vue-jsx": "^3.0.1",
2021
"autoprefixer": "^10.4.14",
2122
"axios": "^1.4.0",
2223
"postcss": "^8.4.24",

‎src/DrawPanel.vue‎

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,21 @@ import MessageItem from './Message.vue'
55
import { getMessagesAPI, createMessageAPI, upscaleMessageAPI } from './api/midjourney'
66
import { sendMessage, createWebsocket } from './utils/websocket'
77
import { useDebounceFn } from '@vueuse/core'
8+
import Toast from './components/Toast/index'
9+
import UseModal from './components/Use-Modal/index.vue'
10+
import SettingsModal from './components/Settings-Modal/index.vue'
11+
import { showModal } from './components/Modal';
12+
import { drawStyles, randomPrompt, getJointPrompt } from './utils/index'
813
9-
const ws = createWebsocket({
14+
15+
const showUseModal = () => {
16+
showModal(UseModal)
17+
}
18+
19+
const showSettingsModal = () => {
20+
showModal(SettingsModal)
21+
}
22+
createWebsocket({
1023
onMessage(data) {
1124
// console.log('message event: ', event)
1225
console.log('message event: ', data)
@@ -24,20 +37,16 @@ const ws = createWebsocket({
2437
}
2538
})
2639
const contentWrap = ref<HTMLDivElement>()
27-
const data = reactive<{
28-
messages: Message[],
29-
prompt: string,
30-
pageNum: number,
31-
pageSize: number,
32-
loading: boolean,
33-
loaded: boolean
34-
}>({
40+
const data = reactive({
3541
prompt: '',
3642
messages: [],
3743
pageNum: 1,
3844
pageSize: 5,
3945
loading: false,
40-
loaded: false
46+
loaded: false,
47+
useModalVisible: false,
48+
styles: [...drawStyles],
49+
currentStyle: ''
4150
})
4251
4352
const findOneAndUpdate = (id: number, msgData: Partial<Message>) => {
@@ -55,38 +64,51 @@ const onClickSend = () => {
5564
}
5665
5766
const onKeyDown = useDebounceFn((event: Event) => {
67+
if (!data.prompt) {
68+
Toast({
69+
value: '描述不能为空'
70+
})
71+
return
72+
}
5873
event.stopPropagation()
5974
event.preventDefault()
6075
sendPrompt()
6176
})
6277
6378
const getList = (params: any) => {
64-
if (data.loading || data.loaded) return
65-
getMessagesAPI(params).then((resData) => {
66-
// data.messages = resData as any;
67-
((resData || [] as any) as []).reverse();
68-
data.messages = [...resData as any, ...data.messages];
69-
data.loaded = !resData || !(resData as any).length
70-
scrollToBottom()
71-
}).finally(() => {
72-
data.loading = false
79+
return new Promise((resolve, reject) => {
80+
if (data.loading || data.loaded) return
81+
getMessagesAPI(params).then((resData) => {
82+
// data.messages = resData as any;
83+
// ((resData || [] as any) as []).reverse();
84+
// data.messages = [...resData as any, ...data.messages];
85+
86+
data.messages = data.messages.concat(resData)
87+
88+
data.loaded = !resData || !(resData as any).length
89+
}).finally(() => {
90+
data.loading = false
91+
resolve(undefined)
92+
})
7393
})
7494
}
7595
7696
const sendPrompt = async () => {
7797
data.prompt = data.prompt.trim()
7898
if (!data.prompt) return
79-
const msgId = await createMessageAPI(data.prompt) as any;
99+
const newPmt = getJointPrompt(data.prompt)
100+
const msgId = await createMessageAPI(newPmt) as any;
80101
console.log("msgId: ", msgId)
81102
const msgObj: Message = {
82-
prompt: data.prompt,
103+
prompt: newPmt,
83104
createTime: Date.now(),
84105
uri: "",
85106
id: msgId,
86107
status: MessageStatus.INIT
87108
}
88109
89-
data.messages.push(msgObj)
110+
// data.messages.push(msgObj)
111+
data.messages.unshift(msgObj)
90112
data.prompt = ''
91113
scrollToBottom()
92114
}
@@ -97,7 +119,6 @@ const onUpscale = async (msg: Message) => {
97119
if (!msgHash || !msgId || !index || !prompt) {
98120
return console.log('upscale消息体不完整')
99121
}
100-
101122
const newId = await upscaleMessageAPI({
102123
prompt,
103124
index,
@@ -111,7 +132,8 @@ const onUpscale = async (msg: Message) => {
111132
index,
112133
status: MessageStatus.INIT,
113134
}
114-
data.messages.push(newMsg)
135+
// data.messages.push(newMsg)
136+
data.messages.unshift(newMsg)
115137
scrollToBottom()
116138
}
117139
@@ -123,19 +145,25 @@ const scrollToBottom = () => {
123145
})
124146
}
125147
126-
const onContentWrapScroll = (event: Event) => {
148+
const onContentWrapScroll = useDebounceFn((event: Event) => {
127149
if (data.loading || data.loaded) return
128150
const el = contentWrap.value
129151
if (el.scrollTop <= 100) {
130152
getList({ pageNum: ++data.pageNum, pageSize: data.pageSize })
131153
}
154+
}, 500)
155+
156+
const helpThink = () => {
157+
const p = randomPrompt()
158+
data.prompt = p
132159
}
133160
134161
onMounted(() => {
135-
getList({ pageNum: 1, pageSize: data.pageSize })
136-
scrollToBottom()
137-
setTimeout(() => {
138-
contentWrap.value.addEventListener('scroll', onContentWrapScroll)
162+
getList({ pageNum: 1, pageSize: data.pageSize }).finally(() => {
163+
setTimeout(() => {
164+
contentWrap.value.addEventListener('scroll', onContentWrapScroll)
165+
scrollToBottom()
166+
}, 32)
139167
})
140168
141169
return () => {
@@ -146,27 +174,28 @@ onMounted(() => {
146174
</script>
147175

148176
<template>
149-
<div class="h-full py-4 max-sm:py-0">
150-
<div class="relative h-full max-w-[980px] m-auto bg-gray-600 text-white px-10 py-4 rounded-lg sm:py-0 max-sm:py-2 max-sm:px-4 max-sm:rounded-none">
151-
<div id="contentWrap" ref="contentWrap" class="h-full pb-[120px] overflow-auto m-auto">
152-
<div class="border-b-2 border-purple-400" v-for="(item, index) in data.messages" :key="index">
153-
<MessageItem :message="item" @on-upscale="onUpscale" />
177+
<div class="h-full w-full max-sm:py-0 bg-gray-600">
178+
<div class="relative h-full max-w-[980px] bg-gray-700 m-auto text-white px-10 py-4 sm:py-0 max-sm:py-2 max-sm:px-4 rounded-none">
179+
<div id="contentWrap" ref="contentWrap" class="h-[calc(100%-150px)] overflow-auto m-auto flex flex-col-reverse">
180+
<div class="border-b-2 border-purple-400" v-for="(item, index) in data.messages" :key="item.id">
181+
<MessageItem :key="item.id":message="item" @on-upscale="onUpscale" />
154182
</div>
155183
</div>
156184
<div class="absolute left-10 right-10 max-sm:left-2 max-sm:right-2 bottom-4 max-sm:bottom-2 flex-row items-center justify-between px-2 py-2 bg-gray-100 border-gray-400 rounded-[4px]">
157185
<!-- 快捷/帮助区域 -->
158-
<div class="flex w-full pb-1 text-sm">
159-
<p class="underline text-orange-400 pr-1 cursor-pointer">垫图</p>
160-
<p class="underline text-orange-400 pr-1 cursor-pointer">帮我想一个</p>
186+
<div class="flex w-full px-2 pb-2 pt-1 text-md select-none">
187+
<p class="underline text-orange-400 pr-4 cursor-pointer" @click.stop="helpThink">帮我想一个</p>
188+
<p class="underline text-orange-400 pr-4 cursor-pointer" @click.stop="showSettingsModal">设置参数</p>
189+
<p class="underline text-orange-400 pr-4 cursor-pointer" @click.stop="showSettingsModal">设置风格</p>
161190
<p class="flex-1"></p>
162-
<p class="underline text-orange-400 pr-1 cursor-pointer">微信交流群</p>
163-
<p class="underline text-orange-400 cursor-pointer">使用说明</p>
164-
</div>
191+
<p class="underline text-orange-400 pr-4 cursor-pointer">微信交流群</p>
192+
<p class="underline text-orange-400 cursor-pointer"@click.stop="showUseModal">使用说明</p>
193+
</div>
165194
<!-- 输入框 -->
166195
<div class="w-full bg-white flex items-center">
167196
<textarea
168197
v-model="data.prompt"
169-
class="flex-1 px-4 py-2 rounded-[8px] outline-none text-sm text-gray-500"
198+
class="flex-1 px-4 py-2 resize-none rounded-[8px] outline-none max-sm:text-sm text-md text-gray-500"
170199
autofocus
171200
placeholder="输入图片描述,例如'可爱的橘黄色的猫咪, 迪士尼风格'、'海边,机器人,小女孩,吉卜力风格'等"
172201
type="textarea"

‎src/Message.vue‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { copyString } from './utils/clipboard';
44
import Tag from './components/Tag.vue';
55
import Image from './components/Image.vue';
66
import { useDebounceFn } from '@vueuse/core'
7+
import { onMounted, ref, computed } from 'vue'
8+
import Toast from './components/Toast';
79
10+
const itemWidth = ref(0)
11+
const imageRef = ref<any>()
812
const props = defineProps({
913
message: {
1014
type: Object as () => Message
@@ -15,6 +19,9 @@ const emits = defineEmits(['on-upscale'])
1519
1620
const clickToCopy = (str: string) => {
1721
copyString(str)
22+
Toast({
23+
value: "复制成功"
24+
})
1825
}
1926
2027
const getTimeStr = (timestamp: number | string) => {
@@ -38,10 +45,15 @@ const getMessageStatus = (status: number) => {
3845
return '已完成'
3946
case MessageStatus.TIMEOUT:
4047
return '已超时'
48+
case MessageStatus.FAILED:
49+
return '服务错误'
50+
case MessageStatus.SENSITIVE:
51+
return '输入错误,敏感词汇'
4152
default:
42-
return '排队中'
53+
return '初始化'
4354
}
4455
}
56+
4557
</script>
4658

4759
<template>
@@ -55,8 +67,10 @@ const getMessageStatus = (status: number) => {
5567
</div>
5668
<div class="text-sm text-slate-300 pt-1">状态:{{ getMessageStatus(message.status) }}</div>
5769
<div class="text-sm text-slate-300">时间:{{ message?.createTime ? getTimeStr(message?.createTime) : '2023年6月2日 下午6:06:32' }}</div>
58-
<Image loadText="排队生成中" :url="message.uri" />
59-
<!-- upscale area -->
70+
71+
<Image v-if="message.uri" ref="imageRef" loadText="排队生成中" :url="message.uri" />
72+
73+
<!-- upscale area 升级区域 -->
6074
<div v-if="message?.status == MessageStatus.DONE && !message.index">
6175
<Tag text="U1" @click="onClickUV(1)" />
6276
<Tag text="U2" @click="onClickUV(2)" />

‎src/components/HelloWorld.vue‎

Lines changed: 0 additions & 38 deletions
This file was deleted.

‎src/components/Image.vue‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@ const onLoad = () => {
4747
</script>
4848

4949
<template>
50-
<img v-if="trueUrl" v-lazy="trueUrl" class="my-1 w-[100%]" loading="lazy" alt="" @loadstart="onLoadStart" @error="onLoadError" @load="onLoad">
50+
<div class="relative w-full bg-gray-500 rounded my-2 h-0 pb-[100%]">
51+
<img v-if="trueUrl" v-lazy="trueUrl" class="absolute top-0 left-0 w-[100%] h-[100%] object-contain" loading="lazy" alt="" @loadstart="onLoadStart" @error="onLoadError" @load="onLoad">
52+
</div>
5153
</template>

‎src/components/Modal/index.tsx‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Modal from './index.vue'
2+
import { createVNode, VNodeTypes, render } from 'vue'
3+
4+
interface IShowModal {
5+
title?: string;
6+
visible?: boolean;
7+
onClose?: () => void;
8+
}
9+
export const showModal = (component: VNodeTypes, props?: IShowModal) => {
10+
return new Promise((resolve, reject) => {
11+
const container = document.createElement('div')
12+
13+
const vm = createVNode(Modal, {
14+
title: "使用说明",
15+
modelValue: true,
16+
onClose: () => {
17+
document.body.removeChild(container)
18+
props?.onClose()
19+
}
20+
}, {
21+
default: () => createVNode(component, {
22+
close: () => {
23+
vm.component.exposed.close()
24+
}
25+
})
26+
})
27+
28+
render(vm, container);
29+
document.body.appendChild(container)
30+
})
31+
}

‎src/components/Modal/index.vue‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script setup lang="ts">
2+
import { ref, watch, VNodeTypes } from 'vue'
3+
const props = defineProps({
4+
modelValue: {
5+
type: Boolean,
6+
default: false
7+
},
8+
title: {
9+
type: String,
10+
default: ""
11+
},
12+
onClose: {
13+
type: Function,
14+
default: null
15+
},
16+
})
17+
const emit = defineEmits(['update:modelValue'])
18+
19+
const visible = ref(props.modelValue)
20+
21+
watch(visible, (value) => {
22+
emit('update:modelValue', value)
23+
})
24+
25+
watch(() => props.modelValue, (value) => {
26+
visible.value = value
27+
})
28+
29+
const onManualClose = () => {
30+
visible.value = false
31+
props.onClose?.();
32+
}
33+
34+
defineExpose({
35+
close: onManualClose
36+
})
37+
38+
</script>
39+
40+
<template>
41+
<div v-if="visible" class="fixed top-0 left-0 right-0 bottom-0 z-[2000] w-full h-full flex items-center justify-center">
42+
<div class="fixed top-0 right-0 bottom-0 left-0 w-full h-full z-[2001] bg-black opacity-50"></div>
43+
<div class="max-sm:w-[90%] text-white bg-gray-500 px-3 p-2 rounded z-[2002]">
44+
<div v-if="title" class="flex py-2 flex-row justify-between">
45+
<div class="">{{ title }}</div>
46+
<div class="text-orange-500 cursor-pointer" @click.stop="onManualClose">x</div>
47+
</div>
48+
<div class="py-1">
49+
<slot />
50+
</div>
51+
</div>
52+
</div>
53+
</template>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /