wallitor/wallitor-gui/src/components/AddItem.vue

304 lines
8.1 KiB
Vue

<template>
<div ref="bg" class="item-add-bg" :class="{
'item-add-show': visible,
}">
<div class="item-add-content">
<header class="colbox item-add-header">
<div class="colbox item-add-header-icon item-add-header-item" @click="toggleVisible">
<template v-if="visible">
<SvgIcon name="keyboard-arrow-down" size="20px"></SvgIcon>
</template>
<template v-else>
<SvgIcon name="add" size="20px"></SvgIcon>
</template>
</div>
<div class="item-add-header-item item-add-header-title">
<div>添加</div>
</div>
</header>
<main class="item-add-main">
<table class="item-add-form">
<tbody>
<tr>
<td class="item-add-form-title">标题</td>
<td><input type="text" v-model="addInfo.name" /></td>
</tr>
<tr>
<td class="item-add-form-title">文件</td>
<td>
<template v-if="image_src">
<img :src="image_src" class="item-add-image" @click="selectPreview">
</template>
<template v-else>
<div class="item-add-preview" @click="selectPreview">
<div class="item-add-preview-text">
<SvgIcon name="add" size="20px"></SvgIcon>点击添加封面
</div>
</div>
</template>
<div class="colbox">
<button class="apply-button" @click="selectMedia">选择</button>
<div>{{ addInfo.media }}</div>
</div>
</td>
</tr>
<tr>
<td class="item-add-form-title">描述</td>
<td><textarea class="item-add-description" v-model="addInfo.description"></textarea></td>
</tr>
<tr>
<td></td>
<td><button class="apply-button" @click="handleAdd">添加</button></td>
</tr>
</tbody>
</table>
</main>
</div>
</div>
</template>
<script setup lang="ts">
import { defineExpose, defineModel, ref } from 'vue';
import SvgIcon from './SvgIcon.vue';
import { open } from '@tauri-apps/plugin-dialog';
import { invoke } from '@tauri-apps/api/core';
import { useStore } from 'vuex';
import { ElMessage } from 'element-plus';
const store = useStore();
const visible = defineModel<boolean>();
const bg = ref<HTMLDivElement | null>(null);
interface AddInfo {
name: string,
preview: string,
media: string,
description: string
}
const addInfo = ref<AddInfo>({
name: "",
preview: "",
media: "",
description: ""
})
const image_src = ref("");
const support_ext = [".mp4", ".mkv", ".flv", ".ts"];
const support_img_ext = [".jpg", ".png", ".gif", ".webp"];
const support_img_ext_map: { [key in (typeof support_img_ext)[number]]: string } = {
'.jpg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
defineExpose({ open })
async function handleFileOpen() {
const file = await open({
multiple: false,
directory: false,
});
return file;
}
function selectMedia() {
handleFileOpen().then((file) => {
if (file) {
let ext = file.substring(file.lastIndexOf("."));
if (support_ext.includes(ext)) {
addInfo.value.media = file;
} else ElMessage({
type: "error",
message: "文件格式不受支持"
})
}
})
}
function selectPreview() {
handleFileOpen().then((file) => {
if (file) {
let ext = file.substring(file.lastIndexOf("."));
if (support_img_ext.includes(ext)) {
addInfo.value.preview = file;
invoke("get_file", {
path: file
}).then((res) => {
let binary_data_arr = new Uint8Array(res as number[]);
const blob = new Blob([binary_data_arr], { type: support_img_ext_map[ext] });
const imageUrl = URL.createObjectURL(blob);
image_src.value = imageUrl;
})
} else ElMessage({
type: "error",
message: "文件格式不受支持"
})
}
})
}
function toggleVisible() {
visible.value = !visible.value
}
function checkInfo(info: AddInfo) {
if (!info.name || !info.media) return false
else return true;
}
function handleAdd() {
if (checkInfo(addInfo.value)) invoke("new_wallpaper", {
info: addInfo.value
}).then((res) => {
if (res as string == "Success") {
addInfo.value = {
name: "",
preview: "",
media: "",
description: ""
}
image_src.value = "";
store.commit("getWpList");
toggleVisible();
ElMessage({
type: "success",
message: "新建成功"
})
}
else ElMessage({
type: "error",
message: `新建失败 ${res}`
})
})
else ElMessage({
type: "error",
message: "请填写名称并选择媒体文件"
})
}
</script>
<style>
.item-add-bg {
border: solid var(--bd-color) 1px;
backdrop-filter: blur(30px) saturate(180%);
box-shadow: var(--shadow-edge-glow), var(--shadow);
background-color: var(--bg-color-alpha-darker);
border-radius: 8px;
left: 50%;
top: calc(100% - 60px);
transform: translate(-50%, 0);
position: absolute;
width: 85%;
height: calc(100% - 55px);
transition: .8s cubic-bezier(0.9, 0, 0, 1.1);
}
.item-add-show {
top: 60px;
}
.item-add-content {
padding: 10px;
height: calc(100% - 20px);
overflow: hidden;
}
.item-add-main {
margin: 10px;
height: calc(100% - 45px);
overflow: auto;
}
.item-add-main::-webkit-scrollbar {
display: none;
}
.item-add-header-icon {
width: 40px;
height: 40px;
border-radius: 5px;
background-color: #3b93ff;
transition: .3s;
align-items: center;
justify-content: center;
}
.item-add-header-icon:hover {
background-color: #1e5daa;
cursor: pointer;
}
.item-add-header-item {
display: flex;
align-items: center;
color: var(--text-color);
}
.item-add-header-title {
font-size: 22px;
font-weight: 600;
margin-left: 10px;
}
.item-add-main {
color: var(--text-color);
}
.item-add-preview {
background: linear-gradient(135deg, #0000000A 0%, #FFFFFF0A 100%);
border-radius: 5px;
width: 400px;
height: 200px;
margin-bottom: 10px;
box-shadow: var(--shadow-edge-glow);
position: relative;
cursor: pointer;
transition: .5s;
}
.item-add-preview:hover {
background: var(--bg-color-alpha-darker);
}
.item-add-preview:active {
transform: scale(0.95);
}
.item-add-image {
background: linear-gradient(135deg, transparent 0%, #FFFFFF10 100%);
border-radius: 5px;
width: 400px;
margin-bottom: 10px;
box-shadow: var(--shadow-edge-glow);
cursor: pointer;
}
.item-add-image:active {
transform: scale(0.95);
}
.item-add-form {
margin-top: 5px;
}
.item-add-form td {
vertical-align: top;
padding: 5px;
}
.item-add-description {
width: 300px;
height: 150px;
}
.item-add-form-title {
font-size: 15px;
font-weight: 600;
}
.item-add-preview-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>