- 删除 params/ 动态控件,恢复 paintingProportion/Quantity 独立组件 - 模型参数 UI 双向绑定:proportion/resolution/quantity/customSize 同步到 paramValues - 模型选择器适配 API tags 数组和 display_name,新增 displayNameMap 映射 - 模型配置对齐 RunningHub 文档,精简即梦/通义万相多余参数 - 模型列表缓存改为 30s TTL + pendingRequests 并发去重 - sessionId 改为从登录态获取,禁止随机生成 - Select 下拉菜单增加 max-height 防止溢出 - 更新 CLAUDE.md 架构文档
671 lines
19 KiB
Vue
671 lines
19 KiB
Vue
<template>
|
||
<Transition name="slide-up">
|
||
<div class="input-container" :class="{ generate : !props.isGenerate }" @click="handleContainerClick">
|
||
<div v-if="!props.isGenerate && props.type === 'Painting'" class="title">AI绘画2026</div>
|
||
<div v-if="!props.isGenerate && props.type === 'Video'" class="title">AI视频2026</div>
|
||
|
||
<div class="sender-top">
|
||
<div v-if="useDisplay.Sender_variant === 'default'" class="scroll-to-bottom-text" @click.stop="handleScrollToBottom">回到底部<img src="@/assets/dialog/ArrowDown.svg"></div>
|
||
|
||
<div v-show="showImageUploader" class="upload-img-container">
|
||
<div class="reference-diagram">
|
||
<ImageUploader
|
||
v-if="props.type === 'Painting'"
|
||
ref="referenceDiagramRef"
|
||
v-model="referenceImages"
|
||
:limit="imageUploadLimit"
|
||
@open-canvas="handleOpenCanvas"
|
||
/>
|
||
<VideoImageUploader
|
||
v-else-if="props.type === 'Video'"
|
||
ref="referenceDiagramRef"
|
||
v-model="referenceImages"
|
||
:model-type="modelType"
|
||
:images-count="modelDisplayConfig?.display?.images || 1"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig">
|
||
<template #prefix>
|
||
<div v-if="useDisplay.Sender_variant !== 'default' && props.type === 'Painting'" class="prefix-self-wrap">
|
||
<paintingModel v-model="model" v-model:typeValue="modelType" />
|
||
<paintingProportion
|
||
v-if="showProportion"
|
||
v-model="proportion"
|
||
v-model:resolution="resolution"
|
||
v-model:width="customWidth"
|
||
v-model:height="customHight"
|
||
:proportion-options="paintingProportionOpts"
|
||
:resolution-options="paintingResolutionOpts"
|
||
:allow-custom="hasCustomSize"
|
||
/>
|
||
<Quantity v-if="showQuantity" v-model="quantity" :max="quantityMax" />
|
||
</div>
|
||
|
||
<div v-if="useDisplay.Sender_variant !== 'default' && props.type === 'Video'" class="prefix-self-wrap">
|
||
<Pattern v-model="videoPattern" />
|
||
<videoModel v-model="model" v-model:typeValue="modelType" :video-pattern="videoPattern" />
|
||
|
||
<videoProportion
|
||
v-model="proportion"
|
||
v-model:resolution="resolution"
|
||
:proportion-options="proportionOptions"
|
||
:resolution-options="resolutionOptions"
|
||
/>
|
||
<Time v-model="duration" :options="durationOptions" />
|
||
</div>
|
||
</template>
|
||
|
||
<template #action-list>
|
||
<div style="display: flex; align-items: center; gap: 8px; height: 100%;">
|
||
<el-button v-if="isgerenate" round color="#626aef">
|
||
<i-ep-loading style="animation: spin 1s linear infinite;" />
|
||
</el-button>
|
||
<div v-else class="gerenate" :class="{ isprompt: prompt }" @click="handleStart">
|
||
<img v-if="!prompt" src="@/assets/dialog/darkArrow.svg" alt="" />
|
||
<img v-else src="@/assets/dialog/writerArrow.svg" alt="" />
|
||
<div v-show="useDisplay.Sender_variant !== 'default'">发送</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</Sender>
|
||
|
||
</div>
|
||
</Transition>
|
||
</template>
|
||
|
||
<script setup>
|
||
import videoProportion from './proportion/video.vue'
|
||
import paintingModel from './model/painting.vue'
|
||
import videoModel from './model/video.vue'
|
||
import Pattern from './pattern/index.vue'
|
||
import ImageUploader from './imageUploader/index.vue'
|
||
import VideoImageUploader from './videoImageUploader/index.vue'
|
||
import Time from './Time/index.vue'
|
||
import paintingProportion from './proportion/painting.vue'
|
||
import Quantity from './quantity/index.vue'
|
||
import { Sender } from 'vue-element-plus-x'
|
||
import { useDisplayStore } from '@/stores'
|
||
import { generate } from '@/utils/websocket'
|
||
import { useRouter } from 'vue-router'
|
||
import { getModelId, fetchPlatformModels, getPlatformCode } from '@/utils/modelApi'
|
||
import { fetchModelConfig } from '@/utils/modelConfig'
|
||
import { getModelConfig } from '@/config/models/index.js'
|
||
|
||
const props = defineProps({
|
||
isGenerate: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
generate: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
type: {
|
||
type: String,
|
||
default: 'Painting'
|
||
}
|
||
})
|
||
|
||
const router = useRouter()
|
||
const useDisplay = useDisplayStore()
|
||
const isgerenate = ref(false)
|
||
|
||
const model = ref() // 模型
|
||
const modelType = ref('text')
|
||
|
||
// 当前模型配置
|
||
const modelConfig = computed(() => {
|
||
return props.type === 'Painting' ? getModelConfig(model.value) : null
|
||
})
|
||
|
||
// 模型参数值
|
||
const paramValues = reactive({})
|
||
|
||
const showImageUploader = computed(() => {
|
||
if (props.type === 'Video') return modelType.value !== 'text'
|
||
return modelType.value !== 'text' || modelConfig.value?.inputType === 'image' || modelConfig.value?.inputType === 'both'
|
||
})
|
||
|
||
// 模型是否有数量参数(imageNum),或生成类模型显示张数选择
|
||
const showQuantity = computed(() => {
|
||
if (props.type !== 'Painting') return false
|
||
const qtyParam = modelConfig.value?.params?.find(p => p.ui === 'quantity')
|
||
if (qtyParam) return true
|
||
return modelType.value === 'text' && !modelConfig.value?.params?.find(p => p.name === 'forceSingle')
|
||
})
|
||
|
||
// 模型是否使用 proportion 组件(aspectRatio 参数)
|
||
const showProportion = computed(() => {
|
||
return !!modelConfig.value?.params?.find(p => p.ui === 'proportion')
|
||
})
|
||
|
||
// 模型是否支持自定义尺寸(aspectRatio 选项含 'custom')
|
||
const hasCustomSize = computed(() => {
|
||
const ratioParam = modelConfig.value?.params?.find(p => p.ui === 'proportion')
|
||
return ratioParam?.options?.includes('custom') || false
|
||
})
|
||
|
||
// 从模型配置派生比例选项,回退到默认值
|
||
const paintingProportionOpts = computed(() => {
|
||
const ratioParam = modelConfig.value?.params?.find(p => p.ui === 'proportion')
|
||
if (ratioParam?.options) {
|
||
return ratioParam.options
|
||
.filter(o => o !== 'custom')
|
||
.map(o => ({ value: o, label: o }))
|
||
}
|
||
return proportionOptions.value
|
||
})
|
||
|
||
// 从模型配置派生分辨率选项(仅模型有 resolution 参数时显示)
|
||
const paintingResolutionOpts = computed(() => {
|
||
const resParam = modelConfig.value?.params?.find(p => p.ui === 'resolution')
|
||
if (resParam?.options) {
|
||
return resParam.options.map(o => ({ value: o, label: o.toUpperCase() }))
|
||
}
|
||
return []
|
||
})
|
||
|
||
const imageUploadLimit = computed(() => {
|
||
if (!modelConfig.value) return 4
|
||
const imageParam = modelConfig.value.params.find(p => p.ui === 'imageUpload')
|
||
return imageParam?.maxCount || modelConfig.value.maxImages || 4
|
||
})
|
||
|
||
const promptPlaceholder = ref('描述你想生成的画面和动作。') // 提示词占位符
|
||
|
||
const prompt = ref('') // 提示词
|
||
const proportion = ref('16:9') // 比例(Video 用)
|
||
const resolution = ref('1k') // 分辨率(Video 用)
|
||
const referenceImages = ref([])
|
||
|
||
// 绘画
|
||
const quantity = ref(1) // 生成数量
|
||
const customWidth = ref(1024) // 自定义宽度
|
||
const customHight = ref(1024) // 自定义高度
|
||
|
||
const quantityMax = computed(() => {
|
||
const qtyParam = modelConfig.value?.params?.find(p => p.ui === 'quantity')
|
||
if (qtyParam?.options?.length) return Math.max(...qtyParam.options)
|
||
return 4
|
||
})
|
||
|
||
// 同步模型默认值到 paramValues 和 UI refs
|
||
watch(modelConfig, (config) => {
|
||
if (!config) return
|
||
config.params.forEach(p => {
|
||
if (!(p.name in paramValues)) {
|
||
if (p.name === 'outputFormat') {
|
||
paramValues[p.name] = 'png'
|
||
} else {
|
||
paramValues[p.name] = p.default ?? ''
|
||
}
|
||
}
|
||
})
|
||
// 同步默认值到 UI 控件
|
||
const ratioParam = config.params.find(p => p.ui === 'proportion')
|
||
if (ratioParam) proportion.value = ratioParam.default || '1:1'
|
||
const resParam = config.params.find(p => p.ui === 'resolution')
|
||
if (resParam) resolution.value = resParam.default || '2k'
|
||
const qtyParam = config.params.find(p => p.ui === 'quantity')
|
||
if (qtyParam) quantity.value = qtyParam.default || 1
|
||
const cwParam = config.params.find(p => p.name === 'customWidth')
|
||
if (cwParam) customWidth.value = cwParam.default || 1024
|
||
const chParam = config.params.find(p => p.name === 'customHight')
|
||
if (chParam) customHight.value = chParam.default || 1024
|
||
}, { immediate: true })
|
||
|
||
// 反向同步:UI refs → paramValues
|
||
watch(proportion, (val) => {
|
||
const p = modelConfig.value?.params?.find(param => param.ui === 'proportion')
|
||
if (p) paramValues[p.name] = val
|
||
})
|
||
watch(resolution, (val) => {
|
||
const p = modelConfig.value?.params?.find(param => param.ui === 'resolution')
|
||
if (p) paramValues[p.name] = val
|
||
})
|
||
watch(quantity, (val) => {
|
||
const p = modelConfig.value?.params?.find(param => param.ui === 'quantity')
|
||
if (p) paramValues[p.name] = val
|
||
})
|
||
watch(customWidth, (val) => {
|
||
if (modelConfig.value?.params?.find(p => p.name === 'customWidth')) {
|
||
paramValues.customWidth = val
|
||
}
|
||
})
|
||
watch(customHight, (val) => {
|
||
if (modelConfig.value?.params?.find(p => p.name === 'customHight')) {
|
||
paramValues.customHight = val
|
||
}
|
||
})
|
||
|
||
// 同步参考图片到 paramValues
|
||
watch(referenceImages, (imgs) => {
|
||
const imageParam = modelConfig.value?.params?.find(p => p.ui === 'imageUpload')
|
||
if (imageParam) {
|
||
paramValues[imageParam.name] = imgs.map(img => img.url)
|
||
}
|
||
}, { deep: true })
|
||
|
||
// 视频
|
||
const duration = ref(5) // 时间
|
||
const videoPattern = ref('文生视频') // 视频模式下,默认值为'文生视频'
|
||
|
||
const resolutionOptions = ref([
|
||
{ value: '1k', label: '标清 1K' },
|
||
{ value: '2k', label: '高清 2K' },
|
||
{ value: '4k', label: '超清 4K' },
|
||
])
|
||
const proportionOptions = ref([
|
||
{ value: '智能', label: '智能' },
|
||
{ value: '21:9', label: '21:9' },
|
||
{ value: '16:9', label: '16:9' },
|
||
{ value: '4:3', label: '4:3' },
|
||
{ value: '1:1', label: '1:1' },
|
||
{ value: '3:4', label: '3:4' },
|
||
{ value: '9:16', label: '9:16' },
|
||
])
|
||
const durationOptions = ref([])
|
||
|
||
const isInitialized = ref(false)
|
||
|
||
const autoSizeConfig = computed(() => {
|
||
if (useDisplay.Sender_variant !== 'default') {
|
||
return { minRows: 5, maxRows: 9 }
|
||
} else {
|
||
return { minRows: 1, maxRows: 1 }
|
||
}
|
||
})
|
||
|
||
const modelDisplayConfig = ref(null)
|
||
|
||
// Video: 从远程加载 workflow 配置(保留旧逻辑)
|
||
const loadVideoModelConfig = async (modelName, currentModelType) => {
|
||
try {
|
||
const config = await fetchModelConfig(props.type, modelName, currentModelType)
|
||
modelDisplayConfig.value = config
|
||
|
||
if (config.display) {
|
||
const display = config.display
|
||
if (display.promptPlaceholder) {
|
||
promptPlaceholder.value = display.promptPlaceholder.default || '描述你想生成的画面和动作。'
|
||
}
|
||
if (display.prompt && !isInitialized.value) {
|
||
prompt.value = display.prompt.default || ''
|
||
}
|
||
if (display.resolution) {
|
||
resolution.value = display.resolution.default || '1k'
|
||
resolutionOptions.value = display.resolution.options || []
|
||
}
|
||
if (display.proportion) {
|
||
proportion.value = display.proportion.default || '16:9'
|
||
proportionOptions.value = display.proportion.options || []
|
||
}
|
||
if (display.duration) {
|
||
duration.value = display.duration.default || 5
|
||
durationOptions.value = display.duration.options || []
|
||
}
|
||
}
|
||
isInitialized.value = true
|
||
} catch (error) {
|
||
console.error('加载视频模型配置失败:', error)
|
||
}
|
||
}
|
||
|
||
const handleStart = async () => {
|
||
const currentType = props.type
|
||
let currentModelType = modelType.value
|
||
|
||
if(model.value === 'Seedance 2.0') {
|
||
ElMessage.primary('敬请期待 Seedance 2.0')
|
||
return
|
||
}
|
||
|
||
if (!props.isGenerate) {
|
||
router.push({ name: 'home', query: { loading: false, Generate: true, type: currentType } })
|
||
}
|
||
if (!prompt.value) {
|
||
// eslint-disable-next-line no-undef
|
||
ElMessage.error('请输入提示词')
|
||
return
|
||
}
|
||
if (showImageUploader.value && !referenceImages.value.length){
|
||
ElMessage.warning('请上传图片')
|
||
return
|
||
}
|
||
|
||
isgerenate.value = true
|
||
console.log('生成开始', isgerenate.value)
|
||
const imgs = []
|
||
referenceImages.value.forEach((img, index) => {
|
||
imgs.push({ name: `image_${index + 1}`, url: img.url })
|
||
})
|
||
|
||
// 构建模型参数(Painting 用)
|
||
const modelParams = { ...paramValues }
|
||
if (prompt.value) modelParams.prompt = prompt.value
|
||
|
||
const generateData = {
|
||
model: model.value,
|
||
modelType: currentModelType,
|
||
prompt: prompt.value,
|
||
proportion: proportion.value,
|
||
referenceImages: referenceImages.value,
|
||
quantity: quantity.value,
|
||
resolution: resolution.value,
|
||
customWidth: customWidth.value,
|
||
customHight: customHight.value,
|
||
duration: duration.value,
|
||
videoPattern: videoPattern.value,
|
||
modelParams,
|
||
}
|
||
|
||
const modelId = await getModelId(currentType, model.value)
|
||
|
||
// Painting 用新架构扁平参数,Video 保留旧 params 数组
|
||
const isPainting = currentType === 'Painting'
|
||
const data = {
|
||
type: currentType,
|
||
modelType: currentModelType,
|
||
AIGC: currentType,
|
||
platform: 'runninghub',
|
||
modelName: model.value,
|
||
modelId: modelId || '',
|
||
modelParams: isPainting ? modelParams : {},
|
||
params: isPainting ? [] : [
|
||
{ name: 'prompt', data: prompt.value },
|
||
{ name: 'quantity', data: quantity.value },
|
||
{ name: 'proportion', data: proportion.value },
|
||
{ name: 'resolution', data: resolution.value },
|
||
{ name: 'duration', data: duration.value },
|
||
],
|
||
imgs,
|
||
request: JSON.stringify(generateData)
|
||
}
|
||
await generate(data, generateData)
|
||
console.log('生成中', isgerenate.value)
|
||
}
|
||
|
||
const fillParamsFromResult = (resultData) => {
|
||
if (!resultData) return
|
||
|
||
if (resultData.model !== undefined) model.value = resultData.model
|
||
if (resultData.modelType !== undefined) modelType.value = resultData.modelType
|
||
if (resultData.prompt !== undefined) prompt.value = resultData.prompt
|
||
if (resultData.proportion !== undefined) proportion.value = resultData.proportion
|
||
if (resultData.referenceImages !== undefined) referenceImages.value = resultData.referenceImages
|
||
if (resultData.quantity !== undefined) quantity.value = resultData.quantity
|
||
if (resultData.resolution !== undefined) resolution.value = resultData.resolution
|
||
if (resultData.customWidth !== undefined) customWidth.value = resultData.customWidth
|
||
if (resultData.customHight !== undefined) customHight.value = resultData.customHight
|
||
if (resultData.duration !== undefined) duration.value = resultData.duration
|
||
if (resultData.videoPattern !== undefined) videoPattern.value = resultData.videoPattern
|
||
if (resultData.modelParams !== undefined) Object.assign(paramValues, resultData.modelParams)
|
||
}
|
||
|
||
defineExpose({
|
||
fillParamsFromResult,
|
||
handleStart
|
||
})
|
||
|
||
const handleContainerClick = () => {
|
||
if (useDisplay.Sender_variant === 'default') {
|
||
useDisplay.Sender_variant = 'updown'
|
||
}
|
||
}
|
||
|
||
const handleScrollToBottom = () => {
|
||
console.log('点击回到底部按钮')
|
||
useDisplay.scrollToBottom()
|
||
}
|
||
|
||
const handleOpenCanvas = (data) => {
|
||
useDisplay.openCanvas(data)
|
||
}
|
||
|
||
watch(() => useDisplay.isSubGerenate, (newValue) => {
|
||
console.log('生成状态', newValue)
|
||
isgerenate.value = newValue
|
||
}, { immediate: true })
|
||
|
||
watch([() => model.value, () => modelType.value], async ([newModel, newModelType]) => {
|
||
console.log('模型或类型改变:', newModel, newModelType)
|
||
if (!newModel) return
|
||
if (props.type !== 'Painting') {
|
||
await loadVideoModelConfig(newModel, newModelType)
|
||
}
|
||
})
|
||
|
||
// 预加载平台模型列表,避免首次点击"发送"时才请求接口
|
||
const prefetchModels = () => {
|
||
const code = getPlatformCode(props.type)
|
||
fetchPlatformModels(code)
|
||
}
|
||
|
||
watch(() => props.type, (newType) => {
|
||
if (newType === 'Video') {
|
||
model.value = 'LTX2.0'
|
||
} else {
|
||
model.value = 'flux'
|
||
}
|
||
prefetchModels()
|
||
}, { immediate: true })
|
||
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
/* 输入区域 */
|
||
.input-container {
|
||
width: 50%;
|
||
max-width: 880px;
|
||
position: absolute;
|
||
bottom: 30px;
|
||
z-index: 100;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
border-radius: 10px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.sender-top {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
z-index: 101;
|
||
margin-bottom: 10px;
|
||
position: relative;
|
||
|
||
.scroll-to-bottom-text {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
z-index: 2;
|
||
padding: 10px;
|
||
border-radius: 10px;
|
||
background-color: #F8F9FA;
|
||
color: #666;
|
||
font-size: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 5px;
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
}
|
||
&:hover {
|
||
background-color: #F0F1F2;
|
||
}
|
||
}
|
||
|
||
// 上传图片
|
||
.upload-img-container{
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 80%;
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: start;
|
||
align-items: center;
|
||
z-index: 1;
|
||
gap: 16px;
|
||
padding-left: 20px;
|
||
|
||
.reference-diagram {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
}
|
||
|
||
.generate{
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
// gap: 40px;
|
||
position: relative;
|
||
border: none;
|
||
box-shadow: none;
|
||
|
||
:deep(.el-sender){
|
||
border: none;
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
.prefix-self-wrap{
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 5px;
|
||
|
||
img{
|
||
height: 50px;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
.title{
|
||
background-color: #FFF;
|
||
color: #333;
|
||
text-align: center;
|
||
font-family: "Alibaba PuHuiTi";
|
||
font-size: 24px;
|
||
font-style: normal;
|
||
font-weight: 500;
|
||
line-height: normal;
|
||
margin-bottom: 106px;
|
||
}
|
||
:deep(.el-sender){
|
||
background-color: #F5F6F7;
|
||
border: none;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
:deep(.el-sender:focus-within){
|
||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
:deep(.el-popover.el-popper){
|
||
border-radius: 20px;
|
||
}
|
||
// 时间选择器
|
||
.select{
|
||
background: #ffffff;
|
||
border-radius: 10px;
|
||
border: 1px solid rgba(0, 0, 0, 0.10);
|
||
}
|
||
// 视频效果选择器
|
||
.upload-btn{
|
||
display: flex;
|
||
height: 40px;
|
||
padding: 0 15px;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 5px;
|
||
border-radius: 10px;
|
||
border: 1px solid rgba(0, 0, 0, 0.10);
|
||
background: #ffffff;
|
||
cursor: pointer;
|
||
position: relative;
|
||
}
|
||
.upload-btn:hover{
|
||
background: #E5E7EB;
|
||
}
|
||
/* 圆形按钮 */
|
||
.circle-btn {
|
||
position: absolute;
|
||
right: 0px;
|
||
top: 0px;
|
||
width: 18px;
|
||
height: 18px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
z-index: 90;
|
||
transition: all 0.3s ease;
|
||
color: rgb(0, 0, 0);
|
||
font-size: 20px;
|
||
|
||
&:hover {
|
||
transform: scale(1.1);
|
||
// box-shadow: 0 6px 16px rgba(98, 106, 239, 0.6);
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
}
|
||
}
|
||
|
||
/* 过渡动画 */
|
||
.slide-up-enter-active,
|
||
.slide-up-leave-active {
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.slide-up-enter-from {
|
||
opacity: 0;
|
||
transform: translate(-50%, 100%);
|
||
}
|
||
|
||
.slide-up-leave-to {
|
||
opacity: 0;
|
||
transform: translate(-50%, 100%);
|
||
}
|
||
|
||
.gerenate{
|
||
display: inline-flex;
|
||
height: 40px;
|
||
padding: 0 20px;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 5px;
|
||
border-radius: 10px;
|
||
background: rgba(0, 15, 51, 0.10);
|
||
cursor: pointer;
|
||
|
||
color: #000F33;
|
||
text-align: center;
|
||
font-family: "Microsoft YaHei";
|
||
font-size: 14px;
|
||
font-style: normal;
|
||
font-weight: 700;
|
||
line-height: normal;
|
||
}
|
||
.isprompt{
|
||
color: #ffffff;
|
||
background-color: #000F33;
|
||
}
|
||
// .gerenate:hover{
|
||
// background: rgba(0, 15, 51, 0.20);
|
||
// }
|
||
</style>
|