diff --git a/babel.config.js b/babel.config.js
index 757ff9b..a7a5f24 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,5 +1,3 @@
-module.exports = {
- presets: [
- '@vue/cli-plugin-babel/preset',
- ],
-};
+export const presets = [
+ '@vue/cli-plugin-babel/preset',
+];
diff --git a/package.json b/package.json
index 3bc577f..ac00915 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"lint": "vite lint"
},
"dependencies": {
- "@types/node": "^20.2.5",
+ "@types/node": "^20.10.5",
"@vicons/fluent": "^0.12.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vueuse/core": "^10.1.2",
@@ -27,15 +27,15 @@
"mitt": "^3.0.1",
"qrcode.vue": "^3.4.1",
"queue-typescript": "^1.0.1",
- "universal-cookie": "^4.0.4",
- "vite": "^4.3.9",
- "vite-svg-loader": "^4.0.0",
- "vue": "^3.2.13",
- "vue-echarts": "^6.6.1",
+ "universal-cookie": "^6.1.1",
+ "vite": "^5.0.10",
+ "vite-svg-loader": "^5.1.0",
+ "vue": "^3.3.12",
+ "vue-echarts": "^6.6.5",
"vue-meta": "^3.0.0-alpha.10",
"vue-request": "^2.0.3",
"vue-router": "4",
- "vue-turnstile": "^1.0.0",
+ "vue-turnstile": "^1.0.6",
"vue3-aplayer": "^1.7.3",
"vue3-marquee": "^4.1.0",
"vueuc": "^0.4.51",
@@ -44,22 +44,24 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.21.3",
- "@typescript-eslint/eslint-plugin": "^5.4.0",
- "@typescript-eslint/parser": "^5.4.0",
+ "@typescript-eslint/eslint-plugin": "^6.14.0",
+ "@typescript-eslint/parser": "^6.14.0",
"@vicons/ionicons5": "^0.12.0",
- "@vue/eslint-config-airbnb": "^6.0.0",
- "@vue/eslint-config-typescript": "^9.1.0",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "@vue/cli-plugin-babel": "^5.0.8",
+ "@vue/eslint-config-airbnb": "^7.0.1",
+ "@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.39.0",
- "eslint-config-prettier": "^8.8.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.25.3",
- "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-vue": "^9.11.0",
- "eslint-plugin-vuejs-accessibility": "^1.1.0",
- "lint-staged": "^11.1.2",
+ "eslint-plugin-vuejs-accessibility": "^2.2.0",
+ "lint-staged": "^15.2.0",
"naive-ui": "^2.34.3",
- "prettier": "^2.8.8",
- "stylus": "^0.55.0",
- "typescript": "~4.5.5",
+ "prettier": "^3.1.1",
+ "stylus": "^0.62.0",
+ "typescript": "~5.3.3",
"vite-plugin-mkcert": "^1.16.0"
}
}
diff --git a/src/components/DynamicForm.vue b/src/components/DynamicForm.vue
new file mode 100644
index 0000000..5dc79f8
--- /dev/null
+++ b/src/components/DynamicForm.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/SongList.vue b/src/components/SongList.vue
index 542ad84..670d1d0 100644
--- a/src/components/SongList.vue
+++ b/src/components/SongList.vue
@@ -7,6 +7,7 @@ import { List } from 'linqts'
import {
DataTableBaseColumn,
DataTableColumns,
+ DataTableRowKey,
FormInst,
FormRules,
NAvatar,
@@ -30,6 +31,8 @@ import {
NPopconfirm,
NSelect,
NSpace,
+ NTabPane,
+ NTabs,
NTag,
NText,
NTooltip,
@@ -55,7 +58,7 @@ watch(
setTimeout(() => {
columns.value = createColumns()
}, 1)
- }
+ },
)
const volume = useLocalStorage('Settings.AplayerVolume', 0.8)
const songsInternal = ref(props.songs)
@@ -69,10 +72,16 @@ const songsComputed = computed(() => {
const message = useMessage()
const showModal = ref(false)
+const showBatchModal = ref(false)
const updateSongModel = ref({} as SongsInfo)
const searchMusicKeyword = ref()
const debouncedInput = refDebounced(searchMusicKeyword, 500)
+const batchUpdate_Author = ref([])
+const batchUpdate_Tag = ref([])
+const batchUpdate_Language = ref([])
+const batchUpdate_Option = ref()
+
const playingSong = ref()
const isLrcLoading = ref()
@@ -139,6 +148,7 @@ const authorsOptions = computed(() => {
})
const columns = ref>()
+const selectedColumn = ref([])
const authorColumn = ref>({
title: '作者',
key: 'artist',
@@ -163,6 +173,10 @@ const onAuthorClick = (author: string) => {
function createColumns(): DataTableColumns {
authorColumn.value.filterOptions = authorsOptions.value
return [
+ {
+ type: 'selection',
+ disabled: () => !props.isSelf,
+ },
{
key: 'name',
resizable: true,
@@ -258,7 +272,7 @@ function createColumns(): DataTableColumns {
},
{
icon: () => h(NIcon, { component: Play24Filled }),
- }
+ },
),
default: () => '试听',
})
@@ -280,7 +294,7 @@ function createColumns(): DataTableColumns {
},
{
icon: () => h(NIcon, { component: NotepadEdit20Filled }),
- }
+ },
),
default: () => '修改',
}),
@@ -302,17 +316,17 @@ function createColumns(): DataTableColumns {
},
{
icon: () => h(NIcon, { component: Delete24Filled }),
- }
+ },
),
default: () => '确认删除该歌曲?',
- }
+ },
),
default: () => '删除',
}),
]
: null,
props.extraButtom?.(data),
- ]
+ ],
)
},
},
@@ -337,8 +351,8 @@ function GetPlayButton(song: SongsInfo) {
},
{
icon: () => h(FiveSingIcon, { class: 'svg-icon fivesing' }),
- }
- )
+ },
+ ),
),
default: () => '在5sing打开',
})
@@ -358,7 +372,7 @@ function GetPlayButton(song: SongsInfo) {
},
{
icon: () => h(NeteaseIcon, { class: 'svg-icon netease' }),
- }
+ },
),
default: () => '在网易云打开',
})
@@ -378,7 +392,7 @@ function GetPlayButton(song: SongsInfo) {
},
{
icon: () => h(NIcon, { component: SquareArrowForward24Filled }),
- }
+ },
),
default: () => '打开链接',
})
@@ -445,6 +459,110 @@ function GetGuardColor(level: number | null | undefined): string {
}
return ''
}
+function batchUpdateAuthor() {
+ if (selectedColumn.value.length == 0) {
+ message.error('请先选择歌曲')
+ return
+ }
+ QueryPostAPI(SONG_API_URL + 'update-batch-author', {
+ ids: selectedColumn.value.map((s) => s.toString()),
+ data: batchUpdate_Author.value,
+ })
+ .then((data) => {
+ if (data.code == 200) {
+ message.success('已更新歌曲')
+ for (const song of songsInternal.value) {
+ if (selectedColumn.value.includes(song.key)) {
+ const index = songsInternal.value.findIndex((s) => s.key == song.key)
+ songsInternal.value[index].author = batchUpdate_Author.value
+ }
+ }
+ } else {
+ message.error('未能更新歌曲: ' + data.message)
+ }
+ })
+ .catch((err) => {
+ message.error('未能更新歌曲: ' + err)
+ })
+}
+function batchUpdateTag() {
+ if (selectedColumn.value.length == 0) {
+ message.error('请先选择歌曲')
+ return
+ }
+ QueryPostAPI(SONG_API_URL + 'update-batch-tag', {
+ ids: selectedColumn.value.map((s) => s.toString()),
+ data: batchUpdate_Tag.value,
+ })
+ .then((data) => {
+ if (data.code == 200) {
+ message.success('已更新歌曲')
+ for (const song of songsInternal.value) {
+ if (selectedColumn.value.includes(song.key)) {
+ const index = songsInternal.value.findIndex((s) => s.key == song.key)
+ songsInternal.value[index].tags = batchUpdate_Tag.value
+ }
+ }
+ } else {
+ message.error('未能更新歌曲: ' + data.message)
+ }
+ })
+ .catch((err) => {
+ message.error('未能更新歌曲: ' + err)
+ })
+}
+function batchUpdateLanguage() {
+ if (selectedColumn.value.length == 0) {
+ message.error('请先选择歌曲')
+ return
+ }
+ QueryPostAPI(SONG_API_URL + 'update-batch-language', {
+ ids: selectedColumn.value.map((s) => s.toString()),
+ data: batchUpdate_Language.value,
+ })
+ .then((data) => {
+ if (data.code == 200) {
+ message.success('已更新歌曲')
+ for (const song of songsInternal.value) {
+ if (selectedColumn.value.includes(song.key)) {
+ const index = songsInternal.value.findIndex((s) => s.key == song.key)
+ songsInternal.value[index].language = batchUpdate_Language.value
+ }
+ }
+ } else {
+ message.error('未能更新歌曲: ' + data.message)
+ }
+ })
+ .catch((err) => {
+ message.error('未能更新歌曲: ' + err)
+ })
+}
+function batchUpdateOption() {
+ if (selectedColumn.value.length == 0) {
+ message.error('请先选择歌曲')
+ return
+ }
+ QueryPostAPI(SONG_API_URL + 'update-batch-option', {
+ ids: selectedColumn.value.map((s) => s.toString()),
+ data: batchUpdate_Option.value ? batchUpdate_Option.value : null,
+ })
+ .then((data) => {
+ if (data.code == 200) {
+ message.success('已更新歌曲')
+ for (const song of songsInternal.value) {
+ if (selectedColumn.value.includes(song.key)) {
+ const index = songsInternal.value.findIndex((s) => s.key == song.key)
+ songsInternal.value[index].options = batchUpdate_Option.value
+ }
+ }
+ } else {
+ message.error('未能更新歌曲: ' + data.message)
+ }
+ })
+ .catch((err) => {
+ message.error('未能更新歌曲: ' + err)
+ })
+}
onMounted(() => {
songsInternal.value = props.songs
@@ -469,7 +587,10 @@ onMounted(() => {
+ 批量编辑
+
{
{updateSongModel.options = checked ? {
- needJianzhang: false,
- needTidu: false,
- needZongdu: false
- } as SongRequestOption : undefined}"
+ @update:checked="
+ (checked: boolean) => {
+ updateSongModel.options = checked
+ ? ({
+ needJianzhang: false,
+ needTidu: false,
+ needZongdu: false,
+ } as SongRequestOption)
+ : undefined
+ }
+ "
>
是否启用
@@ -523,7 +650,11 @@ onMounted(() => {
{if(updateSongModel.options) updateSongModel.options.scMinPrice = checked ? 30 : undefined}"
+ @update:checked="
+ (checked: boolean) => {
+ if (updateSongModel.options) updateSongModel.options.scMinPrice = checked ? 30 : undefined
+ }
+ "
>
需要SC
@@ -535,7 +666,11 @@ onMounted(() => {
{if(updateSongModel.options) updateSongModel.options.fanMedalMinLevel = checked ? 5 : undefined}"
+ @update:checked="
+ (checked: boolean) => {
+ if (updateSongModel.options) updateSongModel.options.fanMedalMinLevel = checked ? 5 : undefined
+ }
+ "
>
需要粉丝牌
@@ -560,6 +695,92 @@ onMounted(() => {
更新
+
+
+
+
+
+ 更新
+
+
+
+
+ 更新
+
+
+
+
+ 更新
+
+
+
+ {
+ batchUpdate_Option = checked
+ ? ({
+ needJianzhang: false,
+ needTidu: false,
+ needZongdu: false,
+ } as SongRequestOption)
+ : undefined
+ }
+ "
+ >
+ 是否启用
+
+
+
+ 需要舰长
+ 需要提督
+ 需要总督
+
+
+ {
+ if (batchUpdate_Option) batchUpdate_Option.scMinPrice = checked ? 30 : undefined
+ }
+ "
+ >
+ 需要SC
+
+
+ SC最低价格
+
+
+
+
+ {
+ if (batchUpdate_Option) batchUpdate_Option.fanMedalMinLevel = checked ? 5 : undefined
+ }
+ "
+ >
+ 需要粉丝牌
+
+
+
+
+ 这个即使不开也会遵循全局点歌设置的粉丝牌等级
+
+
+
+ 最低等级
+
+
+
+
+
+
+ 更新
+
+
+