mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 08:46:38 +08:00
feat: image wall page is ready
This commit is contained in:
parent
1759fd4cf9
commit
9fcd686fda
@ -1,12 +1,12 @@
|
||||
# ChatGPT-Plus
|
||||
|
||||
**ChatGPT-PLUS** 基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案,自带运营管理后台,开箱即用。集成了 OpenAI, Azure,
|
||||
ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。主要有如下特性:
|
||||
ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了 MidJourney 和 Stable Diffusion AI绘画功能。主要有如下特性:
|
||||
|
||||
* 完整的开源系统,前端应用和后台管理系统皆可开箱即用。
|
||||
* 聊天体验跟 ChatGPT 官方版本完全一致。
|
||||
* 内置了各种预训练好的角色,比如小红书写手,英语翻译大师,苏格拉底,孔子,乔布斯,周报助手等。轻松满足你的各种聊天和应用需求。
|
||||
* 支持 MidJourney AI 绘画集成,开箱即用。
|
||||
* 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。
|
||||
* 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。(可定制开发其他支付通道支持)
|
||||
* 集成插件 API 功能,可结合 GPT 开发各种强大的插件,已内置实现了微博热搜,今日头条,今日早报和 AI 绘画函数插件。
|
||||
|
||||
|
@ -315,14 +315,26 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
// JobList 获取 MJ 任务列表
|
||||
func (h *MidJourneyHandler) JobList(c *gin.Context) {
|
||||
status := h.GetInt(c, "status", 0)
|
||||
var items []model.MidJourneyJob
|
||||
var res *gorm.DB
|
||||
userId, _ := c.Get(types.LoginUserID)
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
page := h.GetInt(c, "page", 0)
|
||||
pageSize := h.GetInt(c, "page_size", 0)
|
||||
|
||||
session := h.db.Session(&gorm.Session{})
|
||||
if status == 1 {
|
||||
res = h.db.Where("user_id = ? AND progress = 100", userId).Order("id DESC").Find(&items)
|
||||
session = session.Where("progress = ?", 100).Order("id DESC")
|
||||
} else {
|
||||
res = h.db.Where("user_id = ? AND progress < 100", userId).Order("id ASC").Find(&items)
|
||||
session = session.Where("progress < ?", 100).Order("id ASC")
|
||||
}
|
||||
if userId > 0 {
|
||||
session = session.Where("user_id = ?", userId)
|
||||
}
|
||||
if page > 0 && pageSize > 0 {
|
||||
offset := (page - 1) * pageSize
|
||||
session = session.Offset(offset).Limit(pageSize)
|
||||
}
|
||||
|
||||
var items []model.MidJourneyJob
|
||||
res := session.Find(&items)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, types.NoData)
|
||||
return
|
||||
|
11
web/package-lock.json
generated
11
web/package-lock.json
generated
@ -23,6 +23,7 @@
|
||||
"pinia": "^2.1.4",
|
||||
"qs": "^6.11.1",
|
||||
"sortablejs": "^1.15.0",
|
||||
"v3-waterfall": "^1.2.1",
|
||||
"vant": "^4.5.0",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.15"
|
||||
@ -10459,6 +10460,11 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v3-waterfall": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/v3-waterfall/-/v3-waterfall-1.2.1.tgz",
|
||||
"integrity": "sha512-zjfT1FuHupsAahvS4mr3Yb8k2SHB8srW6st+/cBXwrsyhbCcj8Qhb1QtNUuEIx/tbpLQrMpxtJunZXkaKBfAEA=="
|
||||
},
|
||||
"node_modules/v8-compile-cache": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
||||
@ -19518,6 +19524,11 @@
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true
|
||||
},
|
||||
"v3-waterfall": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/v3-waterfall/-/v3-waterfall-1.2.1.tgz",
|
||||
"integrity": "sha512-zjfT1FuHupsAahvS4mr3Yb8k2SHB8srW6st+/cBXwrsyhbCcj8Qhb1QtNUuEIx/tbpLQrMpxtJunZXkaKBfAEA=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
||||
|
@ -17,14 +17,15 @@
|
||||
"good-storage": "^1.1.1",
|
||||
"highlight.js": "^11.7.0",
|
||||
"json-bigint": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^13.0.1",
|
||||
"md-editor-v3": "^2.2.1",
|
||||
"pinia": "^2.1.4",
|
||||
"qs": "^6.11.1",
|
||||
"sortablejs": "^1.15.0",
|
||||
"v3-waterfall": "^1.2.1",
|
||||
"vant": "^4.5.0",
|
||||
"vue": "^3.2.13",
|
||||
"lodash": "^4.17.21",
|
||||
"vue-router": "^4.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
46
web/src/assets/css/images-wall.css
Normal file
46
web/src/assets/css/images-wall.css
Normal file
@ -0,0 +1,46 @@
|
||||
.page-images-wall {
|
||||
display: flex;
|
||||
background-color: #282c34;
|
||||
}
|
||||
.page-images-wall .inner {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-images-wall .inner .header {
|
||||
display: flex;
|
||||
padding: 0 40px;
|
||||
}
|
||||
.page-images-wall .inner .header h2 {
|
||||
width: 300px;
|
||||
}
|
||||
.page-images-wall .inner .header .settings {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
.page-images-wall .inner .header .settings .el-radio-group {
|
||||
font-size: 16px;
|
||||
}
|
||||
.page-images-wall .inner .header .settings .el-radio-group .el-radio {
|
||||
color: #fff;
|
||||
}
|
||||
.page-images-wall .inner .waterfall {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.page-images-wall .custom-scroll ::-webkit-scrollbar {
|
||||
width: 10px; /* 滚动条宽度 */
|
||||
}
|
||||
.page-images-wall .custom-scroll ::-webkit-scrollbar-track {
|
||||
background-color: #282c34;
|
||||
}
|
||||
.page-images-wall .custom-scroll ::-webkit-scrollbar-thumb {
|
||||
background-color: #444;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.page-images-wall .custom-scroll ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666;
|
||||
}
|
69
web/src/assets/css/images-wall.styl
Normal file
69
web/src/assets/css/images-wall.styl
Normal file
@ -0,0 +1,69 @@
|
||||
.page-images-wall {
|
||||
display: flex;
|
||||
background-color: #282c34;
|
||||
|
||||
.inner {
|
||||
width 100%
|
||||
color #ffffff
|
||||
overflow hidden
|
||||
|
||||
.header {
|
||||
display flex
|
||||
padding 0 40px
|
||||
|
||||
h2 {
|
||||
width 300px
|
||||
}
|
||||
|
||||
.settings {
|
||||
width 100%
|
||||
display flex
|
||||
justify-content right
|
||||
|
||||
.el-radio-group {
|
||||
font-size 16px
|
||||
|
||||
.el-radio {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.waterfall {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.custom-scroll {
|
||||
/* 修改滚动条的颜色 */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 修改滚动条轨道的背景颜色 */
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #282C34;
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #444444;
|
||||
border-radius 10px
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块的悬停颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,8 @@ import {
|
||||
Uploader
|
||||
} from "vant";
|
||||
import router from "@/router";
|
||||
import 'v3-waterfall/dist/style.css'
|
||||
import V3waterfall from "v3-waterfall";
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(createPinia())
|
||||
@ -62,6 +64,7 @@ app.use(ShareSheet);
|
||||
app.use(Switch);
|
||||
app.use(Uploader);
|
||||
app.use(Tag);
|
||||
app.use(V3waterfall)
|
||||
app.use(router).use(ElementPlus).mount('#app')
|
||||
|
||||
|
||||
|
@ -40,9 +40,9 @@ const routes = [
|
||||
},
|
||||
{
|
||||
name: 'images',
|
||||
path: '/images',
|
||||
meta: {title: '绘画社区'},
|
||||
component: () => import('@/views/Images.vue'),
|
||||
path: '/images-wall',
|
||||
meta: {title: '作品展示'},
|
||||
component: () => import('@/views/ImagesWall.vue'),
|
||||
},
|
||||
{
|
||||
name: 'user-invitation',
|
||||
|
@ -45,7 +45,7 @@ const navs = ref([
|
||||
{path: "/mj", icon: "image", title: "MJ 绘画"},
|
||||
{path: "/sd", icon: "palette", title: "SD 绘画"},
|
||||
{path: "/apps", icon: "menu", title: "应用中心"},
|
||||
{path: "/images", icon: "image-list", title: "绘画社区"},
|
||||
{path: "/images-wall", icon: "image-list", title: "作品展示"},
|
||||
{path: "/knowledge", icon: "book", title: "我的知识库"},
|
||||
{path: "/member", icon: "vip-user", title: "会员计划"},
|
||||
{path: "/invite", icon: "share", title: "推广计划"},
|
||||
|
@ -379,7 +379,6 @@ import {getSessionId, getUserToken} from "@/store/session";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const mjBoxHeight = ref(window.innerHeight - 150)
|
||||
|
||||
window.onresize = () => {
|
||||
listBoxHeight.value = window.innerHeight - 40
|
||||
mjBoxHeight.value = window.innerHeight - 150
|
||||
@ -476,14 +475,14 @@ onMounted(() => {
|
||||
checkSession().then(user => {
|
||||
imgCalls.value = user['img_calls']
|
||||
// 获取运行中的任务
|
||||
httpGet("/api/mj/jobs?status=0").then(res => {
|
||||
httpGet(`/api/mj/jobs?status=0&user_id=${user['id']}`).then(res => {
|
||||
runningJobs.value = res.data
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取任务失败:" + e.message)
|
||||
})
|
||||
|
||||
// 获取运行中的任务
|
||||
httpGet("/api/mj/jobs?status=1").then(res => {
|
||||
httpGet(`/api/mj/jobs?status=1&user_id=${user['id']}`).then(res => {
|
||||
finishedJobs.value = res.data
|
||||
previewImgList.value = []
|
||||
for (let index in finishedJobs.value) {
|
||||
|
@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div class="page-images" :style="{ height: winHeight + 'px' }">
|
||||
<div class="inner">
|
||||
<h1>绘画作品广场</h1>
|
||||
<h2>页面正在紧锣密鼓开发中,敬请期待!</h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue"
|
||||
|
||||
const winHeight = ref(window.innerHeight)
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.page-images {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items center
|
||||
background-color: #282c34;
|
||||
|
||||
.inner {
|
||||
text-align center
|
||||
|
||||
h1 {
|
||||
color: #202020;
|
||||
font-size: 80px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
text-shadow: -1px -1px 1px #111111, 2px 2px 1px #363636;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
98
web/src/views/ImagesWall.vue
Normal file
98
web/src/views/ImagesWall.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="page-images-wall">
|
||||
<div class="inner custom-scroll">
|
||||
<div class="header">
|
||||
<h2>AI 绘画作品墙</h2>
|
||||
<div class="settings">
|
||||
<el-radio-group v-model="imgType">
|
||||
<el-radio label="mj" size="large">MidJourney</el-radio>
|
||||
<el-radio label="sd" size="large">Stable Diffusion</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<v3-waterfall class="waterfall" id="waterfall" :list="list" srcKey="img_url" :gap="12" :colWidth="colWidth"
|
||||
:style="{ height:listBoxHeight + 'px' }"
|
||||
:distanceToScroll="100" :isLoading="loading" :isOver="over" @scrollReachBottom="getNext">
|
||||
<template #default="slotProp">
|
||||
<div class="list-item">
|
||||
<el-image :src="slotProp.item['img_url']+'?imageView2/4/w/300/q/75'"
|
||||
:zoom-rate="1.2"
|
||||
:preview-src-list="[slotProp.item['img_url']]"
|
||||
:preview-teleported="true"
|
||||
:initial-index="10"
|
||||
loading="lazy">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
正在加载图片
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon v-if="slotProp.item['img'] !== ''">
|
||||
<Picture/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</template>
|
||||
</v3-waterfall>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue"
|
||||
import {Picture} from "@element-plus/icons-vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const list = ref([])
|
||||
const loading = ref(true)
|
||||
const over = ref(false)
|
||||
const imgType = ref("mj") // 图片类别
|
||||
const listBoxHeight = window.innerHeight - 100
|
||||
const colWidth = ref(240)
|
||||
|
||||
// 计算瀑布流列宽度
|
||||
const calcColWidth = () => {
|
||||
const listBoxWidth = window.innerWidth - 60 - 80
|
||||
const rows = Math.floor(listBoxWidth / colWidth.value)
|
||||
colWidth.value = Math.floor((listBoxWidth - (rows - 1) * 12) / rows)
|
||||
}
|
||||
calcColWidth()
|
||||
window.onresize = () => {
|
||||
calcColWidth()
|
||||
}
|
||||
|
||||
const page = ref(0)
|
||||
const pageSize = ref(20)
|
||||
// 获取下一页数据
|
||||
const getNext = () => {
|
||||
loading.value = true
|
||||
page.value = page.value + 1
|
||||
// 获取运行中的任务
|
||||
httpGet(`/api/mj/jobs?status=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
|
||||
loading.value = false
|
||||
if (list.value.length === 0) {
|
||||
list.value = res.data
|
||||
return
|
||||
} else if (res.data.length < pageSize.value) {
|
||||
over.value = true
|
||||
}
|
||||
|
||||
list.value = list.value.concat(res.data)
|
||||
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取图片失败:" + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
getNext()
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import "@/assets/css/images-wall.styl"
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user