486 lines
13 KiB
Vue
486 lines
13 KiB
Vue
<template>
|
|
<el-container id="main-container">
|
|
<el-header class="list-view-header" style="">
|
|
<el-page-header style="margin: 10px;" @back="router.push('/');">
|
|
<template #content>
|
|
<span class="text-large font-600 mr-3"> 编辑单词本 </span>
|
|
</template>
|
|
<template #extra>
|
|
<div class="colbox">
|
|
<el-button @click="$router.push('/manage/new')"
|
|
type="success"><v-icon>mdi-plus</v-icon>新建单词本</el-button>
|
|
<div class="pconly">
|
|
<el-dropdown trigger="click" style="margin-left: 20px;">
|
|
<el-button type="primary">
|
|
更多<v-icon size="20px">mdi-chevron-down</v-icon>
|
|
</el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item @click="manage_online_wordsets"><v-icon
|
|
size="20px">mdi-earth</v-icon>管理在线单词本</el-dropdown-item>
|
|
<el-dropdown-item @click="export_set"><v-icon
|
|
size="18px">mdi-export</v-icon>导出</el-dropdown-item>
|
|
<el-dropdown-item @click="import_set"><v-icon color="white" name=''
|
|
size="18px">mdi-import</v-icon>导入</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</el-page-header>
|
|
</el-header>
|
|
<el-container style="height:calc(100% - 60px);position: relative;">
|
|
<el-aside class="list-view-aside">
|
|
<div id="sidebar" class="sidebar-hidden">
|
|
<div id="sidebar-content">
|
|
<div class="sidebar-title">
|
|
|本地
|
|
</div>
|
|
<div v-for="class_name in store.wordsets._allclass" :title="class_name" :key="class_name"
|
|
@click="view(class_name)" class="sidebar-item">
|
|
{{ class_name }}
|
|
</div>
|
|
<div class="sidebar-title">
|
|
|在线
|
|
</div>
|
|
<div v-for="set in Object.keys(online_wordsets)" :key="set">
|
|
<div v-for="(set_class, class_name) in online_wordsets[set]" :title="String(class_name)"
|
|
:key="class_name" @click="view_online(set, String(class_name))" class="sidebar-item">
|
|
{{ class_name }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mbonly" id="show-sidebar" @click="taggle_sidebar">
|
|
<v-icon>list</v-icon>
|
|
</div>
|
|
</div>
|
|
</el-aside>
|
|
<el-main id="wordsets-container">
|
|
<div id="sets-container">
|
|
<div v-if="mode === 0" class="colbox wordclass">
|
|
<div v-for="(wordset, index) in store.wordsets.getClass(view_wordsets)" :key="index"
|
|
class="wordset rowbox">
|
|
<div class="no">
|
|
{{ index + 1 }}
|
|
</div>
|
|
<div class="title">
|
|
{{ wordset.name }}
|
|
</div>
|
|
<div class="created-date">
|
|
创建日期:{{ (new Date(wordset.created)).toLocaleString() }}
|
|
</div>
|
|
<div class="option">
|
|
<v-icon class="btn" @click="edit(wordset.id)">mdi-book-edit</v-icon>
|
|
<v-icon class="btn" @click="del(view_wordsets, wordset.id, index)">mdi-delete</v-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="mode === 1" class="colbox wordclass">
|
|
<div v-for="(wordset, index) in online_wordsets[viewing.set][viewing.book]" :key="index"
|
|
class="wordset rowbox">
|
|
<div class="title">
|
|
{{ wordset.name }}
|
|
</div>
|
|
<div class="created-date">
|
|
创建日期:{{ (new Date(wordset.created)).toLocaleString() }}
|
|
</div>
|
|
<div class="option">
|
|
<v-icon class="btn" @click="show(String(index), wordset.name)">mdi-eye</v-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-main>
|
|
</el-container>
|
|
</el-container>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted,nextTick } from 'vue';
|
|
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus';
|
|
import { useRouter } from 'vue-router';
|
|
import { useMainStore } from '@/store';
|
|
import axios from 'axios';
|
|
import anime from 'animejs';
|
|
|
|
interface WordSet {
|
|
id: string;
|
|
name: string;
|
|
created: number;
|
|
}
|
|
|
|
interface OnlineWordSets {
|
|
[set: string]: {
|
|
[book: string]: {
|
|
[id: string]: WordSet
|
|
}
|
|
}
|
|
}
|
|
|
|
const router = useRouter();
|
|
const store = useMainStore();
|
|
|
|
// Convert data properties to refs/reactive
|
|
const view_wordsets = ref<string>("");
|
|
const viewing = reactive<{
|
|
set: string;
|
|
book: string;
|
|
}>({
|
|
set: "",
|
|
book: "",
|
|
});
|
|
const online_wordsets = ref<OnlineWordSets>({});
|
|
const mode = ref<number>(0);
|
|
|
|
// Convert methods to functions
|
|
const view = (classname: string): void => {
|
|
mode.value = 0;
|
|
view_wordsets.value = classname;
|
|
nextTick(() => {
|
|
anime({
|
|
targets: ".wordset",
|
|
translateY: [-50, 0],
|
|
opacity: [0, 1],
|
|
delay: anime.stagger(50, { grid: [6, 6] })
|
|
})
|
|
})
|
|
};
|
|
|
|
const view_online = (set_name: string, class_name: string): void => {
|
|
mode.value = 1;
|
|
viewing.set = set_name;
|
|
viewing.book = class_name;
|
|
nextTick(() => {
|
|
anime({
|
|
targets: ".wordset",
|
|
translateY: [-50, 0],
|
|
opacity: [0, 1],
|
|
delay: anime.stagger(50, { grid: [6, 6] })
|
|
})
|
|
})
|
|
};
|
|
|
|
const manage_online_wordsets = (): void => {
|
|
window.location.href = '/dashboard';
|
|
};
|
|
|
|
const del = (class_name: string, id: string, index: number): void => {
|
|
ElMessageBox.confirm("确定要删除吗?")
|
|
.then(() => {
|
|
store.wordsets.delSet(class_name, index);
|
|
ElMessage(`已经删除 ${class_name} ${id}`);
|
|
});
|
|
};
|
|
|
|
const edit = (id: string): void => {
|
|
router.push({
|
|
path: "./manage/edit",
|
|
query: { id, classname: view_wordsets.value }
|
|
});
|
|
};
|
|
|
|
const show = (id: string, name: string): void => {
|
|
router.push({
|
|
path: "./manage/show",
|
|
query: { set: viewing.set, book: viewing.book, id, name }
|
|
});
|
|
};
|
|
|
|
const export_set = async (): Promise<void> => {
|
|
store.wordsets.export_set((cnt: number) => {
|
|
ElNotification({
|
|
type: "success",
|
|
title: "导出成功",
|
|
message: `已导出 ${cnt} 本单词本`
|
|
});
|
|
});
|
|
};
|
|
|
|
const import_set = async (): Promise<void> => {
|
|
store.wordsets.import_set((cnt: number) => {
|
|
ElNotification({
|
|
type: "success",
|
|
title: "添加成功",
|
|
message: `已添加 ${cnt} 本单词本`
|
|
});
|
|
});
|
|
};
|
|
|
|
const taggle_sidebar = (): void => {
|
|
let node = document.getElementById("sidebar");
|
|
let class_name = "sidebar-hidden";
|
|
if (node?.classList.contains(class_name)) {
|
|
node.classList.remove(class_name);
|
|
}
|
|
else if (node) {
|
|
node.classList.add(class_name);
|
|
}
|
|
};
|
|
|
|
// Convert created lifecycle hook to onMounted
|
|
onMounted(() => {
|
|
anime({
|
|
targets: ".list-view-header",
|
|
translateY: [-50, 0],
|
|
opacity: [0, 1],
|
|
});
|
|
anime({
|
|
targets: ".list-view-aside",
|
|
translateX: [-50, 0],
|
|
opacity: [0, 1],
|
|
});
|
|
let first_set = store.wordsets._firstClass;
|
|
if (first_set) {
|
|
view(first_set);
|
|
}
|
|
|
|
axios.get<OnlineWordSets>("wordset/list").then(
|
|
(res) => {
|
|
online_wordsets.value = res.data;
|
|
}
|
|
);
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
@media screen and (max-width: 500px) {
|
|
.list-view-header{
|
|
height: 70px;
|
|
}
|
|
.list-view-aside{
|
|
width: 0;
|
|
position: relative;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 500px) {
|
|
.list-view-header{
|
|
height: 60px;
|
|
}
|
|
.list-view-aside{
|
|
width: 180px;
|
|
}
|
|
}
|
|
|
|
.list-view-header{
|
|
justify-content: space-between;
|
|
animation: enter ease-out .6s backwards;
|
|
}
|
|
</style>
|
|
|
|
<style scoped>
|
|
@media screen and (max-width: 500px) {
|
|
#sidebar {
|
|
position: absolute;
|
|
width: 180px;
|
|
margin-top: 20px;
|
|
transition: .5s;
|
|
z-index: 100;
|
|
}
|
|
|
|
.sidebar-hidden {
|
|
transform: translate(-100%, 0);
|
|
}
|
|
|
|
.wordset {
|
|
border-radius: 13px;
|
|
padding: 10px;
|
|
height: 180px;
|
|
width: 40%;
|
|
margin: 5px;
|
|
}
|
|
|
|
.no {
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.title {
|
|
font-weight: 800;
|
|
color: var(--text-color);
|
|
font-size: 30px;
|
|
flex-grow: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
min-height: 60px;
|
|
}
|
|
|
|
.created-date {
|
|
font-size: 12px;
|
|
font-weight: 200;
|
|
height: 45px;
|
|
}
|
|
|
|
.wordset .option {
|
|
opacity: 1;
|
|
}
|
|
|
|
#show-sidebar {
|
|
position: absolute;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 100%;
|
|
width: 40px;
|
|
height: 40px;
|
|
bottom: 0;
|
|
right: -50px;
|
|
color: var(--text-color);
|
|
box-shadow: var(--el-box-shadow);
|
|
background-color: var(--bg-color);
|
|
cursor: pointer;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
#wordsets-container {
|
|
width: 100%;
|
|
display: flex;
|
|
transition: .5s;
|
|
}
|
|
|
|
#sets-container {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
@media screen and (min-width: 500px) {
|
|
#sidebar {
|
|
width: 180px;
|
|
padding-left: 10px;
|
|
margin: 10px;
|
|
}
|
|
|
|
.wordset {
|
|
border-radius: 13px;
|
|
padding: 20px;
|
|
height: 230px;
|
|
width: 170px;
|
|
margin: 20px;
|
|
}
|
|
|
|
.no {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.title {
|
|
font-weight: 800;
|
|
color: var(--text-color);
|
|
font-size: 35px;
|
|
flex-grow: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
min-height: 60px;
|
|
}
|
|
|
|
.created-date {
|
|
font-size: 17px;
|
|
font-weight: 200;
|
|
}
|
|
|
|
.wordset:hover .option {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.wordset {
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
border: solid 1px var(--bd-color);
|
|
opacity: 0;
|
|
}
|
|
|
|
.wordset:hover {
|
|
box-shadow: var(--el-box-shadow);
|
|
}
|
|
|
|
#sidebar {
|
|
background-color: var(--bg-color-solid);
|
|
border-radius: 5px;
|
|
height: calc(100% - 20px);
|
|
box-sizing: border-box;
|
|
border: 1px solid var(--bd-color);
|
|
transition: .5s;
|
|
}
|
|
|
|
#sidebar-content {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.sidebar-title {
|
|
width: 100%;
|
|
font-size: 25px;
|
|
line-height: 60px;
|
|
padding-left: 5px;
|
|
font-weight: 800;
|
|
letter-spacing: 3px;
|
|
}
|
|
|
|
html.bgimged #sidebar {
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
html.bgimged .wordset {
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.sidebar-item {
|
|
margin-left: 10px;
|
|
width: 100%;
|
|
font-size: 20px;
|
|
line-height: 50px;
|
|
padding-left: 5px;
|
|
cursor: pointer;
|
|
transition: .5s;
|
|
}
|
|
|
|
.sidebar-item:hover {
|
|
background-color: #00000033;
|
|
}
|
|
|
|
.option {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
opacity: 0;
|
|
transition: .5s;
|
|
margin: 10px;
|
|
}
|
|
|
|
#wordsets-container {
|
|
overflow-x: auto;
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
padding-bottom: 20px;
|
|
position: relative;
|
|
}
|
|
|
|
#main-container {
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
|
|
.wordclass {
|
|
flex-wrap: wrap
|
|
}
|
|
|
|
.card div {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.el-collapse {
|
|
--el-collapse-header-bg-color: #FFFFFF00;
|
|
--el-collapse-content-bg-color: #FFFFFF00;
|
|
--el-collapse-header-font-size: 18px;
|
|
}
|
|
|
|
.el-aside {
|
|
overflow: visible;
|
|
}
|
|
</style> |