ai-ground-quasar/src/pages/create/index.vue

1330 lines
35 KiB
Vue
Raw Normal View History

2024-05-23 02:20:16 +00:00
<template>
<q-page>
<!-- <article></article>
<div class="gallery-track">
<div class="card">
<div class="card-image-wrapper">
<img src="https://source.unsplash.com/kL3u4Tqfn1s" />
</div>
</div>
<div class="card">
<div class="card-image-wrapper">
<img src="https://source.unsplash.com/yVUQlyRlJSw" />
</div>
</div>
</div> -->
<div class="silder">
<div class="menu-box">
<div class="menu-bg"></div>
<div class="menu-button" @click="setActiveButton(1)">
<div class="no-act" v-show="activeButton === 2">
<img src="../../assets/image/ai/ttp.png" />
<div>AI生图</div>
</div>
<div class="act" v-show="activeButton === 1">
<img src="../../assets/image/ai/ttpact.png" />
<div>AI生图</div>
</div>
</div>
<div class="menu-button" @click="setActiveButton(2)">
<div class="no-act" v-show="activeButton === 1">
<img src="../../assets/image/ai/ptp.png" />
<div>生图PLUS</div>
</div>
<div class="act" v-show="activeButton === 2">
<img src="../../assets/image/ai/ptpact.png" />
<div>生图PLUS</div>
</div>
</div>
</div>
</div>
<div style="display: flex; width: 100%">
<div class="setting-content">
<q-scroll-area style="height: 87%">
<div class="setting">
<div class="prompt-title">
<div style="display: flex; align-items: center">
<img class="cirl" src="../../assets/image/ai/cirl.png" />
<span
style="
color: #fff;
font-weight: bold;
margin-left: 10px;
font-size: 16px;
"
>描述内容</span
>
</div>
<n-popover trigger="hover" content-class="popover">
<template #trigger>
<img
@click="beautify"
src="../../assets/image/ai/run.png"
class="cursor-pointer runse"
/>
</template>
<span>润色</span>
</n-popover>
</div>
<div class="card ms-content">
<n-spin :show="beautifyLoading">
<template #icon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 6V3"></path>
<path d="M16.25 7.75L18.4 5.6"></path>
<path d="M18 12h3"></path>
<path d="M16.25 16.25l2.15 2.15"></path>
<path d="M12 18v3"></path>
<path d="M7.75 16.25L5.6 18.4"></path>
<path d="M6 12H3"></path>
<path d="M7.75 7.75L5.6 5.6"></path>
</g>
</svg>
</template>
<n-input
v-model:value="prompt"
type="textarea"
class="content-input"
:maxlength="200"
show-count
placeholder="请输入一段话或短语、词汇,描述你的想法,用逗号隔开。可以是中文或英文"
/>
</n-spin>
</div>
<div
ref="fadeOut1"
class="prompt-title animate__animated animate__fadeInLeft animate__fast"
v-if="activeButton === 2"
>
<div style="display: flex; align-items: center">
<img class="cirl" src="../../assets/image/ai/cirl.png" />
<span
style="
color: #fff;
font-weight: bold;
margin-left: 10px;
font-size: 16px;
"
>反向词</span
>
</div>
</div>
<div
ref="fadeOut2"
v-if="activeButton === 2"
class="ms-content animate__animated animate__fadeInLeft animate__fast"
style="border: 1px solid #7e7e7e; height: 100px"
>
<n-input
v-model:value="negative_prompt"
type="textarea"
class="content-input"
:bordered="false"
:maxlength="50"
show-count
placeholder="输入你画面中不想要的内容,中文或英文的短语、词汇"
/>
</div>
<div
ref="fadeOut3"
class="prompt-title animate__animated animate__fadeInLeft animate__fast"
v-if="activeButton === 2"
>
<div style="display: flex; align-items: center">
<img class="cirl" src="../../assets/image/ai/cirl.png" />
<span
style="
color: #fff;
font-weight: bold;
margin-left: 10px;
font-size: 16px;
"
>参考图</span
>
</div>
</div>
<div
ref="fadeOut4"
v-if="activeButton === 2"
class="animate__animated animate__fadeInLeft animate__fast"
>
<n-upload
list-type="image-card"
:max="1"
:on-finish="handleChange"
action="http://192.168.1.244:8085/api/ai/upload-file"
>
<n-upload-dragger>
<div style="margin-bottom: 12px">
<img src="../../assets/image/ai/plus.png" />
</div>
<div
style="font-size: 16px; color: #7e7e7e; font-weight: bold"
>
拖拽图片至此或点击上传
</div>
</n-upload-dragger>
</n-upload>
</div>
<div class="prompt-title" style="margin-top: 10px">
<div style="display: flex; align-items: center">
<img class="cirl" src="../../assets/image/ai/cirl.png" />
<span
style="
color: #fff;
font-weight: bold;
margin-left: 10px;
font-size: 16px;
"
>画风类型</span
>
</div>
</div>
<div class="model-box">
<div class="row animate__animated animate__bounceIn">
<div
class="style-box card cursor-pointer"
v-for="(n, index) in modelArr"
:key="`lg-${n}`"
:class="n.nowSelect ? 'border-change' : 'border-default'"
@click="chooseModel(n)"
:style="{
marginLeft: index === 0 || index === 4 ? '0' : '32px',
marginBottom: '25px',
}"
>
<img style="width: 100%; height: 100%" :src="n.replaceImg" />
</div>
<div class="all-box cursor-pointer" @click="openAll">
全部模型
</div>
</div>
</div>
<div class="prompt-title">
<div style="display: flex; align-items: center">
<img class="cirl" src="../../assets/image/ai/cirl.png" />
<span
style="
color: #fff;
font-weight: bold;
margin-left: 10px;
font-size: 16px;
"
>图片尺寸</span
>
</div>
</div>
<div class="model-box">
<div class="row">
<div
class="size-box card cursor-pointer"
v-for="(n, index) in sizeArr"
:key="`lg-${n}`"
:class="n.nowSelect ? 'border-change' : 'border-default'"
@click="chooseSize(n)"
:style="{
marginLeft: index === 0 || index === 3 ? '0' : '18px',
marginBottom: '10px',
}"
>
<div
style="
display: flex;
align-items: center;
justify-content: center;
background: #212121;
width: 128px;
height: 65px;
margin-top: 5px;
"
>
<div style="position: relative">
<span
style="
position: absolute;
top: 45%;
left: 38%;
transform: translate(-50%, -50%);
font-size: 10px;
"
>{{ n.proportion }}</span
>
<img class="left-img" :src="n.img" />
</div>
<div>
<div>{{ n.word }}</div>
<div>{{ n.sizeWord }}</div>
</div>
</div>
</div>
</div>
<div class="wh-box">
<n-input-number
v-model:value="width"
:bordered="false"
placeholder="请输入数值"
></n-input-number>
px
<q-separator
vertical
style="
background: #383838;
margin-left: 16px;
margin-right: 40px;
"
/>
<n-input-number
v-model:value="height"
:bordered="false"
placeholder="请输入数值"
></n-input-number>
px
</div>
</div>
</div>
</q-scroll-area>
<div class="bottom-box">
<div class="num-box">
<n-select
class="num-select"
v-model:value="batch_size"
:options="numOptions"
placeholder="请选择"
/>
<div>图片数量</div>
</div>
<div class="btn-box">
<n-spin :show="createLoading">
<template #icon> </template>
<n-button @click="generate" class="create" quaternary>
<img
style="width: 24px; height: 28px; margin-right: 10px"
src="../../assets/image/ai/star.png"
/>
<div style="display: grid" v-show="!createLoading">
<div
style="
color: #fff;
font-weight: bolder;
font-size: 17px;
margin-bottom: 5px;
"
>
立即生成
</div>
<div style="color: #fff; font-size: 12px">
消耗{{ batch_size }}积分
</div>
</div>
<div
v-show="createLoading"
style="
color: #fff;
font-weight: bolder;
font-size: 17px;
margin-bottom: 5px;
"
>
正在生成中
</div>
</n-button>
</n-spin>
<div>当前剩余积分{{ coin }}</div>
</div>
</div>
</div>
<div class="img-content">
<div class="plane" v-if="noImgData">
<img
ref="plane"
class="animate__animated animate__fadeInBottomLeft"
src="../../assets/image/ai/plane.png"
/>
<div ref="planeWord" class="word animate__animated animate__zoomIn">
快开启您的AI之旅吧
</div>
</div>
<div class="created-img-box" v-else>
<q-scroll-area style="height: 820px" ref="scrollAreaRef">
<div
class="img-list-page"
v-for="(n, index) in historyList"
:key="index"
>
<div
class="animate__animated animate__slideInUp"
style="display: flex; align-items: center"
>
<span style="color: #7e7e7e; margin-right: 10px">{{
n.dateTime
}}</span
><q-separator style="width: 86%; background-color: #7e7e7e" />
</div>
<div
class="animate__animated animate__slideInUp"
style="color: #bebebe"
>
{{ n.prompt }}
</div>
<div class="img-list-box">
<div
v-for="(item, index2) in n.imgList"
:key="index2"
class="img-detail-box animate__animated animate__slideInUp"
>
<n-spin :show="item.length < 1">
<template #icon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 6V3"></path>
<path d="M16.25 7.75L18.4 5.6"></path>
<path d="M18 12h3"></path>
<path d="M16.25 16.25l2.15 2.15"></path>
<path d="M12 18v3"></path>
<path d="M7.75 16.25L5.6 18.4"></path>
<path d="M6 12H3"></path>
<path d="M7.75 7.75L5.6 5.6"></path>
</g>
</svg>
</template>
<template #description>
<span style="color: #bebebe">正在生成中请稍等</span>
</template>
<q-parallax
@click="checkDetail(item)"
v-show="item.length"
:src="item"
>
</q-parallax>
</n-spin>
</div>
</div>
</div>
</q-scroll-area>
</div>
</div>
</div>
<image-detail
:show="showModal"
:detailSrc="detailSrc"
@close-dialog="closeModal"
/>
<mode-list
:show="showListModal"
@handCloseDialog="closeListModal"
@selectModal="selectedModel"
/>
</q-page>
</template>
<script setup>
import "animate.css";
import { ref, onMounted, onBeforeMount, computed, reactive, watch } from "vue";
import { ModelApi,UserApi } from "src/api";
import { processError, processSuccess } from "../../utils/message";
import moment from "moment";
import { LocalStorage, SessionStorage } from "quasar";
import imageDetail from "./components/imageDetail.vue";
import modeList from "./components/modeList.vue";
import { useRoute, useRouter } from "vue-router";
import img34 from "../../assets/image/ai/34.png";
import img43 from "../../assets/image/ai/43.png";
import img916 from "../../assets/image/ai/169.png";
const router = useRouter();
const scrollAreaRef = ref(null);
const plane = ref(null);
const planeWord = ref(null);
const activeButton = ref(1);
const createLoading = ref(false);
const beautifyLoading = ref(false);
const noImgData = ref(true);
const prompt = ref("");
const negative_prompt = ref("");
const apiprompt = ref("");
const init_images = ref([]);
const seletedModel = ref({});
const fadeOut1 = ref(null);
const fadeOut2 = ref(null);
const fadeOut3 = ref(null);
const fadeOut4 = ref(null);
const showModal = ref(false);
const detailSrc = ref("");
const coin = ref(0);
const showListModal = ref(false);
const selectedSize = ref({
nowSelect: true,
img: img34,
word: "社交媒体",
sizeWord: "600*800",
proportion: "3:4",
width: 600,
height: 800,
});
const width = ref(0);
const height = ref(0);
const apiWidth = ref(0);
const apiHeight = ref(0);
const imgList = ref([]);
const historyList = ref([]);
//每次张数
const batch_size = ref(1);
const numOptions = ref([
{
label: "1",
value: 1,
},
{
label: "2",
value: 2,
},
{
label: "3",
value: 3,
},
{
label: "4",
value: 4,
},
{
label: "5",
value: 5,
},
{
label: "6",
value: 6,
},
{
label: "7",
value: 7,
},
{
label: "8",
value: 8,
},
{
label: "9",
value: 9,
},
{
label: "10",
value: 10,
},
]);
const modelArr = ref([]);
const sizeArr = ref([
{
nowSelect: true,
img: img34,
word: "社交媒体",
sizeWord: "600*800",
proportion: "3:4",
width: 600,
height: 800,
},
{
nowSelect: false,
img: img43,
word: "文章配图",
sizeWord: "800*600",
proportion: "4:3",
width: 800,
height: 600,
},
{
nowSelect: false,
img: img916,
word: "海报",
sizeWord: "468*832",
proportion: "9:16",
width: 468,
height: 832,
},
{
nowSelect: false,
img: img34,
word: "电脑桌面",
sizeWord: "832*468",
proportion: "16:9",
width: 832,
height: 468,
},
{
nowSelect: false,
img: img34,
word: "正方形",
sizeWord: "512*512",
proportion: "1:1",
width: 512,
height: 512,
},
]);
// 打开详情弹窗
const checkDetail = (item) => {
showModal.value = true;
detailSrc.value = item;
};
// 关闭详情弹窗
const closeModal = () => {
showModal.value = false;
};
// 打开模型列表
const openAll = () => {
showListModal.value = true;
};
// 关闭模型列表
const closeListModal = () => {
showListModal.value = false;
};
// 获取用户信息
const getUserInfo = async() => {
try {
await UserApi.getUserInfo().then((res) => {
if (res.status === 0) {
coin.value = res.data.coin;
} else {
processError(res.msg);
}
});
} catch (error) {
console.error(error);
}
};
// 模型列表中选择模型
const selectedModel = (model) => {
console.log(model,111);
seletedModel.value = model;
// 将所有模型的nowSelect设置为false
modelArr.value.forEach((item) => {
item.nowSelect = false;
});
// 判断列表中选择的模型是否在初始化的七个模型中
const isExist = modelArr.value.some((item) => item.hash === model.hash);
// 如果存在,则将选中的模型放到第一个
if (isExist) {
modelArr.value.forEach((item) => {
if (item.hash === model.hash) {
item.nowSelect = true;
} else {
item.nowSelect = false;
}
});
} else {
// 如果不存在,则将选中的模型放到第一个,并将其他模型放到后面
modelArr.value = [model, ...modelArr.value.slice(0, 6)];
modelArr.value.forEach((item, index) => {
item.nowSelect = index === 0;
});
}
};
// 上传完毕
const handleChange = (file, List) => {
showModal.value = true;
let resData = JSON.parse(file.event.currentTarget.response);
if (resData.status === 0) {
processSuccess("上传成功");
init_images.value = [resData.data.ori_url];
} else {
processError(resData.msg);
}
};
// 获取模型列表
const getModelList = async () => {
try {
await ModelApi.getModelList({
page: 1,
pageSize: 10,
}).then((res) => {
if (res.status === 0) {
modelArr.value = res.data.list.slice(0, 7).map((item, index) => ({
...item,
nowSelect: index === 0,
}));
seletedModel.value = modelArr.value[0];
} else {
processError(res.msg);
}
});
} catch (error) {
console.error(error);
}
};
// 美化
const beautify = async () => {
beautifyLoading.value = true;
try {
await ModelApi.beautify({
txt: prompt.value,
}).then((res) => {
if (res.status === 0) {
beautifyLoading.value = false;
processSuccess("美化成功");
prompt.value = res.data.content;
} else {
processError(res.msg);
beautifyLoading.value = false;
}
});
} catch (error) {
beautifyLoading.value = false;
console.error(error);
}
};
// 生成
const generate = async () => {
// 如果没登录,跳转登录
const token = LocalStorage.getItem("sd-token");
if (!token) {
router.push("/login");
return;
}
if (!prompt.value) {
createLoading.value = false;
processError("请输入描述内容");
return;
}
if (JSON.stringify(selectedSize.value) !== "{}") {
apiWidth.value = selectedSize.value.width;
apiHeight.value = selectedSize.value.height;
} else {
apiHeight.value = height.value;
apiWidth.value = width.value;
}
if (!apiWidth.value || !apiHeight.value) {
createLoading.value = false;
processError("请输入宽高");
return;
}
createLoading.value = true;
apiprompt.value = await getTranslatePrompt();
if (!apiprompt.value) {
processError("描述内容不合法");
createLoading.value = false;
return;
}
if (activeButton.value === 1) {
txt2img();
} else {
// 如果没有上传图片
if (!init_images.value.length) {
createLoading.value = false;
processError("请上传参考图");
return;
}
img2img();
}
if (plane.value) {
plane.value.classList.add("animate__fadeOutTopRight");
planeWord.value.classList.add("animate__zoomOut");
}
// 放入和生成数量相同的空字符串
historyList.value.unshift({
imgList: new Array(batch_size.value).fill(""),
prompt: prompt.value,
dateTime: moment().format("YYYY年MM月DD日 HH:mm:ss"),
});
// 滚动到顶部
if (scrollAreaRef.value) {
scrollAreaRef.value.setScrollPosition("vertical", 0, 300);
}
setTimeout(() => {
noImgData.value = false;
}, 1000);
};
// 翻译描述内容
const getTranslatePrompt = async () => {
return new Promise((resolve, reject) => {
const data = {
txt: prompt.value,
};
ModelApi.translate(data).then((res) => {
if (res.status === 0) {
resolve(res.data.content);
} else {
processError(res.msg);
createLoading.value = false;
reject();
}
});
});
};
// 文生图
const txt2img = async () => {
try {
let data = {
prompt: apiprompt.value,
real_prompt: prompt.value,
sd_model_hash: seletedModel.value.hash,
width: apiWidth.value,
height: apiHeight.value,
batch_size: batch_size.value, //每次张数
n_iter: 1, //生成批次
steps: 30, //生成步数
override_settings_restore_afterwards: true,
override_settings: {
sd_model_checkpoint: seletedModel.value.title,
},
};
await ModelApi.txt2img(data).then((res) => {
if (res.status === 0) {
createLoading.value = false;
historyList.value[0].imgList = res.data.images;
getUserInfo();
} else {
processError(res.msg);
createLoading.value = false;
historyList.value[0].imgList = new Array(batch_size.value).fill(
"https://cdns.fontree.cn/fonchain-main/prod/image/default/ai/wrong.png"
);
}
});
} catch (error) {
createLoading.value = false;
console.error(error);
}
};
// 图生图
const img2img = async () => {
try {
let data = {
prompt: apiprompt.value,
real_prompt: prompt.value,
init_images: init_images.value,
sd_model_hash: seletedModel.value.hash,
real_negative_prompt: negative_prompt.value,
negative_prompt: negative_prompt.value,
width: apiWidth.value,
height: apiHeight.value,
batch_size: batch_size.value, //每次张数
n_iter: 1, //生成批次
steps: 30, //生成步数
override_settings_restore_afterwards: true,
override_settings: {
sd_model_checkpoint: seletedModel.value.title,
},
};
await ModelApi.img2img(data).then((res) => {
if (res.status === 0) {
createLoading.value = false;
historyList.value[0].imgList = res.data.images;
getUserInfo();
} else {
processError(res.msg);
createLoading.value = false;
historyList.value[0].imgList = new Array(batch_size.value).fill(
"https://cdns.fontree.cn/fonchain-main/prod/image/default/ai/wrong.png"
);
}
});
} catch (error) {
createLoading.value = false;
console.error(error);
}
};
// 切换模式
const setActiveButton = (buttonNumber) => {
if (buttonNumber === 1) {
// 添加fade动画
fadeOut1.value.classList.add("animate__fadeOutLeft");
fadeOut2.value.classList.add("animate__fadeOutLeft");
fadeOut3.value.classList.add("animate__fadeOutLeft");
fadeOut4.value.classList.add("animate__fadeOutLeft");
init_images.value = [];
negative_prompt.value = "";
}
let timer = buttonNumber === 1 ? 500 : 0;
setTimeout(() => {
activeButton.value = buttonNumber;
}, timer);
// 背景menu-bg跟随点击的按钮移动
const menuBg = document.querySelector(".menu-bg");
const menuButton = document.querySelectorAll(".menu-button");
const button = menuButton[buttonNumber - 1];
const { top, left, width, height } = button.getBoundingClientRect();
menuBg.style.top = `${top - height + 10}px`;
menuBg.style.left = `${left}px`;
};
const chooseModel = (model) => {
// 如果点击的是已经选中的模型,不做任何操作
if (model.nowSelect) {
return;
}
seletedModel.value = model;
model.nowSelect = !model.nowSelect;
modelArr.value.forEach((item) => {
if (item !== model) {
item.nowSelect = false;
}
});
console.log(seletedModel.value);
};
const chooseSize = (size) => {
// 如果点击的是已经选中的尺寸,不做任何操作
if (size.nowSelect) {
return;
}
selectedSize.value = size;
size.nowSelect = !size.nowSelect;
sizeArr.value.forEach((item) => {
if (item !== size) {
item.nowSelect = false;
}
});
};
// 监听with和height的值如果都有值取消图片和尺寸的选中状态
watch([width, height], ([newWidth, newHeight]) => {
if (newWidth && newHeight) {
selectedSize.value = {};
sizeArr.value.forEach((item) => {
item.nowSelect = false;
});
}
});
onBeforeMount(() => {
getModelList();
getUserInfo();
});
</script>
<style scoped lang="scss">
.q-page {
background: #19191a;
display: flex;
height: 100%;
width: 100%;
}
.q-gutter-sm > * {
margin-left: 15px;
}
.silder {
width: 110px;
background: #212121;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
.menu-box {
position: relative;
display: flex;
flex-direction: column;
margin-top: 30px;
align-items: center;
width: 100%;
height: 100%;
.menu-button {
z-index: 20;
height: 90px;
width: 90px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: transparent;
padding: 20px 10px 20px 10px;
margin-bottom: 20px;
cursor: pointer;
transition: background-color 0.5s ease;
.act img {
width: 55px;
height: auto;
}
.no-act img {
width: 40px;
height: 40px;
margin-bottom: 10px;
}
.act {
color: #fff;
font-weight: bold;
text-shadow: 0px 3px 4px #de53ab;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.no-act {
color: #fff;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
}
.menu-bg {
position: absolute;
padding: 20px 10px 20px 10px;
margin-bottom: 20px;
background: linear-gradient(to right, #62d1ef, #5f33f3, #dc4cac);
z-index: 10;
height: 90px;
width: 90px;
border-radius: 10px;
transition: top 0.5s ease, left 0.5s ease;
top: 0;
left: 10px;
}
}
}
.img-content {
height: 100%;
padding: 15px;
background: #19191a;
flex: 1;
background-image: url("../../assets/image/ai/img-bg.png");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
.plane {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.word {
color: #fff;
font-size: 20px;
font-weight: bold;
margin-top: 20px;
}
}
.created-img-box {
.img-page {
display: flex;
justify-content: space-between;
align-items: center;
span {
color: #7e7e7e;
font-size: 14px;
}
}
.img-list-box {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 20px;
margin-bottom: 20px;
.img-detail-box {
width: 400px;
height: 400px;
background: #212121;
border-radius: 10px;
overflow: hidden;
cursor: pointer;
:deep(.n-spin-content) {
height: 100%;
&:hover {
transform: scale(1.1);
transition: transform 0.5s ease-in-out;
}
&:not(:hover) {
transform: scale(1);
transition: transform 0.5s ease-in-out;
}
}
}
}
}
}
.setting-content {
width: 490px;
padding: 15px;
.setting {
width: 100%;
margin-bottom: 14px;
height: 87%;
background: #212121;
border-radius: 10px;
padding: 20px 20px 20px 20px;
.prompt-title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.cirl {
width: 17px;
height: 20px;
}
// 鼠标悬浮放大后在点击按钮上添加动画
.runse {
width: 22px;
height: 22px;
cursor: pointer;
transition: transform 0.5s ease;
&:hover {
transform: scale(1.2);
}
}
}
}
.ms-content {
border: 2px solid transparent;
animation-play-state: running !important;
height: 125px;
width: 100%;
margin-bottom: 14px;
border-radius: 10px;
.content-input {
background: #212121;
height: 100%;
border-radius: 10px;
}
}
}
.model-box {
width: 100%;
height: 200px;
text-align: center;
.style-box {
width: 80px;
height: 80px;
animation-play-state: running !important;
border-radius: 6px;
display: inline-block;
}
.all-box {
width: 80px;
height: 80px;
// 文字上下左右居中
display: flex;
font-size: 14px;
color: #ffffff;
font-weight: bold;
justify-content: center;
margin-left: 32px;
align-items: center;
background: linear-gradient(260deg, #c448b8, #591df5);
border-radius: 6px;
}
.size-box {
display: flex;
align-items: center;
justify-content: center;
background-color: #212121 !important;
width: 128px;
height: 65px;
animation-play-state: running !important;
border-radius: 6px;
.left-img {
margin-right: 10px;
}
div {
font-size: 11px;
color: #ffffff;
}
}
}
.border-change {
border: 2px solid transparent;
animation-play-state: running;
}
.border-default {
border: 2px solid #7e7e7e;
}
.wh-box {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #ffffff;
padding: 10px 30px 10px 20px;
margin-top: 20px;
border-radius: 6px;
border: 2px solid #7e7e7e;
background: #19191a;
:deep(.n-input) {
background-color: #19191a;
}
:deep(.n-input__suffix) {
display: none;
}
}
.bottom-box {
height: 13%;
margin-top: 10px;
background: #212121;
border-radius: 10px;
padding: 17px 17px 27px 27px;
display: flex;
.num-box {
width: 120px;
text-align: center;
.num-select {
height: 57px;
:deep(.n-base-selection-label) {
height: 57px;
background-color: #212121;
width: 110px;
color: #fff;
}
:deep(.n-base-selection-input__content) {
color: #fff;
}
margin-bottom: 5px;
}
div {
color: #7e7e7e;
}
}
.btn-box {
flex: 1;
text-align: center;
.create {
width: 260px;
height: 55px;
background: linear-gradient(260deg, #c448b8, #591df5);
border-radius: 6px;
color: #fff;
font-weight: bold;
margin-left: 15px;
margin-bottom: 5px;
}
div {
color: #7e7e7e;
}
}
}
.popover {
background: red;
}
.popout {
background: #212121;
}
@property --bg-angle {
inherits: false;
initial-value: 0deg;
syntax: "<angle>";
}
@keyframes spin {
to {
--bg-angle: 360deg;
}
}
//
.gallery-track {
position: fixed;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.25rem;
padding: 0.25rem;
will-change: transform;
}
.card {
border-radius: 1rem;
color: white;
width: min(400px, 90vw);
height: 400px;
overflow: hidden;
animation: spin 2.5s infinite linear paused;
background: linear-gradient(
to bottom,
oklch(0.1 0 240 / 0.95),
oklch(0.1 0 240 / 0.95)
)
padding-box,
conic-gradient(
from var(--bg-angle) in oklch longer hue,
oklch(0.85 0.17 0) 0 0
)
border-box;
& .card-image-wrapper {
height: 135%;
will-change: transform;
& img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
@media (width < 800px) {
.gallery-track {
grid-template-columns: repeat(2, 1fr);
}
}
@media (width < 550px) {
.gallery-track {
grid-template-columns: repeat(1, 1fr);
}
}
//
:deep(.n-input .n-input__border) {
border: 0;
}
// 更改placeholder的颜色
:deep(.n-input__placeholder) {
color: #7e7e7e;
}
:deep(.n-input__textarea-el) {
color: #7e7e7e;
}
:deep(.n-input__input-el) {
color: #7e7e7e;
}
:deep(.n-spin) {
color: #c348b8;
}
:deep(.n-spin-container) {
height: 100%;
}
:deep(.n-spin-content) {
height: 100%;
}
:deep(.n-upload-dragger) {
background: #212121;
height: 420px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
:deep(.n-upload-trigger.n-upload-trigger--image-card) {
width: 420px;
height: 400px;
margin-bottom: 10px;
}
:deep(.n-upload-file) {
width: 420px !important;
height: 400px !important;
}
</style>