@keyflow2/keyflow-kit-store 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,423 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>下载中心</title>
7
+ <link rel="stylesheet" href="../../../shared/ui/ime-panel.css" />
8
+ <link rel="stylesheet" href="../../../shared/ui/kit-shadcn.css" />
9
+ <link rel="stylesheet" href="./styles.css" />
10
+ </head>
11
+ <body>
12
+ <div class="store-shell" id="app" v-cloak :data-route="route" :data-tab="tab" :data-detail-tab="detailTab">
13
+ <header class="store-topbar" :hidden="route !== 'home' || searchOpen">
14
+ <div class="store-tabs" role="tablist" aria-label="store tabs">
15
+ <button class="store-tab" type="button" :aria-selected="tab === 'discover' ? 'true' : 'false'" @click="setTab('discover')">
16
+ 发现
17
+ </button>
18
+ <button class="store-tab" type="button" :aria-selected="tab === 'manage' ? 'true' : 'false'" @click="setTab('manage')">
19
+ 管理
20
+ <span class="tab-badge" v-if="updateCount > 0">{{ updateCount > 99 ? '99+' : updateCount }}</span>
21
+ </button>
22
+ </div>
23
+ <div class="store-actions" aria-label="actions">
24
+ <button class="icon-button" type="button" aria-label="search" @click="openSearch">
25
+ <svg viewBox="0 0 24 24" aria-hidden="true">
26
+ <path
27
+ d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Zm6.7 1.4-4.1-4.1"
28
+ fill="none"
29
+ stroke="currentColor"
30
+ stroke-width="2"
31
+ stroke-linecap="round"
32
+ />
33
+ </svg>
34
+ </button>
35
+ <button class="icon-button" type="button" aria-label="settings" @click="openSettings">
36
+ <svg viewBox="0 0 24 24" aria-hidden="true">
37
+ <path
38
+ d="M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4Z"
39
+ fill="none"
40
+ stroke="currentColor"
41
+ stroke-width="2"
42
+ />
43
+ <path
44
+ d="M19.4 12a7.6 7.6 0 0 0-.1-1.3l2-1.6-2-3.4-2.5 1a7.8 7.8 0 0 0-2.2-1.3l-.4-2.7H10l-.4 2.7a7.8 7.8 0 0 0-2.2 1.3l-2.5-1-2 3.4 2 1.6a7.6 7.6 0 0 0-.1 1.3c0 .4 0 .9.1 1.3l-2 1.6 2 3.4 2.5-1a7.8 7.8 0 0 0 2.2 1.3l.4 2.7h4.2l.4-2.7a7.8 7.8 0 0 0 2.2-1.3l2.5 1 2-3.4-2-1.6c.1-.4.1-.9.1-1.3Z"
45
+ fill="none"
46
+ stroke="currentColor"
47
+ stroke-width="1.6"
48
+ stroke-linejoin="round"
49
+ />
50
+ </svg>
51
+ </button>
52
+ <button class="icon-button" type="button" aria-label="import" @click="openImport">
53
+ <svg viewBox="0 0 24 24" aria-hidden="true">
54
+ <path
55
+ d="M12 5v14M5 12h14"
56
+ fill="none"
57
+ stroke="currentColor"
58
+ stroke-width="2"
59
+ stroke-linecap="round"
60
+ />
61
+ </svg>
62
+ </button>
63
+ <button class="icon-button" type="button" aria-label="collapse" @click="collapsePanel">
64
+ <svg viewBox="0 0 24 24" aria-hidden="true">
65
+ <path
66
+ d="m6 14 6-6 6 6"
67
+ fill="none"
68
+ stroke="currentColor"
69
+ stroke-width="2"
70
+ stroke-linecap="round"
71
+ stroke-linejoin="round"
72
+ />
73
+ </svg>
74
+ </button>
75
+ </div>
76
+ </header>
77
+
78
+ <header class="search-topbar" :hidden="route !== 'home' || !searchOpen">
79
+ <label class="search-field">
80
+ <span class="search-icon" aria-hidden="true">
81
+ <svg viewBox="0 0 24 24">
82
+ <path
83
+ d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Zm6.7 1.4-4.1-4.1"
84
+ fill="none"
85
+ stroke="currentColor"
86
+ stroke-width="2"
87
+ stroke-linecap="round"
88
+ />
89
+ </svg>
90
+ </span>
91
+ <input id="searchInput" type="search" placeholder="搜索名称或标签..." autocomplete="off" v-model="searchQuery" />
92
+ </label>
93
+ <button class="text-button" type="button" @click="closeSearch">取消</button>
94
+ </header>
95
+
96
+ <main class="store-main" id="homeView" :hidden="route !== 'home'">
97
+ <section class="store-list" aria-label="discover list" :hidden="tab !== 'discover' || searchOpen">
98
+ <div class="empty" v-if="discoverItems.length === 0">暂无可用目录,请先在设置里添加 Catalog 源</div>
99
+ <article class="kit-card" v-for="item in discoverItems" :key="item.kitId" :data-kit-id="item.kitId" @click="openDetail(item)">
100
+ <div class="ribbon" v-if="item.updateAvailable || item.featured" :class="item.updateAvailable ? 'ribbon--update' : ''">
101
+ {{ item.updateAvailable ? '更新' : '精选' }}
102
+ </div>
103
+ <div class="kit-icon" :class="item.iconClass">
104
+ <img v-if="item.iconUrl" :src="item.iconUrl" alt="" decoding="async" loading="lazy" />
105
+ <span v-else>{{ emojiFor(item.kitId) }}</span>
106
+ </div>
107
+ <div class="kit-info">
108
+ <h3 class="kit-title">{{ item.title }}</h3>
109
+ <div class="kit-subline">
110
+ <span class="tag-row" v-if="item.sub && item.sub.tags && item.sub.tags.length">
111
+ <span class="tag" v-for="tagText in item.sub.tags.slice(0, 2)" :key="tagText">{{ tagText }}</span>
112
+ </span>
113
+ <span class="tag-row" v-else-if="item.sub && item.sub.tag">
114
+ <span class="tag">{{ item.sub.tag }}</span>
115
+ </span>
116
+ <span class="subtext">{{ (item.sub && item.sub.desc) || '' }}</span>
117
+ </div>
118
+ </div>
119
+ <button
120
+ class="kit-action"
121
+ type="button"
122
+ :class="item.action && (item.action.kind === 'install' || item.action.kind === 'update') ? 'kit-action--get' : 'kit-action--open'"
123
+ @click.stop="handleCardAction(item)"
124
+ >
125
+ {{ (item.action && item.action.label) || '打开' }}
126
+ </button>
127
+ </article>
128
+ </section>
129
+
130
+ <section class="store-list" aria-label="manage list" :hidden="tab !== 'manage' || searchOpen">
131
+ <div class="empty" v-if="manageItems.length === 0">暂无已安装功能件</div>
132
+ <article class="kit-card" v-for="item in manageItems" :key="item.kitId" :data-kit-id="item.kitId" @click="openDetail(item)">
133
+ <div class="ribbon" v-if="item.updateAvailable || item.featured" :class="item.updateAvailable ? 'ribbon--update' : ''">
134
+ {{ item.updateAvailable ? '更新' : '精选' }}
135
+ </div>
136
+ <div class="kit-icon" :class="item.iconClass">
137
+ <img v-if="item.iconUrl" :src="item.iconUrl" alt="" decoding="async" loading="lazy" />
138
+ <span v-else>{{ emojiFor(item.kitId) }}</span>
139
+ </div>
140
+ <div class="kit-info">
141
+ <h3 class="kit-title">{{ item.title }}</h3>
142
+ <div class="kit-subline">
143
+ <span class="tag-row" v-if="item.sub && item.sub.tags && item.sub.tags.length">
144
+ <span class="tag" v-for="tagText in item.sub.tags.slice(0, 2)" :key="tagText">{{ tagText }}</span>
145
+ </span>
146
+ <span class="tag-row" v-else-if="item.sub && item.sub.tag">
147
+ <span class="tag">{{ item.sub.tag }}</span>
148
+ </span>
149
+ <span class="subtext">{{ (item.sub && item.sub.desc) || '' }}</span>
150
+ </div>
151
+ </div>
152
+ <button
153
+ class="kit-action"
154
+ type="button"
155
+ :class="item.action && (item.action.kind === 'install' || item.action.kind === 'update') ? 'kit-action--get' : 'kit-action--open'"
156
+ @click.stop="handleCardAction(item)"
157
+ >
158
+ {{ (item.action && item.action.label) || '打开' }}
159
+ </button>
160
+ </article>
161
+ </section>
162
+
163
+ <section class="store-list" aria-label="search list" :hidden="!searchOpen">
164
+ <div class="empty" v-if="searchItems.length === 0">{{ searchQuery.trim() ? '没有找到匹配结果' : '暂无内容' }}</div>
165
+ <article
166
+ class="kit-card"
167
+ v-for="item in searchItems"
168
+ :key="item.kind + ':' + item.kitId"
169
+ :data-kit-id="item.kitId"
170
+ @click="openDetail(item)"
171
+ >
172
+ <div class="ribbon" v-if="item.updateAvailable || item.featured" :class="item.updateAvailable ? 'ribbon--update' : ''">
173
+ {{ item.updateAvailable ? '更新' : '精选' }}
174
+ </div>
175
+ <div class="kit-icon" :class="item.iconClass">
176
+ <img v-if="item.iconUrl" :src="item.iconUrl" alt="" decoding="async" loading="lazy" />
177
+ <span v-else>{{ emojiFor(item.kitId) }}</span>
178
+ </div>
179
+ <div class="kit-info">
180
+ <h3 class="kit-title">{{ item.title }}</h3>
181
+ <div class="kit-subline">
182
+ <span class="tag-row" v-if="item.sub && item.sub.tags && item.sub.tags.length">
183
+ <span class="tag" v-for="tagText in item.sub.tags.slice(0, 2)" :key="tagText">{{ tagText }}</span>
184
+ </span>
185
+ <span class="tag-row" v-else-if="item.sub && item.sub.tag">
186
+ <span class="tag">{{ item.sub.tag }}</span>
187
+ </span>
188
+ <span class="subtext">{{ (item.sub && item.sub.desc) || '' }}</span>
189
+ </div>
190
+ </div>
191
+ <button
192
+ class="kit-action"
193
+ type="button"
194
+ :class="item.action && (item.action.kind === 'install' || item.action.kind === 'update') ? 'kit-action--get' : 'kit-action--open'"
195
+ @click.stop="handleCardAction(item)"
196
+ >
197
+ {{ (item.action && item.action.label) || '打开' }}
198
+ </button>
199
+ </article>
200
+ </section>
201
+ </main>
202
+
203
+ <footer class="store-pager" aria-label="pages" :hidden="route !== 'home'">
204
+ <button class="pager-dot" type="button" aria-label="发现" :aria-selected="tab === 'discover' ? 'true' : 'false'" @click="setTab('discover')"></button>
205
+ <button class="pager-dot" type="button" aria-label="管理" :aria-selected="tab === 'manage' ? 'true' : 'false'" @click="setTab('manage')"></button>
206
+ </footer>
207
+
208
+ <main class="store-main" :hidden="route !== 'settings'">
209
+ <header class="sub-topbar">
210
+ <button class="back-button" type="button" aria-label="back" @click="backFromSubView">
211
+ <svg viewBox="0 0 24 24" aria-hidden="true">
212
+ <path
213
+ d="m14 6-6 6 6 6"
214
+ fill="none"
215
+ stroke="currentColor"
216
+ stroke-width="2.2"
217
+ stroke-linecap="round"
218
+ stroke-linejoin="round"
219
+ />
220
+ </svg>
221
+ </button>
222
+ <div class="sub-title">设置中心</div>
223
+ <div class="sub-spacer"></div>
224
+ </header>
225
+
226
+ <section class="settings-section">
227
+ <div class="settings-label">Catalog 源管理</div>
228
+ <div class="settings-list">
229
+ <div class="settings-item" v-for="source in catalogSources" :key="source.url">
230
+ <div class="settings-url">{{ source.url }}</div>
231
+ <button class="settings-remove" type="button" @click="removeCatalogSource(source.url)">移除</button>
232
+ </div>
233
+ </div>
234
+
235
+ <div class="settings-row settings-row--add">
236
+ <input class="settings-input" placeholder="新 JSON 源" v-model="catalogAddUrl" />
237
+ <button class="pill pill--primary" type="button" @click="addCatalogSource">添加</button>
238
+ </div>
239
+
240
+ <button class="danger-link" type="button" @click="resetCatalogSources">恢复兜底默认设置</button>
241
+ </section>
242
+ </main>
243
+
244
+ <main class="store-main" :hidden="route !== 'import'">
245
+ <header class="sub-topbar sub-topbar--import">
246
+ <button class="text-button" type="button" @click="backFromSubView">取消</button>
247
+ <div class="sub-title">导入外部功能</div>
248
+ <button class="text-button text-button--disabled" type="button" disabled>完成</button>
249
+ </header>
250
+
251
+ <section class="import-section">
252
+ <input class="import-input" placeholder="npm:@keyflow2/keyflow-kit-wx-reply@0.2.10 或 https://...(.tgz/.zip)" v-model="importUrl" />
253
+ <button class="primary-wide" type="button" @click="installImportUrl">验证并安装</button>
254
+ <div class="or-divider">
255
+ <span>OR</span>
256
+ </div>
257
+ <button class="secondary-wide" type="button" @click="installFromZip">
258
+ <span class="plus" aria-hidden="true">+</span>
259
+ 从本地选择 ZIP
260
+ </button>
261
+ </section>
262
+ </main>
263
+
264
+ <main class="store-main" :hidden="route !== 'detail'">
265
+ <header class="detail-topbar">
266
+ <button class="detail-back" type="button" @click="backFromDetail">
267
+ <svg viewBox="0 0 24 24" aria-hidden="true">
268
+ <path
269
+ d="m14 6-6 6 6 6"
270
+ fill="none"
271
+ stroke="currentColor"
272
+ stroke-width="2.2"
273
+ stroke-linecap="round"
274
+ stroke-linejoin="round"
275
+ />
276
+ </svg>
277
+ 返回
278
+ </button>
279
+ <div class="detail-more" aria-hidden="true"></div>
280
+ </header>
281
+
282
+ <div class="detail-scroll">
283
+ <section class="detail-header" v-if="selectedItem">
284
+ <div class="detail-icon">
285
+ <img v-if="selectedItem.iconUrl" :src="selectedItem.iconUrl" alt="" decoding="async" loading="lazy" />
286
+ <span v-else>{{ emojiFor(selectedItem.kitId) }}</span>
287
+ </div>
288
+
289
+ <div class="detail-headline">
290
+ <div class="detail-title-row">
291
+ <h2 class="detail-title">{{ selectedItem.title }}</h2>
292
+ <span class="detail-badge" :style="{ visibility: selectedItem.featured ? 'visible' : 'hidden' }">精选</span>
293
+ </div>
294
+ <div class="detail-meta">{{ detailMeta }}</div>
295
+ </div>
296
+ <button
297
+ v-if="selectedItem.kind === 'package'"
298
+ class="kit-action kit-action--get"
299
+ type="button"
300
+ @click="installPackage(selectedItem.pkg)"
301
+ >
302
+ {{ selectedItem.action && selectedItem.action.kind === 'install' ? '获取' : selectedItem.action && selectedItem.action.kind === 'update' ? '更新' : '重新安装' }}
303
+ </button>
304
+ </section>
305
+
306
+ <nav class="segmented" aria-label="detail tabs">
307
+ <button class="segment" type="button" :aria-selected="detailTab === 'overview' ? 'true' : 'false'" @click="setDetailTab('overview')">
308
+ 概览
309
+ </button>
310
+ <button class="segment" type="button" :aria-selected="detailTab === 'permissions' ? 'true' : 'false'" @click="setDetailTab('permissions')">
311
+ 权限
312
+ </button>
313
+ <button class="segment" type="button" :aria-selected="detailTab === 'reviews' ? 'true' : 'false'" @click="setDetailTab('reviews')">
314
+ 评价
315
+ </button>
316
+ <button class="segment" type="button" :aria-selected="detailTab === 'security' ? 'true' : 'false'" @click="setDetailTab('security')">
317
+ 安全
318
+ </button>
319
+ </nav>
320
+
321
+ <section class="detail-body" v-if="selectedItem">
322
+ <section class="detail-card overview-card" v-if="detailTab === 'overview'">
323
+ <div class="overview-text">{{ overviewText }}</div>
324
+ <div>
325
+ <div class="version-row">
326
+ <div class="version-title">{{ versionTitle }}</div>
327
+ <div class="version-size">{{ versionSize }}</div>
328
+ </div>
329
+ <div class="version-note">{{ selectedItem.kitId }}</div>
330
+ </div>
331
+ </section>
332
+
333
+ <section class="detail-card" v-if="detailTab === 'permissions'">
334
+ <div class="list-header">
335
+ <span>声明权限</span>
336
+ <span class="risk" v-if="permissionRisky">
337
+ <svg viewBox="0 0 24 24" aria-hidden="true">
338
+ <path
339
+ d="M12 3 1.8 20.2c-.5.9.1 1.8 1.2 1.8h18c1.1 0 1.7-.9 1.2-1.8L12 3Z"
340
+ fill="none"
341
+ stroke="#FF3B30"
342
+ stroke-width="2"
343
+ />
344
+ <path d="M12 9v5" stroke="#FF3B30" stroke-width="2" stroke-linecap="round" />
345
+ <path d="M12 17.2h.01" stroke="#FF3B30" stroke-width="3" stroke-linecap="round" />
346
+ </svg>
347
+ 高风险
348
+ </span>
349
+ </div>
350
+
351
+ <div class="list-row" v-for="perm in detailPermissions" :key="perm">
352
+ <div>{{ permissionLabel(perm) }}</div>
353
+ <div class="row-dot"></div>
354
+ </div>
355
+ </section>
356
+
357
+ <section class="detail-card" v-if="detailTab === 'reviews'">
358
+ <div class="rating-head">
359
+ <div class="rating-left">
360
+ <div class="rating-score">4.8</div>
361
+ <div class="rating-meta">满分 5.0 (1250人)</div>
362
+ </div>
363
+ <button class="rating-action" type="button" @click="showToast('写评价')">写评价</button>
364
+ </div>
365
+
366
+ <div class="review-item" v-for="review in reviews" :key="review.name">
367
+ <div>
368
+ <div class="review-name">{{ review.name }}</div>
369
+ <div class="review-text">{{ review.text }}</div>
370
+ </div>
371
+ <div class="stars">{{ stars(review.stars) }}</div>
372
+ </div>
373
+ </section>
374
+
375
+ <section class="detail-card" v-if="detailTab === 'security'">
376
+ <div
377
+ class="link-row"
378
+ v-for="link in securityLinks"
379
+ :key="link.label"
380
+ :class="{ 'link-row--danger': link.kind === 'danger' }"
381
+ @click="showToast(link.label)"
382
+ >
383
+ <div>{{ link.label }}</div>
384
+ <span class="link-arrow" aria-hidden="true">
385
+ <svg v-if="link.icon === 'upright'" viewBox="0 0 24 24">
386
+ <path d="M7 17 17 7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" />
387
+ <path
388
+ d="M10 7h7v7"
389
+ fill="none"
390
+ stroke="currentColor"
391
+ stroke-width="2.4"
392
+ stroke-linecap="round"
393
+ stroke-linejoin="round"
394
+ />
395
+ </svg>
396
+ <svg v-else viewBox="0 0 24 24">
397
+ <path d="M6 12h12" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" />
398
+ <path
399
+ d="m14 7 5 5-5 5"
400
+ fill="none"
401
+ stroke="currentColor"
402
+ stroke-width="2.4"
403
+ stroke-linecap="round"
404
+ stroke-linejoin="round"
405
+ />
406
+ </svg>
407
+ </span>
408
+ </div>
409
+
410
+ <button class="uninstall" type="button" @click="uninstallSelected">卸载组件</button>
411
+ </section>
412
+ </section>
413
+ </div>
414
+ </main>
415
+
416
+ <div class="toast" id="toast" :hidden="!toast.open">{{ toast.text }}</div>
417
+ </div>
418
+
419
+ <script src="../../../../function-kit-runtime-sdk/dist/function-kit-runtime.js"></script>
420
+ <script src="../../../shared/vendor/petite-vue/petite-vue.iife.js"></script>
421
+ <script src="./main.js"></script>
422
+ </body>
423
+ </html>