1+ /* ============================================================
2+ * HC v0.01 —— 数据层 store.js
3+ * 职责:
4+ * 1) 项目 / 页面 CRUD,localStorage 本地持久化
5+ * 2) 首次零项目引导判定(hasAnyProject / lastActive)
6+ * 3) .hc 文件导出 / 导入
7+ * 数据形态(localStorage 键:HICODE_DATA):
8+ * {
9+ * projects: { <projId>: { id, name, createdAt, updatedAt,
10+ * pages: { <pageId>: { id, name, code,
11+ * images: { <varName>: dataURL } } } } },
12+ * lastProjectId, lastPageId
13+ * }
14+ * 图片以 dataURL 内嵌进项目数据,随 .hc 一并保存。
15+ * ============================================================ */
16+ ( function ( root , factory ) {
17+ const api = factory ( ) ;
18+ if ( typeof module !== "undefined" && module . exports ) module . exports = api ;
19+ if ( root ) root . Store = api ;
20+ } ) ( typeof self !== "undefined" ? self : null , function ( ) {
21+ "use strict" ;
22+
23+ const KEY = "HICODE_DATA" ;
24+ const STORE_VERSION = "v0.01" ;
25+
26+ function uuid ( ) {
27+ return "h" + Date . now ( ) . toString ( 36 ) + Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) ;
28+ }
29+
30+ function now ( ) { return Date . now ( ) ; }
31+
32+ function emptyData ( ) {
33+ return { projects : { } , lastProjectId : null , lastPageId : null } ;
34+ }
35+
36+ // 严格在浏览器可用时读写 localStorage;非浏览器环境退化为内存态,便于测试
37+ let _mem = null ;
38+ function hasLS ( ) {
39+ try { return typeof localStorage !== "undefined" && ! ! localStorage ; } catch ( e ) { return false ; }
40+ }
41+ function load ( ) {
42+ if ( ! hasLS ( ) ) return ( _mem ? JSON . parse ( JSON . stringify ( _mem ) ) : emptyData ( ) ) ;
43+ try {
44+ const raw = localStorage . getItem ( KEY ) ;
45+ if ( ! raw ) return emptyData ( ) ;
46+ const d = JSON . parse ( raw ) ;
47+ if ( ! d || typeof d . projects !== "object" ) return emptyData ( ) ;
48+ return d ;
49+ } catch ( e ) { return emptyData ( ) ; }
50+ }
51+ function save ( d ) {
52+ if ( ! hasLS ( ) ) { _mem = JSON . parse ( JSON . stringify ( d ) ) ; return ; }
53+ try { localStorage . setItem ( KEY , JSON . stringify ( d ) ) ; } catch ( e ) { /* 容量超限忽略 */ }
54+ }
55+ function reset ( ) {
56+ if ( hasLS ( ) ) { try { localStorage . removeItem ( KEY ) ; } catch ( e ) { } }
57+ _mem = emptyData ( ) ;
58+ }
59+
60+ // ---- 项目 ----
61+ function getProject ( id ) {
62+ const d = load ( ) ;
63+ const p = d . projects && d . projects [ id ] ;
64+ return p ? JSON . parse ( JSON . stringify ( p ) ) : null ;
65+ }
66+ function listProjects ( ) {
67+ const d = load ( ) ;
68+ return Object . keys ( d . projects || { } ) . map ( function ( id ) {
69+ const p = d . projects [ id ] ;
70+ return { id : p . id , name : p . name , pageCount : Object . keys ( p . pages ) . length , createdAt : p . createdAt , updatedAt : p . updatedAt } ;
71+ } ) ;
72+ }
73+ function getProjectByPage ( pageId ) {
74+ const d = load ( ) ;
75+ for ( const id in d . projects ) {
76+ if ( Object . prototype . hasOwnProperty . call ( d . projects [ id ] . pages , pageId ) ) return JSON . parse ( JSON . stringify ( d . projects [ id ] ) ) ;
77+ }
78+ return null ;
79+ }
80+ // createProject(name, withDefaultPage=true) -> {id}
81+ function createProject ( name , withDefaultPage ) {
82+ const d = load ( ) ;
83+ const id = uuid ( ) ;
84+ const pages = { } ;
85+ if ( withDefaultPage !== false ) {
86+ const pid = uuid ( ) ;
87+ pages [ pid ] = { id : pid , name : "页面1" , code : "" , images : { } } ;
88+ d . lastPageId = pid ;
89+ }
90+ d . projects [ id ] = { id, name : safeName ( name , "项目" ) , createdAt : now ( ) , updatedAt : now ( ) , pages } ;
91+ d . lastProjectId = id ;
92+ save ( d ) ;
93+ return id ;
94+ }
95+ function renameProject ( id , name ) {
96+ const d = load ( ) ;
97+ if ( ! d . projects [ id ] ) return false ;
98+ d . projects [ id ] . name = safeName ( name , "项目" ) ;
99+ d . projects [ id ] . updatedAt = now ( ) ;
100+ save ( d ) ;
101+ return true ;
102+ }
103+ function deleteProject ( id ) {
104+ const d = load ( ) ;
105+ if ( ! d . projects [ id ] ) return false ;
106+ delete d . projects [ id ] ;
107+ if ( d . lastProjectId === id ) d . lastProjectId = null ;
108+ save ( d ) ;
109+ return true ;
110+ }
111+
112+ // ---- 页面 ----
113+ function createPage ( projId , name ) {
114+ const d = load ( ) ;
115+ if ( ! d . projects [ projId ] ) return null ;
116+ const pid = uuid ( ) ;
117+ d . projects [ projId ] . pages [ pid ] = { id : pid , name : safeName ( name , "页面1" ) , code : "" , images : { } } ;
118+ d . projects [ projId ] . updatedAt = now ( ) ;
119+ d . lastPageId = pid ;
120+ save ( d ) ;
121+ return pid ;
122+ }
123+ function getPage ( projId , pageId ) {
124+ const d = load ( ) ;
125+ const p = d . projects [ projId ] && d . projects [ projId ] . pages [ pageId ] ;
126+ return p ? JSON . parse ( JSON . stringify ( p ) ) : null ;
127+ }
128+ function renamePage ( projId , pageId , name ) {
129+ const d = load ( ) ;
130+ const p = d . projects [ projId ] && d . projects [ projId ] . pages [ pageId ] ;
131+ if ( ! p ) return false ;
132+ p . name = safeName ( name , "页面" ) ;
133+ d . projects [ projId ] . updatedAt = now ( ) ;
134+ save ( d ) ;
135+ return true ;
136+ }
137+ function deletePage ( projId , pageId ) {
138+ const d = load ( ) ;
139+ if ( ! d . projects [ projId ] || ! d . projects [ projId ] . pages [ pageId ] ) return false ;
140+ delete d . projects [ projId ] . pages [ pageId ] ;
141+ d . projects [ projId ] . updatedAt = now ( ) ;
142+ if ( d . lastPageId === pageId ) {
143+ const left = Object . keys ( d . projects [ projId ] . pages ) ;
144+ d . lastPageId = left . length ? left [ 0 ] : null ;
145+ }
146+ save ( d ) ;
147+ return true ;
148+ }
149+ // updatePageCode(projId, pageId, code)
150+ function updatePageCode ( projId , pageId , code ) {
151+ const d = load ( ) ;
152+ const p = d . projects [ projId ] && d . projects [ projId ] . pages [ pageId ] ;
153+ if ( ! p ) return false ;
154+ p . code = code ;
155+ d . projects [ projId ] . updatedAt = now ( ) ;
156+ save ( d ) ;
157+ return true ;
158+ }
159+ // setPageImage(projId, pageId, varName, dataURL) :绑定「it xxx p」的图片
160+ function setPageImage ( projId , pageId , varName , dataURL ) {
161+ const d = load ( ) ;
162+ const p = d . projects [ projId ] && d . projects [ projId ] . pages [ pageId ] ;
163+ if ( ! p ) return false ;
164+ if ( ! p . images ) p . images = { } ;
165+ if ( dataURL == null ) delete p . images [ varName ] ; else p . images [ varName ] = dataURL ;
166+ d . projects [ projId ] . updatedAt = now ( ) ;
167+ save ( d ) ;
168+ return true ;
169+ }
170+ function getPageImages ( projId , pageId ) {
171+ const d = load ( ) ;
172+ const p = d . projects [ projId ] && d . projects [ projId ] . pages [ pageId ] ;
173+ return ( p && p . images ) ? JSON . parse ( JSON . stringify ( p . images ) ) : { } ;
174+ }
175+
176+ // ---- 最近活动(首屏引导定位) ----
177+ function lastProjectId ( ) { return load ( ) . lastProjectId || null ; }
178+ function lastPageId ( ) { return load ( ) . lastPageId || null ; }
179+ function hasAnyProject ( ) { return Object . keys ( load ( ) . projects ) . length > 0 ; }
180+ function setActive ( projId , pageId ) {
181+ const d = load ( ) ;
182+ d . lastProjectId = projId || d . lastProjectId ;
183+ if ( pageId ) d . lastPageId = pageId ;
184+ save ( d ) ;
185+ }
186+
187+ // ---- .hc 导出 / 导入 ----
188+ // 导出单个项目(含全部页面与内嵌图片)为 .hc 文件文本
189+ function exportHcProject ( projId ) {
190+ const p = getProject ( projId ) ;
191+ if ( ! p ) return null ;
192+ return JSON . stringify ( {
193+ app : "HiCode" , language : "HIC" , format : "hc-project" , version : STORE_VERSION ,
194+ project : p
195+ } , null , 2 ) ;
196+ }
197+ function importHc ( text ) {
198+ // 返回 { ok, error?, projectId?, name? }
199+ try {
200+ const obj = JSON . parse ( text ) ;
201+ if ( ! obj || obj . app !== "HiCode" || ! obj . project ) return { ok : false , error : "不是有效的 .hc 文件(缺少 HiCode 标记)" } ;
202+ const proj = obj . project ;
203+ if ( ! proj || ! proj . name || typeof proj . pages !== "object" ) return { ok : false , error : ".hc 文件内容不完整" } ;
204+ const d = load ( ) ;
205+ // 同名覆盖策略:同名则新建一个带序号的名字,避免冲突
206+ const names = Object . keys ( d . projects ) . map ( function ( id ) { return d . projects [ id ] . name ; } ) ;
207+ let name = proj . name ;
208+ if ( names . indexOf ( name ) >= 0 ) {
209+ let n = 2 ;
210+ while ( names . indexOf ( name + "_" + n ) >= 0 ) n ++ ;
211+ name = name + "_" + n ;
212+ }
213+ const id = uuid ( ) ;
214+ d . projects [ id ] = { id, name, createdAt : now ( ) , updatedAt : now ( ) , pages : { } } ;
215+ Object . keys ( proj . pages ) . forEach ( function ( pid ) {
216+ const src = proj . pages [ pid ] ;
217+ const nid = uuid ( ) ;
218+ d . projects [ id ] . pages [ nid ] = {
219+ id : nid , name : src . name || "页面" , code : src . code || "" ,
220+ images : ( src . images && typeof src . images === "object" ) ? JSON . parse ( JSON . stringify ( src . images ) ) : { }
221+ } ;
222+ } ) ;
223+ d . lastProjectId = id ;
224+ save ( d ) ;
225+ return { ok : true , projectId : id , name } ;
226+ } catch ( e ) {
227+ return { ok : false , error : "解析失败:" + e . message } ;
228+ }
229+ }
230+
231+ function safeName ( name , fallback ) {
232+ const s = String ( name == null ? "" : name ) . trim ( ) ;
233+ return s ? s : fallback ;
234+ }
235+
236+ return {
237+ STORE_VERSION , KEY ,
238+ uuid, now, load, save, reset,
239+ getProject, listProjects, getProjectByPage, createProject, renameProject, deleteProject,
240+ createPage, getPage, renamePage, deletePage, updatePageCode, setPageImage, getPageImages,
241+ loadNewData : emptyData ,
242+ lastProjectId, lastPageId, hasAnyProject, setActive,
243+ exportHcProject, importHc
244+ } ;
245+ } ) ;
0 commit comments