mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: change theme for mobile site is ready
This commit is contained in:
		@@ -7,8 +7,12 @@
 | 
			
		||||
* 功能优化:优化首页登录注册页面的 UI
 | 
			
		||||
* BUG修复:修复License验证的逻辑漏洞
 | 
			
		||||
* Bug修复:后台添加用户的时候密码规则限制跟前台注册保持一致
 | 
			
		||||
* 功能新增:管理后台支持切换主题,支持 light 和 dark 模式
 | 
			
		||||
* 功能新增:管理后台支持切换主题,支持 light 和 dark 两种主题
 | 
			
		||||
* 功能新增:移动端新增 DALL-E 绘画功能
 | 
			
		||||
* 功能新增:新增移动端首页功能,移动端支持 light 和 dark 两种主题
 | 
			
		||||
* 功能新增:移动支持免登录预览功能
 | 
			
		||||
* Bug修复:解决在同一个浏览器开启多个对话时候对话内容会相互乱串的问题
 | 
			
		||||
* Bug修复:修复部分中转 API 模型会出现第一输出的字符被淹没的Bug
 | 
			
		||||
 | 
			
		||||
## v4.0.4
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								web/src/store/eventbus.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/src/store/eventbus.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// 导入mitt包
 | 
			
		||||
import mitt from 'mitt'
 | 
			
		||||
// 创建EventBus实例对象
 | 
			
		||||
const bus = mitt()
 | 
			
		||||
// 共享出eventbus的实例对象
 | 
			
		||||
export default bus
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -286,7 +286,7 @@ const notice = ref("")
 | 
			
		||||
const noticeKey = ref("SYSTEM_NOTICE")
 | 
			
		||||
 | 
			
		||||
