mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-11-05 00:33:47 +08:00
Merge tag 'v4.1.7' of gitee.com:blackfox/geekai-plus
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
display flex
|
||||
width 100%
|
||||
|
||||
.el-input,.el-select,.el-switch {
|
||||
.el-input, .el-select, .el-switch {
|
||||
margin-right 10px
|
||||
}
|
||||
|
||||
|
||||
@@ -473,8 +473,13 @@
|
||||
padding 30px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
.iconfont {
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mj-list-item-prompt {
|
||||
|
||||
@@ -172,6 +172,22 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right 5px
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right 10px
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left 5px
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left 10px
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1728891448746') format('woff2'),
|
||||
url('iconfont.woff?t=1728891448746') format('woff'),
|
||||
url('iconfont.ttf?t=1728891448746') format('truetype');
|
||||
src: url('iconfont.woff2?t=1731289567907') format('woff2'),
|
||||
url('iconfont.woff?t=1731289567907') format('woff'),
|
||||
url('iconfont.ttf?t=1731289567907') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-linggan:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.icon-chuangzuo:before {
|
||||
content: "\e6cc";
|
||||
}
|
||||
|
||||
.icon-call:before {
|
||||
content: "\e769";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,20 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "15330210",
|
||||
"name": "创意灵感",
|
||||
"font_class": "linggan",
|
||||
"unicode": "e641",
|
||||
"unicode_decimal": 58945
|
||||
},
|
||||
{
|
||||
"icon_id": "39170417",
|
||||
"name": "创作",
|
||||
"font_class": "chuangzuo",
|
||||
"unicode": "e6cc",
|
||||
"unicode_decimal": 59084
|
||||
},
|
||||
{
|
||||
"icon_id": "11231556",
|
||||
"name": "打电话",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -40,8 +40,8 @@ const props = defineProps({
|
||||
default: 'Tips',
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
type: String,
|
||||
default: 'auto',
|
||||
},
|
||||
hideFooter:{
|
||||
type: Boolean,
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import axios from 'axios'
|
||||
import {getAdminToken, getSessionId, getUserToken, removeAdminToken, removeUserToken} from "@/store/session";
|
||||
import {getAdminToken, getUserToken, removeAdminToken, removeUserToken} from "@/store/session";
|
||||
|
||||
axios.defaults.timeout = 180000
|
||||
axios.defaults.baseURL = process.env.VUE_APP_API_HOST
|
||||
axios.defaults.withCredentials = true;
|
||||
axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||
//axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||
|
||||
// HTTP拦截器
|
||||
axios.interceptors.request.use(
|
||||
@@ -82,4 +82,19 @@ export function httpDownload(url) {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function httpPostDownload(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: data,
|
||||
responseType: 'blob' // 将响应类型设置为 `blob`
|
||||
}).then(response => {
|
||||
resolve(response)
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -225,6 +225,9 @@ export function showLoginDialog(router) {
|
||||
}
|
||||
|
||||
export const replaceImg =(img) => {
|
||||
if (!img.startsWith("http")) {
|
||||
img = `${location.protocol}//${location.host}/${img}`
|
||||
}
|
||||
const devHost = process.env.VUE_APP_API_HOST
|
||||
const localhost = "http://localhost:5678"
|
||||
if (img.includes(localhost)) {
|
||||
|
||||
@@ -59,10 +59,19 @@
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
type="textarea"
|
||||
ref="promptRef"
|
||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
|
||||
placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"
|
||||
v-loading="isGenerating"
|
||||
style="--el-mask-color:rgba(100, 100, 100, 0.8)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<div class="text-info">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
@@ -212,6 +221,7 @@ import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import TaskList from "@/components/TaskList.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
|
||||
const listBoxHeight = ref(0)
|
||||
// const paramBoxHeight = ref(0)
|
||||
@@ -410,6 +420,21 @@ const publishImage = (item, action) => {
|
||||
})
|
||||
}
|
||||
|
||||
const isGenerating = ref(false)
|
||||
const generatePrompt = () => {
|
||||
if (params.value.prompt === "") {
|
||||
return showMessageError("请输入原始提示词")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
|
||||
params.value.prompt = res.data
|
||||
isGenerating.value = false
|
||||
}).catch(e => {
|
||||
showMessageError("生成提示词失败:"+e.message)
|
||||
isGenerating.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
<div class="flex-col items-center"
|
||||
:class="item.value === params.rate ? 'grid-content active' : 'grid-content'"
|
||||
@click="changeRate(item)">
|
||||
<!-- <div :class="'shape ' + item.css"></div>-->
|
||||
<el-image class="icon" :src="item.img" fit="cover"></el-image>
|
||||
<div class="text">{{ item.text }}</div>
|
||||
</div>
|
||||
@@ -183,12 +182,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="param-line pt">
|
||||
<div class="param-line pt" style="position: relative">
|
||||
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||
ref="promptRef"
|
||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||
v-loading="isGenerating"
|
||||
style="--el-mask-color:rgba(100, 100, 100, 0.8)"
|
||||
placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"/>
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<div class="param-line pt">
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
@@ -265,9 +273,18 @@
|
||||
<div class="param-line pt">
|
||||
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||
ref="promptRef"
|
||||
v-loading="isGenerating"
|
||||
style="--el-mask-color:rgba(100, 100, 100, 0.8)"
|
||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<div class="param-line pt">
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
@@ -615,6 +632,7 @@ import {copyObj, removeArrayItem} from "@/utils/libs";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import TaskList from "@/components/TaskList.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
|
||||
const listBoxHeight = ref(0)
|
||||
const paramBoxHeight = ref(0)
|
||||
@@ -820,7 +838,7 @@ const fetchFinishJobs = () => {
|
||||
jobs[i]['thumb_url'] = '/images/img-placeholder.jpg'
|
||||
}
|
||||
|
||||
if ((jobs[i].type === 'image' || jobs[i].type === 'variation') && jobs[i].progress === 100) {
|
||||
if (jobs[i].type !== 'upscale' && jobs[i].progress === 100){
|
||||
jobs[i]['can_opt'] = true
|
||||
}
|
||||
}
|
||||
@@ -999,6 +1017,21 @@ const removeUploadImage = (url) => {
|
||||
imgList.value = removeArrayItem(imgList.value, url)
|
||||
}
|
||||
|
||||
const isGenerating = ref(false)
|
||||
const generatePrompt = () => {
|
||||
if (params.value.prompt === "") {
|
||||
return showMessageError("请输入原始提示词")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
|
||||
params.value.prompt = res.data
|
||||
isGenerating.value = false
|
||||
}).catch(e => {
|
||||
showMessageError("生成提示词失败:"+e.message)
|
||||
isGenerating.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -250,10 +250,19 @@
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
type="textarea"
|
||||
ref="promptRef"
|
||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
|
||||
placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"
|
||||
v-loading="isGenerating"
|
||||
style="--el-mask-color:rgba(100, 100, 100, 0.8)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<div class="param-line pt">
|
||||
<span>反向提示词:</span>
|
||||
<el-tooltip
|
||||
@@ -497,6 +506,7 @@ import {getSessionId} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import TaskList from "@/components/TaskList.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
|
||||
const listBoxHeight = ref(0)
|
||||
// const paramBoxHeight = ref(0)
|
||||
@@ -722,6 +732,21 @@ const publishImage = (item, action) => {
|
||||
})
|
||||
}
|
||||
|
||||
const isGenerating = ref(false)
|
||||
const generatePrompt = () => {
|
||||
if (params.value.prompt === "") {
|
||||
return showMessageError("请输入原始提示词")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
|
||||
params.value.prompt = res.data
|
||||
isGenerating.value = false
|
||||
}).catch(e => {
|
||||
showMessageError("生成提示词失败:"+e.message)
|
||||
isGenerating.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template v-for="(img, index) in images" :key="img">
|
||||
<div class="item">
|
||||
<el-image :src="replaceImg(img)" fit="cover"/>
|
||||
<el-icon @click="remove(img)"><CircleCloseFilled /></el-icon>
|
||||
<el-icon @click="remove(img)">
|
||||
<CircleCloseFilled/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="btn-swap" v-if="images.length === 2 && index === 0">
|
||||
<i class="iconfont icon-exchange" @click="switchReverse"></i>
|
||||
@@ -38,20 +40,26 @@
|
||||
</div>
|
||||
|
||||
<div class="params">
|
||||
<div class="item-group">
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2"
|
||||
:disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
|
||||
<span>生成AI视频提示词</span>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="item-group">
|
||||
<span class="label">循环参考图</span>
|
||||
<el-switch v-model="formData.loop" size="small" style="--el-switch-on-color:#BF78BF;" />
|
||||
<el-switch v-model="formData.loop" size="small" style="--el-switch-on-color:#BF78BF;"/>
|
||||
</div>
|
||||
<div class="item-group">
|
||||
<span class="label">提示词优化</span>
|
||||
<el-switch v-model="formData.expand_prompt" size="small" style="--el-switch-on-color:#BF78BF;" />
|
||||
<el-switch v-model="formData.expand_prompt" size="small" style="--el-switch-on-color:#BF78BF;"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<el-container class="video-container" v-loading="loading" element-loading-background="rgba(100,100,100,0.3)">
|
||||
<h2 class="h-title">你的作品</h2>
|
||||
|
||||
@@ -61,33 +69,35 @@
|
||||
<div class="left">
|
||||
<div class="container">
|
||||
<div v-if="item.progress === 100">
|
||||
<video class="video" :src="replaceImg(item.video_url)" preload="auto" loop="loop" muted="muted">
|
||||
<video class="video" :src="replaceImg(item.video_url)" preload="auto" loop="loop" muted="muted">
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
<button class="play" @click="play(item)">
|
||||
<img src="/images/play.svg" alt=""/>
|
||||
</button>
|
||||
</div>
|
||||
<el-image :src="item.cover_url" fit="cover" v-else-if="item.progress > 100" />
|
||||
<generating message="正在生成视频" v-else />
|
||||
<el-image :src="item.cover_url" fit="cover" v-else-if="item.progress > 100"/>
|
||||
<generating message="正在生成视频" v-else/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="failed" v-if="item.progress === 101">任务执行失败:{{item.err_msg}},任务提示词:{{item.prompt}}</div>
|
||||
<div class="prompt" v-else>{{item.prompt}}</div>
|
||||
<div class="failed" v-if="item.progress === 101">
|
||||
任务执行失败:{{ item.err_msg }},任务提示词:{{ item.prompt }}
|
||||
</div>
|
||||
<div class="prompt" v-else>{{ item.prompt }}</div>
|
||||
</div>
|
||||
<div class="right" v-if="item.progress === 100">
|
||||
<div class="tools">
|
||||
<button class="btn btn-publish">
|
||||
<span class="text">发布</span>
|
||||
<black-switch v-model:value="item.publish" @change="publishJob(item)" size="small" />
|
||||
<black-switch v-model:value="item.publish" @change="publishJob(item)" size="small"/>
|
||||
</button>
|
||||
|
||||
<el-tooltip effect="light" content="下载视频" placement="top">
|
||||
<button class="btn btn-icon" @click="download(item)" :disabled="item.downloading">
|
||||
<i class="iconfont icon-download" v-if="!item.downloading"></i>
|
||||
<el-image src="/images/loading.gif" class="downloading" fit="cover" v-else />
|
||||
<el-image src="/images/loading.gif" class="downloading" fit="cover" v-else/>
|
||||
</button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="删除" placement="top">
|
||||
@@ -105,38 +115,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty :image-size="100" description="没有任何作品,赶紧去创作吧!" v-else />
|
||||
<el-empty :image-size="100" description="没有任何作品,赶紧去创作吧!" v-else/>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination v-if="total > pageSize" background
|
||||
style="--el-pagination-button-bg-color:#414141;
|
||||
style="--el-pagination-button-bg-color:#414141;
|
||||
--el-pagination-button-color:#d1d1d1;
|
||||
--el-disabled-bg-color:#414141;
|
||||
--el-color-primary:#666666;
|
||||
--el-pagination-hover-color:#e1e1e1"
|
||||
layout="total,prev, pager, next"
|
||||
:hide-on-single-page="true"
|
||||
v-model:current-page="page"
|
||||
v-model:page-size="pageSize"
|
||||
@current-change="fetchData(page)"
|
||||
:total="total"/>
|
||||
layout="total,prev, pager, next"
|
||||
:hide-on-single-page="true"
|
||||
v-model:current-page="page"
|
||||
v-model:page-size="pageSize"
|
||||
@current-change="fetchData(page)"
|
||||
:total="total"/>
|
||||
</div>
|
||||
</el-container>
|
||||
<black-dialog v-model:show="showDialog" title="预览视频" hide-footer @cancal="showDialog = false" width="auto">
|
||||
<video style="width: 100%; max-height: 90vh;" :src="currentVideoUrl" preload="auto" :autoplay="true" loop="loop" muted="muted" v-show="showDialog">
|
||||
<video style="width: 100%; max-height: 90vh;" :src="currentVideoUrl" preload="auto" :autoplay="true" loop="loop"
|
||||
muted="muted" v-show="showDialog">
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
</black-dialog>
|
||||
</black-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import {CircleCloseFilled} from "@element-plus/icons-vue";
|
||||
import {httpDownload, httpPost, httpGet} from "@/utils/http";
|
||||
import {httpDownload, httpGet, httpPost} from "@/utils/http";
|
||||
import {checkSession, getClientId} from "@/store/cache";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import { replaceImg } from "@/utils/libs"
|
||||
import {replaceImg} from "@/utils/libs"
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import BlackSwitch from "@/components/ui/BlackSwitch.vue";
|
||||
import Generating from "@/components/ui/Generating.vue";
|
||||
@@ -158,12 +169,12 @@ const formData = reactive({
|
||||
})
|
||||
|
||||
const store = useSharedStore()
|
||||
onMounted(()=>{
|
||||
onMounted(() => {
|
||||
checkSession().then(() => {
|
||||
fetchData(1)
|
||||
})
|
||||
|
||||
store.addMessageHandler("luma",(data) => {
|
||||
store.addMessageHandler("luma", (data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "luma" || data.clientId !== getClientId()) {
|
||||
return
|
||||
@@ -186,7 +197,7 @@ const download = (item) => {
|
||||
const urlObj = new URL(url);
|
||||
const fileName = urlObj.pathname.split('/').pop();
|
||||
item.downloading = true
|
||||
httpDownload(downloadURL).then(response => {
|
||||
httpDownload(downloadURL).then(response => {
|
||||
const blob = new Blob([response.data]);
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
@@ -228,7 +239,7 @@ const removeJob = (item) => {
|
||||
}
|
||||
|
||||
const publishJob = (item) => {
|
||||
httpGet("/api/video/publish", {id: item.id, publish:item.publish}).then(() => {
|
||||
httpGet("/api/video/publish", {id: item.id, publish: item.publish}).then(() => {
|
||||
ElMessage.success("操作成功")
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message)
|
||||
@@ -264,7 +275,7 @@ const fetchData = (_page) => {
|
||||
if (_page) {
|
||||
page.value = _page
|
||||
}
|
||||
httpGet("/api/video/list",{page:page.value, page_size:pageSize.value, type: 'luma'}).then(res => {
|
||||
httpGet("/api/video/list", {page: page.value, page_size: pageSize.value, type: 'luma'}).then(res => {
|
||||
total.value = res.data.total
|
||||
loading.value = false
|
||||
list.value = res.data.items
|
||||
@@ -278,10 +289,10 @@ const fetchData = (_page) => {
|
||||
// 创建视频
|
||||
const create = () => {
|
||||
|
||||
const len = images.value.length;
|
||||
if(len){
|
||||
const len = images.value.length;
|
||||
if (len) {
|
||||
formData.first_frame_img = images.value[0]
|
||||
if(len === 2){
|
||||
if (len === 2) {
|
||||
formData.end_frame_img = images.value[1]
|
||||
}
|
||||
}
|
||||
@@ -290,10 +301,24 @@ const create = () => {
|
||||
fetchData(1)
|
||||
showMessageOK("创建任务成功")
|
||||
}).catch(e => {
|
||||
showMessageError("创建任务失败:"+e.message)
|
||||
showMessageError("创建任务失败:" + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const isGenerating = ref(false)
|
||||
const generatePrompt = () => {
|
||||
if (formData.prompt === "") {
|
||||
return showMessageError("请输入原始提示词")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/prompt/image", {prompt: formData.prompt}).then(res => {
|
||||
formData.prompt = res.data
|
||||
isGenerating.value = false
|
||||
}).catch(e => {
|
||||
showMessageError("生成提示词失败:" + e.message)
|
||||
isGenerating.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
请选择生成思维导图的AI模型
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<el-select v-model="modelID" placeholder="请选择模型" @change="changeModel" style="width:100%">
|
||||
<el-select v-model="modelID" placeholder="请选择模型" style="width:100%">
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
@@ -243,6 +243,8 @@ const downloadImage = () => {
|
||||
canvas.height = svgElement.offsetHeight
|
||||
let context = canvas.getContext('2d')
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.fillStyle = 'white';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
image.onload = function () {
|
||||
context.drawImage(image, 0, 0)
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="item">
|
||||
<black-input v-model:value="data.prompt" type="textarea" :rows="10" placeholder="例如:一首关于鸟人的摇滚歌曲..."/>
|
||||
<black-input v-model:value="data.prompt" type="textarea" :rows="10" placeholder="例如:一首关于爱情的摇滚歌曲..."/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<black-dialog v-model:show="showDialog" title="修改歌曲" @cancal="showDialog = false" @confirm="updateSong" :width="500">
|
||||
<black-dialog v-model:show="showDialog" title="修改歌曲" @cancal="showDialog = false" @confirm="updateSong" :width="500+'px'">
|
||||
<form class="form">
|
||||
<div class="form-item">
|
||||
<div class="label">歌曲名称</div>
|
||||
@@ -615,7 +615,7 @@ const createLyric = () => {
|
||||
return showMessageError("请输入歌词描述")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/suno/lyric", {prompt: data.value.lyrics}).then(res => {
|
||||
httpPost("/api/prompt/lyric", {prompt: data.value.lyrics}).then(res => {
|
||||
const lines = res.data.split('\n');
|
||||
data.value.title = lines.shift().replace(/\*/g,"")
|
||||
lines.shift()
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="打招呼信息" prop="hello_msg"/>
|
||||
<el-table-column label="操作" width="150" align="right">
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button size="small" type="primary" @click="rowEdit(scope.$index, scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确定要删除当前应用吗?" @confirm="removeRole(scope.row)" :width="200">
|
||||
@@ -128,10 +128,14 @@
|
||||
<el-table :data="role.context" :border="childBorder" size="small">
|
||||
<el-table-column label="对话应用" width="120">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.role"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<el-select v-model="scope.row.role" placeholder="Role">
|
||||
<el-option
|
||||
v-for="value in messageRoles"
|
||||
:key="value"
|
||||
:label="value"
|
||||
:value="value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="对话内容">
|
||||
@@ -153,11 +157,40 @@
|
||||
<div class="context-msg-content">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:rows="3"
|
||||
v-model="scope.row.content"
|
||||
autocomplete="off"
|
||||
v-loading="isGenerating"
|
||||
/>
|
||||
<span><el-icon @click="removeContext(scope.$index)"><RemoveFilled/></el-icon></span>
|
||||
<span class="remove-item">
|
||||
<el-tooltip effect="dark" content="删除当前行" placement="right">
|
||||
<el-button circle type="danger" size="small">
|
||||
<el-icon @click="removeContext(scope.$index)"><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-popover placement="right" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<el-button type="primary" circle size="small" class="icon-btn">
|
||||
<i class="iconfont icon-linggan"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model="metaPrompt"
|
||||
autocomplete="off"
|
||||
placeholder="请您输入要 AI实现的目标,任务或者需要AI扮演的角色?"
|
||||
/>
|
||||
<el-row class="text-line">
|
||||
<el-text class="mx-1" type="info" size="small">使用 AI 生成 System 预设指令</el-text>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt(scope.row)" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>立即生成</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -182,13 +215,14 @@
|
||||
|
||||
<script setup>
|
||||
|
||||
import {Plus, RemoveFilled} from "@element-plus/icons-vue";
|
||||
import {Delete, Plus} from "@element-plus/icons-vue";
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {copyObj, removeArrayItem} from "@/utils/libs";
|
||||
import {Sortable} from "sortablejs"
|
||||
import Compressor from "compressorjs";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
|
||||
const showDialog = ref(false)
|
||||
const parentBorder = ref(true)
|
||||
@@ -213,6 +247,7 @@ const rules = reactive({
|
||||
|
||||
const appTypes = ref([])
|
||||
const models = ref([])
|
||||
const messageRoles = ref(["system", "user", "assistant"])
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
|
||||
@@ -354,7 +389,23 @@ const uploadImg = (file) => {
|
||||
ElMessage.error('上传失败:' + e.message)
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const isGenerating = ref(false)
|
||||
const metaPrompt = ref("")
|
||||
const generatePrompt = (row) => {
|
||||
if (metaPrompt.value === "") {
|
||||
return showMessageError("请输入元提示词")
|
||||
}
|
||||
isGenerating.value = true
|
||||
httpPost("/api/prompt/meta", {prompt: metaPrompt.value}).then(res => {
|
||||
row.content = res.data
|
||||
isGenerating.value = false
|
||||
}).catch(e => {
|
||||
showMessageError("生成失败:"+e.message)
|
||||
isGenerating.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@@ -391,12 +442,18 @@ const uploadImg = (file) => {
|
||||
.context-msg-content {
|
||||
display flex
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
margin-top 5px;
|
||||
margin-left 5px;
|
||||
cursor pointer
|
||||
.remove-item {
|
||||
display flex
|
||||
padding 10px
|
||||
flex-flow column
|
||||
align-items center
|
||||
justify-content center
|
||||
|
||||
.icon-btn {
|
||||
margin 10px 0 0 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-input--small {
|
||||
@@ -421,4 +478,14 @@ const uploadImg = (file) => {
|
||||
justify-content right
|
||||
}
|
||||
}
|
||||
|
||||
.text-line {
|
||||
display flex
|
||||
justify-content space-between
|
||||
padding-top 10px
|
||||
.iconfont {
|
||||
margin-right 5px
|
||||
font-size 14px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -49,7 +49,7 @@ watch(() => store.adminTheme, (val) => {
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
<style lang="stylus">
|
||||
@import '@/assets/css/color-dark.styl';
|
||||
@import '@/assets/css/main.styl';
|
||||
@import '@/assets/iconfont/iconfont.css';
|
||||
|
||||
@@ -12,10 +12,14 @@
|
||||
</el-select>
|
||||
<el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button>
|
||||
<el-button type="success" :icon="Plus" @click="add">添加兑换码</el-button>
|
||||
<el-button type="primary" @click="exportItems" :loading="exporting"><i class="iconfont icon-export mr-1"></i> 导出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-row>
|
||||
<el-table :data="items" :row-key="row => row.id">
|
||||
<el-table :data="items" :row-key="row => row.id"
|
||||
@selection-change="handleSelectionChange" table-layout="auto">
|
||||
<el-table-column type="selection" width="38"></el-table-column>
|
||||
<el-table-column prop="name" label="名称"/>
|
||||
<el-table-column prop="code" label="兑换码">
|
||||
<template #default="scope">
|
||||
@@ -48,7 +52,8 @@
|
||||
|
||||
<el-table-column prop="enabled" label="启用状态">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row['enabled']" @change="set('enabled',scope.row)" :disabled="scope.row['redeemed_at']>0"/>
|
||||
<el-switch v-model="scope.row['enabled']" @change="set('enabled',scope.row)"
|
||||
:disabled="scope.row['redeemed_at']>0"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -90,7 +95,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="生成数量:" prop="num">
|
||||
<el-input v-model.number="item.num" />
|
||||
<el-input v-model.number="item.num"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -107,17 +112,17 @@
|
||||
|
||||
<script setup>
|
||||
import {onMounted, onUnmounted, ref} from "vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {httpGet, httpPost, httpPostDownload} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {dateFormat, removeArrayItem, substr} from "@/utils/libs";
|
||||
import {Delete, DocumentCopy, Plus, Search, UploadFilled} from "@element-plus/icons-vue";
|
||||
import {dateFormat, substr, UUID} from "@/utils/libs";
|
||||
import {DocumentCopy, Plus, Search} from "@element-plus/icons-vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
import ClipboardJS from "clipboard";
|
||||
|
||||
// 变量定义
|
||||
const items = ref([])
|
||||
const loading = ref(true)
|
||||
const query = ref({code:"",status:-1})
|
||||
const query = ref({code: "", status: -1})
|
||||
const redeemStatus = ref([
|
||||
{value: -1, label: "全部"},
|
||||
{value: 0, label: "未核销"},
|
||||
@@ -126,6 +131,8 @@ const redeemStatus = ref([
|
||||
const showDialog = ref(false)
|
||||
const dialogLoading = ref(false)
|
||||
const item = ref({name: "", power: 0, num: 1})
|
||||
const itemIds = ref([])
|
||||
const exporting = ref(false)
|
||||
|
||||
const clipboard = ref(null)
|
||||
onMounted(() => {
|
||||
@@ -152,10 +159,10 @@ const add = () => {
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
if (item.value.name ===""){
|
||||
if (item.value.name === "") {
|
||||
return showMessageError("请输入兑换码名称")
|
||||
}
|
||||
if (item.value.power === 0){
|
||||
if (item.value.power === 0) {
|
||||
return showMessageError("请输入算力额度")
|
||||
}
|
||||
if (item.value.num <= 0) {
|
||||
@@ -207,12 +214,39 @@ const remove = function (row) {
|
||||
ElMessage.error("删除失败:" + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const handleSelectionChange = (items) => {
|
||||
itemIds.value = items.map(item => item.id)
|
||||
}
|
||||
|
||||
const exportItems = () => {
|
||||
query.value.ids = itemIds.value
|
||||
exporting.value = true
|
||||
httpPostDownload("/api/admin/redeem/export", query.value).then(response => {
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', UUID() + ".csv"); // 设置下载文件的名称
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// 移除 <a> 标签
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
exporting.value = false
|
||||
}).catch(() => {
|
||||
exporting.value = false
|
||||
showMessageError("下载失败")
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.list {
|
||||
.handle-box {
|
||||
margin-bottom 20px
|
||||
|
||||
.handle-input {
|
||||
max-width 150px;
|
||||
margin-right 10px;
|
||||
|
||||
@@ -153,14 +153,13 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="默认AI模型" prop="default_models">
|
||||
<el-form-item label="默认翻译模型">
|
||||
<template #default>
|
||||
<div class="tip-input">
|
||||
<el-select
|
||||
v-model="system['default_models']"
|
||||
multiple
|
||||
v-model.number="system['translate_model_id']"
|
||||
:filterable="true"
|
||||
placeholder="选择AI模型,多选"
|
||||
placeholder="选择一个默认模型来翻译提示词"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
|
||||
@@ -434,7 +434,7 @@ const fetchFinishJobs = (page) => {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
|
||||
}
|
||||
|
||||
if ((jobs[i].type === 'image' || jobs[i].type === 'variation') && jobs[i].progress === 100){
|
||||
if (jobs[i].type !== 'upscale' && jobs[i].progress === 100){
|
||||
jobs[i]['can_opt'] = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ module.exports = defineConfig({
|
||||
]
|
||||
},
|
||||
|
||||
publicPath: process.env.NODE_ENV === 'production' ? '/' : '/',
|
||||
publicPath: '/',
|
||||
|
||||
outputDir: 'dist',
|
||||
crossorigin: "anonymous",
|
||||
|
||||
Reference in New Issue
Block a user