diff --git a/CHANGELOG.md b/CHANGELOG.md index d0689b25..2d3dd7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,12 @@ * 功能优化:优化首页登录注册页面的 UI * BUG修复:修复License验证的逻辑漏洞 * Bug修复:后台添加用户的时候密码规则限制跟前台注册保持一致 -* 功能新增:管理后台支持切换主题,支持 light 和 dark 模式 +* 功能新增:管理后台支持切换主题,支持 light 和 dark 两种主题 * 功能新增:移动端新增 DALL-E 绘画功能 +* 功能新增:新增移动端首页功能,移动端支持 light 和 dark 两种主题 +* 功能新增:移动支持免登录预览功能 +* Bug修复:解决在同一个浏览器开启多个对话时候对话内容会相互乱串的问题 +* Bug修复:修复部分中转 API 模型会出现第一输出的字符被淹没的Bug ## v4.0.4 diff --git a/api/core/types/session.go b/api/core/types/session.go index ab95de56..c756df66 100644 --- a/api/core/types/session.go +++ b/api/core/types/session.go @@ -5,7 +5,6 @@ const LoginUserCache = "LOGIN_USER_CACHE" const UserAuthHeader = "Authorization" const AdminAuthHeader = "Admin-Authorization" -const ChatTokenHeader = "Chat-Token" // Session configs struct type Session struct { diff --git a/api/handler/chatimpl/chat_handler.go b/api/handler/chatimpl/chat_handler.go index 4b745d0b..80077ea9 100644 --- a/api/handler/chatimpl/chat_handler.go +++ b/api/handler/chatimpl/chat_handler.go @@ -139,6 +139,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { if err != nil { client.Close() h.App.ChatClients.Delete(sessionId) + h.App.ChatSession.Delete(sessionId) cancelFunc := h.App.ReqCancelFunc.Get(sessionId) if cancelFunc != nil { cancelFunc() diff --git a/api/handler/chatimpl/openai_handler.go b/api/handler/chatimpl/openai_handler.go index 2eb32866..d743e7d9 100644 --- a/api/handler/chatimpl/openai_handler.go +++ b/api/handler/chatimpl/openai_handler.go @@ -115,11 +115,8 @@ func (h *ChatHandler) sendOpenAiMessage( break } - // 初始化 role - if responseBody.Choices[0].Delta.Role != "" && message.Role == "" { - message.Role = responseBody.Choices[0].Delta.Role - continue - } else if responseBody.Choices[0].FinishReason != "" { + // output stopped + if responseBody.Choices[0].FinishReason != "" { break // 输出完成或者输出中断了 } else { content := responseBody.Choices[0].Delta.Content diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 29692cc8..4f20acf2 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -232,18 +232,10 @@ func (h *UserHandler) Login(c *gin.Context) { // Logout 注 销 func (h *UserHandler) Logout(c *gin.Context) { - sessionId := c.GetHeader(types.ChatTokenHeader) key := h.GetUserKey(c) if _, err := h.redis.Del(c, key).Result(); err != nil { logger.Error("error with delete session: ", err) } - // 删除 websocket 会话列表 - h.App.ChatSession.Delete(sessionId) - // 关闭 socket 连接 - client := h.App.ChatClients.Get(sessionId) - if client != nil { - client.Close() - } resp.SUCCESS(c) } diff --git a/web/package.json b/web/package.json index 35ff6492..913c8d4a 100644 --- a/web/package.json +++ b/web/package.json @@ -26,6 +26,7 @@ "markmap-lib": "^0.16.1", "markmap-view": "^0.16.0", "md-editor-v3": "^2.2.1", + "mitt": "^3.0.1", "pinia": "^2.1.4", "qrcode": "^1.5.3", "qs": "^6.11.1", diff --git a/web/src/assets/css/mobile/image-mj.styl b/web/src/assets/css/mobile/image-mj.styl index 694d0f0f..c98f1587 100644 --- a/web/src/assets/css/mobile/image-mj.styl +++ b/web/src/assets/css/mobile/image-mj.styl @@ -8,7 +8,7 @@ .rate { display: flex; justify-content center - background-color #f5f5f5 + background-color var(--van-background-3) padding 5px 10px margin 5px 0 border-radius 5px @@ -24,14 +24,14 @@ .text { text-align center - color #555555 + color var(--van-text-color) } } .model { display: flex; justify-content center - background-color #f5f5f5 + background-color var(--van-background-3) padding 6px margin 5px 0 border-radius 5px @@ -48,12 +48,12 @@ .text { text-align center - color #555555 + color var(--van-text-color) } } .active { - background-color #e5e5e5 + background-color var(--van-text-color-3) } } } diff --git a/web/src/main.js b/web/src/main.js index 0e2bd740..2b8ca4cc 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -5,6 +5,7 @@ import 'vant/lib/index.css'; import App from './App.vue' import {createPinia} from "pinia"; import { + ActionSheet, Badge, Button, Cell, @@ -14,7 +15,8 @@ import { Collapse, CollapseItem, ConfigProvider, - Dialog, Divider, + Dialog, + Divider, DropdownItem, DropdownMenu, Empty, @@ -28,7 +30,8 @@ import { Lazyload, List, Loading, - NavBar, NoticeBar, + NavBar, + NoticeBar, Notify, Overlay, Picker, @@ -52,8 +55,8 @@ import {router} from "@/router"; import 'v3-waterfall/dist/style.css' import V3waterfall from "v3-waterfall"; -const app = createApp(App) -app.use(createPinia()) +const app = createApp(App); +app.use(createPinia()); app.use(ConfigProvider); app.use(Tabbar); app.use(TabbarItem); @@ -99,6 +102,7 @@ app.use(Tab); app.use(Tabs); app.use(Divider); app.use(NoticeBar); +app.use(ActionSheet); app.use(router).use(ElementPlus).mount('#app') diff --git a/web/src/store/eventbus.js b/web/src/store/eventbus.js new file mode 100644 index 00000000..f8a14c1c --- /dev/null +++ b/web/src/store/eventbus.js @@ -0,0 +1,6 @@ +// 导入mitt包 +import mitt from 'mitt' +// 创建EventBus实例对象 +const bus = mitt() +// 共享出eventbus的实例对象 +export default bus \ No newline at end of file diff --git a/web/src/store/session.js b/web/src/store/session.js index 770c6bb5..fa379512 100644 --- a/web/src/store/session.js +++ b/web/src/store/session.js @@ -5,25 +5,11 @@ import Storage from "good-storage"; * storage handler */ -const SessionIDKey = process.env.VUE_APP_KEY_PREFIX + 'SESSION_ID'; const UserTokenKey = process.env.VUE_APP_KEY_PREFIX + "Authorization"; const AdminTokenKey = process.env.VUE_APP_KEY_PREFIX + "Admin-Authorization" export function getSessionId() { - let sessionId = Storage.get(SessionIDKey) - if (!sessionId) { - sessionId = randString(42) - setSessionId(sessionId) - } - return sessionId -} - -export function removeSessionId() { - Storage.remove(SessionIDKey) -} - -export function setSessionId(sessionId) { - Storage.set(SessionIDKey, sessionId) + return randString(42) } export function getUserToken() { diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index b0bcc7c7..ae08ffdc 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -286,7 +286,7 @@ const notice = ref("") const noticeKey = ref("SYSTEM_NOTICE") if (isMobile()) { - router.replace("/mobile") + router.replace("/mobile/chat") } // 获取系统配置 diff --git a/web/src/views/ImageMj.vue b/web/src/views/ImageMj.vue index 837dd83e..d6b13933 100644 --- a/web/src/views/ImageMj.vue +++ b/web/src/views/ImageMj.vue @@ -760,6 +760,7 @@ onMounted(() => { }) onUnmounted(() => { + clipboard.value.destroy() socket.value = null }) diff --git a/web/src/views/mobile/Home.vue b/web/src/views/mobile/Home.vue index 8be8f3f1..3b8b425b 100644 --- a/web/src/views/mobile/Home.vue +++ b/web/src/views/mobile/Home.vue @@ -1,13 +1,14 @@ @@ -127,6 +159,8 @@ import {ElMessage} from "element-plus"; import {checkSession} from "@/action/session"; import {useRouter} from "vue-router"; import {removeUserToken} from "@/store/session"; +import bus from '@/store/eventbus' +import {getMobileTheme} from "@/store/system"; const form = ref({ username: 'GeekMaster', @@ -148,6 +182,7 @@ const payWays = ref({}) const router = useRouter() const userId = ref(0) const isLogin = ref(false) +const showSettings = ref(false) onMounted(() => { checkSession().then(user => { @@ -276,6 +311,13 @@ const logout = function () { showFailToast('注销失败!'); }) } + +const dark = ref(getMobileTheme() === 'dark') + +const changeTheme = () => { + bus.emit('changeTheme', dark.value ? 'dark' : 'light') +} + \ No newline at end of file diff --git a/web/src/views/mobile/pages/ImageMj.vue b/web/src/views/mobile/pages/ImageMj.vue index fe44d9e2..f3bcfcca 100644 --- a/web/src/views/mobile/pages/ImageMj.vue +++ b/web/src/views/mobile/pages/ImageMj.vue @@ -270,6 +270,7 @@ + @@ -280,7 +281,6 @@ import { showFailToast, showNotify, showToast, - showDialog, showImagePreview, showSuccessToast } from "vant"; @@ -291,6 +291,7 @@ import {checkSession} from "@/action/session"; import {useRouter} from "vue-router"; import {Delete} from "@element-plus/icons-vue"; import {showLoginDialog} from "@/utils/libs"; +import Clipboard from "clipboard"; const activeColspan = ref([""]) @@ -337,8 +338,18 @@ const socket = ref(null) const power = ref(0) const activeName = ref("txt2img") const isLogin = ref(false) +const prompt = ref('') +const clipboard = ref(null) onMounted(() => { + clipboard.value = new Clipboard(".copy-prompt"); + clipboard.value.on('success', () => { + showNotify({type: 'success', message: '复制成功', duration: 1000}) + }) + clipboard.value.on('error', () => { + showNotify({type: 'danger', message: '复制失败', duration: 2000}) + }) + checkSession().then(user => { power.value = user['power'] userId.value = user.id @@ -354,6 +365,7 @@ onMounted(() => { onUnmounted(() => { socket.value = null + clipboard.value.destroy() }) const mjPower = ref(1) @@ -616,11 +628,15 @@ const publishImage = (item, action) => { } const showPrompt = (item) => { - showDialog({ + prompt.value = item.prompt + showConfirmDialog({ title: "绘画提示词", message: item.prompt, + confirmButtonText: "复制", + cancelButtonText: "关闭", }).then(() => { - // on close + document.querySelector('#copy-btn').click() + }).catch(() => { }); } diff --git a/web/src/views/mobile/pages/ImageSd.vue b/web/src/views/mobile/pages/ImageSd.vue index 407cb3f1..2e8a8605 100644 --- a/web/src/views/mobile/pages/ImageSd.vue +++ b/web/src/views/mobile/pages/ImageSd.vue @@ -198,7 +198,7 @@ - + @@ -208,7 +208,7 @@ - + @@ -232,7 +232,6 @@ import {showLoginDialog} from "@/utils/libs"; const listBoxHeight = ref(window.innerHeight - 40) const mjBoxHeight = ref(window.innerHeight - 150) -const showTaskDialog = ref(false) const item = ref({}) const isLogin = ref(false) const activeColspan = ref([""]) @@ -338,15 +337,15 @@ const connect = () => { } const clipboard = ref(null) +const prompt = ref('') onMounted(() => { initData() - clipboard.value = new Clipboard('.copy-prompt-sd'); + clipboard.value = new Clipboard(".copy-prompt-sd"); clipboard.value.on('success', () => { - showNotify({type: "success", message: "复制成功!"}); + showNotify({type: 'success', message: '复制成功', duration: 1000}) }) - clipboard.value.on('error', () => { - showNotify({type: "danger", message: '复制失败!'}); + showNotify({type: 'danger', message: '复制失败', duration: 2000}) }) httpGet("/api/config/get?key=system").then(res => { @@ -438,7 +437,7 @@ const generate = () => { return showToast("请输入绘画提示词!") } - if (params.value.seed === '') { + if (!params.value.seed) { params.value.seed = -1 } params.value.session_id = getSessionId() @@ -450,14 +449,17 @@ const generate = () => { }) } -const showTask = (row) => { - item.value = row - showTaskDialog.value = true -} - -const copyParams = (row) => { - params.value = row.params - showTaskDialog.value = false +const showPrompt = (item) => { + prompt.value = item.prompt + showConfirmDialog({ + title: "绘画提示词", + message: item.prompt, + confirmButtonText: "复制", + cancelButtonText: "关闭", + }).then(() => { + document.querySelector('#copy-btn-sd').click() + }).catch(() => { + }); } const removeImage = (event, item) => { diff --git a/web/src/views/mobile/pages/ImgWall.vue b/web/src/views/mobile/pages/ImgWall.vue index 5e8121f1..65edbc2a 100644 --- a/web/src/views/mobile/pages/ImgWall.vue +++ b/web/src/views/mobile/pages/ImgWall.vue @@ -13,7 +13,13 @@ style="height: 100%;width: 100%;" > - + + +
+ + + +
@@ -27,7 +33,13 @@ @load="onLoad" > - + + +
+ + + +
@@ -36,13 +48,19 @@ + +