mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-05-17 02:07:29 +08:00
✨ feat: 添加远程控制功能,支持远程控制音乐播放操作
This commit is contained in:
Vendored
+3
@@ -19,8 +19,10 @@ declare module 'vue' {
|
||||
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
||||
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
@@ -50,6 +52,7 @@ declare module 'vue' {
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NText: typeof import('naive-ui')['NText']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
@@ -168,7 +168,6 @@ import {
|
||||
setBackgroundImg
|
||||
} from '@/utils';
|
||||
import { getArtistDetail } from '@/api/artist';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const playerStore = usePlayerStore();
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDateFormat, useThrottleFn } from '@vueuse/core';
|
||||
import { useDateFormat } from '@vueuse/core';
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
@@ -271,6 +271,16 @@
|
||||
}}</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="set-item">
|
||||
<div>
|
||||
<div class="set-item-title">{{ t('settings.application.remoteControl') }}</div>
|
||||
<div class="set-item-content">{{ t('settings.application.remoteControlDesc') }}</div>
|
||||
</div>
|
||||
<n-button size="small" @click="showRemoteControlModal = true">{{
|
||||
t('common.configure')
|
||||
}}</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -548,6 +558,9 @@
|
||||
</div>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
|
||||
<!-- 远程控制设置弹窗 -->
|
||||
<remote-control-setting v-model:visible="showRemoteControlModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -569,6 +582,7 @@ import { useUserStore } from '@/store/modules/user';
|
||||
import { isElectron, isMobile } from '@/utils';
|
||||
import { openDirectory, selectDirectory } from '@/utils/fileOperation';
|
||||
import { checkUpdate, UpdateResult } from '@/utils/update';
|
||||
import RemoteControlSetting from '@/views/setting/ServerSetting.vue';
|
||||
|
||||
import config from '../../../../package.json';
|
||||
|
||||
@@ -1096,6 +1110,9 @@ const getSourceLabel = (source: Platform) => {
|
||||
const sourceLabel = musicSourceOptions.value.find(s => s.value === source)?.label;
|
||||
return sourceLabel || source;
|
||||
};
|
||||
|
||||
// 远程控制设置弹窗
|
||||
const showRemoteControlModal = ref(false);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="visible"
|
||||
preset="card"
|
||||
:title="t('settings.remoteControl.title')"
|
||||
class="remote-control-modal"
|
||||
style="max-width: 650px; width: 100%"
|
||||
>
|
||||
<n-scrollbar>
|
||||
<div class="remote-control-setting">
|
||||
<n-form label-placement="left" label-width="auto" :style="{ maxWidth: '640px' }">
|
||||
<n-form-item :label="t('settings.remoteControl.enable')">
|
||||
<n-switch v-model:value="remoteControlConfig.enabled" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('settings.remoteControl.port')">
|
||||
<n-input-number
|
||||
v-model:value="remoteControlConfig.port"
|
||||
:min="1024"
|
||||
:max="65535"
|
||||
:disabled="!remoteControlConfig.enabled"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('settings.remoteControl.allowedIps')">
|
||||
<div class="allowed-ips-container">
|
||||
<div v-for="(_, index) in remoteControlConfig.allowedIps" :key="index" class="ip-item">
|
||||
<n-input v-model:value="remoteControlConfig.allowedIps[index]" :disabled="!remoteControlConfig.enabled" />
|
||||
<n-button
|
||||
quaternary
|
||||
circle
|
||||
type="error"
|
||||
:disabled="!remoteControlConfig.enabled"
|
||||
@click="removeIp(index)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon><i class="ri-delete-bin-line"></i></n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
<n-button
|
||||
secondary
|
||||
size="small"
|
||||
:disabled="!remoteControlConfig.enabled"
|
||||
@click="addIp"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon><i class="ri-add-line"></i></n-icon>
|
||||
</template>
|
||||
{{ t('settings.remoteControl.addIp') }}
|
||||
</n-button>
|
||||
<n-text depth="3" size="small" class="allow-all-hint">
|
||||
{{ t('settings.remoteControl.emptyListHint') }}
|
||||
</n-text>
|
||||
</div>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item>
|
||||
<n-space>
|
||||
<n-button
|
||||
type="primary"
|
||||
:disabled="!remoteControlConfig.enabled"
|
||||
@click="saveConfig"
|
||||
>
|
||||
{{ t('common.save') }}
|
||||
</n-button>
|
||||
<n-button @click="resetConfig">
|
||||
{{ t('common.reset') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
|
||||
<n-collapse-transition :show="remoteControlConfig.enabled">
|
||||
<div class="remote-info">
|
||||
<n-alert type="info">
|
||||
<template #icon>
|
||||
<n-icon><i class="ri-information-line"></i></n-icon>
|
||||
</template>
|
||||
<p>{{ t('settings.remoteControl.accessInfo') }}</p>
|
||||
<div class="access-url">
|
||||
<n-tag type="success">
|
||||
http://localhost:{{ remoteControlConfig.port }}/
|
||||
</n-tag>
|
||||
</div>
|
||||
<div v-if="localIpAddresses.length" class="local-ips">
|
||||
<div v-for="ip in localIpAddresses" :key="ip" class="ip-address">
|
||||
<n-tag type="info">
|
||||
http://{{ ip }}:{{ remoteControlConfig.port }}/
|
||||
</n-tag>
|
||||
</div>
|
||||
</div>
|
||||
</n-alert>
|
||||
</div>
|
||||
</n-collapse-transition>
|
||||
</n-form>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const { t } = useI18n();
|
||||
const message = useMessage();
|
||||
|
||||
// 控制弹窗显示的属性
|
||||
const visible = defineModel('visible', { default: false });
|
||||
|
||||
// 默认配置
|
||||
const defaultConfig:{
|
||||
enabled: boolean,
|
||||
port: number,
|
||||
allowedIps: string[]
|
||||
} = {
|
||||
enabled: false,
|
||||
port: 31888,
|
||||
allowedIps: []
|
||||
};
|
||||
|
||||
// 远程控制配置
|
||||
const remoteControlConfig = ref({...defaultConfig});
|
||||
|
||||
// 本地IP地址列表
|
||||
const localIpAddresses = ref<string[]>([]);
|
||||
|
||||
// 获取本地IP地址
|
||||
const getLocalIpAddresses = () => {
|
||||
if (window.electron) {
|
||||
window.electron.ipcRenderer.invoke('get-local-ip-addresses').then((ips: string[]) => {
|
||||
localIpAddresses.value = ips;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 添加IP地址
|
||||
const addIp = () => {
|
||||
remoteControlConfig.value.allowedIps.push('');
|
||||
};
|
||||
|
||||
// 删除IP地址
|
||||
const removeIp = (index: number) => {
|
||||
remoteControlConfig.value.allowedIps.splice(index, 1);
|
||||
};
|
||||
|
||||
// 保存配置
|
||||
const saveConfig = () => {
|
||||
// 过滤空IP
|
||||
remoteControlConfig.value.allowedIps = remoteControlConfig.value.allowedIps.filter(ip => ip.trim() !== '');
|
||||
|
||||
if (window.electron) {
|
||||
window.electron.ipcRenderer.send('update-remote-control-config', cloneDeep(remoteControlConfig.value));
|
||||
message.success(t('settings.remoteControl.saveSuccess'));
|
||||
}
|
||||
};
|
||||
|
||||
// 重置配置
|
||||
const resetConfig = () => {
|
||||
if (window.electron) {
|
||||
window.electron.ipcRenderer.invoke('get-remote-control-config').then((config) => {
|
||||
if (config) {
|
||||
remoteControlConfig.value = config;
|
||||
} else {
|
||||
remoteControlConfig.value = { ...defaultConfig };
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时,获取当前配置
|
||||
onMounted(async () => {
|
||||
if (window.electron) {
|
||||
try {
|
||||
const config = await window.electron.ipcRenderer.invoke('get-remote-control-config');
|
||||
if (config) {
|
||||
remoteControlConfig.value = config;
|
||||
}
|
||||
// 获取本地IP地址
|
||||
getLocalIpAddresses();
|
||||
} catch (error) {
|
||||
console.error('获取远程控制配置失败:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remote-control-setting {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.allowed-ips-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
|
||||
.ip-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.allow-all-hint {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-info {
|
||||
margin-top: 16px;
|
||||
|
||||
.access-url {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.local-ips {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user