cloud-web-corejs 1.0.54-dev.679 → 1.0.54-dev.680

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cloud-web-corejs",
3
3
  "private": false,
4
- "version": "1.0.54-dev.679",
4
+ "version": "1.0.54-dev.680",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve",
7
7
  "lint": "eslint --ext .js,.vue src",
@@ -33,7 +33,7 @@
33
33
  v-for="item in searchList"
34
34
  :key="'search-' + item.id"
35
35
  class="base-area-result-item"
36
- :class="{ on: value === item.id }"
36
+ :class="{ on: isSearchItemActive(item) }"
37
37
  @mousedown.prevent
38
38
  @click="selectSearchItem(item)"
39
39
  >
@@ -48,46 +48,57 @@
48
48
  </div>
49
49
  </transition>
50
50
  </div>
51
- <div class="base-area-tabs">
52
- <span
53
- v-for="(tab, index) in tabs"
54
- :key="index"
55
- class="base-area-tab"
56
- :class="{
57
- active: activeLevel === index + 1,
58
- selected: !!tab.selected,
59
- disabled: !isTabEnabled(index + 1),
60
- }"
61
- @click="switchLevel(index + 1)"
62
- >{{ getTabLabel(tab, index) }}</span>
63
- </div>
64
- <div class="base-area-grid" v-loading="browseLoading">
65
- <div
66
- v-for="item in pageItems"
67
- :key="item.id"
68
- class="base-area-item"
69
- :class="{ on: isSelected(item) }"
70
- @click="selectItem(item)"
71
- >
72
- <span>{{ item.name }}</span>
51
+ <div class="base-area-path">
52
+ <div class="base-area-tabs">
53
+ <template v-for="(tab, index) in tabs">
54
+ <span
55
+ v-if="index > 0"
56
+ :key="'sep-' + index"
57
+ class="base-area-path-sep"
58
+ >/</span>
59
+ <span
60
+ :key="'tab-' + index"
61
+ class="base-area-tab"
62
+ :class="{
63
+ active: activeLevel === index + 1,
64
+ selected: !!tab.selected,
65
+ disabled: !isTabEnabled(index + 1),
66
+ }"
67
+ @click="switchLevel(index + 1)"
68
+ >{{ getTabLabel(tab) }}</span>
69
+ </template>
73
70
  </div>
74
- <div v-if="!browseLoading && !pageItems.length" class="base-area-empty">
75
- {{ emptyText }}
71
+ </div>
72
+ <div class="base-area-grid-wrap">
73
+ <div class="base-area-grid" v-loading="browseLoading">
74
+ <div
75
+ v-for="item in levelItems"
76
+ :key="item.id"
77
+ class="base-area-item"
78
+ :class="{ on: isSelected(item) }"
79
+ @click="selectItem(item)"
80
+ >
81
+ <span>{{ item.name }}</span>
82
+ </div>
83
+ <div v-if="!browseLoading && !levelItems.length" class="base-area-empty">
84
+ {{ emptyText }}
85
+ </div>
76
86
  </div>
77
87
  </div>
78
- <div class="base-area-pagination">
88
+ <div class="base-area-level-nav">
79
89
  <el-button
80
90
  size="mini"
81
- :disabled="currentPage <= 1"
82
- @click="prevPage"
91
+ :disabled="!canPrevLevel"
92
+ @click="prevLevel"
83
93
  >
84
94
  <i class="el-icon-arrow-left"></i>
85
95
  </el-button>
96
+ <span class="base-area-level-label">{{ currentLevelLabel }}</span>
86
97
  <el-button
87
98
  size="mini"
88
99
  type="primary"
89
- :disabled="!hasNextPage"
90
- @click="nextPage"
100
+ :disabled="!canNextLevel"
101
+ @click="nextLevel"
91
102
  >
92
103
  <i class="el-icon-arrow-right"></i>
93
104
  </el-button>
@@ -121,7 +132,6 @@
121
132
 
122
133
  <script>
123
134
  const LEVEL_LABELS = ["省份", "城市", "区县", "乡镇"];
124
- const PAGE_SIZE = 28;
125
135
  const SEARCH_DEBOUNCE = 300;
126
136
 
127
137
  export default {
@@ -175,6 +185,11 @@ export default {
175
185
  type: Number,
176
186
  default: 630,
177
187
  },
188
+ /** path: 逗号分隔各级 id,如 1,2,3,4;leaf: 仅末级 id */
189
+ valueFormat: {
190
+ type: String,
191
+ default: "path",
192
+ },
178
193
  },
