mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-05-10 19:54:25 +08:00
Jimeng AI 4.0 for mobile is ready
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
- Bug 修复:修复超级管理员无法修改密码的 Bug
|
- Bug 修复:修复超级管理员无法修改密码的 Bug
|
||||||
- Bug 修复:微信登录配置更新后,没有同步更新到系统配置
|
- Bug 修复:微信登录配置更新后,没有同步更新到系统配置
|
||||||
- 功能优化: 给 AI 对话 API 加上线程锁,确保同一个用户同时只有一个对话请求
|
- 功能优化: 给 AI 对话 API 加上线程锁,确保同一个用户同时只有一个对话请求
|
||||||
- 功能新增:支持即梦 AI 4.0 图片编辑,即梦 AI 数字人,动作迁移功能
|
- 功能新增:支持即梦 AI 4.0 图片编辑,即梦 AI 数字人,动作迁移功能。🔥🔥🔥
|
||||||
|
|
||||||
## v4.2.6
|
## v4.2.6
|
||||||
|
|
||||||
|
|||||||
@@ -887,3 +887,284 @@
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark 主题样式 - 按照 theme-dark.scss 的模式 */
|
||||||
|
:root[data-theme='dark'] .jimeng-create {
|
||||||
|
background-color: rgb(13, 20, 53);
|
||||||
|
|
||||||
|
/* 页面头部样式 */
|
||||||
|
.sticky {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 功能分类选择 */
|
||||||
|
.jimeng-create__content {
|
||||||
|
.bg-white {
|
||||||
|
background-color: rgb(55, 65, 81) !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-700 {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-900 {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-600 {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-500 {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gray-100:hover {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Element Plus 组件样式覆盖 */
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
border-color: rgb(75, 85, 99) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner::placeholder) {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-textarea__inner) {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-textarea__inner::placeholder) {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-switch__core) {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
border-color: rgb(75, 85, 99) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-switch.is-checked .el-switch__core) {
|
||||||
|
background-color: rgb(139, 92, 246) !important;
|
||||||
|
border-color: rgb(139, 92, 246) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__runway) {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__bar) {
|
||||||
|
background-color: rgb(139, 92, 246) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button) {
|
||||||
|
border-color: rgb(139, 92, 246) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tooltip__trigger) {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提交按钮 */
|
||||||
|
.bg-gradient-to-r {
|
||||||
|
background: linear-gradient(88deg, #af61f0 1.44%, #5b62ce) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(88deg, #9f51e0 1.44%, #4b52be) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: linear-gradient(88deg, #6b7280 1.44%, #4b5563) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 作品列表 */
|
||||||
|
.jimeng-create__works {
|
||||||
|
&-title {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
background-color: rgb(55, 65, 81) !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
.jimeng-create__works-item-info {
|
||||||
|
&-title {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-prompt {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
&-item {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background-color: rgb(239, 68, 68) !important;
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
background-color: rgb(59, 130, 246) !important;
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--power {
|
||||||
|
background-color: rgb(139, 92, 246) !important;
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-quick-actions {
|
||||||
|
button {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-error {
|
||||||
|
&-content {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
border-color: rgb(239, 68, 68) !important;
|
||||||
|
|
||||||
|
.jimeng-create__works-item-error-text {
|
||||||
|
color: rgb(239, 68, 68) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jimeng-create__works-item-error-copy-btn {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-loading {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-finished {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 媒体预览弹窗 */
|
||||||
|
.jimeng-create__media-dialog {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8) !important;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
background-color: rgb(55, 65, 81) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
border-bottom-color: rgb(75, 85, 99) !important;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片上传组件 */
|
||||||
|
:deep(.image-upload) {
|
||||||
|
.upload-area {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
border-color: rgb(75, 85, 99) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgb(139, 92, 246) !important;
|
||||||
|
background-color: rgb(55, 65, 81) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-icon {
|
||||||
|
color: rgb(139, 92, 246) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义选择组件 */
|
||||||
|
:deep(.custom-select) {
|
||||||
|
.select-trigger {
|
||||||
|
background-color: rgb(31, 41, 55) !important;
|
||||||
|
border-color: rgb(75, 85, 99) !important;
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-dropdown {
|
||||||
|
background-color: rgb(55, 65, 81) !important;
|
||||||
|
border-color: rgb(75, 85, 99) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-option {
|
||||||
|
color: rgb(209, 213, 219) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(75, 85, 99) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: rgb(139, 92, 246) !important;
|
||||||
|
color: rgb(255, 255, 255) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态组件 */
|
||||||
|
:deep(.van-empty) {
|
||||||
|
.van-empty__description {
|
||||||
|
color: rgb(156, 163, 175) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export const useJimengStore = defineStore('jimeng', () => {
|
|||||||
const response = await httpGet('/api/jimeng/remove', { id: item.id })
|
const response = await httpGet('/api/jimeng/remove', { id: item.id })
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
showMessageOK('删除成功')
|
showMessageOK('删除成功')
|
||||||
|
isOver.value = false
|
||||||
await fetchData(1)
|
await fetchData(1)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -346,6 +347,7 @@ export const useJimengStore = defineStore('jimeng', () => {
|
|||||||
getTaskStatusText,
|
getTaskStatusText,
|
||||||
getTaskType,
|
getTaskType,
|
||||||
switchTaskFilter,
|
switchTaskFilter,
|
||||||
|
setFunctionPowers,
|
||||||
fetchData,
|
fetchData,
|
||||||
submitTask,
|
submitTask,
|
||||||
downloadFile,
|
downloadFile,
|
||||||
|
|||||||
@@ -1,525 +0,0 @@
|
|||||||
import { showMessageError, showMessageOK } from '@/utils/dialog'
|
|
||||||
import { httpDownload, httpGet, httpPost } from '@/utils/http'
|
|
||||||
import { replaceImg } from '@/utils/libs'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { showConfirmDialog } from 'vant'
|
|
||||||
import { computed, reactive, ref, watch } from 'vue'
|
|
||||||
|
|
||||||
export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|
||||||
// 响应式数据
|
|
||||||
const activeCategory = ref('image_generation')
|
|
||||||
const useImageInput = ref(false)
|
|
||||||
const submitting = ref(false)
|
|
||||||
const listLoading = ref(false)
|
|
||||||
const listFinished = ref(false)
|
|
||||||
const currentList = ref([])
|
|
||||||
const showMediaDialog = ref(false)
|
|
||||||
const currentMediaUrl = ref('')
|
|
||||||
const currentPrompt = ref('')
|
|
||||||
const page = ref(1)
|
|
||||||
const pageSize = ref(10)
|
|
||||||
const total = ref(0)
|
|
||||||
const currentPowerCost = ref(0)
|
|
||||||
const taskPulling = ref(true)
|
|
||||||
const tastPullHandler = ref(null)
|
|
||||||
|
|
||||||
// 新增:算力配置
|
|
||||||
const powerConfig = ref({
|
|
||||||
text_to_image: 20,
|
|
||||||
image_to_image: 30,
|
|
||||||
image_edit: 25,
|
|
||||||
image_effects: 15,
|
|
||||||
text_to_video: 100,
|
|
||||||
image_to_video: 120,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 功能分类
|
|
||||||
const categories = ref([
|
|
||||||
{ key: 'image_generation', name: '图像生成' },
|
|
||||||
{ key: 'image_editing', name: '图像编辑' },
|
|
||||||
{ key: 'image_effects', name: '图像特效' },
|
|
||||||
{ key: 'video_generation', name: '视频生成' },
|
|
||||||
])
|
|
||||||
|
|
||||||
// 选项数据
|
|
||||||
const imageSizeOptions = [
|
|
||||||
{ label: '1:1 (1328x1328)', value: '1328x1328' },
|
|
||||||
{ label: '3:2 (1584x1056)', value: '1584x1056' },
|
|
||||||
{ label: '2:3 (1056x1584)', value: '1056x1584' },
|
|
||||||
{ label: '4:3 (1472x1104)', value: '1472x1104' },
|
|
||||||
{ label: '3:4 (1104x1472)', value: '1104x1472' },
|
|
||||||
{ label: '16:9 (1664x936)', value: '1664x936' },
|
|
||||||
{ label: '9:16 (936x1664)', value: '936x1664' },
|
|
||||||
{ label: '21:9 (2016x864)', value: '2016x864' },
|
|
||||||
{ label: '9:21 (864x2016)', value: '864x2016' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const videoAspectRatioOptions = [
|
|
||||||
{ label: '1:1 (正方形)', value: '1:1' },
|
|
||||||
{ label: '16:9 (横版)', value: '16:9' },
|
|
||||||
{ label: '9:16 (竖版)', value: '9:16' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const imageEffectsTemplateOptions = [
|
|
||||||
{
|
|
||||||
label: '毛毡3D拍立得风格',
|
|
||||||
value: 'felt_3d_polaroid',
|
|
||||||
preview: '/images/jimeng/templates/felt_3d_polaroid.png',
|
|
||||||
},
|
|
||||||
{ label: '像素世界风', value: 'my_world', preview: '/images/jimeng/templates/my_world.png' },
|
|
||||||
{
|
|
||||||
label: '像素世界-万物通用版',
|
|
||||||
value: 'my_world_universal',
|
|
||||||
preview: '/images/jimeng/templates/my_world_universal.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '盲盒玩偶风',
|
|
||||||
value: 'plastic_bubble_figure',
|
|
||||||
preview: '/images/jimeng/templates/plastic_bubble_figure.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '塑料泡罩人偶-文字卡头版',
|
|
||||||
value: 'plastic_bubble_figure_cartoon_text',
|
|
||||||
preview: '/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '毛绒玩偶风',
|
|
||||||
value: 'furry_dream_doll',
|
|
||||||
preview: '/images/jimeng/templates/furry_dream_doll.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '迷你世界玩偶风',
|
|
||||||
value: 'micro_landscape_mini_world',
|
|
||||||
preview: '/images/jimeng/templates/micro_landscape_mini_world.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '微型景观小世界-职业版',
|
|
||||||
value: 'micro_landscape_mini_world_professional',
|
|
||||||
preview: '/images/jimeng/templates/micro_landscape_mini_world_professional.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '亚克力挂饰',
|
|
||||||
value: 'acrylic_ornaments',
|
|
||||||
preview: '/images/jimeng/templates/acrylic_ornaments.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '毛毡钥匙扣',
|
|
||||||
value: 'felt_keychain',
|
|
||||||
preview: '/images/jimeng/templates/felt_keychain.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Lofi 像素人物小卡',
|
|
||||||
value: 'lofi_pixel_character_mini_card',
|
|
||||||
preview: '/images/jimeng/templates/lofi_pixel_character_mini_card.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '天使形象手办',
|
|
||||||
value: 'angel_figurine',
|
|
||||||
preview: '/images/jimeng/templates/angel_figurine.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '躺在毛茸茸肚皮里',
|
|
||||||
value: 'lying_in_fluffy_belly',
|
|
||||||
preview: '/images/jimeng/templates/lying_in_fluffy_belly.png',
|
|
||||||
},
|
|
||||||
{ label: '玻璃球', value: 'glass_ball', preview: '/images/jimeng/templates/glass_ball.png' },
|
|
||||||
]
|
|
||||||
|
|
||||||
// 功能参数
|
|
||||||
// 各功能的参数
|
|
||||||
const textToImageParams = reactive({
|
|
||||||
size: '1328x1328',
|
|
||||||
scale: 2.5,
|
|
||||||
seed: -1,
|
|
||||||
use_pre_llm: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const imageToImageParams = reactive({
|
|
||||||
image_input: '',
|
|
||||||
size: '1328x1328',
|
|
||||||
gpen: 0.4,
|
|
||||||
skin: 0.3,
|
|
||||||
skin_unifi: 0,
|
|
||||||
gen_mode: 'creative',
|
|
||||||
seed: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
const imageEditParams = reactive({
|
|
||||||
image_input: '',
|
|
||||||
scale: 0.5,
|
|
||||||
seed: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
const imageEffectsParams = reactive({
|
|
||||||
image_input: '',
|
|
||||||
template_id: '',
|
|
||||||
size: '1328x1328',
|
|
||||||
})
|
|
||||||
|
|
||||||
const textToVideoParams = reactive({
|
|
||||||
aspect_ratio: '16:9',
|
|
||||||
seed: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
const imageToVideoParams = reactive({
|
|
||||||
image_urls: [],
|
|
||||||
aspect_ratio: '16:9',
|
|
||||||
seed: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 计算属性
|
|
||||||
const activeFunction = computed(() => {
|
|
||||||
if (activeCategory.value === 'image_generation') {
|
|
||||||
return useImageInput.value ? 'image_to_image' : 'text_to_image'
|
|
||||||
} else if (activeCategory.value === 'image_editing') {
|
|
||||||
return 'image_edit'
|
|
||||||
} else if (activeCategory.value === 'image_effects') {
|
|
||||||
return 'image_effects'
|
|
||||||
} else if (activeCategory.value === 'video_generation') {
|
|
||||||
return useImageInput.value ? 'image_to_video' : 'text_to_video'
|
|
||||||
}
|
|
||||||
return 'text_to_image'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 新增:动态计算当前算力消耗
|
|
||||||
const updateCurrentPowerCost = () => {
|
|
||||||
const functionKey = activeFunction.value
|
|
||||||
currentPowerCost.value = powerConfig.value[functionKey] || 10
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听任务类型变化,自动更新算力
|
|
||||||
watch(
|
|
||||||
[activeCategory, useImageInput],
|
|
||||||
() => {
|
|
||||||
updateCurrentPowerCost()
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
const getCategoryIcon = (category) => {
|
|
||||||
const iconMap = {
|
|
||||||
image_generation: 'iconfont icon-image',
|
|
||||||
image_editing: 'iconfont icon-edit',
|
|
||||||
image_effects: 'iconfont icon-chuangzuo',
|
|
||||||
video_generation: 'iconfont icon-video',
|
|
||||||
}
|
|
||||||
return iconMap[category] || 'iconfont icon-image'
|
|
||||||
}
|
|
||||||
|
|
||||||
const switchCategory = (key) => {
|
|
||||||
activeCategory.value = key
|
|
||||||
useImageInput.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增:获取算力配置
|
|
||||||
const fetchPowerConfig = async () => {
|
|
||||||
try {
|
|
||||||
const res = await httpGet('/api/jimeng/power-config')
|
|
||||||
if (res.data) {
|
|
||||||
powerConfig.value = res.data
|
|
||||||
updateCurrentPowerCost() // 更新当前算力消耗
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取算力配置失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitTask = () => {
|
|
||||||
if (!currentPrompt.value.trim()) {
|
|
||||||
showMessageError('请输入提示词')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
submitting.value = true
|
|
||||||
let requestData = { task_type: activeFunction.value, prompt: currentPrompt.value }
|
|
||||||
// 根据功能类型添加相应参数
|
|
||||||
switch (activeFunction.value) {
|
|
||||||
case 'text_to_image':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
width: parseInt(textToImageParams.size.split('x')[0]),
|
|
||||||
height: parseInt(textToImageParams.size.split('x')[1]),
|
|
||||||
scale: textToImageParams.scale,
|
|
||||||
seed: textToImageParams.seed,
|
|
||||||
use_pre_llm: textToImageParams.use_pre_llm,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'image_to_image':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
image_input: imageToImageParams.image_input[0],
|
|
||||||
width: parseInt(imageToImageParams.size.split('x')[0]),
|
|
||||||
height: parseInt(imageToImageParams.size.split('x')[1]),
|
|
||||||
gpen: imageToImageParams.gpen,
|
|
||||||
skin: imageToImageParams.skin,
|
|
||||||
skin_unifi: imageToImageParams.skin_unifi,
|
|
||||||
gen_mode: imageToImageParams.gen_mode,
|
|
||||||
seed: imageToImageParams.seed,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'image_edit':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
image_input: imageEditParams.image_input[0],
|
|
||||||
scale: imageEditParams.scale,
|
|
||||||
seed: imageEditParams.seed,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'image_effects':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
image_input: imageEffectsParams.image_input[0],
|
|
||||||
template_id: imageEffectsParams.template_id,
|
|
||||||
width: parseInt(imageEffectsParams.size.split('x')[0]),
|
|
||||||
height: parseInt(imageEffectsParams.size.split('x')[1]),
|
|
||||||
prompt: imageEffectsParams.prompt,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'text_to_video':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
aspect_ratio: textToVideoParams.aspect_ratio,
|
|
||||||
seed: textToVideoParams.seed,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'image_to_video':
|
|
||||||
Object.assign(requestData, {
|
|
||||||
image_urls: imageToVideoParams.image_input,
|
|
||||||
aspect_ratio: imageToVideoParams.aspect_ratio,
|
|
||||||
seed: imageToVideoParams.seed,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpPost('/api/jimeng/task', requestData)
|
|
||||||
.then(() => {
|
|
||||||
fetchData(1)
|
|
||||||
taskPulling.value = true
|
|
||||||
showMessageOK('创建任务成功')
|
|
||||||
currentPrompt.value = ''
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
showMessageError('创建任务失败:' + e.message)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
submitting.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchData = (_page) => {
|
|
||||||
if (_page) {
|
|
||||||
page.value = _page
|
|
||||||
}
|
|
||||||
listLoading.value = true
|
|
||||||
|
|
||||||
return httpPost('/api/jimeng/jobs', { page: page.value, page_size: pageSize.value })
|
|
||||||
.then((res) => {
|
|
||||||
total.value = res.data.total
|
|
||||||
let needPull = false
|
|
||||||
const items = []
|
|
||||||
if (res.data.items) {
|
|
||||||
for (let v of res.data.items) {
|
|
||||||
if (v.status === 'in_queue' || v.status === 'generating') {
|
|
||||||
needPull = true
|
|
||||||
}
|
|
||||||
items.push(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listLoading.value = false
|
|
||||||
taskPulling.value = needPull
|
|
||||||
|
|
||||||
if (page.value === 1) {
|
|
||||||
currentList.value = items
|
|
||||||
} else {
|
|
||||||
currentList.value.push(...items)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.length < pageSize.value) {
|
|
||||||
listFinished.value = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
listLoading.value = false
|
|
||||||
showMessageError('获取作品列表失败:' + e.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadMore = () => {
|
|
||||||
page.value++
|
|
||||||
fetchData()
|
|
||||||
}
|
|
||||||
|
|
||||||
const playMedia = (item) => {
|
|
||||||
currentMediaUrl.value = item.img_url || item.video_url
|
|
||||||
showMediaDialog.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadFile = async (item) => {
|
|
||||||
const url = replaceImg(item.video_url || item.img_url)
|
|
||||||
const downloadURL = `/api/download?url=${url}`
|
|
||||||
const urlObj = new URL(url)
|
|
||||||
const fileName = urlObj.pathname.split('/').pop()
|
|
||||||
|
|
||||||
item.downloading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await httpDownload(downloadURL)
|
|
||||||
const blob = new Blob([response.data])
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = URL.createObjectURL(blob)
|
|
||||||
link.download = fileName
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
URL.revokeObjectURL(link.href)
|
|
||||||
item.downloading = false
|
|
||||||
} catch (error) {
|
|
||||||
showMessageError('下载失败')
|
|
||||||
item.downloading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const retryTask = (id) => {
|
|
||||||
return httpGet('/api/jimeng/retry', { id })
|
|
||||||
.then(() => {
|
|
||||||
showMessageOK('重试任务成功')
|
|
||||||
fetchData(1)
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
showMessageError('重试任务失败:' + e.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeJob = async (item) => {
|
|
||||||
return showConfirmDialog({
|
|
||||||
title: '确认删除',
|
|
||||||
message: '此操作将会删除任务相关文件,继续操作吗?',
|
|
||||||
confirmButtonText: '确认删除',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return httpGet('/api/jimeng/remove', { id: item.id })
|
|
||||||
.then(() => {
|
|
||||||
showMessageOK('任务删除成功')
|
|
||||||
fetchData(1)
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
showMessageError('任务删除失败:' + e.message)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFunctionName = (type) => {
|
|
||||||
const nameMap = {
|
|
||||||
text_to_image: '文生图',
|
|
||||||
image_to_image: '图生图',
|
|
||||||
image_edit: '图像编辑',
|
|
||||||
image_effects: '图像特效',
|
|
||||||
text_to_video: '文生视频',
|
|
||||||
image_to_video: '图生视频',
|
|
||||||
}
|
|
||||||
return nameMap[type] || type
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTaskType = (type) => {
|
|
||||||
return type.includes('video') ? 'warning' : 'primary'
|
|
||||||
}
|
|
||||||
|
|
||||||
const startTaskPolling = () => {
|
|
||||||
tastPullHandler.value = setInterval(() => {
|
|
||||||
if (taskPulling.value) {
|
|
||||||
fetchData(1)
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopTaskPolling = () => {
|
|
||||||
if (tastPullHandler.value) {
|
|
||||||
clearInterval(tastPullHandler.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeMediaDialog = () => {
|
|
||||||
showMediaDialog.value = false
|
|
||||||
currentMediaUrl.value = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增:复制提示词功能
|
|
||||||
const copyPrompt = (prompt) => {
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(prompt)
|
|
||||||
.then(() => {
|
|
||||||
showMessageOK('提示词已复制')
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
showMessageError('复制失败')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增:复制错误信息功能
|
|
||||||
const copyErrorMsg = (msg) => {
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(msg)
|
|
||||||
.then(() => {
|
|
||||||
showMessageOK('错误信息已复制')
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
showMessageError('复制失败')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增:初始化方法
|
|
||||||
const init = async () => {
|
|
||||||
await fetchPowerConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
// State
|
|
||||||
activeCategory,
|
|
||||||
useImageInput,
|
|
||||||
submitting,
|
|
||||||
listLoading,
|
|
||||||
listFinished,
|
|
||||||
currentList,
|
|
||||||
showMediaDialog,
|
|
||||||
currentMediaUrl,
|
|
||||||
currentPrompt,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
total,
|
|
||||||
currentPowerCost,
|
|
||||||
taskPulling,
|
|
||||||
tastPullHandler,
|
|
||||||
categories,
|
|
||||||
imageSizeOptions,
|
|
||||||
videoAspectRatioOptions,
|
|
||||||
imageEffectsTemplateOptions,
|
|
||||||
textToImageParams,
|
|
||||||
imageToImageParams,
|
|
||||||
imageEditParams,
|
|
||||||
imageEffectsParams,
|
|
||||||
textToVideoParams,
|
|
||||||
imageToVideoParams,
|
|
||||||
powerConfig,
|
|
||||||
|
|
||||||
// Computed
|
|
||||||
activeFunction,
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
getCategoryIcon,
|
|
||||||
switchCategory,
|
|
||||||
submitTask,
|
|
||||||
fetchData,
|
|
||||||
loadMore,
|
|
||||||
playMedia,
|
|
||||||
downloadFile,
|
|
||||||
retryTask,
|
|
||||||
removeJob,
|
|
||||||
getFunctionName,
|
|
||||||
getTaskType,
|
|
||||||
startTaskPolling,
|
|
||||||
stopTaskPolling,
|
|
||||||
closeMediaDialog,
|
|
||||||
fetchPowerConfig,
|
|
||||||
copyPrompt,
|
|
||||||
copyErrorMsg,
|
|
||||||
init,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -14,252 +14,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 功能分类选择 -->
|
<!-- 功能与参数(复用 PC 端逻辑) -->
|
||||||
<div class="jimeng-create__content">
|
<div class="jimeng-create__content">
|
||||||
<CustomTabs
|
<!-- 功能分类按钮(来源于 PC 端 store.functions) -->
|
||||||
v-model="jimengStore.activeCategory"
|
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
||||||
@update:modelValue="jimengStore.switchCategory"
|
<CustomTabs v-model="store.activeFunction" @tab-click="store.setFunctionPowers">
|
||||||
>
|
<CustomTabPane v-for="f in store.functions" :key="f.key" :name="f.key" :label="f.name">
|
||||||
<CustomTabPane
|
|
||||||
:label="jimengStore.categories[0].name"
|
|
||||||
:name="jimengStore.categories[0].key"
|
|
||||||
>
|
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>{{ jimengStore.categories[0].name }}</span>
|
<i class="iconfont mr-1" :class="f.icon"></i>
|
||||||
|
{{ f.name }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 参数容器 -->
|
|
||||||
<div class="py-3">
|
|
||||||
<!-- 文生图 -->
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">提示词:</label>
|
|
||||||
</div>
|
|
||||||
<el-input
|
|
||||||
v-model="jimengStore.currentPrompt"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="请输入图片描述,越详细越好"
|
|
||||||
:rows="4"
|
|
||||||
maxlength="2000"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 功能开关 -->
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="flex justify-between items-center w-full">
|
|
||||||
<span class="text-gray-700 font-semibold">图生图人像写真</span>
|
|
||||||
<el-switch v-model="jimengStore.useImageInput" size="default" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 图生图参数 -->
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3" v-if="jimengStore.useImageInput">
|
|
||||||
<ImageUpload
|
|
||||||
v-model="jimengStore.imageToImageParams.image_input"
|
|
||||||
:max-count="1"
|
|
||||||
:multiple="false"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<label class="block text-gray-700 mb-3 font-semibold">图片尺寸:</label>
|
|
||||||
<CustomSelect
|
|
||||||
v-model="jimengStore.textToImageParams.size"
|
|
||||||
:options="jimengStore.imageSizeOptions"
|
|
||||||
title="选择尺寸"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<span class="flex justify-between items-center mb-3">
|
|
||||||
<span class="text-gray-700 font-semibold">创意度:</span>
|
|
||||||
<el-tooltip content="创意度越高,影响文本描述的程度越高" placement="top">
|
|
||||||
<i class="iconfont icon-info cursor-pointer ml-1"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="mt-3">
|
|
||||||
<el-slider
|
|
||||||
v-model="jimengStore.textToImageParams.scale"
|
|
||||||
:min="1"
|
|
||||||
:max="10"
|
|
||||||
:step="0.5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="flex justify-between items-center w-full">
|
|
||||||
<label class="text-gray-700 font-semibold">智能优化提示词</label>
|
|
||||||
<el-switch v-model="jimengStore.textToImageParams.use_pre_llm" size="default" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CustomTabPane>
|
|
||||||
|
|
||||||
<CustomTabPane
|
|
||||||
:name="jimengStore.categories[1].key"
|
|
||||||
:label="jimengStore.categories[1].name"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<span>{{ jimengStore.categories[1].name }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="py-3">
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">编辑提示词:</label>
|
|
||||||
</div>
|
|
||||||
<el-input
|
|
||||||
v-model="jimengStore.currentPrompt"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="描述你想要的编辑效果"
|
|
||||||
:rows="4"
|
|
||||||
maxlength="2000"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<ImageUpload
|
|
||||||
v-model="jimengStore.imageEditParams.image_input"
|
|
||||||
:max-count="1"
|
|
||||||
:multiple="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">编辑强度:</label>
|
|
||||||
</div>
|
|
||||||
<el-slider
|
|
||||||
v-model="jimengStore.imageEditParams.scale"
|
|
||||||
:min="0"
|
|
||||||
:max="1"
|
|
||||||
:step="0.1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CustomTabPane>
|
|
||||||
|
|
||||||
<CustomTabPane
|
|
||||||
:name="jimengStore.categories[2].key"
|
|
||||||
:label="jimengStore.categories[2].name"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<span>{{ jimengStore.categories[2].name }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="py-3">
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<ImageUpload
|
|
||||||
v-model="jimengStore.imageEffectsParams.image_input"
|
|
||||||
:max-count="1"
|
|
||||||
:multiple="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">特效模板:</label>
|
|
||||||
</div>
|
|
||||||
<CustomSelect
|
|
||||||
v-model="jimengStore.imageEffectsParams.template_id"
|
|
||||||
:options="jimengStore.imageEffectsTemplateOptions"
|
|
||||||
title="选择模板"
|
|
||||||
>
|
|
||||||
<template #option="{ option, selected }">
|
|
||||||
<div class="flex items-center w-full">
|
|
||||||
<el-image :src="option.preview" fit="cover" class="w-10 h-10 rounded-lg mr-2" />
|
|
||||||
<span
|
|
||||||
class="font-bold text-gray-900 mr-2"
|
|
||||||
:class="{ '!text-purple-600': selected }"
|
|
||||||
>{{ option.label }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</CustomSelect>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">输出尺寸:</label>
|
|
||||||
</div>
|
|
||||||
<CustomSelect
|
|
||||||
v-model="jimengStore.imageEffectsParams.size"
|
|
||||||
:options="jimengStore.imageSizeOptions"
|
|
||||||
title="选择尺寸"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CustomTabPane>
|
|
||||||
|
|
||||||
<CustomTabPane
|
|
||||||
:name="jimengStore.categories[3].key"
|
|
||||||
:label="jimengStore.categories[3].name"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<span>{{ jimengStore.categories[3].name }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="py-3">
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">提示词:</label>
|
|
||||||
</div>
|
|
||||||
<el-input
|
|
||||||
v-model="jimengStore.currentPrompt"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="请输入你想要的视频效果"
|
|
||||||
:rows="4"
|
|
||||||
maxlength="2000"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="flex justify-between items-center w-full">
|
|
||||||
<label class="text-gray-700 font-semibold">使用图片辅助生成:</label>
|
|
||||||
<el-switch v-model="jimengStore.useImageInput" size="default" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3" v-if="jimengStore.useImageInput">
|
|
||||||
<ImageUpload
|
|
||||||
v-model="jimengStore.imageToVideoParams.image_input"
|
|
||||||
:max-count="2"
|
|
||||||
:multiple="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="text-gray-700 font-semibold">视频比例:</label>
|
|
||||||
</div>
|
|
||||||
<CustomSelect
|
|
||||||
v-model="jimengStore.textToVideoParams.aspect_ratio"
|
|
||||||
:options="jimengStore.videoAspectRatioOptions"
|
|
||||||
title="选择比例"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CustomTabPane>
|
</CustomTabPane>
|
||||||
</CustomTabs>
|
</CustomTabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 参数构建器(移动端组件) -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<ParamBuilderMobile
|
||||||
|
v-model="store.formData"
|
||||||
|
:required-keys="store.requiredKeys"
|
||||||
|
@update:required-keys="(v) => (store.requiredKeys = v)"
|
||||||
|
:items="store.functionParams[store.activeFunction]"
|
||||||
|
:progress="store.progress[store.activeFunction]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
<div
|
||||||
|
class="bg-white rounded-xl p-4 shadow-sm mb-3"
|
||||||
|
v-if="store.functionParams[store.activeFunction].length > 0"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2 text-base"
|
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2 text-base"
|
||||||
type="button"
|
type="button"
|
||||||
@click="jimengStore.submitTask"
|
@click="store.submitTask"
|
||||||
:disabled="jimengStore.submitting"
|
:disabled="store.submitting"
|
||||||
>
|
>
|
||||||
<i v-if="jimengStore.submitting" class="iconfont icon-loading animate-spin"></i>
|
<i v-if="store.submitting" class="iconfont icon-loading animate-spin"></i>
|
||||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||||
<span>{{
|
<span>立即生成 ({{ store.currentPowerCost }})</span>
|
||||||
jimengStore.submitting ? '创作中...' : `立即生成 (${jimengStore.currentPowerCost}算力)`
|
|
||||||
}}</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -267,11 +60,18 @@
|
|||||||
<!-- 作品列表 -->
|
<!-- 作品列表 -->
|
||||||
<div class="jimeng-create__works">
|
<div class="jimeng-create__works">
|
||||||
<h2 class="jimeng-create__works-title">我的作品</h2>
|
<h2 class="jimeng-create__works-title">我的作品</h2>
|
||||||
<div class="jimeng-create__works-list space-y-4" v-if="jimengStore.currentList.length > 0">
|
<van-list
|
||||||
|
:loading="store.loading"
|
||||||
|
@update:loading="store.loading = $event"
|
||||||
|
:finished="store.isOver"
|
||||||
|
finished-text="没有更多了"
|
||||||
|
@load="onLoadMore"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col space-y-4">
|
||||||
<div
|
<div
|
||||||
v-for="item in jimengStore.currentList"
|
v-for="item in store.currentList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="jimeng-create__works-item"
|
class="jimeng-create__works-item w-full"
|
||||||
>
|
>
|
||||||
<div class="jimeng-create__works-item-content">
|
<div class="jimeng-create__works-item-content">
|
||||||
<div class="jimeng-create__works-item-thumb">
|
<div class="jimeng-create__works-item-thumb">
|
||||||
@@ -304,7 +104,7 @@
|
|||||||
</video>
|
</video>
|
||||||
<div
|
<div
|
||||||
class="video-mask absolute top-0 left-0 w-full h-full flex justify-center items-center"
|
class="video-mask absolute top-0 left-0 w-full h-full flex justify-center items-center"
|
||||||
@click="jimengStore.playMedia(item)"
|
@click="playMedia(item)"
|
||||||
>
|
>
|
||||||
<div class="play-btn">
|
<div class="play-btn">
|
||||||
<img src="/images/play.svg" alt="播放" />
|
<img src="/images/play.svg" alt="播放" />
|
||||||
@@ -332,14 +132,17 @@
|
|||||||
<div class="jimeng-create__works-item-info-header">
|
<div class="jimeng-create__works-item-info-header">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<h3 class="jimeng-create__works-item-info-title">
|
<h3 class="jimeng-create__works-item-info-title">
|
||||||
{{ jimengStore.getFunctionName(item.type) }}
|
{{ store.getFunctionName(item.type) }}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="jimeng-create__works-item-info-prompt line-clamp-2">
|
<p class="jimeng-create__works-item-info-prompt line-clamp-2">
|
||||||
{{ item.prompt }}
|
{{ item.prompt }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- 任务状态 -->
|
<!-- 任务状态 -->
|
||||||
<div v-if="item.status !== 'success'" class="jimeng-create__works-item-info-status">
|
<div
|
||||||
|
v-if="item.status !== 'success'"
|
||||||
|
class="jimeng-create__works-item-info-status"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="item.status === 'failed'"
|
v-if="item.status === 'failed'"
|
||||||
class="jimeng-create__works-item-info-status--failed"
|
class="jimeng-create__works-item-info-status--failed"
|
||||||
@@ -361,12 +164,12 @@
|
|||||||
<span
|
<span
|
||||||
:class="[
|
:class="[
|
||||||
'jimeng-create__works-item-info-tags-item',
|
'jimeng-create__works-item-info-tags-item',
|
||||||
jimengStore.getTaskType(item.type) === 'warning'
|
store.getTaskType(item.type) === 'warning'
|
||||||
? 'jimeng-create__works-item-info-tags-item--warning'
|
? 'jimeng-create__works-item-info-tags-item--warning'
|
||||||
: 'jimeng-create__works-item-info-tags-item--primary',
|
: 'jimeng-create__works-item-info-tags-item--primary',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ jimengStore.getFunctionName(item.type) }}
|
{{ store.getFunctionName(item.type) }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="item.power"
|
v-if="item.power"
|
||||||
@@ -384,7 +187,7 @@
|
|||||||
<!-- 复制提示词 -->
|
<!-- 复制提示词 -->
|
||||||
<button
|
<button
|
||||||
v-if="item.prompt"
|
v-if="item.prompt"
|
||||||
@click="jimengStore.copyPrompt(item.prompt)"
|
@click="store.copyPrompt(item.prompt)"
|
||||||
class="jimeng-create__works-item-quick-action-btn"
|
class="jimeng-create__works-item-quick-action-btn"
|
||||||
title="复制提示词"
|
title="复制提示词"
|
||||||
>
|
>
|
||||||
@@ -394,7 +197,7 @@
|
|||||||
<!-- 下载 -->
|
<!-- 下载 -->
|
||||||
<button
|
<button
|
||||||
v-if="item.status === 'success' && (item.img_url || item.video_url)"
|
v-if="item.status === 'success' && (item.img_url || item.video_url)"
|
||||||
@click="jimengStore.downloadFile(item)"
|
@click="store.downloadFile(item)"
|
||||||
:disabled="item.downloading"
|
:disabled="item.downloading"
|
||||||
class="p-2 text-blue-500"
|
class="p-2 text-blue-500"
|
||||||
>
|
>
|
||||||
@@ -407,7 +210,7 @@
|
|||||||
<!-- 重试 -->
|
<!-- 重试 -->
|
||||||
<button
|
<button
|
||||||
v-if="item.status === 'failed'"
|
v-if="item.status === 'failed'"
|
||||||
@click="jimengStore.retryTask(item.id)"
|
@click="store.retryTask(item.id)"
|
||||||
class="p-2 text-green-500"
|
class="p-2 text-green-500"
|
||||||
>
|
>
|
||||||
<i class="iconfont icon-refresh"></i>
|
<i class="iconfont icon-refresh"></i>
|
||||||
@@ -415,7 +218,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 删除 -->
|
<!-- 删除 -->
|
||||||
<button @click="jimengStore.removeJob(item)" class="p-2 text-red-500">
|
<button @click="store.removeJob(item)" class="p-2 text-red-500">
|
||||||
<i class="iconfont icon-remove"></i>
|
<i class="iconfont icon-remove"></i>
|
||||||
<span class="ml-1">删除</span>
|
<span class="ml-1">删除</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -431,7 +234,7 @@
|
|||||||
item.err_msg
|
item.err_msg
|
||||||
}}</span>
|
}}</span>
|
||||||
<button
|
<button
|
||||||
@click="jimengStore.copyErrorMsg(item.err_msg)"
|
@click="store.copyErrorMsg(item.err_msg)"
|
||||||
class="jimeng-create__works-item-error-copy-btn"
|
class="jimeng-create__works-item-error-copy-btn"
|
||||||
title="复制错误信息"
|
title="复制错误信息"
|
||||||
>
|
>
|
||||||
@@ -440,42 +243,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
|
||||||
<div v-if="jimengStore.listLoading" class="jimeng-create__works-loading">
|
|
||||||
<i class="iconfont icon-loading animate-spin"></i>
|
|
||||||
</div>
|
</div>
|
||||||
|
</van-list>
|
||||||
|
|
||||||
<!-- 没有更多了 -->
|
<div class="px-4" v-if="store.currentList.length === 0 && !store.loading">
|
||||||
<div
|
|
||||||
v-if="jimengStore.listFinished && !jimengStore.listLoading"
|
|
||||||
class="jimeng-create__works-finished"
|
|
||||||
>
|
|
||||||
没有更多了
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="px-4" v-else>
|
|
||||||
<van-empty description="暂无数据" image-size="120" />
|
<van-empty description="暂无数据" image-size="120" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 媒体预览弹窗 -->
|
<!-- 媒体预览弹窗 -->
|
||||||
<div
|
<div v-if="store.showDialog" class="jimeng-create__media-dialog" @click="closeMediaDialog">
|
||||||
v-if="jimengStore.showMediaDialog"
|
|
||||||
class="jimeng-create__media-dialog"
|
|
||||||
@click="jimengStore.closeMediaDialog"
|
|
||||||
>
|
|
||||||
<div @click.stop class="jimeng-create__media-dialog-content animate-scale-up">
|
<div @click.stop class="jimeng-create__media-dialog-content animate-scale-up">
|
||||||
<div class="jimeng-create__media-dialog-header">
|
<div class="jimeng-create__media-dialog-header">
|
||||||
<h3>媒体预览</h3>
|
<h3>媒体预览</h3>
|
||||||
<button @click="jimengStore.closeMediaDialog">
|
<button @click="closeMediaDialog">
|
||||||
<i class="iconfont icon-error"></i>
|
<i class="iconfont icon-error"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="jimeng-create__media-dialog-body">
|
<div class="jimeng-create__media-dialog-body">
|
||||||
<video
|
<video
|
||||||
:src="jimengStore.currentMediaUrl"
|
:src="store.currentVideoUrl"
|
||||||
controls
|
controls
|
||||||
autoplay
|
autoplay
|
||||||
class="w-full max-h-[60vh] rounded-lg object-cover"
|
class="w-full max-h-[60vh] rounded-lg object-cover"
|
||||||
@@ -489,335 +276,45 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ImageUpload from '@/components/ImageUpload.vue'
|
import ParamBuilderMobile from '@/components/mobile/ParamBuilderMobile.vue'
|
||||||
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
|
||||||
import CustomTabPane from '@/components/ui/CustomTabPane.vue'
|
import CustomTabPane from '@/components/ui/CustomTabPane.vue'
|
||||||
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
||||||
import { checkSession } from '@/store/cache'
|
import { useJimengStore } from '@/store/jimeng'
|
||||||
import { useJimengStore } from '@/store/mobile/jimeng'
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
import { onMounted, onUnmounted, ref } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const jimengStore = useJimengStore()
|
const store = useJimengStore()
|
||||||
|
|
||||||
// 模板预览相关
|
function goBack() {
|
||||||
const templatePreview = ref('')
|
router.back()
|
||||||
|
}
|
||||||
// 处理模板变更
|
|
||||||
const handleTemplateChange = (value) => {
|
function playMedia(item) {
|
||||||
const selectedTemplate = jimengStore.imageEffectsTemplateOptions.find(
|
store.currentVideoUrl = item.video_url
|
||||||
(opt) => opt.value === value
|
store.showDialog = true
|
||||||
)
|
}
|
||||||
if (selectedTemplate) {
|
|
||||||
templatePreview.value = selectedTemplate.preview || ''
|
function closeMediaDialog() {
|
||||||
// 自动设置提示词为模板名称
|
store.showDialog = false
|
||||||
jimengStore.currentPrompt = selectedTemplate.label
|
store.currentVideoUrl = ''
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkSession()
|
store.init()
|
||||||
.then(() => {
|
|
||||||
jimengStore.init() // 初始化算力配置
|
|
||||||
jimengStore.fetchData(1)
|
|
||||||
jimengStore.startTaskPolling()
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
jimengStore.stopTaskPolling()
|
if (store.cleanup) {
|
||||||
|
store.cleanup()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 工具方法
|
function onLoadMore() {
|
||||||
const goBack = () => {
|
store.fetchData(store.page + 1)
|
||||||
router.back()
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use '@/assets/css/mobile/jimeng.scss';
|
@use '@/assets/css/mobile/jimeng.scss';
|
||||||
|
|
||||||
/* Dark 主题样式 - 按照 theme-dark.scss 的模式 */
|
|
||||||
:root[data-theme='dark'] .jimeng-create {
|
|
||||||
background-color: rgb(13, 20, 53);
|
|
||||||
|
|
||||||
/* 页面头部样式 */
|
|
||||||
.sticky {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 功能分类选择 */
|
|
||||||
.jimeng-create__content {
|
|
||||||
.bg-white {
|
|
||||||
background-color: rgb(55, 65, 81) !important;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-gray-700 {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-gray-900 {
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-gray-600 {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-gray-500 {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-gray-100:hover {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Element Plus 组件样式覆盖 */
|
|
||||||
:deep(.el-input__wrapper) {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
border-color: rgb(75, 85, 99) !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-input__inner) {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-input__inner::placeholder) {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-textarea__inner) {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-textarea__inner::placeholder) {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-switch__core) {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
border-color: rgb(75, 85, 99) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-switch.is-checked .el-switch__core) {
|
|
||||||
background-color: rgb(139, 92, 246) !important;
|
|
||||||
border-color: rgb(139, 92, 246) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-slider__runway) {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-slider__bar) {
|
|
||||||
background-color: rgb(139, 92, 246) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-slider__button) {
|
|
||||||
border-color: rgb(139, 92, 246) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-tooltip__trigger) {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提交按钮 */
|
|
||||||
.bg-gradient-to-r {
|
|
||||||
background: linear-gradient(88deg, #af61f0 1.44%, #5b62ce) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: linear-gradient(88deg, #9f51e0 1.44%, #4b52be) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
background: linear-gradient(88deg, #6b7280 1.44%, #4b5563) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 作品列表 */
|
|
||||||
.jimeng-create__works {
|
|
||||||
&-title {
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
background-color: rgb(55, 65, 81) !important;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
|
|
||||||
&-content {
|
|
||||||
.jimeng-create__works-item-info {
|
|
||||||
&-title {
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-prompt {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-tags {
|
|
||||||
&-item {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
|
|
||||||
&--warning {
|
|
||||||
background-color: rgb(239, 68, 68) !important;
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--primary {
|
|
||||||
background-color: rgb(59, 130, 246) !important;
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--power {
|
|
||||||
background-color: rgb(139, 92, 246) !important;
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-quick-actions {
|
|
||||||
button {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-error {
|
|
||||||
&-content {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
border-color: rgb(239, 68, 68) !important;
|
|
||||||
|
|
||||||
.jimeng-create__works-item-error-text {
|
|
||||||
color: rgb(239, 68, 68) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jimeng-create__works-item-error-copy-btn {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-loading {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-finished {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 媒体预览弹窗 */
|
|
||||||
.jimeng-create__media-dialog {
|
|
||||||
background-color: rgba(0, 0, 0, 0.8) !important;
|
|
||||||
|
|
||||||
&-content {
|
|
||||||
background-color: rgb(55, 65, 81) !important;
|
|
||||||
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
border-bottom-color: rgb(75, 85, 99) !important;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图片上传组件 */
|
|
||||||
:deep(.image-upload) {
|
|
||||||
.upload-area {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
border-color: rgb(75, 85, 99) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: rgb(139, 92, 246) !important;
|
|
||||||
background-color: rgb(55, 65, 81) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-text {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-icon {
|
|
||||||
color: rgb(139, 92, 246) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 自定义选择组件 */
|
|
||||||
:deep(.custom-select) {
|
|
||||||
.select-trigger {
|
|
||||||
background-color: rgb(31, 41, 55) !important;
|
|
||||||
border-color: rgb(75, 85, 99) !important;
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-dropdown {
|
|
||||||
background-color: rgb(55, 65, 81) !important;
|
|
||||||
border-color: rgb(75, 85, 99) !important;
|
|
||||||
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-option {
|
|
||||||
color: rgb(209, 213, 219) !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgb(75, 85, 99) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background-color: rgb(139, 92, 246) !important;
|
|
||||||
color: rgb(255, 255, 255) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 空状态组件 */
|
|
||||||
:deep(.van-empty) {
|
|
||||||
.van-empty__description {
|
|
||||||
color: rgb(156, 163, 175) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user