Skip to content

Commit

Permalink
Add layout import/export functionality to config page
Browse files Browse the repository at this point in the history
- Implement layout import and export handlers in ConfigPage
- Add new translation keys for layout import/export messages
- Update UniversalCard to conditionally render group names
- Adjust UniversalCard and group name styles
  • Loading branch information
mrtian2016 committed Mar 4, 2025
1 parent 1dea99e commit 458c19f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 2 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/UniversalCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ function UniversalCard({ config }) {
<div className="universal-data">
{loadedEntityGroups.map(group => (
<div key={group.id} className="entity-group">
<div className="group-name">{group.name}</div>
{group.name && <div className="group-name">{group.name}</div>}
<div className="card-entity-items">
{Object.entries(group.entities).map(([id, entity]) => (
<div
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/UniversalCard/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
padding: var(--spacing-sm);
border-radius: var(--border-radius-small);
background: var(--color-card-bg-transparent);
gap: var(--spacing-md);
}

.group-name {
Expand All @@ -22,6 +21,7 @@
flex: 1;
flex-shrink: 0;
text-align: center;
margin-bottom: var(--spacing-md);
}

.card-entity-items {
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/i18n/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ export const translations = {
debugOff: '关闭',
import: '导入配置',
export: '导出配置',
importLayout: '导入布局',
exportLayout: '导出布局',
layoutImportSuccess: '布局导入成功',
layoutExportSuccess: '布局导出成功',
layoutImportFailed: '布局导入失败',
layoutExportFailed: '布局导出失败',
importSuccess: '导入成功',
exportSuccess: '导出成功',
importFailed: '导入失败',
Expand Down Expand Up @@ -1053,6 +1059,12 @@ export const translations = {
exportSuccess: 'Export Success',
importFailed: 'Import Failed',
exportFailed: 'Export Failed',
importLayout: 'Import Layout',
exportLayout: 'Export Layout',
layoutImportSuccess: 'Layout Import Success',
layoutExportSuccess: 'Layout Export Success',
layoutImportFailed: 'Layout Import Failed',
layoutExportFailed: 'Layout Export Failed',
globalConfig: 'Global Settings',
backgroundColor: 'Background Color',
backgroundColorHint: 'Choose the page background color',
Expand Down
82 changes: 82 additions & 0 deletions frontend/src/pages/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,69 @@ function ConfigPage({ sidebarVisible, setSidebarVisible }) {
}
};

// 添加导入布局的处理函数
const handleImportLayout = async (event) => {
const file = event.target.files[0];
if (file) {
try {
const content = await file.text();
const importedLayout = JSON.parse(content);

// 验证导入的布局
if (!importedLayout.layouts) {
throw new Error('无效的布局文件格式');
}

// 分别保存移动端和桌面端布局
localStorage.setItem('mobile-dashboard-layouts', JSON.stringify(importedLayout.layouts));
localStorage.setItem('desktop-dashboard-layouts', JSON.stringify(importedLayout.layouts));

message.success(t('config.layoutImportSuccess'));
} catch (error) {
console.error('导入布局失败:', error);
message.error(t('config.layoutImportFailed'));
}
// 清除文件输入
event.target.value = '';
}
};

// 添加导出布局的处理函数
const handleExportLayout = () => {
try {
// 获取桌面端和移动端布局
const desktopLayouts = localStorage.getItem('desktop-dashboard-layouts');
const mobileLayouts = localStorage.getItem('mobile-dashboard-layouts');

if (!desktopLayouts && !mobileLayouts) {
throw new Error('没有找到布局配置');
}

// 优先使用桌面端布局,如果没有则使用移动端布局
const layouts = desktopLayouts ? JSON.parse(desktopLayouts) : JSON.parse(mobileLayouts);

// 创建导出对象
const exportData = {
layouts: layouts
};

// 创建下载
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `hass-panel-layout-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
message.success(t('config.layoutExportSuccess'));
} catch (error) {
console.error('导出布局失败:', error);
message.error(t('config.layoutExportFailed'));
}
};

// 修改导出函数
const handleExport = async () => {
try {
Expand Down Expand Up @@ -763,6 +826,18 @@ function ConfigPage({ sidebarVisible, setSidebarVisible }) {
icon: <Icon path={mdiExport} size={0.8} />,
onClick: handleExport
},
{
key: 'importLayout',
label: t('config.importLayout'),
icon: <Icon path={mdiImport} size={0.8} />,
onClick: () => document.getElementById('layoutFileInput').click()
},
{
key: 'exportLayout',
label: t('config.exportLayout'),
icon: <Icon path={mdiExport} size={0.8} />,
onClick: handleExportLayout
},
{
key: 'versions',
label: t('config.versionList'),
Expand Down Expand Up @@ -801,6 +876,13 @@ function ConfigPage({ sidebarVisible, setSidebarVisible }) {
accept=".json"
style={{ display: 'none' }}
/>
<input
type="file"
id="layoutFileInput"
onChange={handleImportLayout}
accept=".json"
style={{ display: 'none' }}
/>

<Dropdown menu={{ items: configMenuItems }} placement="bottomLeft">
<Button>
Expand Down

0 comments on commit 458c19f

Please sign in to comment.