167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
import { defineStore, acceptHMRUpdate } from 'pinia';
|
|
|
|
type IconSize = '1x1' | '1x2' | '2x1' | '2x2' | '2x4';
|
|
const DEFAULT_GROUP_ID = 'home';
|
|
|
|
// 模拟数据(来自截图参考)
|
|
interface Icon {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
size?: IconSize;
|
|
img?: string; // 可选:用于图片图标(如徽标)
|
|
text?: string; // 可选:用于文字图标
|
|
bgColor?: string; // 可选:纯色背景
|
|
groupId?: string;
|
|
}
|
|
|
|
interface DragState {
|
|
isDragging: boolean;
|
|
itemId: string | null;
|
|
itemType: 'icon' | 'widget' | null;
|
|
startX: number;
|
|
startY: number;
|
|
currentX: number;
|
|
currentY: number;
|
|
}
|
|
|
|
interface LayoutState {
|
|
icons: Icon[];
|
|
dragState: DragState;
|
|
}
|
|
|
|
const defaultIcons: Icon[] = [
|
|
{ id: '1', name: '淘宝', url: 'https://taobao.com', bgColor: '#ff4f00' },
|
|
{ id: '2', name: '京东商城', url: 'https://jd.com', bgColor: '#e4393c' },
|
|
{ id: '3', name: '百度', url: 'https://baidu.com', bgColor: '#3388ff' },
|
|
{ id: '4', name: '备忘录', url: '#', bgColor: '#f9ca24' },
|
|
{ id: '5', name: '爱奇艺', url: 'https://iqiyi.com', bgColor: '#00be06' },
|
|
{ id: '6', name: '文件夹', url: '#', bgColor: '#4285f4' },
|
|
{ id: '7', name: '抖音', url: 'https://douyin.com', bgColor: '#222' },
|
|
{ id: '8', name: '小浣熊', url: '#', bgColor: '#f0932b' },
|
|
{ id: '9', name: 'AiPPT', url: '#', bgColor: '#d63031' },
|
|
{ id: '10', name: '电影日历', url: '#', bgColor: 'transparent', img: 'https://example.com/movie_poster.png' }, // 图片示例
|
|
{ id: '11', name: '稿定设计', url: '#', bgColor: '#00aaff' },
|
|
{ id: '12', name: '壁纸', url: '#', bgColor: '#1dd1a1' },
|
|
{ id: '13', 'name': '即梦AI', url: '#', bgColor: '#6c5ce7' },
|
|
{ id: '14', name: '码上掘金', url: '#', bgColor: '#1e80ff' },
|
|
{ id: '15', name: '扩展管理', url: '#', bgColor: '#7f8c8d' },
|
|
{ id: '16', name: '书签管理', url: '#', bgColor: '#f1c40f' },
|
|
];
|
|
|
|
const defaultGroupById: Record<string, string> = {
|
|
'1': 'product',
|
|
'2': 'product',
|
|
'3': 'home',
|
|
'4': 'product',
|
|
'5': 'fun',
|
|
'6': 'home',
|
|
'7': 'fun',
|
|
'8': 'fun',
|
|
'9': 'ai',
|
|
'10': 'fun',
|
|
'11': 'design',
|
|
'12': 'design',
|
|
'13': 'ai',
|
|
'14': 'code',
|
|
'15': 'home',
|
|
'16': 'home',
|
|
};
|
|
|
|
const savedIcons = localStorage.getItem('itab_icons');
|
|
|
|
const normalizeIcons = (icons: Icon[]): Icon[] =>
|
|
icons.map(icon => ({
|
|
...icon,
|
|
size: icon.size ?? '1x1',
|
|
groupId: icon.groupId ?? defaultGroupById[icon.id] ?? DEFAULT_GROUP_ID,
|
|
}));
|
|
|
|
const loadIcons = (): Icon[] => {
|
|
if (!savedIcons) return normalizeIcons(defaultIcons);
|
|
try {
|
|
return normalizeIcons(JSON.parse(savedIcons) as Icon[]);
|
|
} catch {
|
|
return normalizeIcons(defaultIcons);
|
|
}
|
|
};
|
|
|
|
export const useLayoutStore = defineStore('layout', {
|
|
state: (): LayoutState => ({
|
|
icons: loadIcons(),
|
|
dragState: {
|
|
isDragging: false,
|
|
itemId: null,
|
|
itemType: null,
|
|
startX: 0,
|
|
startY: 0,
|
|
currentX: 0,
|
|
currentY: 0,
|
|
}
|
|
}),
|
|
actions: {
|
|
addIcon(icon: Omit<Icon, 'id'>) {
|
|
const nextId = `custom-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
this.icons.push({ ...icon, id: nextId, groupId: icon.groupId ?? DEFAULT_GROUP_ID });
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
},
|
|
updateIcon(iconId: string, updates: Partial<Omit<Icon, 'id'>>) {
|
|
const icon = this.icons.find(item => item.id === iconId);
|
|
if (!icon) return;
|
|
Object.assign(icon, updates);
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
},
|
|
reorderIcons(draggedId: string, targetId: string) {
|
|
const draggedIndex = this.icons.findIndex(p => p.id === draggedId);
|
|
const targetIndex = this.icons.findIndex(p => p.id === targetId);
|
|
|
|
if (draggedIndex !== -1 && targetIndex !== -1) {
|
|
// 直接交换
|
|
const draggedItem = this.icons[draggedIndex];
|
|
this.icons[draggedIndex] = this.icons[targetIndex];
|
|
this.icons[targetIndex] = draggedItem;
|
|
|
|
// 持久化顺序
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
}
|
|
},
|
|
setIconOrder(orderIds: string[]) {
|
|
const ordered: Icon[] = [];
|
|
const seen = new Set<string>();
|
|
for (const id of orderIds) {
|
|
const icon = this.icons.find(item => item.id === id);
|
|
if (icon) {
|
|
ordered.push(icon);
|
|
seen.add(icon.id);
|
|
}
|
|
}
|
|
for (const icon of this.icons) {
|
|
if (!seen.has(icon.id)) {
|
|
ordered.push(icon);
|
|
}
|
|
}
|
|
this.icons = ordered;
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
},
|
|
updateIconSize(iconId: string, newSize: IconSize) {
|
|
const icon = this.icons.find(item => item.id === iconId);
|
|
if (icon) {
|
|
icon.size = newSize;
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
}
|
|
},
|
|
deleteIcon(itemId: string) {
|
|
const index = this.icons.findIndex(p => p.id === itemId);
|
|
if (index !== -1) {
|
|
this.icons.splice(index, 1);
|
|
// 持久化顺序
|
|
localStorage.setItem('itab_icons', JSON.stringify(this.icons));
|
|
}
|
|
},
|
|
}
|
|
});
|
|
|
|
if (import.meta.hot) {
|
|
import.meta.hot.accept(acceptHMRUpdate(useLayoutStore, import.meta.hot));
|
|
}
|