新增单个列表导入/导出功能
This commit is contained in:
+135
-7
@@ -7,9 +7,13 @@
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='70%' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-list-add')
|
||||
ul.scroll(:class="$style.listsContent" ref="dom_lists_list")
|
||||
li(:class="[$style.listsItem, defaultList.id == listId ? $style.active : null]" :tips="defaultList.name" @click="handleListToggle(defaultList.id)")
|
||||
li(:class="[$style.listsItem, defaultList.id == listId ? $style.active : null]" :tips="defaultList.name"
|
||||
@contextmenu="handleListsItemRigthClick($event, -2)"
|
||||
@click="handleListToggle(defaultList.id)")
|
||||
span(:class="$style.listsLabel") {{defaultList.name}}
|
||||
li(:class="[$style.listsItem, loveList.id == listId ? $style.active : null]" :tips="loveList.name" @click="handleListToggle(loveList.id)")
|
||||
li(:class="[$style.listsItem, loveList.id == listId ? $style.active : null]" :tips="loveList.name"
|
||||
@contextmenu="handleListsItemRigthClick($event, -1)"
|
||||
@click="handleListToggle(loveList.id)")
|
||||
span(:class="$style.listsLabel") {{loveList.name}}
|
||||
li.user-list(
|
||||
:class="[$style.listsItem, item.id == listId ? $style.active : null, listsData.rightClickItemIndex == index ? $style.clicked : null, fetchingListStatus[item.id] ? $style.fetching : null]"
|
||||
@@ -74,7 +78,7 @@
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapGetters, mapActions } from 'vuex'
|
||||
import { throttle, scrollTo, clipboardWriteText, assertApiSupport, openUrl } from '../utils'
|
||||
import { throttle, scrollTo, clipboardWriteText, assertApiSupport, openUrl, openSaveDir, saveLxConfigFile, selectDir, readLxConfigFile, filterFileName } from '../utils'
|
||||
import musicSdk from '../utils/music'
|
||||
export default {
|
||||
name: 'List',
|
||||
@@ -105,6 +109,8 @@ export default {
|
||||
isShowItemMenu: false,
|
||||
itemMenuControl: {
|
||||
rename: true,
|
||||
import: true,
|
||||
export: true,
|
||||
sync: false,
|
||||
moveup: true,
|
||||
movedown: true,
|
||||
@@ -190,6 +196,16 @@ export default {
|
||||
action: 'rename',
|
||||
disabled: !this.listsData.itemMenuControl.rename,
|
||||
},
|
||||
{
|
||||
name: this.$t('view.list.lists_import'),
|
||||
action: 'import',
|
||||
disabled: !this.listsData.itemMenuControl.export,
|
||||
},
|
||||
{
|
||||
name: this.$t('view.list.lists_export'),
|
||||
action: 'export',
|
||||
disabled: !this.listsData.itemMenuControl.export,
|
||||
},
|
||||
{
|
||||
name: this.$t('view.list.lists_sync'),
|
||||
action: 'sync',
|
||||
@@ -722,10 +738,25 @@ export default {
|
||||
}).catch(_ => _)
|
||||
},
|
||||
handleListsItemRigthClick(event, index) {
|
||||
const source = this.userList[index].source
|
||||
this.listsData.itemMenuControl.sync = !!source && !!musicSdk[source].songList
|
||||
this.listsData.itemMenuControl.moveup = index > 0
|
||||
this.listsData.itemMenuControl.movedown = index < this.userList.length - 1
|
||||
let source
|
||||
switch (index) {
|
||||
case -1:
|
||||
case -2:
|
||||
this.listsData.itemMenuControl.rename = false
|
||||
this.listsData.itemMenuControl.remove = false
|
||||
this.listsData.itemMenuControl.sync = false
|
||||
this.listsData.itemMenuControl.moveup = false
|
||||
this.listsData.itemMenuControl.movedown = false
|
||||
break
|
||||
default:
|
||||
this.listsData.itemMenuControl.rename = true
|
||||
this.listsData.itemMenuControl.remove = true
|
||||
source = this.userList[index].source
|
||||
this.listsData.itemMenuControl.sync = !!source && !!musicSdk[source]?.songList
|
||||
this.listsData.itemMenuControl.moveup = index > 0
|
||||
this.listsData.itemMenuControl.movedown = index < this.userList.length - 1
|
||||
break
|
||||
}
|
||||
this.listsData.rightClickItemIndex = index
|
||||
this.listsData.menuLocation.x = event.currentTarget.offsetLeft + event.offsetX
|
||||
this.listsData.menuLocation.y = event.currentTarget.offsetTop + event.offsetY - this.$refs.dom_lists_list.scrollTop
|
||||
@@ -771,6 +802,12 @@ export default {
|
||||
dom.querySelector('input').focus()
|
||||
})
|
||||
break
|
||||
case 'import':
|
||||
this.handleImportList(index)
|
||||
break
|
||||
case 'export':
|
||||
this.handleExportList(index)
|
||||
break
|
||||
case 'sync':
|
||||
this.handleSyncSourceList(index)
|
||||
break
|
||||
@@ -946,6 +983,97 @@ export default {
|
||||
},
|
||||
})
|
||||
},
|
||||
handleExportList(index) {
|
||||
let list
|
||||
switch (index) {
|
||||
case -2:
|
||||
list = this.defaultList
|
||||
break
|
||||
case -1:
|
||||
list = this.loveList
|
||||
break
|
||||
default:
|
||||
list = this.userList[index]
|
||||
break
|
||||
}
|
||||
if (!list) return
|
||||
openSaveDir({
|
||||
title: this.$t('view.list.lists_export_part_desc'),
|
||||
defaultPath: `lx_list_part_${filterFileName(list.name)}.lxmc`,
|
||||
}).then(async result => {
|
||||
if (result.canceled) return
|
||||
const data = JSON.parse(JSON.stringify({
|
||||
type: 'playListPart',
|
||||
data: list,
|
||||
}))
|
||||
for await (const item of data.data.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
if (item.lrc) delete item.lrc
|
||||
}
|
||||
saveLxConfigFile(result.filePath, data)
|
||||
})
|
||||
},
|
||||
handleImportList(index) {
|
||||
let list
|
||||
switch (index) {
|
||||
case -2:
|
||||
list = this.defaultList
|
||||
break
|
||||
case -1:
|
||||
list = this.loveList
|
||||
break
|
||||
default:
|
||||
list = this.userList[index]
|
||||
break
|
||||
}
|
||||
if (!list) return
|
||||
|
||||
selectDir({
|
||||
title: this.$t('view.list.lists_import_part_desc'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Play List Part', extensions: ['json', 'lxmc'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
}).then(async result => {
|
||||
if (result.canceled) return
|
||||
let listData
|
||||
try {
|
||||
listData = JSON.parse(await readLxConfigFile(result.filePaths[0]))
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
if (listData.type !== 'playListPart') return
|
||||
const targetList = this.lists.find(l => l.id == listData.data.id)
|
||||
if (targetList) {
|
||||
const confirm = await this.$dialog.confirm({
|
||||
message: this.$t('view.list.lists_import_part_confirm', { importName: listData.data.name, localName: targetList.name }),
|
||||
cancelButtonText: this.$t('view.list.lists_import_part_button_cancel'),
|
||||
confirmButtonText: this.$t('view.list.lists_import_part_button_confirm'),
|
||||
})
|
||||
if (confirm) {
|
||||
listData.data.name = list.name
|
||||
this.setList({
|
||||
name: listData.data.name,
|
||||
id: listData.data.id,
|
||||
list: listData.data.list,
|
||||
source: listData.data.source,
|
||||
sourceListId: listData.data.sourceListId,
|
||||
})
|
||||
return
|
||||
}
|
||||
listData.data.id += `__${Date.now()}`
|
||||
}
|
||||
this.createUserList({
|
||||
name: listData.data.name,
|
||||
id: listData.data.id,
|
||||
list: listData.data.list,
|
||||
source: listData.data.source,
|
||||
sourceListId: listData.data.sourceListId,
|
||||
position: Math.max(index, -1),
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -329,16 +329,16 @@ import {
|
||||
setWindowSize,
|
||||
getSetting,
|
||||
saveSetting,
|
||||
saveLxConfigFile,
|
||||
readLxConfigFile,
|
||||
} from '../utils'
|
||||
import { rendererSend, rendererInvoke, rendererOn, NAMES, rendererOff } from '@common/ipc'
|
||||
import { mergeSetting, isMac } from '../../common/utils'
|
||||
import apiSourceInfo from '../utils/music/api-source-info'
|
||||
import fs from 'fs'
|
||||
import languageList from '@renderer/lang/languages.json'
|
||||
import { base as eventBaseName } from '../event/names'
|
||||
import * as hotKeys from '../../common/hotKey'
|
||||
import { mainWindow as eventsNameMainWindow, winLyric as eventsNameWinLyric } from '../../main/events/_name'
|
||||
import { gzip, gunzip } from 'zlib'
|
||||
import music from '../utils/music'
|
||||
|
||||
let hotKeyTargetInput
|
||||
@@ -815,7 +815,7 @@ export default {
|
||||
async importSetting(path) {
|
||||
let settingData
|
||||
try {
|
||||
settingData = JSON.parse(await this.handleReadFile(path))
|
||||
settingData = JSON.parse(await readLxConfigFile(path))
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
@@ -830,12 +830,12 @@ export default {
|
||||
type: 'setting',
|
||||
data: Object.assign({ version: this.settingVersion }, this.setting),
|
||||
}
|
||||
this.handleSaveFile(path, JSON.stringify(data))
|
||||
saveLxConfigFile(path, JSON.stringify(data))
|
||||
},
|
||||
async importPlayList(path) {
|
||||
let listData
|
||||
try {
|
||||
listData = JSON.parse(await this.handleReadFile(path))
|
||||
listData = JSON.parse(await readLxConfigFile(path))
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
@@ -867,12 +867,12 @@ export default {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
this.handleSaveFile(path, JSON.stringify(data))
|
||||
saveLxConfigFile(path, JSON.stringify(data))
|
||||
},
|
||||
async importAllData(path) {
|
||||
let allData
|
||||
try {
|
||||
allData = JSON.parse(await this.handleReadFile(path))
|
||||
allData = JSON.parse(await readLxConfigFile(path))
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
@@ -906,7 +906,7 @@ export default {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
this.handleSaveFile(path, JSON.stringify(allData))
|
||||
saveLxConfigFile(path, JSON.stringify(allData))
|
||||
},
|
||||
handleImportAllData() {
|
||||
selectDir({
|
||||
@@ -1198,35 +1198,6 @@ export default {
|
||||
handleTrayShowChange(isShow) {
|
||||
this.current_setting.tray.isToTray = isShow
|
||||
},
|
||||
async handleSaveFile(path, data) {
|
||||
if (!path.endsWith('.lxmc')) path += '.lxmc'
|
||||
fs.writeFile(path, await this.gzip(data), 'binary', err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
async handleReadFile(path) {
|
||||
let isJSON = path.endsWith('.json')
|
||||
let data = await fs.promises.readFile(path, isJSON ? 'utf8' : 'binary')
|
||||
if (!data || isJSON) return data
|
||||
data = await this.gunzip(Buffer.from(data, 'binary'))
|
||||
return data.toString('utf8')
|
||||
},
|
||||
gzip(str) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gzip(str, (err, result) => {
|
||||
if (err) return reject(err)
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
},
|
||||
gunzip(buf) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gunzip(buf, (err, result) => {
|
||||
if (err) return reject(err)
|
||||
resolve(result.toString())
|
||||
})
|
||||
})
|
||||
},
|
||||
getApiStatus() {
|
||||
let status
|
||||
if (window.globalObj.userApi.status) status = this.$t('view.setting.basic_source_status_success')
|
||||
|
||||
Reference in New Issue
Block a user