@xlui/xux-ui 0.2.1 → 1.1.0
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.
- package/dist/assets/areas.worker-Ci--LHMH.js +1 -0
- package/dist/browser-Bfcp93e9.js +8 -0
- package/dist/browser-Dj1SWzn2.mjs +1456 -0
- package/dist/index.css +1 -1
- package/dist/index.js +32 -32
- package/dist/index.mjs +18021 -2703
- package/package.json +70 -63
- package/src/components/Button/index.vue +6 -6
- package/src/components/Card/index.vue +44 -11
- package/src/components/DateTimePicker/index.vue +121 -39
- package/src/components/Qrcode/index.vue +390 -0
- package/src/components/RegionCascader/areas.worker.ts +196 -0
- package/src/components/RegionCascader/data/china-areas.full.json +14464 -0
- package/src/components/RegionCascader/data/init.mjs +377 -0
- package/src/components/RegionCascader/index.vue +870 -0
- package/src/components/Select/index.vue +25 -22
- package/src/components/SpecialEffects/fireworks.vue +134 -0
- package/src/components/SpecialEffects/glow.vue +377 -0
- package/src/components/Switch/index.vue +335 -0
- package/src/index.ts +9 -0
- package/LICENSE +0 -194
@@ -0,0 +1,377 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
/**
|
3
|
+
* Build China Cascader JSON (Advanced: 4/5 levels + Incremental + Pinyin)
|
4
|
+
* ----------------------------------------------------------------------
|
5
|
+
* 输出:Cascader 结构 [{ value, label, pinyin, abbr, aliases, search, children: [...] }]
|
6
|
+
*
|
7
|
+
* 依赖:axios, glob, pinyin
|
8
|
+
*
|
9
|
+
* CLI:
|
10
|
+
* --levels=3|4|5
|
11
|
+
* --out=china-areas.json
|
12
|
+
* --prev=previous.json # 增量基准(可选)
|
13
|
+
* --pca=./pca-code.json # 本地 pca-code.json(可选,传了就不走网络)
|
14
|
+
* --townDir=./dist/town # 4级目录(必传于 levels>=4)
|
15
|
+
* --villageDir=./dist/village # 5级目录(必传于 levels>=5)
|
16
|
+
* --excludeHKTW # 过滤港澳台
|
17
|
+
* --gzip # 额外输出 .gz
|
18
|
+
* --timeout=20000 --retries=3
|
19
|
+
*/
|
20
|
+
|
21
|
+
import fs from 'node:fs'
|
22
|
+
import zlib from 'node:zlib'
|
23
|
+
import axios from 'axios'
|
24
|
+
import { glob } from 'glob'
|
25
|
+
import pinyin from 'pinyin'
|
26
|
+
|
27
|
+
// ---------------- CLI ----------------
|
28
|
+
const args = process.argv.slice(2)
|
29
|
+
const getArg = (name, def = undefined) => {
|
30
|
+
const hit = args.find(a => a === `--${name}` || a.startsWith(`--${name}=`))
|
31
|
+
if (!hit) return def
|
32
|
+
const parts = hit.split('=')
|
33
|
+
return parts.length > 1 ? parts.slice(1).join('=') : true
|
34
|
+
}
|
35
|
+
|
36
|
+
const LEVELS = Number(getArg('levels', 3))
|
37
|
+
const OUT = getArg('out', 'china-areas.full.json')
|
38
|
+
const PREV = getArg('prev')
|
39
|
+
const PCA_LOCAL = getArg('pca') // 本地 pca-code.json
|
40
|
+
const TOWN_DIR = getArg('townDir')
|
41
|
+
const VILLAGE_DIR = getArg('villageDir')
|
42
|
+
const INCLUDE_HKTW = getArg('includeHKTW', true) && !getArg('excludeHKTW', false)
|
43
|
+
const TIMEOUT = Number(getArg('timeout', 20000))
|
44
|
+
const RETRIES = Number(getArg('retries', 3))
|
45
|
+
const DO_GZIP = !!getArg('gzip', false)
|
46
|
+
|
47
|
+
const PCA_URL = 'https://raw.githubusercontent.com/modood/Administrative-divisions-of-China/master/dist/pca-code.json'
|
48
|
+
|
49
|
+
// --------------- Utils ---------------
|
50
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
51
|
+
|
52
|
+
async function httpGetJson(url, { timeout = TIMEOUT, retries = RETRIES } = {}) {
|
53
|
+
let lastErr
|
54
|
+
for (let i = 0; i <= retries; i++) {
|
55
|
+
try {
|
56
|
+
const { data } = await axios.get(url, { timeout, responseType: 'json' })
|
57
|
+
return data
|
58
|
+
} catch (err) {
|
59
|
+
lastErr = err
|
60
|
+
const delay = 500 * (i + 1)
|
61
|
+
console.warn(`⚠️ GET ${url} 失败(第 ${i + 1}/${retries + 1} 次),${delay}ms 后重试…`)
|
62
|
+
await sleep(delay)
|
63
|
+
}
|
64
|
+
}
|
65
|
+
throw lastErr
|
66
|
+
}
|
67
|
+
|
68
|
+
function readJson(file) {
|
69
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'))
|
70
|
+
}
|
71
|
+
|
72
|
+
function sortTree(arr) {
|
73
|
+
return arr
|
74
|
+
.map(n => ({
|
75
|
+
...n,
|
76
|
+
children: Array.isArray(n.children) ? sortTree(n.children) : undefined
|
77
|
+
}))
|
78
|
+
.sort((a, b) => a.label.localeCompare(b.label, 'zh-Hans-CN'))
|
79
|
+
}
|
80
|
+
|
81
|
+
function dedupe(arr) {
|
82
|
+
const seen = new Set()
|
83
|
+
const out = []
|
84
|
+
for (const n of arr) {
|
85
|
+
const k = String(n.value)
|
86
|
+
if (!seen.has(k)) {
|
87
|
+
seen.add(k)
|
88
|
+
out.push(n)
|
89
|
+
}
|
90
|
+
}
|
91
|
+
return out
|
92
|
+
}
|
93
|
+
|
94
|
+
function filterHKTW(provinces) {
|
95
|
+
return provinces.filter(p => !/^71|^81|^82/.test(String(p.value)))
|
96
|
+
}
|
97
|
+
|
98
|
+
async function readAllJsonFiles(dir) {
|
99
|
+
const files = await glob('**/*.json', { cwd: dir, absolute: true })
|
100
|
+
const results = []
|
101
|
+
for (const fp of files) {
|
102
|
+
try {
|
103
|
+
const raw = fs.readFileSync(fp, 'utf8')
|
104
|
+
const data = JSON.parse(raw)
|
105
|
+
results.push({ file: fp, data })
|
106
|
+
} catch (e) {
|
107
|
+
console.warn(`⚠️ 读取 ${fp} 失败:${e.message}`)
|
108
|
+
}
|
109
|
+
}
|
110
|
+
return results
|
111
|
+
}
|
112
|
+
|
113
|
+
function bucketByPrefix(list, prefixLen) {
|
114
|
+
const map = new Map()
|
115
|
+
for (const item of list) {
|
116
|
+
const code = String(item.code || item.value)
|
117
|
+
const key = code.slice(0, prefixLen)
|
118
|
+
if (!map.has(key)) map.set(key, [])
|
119
|
+
map.get(key).push(item)
|
120
|
+
}
|
121
|
+
return map
|
122
|
+
}
|
123
|
+
|
124
|
+
// ------ Pinyin / Aliases Enhance ------
|
125
|
+
function toPinyinWords(han) {
|
126
|
+
const arr = pinyin(han, { style: pinyin.STYLE_NORMAL })
|
127
|
+
return arr.map(syls => syls[0] || '')
|
128
|
+
}
|
129
|
+
|
130
|
+
function buildEnhanceFields(label) {
|
131
|
+
const words = toPinyinWords(label)
|
132
|
+
const full = words.join(' ') // "bei jing shi"
|
133
|
+
const abbr = words.map(w => w[0] || '').join('') // "bjs"
|
134
|
+
|
135
|
+
const weakTerms = ['市辖区','市辖县','自治区直辖县级行政区划','省直辖县级行政区划']
|
136
|
+
const aliases = []
|
137
|
+
// 简化别名:去掉末尾常见后缀(便于检索)
|
138
|
+
const simplified = label.replace(/[市县区盟旗]$/u, '')
|
139
|
+
if (simplified && simplified !== label && !weakTerms.includes(label)) {
|
140
|
+
aliases.push(simplified)
|
141
|
+
}
|
142
|
+
|
143
|
+
const search = [label, full, abbr, ...aliases].join(' ').trim()
|
144
|
+
return { pinyin: full, abbr, aliases, search }
|
145
|
+
}
|
146
|
+
|
147
|
+
// -------- Incremental Merge ----------
|
148
|
+
function indexOldTree(oldRootArr) {
|
149
|
+
const map = new Map()
|
150
|
+
const dfs = (node) => {
|
151
|
+
map.set(String(node.value), node)
|
152
|
+
if (Array.isArray(node.children)) {
|
153
|
+
for (const c of node.children) dfs(c)
|
154
|
+
}
|
155
|
+
}
|
156
|
+
for (const n of oldRootArr || []) dfs(n)
|
157
|
+
return map
|
158
|
+
}
|
159
|
+
|
160
|
+
function isStructurallySame(newNode, oldNode) {
|
161
|
+
if (!oldNode) return false
|
162
|
+
if (newNode.label !== oldNode.label) return false
|
163
|
+
|
164
|
+
const nc = newNode.children || []
|
165
|
+
const oc = oldNode.children || []
|
166
|
+
if (nc.length !== oc.length) return false
|
167
|
+
|
168
|
+
const setNew = new Set(nc.map(n => String(n.value)))
|
169
|
+
for (const c of oc) {
|
170
|
+
if (!setNew.has(String(c.value))) return false
|
171
|
+
}
|
172
|
+
return true
|
173
|
+
}
|
174
|
+
|
175
|
+
function mergeNodeWithOld(newNode, oldIndex) {
|
176
|
+
const oldNode = oldIndex.get(String(newNode.value))
|
177
|
+
let merged = { ...newNode }
|
178
|
+
|
179
|
+
if (isStructurallySame(newNode, oldNode)) {
|
180
|
+
merged = {
|
181
|
+
...merged,
|
182
|
+
pinyin: oldNode.pinyin,
|
183
|
+
abbr: oldNode.abbr,
|
184
|
+
aliases: oldNode.aliases,
|
185
|
+
search: oldNode.search
|
186
|
+
}
|
187
|
+
} else {
|
188
|
+
Object.assign(merged, buildEnhanceFields(merged.label))
|
189
|
+
}
|
190
|
+
|
191
|
+
if (Array.isArray(newNode.children) && newNode.children.length) {
|
192
|
+
merged.children = newNode.children.map(c => mergeNodeWithOld(c, oldIndex))
|
193
|
+
}
|
194
|
+
|
195
|
+
return merged
|
196
|
+
}
|
197
|
+
|
198
|
+
// ---------------- Main ----------------
|
199
|
+
;(async function main() {
|
200
|
+
console.log(`\n== 构建中国级联(4/5 级 + 增量 + 拼音)==`)
|
201
|
+
console.log(`- levels: ${LEVELS}`)
|
202
|
+
console.log(`- out: ${OUT}`)
|
203
|
+
if (PREV) console.log(`- prev: ${PREV}`)
|
204
|
+
if (PCA_LOCAL) console.log(`- pca(local): ${PCA_LOCAL}`)
|
205
|
+
if (TOWN_DIR) console.log(`- townDir: ${TOWN_DIR}`)
|
206
|
+
if (VILLAGE_DIR) console.log(`- villageDir: ${VILLAGE_DIR}`)
|
207
|
+
console.log(`- includeHKTW: ${INCLUDE_HKTW}`)
|
208
|
+
console.log(`- gzip: ${DO_GZIP}\n`)
|
209
|
+
|
210
|
+
// 旧树索引
|
211
|
+
let oldIndex = new Map()
|
212
|
+
if (PREV && fs.existsSync(PREV)) {
|
213
|
+
try {
|
214
|
+
const old = readJson(PREV)
|
215
|
+
oldIndex = indexOldTree(old)
|
216
|
+
console.log(`🗂️ 载入旧文件 ${PREV},节点数:${oldIndex.size}`)
|
217
|
+
} catch (e) {
|
218
|
+
console.warn(`⚠️ 读取 prev 失败:${e.message}`)
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
// 1) 3级:省/市/区
|
223
|
+
let pca
|
224
|
+
if (PCA_LOCAL) {
|
225
|
+
console.log(`📖 使用本地 pca-code.json:${PCA_LOCAL}`)
|
226
|
+
pca = readJson(PCA_LOCAL)
|
227
|
+
} else {
|
228
|
+
console.log(`📥 下载省/市/区:${PCA_URL}`)
|
229
|
+
pca = await httpGetJson(PCA_URL)
|
230
|
+
}
|
231
|
+
|
232
|
+
const mapNode3 = (node) => ({
|
233
|
+
value: String(node.code),
|
234
|
+
label: String(node.name),
|
235
|
+
children: Array.isArray(node.children) ? node.children.map(mapNode3) : undefined
|
236
|
+
})
|
237
|
+
|
238
|
+
let cascader = sortTree(pca.map(mapNode3))
|
239
|
+
cascader = dedupe(cascader)
|
240
|
+
if (!INCLUDE_HKTW) cascader = filterHKTW(cascader)
|
241
|
+
|
242
|
+
// 2) 4级:乡镇(9位)
|
243
|
+
if (LEVELS >= 4) {
|
244
|
+
if (!TOWN_DIR) {
|
245
|
+
console.error('❌ levels>=4 需要 --townDir 指定乡镇目录')
|
246
|
+
process.exit(2)
|
247
|
+
}
|
248
|
+
console.log(`🧩 拼接乡镇/街道(9 位码) from ${TOWN_DIR}`)
|
249
|
+
const townFiles = await readAllJsonFiles(TOWN_DIR)
|
250
|
+
|
251
|
+
const towns = []
|
252
|
+
for (const { data } of townFiles) {
|
253
|
+
if (Array.isArray(data)) {
|
254
|
+
for (const t of data) {
|
255
|
+
const code = String(t.code ?? t.value ?? '')
|
256
|
+
const name = String(t.name ?? t.label ?? '')
|
257
|
+
if (/^\d{9}$/.test(code)) towns.push({ code, name })
|
258
|
+
}
|
259
|
+
} else if (data && typeof data === 'object') {
|
260
|
+
for (const key of Object.keys(data)) {
|
261
|
+
const arr = data[key]
|
262
|
+
if (Array.isArray(arr)) {
|
263
|
+
for (const t of arr) {
|
264
|
+
const code = String(t.code ?? t.value ?? '')
|
265
|
+
const name = String(t.name ?? t.label ?? '')
|
266
|
+
if (/^\d{9}$/.test(code)) towns.push({ code, name })
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
const townBuckets = bucketByPrefix(towns, 6)
|
274
|
+
let attached = 0
|
275
|
+
for (const prov of cascader) {
|
276
|
+
if (!prov.children) continue
|
277
|
+
for (const city of prov.children) {
|
278
|
+
if (!city.children) continue
|
279
|
+
for (const dist of city.children) {
|
280
|
+
const k6 = String(dist.value).slice(0, 6)
|
281
|
+
const arr = townBuckets.get(k6)
|
282
|
+
if (arr && arr.length) {
|
283
|
+
const children = arr.map(t => ({ value: t.code, label: t.name }))
|
284
|
+
dist.children = sortTree(dedupe([...(dist.children || []), ...children]))
|
285
|
+
attached += arr.length
|
286
|
+
}
|
287
|
+
}
|
288
|
+
}
|
289
|
+
}
|
290
|
+
console.log(`✅ 乡镇/街道已挂接:${attached} 条`)
|
291
|
+
}
|
292
|
+
|
293
|
+
// 3) 5级:村(12位)
|
294
|
+
if (LEVELS >= 5) {
|
295
|
+
if (!VILLAGE_DIR) {
|
296
|
+
console.error('❌ levels>=5 需要 --villageDir 指定村目录')
|
297
|
+
process.exit(3)
|
298
|
+
}
|
299
|
+
console.log(`🧩 拼接村/居(12 位码) from ${VILLAGE_DIR}`)
|
300
|
+
const villageFiles = await readAllJsonFiles(VILLAGE_DIR)
|
301
|
+
|
302
|
+
const villages = []
|
303
|
+
for (const { data } of villageFiles) {
|
304
|
+
if (Array.isArray(data)) {
|
305
|
+
for (const v of data) {
|
306
|
+
const code = String(v.code ?? v.value ?? '')
|
307
|
+
const name = String(v.name ?? v.label ?? '')
|
308
|
+
if (/^\d{12}$/.test(code)) villages.push({ code, name })
|
309
|
+
}
|
310
|
+
} else if (data && typeof data === 'object') {
|
311
|
+
for (const key of Object.keys(data)) {
|
312
|
+
const arr = data[key]
|
313
|
+
if (Array.isArray(arr)) {
|
314
|
+
for (const v of arr) {
|
315
|
+
const code = String(v.code ?? v.value ?? '')
|
316
|
+
const name = String(v.name ?? v.label ?? '')
|
317
|
+
if (/^\d{12}$/.test(code)) villages.push({ code, name })
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
const villageBuckets = bucketByPrefix(villages, 9)
|
325
|
+
let attached = 0
|
326
|
+
|
327
|
+
for (const prov of cascader) {
|
328
|
+
if (!prov.children) continue
|
329
|
+
for (const city of prov.children) {
|
330
|
+
if (!city.children) continue
|
331
|
+
for (const dist of city.children) {
|
332
|
+
if (!dist.children) continue
|
333
|
+
for (const town of dist.children) {
|
334
|
+
const k9 = String(town.value).slice(0, 9)
|
335
|
+
const arr = villageBuckets.get(k9)
|
336
|
+
if (arr && arr.length) {
|
337
|
+
const children = arr.map(v => ({ value: v.code, label: v.name }))
|
338
|
+
town.children = sortTree(dedupe([...(town.children || []), ...children]))
|
339
|
+
attached += arr.length
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
console.log(`✅ 村/居 已挂接:${attached} 条`)
|
346
|
+
}
|
347
|
+
|
348
|
+
// 4) 增量合并 + 拼音增强
|
349
|
+
const oldIndex = PREV && fs.existsSync(PREV) ? indexOldTree(readJson(PREV)) : new Map()
|
350
|
+
const enhanceRecursively = (node) => {
|
351
|
+
// 保底:如果没有增量参照,也要生成拼音增强
|
352
|
+
if (!('pinyin' in node)) Object.assign(node, buildEnhanceFields(node.label))
|
353
|
+
if (Array.isArray(node.children)) node.children.forEach(enhanceRecursively)
|
354
|
+
}
|
355
|
+
|
356
|
+
let merged = cascader.map(n => mergeNodeWithOld(n, oldIndex))
|
357
|
+
// 对没有 prev 的场景,确保增强字段存在
|
358
|
+
merged.forEach(enhanceRecursively)
|
359
|
+
|
360
|
+
// 5) 写出
|
361
|
+
merged = sortTree(merged)
|
362
|
+
fs.writeFileSync(OUT, JSON.stringify(merged, null, 2), 'utf8')
|
363
|
+
const sizeKB = (fs.statSync(OUT).size / 1024).toFixed(1)
|
364
|
+
console.log(`\n🎉 完成!输出 -> ${OUT}(约 ${sizeKB} KB)`)
|
365
|
+
|
366
|
+
if (DO_GZIP) {
|
367
|
+
const gz = zlib.gzipSync(fs.readFileSync(OUT))
|
368
|
+
fs.writeFileSync(`${OUT}.gz`, gz)
|
369
|
+
const gk = (gz.length / 1024).toFixed(1)
|
370
|
+
console.log(`🗜️ 同步输出 -> ${OUT}.gz(约 ${gk} KB)`)
|
371
|
+
}
|
372
|
+
console.log('')
|
373
|
+
})().catch(err => {
|
374
|
+
console.error('❌ 发生错误:', err?.message || err)
|
375
|
+
process.exit(1)
|
376
|
+
})
|
377
|
+
|