if (isMobile()) {
 | 
			
		||||
  router.replace("/mobile")
 | 
			
		||||
  router.replace("/mobile/chat")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取系统配置
 | 
			
		||||
 
 | 
			
		||||
@@ -760,6 +760,7 @@ onMounted(() => {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  clipboard.value.destroy()
 | 
			
		||||
  socket.value = null
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,14 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <van-config-provider :theme="getMobileTheme()">
 | 
			
		||||
  <van-config-provider :theme="theme">
 | 
			
		||||
    <div class="mobile-home">
 | 
			
		||||
      <router-view/>
 | 
			
		||||
 | 
			
		||||
      <van-tabbar route v-model="active" @change="onChange">
 | 
			
		||||
      <van-tabbar route v-model="active">
 | 
			
		||||
        <van-tabbar-item to="/mobile/index" name="home" icon="home-o">首页</van-tabbar-item>
 | 
			
		||||
        <van-tabbar-item to="/mobile/chat" name="chat" icon="chat-o">对话</van-tabbar-item>
 | 
			
		||||
        <van-tabbar-item to="/mobile/image" name="image" icon="photo-o">绘图</van-tabbar-item>
 | 
			
		||||
        <van-tabbar-item to="/mobile/profile" name="profile" icon="user-o">我的</van-tabbar-item>
 | 
			
		||||
        <van-tabbar-item to="/mobile/profile" name="profile" icon="user-o">我的
 | 
			
		||||
        </van-tabbar-item>
 | 
			
		||||
      </van-tabbar>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -17,9 +18,10 @@
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {getMobileTheme} from "@/store/system";
 | 
			
		||||
import {getMobileTheme, setMobileTheme} from "@/store/system";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {isMobile} from "@/utils/libs";
 | 
			
		||||
import bus from '@/store/eventbus'
 | 
			
		||||
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
if (!isMobile()) {
 | 
			
		||||
@@ -27,9 +29,12 @@ if (!isMobile()) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const active = ref('home')
 | 
			
		||||
const onChange = (index) => {
 | 
			
		||||
  console.log(index)
 | 
			
		||||
}
 | 
			
		||||
const theme = ref(getMobileTheme())
 | 
			
		||||
 | 
			
		||||
bus.on('changeTheme', (value) => {
 | 
			
		||||
  theme.value = value
 | 
			
		||||
  setMobileTheme(theme.value)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
 | 
			
		||||
          <van-field label="有效期" v-if="form.expired_time > 0">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-tag type="warning">{{ dateFormat(form.expired_time) }}</van-tag>
 | 
			
		||||
              {{ dateFormat(form.expired_time) }}
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
 | 
			
		||||
@@ -39,11 +39,15 @@
 | 
			
		||||
 | 
			
		||||
      <div class="opt" v-if="isLogin">
 | 
			
		||||
        <van-row :gutter="10">
 | 
			
		||||
          <van-col :span="12">
 | 
			
		||||
            <van-button round block type="primary" @click="showPasswordDialog = true">修改密码</van-button>
 | 
			
		||||
          <van-col :span="8">
 | 
			
		||||
            <van-button round block @click="showPasswordDialog = true" size="small">修改密码</van-button>
 | 
			
		||||
          </van-col>
 | 
			
		||||
          <van-col :span="12">
 | 
			
		||||
            <van-button round block @click="logout">退出登录</van-button>
 | 
			
		||||
          <van-col :span="8">
 | 
			
		||||
            <van-button round block @click="logout" size="small">退出登录</van-button>
 | 
			
		||||
          </van-col>
 | 
			
		||||
 | 
			
		||||
          <van-col :span="8">
 | 
			
		||||
            <van-button round block @click="showSettings = true" icon="setting" size="small">设置</van-button>
 | 
			
		||||
          </van-col>
 | 
			
		||||
        </van-row>
 | 
			
		||||
      </div>
 | 
			
		||||
@@ -114,6 +118,34 @@
 | 
			
		||||
        </van-cell-group>
 | 
			
		||||
      </van-form>
 | 
			
		||||
    </van-dialog>
 | 
			
		||||
 | 
			
		||||
    <van-action-sheet v-model:show="showSettings" title="用户设置">
 | 
			
		||||
      <div class="setting-content">
 | 
			
		||||
        <van-form>
 | 
			
		||||
          <van-cell-group inset>
 | 
			
		||||
            <van-field name="switch" label="暗黑主题">
 | 
			
		||||
              <template #input>
 | 
			
		||||
                <van-switch v-model="dark" @change="changeTheme"/>
 | 
			
		||||
              </template>
 | 
			
		||||
            </van-field>
 | 
			
		||||
            <!--            <van-field-->
 | 
			
		||||
            <!--                v-model="password"-->
 | 
			
		||||
            <!--                type="password"-->
 | 
			
		||||
            <!--                name="密码"-->
 | 
			
		||||
            <!--                label="密码"-->
 | 
			
		||||
            <!--                placeholder="密码"-->
 | 
			
		||||
            <!--                :rules="[{ required: true, message: '请填写密码' }]"-->
 | 
			
		||||
            <!--            />-->
 | 
			
		||||
          </van-cell-group>
 | 
			
		||||
          <!--          <div style="margin: 16px;">-->
 | 
			
		||||
          <!--            <van-button round block type="primary" native-type="submit">-->
 | 
			
		||||
          <!--              提交-->
 | 
			
		||||
          <!--            </van-button>-->
 | 
			
		||||
          <!--          </div>-->
 | 
			
		||||
        </van-form>
 | 
			
		||||
      </div>
 | 
			
		||||
    </van-action-sheet>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -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')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
@@ -305,8 +347,10 @@ const logout = function () {
 | 
			
		||||
    .product-list {
 | 
			
		||||
      padding 0 15px
 | 
			
		||||
 | 
			
		||||
      color var(--van-text-color)
 | 
			
		||||
 | 
			
		||||
      .item {
 | 
			
		||||
        border 1px solid #e5e5e5
 | 
			
		||||
        border 1px solid var(--van-border-color)
 | 
			
		||||
        border-radius 10px
 | 
			
		||||
        margin-bottom 15px
 | 
			
		||||
        overflow hidden
 | 
			
		||||
@@ -327,6 +371,10 @@ const logout = function () {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .van-cell__value {
 | 
			
		||||
          flex 2
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .price {
 | 
			
		||||
          font-size 18px
 | 
			
		||||
          color #f56c6c
 | 
			
		||||
@@ -334,5 +382,9 @@ const logout = function () {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .setting-content {
 | 
			
		||||
    padding 16px
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -270,6 +270,7 @@
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <button style="display: none" class="copy-prompt" :data-clipboard-text="prompt" id="copy-btn">复制</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -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(() => {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -198,7 +198,7 @@
 | 
			
		||||
                <el-button type="success" v-else @click="publishImage($event, item, true)" circle>
 | 
			
		||||
                  <i class="iconfont icon-share-bold"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
                <el-button type="primary" @click="showTask(item)" circle>
 | 
			
		||||
                <el-button type="primary" @click="showPrompt(item)" circle>
 | 
			
		||||
                  <i class="iconfont icon-prompt"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </div>
 | 
			
		||||
@@ -208,7 +208,7 @@
 | 
			
		||||
      </van-list>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <button style="display: none" class="copy-prompt-sd" :data-clipboard-text="prompt" id="copy-btn-sd">复制</button>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -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) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,13 @@
 | 
			
		||||
              style="height: 100%;width: 100%;"
 | 
			
		||||
          >
 | 
			
		||||
            <van-cell v-for="item in data['mj'].data" :key="item.id">
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="showPrompt(item)" fit="cover"/>
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="imageView(item)" fit="cover"/>
 | 
			
		||||
 | 
			
		||||
              <div class="opt-box">
 | 
			
		||||
                <el-button type="primary" @click="showPrompt(item)" circle>
 | 
			
		||||
                  <i class="iconfont icon-prompt"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </van-cell>
 | 
			
		||||
          </van-list>
 | 
			
		||||
        </van-tab>
 | 
			
		||||
@@ -27,7 +33,13 @@
 | 
			
		||||
              @load="onLoad"
 | 
			
		||||
          >
 | 
			
		||||
            <van-cell v-for="item in data['sd'].data" :key="item.id">
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="showPrompt(item)" fit="cover"/>
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="imageView(item)" fit="cover"/>
 | 
			
		||||
 | 
			
		||||
              <div class="opt-box">
 | 
			
		||||
                <el-button type="primary" @click="showPrompt(item)" circle>
 | 
			
		||||
                  <i class="iconfont icon-prompt"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </van-cell>
 | 
			
		||||
          </van-list>
 | 
			
		||||
        </van-tab>
 | 
			
		||||
@@ -36,13 +48,19 @@
 | 
			
		||||
        </van-tab>
 | 
			
		||||
      </van-tabs>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <button style="display: none" class="copy-prompt-wall" :data-clipboard-text="prompt" id="copy-btn-wall">复制
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {onMounted, onUnmounted, ref} from "vue";
 | 
			
		||||
import {httpGet} from "@/utils/http";
 | 
			
		||||
import {showDialog, showFailToast} from "vant";
 | 
			
		||||
import {showConfirmDialog, showDialog, showFailToast, showImagePreview, showNotify} from "vant";
 | 
			
		||||
import {Delete} from "@element-plus/icons-vue";
 | 
			
		||||
import Clipboard from "clipboard";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
 | 
			
		||||
const activeName = ref("mj")
 | 
			
		||||
const data = ref({
 | 
			
		||||
@@ -52,7 +70,7 @@ const data = ref({
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/mj/jobs",
 | 
			
		||||
    url: "/api/mj/imgWall",
 | 
			
		||||
    data: []
 | 
			
		||||
  },
 | 
			
		||||
  "sd": {
 | 
			
		||||
@@ -61,7 +79,7 @@ const data = ref({
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/sd/jobs",
 | 
			
		||||
    url: "/api/sd/imgWall",
 | 
			
		||||
    data: []
 | 
			
		||||
  },
 | 
			
		||||
  "dalle3": {
 | 
			
		||||
@@ -70,11 +88,32 @@ const data = ref({
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/dalle3/jobs",
 | 
			
		||||
    url: "/api/dalle3/imgWall",
 | 
			
		||||
    data: []
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const prompt = ref('')
 | 
			
		||||
const clipboard = ref(null)
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  clipboard.value = new Clipboard(".copy-prompt-wall");
 | 
			
		||||
  clipboard.value.on('success', () => {
 | 
			
		||||
    showNotify({type: 'success', message: '复制成功', duration: 1000})
 | 
			
		||||
  })
 | 
			
		||||
  clipboard.value.on('error', () => {
 | 
			
		||||
    showNotify({type: 'danger', message: '复制失败', duration: 2000})
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  clipboard.value.on('error', () => {
 | 
			
		||||
    ElMessage.error('复制失败!');
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  clipboard.value.destroy()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const onLoad = () => {
 | 
			
		||||
  const d = data.value[activeName.value]
 | 
			
		||||
  httpGet(`${d.url}?status=1&page=${d.page}&page_size=${d.pageSize}&publish=true`).then(res => {
 | 
			
		||||
@@ -105,22 +144,39 @@ const onLoad = () => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showPrompt = (item) => {
 | 
			
		||||
  showDialog({
 | 
			
		||||
  prompt.value = item.prompt
 | 
			
		||||
  showConfirmDialog({
 | 
			
		||||
    title: "绘画提示词",
 | 
			
		||||
    message: item.prompt,
 | 
			
		||||
    confirmButtonText: "复制",
 | 
			
		||||
    cancelButtonText: "关闭",
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    // on close
 | 
			
		||||
    document.querySelector('#copy-btn-wall').click()
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const imageView = (item) => {
 | 
			
		||||
  showImagePreview([item['img_url']]);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.img-wall {
 | 
			
		||||
  .content {
 | 
			
		||||
    .van-cell__value {
 | 
			
		||||
      min-height 80px
 | 
			
		||||
 | 
			
		||||
      .van-image {
 | 
			
		||||
        width 100%
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .opt-box {
 | 
			
		||||
        position absolute
 | 
			
		||||
        right 0
 | 
			
		||||
        top 0
 | 
			
		||||
        padding 10px
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user