import { createStore } from "solid-js/store"; export interface Pin { x: number; y: number; label: string; } interface PinsState { pins: Pin[]; rawSrc: string; isFixed: boolean; labelStart: string; } // 判断 labelStart 是否为数字 function isNumericLabelStart(start: string): boolean { return /^\d+$/.test(start); } // 生成字母标签 A-Z, AA-ZZ, AAA-ZZZ ... 从指定起始值开始 export function generateAlphaLabel(index: number, startLabel: string = 'A'): string { const startOffset = alphaLabelToIndex(startLabel); return indexToAlphaLabel(index + startOffset); } // 将字母标签转换为索引 (A=0, B=1, ..., Z=25, AA=26, ...) export function alphaLabelToIndex(label: string): number { let index = 0; for (let i = 0; i < label.length; i++) { index = index * 26 + (label.charCodeAt(i) - 64); } return index - 1; } // 将索引转换为字母标签 (0=A, 1=B, ..., 25=Z, 26=AA, ...) export function indexToAlphaLabel(index: number): string { const labels: string[] = []; let num = index; do { const remainder = num % 26; labels.unshift(String.fromCharCode(65 + remainder)); num = Math.floor(num / 26) - 1; } while (num >= 0); return labels.join(''); } // 生成数字标签,从指定起始值开始 export function generateNumberLabel(index: number, startNum: number): string { return String(startNum + index); } // 解析 pins 字符串 "A:30,40 B:10,30" 或 "1:30,40 2:10,30" -> Pin[] export function parsePins(pinsStr: string): Pin[] { if (!pinsStr) return []; const pins: Pin[] = []; // 支持任意非空白字符作为标签(不仅限于大写字母) const regex = /([^:\s]+):(\d+),(\d+)/g; let match; while ((match = regex.exec(pinsStr)) !== null) { pins.push({ label: match[1], x: parseInt(match[2]), y: parseInt(match[3]) }); } return pins; } // 格式化 pins 为字符串 "A:30,40 B:10,30" export function formatPins(pins: Pin[]): string { return pins.map(pin => `${pin.label}:${pin.x},${pin.y}`).join(' '); } // 找到最早未使用的标签 export function findNextUnusedLabel(pins: Pin[], labelStart: string): string { const usedLabels = new Set(pins.map(p => p.label)); if (isNumericLabelStart(labelStart)) { // 数字标签模式 const startNum = parseInt(labelStart); let index = 0; while (true) { const label = generateNumberLabel(index, startNum); if (!usedLabels.has(label)) { return label; } index++; if (index > 10000) break; // 安全限制 } } else { // 字母标签模式 let index = 0; while (true) { const label = generateAlphaLabel(index, labelStart); if (!usedLabels.has(label)) { return label; } index++; if (index > 10000) break; // 安全限制 } } // 回退值 if (isNumericLabelStart(labelStart)) { return generateNumberLabel(pins.length, parseInt(labelStart)); } return generateAlphaLabel(pins.length, labelStart); } // 创建 store 实例 export function createPinsStore( initialPinsStr: string = "", initialFixed: boolean = false, initialLabelStart: string = "A" ) { const [state, setState] = createStore({ pins: parsePins(initialPinsStr), rawSrc: "", isFixed: initialFixed, labelStart: initialLabelStart }); const setRawSrc = (src: string) => { setState("rawSrc", src); }; const addPin = (x: number, y: number) => { const label = findNextUnusedLabel(state.pins, state.labelStart); setState("pins", [...state.pins, { x, y, label }]); }; const removePin = (index: number) => { setState("pins", state.pins.filter((_, i) => i !== index)); }; const formatCurrentPins = () => formatPins(state.pins); const getCopyText = () => { const pinsStr = formatCurrentPins(); const labelStartAttr = state.labelStart !== 'A' ? ` labelStart="${state.labelStart}"` : ''; return `:md-pins[${state.rawSrc}]{pins="${pinsStr}" fixed${labelStartAttr}`; }; return { state, setState, setRawSrc, addPin, removePin, formatCurrentPins, getCopyText }; }