升级到 vue3
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<template lang="pug">
|
||||
dt#about {{$t('setting__about')}}
|
||||
dd
|
||||
p.small
|
||||
| 本软件完全免费,代码已开源,开源地址:
|
||||
span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop#readme')") https://github.com/lyswhut/lx-music-desktop
|
||||
p.small
|
||||
| 最新版网盘下载地址(网盘内有Windows、MAC版):
|
||||
span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://www.lanzoui.com/b0bf2cfa/')") 网盘地址
|
||||
| 密码:
|
||||
span.hover(:tips="$t('setting__click_copy')" @click="clipboardWriteText('glqw')") glqw
|
||||
p.small
|
||||
| 软件的常见问题可转至:
|
||||
span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md')") 常见问题
|
||||
p.small
|
||||
strong 本软件没有客服
|
||||
| ,但我们整理了一些常见的使用问题,
|
||||
strong 仔细 仔细 仔细
|
||||
| 地阅读常见问题后,
|
||||
p.small
|
||||
| 仍有问题可加企鹅群
|
||||
span.hover(:tips="$t('setting__click_open')" @click="openUrl('https://jq.qq.com/?_wv=1027&k=51ECeq2')") 830125506
|
||||
| 反馈
|
||||
strong (为免满人,无事勿加,入群先看群公告)
|
||||
| ,或到 GitHub 提交
|
||||
span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/issues')") issue
|
||||
|
||||
br
|
||||
p.small
|
||||
| 如果你喜欢并经常使用洛雪音乐,并想要第一时间尝鲜洛雪的新功能
|
||||
span(style="text-decoration: line-through;") (当小白鼠)
|
||||
| ,
|
||||
p
|
||||
| 可以加入测试企鹅群
|
||||
span.hover(:tips="$t('setting__click_open')" @click="openUrl('https://qm.qq.com/cgi-bin/qm/qr?k=zR6aYosQoKb07g4FGFZdO9n9zL1dhFpE&jump_from=webapi')") 768786588
|
||||
| ,注意:测试版的功可能会不稳定,
|
||||
strong 打算潜水的勿加
|
||||
|
||||
br
|
||||
p.small 由于软件开发的初衷仅是为了对新技术的学习与研究,因此软件直至停止维护都将会一直保持纯净。
|
||||
|
||||
p.small
|
||||
| 你已签署本软件的
|
||||
base-btn(min @click="handleShowPact") 许可协议
|
||||
| ,协议的在线版本在
|
||||
strong.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop#%E9%A1%B9%E7%9B%AE%E5%8D%8F%E8%AE%AE')") 这里
|
||||
| 。
|
||||
br
|
||||
|
||||
p
|
||||
small By:
|
||||
| 落雪无痕
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { ref, onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { isShowPact } from '@renderer/core/share'
|
||||
import { openUrl, clipboardWriteText } from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'About',
|
||||
setup() {
|
||||
const handleShowPact = () => {
|
||||
isShowPact.value = true
|
||||
}
|
||||
return {
|
||||
currentStting,
|
||||
openUrl,
|
||||
clipboardWriteText,
|
||||
handleShowPact,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,243 @@
|
||||
<template lang="pug">
|
||||
dt#backup {{$t('setting__backup')}}
|
||||
dd
|
||||
h3#backup_part {{$t('setting__backup_part')}}
|
||||
div
|
||||
base-btn.btn.gap-left(min @click="handleImportPlayList") {{$t('setting__backup_part_import_list')}}
|
||||
base-btn.btn.gap-left(min @click="handleExportPlayList") {{$t('setting__backup_part_export_list')}}
|
||||
base-btn.btn.gap-left(min @click="handleImportSetting") {{$t('setting__backup_part_import_setting')}}
|
||||
base-btn.btn.gap-left(min @click="handleExportSetting") {{$t('setting__backup_part_export_setting')}}
|
||||
dd
|
||||
h3#backup_all {{$t('setting__backup_all')}}
|
||||
div
|
||||
base-btn.btn.gap-left(min @click="handleImportAllData") {{$t('setting__backup_all_import')}}
|
||||
base-btn.btn.gap-left(min @click="handleExportAllData") {{$t('setting__backup_all_export')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useCommit, useI18n, onBeforeUnmount, useGetter, toRaw } from '@renderer/utils/vueTools'
|
||||
import { mergeSetting } from '@common/utils'
|
||||
import { base as eventBaseName } from '@renderer/event/names'
|
||||
import { defaultList, loveList, userLists } from '@renderer/core/share/list'
|
||||
import {
|
||||
getSetting,
|
||||
saveSetting,
|
||||
saveLxConfigFile,
|
||||
readLxConfigFile,
|
||||
selectDir,
|
||||
openSaveDir,
|
||||
} from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
import { getList } from '@renderer/core/share/utils'
|
||||
|
||||
export default {
|
||||
name: 'Update',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
const setting = useGetter('setting')
|
||||
const settingVersion = useGetter('settingVersion')
|
||||
const setSetting = useCommit('setSetting')
|
||||
const setSettingVersion = useCommit('setSettingVersion')
|
||||
const setList = useCommit('list', 'setList')
|
||||
|
||||
const handleUpdateSetting = (config) => {
|
||||
currentStting.value = JSON.parse(JSON.stringify(config))
|
||||
}
|
||||
const refreshSetting = async(newSetting, newVersion) => {
|
||||
await saveSetting(newSetting)
|
||||
const { setting, version } = await getSetting()
|
||||
setSetting(setting)
|
||||
setSettingVersion(version)
|
||||
}
|
||||
|
||||
const importAllData = async(path) => {
|
||||
let allData
|
||||
try {
|
||||
allData = await readLxConfigFile(path)
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
if (allData.type !== 'allData') return
|
||||
|
||||
// 兼容0.6.2及以前版本的列表数据
|
||||
if (allData.defaultList) return setList({ id: 'default', list: allData.defaultList.list, name: '试听列表' })
|
||||
|
||||
for (const list of allData.playList) {
|
||||
setList(list)
|
||||
}
|
||||
|
||||
const { version: settingVersion, setting } = mergeSetting(allData.setting)
|
||||
setting.isAgreePact = false
|
||||
|
||||
await refreshSetting(setting, settingVersion)
|
||||
}
|
||||
const handleImportAllData = () => {
|
||||
selectDir({
|
||||
title: t('setting__backup_all_import_desc'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Setting', extensions: ['json', 'lxmc'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
importAllData(result.filePaths[0])
|
||||
})
|
||||
}
|
||||
|
||||
const exportAllData = async(path) => {
|
||||
let allData = JSON.parse(JSON.stringify({
|
||||
type: 'allData',
|
||||
setting: Object.assign({ version: settingVersion.value }, toRaw(setting.value)),
|
||||
playList: [
|
||||
{ ...toRaw(defaultList), list: toRaw(getList(defaultList.id)) },
|
||||
{ ...toRaw(loveList), list: toRaw(getList(loveList.id)) },
|
||||
...userLists.map(l => ({ ...toRaw(l), list: toRaw(getList(l.id)) })),
|
||||
],
|
||||
}))
|
||||
for await (const list of allData.playList) {
|
||||
for await (const item of list.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
saveLxConfigFile(path, allData)
|
||||
}
|
||||
const handleExportAllData = () => {
|
||||
openSaveDir({
|
||||
title: t('setting__backup_all_export_desc'),
|
||||
defaultPath: 'lx_datas.lxmc',
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
exportAllData(result.filePath)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const exportSetting = (path) => {
|
||||
console.log(path)
|
||||
const data = {
|
||||
type: 'setting',
|
||||
data: Object.assign({ version: settingVersion.value }, toRaw(setting.value)),
|
||||
}
|
||||
saveLxConfigFile(path, data)
|
||||
}
|
||||
const handleExportSetting = () => {
|
||||
openSaveDir({
|
||||
title: t('setting__backup_part_export_setting_desc'),
|
||||
defaultPath: 'lx_setting.lxmc',
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
exportSetting(result.filePath)
|
||||
})
|
||||
}
|
||||
|
||||
const importSetting = async(path) => {
|
||||
let settingData
|
||||
try {
|
||||
settingData = await readLxConfigFile(path)
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
if (settingData.type !== 'setting') return
|
||||
const { version: settingVersion, setting } = mergeSetting(settingData.data)
|
||||
setting.isAgreePact = false
|
||||
refreshSetting(setting, settingVersion)
|
||||
}
|
||||
const handleImportSetting = () => {
|
||||
selectDir({
|
||||
title: t('setting__backup_part_import_setting_desc'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Setting', extensions: ['json', 'lxmc'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
importSetting(result.filePaths[0])
|
||||
})
|
||||
}
|
||||
|
||||
const exportPlayList = async(path) => {
|
||||
const data = JSON.parse(JSON.stringify({
|
||||
type: 'playList',
|
||||
data: [
|
||||
{ ...toRaw(defaultList), list: toRaw(getList(defaultList.id)) },
|
||||
{ ...toRaw(loveList), list: toRaw(getList(loveList.id)) },
|
||||
...userLists.map(l => ({ ...toRaw(l), list: toRaw(getList(l.id)) })),
|
||||
],
|
||||
}))
|
||||
for await (const list of data.data) {
|
||||
for await (const item of list.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
saveLxConfigFile(path, data)
|
||||
}
|
||||
const handleExportPlayList = () => {
|
||||
openSaveDir({
|
||||
title: t('setting__backup_part_export_list_desc'),
|
||||
defaultPath: 'lx_list.lxmc',
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
exportPlayList(result.filePath)
|
||||
})
|
||||
}
|
||||
|
||||
const importPlayList = async(path) => {
|
||||
let listData
|
||||
try {
|
||||
listData = await readLxConfigFile(path)
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
console.log(listData.type)
|
||||
|
||||
// 兼容0.6.2及以前版本的列表数据
|
||||
if (listData.type === 'defautlList') return setList({ id: 'default', list: listData.data.list, name: '试听列表' })
|
||||
|
||||
if (listData.type !== 'playList') return
|
||||
|
||||
for (const list of listData.data) {
|
||||
setList(list)
|
||||
}
|
||||
|
||||
await refreshSetting(setting.value, settingVersion.value)
|
||||
}
|
||||
const handleImportPlayList = () => {
|
||||
selectDir({
|
||||
title: t('setting__backup_part_import_list_desc'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Play List', extensions: ['json', 'lxmc'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
importPlayList(result.filePaths[0])
|
||||
})
|
||||
}
|
||||
|
||||
window.eventHub.on(eventBaseName.set_config, handleUpdateSetting)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off(eventBaseName.set_config, handleUpdateSetting)
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
handleExportPlayList,
|
||||
handleImportPlayList,
|
||||
handleExportSetting,
|
||||
handleImportSetting,
|
||||
handleExportAllData,
|
||||
handleImportAllData,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
.save-path {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,235 @@
|
||||
<template lang="pug">
|
||||
dt#basic {{$t('setting__basic')}}
|
||||
dd
|
||||
h3#basic_theme {{$t('setting__basic_theme')}}
|
||||
div
|
||||
ul(:class="$style.theme")
|
||||
li(v-for="theme in themes.list" :key="theme.id" :tips="$t('theme_' + theme.className)" @click="currentStting.themeId = theme.id" :class="[theme.className, {[$style.active]: themes.active == theme.id}]")
|
||||
span
|
||||
label {{$t('theme_' + theme.className)}}
|
||||
|
||||
dd
|
||||
.gap-top.top
|
||||
base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
|
||||
|
||||
dd(:tips="$t('setting__basic_source_title')")
|
||||
h3#basic_source {{$t('setting__basic_source')}}
|
||||
div
|
||||
.gap-top(v-for="item in apiSources" :key="item.id")
|
||||
base-checkbox(:id="`setting_api_source_${item.id}`" name="setting_api_source"
|
||||
need v-model="currentStting.apiSource" :disabled="item.disabled" :value="item.id" :label="item.label")
|
||||
p.gap-top
|
||||
base-btn.btn(min @click="isShowUserApiModal = true") {{$t('setting__basic_source_user_api_btn')}}
|
||||
|
||||
dd(:tips="$t('setting__basic_window_size_title')")
|
||||
h3#basic_window_size {{$t('setting__basic_window_size')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="(item, index) in windowSizeList" :id="`setting_window_size_${item.id}`" name="setting_window_size"
|
||||
need v-model="currentStting.windowSizeId" :value="item.id" :label="$t('setting__basic_window_size_' + item.name)" :key="item.id")
|
||||
|
||||
dd(:tips="$t('setting__basic_lang_title')")
|
||||
h3#basic_lang {{$t('setting__basic_lang')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="item in langList" :key="item.locale" :id="`setting_lang_${item.locale}`" name="setting_lang"
|
||||
need v-model="currentStting.langId" :value="item.locale" :label="item.name")
|
||||
|
||||
dd(:tips="$t('setting__basic_sourcename_title')")
|
||||
h3#basic_sourcename {{$t('setting__basic_sourcename')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="item in sourceNameTypes" :key="item.id" :id="`setting_abasic_sourcename_${item.id}`"
|
||||
name="setting_basic_sourcename" need v-model="currentStting.sourceNameType" :value="item.id" :label="item.label")
|
||||
|
||||
dd
|
||||
h3#basic_control_btn_position {{$t('setting__basic_control_btn_position')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="item in controlBtnPositionList" :key="item.id" :id="`setting_basic_control_btn_position_${item.id}`"
|
||||
name="setting_basic_control_btn_position" need v-model="currentStting.controlBtnPosition" :value="item.id" :label="item.name")
|
||||
teleport(to="#view")
|
||||
user-api-modal(v-model="isShowUserApiModal")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, ref, useI18n, watch } from '@renderer/utils/vueTools'
|
||||
import { themes as themeList, windowSizeList, apiSource, userApi } from '@renderer/core/share'
|
||||
import { langList } from '@/lang'
|
||||
import { currentStting } from '../setting'
|
||||
import { setWindowSize } from '@renderer/utils'
|
||||
import apiSourceInfo from '@renderer/utils/music/api-source-info'
|
||||
|
||||
import UserApiModal from './UserApiModal'
|
||||
|
||||
export default {
|
||||
name: 'Basic',
|
||||
components: {
|
||||
UserApiModal,
|
||||
},
|
||||
setup() {
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
|
||||
const themes = computed(() => {
|
||||
return {
|
||||
active: currentStting.value.themeId,
|
||||
list: themeList,
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => currentStting.value.apiSource, visible => {
|
||||
apiSource.value = visible
|
||||
})
|
||||
|
||||
const isShowUserApiModal = ref(false)
|
||||
const getApiStatus = () => {
|
||||
let status
|
||||
if (userApi.status) status = t('setting__basic_source_status_success')
|
||||
else if (userApi.message == 'initing') status = t('setting__basic_source_status_initing')
|
||||
else status = `${t('setting__basic_source_status_failed')} - ${userApi.message}`
|
||||
|
||||
return status
|
||||
}
|
||||
const apiSources = computed(() => {
|
||||
return [
|
||||
...apiSourceInfo.map(api => ({
|
||||
id: api.id,
|
||||
label: t('setting__basic_source_' + api.id) || api.name,
|
||||
disabled: api.disabled,
|
||||
})),
|
||||
...userApi.list.map(api => ({
|
||||
id: api.id,
|
||||
label: `${api.name}${api.description ? `(${api.description})` : ''}${api.id == currentStting.value.apiSource ? `[${getApiStatus()}]` : ''}`,
|
||||
status: api.status,
|
||||
message: api.message,
|
||||
disabled: false,
|
||||
})),
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
watch(() => currentStting.value.windowSizeId, (index) => {
|
||||
let info = index == null ? windowSizeList[2] : windowSizeList[index]
|
||||
setWindowSize(info.width, info.height)
|
||||
})
|
||||
|
||||
|
||||
watch(() => currentStting.value.langId, (id) => {
|
||||
locale.value = id
|
||||
})
|
||||
|
||||
|
||||
const sourceNameTypes = computed(() => {
|
||||
return [
|
||||
{ id: 'real', label: t('setting__basic_sourcename_real') },
|
||||
{ id: 'alias', label: t('setting__basic_sourcename_alias') },
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
const controlBtnPositionList = computed(() => {
|
||||
return [
|
||||
{ id: 'left', name: t('setting__basic_control_btn_position_left') },
|
||||
{ id: 'right', name: t('setting__basic_control_btn_position_right') },
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
themes,
|
||||
apiSources,
|
||||
isShowUserApiModal,
|
||||
windowSizeList,
|
||||
langList,
|
||||
sourceNameTypes,
|
||||
controlBtnPositionList,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.theme {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
// padding: 0 15px;
|
||||
margin-bottom: -20px;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
// color: @color-theme;
|
||||
margin-right: 30px;
|
||||
transition: color .3s ease;
|
||||
margin-bottom: 18px;
|
||||
width: 56px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-bottom: 5px;
|
||||
border: 2px solid transparent;
|
||||
padding: 2px;
|
||||
transition: border-color .3s ease;
|
||||
border-radius: 5px;
|
||||
&:after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: @radius-border;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
height: 1.2em;
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
&:global(.@{value}) {
|
||||
span {
|
||||
&:after {
|
||||
background-color: ~'@{color-@{value}-theme}';
|
||||
background-image: ~'@{color-@{value}-theme-bgimg}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#root.@{value}) {
|
||||
.theme {
|
||||
li {
|
||||
&.active {
|
||||
&:global(.@{value}) {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
span {
|
||||
border-color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template lang="pug">
|
||||
dt#desktop_lyric {{$t('setting__desktop_lyric')}}
|
||||
dd
|
||||
.gap-top
|
||||
base-checkbox(id="setting_desktop_lyric_enable" v-model="currentStting.desktopLyric.enable" :label="$t('setting__desktop_lyric_enable')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_desktop_lyric_lock" v-model="currentStting.desktopLyric.isLock" :label="$t('setting__desktop_lyric_lock')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_desktop_lyric_alwaysOnTop" v-model="currentStting.desktopLyric.isAlwaysOnTop" :label="$t('setting__desktop_lyric_always_on_top')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_desktop_lyric_lockScreen" v-model="currentStting.desktopLyric.isLockScreen" :label="$t('setting__desktop_lyric_lock_screen')")
|
||||
dd
|
||||
h3#desktop_lyric_font {{$t('setting__desktop_lyric_font')}}
|
||||
div
|
||||
base-selection.gap-teft(:list="fontList" v-model="currentStting.desktopLyric.style.font" item-key="id" item-name="label")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, useI18n } from '@renderer/utils/vueTools'
|
||||
import { getSystemFonts } from '@renderer/utils/tools'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'DesktopLyric',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
const systemFontList = ref([])
|
||||
const fontList = computed(() => {
|
||||
return [{ id: '', label: t('setting__desktop_lyric_font_default') }, ...systemFontList.value]
|
||||
})
|
||||
getSystemFonts().then(fonts => {
|
||||
systemFontList.value = fonts.map(f => ({ id: f, label: f.replace(/(^"|"$)/g, '') }))
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
fontList,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template lang="pug">
|
||||
dt#download {{$t('setting__download')}}
|
||||
dd
|
||||
base-checkbox(id="setting_download_enable" v-model="currentStting.download.enable" :label="$t('setting__download_enable')")
|
||||
dd(:tips="$t('setting__download_path_title')")
|
||||
h3#download_path {{$t('setting__download_path')}}
|
||||
div
|
||||
p
|
||||
| {{$t('setting__download_path_label')}}
|
||||
span.auto-hidden.hover(:class="$style.savePath" @click="openDirInExplorer(currentStting.download.savePath)" :tips="$t('setting__download_path_open_label')") {{currentStting.download.savePath}}
|
||||
p
|
||||
base-btn.btn(min @click="handleChangeSavePath") {{$t('setting__download_path_change_btn')}}
|
||||
dd
|
||||
h3#download_use_other_source {{$t('setting__download_use_other_source')}}
|
||||
div
|
||||
base-checkbox(id="setting_download_isUseOtherSource" v-model="currentStting.download.isUseOtherSource" :label="$t('setting__is_enable')")
|
||||
div
|
||||
dd(:tips="$t('setting__download_name_title')")
|
||||
h3#download_name {{$t('setting__download_name')}}
|
||||
div
|
||||
base-checkbox.gap-left(:id="`setting_download_musicName_${item.value}`" name="setting_download_musicName" :value="item.value" :key="item.value" need
|
||||
v-model="currentStting.download.fileName" v-for="item in musicNames" :label="item.name")
|
||||
dd
|
||||
h3#download_data_embed {{$t('setting__download_data_embed')}}
|
||||
.gap-top
|
||||
base-checkbox(id="setting_download_isEmbedPic" v-model="currentStting.download.isEmbedPic" :label="$t('setting__download_embed_pic')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_download_isEmbedLyric" v-model="currentStting.download.isEmbedLyric" :label="$t('setting__download_embed_lyric')")
|
||||
dd(:tips="$t('setting__download_lyric_title')")
|
||||
h3#download_lyric {{$t('setting__download_lyric')}}
|
||||
div
|
||||
base-checkbox(id="setting_download_isDownloadLrc" v-model="currentStting.download.isDownloadLrc" :label="$t('setting__is_enable')")
|
||||
dd
|
||||
h3#download_lyric {{$t('setting__download_lyric_format')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="item in lrcFormatList" :key="item.id" :id="`setting_download_lrcFormat_${item.id}`"
|
||||
name="setting_download_lrcFormat" need v-model="currentStting.download.lrcFormat" :value="item.id" :label="item.name")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, useI18n } from '@renderer/utils/vueTools'
|
||||
// import { getSystemFonts } from '@renderer/utils/tools'
|
||||
import { openDirInExplorer, selectDir } from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Download',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
|
||||
const handleChangeSavePath = () => {
|
||||
selectDir({
|
||||
title: t('setting__download_select_save_path'),
|
||||
defaultPath: currentStting.value.download.savePath,
|
||||
properties: ['openDirectory'],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
currentStting.value.download.savePath = result.filePaths[0]
|
||||
})
|
||||
}
|
||||
|
||||
const musicNames = computed(() => {
|
||||
return [
|
||||
{ value: '歌名 - 歌手', name: t('setting__download_name1') },
|
||||
{ value: '歌手 - 歌名', name: t('setting__download_name2') },
|
||||
{ value: '歌名', name: t('setting__download_name3') },
|
||||
]
|
||||
})
|
||||
|
||||
const lrcFormatList = computed(() => {
|
||||
return [
|
||||
{ id: 'utf8', name: t('setting__download_lyric_format_utf8') },
|
||||
{ id: 'gbk', name: t('setting__download_lyric_format_gbk') },
|
||||
]
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
openDirInExplorer,
|
||||
handleChangeSavePath,
|
||||
musicNames,
|
||||
lrcFormatList,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
.save-path {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,314 @@
|
||||
<template lang="pug">
|
||||
dt#hot_key {{$t('setting__hot_key')}}
|
||||
dd
|
||||
h3#hot_key_local_title {{$t('setting__hot_key_local_title')}}
|
||||
div
|
||||
base-checkbox(id="setting_download_hotKeyLocal" v-model="current_hot_key.local.enable" :label="$t('setting__is_enable')" @change="handleHotKeySaveConfig")
|
||||
div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.local.enable ? 1 : .6 }")
|
||||
div(:class="$style.hotKeyItem" v-for="item in allHotKeys.local")
|
||||
h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}}
|
||||
base-input.key-bind(:class="$style.hotKeyItemInput" readonly @keyup.prevent :placeholder="$t('setting__hot_key_unset_input')"
|
||||
:value="hotKeyConfig.local[item.name] && formatHotKeyName(hotKeyConfig.local[item.name].key)"
|
||||
@focus="handleHotKeyFocus($event, item, 'local')"
|
||||
@blur="handleHotKeyBlur($event, item, 'local')")
|
||||
|
||||
h3#hot_key_global_title {{$t('setting__hot_key_global_title')}}
|
||||
div
|
||||
base-checkbox(id="setting_download_hotKeyGlobal" v-model="current_hot_key.global.enable" :label="$t('setting__is_enable')" @change="handleEnableHotKey")
|
||||
div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.global.enable ? 1 : .6 }")
|
||||
div(:class="$style.hotKeyItem" v-for="item in allHotKeys.global")
|
||||
h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}}
|
||||
base-input.key-bind(:class="[$style.hotKeyItemInput, hotKeyConfig.global[item.name] && hotKeyStatus[hotKeyConfig.global[item.name].key] && hotKeyStatus[hotKeyConfig.global[item.name].key].status === false ? $style.hotKeyFailed : null]"
|
||||
:value="hotKeyConfig.global[item.name] && formatHotKeyName(hotKeyConfig.global[item.name].key)" @input.prevent readonly :placeholder="$t('setting__hot_key_unset_input')"
|
||||
@focus="handleHotKeyFocus($event, item, 'global')"
|
||||
@blur="handleHotKeyBlur($event, item, 'global')")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, useI18n, onBeforeUnmount, toRaw } from '@renderer/utils/vueTools'
|
||||
import { allHotKeys, hotKeySetEnable, hotKeySetConfig, hotKeyGetStatus } from '@renderer/utils/tools'
|
||||
import { isMac } from '@common/utils'
|
||||
import { base as eventBaseName } from '@renderer/event/names'
|
||||
import { mainWindow as eventsNameMainWindow } from '@main/events/_name'
|
||||
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
const formatHotKeyName = (name) => {
|
||||
if (name.includes('arrow')) {
|
||||
name = name.replace(/arrow(left|right|up|down)/, s => {
|
||||
switch (s) {
|
||||
case 'arrowleft': return '←'
|
||||
case 'arrowright': return '→'
|
||||
case 'arrowup': return '↑'
|
||||
case 'arrowdown': return '↓'
|
||||
}
|
||||
})
|
||||
}
|
||||
if (name.includes('mod')) name = name.replace('mod', isMac ? 'Command' : 'Ctrl')
|
||||
name = name.replace(/(\+|^)[a-z]/g, l => l.toUpperCase())
|
||||
if (name.length > 1) name = name.replace(/\+/g, ' + ')
|
||||
return name
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'HotKey',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
const current_hot_key = ref({
|
||||
local: {
|
||||
enable: false,
|
||||
keys: {},
|
||||
},
|
||||
global: {
|
||||
enable: false,
|
||||
keys: {},
|
||||
},
|
||||
})
|
||||
|
||||
const hotKeyConfig = ref({
|
||||
local: {},
|
||||
global: {},
|
||||
})
|
||||
|
||||
const hotKeyStatus = ref({})
|
||||
let isEditHotKey = false
|
||||
let hotKeyTargetInput
|
||||
let newHotKey
|
||||
|
||||
const initHotKeyConfig = () => {
|
||||
let config = {}
|
||||
for (const [type, typeInfo] of Object.entries(current_hot_key.value)) {
|
||||
let configInfo = config[type] = {}
|
||||
for (const [key, info] of Object.entries(typeInfo.keys)) {
|
||||
if (info.name == null) continue
|
||||
configInfo[info.name] = {
|
||||
key,
|
||||
info,
|
||||
}
|
||||
}
|
||||
}
|
||||
hotKeyConfig.value = config
|
||||
}
|
||||
|
||||
const handleHotKeyFocus = async(event, info, type) => {
|
||||
await hotKeySetEnable(false)
|
||||
window.isEditingHotKey = true
|
||||
isEditHotKey = true
|
||||
let config = hotKeyConfig.value[type][info.name]
|
||||
newHotKey = config?.key
|
||||
hotKeyTargetInput = event.target
|
||||
event.target.value = t('setting__hot_key_tip_input')
|
||||
}
|
||||
|
||||
const handleHotKeyBlur = async(event, info, type) => {
|
||||
await hotKeySetEnable(true)
|
||||
window.isEditingHotKey = false
|
||||
isEditHotKey = false
|
||||
const prevInput = hotKeyTargetInput
|
||||
hotKeyTargetInput = null
|
||||
if (prevInput.value == t('setting__hot_key_tip_input')) {
|
||||
prevInput.value = newHotKey ? formatHotKeyName(newHotKey) : ''
|
||||
return
|
||||
}
|
||||
let config = hotKeyConfig.value[type][info.name]
|
||||
let originKey
|
||||
if (type == 'global' && newHotKey && current_hot_key.value.global.enable) {
|
||||
try {
|
||||
await hotKeySetConfig({
|
||||
action: 'register',
|
||||
data: {
|
||||
key: newHotKey,
|
||||
info,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (config) {
|
||||
if (config.key == newHotKey) return
|
||||
originKey = config.key
|
||||
delete current_hot_key.value[type].keys[config.key]
|
||||
} else if (!newHotKey) return
|
||||
|
||||
if (newHotKey) {
|
||||
for (const [tempType, tempInfo] of Object.entries(current_hot_key.value)) {
|
||||
if (tempType == type) continue
|
||||
config = tempInfo.keys[newHotKey]
|
||||
if (config) {
|
||||
console.log(newHotKey, info, config, info.name, config.name)
|
||||
delete current_hot_key.value[tempType].keys[newHotKey]
|
||||
break
|
||||
}
|
||||
}
|
||||
current_hot_key.value[type].keys[newHotKey] = info
|
||||
}
|
||||
|
||||
initHotKeyConfig()
|
||||
// console.log(this.current_hot_key.global.keys)
|
||||
if (originKey && current_hot_key.value.global.enable) {
|
||||
try {
|
||||
await hotKeySetConfig({
|
||||
action: 'unregister',
|
||||
data: originKey,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
await handleHotKeySaveConfig()
|
||||
await getHotKeyStatus()
|
||||
}
|
||||
|
||||
const handleKeyDown = ({ event, keys, key, type }) => {
|
||||
// if (!event || event.repeat) return
|
||||
if (!event || event.repeat || type == 'up' || !isEditHotKey) return
|
||||
event.preventDefault()
|
||||
// console.log(event, key)
|
||||
switch (key) {
|
||||
case 'delete':
|
||||
case 'backspace':
|
||||
key = ''
|
||||
break
|
||||
}
|
||||
hotKeyTargetInput.value = formatHotKeyName(key)
|
||||
// console.log(keys, key, type)
|
||||
newHotKey = key
|
||||
}
|
||||
const handleUpdateHotKeyConfig = (config) => {
|
||||
// console.log(config)
|
||||
for (const [type, info] of Object.entries(config)) {
|
||||
current_hot_key.value[type] = info
|
||||
}
|
||||
}
|
||||
const handleHotKeySaveConfig = async() => {
|
||||
// console.log(this.current_hot_key)
|
||||
window.appHotKeyConfig = current_hot_key.value
|
||||
await hotKeySetConfig({
|
||||
action: 'config',
|
||||
data: toRaw(current_hot_key.value),
|
||||
source: eventsNameMainWindow.name,
|
||||
})
|
||||
}
|
||||
const handleEnableHotKey = async() => {
|
||||
await hotKeySetConfig({
|
||||
action: 'enable',
|
||||
data: current_hot_key.value.global.enable,
|
||||
source: eventsNameMainWindow.name,
|
||||
})
|
||||
await handleHotKeySaveConfig()
|
||||
await getHotKeyStatus()
|
||||
}
|
||||
const getHotKeyStatus = () => {
|
||||
return hotKeyGetStatus().then(status => {
|
||||
// console.log(status)
|
||||
hotKeyStatus.value = status
|
||||
return status
|
||||
})
|
||||
}
|
||||
|
||||
current_hot_key.value = window.appHotKeyConfig
|
||||
initHotKeyConfig()
|
||||
getHotKeyStatus()
|
||||
|
||||
window.eventHub.on(eventBaseName.set_hot_key_config, handleUpdateHotKeyConfig)
|
||||
window.eventHub.on(eventBaseName.key_down, handleKeyDown)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off(eventBaseName.set_hot_key_config, handleUpdateHotKeyConfig)
|
||||
window.eventHub.off(eventBaseName.key_down, handleKeyDown)
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
allHotKeys,
|
||||
current_hot_key,
|
||||
hotKeyConfig,
|
||||
hotKeyStatus,
|
||||
handleHotKeyFocus,
|
||||
handleHotKeyBlur,
|
||||
handleEnableHotKey,
|
||||
formatHotKeyName,
|
||||
handleHotKeySaveConfig,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.hotKeyContainer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
// margin-top: -15px;
|
||||
margin-bottom: 15px;
|
||||
transition: opacity @transition-theme;
|
||||
}
|
||||
.hotKeyItem {
|
||||
width: 30%;
|
||||
padding-right: 35px;
|
||||
margin-top: 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.hotKeyItemTitle {
|
||||
.mixin-ellipsis-1;
|
||||
padding-bottom: 5px;
|
||||
color: @color-theme_2-font-label;
|
||||
font-size: 12px;
|
||||
}
|
||||
.hotKeyItemInput {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
// font-family: monospace;
|
||||
&:focus {
|
||||
background-color: @color-theme_2-active;
|
||||
text-decoration: none;
|
||||
}
|
||||
&::placeholder {
|
||||
color: rgba(197, 197, 197, 0.7)!important;
|
||||
}
|
||||
}
|
||||
.hotKeyFailed {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.del-line {
|
||||
position: relative;
|
||||
&:before {
|
||||
display: block;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
width: 110%;
|
||||
content: ' ';
|
||||
left: 0;
|
||||
background-color: #000;
|
||||
transform: rotate(-24deg);
|
||||
transform-origin: 0;
|
||||
top: 83%;
|
||||
z-index: 1;
|
||||
}
|
||||
&:after {
|
||||
display: block;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
width: 110%;
|
||||
content: ' ';
|
||||
left: 0;
|
||||
background-color: #000;
|
||||
transform: rotate(23deg);
|
||||
transform-origin: 0px;
|
||||
top: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#root.@{value}) {
|
||||
.hotKeyItemTitle {
|
||||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
}
|
||||
})
|
||||
</style>
|
||||
@@ -0,0 +1,30 @@
|
||||
<template lang="pug">
|
||||
dt#list {{$t('setting__list')}}
|
||||
dd
|
||||
.gap-top
|
||||
base-checkbox(id="setting_list_showSource_enable" v-model="currentStting.list.isShowSource" :label="$t('setting__list_source')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_list_scroll_enable" v-model="currentStting.list.isSaveScrollLocation" :label="$t('setting__list_scroll')")
|
||||
dd(:tips="$t('setting__basic_sourcename_title')")
|
||||
h3#list_addMusicLocationType {{$t('setting__list_add_music_location_type')}}
|
||||
div
|
||||
base-checkbox.gap-left(id="setting_list_add_music_location_type_top"
|
||||
name="setting_list_add_music_location_type" need v-model="currentStting.list.addMusicLocationType" value="top" :label="$t('setting__list_add_music_location_type_top')")
|
||||
base-checkbox.gap-left(id="setting_list_add_music_location_type_bottom"
|
||||
name="setting_list_add_music_location_type" need v-model="currentStting.list.addMusicLocationType" value="bottom" :label="$t('setting__list_add_music_location_type_bottom')")
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { ref, onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'List',
|
||||
setup() {
|
||||
return {
|
||||
currentStting,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template lang="pug">
|
||||
dt#network {{$t('setting__network')}}
|
||||
dd
|
||||
h3#network_proxy_title {{$t('setting__network_proxy_title')}}
|
||||
div
|
||||
p
|
||||
base-checkbox(id="setting_network_proxy_enable" v-model="currentStting.network.proxy.enable" :label="$t('setting__is_enable')")
|
||||
p
|
||||
base-input.gap-left(v-model.trim="currentStting.network.proxy.host" :placeholder="$t('setting__network_proxy_host')")
|
||||
base-input.gap-left(v-model.trim="currentStting.network.proxy.port" :placeholder="$t('setting__network_proxy_port')")
|
||||
p
|
||||
base-input.gap-left(v-model.trim="currentStting.network.proxy.username" :placeholder="$t('setting__network_proxy_username')")
|
||||
base-input.gap-left(v-model.trim="currentStting.network.proxy.password" type="password" :placeholder="$t('setting__network_proxy_password')")
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { watch, onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { proxy } from '@renderer/core/share'
|
||||
import { debounce } from '@renderer/utils'
|
||||
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Network',
|
||||
setup() {
|
||||
watch(() => currentStting.value.network.proxy.enable, enable => {
|
||||
proxy.enable = enable
|
||||
})
|
||||
|
||||
const setHost = debounce(host => {
|
||||
proxy.host = host
|
||||
}, 500)
|
||||
watch(() => currentStting.value.network.proxy.host, setHost)
|
||||
const setPort = debounce(port => {
|
||||
proxy.port = port
|
||||
}, 500)
|
||||
watch(() => currentStting.value.network.proxy.port, setPort)
|
||||
const setUserName = debounce(username => {
|
||||
proxy.username = username
|
||||
}, 500)
|
||||
watch(() => currentStting.value.network.proxy.username, setUserName)
|
||||
const setPassword = debounce(password => {
|
||||
proxy.password = password
|
||||
}, 500)
|
||||
watch(() => currentStting.value.network.proxy.password, setPassword)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (currentStting.value.network.proxy.enable && !currentStting.value.network.proxy.host) proxy.enable = false
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template lang="pug">
|
||||
dt#odc {{$t('setting__odc')}}
|
||||
dd
|
||||
.gap-top
|
||||
base-checkbox(id="setting_odc_isAutoClearSearchInput" v-model="currentStting.odc.isAutoClearSearchInput" :label="$t('setting__odc_clear_search_input')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_odc_isAutoClearSearchList" v-model="currentStting.odc.isAutoClearSearchList" :label="$t('setting__odc_clear_search_list')")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { ref, onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Odc',
|
||||
setup() {
|
||||
return {
|
||||
currentStting,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template lang="pug">
|
||||
dt#other {{$t('setting__other')}}
|
||||
dd
|
||||
h3#other_tray_theme {{$t('setting__other_tray_theme')}}
|
||||
div
|
||||
base-checkbox.gap-left(:id="'setting_tray_theme_' + item.id" v-model="currentStting.tray.themeId" name="setting_tray_theme" need
|
||||
:label="item.label" :key="item.id" :value="item.id" v-for="item in trayThemeList")
|
||||
dd
|
||||
h3#other_resource_cache {{$t('setting__other_resource_cache')}}
|
||||
div
|
||||
p
|
||||
| {{$t('setting__other_resource_cache_label')}}
|
||||
span.auto-hidden {{cacheSize}}
|
||||
p
|
||||
base-btn.btn(min :disabled="isDisabledResourceCacheClear" @click="clearResourceCache") {{$t('setting__other_resource_cache_clear_btn')}}
|
||||
dd
|
||||
h3#other_play_list_cache {{$t('setting__other_play_list_cache')}}
|
||||
div
|
||||
base-btn.btn(min :disabled="isDisabledListCacheClear" @click="clearListCache") {{$t('setting__other_play_list_cache_clear_btn')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, useI18n, useCommit } from '@renderer/utils/vueTools'
|
||||
import { sizeFormate, clearCache, getCacheSize } from '@renderer/utils'
|
||||
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Other',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
|
||||
const trayThemeList = computed(() => {
|
||||
return [
|
||||
{ id: 0, name: 'native', label: t('setting__other_tray_theme_native') },
|
||||
{ id: 1, name: 'origin', label: t('setting__other_tray_theme_origin') },
|
||||
{ id: 2, name: 'black', label: t('setting__other_tray_theme_black') },
|
||||
]
|
||||
})
|
||||
|
||||
const cacheSize = ref('0 B')
|
||||
const isDisabledResourceCacheClear = ref(false)
|
||||
const isDisabledListCacheClear = ref(false)
|
||||
const refreshCacheSize = () => {
|
||||
getCacheSize().then(size => {
|
||||
cacheSize.value = sizeFormate(size)
|
||||
})
|
||||
}
|
||||
const clearResourceCache = () => {
|
||||
isDisabledResourceCacheClear.value = true
|
||||
clearCache().then(() => {
|
||||
refreshCacheSize()
|
||||
isDisabledResourceCacheClear.value = false
|
||||
})
|
||||
}
|
||||
refreshCacheSize()
|
||||
|
||||
const clearMyListCache = useCommit('list', 'clearCache')
|
||||
const clearListCache = () => {
|
||||
isDisabledListCacheClear.value = true
|
||||
clearMyListCache()
|
||||
isDisabledListCacheClear.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
trayThemeList,
|
||||
cacheSize,
|
||||
isDisabledResourceCacheClear,
|
||||
isDisabledListCacheClear,
|
||||
clearResourceCache,
|
||||
clearListCache,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template lang="pug">
|
||||
dt#play {{$t('setting__play')}}
|
||||
dd
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_save_play_time" v-model="currentStting.player.isSavePlayTime" :label="$t('setting__play_save_play_time')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_lyric_transition" v-model="currentStting.player.isShowLyricTranslation" :label="$t('setting__play_lyric_transition')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_lyric_play_lxlrc" v-model="currentStting.player.isPlayLxlrc" :label="$t('setting__play_lyric_lxlrc')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_highQuality" v-model="currentStting.player.highQuality" :label="$t('setting__play_quality')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_showTaskProgess" v-model="currentStting.player.isShowTaskProgess" :label="$t('setting__play_task_bar')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" v-model="currentStting.player.isMediaDeviceRemovedStopPlay" :label="$t('setting__play_mediaDevice_remove_stop_play')")
|
||||
dd(:tips="$t('setting__play_mediaDevice_title')")
|
||||
h3#play_mediaDevice {{$t('setting__play_mediaDevice')}}
|
||||
div
|
||||
base-selection.gap-left(:list="mediaDevices" v-model="currentStting.player.mediaDeviceId" item-key="deviceId" item-name="label")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onBeforeUnmount, watch, nextTick } from '@renderer/utils/vueTools'
|
||||
import { setTaskBarProgress } from '@renderer/utils/tools'
|
||||
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Play',
|
||||
setup() {
|
||||
const mediaDevices = ref([])
|
||||
const getMediaDevice = async() => {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices()
|
||||
let audioDevices = devices.filter(device => device.kind === 'audiooutput')
|
||||
mediaDevices.value = audioDevices
|
||||
// console.log(this.mediaDevices)
|
||||
}
|
||||
getMediaDevice()
|
||||
|
||||
navigator.mediaDevices.addEventListener('devicechange', getMediaDevice)
|
||||
onBeforeUnmount(() => {
|
||||
navigator.mediaDevices.removeEventListener('devicechange', getMediaDevice)
|
||||
})
|
||||
|
||||
watch(() => currentStting.value.player.isShowTaskProgess, val => {
|
||||
if (val) return
|
||||
nextTick(() => {
|
||||
setTaskBarProgress({
|
||||
status: -1,
|
||||
mode: 'normal',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
mediaDevices,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template lang="pug">
|
||||
dt#search {{$t('setting__search')}}
|
||||
dd
|
||||
.gap-top
|
||||
base-checkbox(id="setting_search_showHot_enable" v-model="currentStting.search.isShowHotSearch" :label="$t('setting__search_hot')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_search_showHistory_enable" v-model="currentStting.search.isShowHistorySearch" :label="$t('setting__search_history')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_search_focusSearchBox_enable" v-model="currentStting.search.isFocusSearchBox" :label="$t('setting__search_focus_search_box')")
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { ref, onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Search',
|
||||
setup() {
|
||||
return {
|
||||
currentStting,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template lang="pug">
|
||||
dt#sync {{$t('setting__sync')}}
|
||||
dd
|
||||
base-checkbox(id="setting_sync_enable" v-model="currentStting.sync.enable" :label="syncEnableTitle")
|
||||
div
|
||||
p.small {{$t('setting__sync_auth_code', { code: sync.status.code || '' })}}
|
||||
p.small {{$t('setting__sync_address', { address: sync.status.address.join(', ') || '' })}}
|
||||
p.small {{$t('setting__sync_device', { devices: syncDevices })}}
|
||||
p
|
||||
base-btn.btn(min :disabled="!sync.status.status" @click="refreshSyncCode") {{$t('setting__sync_refresh_code')}}
|
||||
dd
|
||||
h3#sync_port {{$t('setting__sync_port')}}
|
||||
div
|
||||
p
|
||||
base-input.gap-left(v-model.trim="currentStting.sync.port" :placeholder="$t('setting__sync_port_tip')")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, useI18n, watch } from '@renderer/utils/vueTools'
|
||||
import { sync } from '@renderer/core/share'
|
||||
import { refreshSyncCode, syncEnable } from '@renderer/utils/tools'
|
||||
import { debounce } from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Update',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
|
||||
|
||||
const syncEnableTitle = computed(() => {
|
||||
let title = t('setting__sync_enable')
|
||||
if (sync.status.message) {
|
||||
title += ` [${sync.status.message}]`
|
||||
}
|
||||
// else if (this.sync.status.address.length) {
|
||||
// // title += ` [${this.sync.status.address.join(', ')}]`
|
||||
// }
|
||||
return title
|
||||
})
|
||||
const syncDevices = computed(() => {
|
||||
return sync.status.devices.length
|
||||
? sync.status.devices.map(d => `${d.deviceName} (${d.clientId.substring(0, 5)})`).join(', ')
|
||||
: ''
|
||||
})
|
||||
|
||||
watch(() => currentStting.value.sync.enable, enable => {
|
||||
syncEnable({
|
||||
enable,
|
||||
port: currentStting.value.sync.port,
|
||||
})
|
||||
sync.enable = enable
|
||||
})
|
||||
|
||||
const setPort = debounce(port => {
|
||||
syncEnable({
|
||||
enable: currentStting.value.sync.enable,
|
||||
port,
|
||||
})
|
||||
sync.port = port
|
||||
}, 500)
|
||||
watch(() => currentStting.value.sync.port, setPort)
|
||||
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
sync,
|
||||
syncEnableTitle,
|
||||
syncDevices,
|
||||
refreshSyncCode,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
.save-path {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template lang="pug">
|
||||
dt#update {{$t('setting__update')}}
|
||||
dd
|
||||
p.small
|
||||
| {{$t('setting__update_latest_label')}}{{versionInfo.newVersion ? versionInfo.newVersion.version : $t('setting__update_unknown')}}
|
||||
p.small {{$t('setting__update_current_label')}}{{versionInfo.version}}
|
||||
p.small(v-if="versionInfo.downloadProgress" style="line-height: 1.5;")
|
||||
| {{$t('setting__update_downloading')}}
|
||||
br
|
||||
| {{$t('setting__update_progress')}}{{downloadProgress}}
|
||||
p(v-if="versionInfo.newVersion")
|
||||
span(v-if="versionInfo.isLatestVer") {{$t('setting__update_latest')}}
|
||||
base-btn.btn.gap-left(v-else min @click="showUpdateModal") {{$t('setting__update_open_version_modal_btn')}}
|
||||
p.small(v-else) {{$t('setting__update_checking')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, useI18n } from '@renderer/utils/vueTools'
|
||||
import { versionInfo } from '@renderer/core/share'
|
||||
import { sizeFormate } from '@renderer/utils/tools'
|
||||
// import { openDirInExplorer, selectDir } from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
|
||||
export default {
|
||||
name: 'Update',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
|
||||
const downloadProgress = computed(() => {
|
||||
return versionInfo.downloadProgress
|
||||
? `${versionInfo.downloadProgress.percent.toFixed(2)}% - ${sizeFormate(versionInfo.downloadProgress.transferred)}/${sizeFormate(versionInfo.downloadProgress.total)} - ${sizeFormate(versionInfo.downloadProgress.bytesPerSecond)}/s`
|
||||
: t('setting__update_init')
|
||||
})
|
||||
|
||||
const showUpdateModal = () => {
|
||||
versionInfo.showModal = true
|
||||
}
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
versionInfo,
|
||||
downloadProgress,
|
||||
showUpdateModal,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
.save-path {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,234 @@
|
||||
<template lang="pug">
|
||||
material-modal(:show="modelValue" bg-close @close="handleClose")
|
||||
main(:class="$style.main")
|
||||
h2 {{$t('user_api__title')}}
|
||||
ul.scroll(v-if="apiList.length" :class="$style.content")
|
||||
li(:class="[$style.listItem, {[$style.active]: setting.apiSource == api.id}]" v-for="(api, index) in apiList" :key="api.id")
|
||||
div(:class="$style.listLeft")
|
||||
h3 {{api.name}}
|
||||
p {{api.description}}
|
||||
base-btn(:class="$style.listBtn" outline :tips="$t('user_api__btn_remove')" @click.stop="handleRemove(index)")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 212.982 212.982' space='preserve' v-once)
|
||||
use(xlink:href='#icon-delete')
|
||||
div(v-else :class="$style.content")
|
||||
div(:class="$style.noitem") {{$t('user_api__noitem')}}
|
||||
div(:class="$style.note")
|
||||
p(:class="[$style.ruleLink]")
|
||||
| {{$t('user_api__readme')}}
|
||||
span.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%BA%90%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E8%AF%B4%E6%98%8E')" tips="https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md") FAQ.md
|
||||
p {{$t('user_api__note')}}
|
||||
div(:class="$style.footer")
|
||||
base-btn(:class="$style.footerBtn" @click="handleImport") {{$t('user_api__btn_import')}}
|
||||
//- base-btn(:class="$style.footerBtn" @click="handleExport") {{$t('user_api__btn_export')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { rendererInvoke, NAMES } from '@common/ipc'
|
||||
import { promises as fsPromises } from 'fs'
|
||||
import { selectDir, openUrl } from '@renderer/utils'
|
||||
import apiSourceInfo from '@renderer/utils/music/api-source-info'
|
||||
import { userApi, apiSource } from '@renderer/core/share'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
userApi,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['setting']),
|
||||
apiList() {
|
||||
return this.userApi.list
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleImport() {
|
||||
selectDir({
|
||||
title: this.$t('user_api__import_file'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'LX API File', extensions: ['js'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
return fsPromises.readFile(result.filePaths[0]).then(data => {
|
||||
return rendererInvoke(NAMES.mainWindow.import_user_api, data.toString()).then(({ apiList }) => {
|
||||
userApi.list = apiList
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
handleExport() {
|
||||
|
||||
},
|
||||
async handleRemove(index) {
|
||||
const api = this.apiList[index]
|
||||
if (!api) return
|
||||
if (this.setting.apiSource == api.id) {
|
||||
let backApi = apiSourceInfo.find(api => !api.disabled)
|
||||
if (backApi) apiSource.value = backApi.id
|
||||
}
|
||||
userApi.list = await rendererInvoke(NAMES.mainWindow.remove_user_api, [api.id])
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit('update:modelValue', false)
|
||||
},
|
||||
handleOpenUrl(url) {
|
||||
openUrl(url)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.main {
|
||||
padding: 15px;
|
||||
max-width: 400px;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
// max-height: 100%;
|
||||
// overflow: hidden;
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: @color-theme_2-font;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: @color-theme;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: auto;
|
||||
min-height: 100px;
|
||||
max-height: 100%;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.listItem {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
transition: background-color 0.2s ease;
|
||||
padding: 10px;
|
||||
border-radius: @radius-border;
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&.active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
h3 {
|
||||
font-size: 15px;
|
||||
color: @color-theme_2-font;
|
||||
word-break: break-all;
|
||||
}
|
||||
p {
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
color: @color-theme_2-font-label;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
.noitem {
|
||||
height: 100px;
|
||||
font-size: 18px;
|
||||
color: @color-theme_2-font-label;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.listLeft {
|
||||
flex: auto;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.listBtn {
|
||||
flex: none;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
svg {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
.note {
|
||||
margin-top: 15px;
|
||||
font-size: 12px;
|
||||
line-height: 1.25;
|
||||
color: @color-theme_2-font;
|
||||
p {
|
||||
+ p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.footerBtn {
|
||||
flex: auto;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 10px !important;
|
||||
width: 150px;
|
||||
.mixin-ellipsis-1;
|
||||
+ .footerBtn {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
.ruleLink {
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
|
||||
each(@themes, {
|
||||
:global(#root.@{value}) {
|
||||
.main {
|
||||
h2 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
}
|
||||
.listItem {
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&.active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
h3 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
p {
|
||||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
}
|
||||
.noitem {
|
||||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user