emby支持多源
This commit is contained in:
+438
-142
@@ -3399,7 +3399,7 @@ const OpenListConfigComponent = ({
|
||||
);
|
||||
};
|
||||
|
||||
// Emby 媒体库配置组件
|
||||
// Emby 媒体库配置组件 - 多源管理版本
|
||||
const EmbyConfigComponent = ({
|
||||
config,
|
||||
refreshConfig,
|
||||
@@ -3409,67 +3409,209 @@ const EmbyConfigComponent = ({
|
||||
}) => {
|
||||
const { alertModal, showAlert, hideAlert } = useAlertModal();
|
||||
const { isLoading, withLoading } = useLoadingState();
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const [serverURL, setServerURL] = useState('');
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [userId, setUserId] = useState('');
|
||||
|
||||
// 源列表状态
|
||||
const [sources, setSources] = useState<any[]>([]);
|
||||
const [editingSource, setEditingSource] = useState<any | null>(null);
|
||||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
|
||||
// 表单状态
|
||||
const [formData, setFormData] = useState({
|
||||
key: '',
|
||||
name: '',
|
||||
enabled: true,
|
||||
ServerURL: '',
|
||||
ApiKey: '',
|
||||
Username: '',
|
||||
Password: '',
|
||||
UserId: '',
|
||||
isDefault: false,
|
||||
});
|
||||
|
||||
// 从配置加载源列表
|
||||
useEffect(() => {
|
||||
if (config?.EmbyConfig) {
|
||||
setEnabled(config.EmbyConfig.Enabled || false);
|
||||
setServerURL(config.EmbyConfig.ServerURL || '');
|
||||
setApiKey(config.EmbyConfig.ApiKey || '');
|
||||
setUsername(config.EmbyConfig.Username || '');
|
||||
setPassword(config.EmbyConfig.Password || '');
|
||||
setUserId(config.EmbyConfig.UserId || '');
|
||||
if (config?.EmbyConfig?.Sources) {
|
||||
setSources(config.EmbyConfig.Sources);
|
||||
} else if (config?.EmbyConfig?.ServerURL) {
|
||||
// 兼容旧格式
|
||||
setSources([{
|
||||
key: 'default',
|
||||
name: 'Emby',
|
||||
enabled: config.EmbyConfig.Enabled || false,
|
||||
ServerURL: config.EmbyConfig.ServerURL,
|
||||
ApiKey: config.EmbyConfig.ApiKey,
|
||||
Username: config.EmbyConfig.Username,
|
||||
Password: config.EmbyConfig.Password,
|
||||
UserId: config.EmbyConfig.UserId,
|
||||
isDefault: true,
|
||||
}]);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
key: '',
|
||||
name: '',
|
||||
enabled: true,
|
||||
ServerURL: '',
|
||||
ApiKey: '',
|
||||
Username: '',
|
||||
Password: '',
|
||||
UserId: '',
|
||||
isDefault: false,
|
||||
});
|
||||
setEditingSource(null);
|
||||
setShowAddForm(false);
|
||||
};
|
||||
|
||||
// 开始编辑
|
||||
const handleEdit = (source: any) => {
|
||||
setFormData({ ...source });
|
||||
setEditingSource(source);
|
||||
setShowAddForm(false);
|
||||
};
|
||||
|
||||
// 开始添加
|
||||
const handleAdd = () => {
|
||||
resetForm();
|
||||
setShowAddForm(true);
|
||||
};
|
||||
|
||||
// 保存源(添加或更新)
|
||||
const handleSave = async () => {
|
||||
await withLoading('saveEmby', async () => {
|
||||
// 验证必填字段
|
||||
if (!formData.key || !formData.name || !formData.ServerURL) {
|
||||
showError('请填写必填字段:标识符、名称、服务器地址', showAlert);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证key唯一性
|
||||
if (!editingSource && sources.some(s => s.key === formData.key)) {
|
||||
showError('标识符已存在,请使用其他标识符', showAlert);
|
||||
return;
|
||||
}
|
||||
|
||||
await withLoading('saveEmbySource', async () => {
|
||||
try {
|
||||
const response = await fetch('/api/admin/emby', {
|
||||
let newSources;
|
||||
if (editingSource) {
|
||||
// 更新现有源
|
||||
newSources = sources.map(s =>
|
||||
s.key === editingSource.key ? formData : s
|
||||
);
|
||||
} else {
|
||||
// 添加新源
|
||||
newSources = [...sources, formData];
|
||||
}
|
||||
|
||||
// 保存到配置
|
||||
const response = await fetch('/api/admin/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'save',
|
||||
Enabled: enabled,
|
||||
ServerURL: serverURL,
|
||||
ApiKey: apiKey,
|
||||
Username: username,
|
||||
Password: password,
|
||||
UserId: userId,
|
||||
...config,
|
||||
EmbyConfig: {
|
||||
Sources: newSources,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
throw new Error(data.error || '保存失败');
|
||||
throw new Error('保存失败');
|
||||
}
|
||||
|
||||
await refreshConfig();
|
||||
showSuccess('保存成功', showAlert);
|
||||
resetForm();
|
||||
showSuccess(editingSource ? '更新成功' : '添加成功', showAlert);
|
||||
} catch (error) {
|
||||
showError(error instanceof Error ? error.message : '保存失败', showAlert);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleTest = async () => {
|
||||
await withLoading('testEmby', async () => {
|
||||
// 删除源
|
||||
const handleDelete = async (source: any) => {
|
||||
if (sources.length === 1) {
|
||||
showError('至少需要保留一个Emby源', showAlert);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`确定要删除 "${source.name}" 吗?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await withLoading('deleteEmbySource', async () => {
|
||||
try {
|
||||
const newSources = sources.filter(s => s.key !== source.key);
|
||||
|
||||
const response = await fetch('/api/admin/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
...config,
|
||||
EmbyConfig: {
|
||||
Sources: newSources,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('删除失败');
|
||||
}
|
||||
|
||||
await refreshConfig();
|
||||
showSuccess('删除成功', showAlert);
|
||||
} catch (error) {
|
||||
showError(error instanceof Error ? error.message : '删除失败', showAlert);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 切换启用状态
|
||||
const handleToggleEnabled = async (source: any) => {
|
||||
await withLoading('toggleEmbySource', async () => {
|
||||
try {
|
||||
const newSources = sources.map(s =>
|
||||
s.key === source.key ? { ...s, enabled: !s.enabled } : s
|
||||
);
|
||||
|
||||
const response = await fetch('/api/admin/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
...config,
|
||||
EmbyConfig: {
|
||||
Sources: newSources,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('更新失败');
|
||||
}
|
||||
|
||||
await refreshConfig();
|
||||
showSuccess(source.enabled ? '已禁用' : '已启用', showAlert);
|
||||
} catch (error) {
|
||||
showError(error instanceof Error ? error.message : '更新失败', showAlert);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 测试连接
|
||||
const handleTest = async (source: any) => {
|
||||
await withLoading('testEmbySource', async () => {
|
||||
try {
|
||||
const response = await fetch('/api/admin/emby', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'test',
|
||||
ServerURL: serverURL,
|
||||
ApiKey: apiKey,
|
||||
Username: username,
|
||||
Password: password,
|
||||
ServerURL: source.ServerURL,
|
||||
ApiKey: source.ApiKey,
|
||||
Username: source.Username,
|
||||
Password: source.Password,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -3486,6 +3628,7 @@ const EmbyConfigComponent = ({
|
||||
});
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const handleClearCache = async () => {
|
||||
await withLoading('clearEmbyCache', async () => {
|
||||
try {
|
||||
@@ -3523,127 +3666,280 @@ const EmbyConfigComponent = ({
|
||||
onConfirm={alertModal.onConfirm}
|
||||
/>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<label className='text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
启用 Emby 媒体库
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setEnabled(!enabled)}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
||||
enabled ? 'bg-blue-600' : 'bg-gray-200 dark:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
enabled ? 'translate-x-6' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
{/* 源列表 */}
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h3 className='text-lg font-medium text-gray-900 dark:text-gray-100'>
|
||||
Emby 源列表 ({sources.length})
|
||||
</h3>
|
||||
<button
|
||||
onClick={handleAdd}
|
||||
className={buttonStyles.success}
|
||||
>
|
||||
添加新源
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{sources.length === 0 ? (
|
||||
<div className='text-center py-8 text-gray-500 dark:text-gray-400'>
|
||||
暂无Emby源,点击"添加新源"开始配置
|
||||
</div>
|
||||
) : (
|
||||
sources.map((source) => (
|
||||
<div
|
||||
key={source.key}
|
||||
className='border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-white dark:bg-gray-800'
|
||||
>
|
||||
<div className='flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3'>
|
||||
<div className='flex-1'>
|
||||
<div className='flex items-center gap-3 flex-wrap'>
|
||||
<h4 className='text-base font-medium text-gray-900 dark:text-gray-100'>
|
||||
{source.name}
|
||||
</h4>
|
||||
{source.isDefault && (
|
||||
<span className='px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-200 rounded'>
|
||||
默认
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`px-2 py-0.5 text-xs font-medium rounded ${
|
||||
source.enabled
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-200'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
{source.enabled ? '已启用' : '已禁用'}
|
||||
</span>
|
||||
</div>
|
||||
<p className='mt-1 text-sm text-gray-600 dark:text-gray-400'>
|
||||
标识符: {source.key}
|
||||
</p>
|
||||
<p className='mt-1 text-sm text-gray-600 dark:text-gray-400'>
|
||||
服务器: {source.ServerURL}
|
||||
</p>
|
||||
{source.UserId && (
|
||||
<p className='mt-1 text-sm text-gray-600 dark:text-gray-400'>
|
||||
用户ID: {source.UserId}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex gap-2 flex-wrap sm:flex-nowrap'>
|
||||
<button
|
||||
onClick={() => handleToggleEnabled(source)}
|
||||
disabled={isLoading('toggleEmbySource')}
|
||||
className={source.enabled ? buttonStyles.warningSmall : buttonStyles.successSmall}
|
||||
>
|
||||
{source.enabled ? '禁用' : '启用'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleTest(source)}
|
||||
disabled={isLoading('testEmbySource')}
|
||||
className={buttonStyles.primarySmall}
|
||||
>
|
||||
测试
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(source)}
|
||||
className={buttonStyles.primarySmall}
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(source)}
|
||||
disabled={isLoading('deleteEmbySource')}
|
||||
className={buttonStyles.dangerSmall}
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 服务器地址 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
Emby 服务器地址
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={serverURL}
|
||||
onChange={(e) => setServerURL(e.target.value)}
|
||||
placeholder='http://192.168.1.100:8096'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
{/* 添加/编辑表单 */}
|
||||
{(showAddForm || editingSource) && (
|
||||
<div className='border border-gray-200 dark:border-gray-700 rounded-lg p-6 bg-gray-50 dark:bg-gray-800/50'>
|
||||
<h3 className='text-lg font-medium text-gray-900 dark:text-gray-100 mb-4'>
|
||||
{editingSource ? '编辑 Emby 源' : '添加新的 Emby 源'}
|
||||
</h3>
|
||||
|
||||
{/* API Key */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
API Key(推荐)
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
placeholder='输入 Emby API Key'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
推荐使用 API Key 认证。如果不使用 API Key,请填写下方的用户名和密码。
|
||||
</p>
|
||||
</div>
|
||||
<div className='space-y-4'>
|
||||
{/* 标识符 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
标识符 *
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={formData.key}
|
||||
onChange={(e) => setFormData({ ...formData, key: e.target.value })}
|
||||
disabled={!!editingSource}
|
||||
placeholder='home, office, etc.'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 disabled:bg-gray-100 dark:disabled:bg-gray-700'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
唯一标识符,只能包含字母、数字、下划线,创建后不可修改
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 用户名 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
用户名(可选)
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder='Emby 用户名'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
{/* 名称 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
显示名称 *
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder='家庭Emby, 公司Emby, etc.'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 密码 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
密码(可选)
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder='Emby 密码'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
{/* 服务器地址 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
Emby 服务器地址 *
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={formData.ServerURL}
|
||||
onChange={(e) => setFormData({ ...formData, ServerURL: e.target.value })}
|
||||
placeholder='http://192.168.1.100:8096'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 用户 ID */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
用户 ID(使用 API Key 时必填)
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={userId}
|
||||
onChange={(e) => setUserId(e.target.value)}
|
||||
placeholder='aab507c58e874de6a9bd12388d72f4d2'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
从你的 Emby 抓包数据中获取用户 ID,通常在 URL 中如 /Users/[userId]/...
|
||||
</p>
|
||||
</div>
|
||||
{/* API Key */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
API Key(推荐)
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
value={formData.ApiKey}
|
||||
onChange={(e) => setFormData({ ...formData, ApiKey: e.target.value })}
|
||||
placeholder='输入 Emby API Key'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
推荐使用 API Key 认证。如果不使用 API Key,请填写下方的用户名和密码。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className='flex gap-3'>
|
||||
<button
|
||||
onClick={handleTest}
|
||||
disabled={isLoading('testEmby')}
|
||||
className='px-4 py-2 bg-gray-600 hover:bg-gray-700 disabled:bg-gray-400 text-white rounded-lg transition-colors'
|
||||
>
|
||||
{isLoading('testEmby') ? '测试中...' : '测试连接'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={isLoading('saveEmby')}
|
||||
className='px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white rounded-lg transition-colors'
|
||||
>
|
||||
{isLoading('saveEmby') ? '保存中...' : '保存配置'}
|
||||
</button>
|
||||
</div>
|
||||
{/* 用户名 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
用户名(可选)
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={formData.Username}
|
||||
onChange={(e) => setFormData({ ...formData, Username: e.target.value })}
|
||||
placeholder='Emby 用户名'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 清除缓存按钮 */}
|
||||
<div className='flex gap-3'>
|
||||
{/* 密码 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
密码(可选)
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
value={formData.Password}
|
||||
onChange={(e) => setFormData({ ...formData, Password: e.target.value })}
|
||||
placeholder='Emby 密码'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 用户 ID */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
用户 ID(使用 API Key 时必填)
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
value={formData.UserId}
|
||||
onChange={(e) => setFormData({ ...formData, UserId: e.target.value })}
|
||||
placeholder='aab507c58e874de6a9bd12388d72f4d2'
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
从你的 Emby 抓包数据中获取用户 ID,通常在 URL 中如 /Users/[userId]/...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<label className='text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
启用此源
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setFormData({ ...formData, enabled: !formData.enabled })}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
||||
formData.enabled ? 'bg-blue-600' : 'bg-gray-200 dark:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
formData.enabled ? 'translate-x-6' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 默认源开关 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<label className='text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
设为默认源
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setFormData({ ...formData, isDefault: !formData.isDefault })}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
||||
formData.isDefault ? 'bg-blue-600' : 'bg-gray-200 dark:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
formData.isDefault ? 'translate-x-6' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className='flex gap-3 pt-4'>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={isLoading('saveEmbySource')}
|
||||
className={buttonStyles.success}
|
||||
>
|
||||
{isLoading('saveEmbySource') ? '保存中...' : '保存'}
|
||||
</button>
|
||||
<button
|
||||
onClick={resetForm}
|
||||
className={buttonStyles.secondary}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 全局操作 */}
|
||||
<div className='flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
||||
<button
|
||||
onClick={handleClearCache}
|
||||
disabled={isLoading('clearEmbyCache')}
|
||||
className='px-4 py-2 bg-orange-600 hover:bg-orange-700 disabled:bg-orange-400 text-white rounded-lg transition-colors'
|
||||
className={buttonStyles.warning}
|
||||
>
|
||||
{isLoading('clearEmbyCache') ? '清除中...' : '清除缓存'}
|
||||
{isLoading('clearEmbyCache') ? '清除中...' : '清除所有缓存'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user