Compare commits
No commits in common. 'main' and 'pyw' have entirely different histories.
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "music_administrate",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "1.2.1",
|
||||||
|
"echarts": "^5.5.1",
|
||||||
|
"element-plus": "^2.8.6",
|
||||||
|
"form-data": "^4.0.1",
|
||||||
|
"vue": "^3.5.12",
|
||||||
|
"vue-router": "^4.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"vite": "^5.4.10"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<router-view></router-view>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 584 B |
After Width: | Height: | Size: 583 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 587 B |
After Width: | Height: | Size: 1001 B |
After Width: | Height: | Size: 601 B |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 195 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 496 B |
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<h1>PlaylistProfile</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<section class="playlist-page">
|
||||||
|
<!-- 分类部分 -->
|
||||||
|
<div class="playlist-categories">
|
||||||
|
<span
|
||||||
|
v-for="category in categories"
|
||||||
|
:key="category"
|
||||||
|
@click="filterByCategory(category)"
|
||||||
|
:class="{ active: selectedCategory === category }"
|
||||||
|
>
|
||||||
|
{{ category }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 歌单展示 -->
|
||||||
|
<div class="playlist">
|
||||||
|
<div
|
||||||
|
class="playlist-item"
|
||||||
|
v-for="item in filteredPlaylist"
|
||||||
|
:key="item.id"
|
||||||
|
@click="openPlaylistDetail(item)"
|
||||||
|
>
|
||||||
|
<img :src="item.cover" :alt="item.title" />
|
||||||
|
<p>{{ item.title }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// import PlaylistDetailPage from "../view/web/PlaylistDetailPage.vue"
|
||||||
|
import PlayListProfile from './PlayListProfile.vue';
|
||||||
|
export default {
|
||||||
|
name: "PlaylistPage",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 歌单数据
|
||||||
|
playlist: [
|
||||||
|
{ id: 1, cover: "https://via.placeholder.com/150", title: "歌单标题1", category: "全部歌单" },
|
||||||
|
{ id: 2, cover: "https://via.placeholder.com/150", title: "歌单标题2", category: "华语" },
|
||||||
|
{ id: 3, cover: "https://via.placeholder.com/150", title: "歌单标题3", category: "欧美" },
|
||||||
|
{ id: 4, cover: "https://via.placeholder.com/150", title: "歌单标题4", category: "日韩" },
|
||||||
|
{ id: 5, cover: "https://via.placeholder.com/150", title: "歌单标题5", category: "轻音乐" },
|
||||||
|
{ id: 6, cover: "https://via.placeholder.com/150", title: "歌单标题6", category: "BGM" },
|
||||||
|
{ id: 7, cover: "https://via.placeholder.com/150", title: "歌单标题7", category: "乐器" },
|
||||||
|
{ id: 8, cover: "https://via.placeholder.com/150", title: "歌单标题8", category: "粤语" },
|
||||||
|
],
|
||||||
|
// 分类列表
|
||||||
|
categories: ["全部歌单", "华语", "粤语", "欧美", "日韩", "轻音乐", "BGM", "乐器"],
|
||||||
|
// 当前选择的分类
|
||||||
|
selectedCategory: "全部歌单",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 根据分类筛选后的歌单
|
||||||
|
filteredPlaylist() {
|
||||||
|
if (this.selectedCategory === "全部歌单") {
|
||||||
|
return this.playlist;
|
||||||
|
}
|
||||||
|
return this.playlist.filter((item) => item.category === this.selectedCategory);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 定义 filterByCategory 方法
|
||||||
|
filterByCategory(category) {
|
||||||
|
this.selectedCategory = category;
|
||||||
|
},
|
||||||
|
openPlaylistDetail(playlist) {
|
||||||
|
this.$emit("switchPage", "PlayListProfile", playlist); // 发射事件到父组件
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 页面样式 */
|
||||||
|
.playlist-page {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分类样式 */
|
||||||
|
.playlist-categories {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-categories span {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
transition: color 0.3s ease, font-weight 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-categories span.active {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #007bff; /* 选中时加深颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 歌单容器样式 */
|
||||||
|
.playlist {
|
||||||
|
display: flex; /* 使用 Flex 布局 */
|
||||||
|
flex-wrap: wrap; /* 子项换行 */
|
||||||
|
gap: 16px; /* 设置间距 */
|
||||||
|
justify-content: space-between; /* 子项均匀分布 */
|
||||||
|
padding: 10px; /* 添加内边距 */
|
||||||
|
box-sizing: border-box; /* 包含内外边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 单个歌单项样式 */
|
||||||
|
.playlist-item {
|
||||||
|
flex: 1 1 calc(25% - 16px); /* 每项占25%的宽度,减去间距 */
|
||||||
|
max-width: calc(25% - 50px); /* 限制最大宽度 */
|
||||||
|
box-sizing: border-box; /* 确保内外边距计算正确 */
|
||||||
|
background-color: #fff; /* 设置背景色 */
|
||||||
|
border-radius: 8px; /* 圆角效果 */
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影 */
|
||||||
|
text-align: center; /* 居中对齐 */
|
||||||
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停效果 */
|
||||||
|
.playlist-item:hover {
|
||||||
|
transform: scale(1.05); /* 放大效果 */
|
||||||
|
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15); /* 增强阴影效果 */
|
||||||
|
background-color: #f0f8ff; /* 更改背景颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片样式 */
|
||||||
|
.playlist-item img {
|
||||||
|
width: 150px; /* 图片宽度 */
|
||||||
|
height: auto; /* 保持图片比例 */
|
||||||
|
border-radius: 8px; /* 圆角效果 */
|
||||||
|
margin-bottom: 8px; /* 图片和文字之间的间距 */
|
||||||
|
transition: transform 0.3s ease; /* 添加平滑缩放效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片悬停缩放 */
|
||||||
|
.playlist-item:hover img {
|
||||||
|
transform: scale(1.1); /* 图片放大 */
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<section class="singer-page">
|
||||||
|
<!-- 分类部分 -->
|
||||||
|
<div class="singer-categories">
|
||||||
|
<span
|
||||||
|
v-for="category in categories"
|
||||||
|
:key="category"
|
||||||
|
@click="filterByCategory(category)"
|
||||||
|
:class="{ active: selectedCategory === category }"
|
||||||
|
>
|
||||||
|
{{ category }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 歌手展示 -->
|
||||||
|
<div class="singer">
|
||||||
|
<div class="singer-item" v-for="singer in filteredSingers" :key="singer.id">
|
||||||
|
<img :src="singer.avatar" :alt="singer.name" />
|
||||||
|
<p>{{ singer.name }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SingerPage",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 歌手数据
|
||||||
|
singers: [
|
||||||
|
{ id: 1, avatar: "https://via.placeholder.com/100", name: "歌手1", gender: "男歌手" },
|
||||||
|
{ id: 2, avatar: "https://via.placeholder.com/100", name: "歌手2", gender: "女歌手" },
|
||||||
|
{ id: 3, avatar: "https://via.placeholder.com/100", name: "歌手3", gender: "男歌手" },
|
||||||
|
{ id: 4, avatar: "https://via.placeholder.com/100", name: "歌手4", gender: "组合歌手" },
|
||||||
|
],
|
||||||
|
// 分类列表
|
||||||
|
categories: ["全部歌手", "男歌手", "女歌手", "组合歌手"],
|
||||||
|
// 当前选择的分类
|
||||||
|
selectedCategory: "全部歌手",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 根据分类筛选后的歌手
|
||||||
|
filteredSingers() {
|
||||||
|
if (this.selectedCategory === "全部歌手") {
|
||||||
|
return this.singers;
|
||||||
|
}
|
||||||
|
return this.singers.filter((singer) => singer.gender === this.selectedCategory);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 切换分类并通知父组件
|
||||||
|
filterByCategory(category) {
|
||||||
|
this.selectedCategory = category;
|
||||||
|
this.$emit('changePage', 'SingerProfile'); // 触发事件修改父组件的 currentPage
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 页面样式 */
|
||||||
|
.singer-page {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分类样式 */
|
||||||
|
.singer-categories {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.singer-categories span {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.singer-categories span.active {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #007bff; /* 选中时加深颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 歌手展示样式 */
|
||||||
|
.singer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.singer-item {
|
||||||
|
flex: 1 1 calc(25% - 20px);
|
||||||
|
max-width: calc(25% - 20px);
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.singer-item img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Singerprofile</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<div class="system-setting">
|
||||||
|
<h2>系统设置</h2>
|
||||||
|
|
||||||
|
<!-- 个人资料 -->
|
||||||
|
<section class="setting-section">
|
||||||
|
<h3>个人资料</h3>
|
||||||
|
<form class="profile-form">
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="username">用户名</label>
|
||||||
|
<input type="text" id="username" v-model="profile.username" placeholder="请输入用户名" />
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="email">邮箱</label>
|
||||||
|
<input type="email" id="email" v-model="profile.email" placeholder="请输入邮箱" />
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="avatar">头像</label>
|
||||||
|
<input type="file" id="avatar" @change="handleAvatarChange" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-save" @click="saveProfile">保存修改</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 更改密码 -->
|
||||||
|
<section class="setting-section">
|
||||||
|
<h3>更改密码</h3>
|
||||||
|
<form class="password-form">
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="current-password">当前密码</label>
|
||||||
|
<input type="password" id="current-password" v-model="password.current" placeholder="请输入当前密码" />
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="new-password">新密码</label>
|
||||||
|
<input type="password" id="new-password" v-model="password.new" placeholder="请输入新密码" />
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label for="confirm-password">确认密码</label>
|
||||||
|
<input type="password" id="confirm-password" v-model="password.confirm" placeholder="再次输入新密码" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-save" @click="changePassword">保存密码</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 账号与安全 -->
|
||||||
|
<section class="setting-section">
|
||||||
|
<h3>账号与安全</h3>
|
||||||
|
<div class="security-item">
|
||||||
|
<p>绑定手机</p>
|
||||||
|
<span>{{ security.phone }}</span>
|
||||||
|
<button @click="editPhone">修改</button>
|
||||||
|
</div>
|
||||||
|
<div class="security-item">
|
||||||
|
<p>两步验证</p>
|
||||||
|
<span>{{ security.twoFactor ? '已启用' : '未启用' }}</span>
|
||||||
|
<button @click="toggleTwoFactor">{{ security.twoFactor ? '关闭' : '启用' }}</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SystemSetting",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 个人资料
|
||||||
|
profile: {
|
||||||
|
username: "音乐达人",
|
||||||
|
email: "example@mail.com",
|
||||||
|
avatar: null,
|
||||||
|
},
|
||||||
|
// 修改密码
|
||||||
|
password: {
|
||||||
|
current: "",
|
||||||
|
new: "",
|
||||||
|
confirm: "",
|
||||||
|
},
|
||||||
|
// 账号与安全
|
||||||
|
security: {
|
||||||
|
phone: "未绑定",
|
||||||
|
twoFactor: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveProfile() {
|
||||||
|
// 提交个人资料修改
|
||||||
|
alert("个人资料已保存!");
|
||||||
|
},
|
||||||
|
handleAvatarChange(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
this.profile.avatar = URL.createObjectURL(file);
|
||||||
|
alert("头像已更换!");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changePassword() {
|
||||||
|
// 校验密码
|
||||||
|
if (this.password.new !== this.password.confirm) {
|
||||||
|
alert("两次密码输入不一致!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alert("密码已修改!");
|
||||||
|
},
|
||||||
|
editPhone() {
|
||||||
|
alert("跳转到手机绑定页面!");
|
||||||
|
},
|
||||||
|
toggleTwoFactor() {
|
||||||
|
this.security.twoFactor = !this.security.twoFactor;
|
||||||
|
alert(this.security.twoFactor ? "两步验证已启用!" : "两步验证已关闭!");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.system-setting {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题样式 */
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #1db954;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-save {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1db954;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-save:hover {
|
||||||
|
background-color: #149740;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 账号与安全样式 */
|
||||||
|
.security-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-item p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-item span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-item button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1db954;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-item button:hover {
|
||||||
|
background-color: #149740;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,10 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
import router from "./router";
|
||||||
|
import ElementPlus from "element-plus";
|
||||||
|
import "element-plus/dist/index.css";
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
app.use(router);
|
||||||
|
app.use(ElementPlus);
|
||||||
|
app.mount("#app");
|
@ -0,0 +1,79 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import LoginPage from '@/components/LoginPage.vue';
|
||||||
|
import Web from '@/view/web/Home.vue';
|
||||||
|
import Admin from '@/view/admin/index.vue';
|
||||||
|
import SystemIndex from '@/view/admin/systemIndex.vue';
|
||||||
|
import UserManager from '@/view/admin/userManager.vue';
|
||||||
|
import ArtistManager from '@/view/admin/artistManager.vue';
|
||||||
|
import PlaylistManager from '@/view/admin/playlistManager.vue';
|
||||||
|
import SonglistManager from '@/view/admin/songlistManager.vue';
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'web',
|
||||||
|
component: Web,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'LoginPage',
|
||||||
|
component: LoginPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/web',
|
||||||
|
name: 'webindex',
|
||||||
|
component: Web,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin',
|
||||||
|
name: 'adminindex',
|
||||||
|
component: Admin,
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
const isAdmin = localStorage.getItem('isLoggedIn');
|
||||||
|
if (isAdmin === 'true') {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next({ name: 'LoginPage' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '', // 默认子路由
|
||||||
|
name: 'SystemIndex',
|
||||||
|
component: SystemIndex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'systemIndex',
|
||||||
|
name: 'systemIndex',
|
||||||
|
component: SystemIndex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'userManager',
|
||||||
|
name: 'userManager',
|
||||||
|
component: UserManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'artistManager',
|
||||||
|
name: 'artistManager',
|
||||||
|
component: ArtistManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'playlistManager',
|
||||||
|
name: 'playlistManager',
|
||||||
|
component: PlaylistManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'songlistManager',
|
||||||
|
name: 'songlistManager',
|
||||||
|
component: SonglistManager,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -0,0 +1,80 @@
|
|||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除 body 的默认边距和填充 */
|
||||||
|
body {
|
||||||
|
margin: 0; /* 移除顶部和底部边距 */
|
||||||
|
padding: 0; /* 移除左右边距 */
|
||||||
|
width: 100vw; /* 设置宽度为视口宽度 */
|
||||||
|
height: 100vh; /* 设置高度为视口高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #app 设置为全屏 */
|
||||||
|
#app {
|
||||||
|
display: flex; /* 使用 flexbox 进行布局 */
|
||||||
|
flex-direction: column; /* 纵向布局 */
|
||||||
|
width: 100%; /* 使宽度填满 */
|
||||||
|
height: 100%; /* 使高度填满 */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import wuwuImg from '../assets/img/wuwu.jpeg';
|
||||||
|
import waokuImg from '../assets/img/waoku.jpg';
|
||||||
|
|
||||||
|
let flag = ref(true);
|
||||||
|
|
||||||
|
const mySwitch = () => {
|
||||||
|
const pre_box = document.querySelector('.pre-box');
|
||||||
|
const img = document.querySelector('#avatar');
|
||||||
|
|
||||||
|
if (!pre_box || !img) {
|
||||||
|
console.error('DOM 元素未找到');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.value) {
|
||||||
|
pre_box.style.transform = 'translateX(100%)';
|
||||||
|
pre_box.style.backgroundColor = '#c9e0ed';
|
||||||
|
img.src = wuwuImg;
|
||||||
|
} else {
|
||||||
|
pre_box.style.transform = 'translateX(0%)';
|
||||||
|
pre_box.style.backgroundColor = '#edd4dc';
|
||||||
|
img.src = waokuImg;
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.value = !flag.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default mySwitch;
|
@ -0,0 +1,8 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const instance = axios.create({
|
||||||
|
baseURL: 'http://localhost:8080/api/users',
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default instance;
|
@ -0,0 +1,343 @@
|
|||||||
|
<template>
|
||||||
|
<div class="artist-management">
|
||||||
|
<!-- 顶部操作栏 -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="search-section">
|
||||||
|
<el-input
|
||||||
|
v-model="searchQuery"
|
||||||
|
placeholder="搜索歌手"
|
||||||
|
style="width: 300px;"
|
||||||
|
@input="filterArtists"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="button-section">
|
||||||
|
<el-button type="primary" @click="openAddArtistDialog">添加歌手</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
@click="batchDelete"
|
||||||
|
:disabled="selection.length === 0"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 歌手列表表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="filteredArtistsData"
|
||||||
|
border
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:selection="selection"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column label="歌手图片" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<img
|
||||||
|
:src="row.imageUrl || 'https://via.placeholder.com/60'"
|
||||||
|
alt="artist image"
|
||||||
|
class="artist-image"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="歌手名称" prop="name" />
|
||||||
|
<el-table-column label="性别" prop="gender" />
|
||||||
|
<el-table-column label="生日" prop="birthday" />
|
||||||
|
<el-table-column label="地区" prop="region" />
|
||||||
|
<el-table-column label="简介" prop="bio" />
|
||||||
|
<el-table-column label="歌曲管理">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="text" @click="manageSongs(row)">管理歌曲</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" size="small" @click="editArtist(row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="deleteArtist(row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 添加歌手对话框 -->
|
||||||
|
<el-dialog title="添加歌手" v-model="isAddDialogVisible" width="500px">
|
||||||
|
<el-form :model="newArtist" label-width="100px">
|
||||||
|
<el-form-item label="歌手名称">
|
||||||
|
<el-input v-model="newArtist.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别">
|
||||||
|
<el-select v-model="newArtist.gender" placeholder="请选择">
|
||||||
|
<el-option label="男" value="男" />
|
||||||
|
<el-option label="女" value="女" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生日">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="newArtist.birthday"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区">
|
||||||
|
<el-input v-model="newArtist.region" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="简介">
|
||||||
|
<el-input type="textarea" v-model="newArtist.bio" rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="isAddDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveNewArtist">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 编辑歌手对话框 -->
|
||||||
|
<el-dialog title="编辑歌手信息" v-model="isEditDialogVisible" width="500px">
|
||||||
|
<el-form :model="currentArtist" label-width="100px">
|
||||||
|
<el-form-item label="歌手名称">
|
||||||
|
<el-input v-model="currentArtist.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别">
|
||||||
|
<el-select v-model="currentArtist.gender" placeholder="请选择">
|
||||||
|
<el-option label="男" value="男" />
|
||||||
|
<el-option label="女" value="女" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生日">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="currentArtist.birthday"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="地区">
|
||||||
|
<el-input v-model="currentArtist.region" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="简介">
|
||||||
|
<el-input type="textarea" v-model="currentArtist.bio" rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="isEditDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveArtist">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script >
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchQuery: "",
|
||||||
|
selection: [],
|
||||||
|
artistsData: [],
|
||||||
|
filteredArtistsData: [],
|
||||||
|
isAddDialogVisible: false,
|
||||||
|
isEditDialogVisible: false,
|
||||||
|
currentArtist: {},
|
||||||
|
newArtist: {
|
||||||
|
imageUrl: "",
|
||||||
|
name: "",
|
||||||
|
gender: "",
|
||||||
|
birthday: "",
|
||||||
|
region: "",
|
||||||
|
bio: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchArtists();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchArtists() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get("http://localhost:8081/singers");
|
||||||
|
|
||||||
|
this.artistsData = response.data;
|
||||||
|
this.filteredArtistsData = [...this.artistsData];
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error("加载歌手数据失败");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openAddArtistDialog() {
|
||||||
|
this.isAddDialogVisible = true;
|
||||||
|
this.resetNewArtist();
|
||||||
|
},
|
||||||
|
async saveNewArtist() {
|
||||||
|
try {
|
||||||
|
const response = await axios.post("http://localhost:8081/singers", this.newArtist);
|
||||||
|
this.artistsData.push(response.data);
|
||||||
|
this.filteredArtistsData = [...this.artistsData];
|
||||||
|
this.isAddDialogVisible = false;
|
||||||
|
this.$message.success("添加成功");
|
||||||
|
} catch {
|
||||||
|
this.$message.error("添加失败");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetNewArtist() {
|
||||||
|
this.newArtist = {
|
||||||
|
imageUrl: "",
|
||||||
|
name: "",
|
||||||
|
gender: "",
|
||||||
|
birthday: "",
|
||||||
|
region: "",
|
||||||
|
bio: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
editArtist(artist) {
|
||||||
|
this.currentArtist = JSON.parse(JSON.stringify(artist));
|
||||||
|
this.isEditDialogVisible = true;
|
||||||
|
},
|
||||||
|
async saveArtist() {
|
||||||
|
try {
|
||||||
|
const response = await axios.put(
|
||||||
|
`http://localhost:8081/singers/${this.currentArtist.id}`,
|
||||||
|
this.currentArtist
|
||||||
|
);
|
||||||
|
const index = this.artistsData.findIndex(
|
||||||
|
(item) => item.id === this.currentArtist.id
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.artistsData[index] = response.data;
|
||||||
|
this.filteredArtistsData = [...this.artistsData];
|
||||||
|
this.isEditDialogVisible = false;
|
||||||
|
this.$message.success("编辑成功");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
this.$message.error("编辑失败");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteArtist(artist) {
|
||||||
|
try {
|
||||||
|
await axios.delete(`http://localhost:8081/singers/${artist.id}`);
|
||||||
|
this.artistsData = this.artistsData.filter((item) => item.id !== artist.id);
|
||||||
|
this.filteredArtistsData = [...this.artistsData];
|
||||||
|
this.$message.success("删除成功");
|
||||||
|
} catch {
|
||||||
|
this.$message.error("删除失败");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async batchDelete() {
|
||||||
|
try {
|
||||||
|
const ids = this.selection.map((item) => item.id);
|
||||||
|
const promises = ids.map((id) =>
|
||||||
|
axios.delete(`http://localhost:8081/singers/${id}`).catch(() => id)
|
||||||
|
);
|
||||||
|
const failedIds = (await Promise.all(promises)).filter((id) => id);
|
||||||
|
this.artistsData = this.artistsData.filter(
|
||||||
|
(item) => !ids.includes(item.id)
|
||||||
|
);
|
||||||
|
this.filteredArtistsData = [...this.artistsData];
|
||||||
|
if (failedIds.length) {
|
||||||
|
this.$message.warning(`部分删除失败: ${failedIds.join(", ")}`);
|
||||||
|
} else {
|
||||||
|
this.$message.success("批量删除成功");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
this.$message.error("批量删除失败");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterArtists() {
|
||||||
|
const query = this.searchQuery.toLowerCase();
|
||||||
|
this.filteredArtistsData = this.artistsData.filter((item) =>
|
||||||
|
item.name.toLowerCase().includes(query)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.artist-management {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artist-image {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化表格 */
|
||||||
|
.el-table {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整操作列按钮的排列 */
|
||||||
|
.el-table-column .cell {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table-column .cell .el-button {
|
||||||
|
margin-right: 10px; /* 按钮之间的间距 */
|
||||||
|
font-size: 12px; /* 更小的字体 */
|
||||||
|
padding: 5px 10px; /* 更小的内边距 */
|
||||||
|
width: auto; /* 按钮宽度自适应 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索框和按钮区域优化 */
|
||||||
|
.search-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px; /* 增加按钮之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化按钮尺寸 */
|
||||||
|
.el-button {
|
||||||
|
font-size: 12px; /* 更小的字体 */
|
||||||
|
padding: 5px 10px; /* 调整按钮内边距 */
|
||||||
|
min-width: 80px; /* 设置最小宽度 */
|
||||||
|
white-space: nowrap; /* 防止文本换行 */
|
||||||
|
max-width: 100px; /* 限制按钮最大宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化操作按钮排列,确保它们不换行 */
|
||||||
|
.el-table-column .cell {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table-column .cell .el-button {
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 5px 10px; /* 更小的内边距 */
|
||||||
|
min-width: 80px; /* 设置最小宽度 */
|
||||||
|
max-width: 120px; /* 限制按钮最大宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保按钮按一行排列 */
|
||||||
|
.button-section {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap; /* 防止换行 */
|
||||||
|
gap: 10px; /* 按钮间隔 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -0,0 +1,486 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@babel/helper-string-parser@^7.25.9":
|
||||||
|
version "7.25.9"
|
||||||
|
resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz"
|
||||||
|
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.25.9":
|
||||||
|
version "7.25.9"
|
||||||
|
resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz"
|
||||||
|
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||||
|
|
||||||
|
"@babel/parser@^7.25.3":
|
||||||
|
version "7.26.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.1.tgz"
|
||||||
|
integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.26.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.26.0":
|
||||||
|
version "7.26.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@babel/types/-/types-7.26.0.tgz"
|
||||||
|
integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.25.9"
|
||||||
|
"@babel/helper-validator-identifier" "^7.25.9"
|
||||||
|
|
||||||
|
"@ctrl/tinycolor@^3.4.1":
|
||||||
|
version "3.6.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz"
|
||||||
|
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
|
||||||
|
|
||||||
|
"@element-plus/icons-vue@^2.3.1":
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz"
|
||||||
|
integrity sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz"
|
||||||
|
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
|
||||||
|
|
||||||
|
"@floating-ui/core@^1.6.0":
|
||||||
|
version "1.6.8"
|
||||||
|
resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.8.tgz"
|
||||||
|
integrity sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/utils" "^0.2.8"
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.0.1":
|
||||||
|
version "1.6.11"
|
||||||
|
resolved "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.11.tgz"
|
||||||
|
integrity sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.6.0"
|
||||||
|
"@floating-ui/utils" "^0.2.8"
|
||||||
|
|
||||||
|
"@floating-ui/utils@^0.2.8":
|
||||||
|
version "0.2.8"
|
||||||
|
resolved "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.8.tgz"
|
||||||
|
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
|
||||||
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
|
|
||||||
|
"@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
|
||||||
|
version "2.11.7"
|
||||||
|
resolved "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz"
|
||||||
|
integrity sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.24.3":
|
||||||
|
version "4.24.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz"
|
||||||
|
integrity sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==
|
||||||
|
|
||||||
|
"@types/estree@1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz"
|
||||||
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
|
"@types/lodash-es@*", "@types/lodash-es@^4.17.6":
|
||||||
|
version "4.17.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz"
|
||||||
|
integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/lodash" "*"
|
||||||
|
|
||||||
|
"@types/lodash@*", "@types/lodash@^4.14.182":
|
||||||
|
version "4.17.13"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.13.tgz"
|
||||||
|
integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
|
||||||
|
|
||||||
|
"@types/web-bluetooth@^0.0.16":
|
||||||
|
version "0.0.16"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz"
|
||||||
|
integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==
|
||||||
|
|
||||||
|
"@vitejs/plugin-vue@^5.1.4":
|
||||||
|
version "5.1.4"
|
||||||
|
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz"
|
||||||
|
integrity sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==
|
||||||
|
|
||||||
|
"@vue/compiler-core@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz"
|
||||||
|
integrity sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.25.3"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
entities "^4.5.0"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
|
"@vue/compiler-dom@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz"
|
||||||
|
integrity sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-core" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
"@vue/compiler-sfc@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz"
|
||||||
|
integrity sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.25.3"
|
||||||
|
"@vue/compiler-core" "3.5.12"
|
||||||
|
"@vue/compiler-dom" "3.5.12"
|
||||||
|
"@vue/compiler-ssr" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.30.11"
|
||||||
|
postcss "^8.4.47"
|
||||||
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
|
"@vue/compiler-ssr@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz"
|
||||||
|
integrity sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
"@vue/devtools-api@^6.6.4":
|
||||||
|
version "6.6.4"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz"
|
||||||
|
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
|
||||||
|
|
||||||
|
"@vue/reactivity@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.12.tgz"
|
||||||
|
integrity sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
"@vue/runtime-core@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz"
|
||||||
|
integrity sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
"@vue/runtime-dom@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz"
|
||||||
|
integrity sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.5.12"
|
||||||
|
"@vue/runtime-core" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
csstype "^3.1.3"
|
||||||
|
|
||||||
|
"@vue/server-renderer@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz"
|
||||||
|
integrity sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-ssr" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
"@vue/shared@3.5.12":
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz"
|
||||||
|
integrity sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==
|
||||||
|
|
||||||
|
"@vueuse/core@^9.1.0":
|
||||||
|
version "9.13.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz"
|
||||||
|
integrity sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==
|
||||||
|
dependencies:
|
||||||
|
"@types/web-bluetooth" "^0.0.16"
|
||||||
|
"@vueuse/metadata" "9.13.0"
|
||||||
|
"@vueuse/shared" "9.13.0"
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
|
"@vueuse/metadata@9.13.0":
|
||||||
|
version "9.13.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz"
|
||||||
|
integrity sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==
|
||||||
|
|
||||||
|
"@vueuse/shared@9.13.0":
|
||||||
|
version "9.13.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz"
|
||||||
|
integrity sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==
|
||||||
|
dependencies:
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
|
async-validator@^4.2.5:
|
||||||
|
version "4.2.5"
|
||||||
|
resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz"
|
||||||
|
integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
|
||||||
|
|
||||||
|
asynckit@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
|
||||||
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
|
axios@1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.npmmirror.com/axios/-/axios-1.2.1.tgz"
|
||||||
|
integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.0"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
|
combined-stream@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz"
|
||||||
|
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||||
|
dependencies:
|
||||||
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
|
csstype@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz"
|
||||||
|
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||||
|
|
||||||
|
dayjs@^1.11.3:
|
||||||
|
version "1.11.13"
|
||||||
|
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz"
|
||||||
|
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||||
|
|
||||||
|
delayed-stream@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
||||||
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
|
echarts@^5.5.1:
|
||||||
|
version "5.5.1"
|
||||||
|
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz"
|
||||||
|
integrity sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==
|
||||||
|
dependencies:
|
||||||
|
tslib "2.3.0"
|
||||||
|
zrender "5.6.0"
|
||||||
|
|
||||||
|
element-plus@^2.8.6:
|
||||||
|
version "2.8.6"
|
||||||
|
resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.8.6.tgz"
|
||||||
|
integrity sha512-fk5jB8V3efM02/4roZ5SWOLArgaYXbxEydZLlXSr+KPAwjNyHBlk2+HO5em8YKo5+RLBoHnn6BaThj6IE4nXoQ==
|
||||||
|
dependencies:
|
||||||
|
"@ctrl/tinycolor" "^3.4.1"
|
||||||
|
"@element-plus/icons-vue" "^2.3.1"
|
||||||
|
"@floating-ui/dom" "^1.0.1"
|
||||||
|
"@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7"
|
||||||
|
"@types/lodash" "^4.14.182"
|
||||||
|
"@types/lodash-es" "^4.17.6"
|
||||||
|
"@vueuse/core" "^9.1.0"
|
||||||
|
async-validator "^4.2.5"
|
||||||
|
dayjs "^1.11.3"
|
||||||
|
escape-html "^1.0.3"
|
||||||
|
lodash "^4.17.21"
|
||||||
|
lodash-es "^4.17.21"
|
||||||
|
lodash-unified "^1.0.2"
|
||||||
|
memoize-one "^6.0.0"
|
||||||
|
normalize-wheel-es "^1.2.0"
|
||||||
|
|
||||||
|
entities@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz"
|
||||||
|
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||||
|
|
||||||
|
esbuild@^0.21.3:
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz"
|
||||||
|
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/aix-ppc64" "0.21.5"
|
||||||
|
"@esbuild/android-arm" "0.21.5"
|
||||||
|
"@esbuild/android-arm64" "0.21.5"
|
||||||
|
"@esbuild/android-x64" "0.21.5"
|
||||||
|
"@esbuild/darwin-arm64" "0.21.5"
|
||||||
|
"@esbuild/darwin-x64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-arm64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-x64" "0.21.5"
|
||||||
|
"@esbuild/linux-arm" "0.21.5"
|
||||||
|
"@esbuild/linux-arm64" "0.21.5"
|
||||||
|
"@esbuild/linux-ia32" "0.21.5"
|
||||||
|
"@esbuild/linux-loong64" "0.21.5"
|
||||||
|
"@esbuild/linux-mips64el" "0.21.5"
|
||||||
|
"@esbuild/linux-ppc64" "0.21.5"
|
||||||
|
"@esbuild/linux-riscv64" "0.21.5"
|
||||||
|
"@esbuild/linux-s390x" "0.21.5"
|
||||||
|
"@esbuild/linux-x64" "0.21.5"
|
||||||
|
"@esbuild/netbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/openbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/sunos-x64" "0.21.5"
|
||||||
|
"@esbuild/win32-arm64" "0.21.5"
|
||||||
|
"@esbuild/win32-ia32" "0.21.5"
|
||||||
|
"@esbuild/win32-x64" "0.21.5"
|
||||||
|
|
||||||
|
escape-html@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz"
|
||||||
|
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
||||||
|
|
||||||
|
estree-walker@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz"
|
||||||
|
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||||
|
|
||||||
|
follow-redirects@^1.15.0:
|
||||||
|
version "1.15.9"
|
||||||
|
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz"
|
||||||
|
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
|
||||||
|
|
||||||
|
form-data@^4.0.0, form-data@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz"
|
||||||
|
integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
lodash-es@*, lodash-es@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
|
||||||
|
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||||
|
|
||||||
|
lodash-unified@^1.0.2:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz"
|
||||||
|
integrity sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==
|
||||||
|
|
||||||
|
lodash@*, lodash@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
magic-string@^0.30.11:
|
||||||
|
version "0.30.12"
|
||||||
|
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.12.tgz"
|
||||||
|
integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
|
memoize-one@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz"
|
||||||
|
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
version "1.52.0"
|
||||||
|
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz"
|
||||||
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
|
mime-types@^2.1.12:
|
||||||
|
version "2.1.35"
|
||||||
|
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz"
|
||||||
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
dependencies:
|
||||||
|
mime-db "1.52.0"
|
||||||
|
|
||||||
|
nanoid@^3.3.7:
|
||||||
|
version "3.3.7"
|
||||||
|
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz"
|
||||||
|
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||||
|
|
||||||
|
normalize-wheel-es@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz"
|
||||||
|
integrity sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==
|
||||||
|
|
||||||
|
picocolors@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"
|
||||||
|
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||||
|
|
||||||
|
postcss@^8.4.43, postcss@^8.4.47:
|
||||||
|
version "8.4.47"
|
||||||
|
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.47.tgz"
|
||||||
|
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.7"
|
||||||
|
picocolors "^1.1.0"
|
||||||
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
|
proxy-from-env@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
||||||
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
|
||||||
|
rollup@^4.20.0:
|
||||||
|
version "4.24.3"
|
||||||
|
resolved "https://registry.npmmirror.com/rollup/-/rollup-4.24.3.tgz"
|
||||||
|
integrity sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "1.0.6"
|
||||||
|
optionalDependencies:
|
||||||
|
"@rollup/rollup-android-arm-eabi" "4.24.3"
|
||||||
|
"@rollup/rollup-android-arm64" "4.24.3"
|
||||||
|
"@rollup/rollup-darwin-arm64" "4.24.3"
|
||||||
|
"@rollup/rollup-darwin-x64" "4.24.3"
|
||||||
|
"@rollup/rollup-freebsd-arm64" "4.24.3"
|
||||||
|
"@rollup/rollup-freebsd-x64" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-arm64-gnu" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-arm64-musl" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-s390x-gnu" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-x64-gnu" "4.24.3"
|
||||||
|
"@rollup/rollup-linux-x64-musl" "4.24.3"
|
||||||
|
"@rollup/rollup-win32-arm64-msvc" "4.24.3"
|
||||||
|
"@rollup/rollup-win32-ia32-msvc" "4.24.3"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.24.3"
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz"
|
||||||
|
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||||
|
|
||||||
|
vite@^5.0.0, vite@^5.4.10:
|
||||||
|
version "5.4.10"
|
||||||
|
resolved "https://registry.npmmirror.com/vite/-/vite-5.4.10.tgz"
|
||||||
|
integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.21.3"
|
||||||
|
postcss "^8.4.43"
|
||||||
|
rollup "^4.20.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
|
vue-demi@*:
|
||||||
|
version "0.14.10"
|
||||||
|
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz"
|
||||||
|
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
|
||||||
|
|
||||||
|
vue-router@^4.4.5:
|
||||||
|
version "4.4.5"
|
||||||
|
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.5.tgz"
|
||||||
|
integrity sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==
|
||||||
|
dependencies:
|
||||||
|
"@vue/devtools-api" "^6.6.4"
|
||||||
|
|
||||||
|
"vue@^3.0.0-0 || ^2.6.0", vue@^3.2.0, vue@^3.2.25, vue@^3.5.12, vue@3.5.12:
|
||||||
|
version "3.5.12"
|
||||||
|
resolved "https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz"
|
||||||
|
integrity sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.5.12"
|
||||||
|
"@vue/compiler-sfc" "3.5.12"
|
||||||
|
"@vue/runtime-dom" "3.5.12"
|
||||||
|
"@vue/server-renderer" "3.5.12"
|
||||||
|
"@vue/shared" "3.5.12"
|
||||||
|
|
||||||
|
zrender@5.6.0:
|
||||||
|
version "5.6.0"
|
||||||
|
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz"
|
||||||
|
integrity sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==
|
||||||
|
dependencies:
|
||||||
|
tslib "2.3.0"
|
@ -0,0 +1,2 @@
|
|||||||
|
/mvnw text eol=lf
|
||||||
|
*.cmd text eol=crlf
|
@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
@ -0,0 +1,19 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
wrapperVersion=3.3.2
|
||||||
|
distributionType=only-script
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
@ -0,0 +1,259 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
|
# OS specific support.
|
||||||
|
native_path() { printf %s\\n "$1"; }
|
||||||
|
case "$(uname)" in
|
||||||
|
CYGWIN* | MINGW*)
|
||||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# set JAVACMD and JAVACCMD
|
||||||
|
set_java_home() {
|
||||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
|
if [ -n "${JAVA_HOME-}" ]; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v java
|
||||||
|
)" || :
|
||||||
|
JAVACCMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v javac
|
||||||
|
)" || :
|
||||||
|
|
||||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# hash string like Java String::hashCode
|
||||||
|
hash_string() {
|
||||||
|
str="${1:-}" h=0
|
||||||
|
while [ -n "$str" ]; do
|
||||||
|
char="${str%"${str#?}"}"
|
||||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||||
|
str="${str#?}"
|
||||||
|
done
|
||||||
|
printf %x\\n $h
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose() { :; }
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf %s\\n "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trim() {
|
||||||
|
# MWRAPPER-139:
|
||||||
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||||
|
# Needed for removing poorly interpreted newline sequences when running in more
|
||||||
|
# exotic environments such as mingw bash on Windows.
|
||||||
|
printf "%s" "${1}" | tr -d '[:space:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
case "${key-}" in
|
||||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||||
|
esac
|
||||||
|
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
|
||||||
|
case "${distributionUrl##*/}" in
|
||||||
|
maven-mvnd-*bin.*)
|
||||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||||
|
*)
|
||||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||||
|
distributionPlatform=linux-amd64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
|
;;
|
||||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
|
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||||
|
|
||||||
|
exec_maven() {
|
||||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "$MAVEN_HOME" ]; then
|
||||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
exec_maven "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${distributionUrl-}" in
|
||||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||||
|
trap clean HUP INT TERM EXIT
|
||||||
|
else
|
||||||
|
die "cannot create temp dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
verbose "Downloading from: $distributionUrl"
|
||||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
# select .zip or .tar.gz
|
||||||
|
if ! command -v unzip >/dev/null; then
|
||||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verbose opt
|
||||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||||
|
|
||||||
|
# normalize http auth
|
||||||
|
case "${MVNW_PASSWORD:+has-password}" in
|
||||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||||
|
verbose "Found wget ... using wget"
|
||||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||||
|
verbose "Found curl ... using curl"
|
||||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||||
|
elif set_java_home; then
|
||||||
|
verbose "Falling back to use Java to download"
|
||||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
cat >"$javaSource" <<-END
|
||||||
|
public class Downloader extends java.net.Authenticator
|
||||||
|
{
|
||||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||||
|
{
|
||||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception
|
||||||
|
{
|
||||||
|
setDefault( new Downloader() );
|
||||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END
|
||||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||||
|
verbose " - Compiling Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||||
|
verbose " - Running Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
|
distributionSha256Result=false
|
||||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
elif command -v sha256sum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
elif command -v shasum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $distributionSha256Result = false ]; then
|
||||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
if command -v unzip >/dev/null; then
|
||||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||||
|
else
|
||||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
|
fi
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
|
clean || :
|
||||||
|
exec_maven "$@"
|
@ -0,0 +1,149 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
|
@SET __MVNW_CMD__=
|
||||||
|
@SET __MVNW_ERROR__=
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
|
@SET PSModulePath=
|
||||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
|
)
|
||||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
|
@SET __MVNW_ARG0_NAME__=
|
||||||
|
@SET MVNW_USERNAME=
|
||||||
|
@SET MVNW_PASSWORD=
|
||||||
|
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
|
@GOTO :EOF
|
||||||
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
|
$VerbosePreference = "Continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
|
if (!$distributionUrl) {
|
||||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
|
"maven-mvnd-*" {
|
||||||
|
$USE_MVND = $true
|
||||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
|
$MVN_CMD = "mvnd.cmd"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
$USE_MVND = $false
|
||||||
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||||
|
}
|
||||||
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||||
|
if ($env:MAVEN_USER_HOME) {
|
||||||
|
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||||
|
}
|
||||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
|
|
||||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
|
trap {
|
||||||
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
|
if ($distributionSha256Sum) {
|
||||||
|
if ($USE_MVND) {
|
||||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
|
try {
|
||||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
|
} catch {
|
||||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.demo;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableJpaRepositories("com.example.demo.repository") // 启用 JPA 仓库扫描
|
||||||
|
@EntityScan("com.example.demo.entity") // 显式指定扫描实体类路径
|
||||||
|
public class DemoApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DemoApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Playlist;
|
||||||
|
import com.example.demo.service.PlaylistService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/playlists")
|
||||||
|
@CrossOrigin(origins = "http://localhost:5173") // 允许来自 5173 端口的请求
|
||||||
|
public class PlaylistController {
|
||||||
|
@Autowired
|
||||||
|
private PlaylistService playlistService;
|
||||||
|
|
||||||
|
// 获取歌单列表
|
||||||
|
@GetMapping
|
||||||
|
public List<Playlist> getPlaylists() {
|
||||||
|
return playlistService.getAllPlaylists();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加歌单
|
||||||
|
@PostMapping
|
||||||
|
public Playlist addPlaylist(@RequestBody Playlist playlist) {
|
||||||
|
return playlistService.addPlaylist(playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除单个歌单
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deletePlaylist(@PathVariable Long id) {
|
||||||
|
playlistService.deletePlaylist(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除歌单
|
||||||
|
@PostMapping("/batch-delete")
|
||||||
|
public void batchDeletePlaylists(@RequestBody List<Long> ids) {
|
||||||
|
playlistService.batchDeletePlaylists(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新歌单
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Playlist updatePlaylist(@PathVariable Long id, @RequestBody Playlist playlist) {
|
||||||
|
// 设置歌单 ID 确保服务层能正确识别要更新的歌单
|
||||||
|
playlist.setId(id);
|
||||||
|
return playlistService.updatePlaylist(playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Singer;
|
||||||
|
import com.example.demo.repository.SingerRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/singers")
|
||||||
|
@CrossOrigin(origins = "http://localhost:5173") // 允许来自 5173 端口的请求
|
||||||
|
public class SingerController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SingerRepository singerRepository;
|
||||||
|
|
||||||
|
// 获取所有歌手
|
||||||
|
@GetMapping
|
||||||
|
public List<Singer> getAllSingers() {
|
||||||
|
return singerRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 ID 获取歌手
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Optional<Singer> getSingerById(@PathVariable Long id) {
|
||||||
|
return singerRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据歌手名称搜索歌手
|
||||||
|
@GetMapping("/search")
|
||||||
|
public List<Singer> searchSingers(@RequestParam String name) {
|
||||||
|
return singerRepository.findByNameContainingIgnoreCase(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新歌手
|
||||||
|
@PostMapping
|
||||||
|
public Singer addSinger(@RequestBody Singer singer) {
|
||||||
|
return singerRepository.save(singer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新歌手信息
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Singer updateSinger(@PathVariable Long id, @RequestBody Singer singer) {
|
||||||
|
// 可以根据需要在这里添加更多的更新逻辑
|
||||||
|
singer.setId(id); // 设置歌手 ID 以保证更新的是正确的歌手
|
||||||
|
return singerRepository.save(singer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除歌手
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deleteSinger(@PathVariable Long id) {
|
||||||
|
singerRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Song;
|
||||||
|
import com.example.demo.repository.SongRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/songs")
|
||||||
|
@CrossOrigin(origins = "http://localhost:5173") // 允许来自 5173 端口的请求
|
||||||
|
public class SongController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SongRepository songRepository;
|
||||||
|
|
||||||
|
// 获取所有歌曲
|
||||||
|
@GetMapping
|
||||||
|
public List<Song> getAllSongs() {
|
||||||
|
return songRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据ID获取歌曲
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Song> getSongById(@PathVariable Long id) {
|
||||||
|
return songRepository.findById(id)
|
||||||
|
.map(song -> ResponseEntity.ok(song))
|
||||||
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加歌曲
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Song> addSong(@RequestBody Song song) {
|
||||||
|
Song savedSong = songRepository.save(song);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(savedSong);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑歌曲
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<Song> updateSong(@PathVariable Long id, @RequestBody Song song) {
|
||||||
|
if (!songRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
song.setId(id);
|
||||||
|
Song updatedSong = songRepository.save(song);
|
||||||
|
return ResponseEntity.ok(updatedSong);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除歌曲
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> deleteSong(@PathVariable Long id) {
|
||||||
|
if (!songRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
songRepository.deleteById(id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import com.example.demo.dto.UserLoginResponse;
|
||||||
|
import com.example.demo.dto.UserUpdateDto;
|
||||||
|
import com.example.demo.entity.User;
|
||||||
|
import com.example.demo.repository.UserRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/users")
|
||||||
|
@CrossOrigin(origins = "http://localhost:5173") // 允许来自 5173 端口的请求
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
// 加密工具
|
||||||
|
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
|
||||||
|
// 注册用户
|
||||||
|
@PostMapping("/register")
|
||||||
|
@Transactional
|
||||||
|
public ResponseEntity<?> register(@RequestBody User user) {
|
||||||
|
// 检查用户名是否已存在
|
||||||
|
if (userRepository.existsByUsername(user.getUsername())) {
|
||||||
|
return ResponseEntity.badRequest().body("用户名已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码加密
|
||||||
|
String encryptedPassword = passwordEncoder.encode(user.getPassword());
|
||||||
|
user.setPassword(encryptedPassword);
|
||||||
|
|
||||||
|
// 默认角色为 'user'
|
||||||
|
if (user.getRole() == null || user.getRole().isEmpty()) {
|
||||||
|
user.setRole("user");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存用户信息
|
||||||
|
userRepository.save(user);
|
||||||
|
return ResponseEntity.ok("注册成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户登录
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseEntity<?> login(@RequestBody User user) {
|
||||||
|
|
||||||
|
System.out.println("==============="+user);
|
||||||
|
User foundUser = userRepository.findByUsername(user.getUsername());
|
||||||
|
|
||||||
|
// 验证用户是否存在及密码是否正确
|
||||||
|
if (foundUser == null || !passwordEncoder.matches(user.getPassword(), foundUser.getPassword())) {
|
||||||
|
return ResponseEntity.badRequest().body("用户名或密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录成功后返回用户的基本信息(包括角色)
|
||||||
|
return ResponseEntity.ok(new UserLoginResponse(foundUser.getUsername(), foundUser.getRole()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有用户
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<?> getAllUsers() {
|
||||||
|
return ResponseEntity.ok(userRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
@PostMapping("update")
|
||||||
|
public ResponseEntity<?> updateUser(@RequestBody User user) {
|
||||||
|
System.out.println("=============="+user);
|
||||||
|
Long id = user.getId();
|
||||||
|
if (!userRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果传来的是密码,则进行加密
|
||||||
|
if (user.getPassword() != null) {
|
||||||
|
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setId(id);
|
||||||
|
userRepository.save(user);
|
||||||
|
return ResponseEntity.ok("用户更新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<?> deleteUser(@PathVariable long id) {
|
||||||
|
if (!userRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
userRepository.deleteById(id);
|
||||||
|
return ResponseEntity.ok("用户删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增接口:更新管理员信息
|
||||||
|
@PutMapping("/admin")
|
||||||
|
public ResponseEntity<?> updateAdmin(@RequestBody UserUpdateDto userUpdateDto) {
|
||||||
|
System.out.println("============" + userUpdateDto);
|
||||||
|
|
||||||
|
// 查找管理员,假设最多一个管理员
|
||||||
|
User admin = userRepository.findByRole("admin");
|
||||||
|
if (admin == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("管理员不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新允许修改的字段
|
||||||
|
if (userUpdateDto.getUsername() != null) admin.setUsername(userUpdateDto.getUsername());
|
||||||
|
if (userUpdateDto.getProfileImage() != null) admin.setProfileImage(userUpdateDto.getProfileImage());
|
||||||
|
if (userUpdateDto.getGender() != null) admin.setGender(userUpdateDto.getGender());
|
||||||
|
if (userUpdateDto.getPhone() != null) admin.setPhone(userUpdateDto.getPhone());
|
||||||
|
if (userUpdateDto.getEmail() != null) admin.setEmail(userUpdateDto.getEmail());
|
||||||
|
if (userUpdateDto.getBirthday() != null) admin.setBirthday(userUpdateDto.getBirthday());
|
||||||
|
if (userUpdateDto.getSignature() != null) admin.setSignature(userUpdateDto.getSignature());
|
||||||
|
if (userUpdateDto.getLocation() != null) admin.setLocation(userUpdateDto.getLocation());
|
||||||
|
|
||||||
|
// 如果需要更新密码
|
||||||
|
if (userUpdateDto.getPassword() != null && !userUpdateDto.getPassword().isEmpty()) {
|
||||||
|
String encryptedPassword = passwordEncoder.encode(userUpdateDto.getPassword());
|
||||||
|
admin.setPassword(encryptedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存更新后的管理员信息
|
||||||
|
userRepository.save(admin);
|
||||||
|
|
||||||
|
// 返回更新后的信息
|
||||||
|
return ResponseEntity.ok(admin); // 返回更新后的管理员信息
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.example.demo.dto;
|
||||||
|
|
||||||
|
public class UserLoginResponse {
|
||||||
|
private String username;
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
// 构造函数
|
||||||
|
public UserLoginResponse(String username, String role) {
|
||||||
|
this.username = username;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getters 和 setters
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.demo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.sql.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserUpdateDto {
|
||||||
|
private String username;
|
||||||
|
private String profileImage;
|
||||||
|
private String gender;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private Date birthday;
|
||||||
|
private String signature;
|
||||||
|
private String location;
|
||||||
|
private String password; // 可选,若需要修改密码
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.example.demo.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
public class Playlist {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id; // 歌单ID
|
||||||
|
|
||||||
|
private String name; // 歌单名称
|
||||||
|
private String description; // 歌单简介
|
||||||
|
private java.sql.Timestamp createTime; // 创建时间
|
||||||
|
private java.sql.Timestamp updateTime; // 更新时间
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.demo.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
public class Singer {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id; // 歌手ID
|
||||||
|
|
||||||
|
private String name; // 歌手名称
|
||||||
|
private String gender; // 性别
|
||||||
|
private java.sql.Date birthday; // 生日
|
||||||
|
private String location; // 地区
|
||||||
|
private String bio; // 歌手简介
|
||||||
|
private String profileImage; // 歌手图片
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.example.demo.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
public class Song {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id; // 歌曲ID
|
||||||
|
private String artist; // 歌手名称
|
||||||
|
private String name; // 歌曲名称
|
||||||
|
private String title; // 歌曲title
|
||||||
|
private String genre; // 歌曲类型
|
||||||
|
private String album; // 专辑名称
|
||||||
|
private String duration; // 歌曲时长
|
||||||
|
private String lyrics; // 歌词
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package com.example.demo.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
public class User {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String username;
|
||||||
|
private String role;
|
||||||
|
private String profileImage;
|
||||||
|
private String gender;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private java.sql.Date birthday;
|
||||||
|
private String signature;
|
||||||
|
private String location;
|
||||||
|
private String isMember;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setProfileImage(String profileImage) {
|
||||||
|
this.profileImage = profileImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setGender(String gender) {
|
||||||
|
this.gender = gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setBirthday(java.sql.Date birthday) {
|
||||||
|
this.birthday = birthday;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setSignature(String signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setLocation(String location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setIsMember(String isMember) {
|
||||||
|
this.isMember = isMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.example.demo.middle_class;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Playlist;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Singervo {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id; // 歌手ID
|
||||||
|
|
||||||
|
private String name; // 歌手名称
|
||||||
|
private String gender; // 性别
|
||||||
|
private java.sql.Date birthday; // 生日
|
||||||
|
private String location; // 地区
|
||||||
|
private String bio; // 歌手简介
|
||||||
|
private String profileImage; // 歌手图片
|
||||||
|
|
||||||
|
private List<Playlist> playlists;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.example.demo.repository;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Playlist;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
|
||||||
|
// 根据名称查询歌单
|
||||||
|
List<Playlist> findByNameContaining(String name);
|
||||||
|
|
||||||
|
// 根据更新时间排序(示例:按更新时间降序)
|
||||||
|
List<Playlist> findAllByOrderByUpdateTimeDesc();
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.demo.repository;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Singer;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SingerRepository extends JpaRepository<Singer, Long> {
|
||||||
|
// 可以定义一些自定义查询方法
|
||||||
|
List<Singer> findByNameContainingIgnoreCase(String name);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.example.demo.repository;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Song;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface SongRepository extends JpaRepository<Song, Long> {
|
||||||
|
// JpaRepository 提供了基本的增删改查方法
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.demo.repository;
|
||||||
|
|
||||||
|
import com.example.demo.entity.User;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
boolean existsByUsername(String username);
|
||||||
|
User findByUsername(String username);
|
||||||
|
User findByRole(String role); // 添加此方法
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.example.demo.service;
|
||||||
|
|
||||||
|
import com.example.demo.entity.Playlist;
|
||||||
|
import com.example.demo.repository.PlaylistRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PlaylistService {
|
||||||
|
@Autowired
|
||||||
|
private PlaylistRepository playlistRepository;
|
||||||
|
|
||||||
|
public List<Playlist> getAllPlaylists() {
|
||||||
|
return playlistRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Playlist addPlaylist(Playlist playlist) {
|
||||||
|
playlist.setCreateTime(new Timestamp(System.currentTimeMillis()));
|
||||||
|
playlist.setUpdateTime(new Timestamp(System.currentTimeMillis()));
|
||||||
|
return playlistRepository.save(playlist);
|
||||||
|
}
|
||||||
|
public Playlist updatePlaylist(Playlist playlist) {
|
||||||
|
// 确保歌单 ID 已经存在,进行更新
|
||||||
|
if (playlistRepository.existsById(playlist.getId())) {
|
||||||
|
return playlistRepository.save(playlist);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("歌单不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePlaylist(Long id) {
|
||||||
|
playlistRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void batchDeletePlaylists(List<Long> ids) {
|
||||||
|
playlistRepository.deleteAllById(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/music_admin?useUnicode=true&characterEncoding=utf-8&useSSL=false
|
||||||
|
spring.datasource.username=root
|
||||||
|
spring.datasource.password=123456
|
||||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
|
||||||
|
|
||||||
|
# MyBatis ??
|
||||||
|
mybatis.mapper-locations=classpath:mapper/**/*.xml
|
||||||
|
mybatis.type-aliases-package=com.example.demo.entity
|
||||||
|
|
||||||
|
server.port=8081
|
||||||
|
|
||||||
|
debug=true
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.demo;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class DemoApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|