179
194
  data() {
180
195
  return {
@@ -188,8 +203,9 @@ export default {
188
203
  searchLoading: false,
189
204
  searchDropdownVisible: false,
190
205
  referenceFocused: false,
191
- currentPage: 1,
192
206
  searchTimer: null,
207
+ syncToken: 0,
208
+ skipValueSync: false,
193
209
  };
194
210
  },
195
211
  computed: {
@@ -200,14 +216,16 @@ export default {
200
216
  return this.searchDropdownVisible && this.hasSearchKeyword;
201
217
  },
202
218
  displayText() {
219
+ const pathLabel = this.selectedPath.length
220
+ ? this.buildPathLabel(this.selectedPath)
221
+ : "";
222
+ if (this.popoverVisible && pathLabel) {
223
+ return pathLabel;
224
+ }
203
225
  if (this.labelValue) {
204
226
  return this.labelValue;
205
227
  }
206
- if (this.selectedPath.length) {
207
- const last = this.selectedPath[this.selectedPath.length - 1];
208
- return last.fullName || last.name || "";
209
- }
210
- return "";
228
+ return pathLabel || "";
211
229
  },
212
230
  tabs() {
213
231
  const tabs = [];
@@ -220,13 +238,6 @@ export default {
220
238
  }
221
239
  return tabs;
222
240
  },
223
- pageItems() {
224
- const start = (this.currentPage - 1) * PAGE_SIZE;
225
- return this.levelItems.slice(start, start + PAGE_SIZE);
226
- },
227
- hasNextPage() {
228
- return this.currentPage * PAGE_SIZE < this.levelItems.length;
229
- },
230
241
  showClearIcon() {
231
242
  return (
232
243
  this.clearable &&
@@ -235,11 +246,33 @@ export default {
235
246
  !!this.displayText
236
247
  );
237
248
  },
249
+ canPrevLevel() {
250
+ return this.activeLevel > 1;
251
+ },
252
+ canNextLevel() {
253
+ return (
254
+ this.activeLevel < this.maxLevel &&
255
+ this.isTabEnabled(this.activeLevel + 1)
256
+ );
257
+ },
258
+ currentLevelLabel() {
259
+ return LEVEL_LABELS[this.activeLevel - 1] || `第${this.activeLevel}级`;
260
+ },
238
261
  },
239
262
  watch: {
240
- value(val) {
241
- if (!val) {
263
+ value: {
264
+ immediate: true,
265
+ handler() {
266
+ this.syncFromValue();
267
+ },
268
+ },
269
+ labelValue(val) {
270
+ if (!val && !this.value) {
242
271
  this.resetBrowseState(false);
272
+ return;
273
+ }
274
+ if (this.value && !this.isValueSynced(this.parseValueIds(this.value))) {
275
+ this.syncFromValue();
243
276
  }
244
277
  },
245
278
  },
@@ -247,18 +280,103 @@ export default {
247
280
  clearTimeout(this.searchTimer);
248
281
  },
249
282
  methods: {
250
- getTabLabel(tab, index) {
283
+ isSearchItemActive(item) {
284
+ if (!item || this.value == null || this.value === "") {
285
+ return false;
286
+ }
287
+ const ids = this.parseValueIds(this.value);
288
+ if (!ids.length) {
289
+ return false;
290
+ }
291
+ return ids[ids.length - 1] == this.getAreaId(item);
292
+ },
293
+ parseValueIds(val) {
294
+ if (val == null || val === "") {
295
+ return [];
296
+ }
297
+ const str = String(val).trim();
298
+ if (this.valueFormat === "path" && str.includes(",")) {
299
+ return str.split(",").map((part) => part.trim()).filter((part) => part !== "");
300
+ }
301
+ return [str];
302
+ },
303
+ isPathValue(val) {
304
+ return (
305
+ this.valueFormat === "path" &&
306
+ val != null &&
307
+ String(val).includes(",")
308
+ );
309
+ },
310
+ formatPathValue(path) {
311
+ if (!path || !path.length) {
312
+ return null;
313
+ }
314
+ const ids = path
315
+ .map((item) => this.getAreaId(item))
316
+ .filter((id) => id != null && id !== "");
317
+ if (!ids.length) {
318
+ return null;
319
+ }
320
+ if (this.valueFormat === "leaf") {
321
+ return ids[ids.length - 1];
322
+ }
323
+ return ids.join(",");
324
+ },
325
+ getAreaId(item) {
326
+ if (!item) {
327
+ return null;
328
+ }
329
+ if (item.id != null && item.id !== "") {
330
+ return item.id;
331
+ }
332
+ if (item.value != null && item.value !== "") {
333
+ return item.value;
334
+ }
335
+ if (item.areaId != null && item.areaId !== "") {
336
+ return item.areaId;
337
+ }
338
+ return null;
339
+ },
340
+ isValueSynced(ids) {
341
+ if (!ids.length || this.selectedPath.length !== ids.length) {
342
+ return false;
343
+ }
344
+ return ids.every((id, index) => this.getAreaId(this.selectedPath[index]) == id);
345
+ },
346
+ buildPathLabel(path) {
347
+ if (!path || !path.length) {
348
+ return "";
349
+ }
350
+ const last = path[path.length - 1];
351
+ if (last && last.fullName) {
352
+ return last.fullName;
353
+ }
354
+ return path
355
+ .map((item) => item.name || "")
356
+ .filter((name) => !!name)
357
+ .join("");
358
+ },
359
+ emitResolvedLabelIfNeeded() {
360
+ if (this.popoverVisible || this.labelValue) {
361
+ return;
362
+ }
363
+ const label = this.buildPathLabel(this.selectedPath);
364
+ if (label) {
365
+ this.$emit("update:labelValue", label);
366
+ }
367
+ },
368
+ cancelPendingSync() {
369
+ this.syncToken += 1;
370
+ },
371
+ getTabLabel(tab) {
251
372
  if (tab.selected) {
252
- if (index === this.selectedPath.length - 1 && tab.selected.fullName) {
253
- return tab.selected.fullName;
254
- }
255
- return tab.selected.name;
373
+ return tab.selected.name || tab.label;
256
374
  }
257
375
  return tab.label;
258
376
  },
259
377
  isSelected(item) {
260
378
  const current = this.selectedPath[this.activeLevel - 1];
261
- return current && current.id === item.id;
379
+ return current && this.getAreaId(current) === this.getAreaId(item);
262
380
  },
263
381
  isTabEnabled(level) {
264
382
  return level === 1 || this.selectedPath.length >= level - 1;
@@ -267,10 +385,298 @@ export default {
267
385
  this.$nextTick(() => {
268
386
  this.$refs.searchInputRef && this.$refs.searchInputRef.focus();
269
387
  });
388
+ this.initBrowseStateFromValue();
389
+ },
390
+ syncFromValue() {
391
+ if (this.skipValueSync) {
392
+ return;
393
+ }
394
+ const val = this.value;
395
+ if (!val) {
396
+ this.resetBrowseState(false);
397
+ return;
398
+ }
399
+ const ids = this.parseValueIds(val);
400
+ if (this.isValueSynced(ids)) {
401
+ return;
402
+ }
403
+ if (this.isPathValue(val)) {
404
+ this.syncBrowseStateFromPathIds(ids);
405
+ return;
406
+ }
407
+ this.syncBrowseStateFromArea({
408
+ id: ids[0],
409
+ fullName: this.labelValue || undefined,
410
+ });
411
+ },
412
+ initBrowseStateFromValue() {
413
+ if (!this.value) {
414
+ if (!this.levelItems.length) {
415
+ this.loadLevelItems(this.getParentId());
416
+ }
417
+ return;
418
+ }
419
+ const ids = this.parseValueIds(this.value);
420
+ if (!this.isValueSynced(ids)) {
421
+ this.syncFromValue();
422
+ return;
423
+ }
270
424
  if (!this.levelItems.length) {
271
425
  this.loadLevelItems(this.getParentId());
272
426
  }
273
427
  },
428
+ getAreaRequestData(extra = {}) {
429
+ return {
430
+ enabled: true,
431
+ locale: this.locale || null,
432
+ ...extra,
433
+ };
434
+ },
435
+ normalizeAreaRows(res) {
436
+ const objx = res && res.objx;
437
+ if (Array.isArray(objx)) {
438
+ return objx;
439
+ }
440
+ if (objx && Array.isArray(objx.records)) {
441
+ return objx.records;
442
+ }
443
+ return [];
444
+ },
445
+ requestAreaList(extra = {}) {
446
+ return new Promise((resolve) => {
447
+ this.$http({
448
+ url: USER_PREFIX + "/area/list",
449
+ method: "post",
450
+ data: this.getAreaRequestData(extra),
451
+ success: (res) => resolve(this.normalizeAreaRows(res)),
452
+ error: () => resolve([]),
453
+ });
454
+ });
455
+ },
456
+ fetchChildren(parentId) {
457
+ return new Promise((resolve) => {
458
+ this.$http({
459
+ url: USER_PREFIX + "/area/getChildren",
460
+ method: "post",
461
+ data: this.getAreaRequestData({ id: parentId }),
462
+ success: (res) => resolve(this.normalizeAreaRows(res)),
463
+ error: () => resolve([]),
464
+ });
465
+ });
466
+ },
467
+ getParentIdFromRow(row) {
468
+ if (!row) return null;
469
+ if (row.parentId != null && row.parentId !== "") {
470
+ return row.parentId;
471
+ }
472
+ const parent = row.parent;
473
+ if (parent == null || parent === undefined || parent === "") {
474
+ return null;
475
+ }
476
+ if (typeof parent === "object") {
477
+ return parent.id != null ? parent.id : null;
478
+ }
479
+ return parent;
480
+ },
481
+ fetchAreaById(id) {
482
+ if (id == null || id === "") {
483
+ return Promise.resolve(null);
484
+ }
485
+ return this.requestAreaList({ id }).then(
486
+ (rows) => rows.find((row) => this.getAreaId(row) == id) || null
487
+ );
488
+ },
489
+ isAreaObject(node) {
490
+ return node && typeof node === "object" && node.id != null;
491
+ },
492
+ buildAreaPath(item) {
493
+ if (item.pathList && Array.isArray(item.pathList) && item.pathList.length > 1) {
494
+ if (this.isAreaObject(item.pathList[0])) {
495
+ return item.pathList.slice(0, this.maxLevel);
496
+ }
497
+ }
498
+ if (item.areaPath && Array.isArray(item.areaPath) && item.areaPath.length > 1) {
499
+ if (this.isAreaObject(item.areaPath[0])) {
500
+ return item.areaPath.slice(0, this.maxLevel);
501
+ }
502
+ }
503
+ return null;
504
+ },
505
+ matchAreaByPrefix(fullName, prefix, children) {
506
+ const sorted = [...children].sort(
507
+ (a, b) => (b.name || "").length - (a.name || "").length
508
+ );
509
+ for (let i = 0; i < sorted.length; i++) {
510
+ const child = sorted[i];
511
+ const name = child.name || "";
512
+ if (!name) {
513
+ continue;
514
+ }
515
+ if (fullName.startsWith(prefix + name)) {
516
+ return child;
517
+ }
518
+ }
519
+ return null;
520
+ },
521
+ async resolvePathByFullName(item) {
522
+ const fullName = (item.fullName || "").trim();
523
+ if (!fullName) {
524
+ return null;
525
+ }
526
+ const path = [];
527
+ let parentId = 0;
528
+ let prefix = "";
529
+ for (let level = 0; level < this.maxLevel; level++) {
530
+ const children = await this.fetchChildren(parentId);
531
+ if (!children.length) {
532
+ break;
533
+ }
534
+ const matched = this.matchAreaByPrefix(fullName, prefix, children);
535
+ if (!matched) {
536
+ break;
537
+ }
538
+ path.push(matched);
539
+ prefix += matched.name || "";
540
+ if (matched.id == item.id) {
541
+ return path;
542
+ }
543
+ parentId = matched.id;
544
+ }
545
+ if (path.length && path[path.length - 1].id != item.id) {
546
+ const siblings = await this.fetchChildren(path[path.length - 1].id);
547
+ const leaf = siblings.find((row) => row.id == item.id);
548
+ if (leaf) {
549
+ path.push(leaf);
550
+ } else if (item.name && fullName.endsWith(item.name)) {
551
+ path.push(item);
552
+ }
553
+ }
554
+ return path.length ? path.slice(0, this.maxLevel) : null;
555
+ },
556
+ async resolvePathByParentChain(item) {
557
+ const chain = [];
558
+ let current = item;
559
+ let safety = 0;
560
+ while (current && safety < this.maxLevel + 5) {
561
+ safety += 1;
562
+ chain.unshift(current);
563
+ const parentId = this.getParentIdFromRow(current);
564
+ if (!parentId) {
565
+ break;
566
+ }
567
+ const parent = await this.fetchAreaById(parentId);
568
+ if (!parent || parent.id != parentId) {
569
+ break;
570
+ }
571
+ current = parent;
572
+ }
573
+ return chain.length ? chain.slice(0, this.maxLevel) : null;
574
+ },
575
+ async resolveAreaPath(item) {
576
+ const presetPath = this.buildAreaPath(item);
577
+ if (presetPath && presetPath.length) {
578
+ return presetPath;
579
+ }
580
+ const fullNamePath = await this.resolvePathByFullName(item);
581
+ if (fullNamePath && fullNamePath.length) {
582
+ return fullNamePath;
583
+ }
584
+ const parentPath = await this.resolvePathByParentChain(item);
585
+ if (parentPath && parentPath.length) {
586
+ return parentPath;
587
+ }
588
+ return [item];
589
+ },
590
+ async syncBrowseStateFromArea(item) {
591
+ const token = ++this.syncToken;
592
+ let seed = item || {};
593
+ if (
594
+ seed.id != null &&
595
+ !seed.fullName &&
596
+ !seed.name &&
597
+ !this.getParentIdFromRow(seed)
598
+ ) {
599
+ const detail = await this.fetchAreaById(seed.id);
600
+ if (detail) {
601
+ seed = detail;
602
+ }
603
+ } else if (seed.id != null && !seed.fullName && this.labelValue) {
604
+ seed = { ...seed, fullName: this.labelValue };
605
+ }
606
+ let path = await this.resolveAreaPath(seed);
607
+ if (token !== this.syncToken) {
608
+ return;
609
+ }
610
+ if (!path.length) {
611
+ path = [seed];
612
+ }
613
+ const last = path[path.length - 1];
614
+ if (last && last.id != seed.id && seed.id != null) {
615
+ const siblings = await this.fetchChildren(
616
+ path.length > 1 ? path[path.length - 2].id : 0
617
+ );
618
+ const exact = siblings.find((row) => row.id == seed.id);
619
+ if (exact) {
620
+ path = path.slice(0, -1).concat(exact);
621
+ } else {
622
+ path = path.concat(seed);
623
+ }
624
+ }
625
+ if (token !== this.syncToken) {
626
+ return;
627
+ }
628
+ path = path.slice(0, this.maxLevel);
629
+ this.selectedPath = path;
630
+ const pathLen = path.length;
631
+ if (pathLen >= this.maxLevel) {
632
+ this.activeLevel = this.maxLevel;
633
+ } else {
634
+ this.activeLevel = pathLen;
635
+ }
636
+ this.loadLevelItems(this.getParentId());
637
+ this.emitResolvedLabelIfNeeded();
638
+ },
639
+ async resolvePathByIds(ids) {
640
+ const path = [];
641
+ let parentId = 0;
642
+ for (let i = 0; i < ids.length && i < this.maxLevel; i++) {
643
+ const targetId = ids[i];
644
+ const children = await this.fetchChildren(parentId);
645
+ let matched = children.find((row) => this.getAreaId(row) == targetId);
646
+ if (matched) {
647
+ path.push(matched);
648
+ parentId = matched.id;
649
+ continue;
650
+ }
651
+ const fetched = await this.fetchAreaById(targetId);
652
+ if (fetched) {
653
+ path.push(fetched);
654
+ parentId = fetched.id;
655
+ } else {
656
+ path.push({ id: targetId });
657
+ break;
658
+ }
659
+ }
660
+ return path;
661
+ },
662
+ async syncBrowseStateFromPathIds(ids) {
663
+ const token = ++this.syncToken;
664
+ const path = await this.resolvePathByIds(ids);
665
+ if (token !== this.syncToken) {
666
+ return;
667
+ }
668
+ if (!path.length) {
669
+ if (!this.levelItems.length) {
670
+ this.loadLevelItems(0);
671
+ }
672
+ return;
673
+ }
674
+ this.selectedPath = path.slice(0, this.maxLevel);
675
+ const pathLen = this.selectedPath.length;
676
+ this.activeLevel = pathLen >= this.maxLevel ? this.maxLevel : pathLen;
677
+ this.loadLevelItems(this.getParentId());
678
+ this.emitResolvedLabelIfNeeded();
679
+ },
274
680
  handlePopoverHide() {
275
681
  this.searchKeyword = "";
276
682
  this.searchList = [];
@@ -294,24 +700,9 @@ export default {
294
700
  },
295
701
  doSearch(keyword) {
296
702
  this.searchLoading = true;
297
- this.$http({
298
- url: USER_PREFIX + "/area/listPage",
299
- method: "post",
300
- data: {
301
- name: keyword,
302
- enabled: true,
303
- locale: this.locale || null,
304
- pageNum: 1,
305
- pageSize: 200,
306
- },
307
- success: (res) => {
308
- this.searchList = res.objx?.records || res.objx || [];
309
- this.searchLoading = false;
310
- },
311
- error: () => {
312
- this.searchList = [];
313
- this.searchLoading = false;
314
- },
703
+ this.requestAreaList({ name: keyword }).then((rows) => {
704
+ this.searchList = rows;
705
+ this.searchLoading = false;
315
706
  });
316
707
  },
317
708
  getParentId() {
@@ -319,47 +710,49 @@ export default {
319
710
  return 0;
320
711
  }
321
712
  const parent = this.selectedPath[this.activeLevel - 2];
322
- return parent ? parent.id : 0;
713
+ return parent ? this.getAreaId(parent) : 0;
323
714
  },
324
715
  loadLevelItems(parentId) {
325
716
  this.browseLoading = true;
326
- this.$http({
327
- url: USER_PREFIX + "/area/getChildren",
328
- method: "post",
329
- data: {
330
- id: parentId,
331
- locale: this.locale || null,
332
- enabled: true,
333
- },
334
- success: (res) => {
335
- this.levelItems = res.objx || [];
336
- this.browseLoading = false;
337
- },
338
- error: () => {
339
- this.levelItems = [];
340
- this.browseLoading = false;
341
- },
717
+ this.fetchChildren(parentId).then((rows) => {
718
+ this.levelItems = rows;
719
+ this.browseLoading = false;
342
720
  });
343
721
  },
344
722
  switchLevel(level) {
345
723
  if (level > this.maxLevel || !this.isTabEnabled(level)) {
346
724
  return;
347
725
  }
726
+ this.cancelPendingSync();
348
727
  this.activeLevel = level;
349
- this.currentPage = 1;
350
- if (level <= this.selectedPath.length) {
351
- this.selectedPath = this.selectedPath.slice(0, level - 1);
352
- }
353
728
  this.loadLevelItems(this.getParentId());
354
729
  },
730
+ prevLevel() {
731
+ if (!this.canPrevLevel) {
732
+ return;
733
+ }
734
+ this.switchLevel(this.activeLevel - 1);
735
+ },
736
+ nextLevel() {
737
+ if (!this.canNextLevel) {
738
+ return;
739
+ }
740
+ this.switchLevel(this.activeLevel + 1);
741
+ },
355
742
  selectSearchItem(item) {
356
- this.applySelection(item);
357
- this.popoverVisible = false;
743
+ this.cancelPendingSync();
744
+ this.syncBrowseStateFromArea(item).then(() => {
745
+ this.searchKeyword = "";
746
+ this.searchList = [];
747
+ this.searchDropdownVisible = false;
748
+ this.applySelection(item);
749
+ });
358
750
  },
359
751
  selectItem(item) {
360
752
  if (!this.isTabEnabled(this.activeLevel)) {
361
753
  return;
362
754
  }
755
+ this.cancelPendingSync();
363
756
  const level = this.activeLevel;
364
757
  this.selectedPath = this.selectedPath.slice(0, level - 1);
365
758
  this.selectedPath.push(item);
@@ -369,14 +762,27 @@ export default {
369
762
  return;
370
763
  }
371
764
  this.activeLevel = level + 1;
372
- this.currentPage = 1;
373
- this.loadLevelItems(item.id);
765
+ this.loadLevelItems(this.getAreaId(item));
374
766
  },
375
767
  applySelection(item) {
376
- const label = item.fullName || item.name || "";
377
- this.$emit("input", item.id);
768
+ const path = this.selectedPath.length ? this.selectedPath : [item];
769
+ const last = path[path.length - 1] || item;
770
+ const label = this.buildPathLabel(path);
771
+ let formattedValue = this.formatPathValue(path);
772
+ if (formattedValue == null) {
773
+ const leafId = this.getAreaId(last);
774
+ if (leafId != null) {
775
+ formattedValue =
776
+ this.valueFormat === "leaf" ? leafId : String(leafId);
777
+ }
778
+ }
779
+ this.skipValueSync = true;
780
+ this.$emit("input", formattedValue);
378
781
  this.$emit("update:labelValue", label);
379
- this.$emit("change", item);
782
+ this.$emit("change", { value: formattedValue, row: last, label });
783
+ this.$nextTick(() => {
784
+ this.skipValueSync = false;
785
+ });
380
786
  },
381
787
  handleClear() {
382
788
  this.resetBrowseState(true);
@@ -387,22 +793,11 @@ export default {
387
793
  this.searchList = [];
388
794
  this.selectedPath = [];
389
795
  this.activeLevel = 1;
390
- this.currentPage = 1;
391
796
  this.levelItems = [];
392
797
  if (emitEvent) {
393
798
  this.$emit("input", null);
394
799
  this.$emit("update:labelValue", null);
395
- this.$emit("change", null);
396
- }
397
- },
398
- prevPage() {
399
- if (this.currentPage > 1) {
400
- this.currentPage -= 1;
401
- }
402
- },
403
- nextPage() {
404
- if (this.hasNextPage) {
405
- this.currentPage += 1;
800
+ this.$emit("change", { value: null, row: null });
406
801
  }
407
802
  },
408
803
  },
@@ -500,55 +895,86 @@ export default {
500
895
  }
501
896
  }
502
897
 
898
+ .base-area-path {
899
+ padding: 6px 10px;
900
+ margin-bottom: 10px;
901
+ background: #f5f7fa;
902
+ border: 1px solid #ebeef5;
903
+ border-radius: 4px;
904
+ }
905
+
503
906
  .base-area-tabs {
504
907
  display: flex;
505
908
  flex-wrap: wrap;
909
+ align-items: center;
506
910
  width: 100%;
507
- margin-bottom: 8px;
508
- gap: 6px;
911
+ gap: 0;
912
+ }
913
+
914
+ .base-area-path-sep {
915
+ flex-shrink: 0;
916
+ padding: 0 2px;
917
+ font-size: 12px;
918
+ color: #c0c4cc;
919
+ line-height: 24px;
920
+ user-select: none;
509
921
  }
510
922
 
511
923
  .base-area-tab {
512
- flex: 1;
924
+ flex-shrink: 1;
513
925
  min-width: 0;
514
- display: inline-block;
515
926
  max-width: 100%;
516
- padding: 0 10px;
517
- height: 28px;
518
- line-height: 28px;
519
- border: 1px solid #eee;
520
- border-radius: 4px;
927
+ padding: 0 4px;
928
+ height: 24px;
929
+ line-height: 24px;
930
+ border: none;
931
+ border-bottom: 2px solid transparent;
932
+ border-radius: 0;
933
+ background: transparent;
521
934
  font-size: 12px;
522
- color: #606266;
935
+ color: #909399;
523
936
  cursor: pointer;
524
937
  overflow: hidden;
525
938
  text-overflow: ellipsis;
526
939
  white-space: nowrap;
940
+ transition: color 0.15s, border-color 0.15s;
527
941
 
528
942
  &.active {
529
- background: $baseColor;
530
- border-color: $baseColor;
531
- color: #fff;
943
+ color: $baseColor;
944
+ border-bottom-color: $baseColor;
945
+ font-weight: 500;
946
+ background: transparent;
532
947
  }
533
948
 
534
949
  &.selected:not(.active) {
535
- color: $baseColor;
536
- border-color: rgba($baseColor, 0.35);
950
+ color: #303133;
537
951
  }
538
952
 
539
953
  &.disabled {
540
954
  color: #c0c4cc;
541
955
  cursor: not-allowed;
542
- background: #f5f7fa;
543
- border-color: #eee;
956
+ background: transparent;
544
957
  pointer-events: none;
545
958
  }
959
+
960
+ &:hover:not(.disabled):not(.active) {
961
+ color: $baseColor;
962
+ }
963
+ }
964
+
965
+ .base-area-grid-wrap {
966
+ padding: 8px;
967
+ background: #fff;
968
+ border: 1px solid #eee;
969
+ border-radius: 8px;
546
970
  }
547
971
 
548
972
  .base-area-grid {
549
973
  display: flex;
550
974
  flex-wrap: wrap;
551
975
  min-height: 120px;
976
+ max-height: 294px;
977
+ overflow-y: auto;
552
978
  margin: 0 -4px;
553
979
  }
554
980
 
@@ -586,10 +1012,20 @@ export default {
586
1012
  font-size: 12px;
587
1013
  }
588
1014
 
589
- .base-area-pagination {
1015
+ .base-area-level-nav {
590
1016
  display: flex;
1017
+ align-items: center;
591
1018
  justify-content: space-between;
592
1019
  margin-top: 4px;
1020
+
1021
+ .base-area-level-label {
1022
+ flex: 1;
1023
+ text-align: center;
1024
+ font-size: 12px;
1025
+ color: #606266;
1026
+ line-height: 28px;
1027
+ user-select: none;
1028
+ }
593
1029
  }
594
1030
  </style>
595
1031
 
@@ -657,8 +1093,7 @@ export default {
657
1093
  .base-area-tab.disabled {
658
1094
  color: #c0c4cc;
659
1095
  cursor: not-allowed;
660
- background: #f5f7fa;
661
- border-color: #eee;
1096
+ background: transparent;
662
1097
  pointer-events: none;
663
1098
  }
664
1099
  }
@@ -11,19 +11,32 @@
11
11
  :sub-form-col-index="subFormColIndex"
12
12
  :sub-form-row-id="subFormRowId"
13
13
  >
14
+ <el-input
15
+ v-if="designState"
16
+ class="full-width-input design-area-preview"
17
+ :style="widgetStyle"
18
+ readonly
19
+ disabled
20
+ :size="field.options.size || 'small'"
21
+ :placeholder="getI18nLabel(field.options.placeholder || '请选择地区')"
22
+ />
14
23
  <baseArea
24
+ v-else
15
25
  v-show="!isReadMode"
16
26
  class="full-width-input"
17
27
  :style="widgetStyle"
18
- v-model="fieldModel"
19
- :label-value.sync="areaLabel"
28
+ :value="fieldModel"
29
+ :label-value="areaNameLabel"
30
+ @update:labelValue="handleAreaLabelUpdate"
20
31
  :max-level="maxLevel"
21
32
  :locale="field.options.locale"
22
33
  :disabled="field.options.disabled"
23
34
  :readonly="field.options.readonly"
24
35
  :clearable="field.options.clearable"
36
+ value-format="path"
25
37
  :placeholder="getI18nLabel(field.options.placeholder || '请选择地区')"
26
38
  :size="field.options.size || 'small'"
39
+ @input="handleAreaInput"
27
40
  @change="handleAreaChange"
28
41
  />
29
42
  <template v-if="isReadMode">
@@ -38,6 +51,7 @@ import emitter from "../../../../../components/xform/utils/emitter";
38
51
  import i18n from "../../../../../components/xform/utils/i18n";
39
52
  import fieldMixin from "../../../../../components/xform/form-designer/form-widget/field-widget/fieldMixin";
40
53
  import baseArea from "@base/components/baseArea/index.vue";
54
+ import { deepClone } from "../../../../../components/xform/utils/util";
41
55
 
42
56
  export default {
43
57
  name: "area-select-widget",
@@ -94,7 +108,7 @@ export default {
94
108
  return 4;
95
109
  },
96
110
  readModeText() {
97
- return this.areaLabel || this.currentAreaNameValue || "--";
111
+ return this.areaNameLabel || "--";
98
112
  },
99
113
  widgetStyle() {
100
114
  const widgetWidth = this.field.options.widgetWidth;
@@ -109,13 +123,12 @@ export default {
109
123
  }
110
124
  return this.currentData[this.areaNameField] ?? null;
111
125
  },
112
- },
113
- watch: {
114
- currentAreaNameValue: {
115
- immediate: true,
116
- handler(val) {
117
- this.areaLabel = val;
118
- },
126
+ areaNameLabel() {
127
+ const formLabel = this.currentAreaNameValue;
128
+ if (formLabel != null && formLabel !== "") {
129
+ return formLabel;
130
+ }
131
+ return this.areaLabel;
119
132
  },
120
133
  },
121
134
  created() {
@@ -132,19 +145,86 @@ export default {
132
145
  this.unregisterFromRefList();
133
146
  },
134
147
  methods: {
148
+ handleAreaLabelUpdate(label) {
149
+ if (this.designState) {
150
+ return;
151
+ }
152
+ this.areaLabel = label;
153
+ this.syncAreaName(label);
154
+ },
135
155
  syncAreaName(label) {
136
156
  if (this.areaNameField) {
137
157
  this.$set(this.currentData, this.areaNameField, label);
138
158
  }
139
159
  },
140
- handleAreaChange(row) {
141
- const label = row ? row.fullName || row.name || null : null;
142
- this.areaLabel = label;
143
- this.syncAreaName(label);
144
- this.handleChangeEvent(this.fieldModel);
160
+ syncAreaLabelFromForm() {
161
+ if (this.areaNameField) {
162
+ this.areaLabel = this.currentData[this.areaNameField] ?? null;
163
+ }
164
+ },
165
+ commitAreaValue(value, label) {
166
+ this.fieldModel = value;
167
+ this.$set(this.currentData, this.fieldKeyName, value);
168
+ this.syncUpdateFormModel(value);
169
+ if (label !== undefined) {
170
+ this.areaLabel = label;
171
+ this.syncAreaName(label);
172
+ }
173
+ },
174
+ handleAreaInput(val) {
175
+ if (this.designState) {
176
+ return;
177
+ }
178
+ this.commitAreaValue(val);
179
+ },
180
+ handleAreaChange(payload) {
181
+ if (this.designState) {
182
+ return;
183
+ }
184
+ const row =
185
+ payload && Object.prototype.hasOwnProperty.call(payload, "row")
186
+ ? payload.row
187
+ : payload;
188
+ const value =
189
+ payload && Object.prototype.hasOwnProperty.call(payload, "value")
190
+ ? payload.value
191
+ : this.fieldModel;
192
+ const label =
193
+ payload && payload.label != null
194
+ ? payload.label
195
+ : row
196
+ ? row.fullName || row.name || null
197
+ : null;
198
+ this.commitAreaValue(value, label);
199
+ this.handleChangeEvent(value);
200
+ },
201
+ getValue() {
202
+ const fieldKey = this.fieldKeyName;
203
+ if (this.fieldModel != null && this.fieldModel !== "") {
204
+ return this.fieldModel;
205
+ }
206
+ const formVal = this.currentData[fieldKey];
207
+ if (formVal != null && formVal !== "") {
208
+ return formVal;
209
+ }
210
+ return this.fieldModel;
211
+ },
212
+ setValue(val) {
213
+ if (!this.field.formItemFlag) {
214
+ return;
215
+ }
216
+ const oldVal = deepClone(this.fieldModel);
217
+ const value = val ?? null;
218
+ this.commitAreaValue(value);
219
+ this.syncAreaLabelFromForm();
220
+ this.emitFieldDataChange(value, oldVal);
221
+ },
222
+ fieldModelLabel() {
223
+ return this.areaNameLabel || this.getValue() || "";
145
224
  },
146
225
  },
147
226
  };
227
+
148
228
  </script>
149
229
 
150
230
  <style lang="scss" scoped>
@@ -264,6 +264,10 @@ modules = {
264
264
  ? this.formConfig.customClass
265
265
  : "";
266
266
  },
267
+
268
+ formModel() {
269
+ return this.formDataModel;
270
+ },
267
271
  },
268
272
  watch: {
269
273
  //
@@ -34,14 +34,6 @@ export function getDetailRowKey(row, index = 0) {
34
34
  }
35
35
 
36
36
  export function sortDetailRowsByZdEn(rows = []) {
37
- /* return [...rows].sort((a, b) => {
38
- const aVal = String(a?.zdEn ?? "");
39
- const bVal = String(b?.zdEn ?? "");
40
- return aVal.localeCompare(bVal, "zh-CN", {
41
- numeric: true,
42
- sensitivity: "base",
43
- });
44
- }); */
45
37
  return [...rows];
46
38
  }
47
39