jianghu-ui 1.0.5 → 1.0.7
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/jianghu-ui.css +186 -115
- package/dist/jianghu-ui.js +1 -1
- package/package.json +1 -1
- package/src/components/JhAddressSelect/JhAddressSelect.vue +6 -6
- package/src/components/JhDrawer/JhDrawer.stories.js +6 -6
- package/src/components/JhDrawerForm/JhDrawerForm.vue +1 -1
- package/src/components/JhForm/JhForm.stories.js +114 -95
- package/src/components/JhForm/JhForm.vue +148 -21
- package/src/components/JhFormFields/JhFormFields.vue +33 -14
- package/src/components/JhModal/JhModal.stories.js +6 -6
- package/src/components/JhModal/JhModal.vue +1 -1
- package/src/components/JhModalForm/JhModalForm.vue +1 -1
- package/src/components/JhTable/JhTable.stories.js +192 -154
- package/src/components/JhTable/JhTable.vue +122 -26
- package/src/style/globalCSSVuetifyV4.css +1 -2
- package/src/components/JhAddressSelect/JhAddressSelect.md +0 -267
- package/src/components/JhCard/JhCard.md +0 -246
- package/src/components/JhCheckCard/JhCheckCard.md +0 -245
- package/src/components/JhConfirmDialog/JhConfirmDialog.md +0 -70
- package/src/components/JhDateRangePicker/JhDateRangePicker.md +0 -56
- package/src/components/JhDescriptions/JhDescriptions.md +0 -724
- package/src/components/JhDraggable/JhDraggable.md +0 -66
- package/src/components/JhDrawer/JhDrawer.md +0 -68
- package/src/components/JhDrawerForm/JhDrawerForm.md +0 -69
- package/src/components/JhEditableTable/JhEditableTable.md +0 -507
- package/src/components/JhFileInput/JhFileInput.md +0 -56
- package/src/components/JhForm/JhForm.md +0 -676
- package/src/components/JhFormFields/JhFormFields.md +0 -647
- package/src/components/JhFormList/JhFormList.md +0 -303
- package/src/components/JhJsonEditor/JhJsonEditor.md +0 -54
- package/src/components/JhLayout/JhLayout.md +0 -580
- package/src/components/JhList/JhList.md +0 -441
- package/src/components/JhMarkdownEditor/JhMarkdownEditor.md +0 -56
- package/src/components/JhMask/JhMask.md +0 -62
- package/src/components/JhMenu/JhMenu.md +0 -85
- package/src/components/JhModal/JhModal.md +0 -68
- package/src/components/JhModalForm/JhModalForm.md +0 -69
- package/src/components/JhPageContainer/JhPageContainer.md +0 -409
- package/src/components/JhQueryFilter/JhQueryFilter.md +0 -77
- package/src/components/JhScene/JhScene.md +0 -64
- package/src/components/JhStatisticCard/JhStatisticCard.md +0 -363
- package/src/components/JhStepsForm/JhStepsForm.md +0 -666
- package/src/components/JhTable/JhTable.md +0 -730
- package/src/components/JhTableAttachment/JhTableAttachment.md +0 -70
- package/src/components/JhToast/JhToast.md +0 -67
- package/src/components/JhTreeSelect/JhTreeSelect.md +0 -82
- package/src/components/JhWaterMark/JhWaterMark.md +0 -190
- package/src/components/README.md +0 -52
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
/>
|
|
66
66
|
<template v-else>
|
|
67
67
|
<v-icon small class="mr-2" color="primary">mdi-checkbox-marked-circle</v-icon>
|
|
68
|
-
<
|
|
69
|
-
<v-
|
|
68
|
+
<div>已选择 <strong class="primary--text">{{ selectedItems.length }}</strong> 项</div>
|
|
69
|
+
<v-spacer></v-spacer>
|
|
70
|
+
<a text x-small class="ml-2" @click="clearSelection">清空</a>
|
|
70
71
|
</template>
|
|
71
72
|
</div>
|
|
72
73
|
<div v-if="hasAlertActionsContent" class="jh-pro-table-alert-actions">
|
|
@@ -254,7 +255,7 @@
|
|
|
254
255
|
:show-select="showSelectComputed"
|
|
255
256
|
:single-select="singleSelectComputed"
|
|
256
257
|
:value="selectedItems"
|
|
257
|
-
:item-key="
|
|
258
|
+
:item-key="computedRowKey"
|
|
258
259
|
:dense="tableDense"
|
|
259
260
|
:multi-sort="multiSort"
|
|
260
261
|
:must-sort="mustSort"
|
|
@@ -277,7 +278,7 @@
|
|
|
277
278
|
<slot :name="`header.${header.value}`" :header="h">{{ h.text || h.title }}</slot>
|
|
278
279
|
</template>
|
|
279
280
|
|
|
280
|
-
<!-- 自定义列插槽 -->
|
|
281
|
+
<!-- 自定义列插槽 (透传 item.* 并在没有时回退到默认渲染) -->
|
|
281
282
|
<template
|
|
282
283
|
v-for="header in visibleHeaders"
|
|
283
284
|
v-slot:[`item.${header.value}`]="{ item, value, index }"
|
|
@@ -383,10 +384,10 @@
|
|
|
383
384
|
|
|
384
385
|
<!-- 普通列 -->
|
|
385
386
|
<template v-else>
|
|
386
|
-
<!-- 自定义插槽 -->
|
|
387
|
-
<slot v-if="header.slot || $scopedSlots[`item.${header.value}`]" :name="`item.${header.value}`" :item="item" :value="value"></slot>
|
|
387
|
+
<!-- 自定义插槽 (透传 item.[value]) -->
|
|
388
|
+
<slot v-if="header.slot || $scopedSlots[`item.${header.value}`]" :name="`item.${header.value}`" :item="item" :value="value" :index="index"></slot>
|
|
388
389
|
|
|
389
|
-
<!-- 列配置渲染 -->
|
|
390
|
+
<!-- 列配置渲染 (没有自定义插槽时) -->
|
|
390
391
|
<div v-else class="jh-table-schema-cell d-flex align-center flex-wrap">
|
|
391
392
|
<!-- 状态标签 -->
|
|
392
393
|
<template v-if="header.valueType === 'status'">
|
|
@@ -486,8 +487,13 @@
|
|
|
486
487
|
</template>
|
|
487
488
|
</template>
|
|
488
489
|
|
|
490
|
+
<!-- 透传所有其他插槽 (v-data-table 的原生插槽,排除已处理的) -->
|
|
491
|
+
<template v-for="slotName in passthroughSlots" v-slot:[slotName]="slotProps">
|
|
492
|
+
<slot :name="slotName" v-bind="slotProps"></slot>
|
|
493
|
+
</template>
|
|
494
|
+
|
|
489
495
|
<!-- 加载中 -->
|
|
490
|
-
<template v-slot:loading>
|
|
496
|
+
<template v-if="!$scopedSlots.loading" v-slot:loading>
|
|
491
497
|
<div class="jh-no-data pa-6">
|
|
492
498
|
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
|
493
499
|
<div class="mt-2">数据加载中...</div>
|
|
@@ -495,7 +501,7 @@
|
|
|
495
501
|
</template>
|
|
496
502
|
|
|
497
503
|
<!-- 无数据 -->
|
|
498
|
-
<template v-slot:no-data>
|
|
504
|
+
<template v-if="!$scopedSlots['no-data']" v-slot:no-data>
|
|
499
505
|
<div class="jh-no-data pa-6">
|
|
500
506
|
<v-icon large color="grey lighten-1">mdi-inbox-outline</v-icon>
|
|
501
507
|
<div class="mt-2 text-body-2 grey--text">暂无数据</div>
|
|
@@ -503,7 +509,7 @@
|
|
|
503
509
|
</template>
|
|
504
510
|
|
|
505
511
|
<!-- 无结果 -->
|
|
506
|
-
<template v-slot:no-results>
|
|
512
|
+
<template v-if="!$scopedSlots['no-results']" v-slot:no-results>
|
|
507
513
|
<div class="jh-no-data pa-6">
|
|
508
514
|
<v-icon large color="grey lighten-1">mdi-magnify</v-icon>
|
|
509
515
|
<div class="mt-2 text-body-2 grey--text">未找到匹配的数据</div>
|
|
@@ -511,7 +517,7 @@
|
|
|
511
517
|
</template>
|
|
512
518
|
|
|
513
519
|
<!-- 分页文本 -->
|
|
514
|
-
<template v-slot:[`footer.page-text`]="pagination">
|
|
520
|
+
<template v-if="!$scopedSlots['footer.page-text']" v-slot:[`footer.page-text`]="pagination">
|
|
515
521
|
<span>{{ pagination.pageStart }}-{{ pagination.pageStop }}</span>
|
|
516
522
|
<span class="ml-1">共{{ pagination.itemsLength }}条</span>
|
|
517
523
|
</template>
|
|
@@ -808,11 +814,16 @@ export default {
|
|
|
808
814
|
},
|
|
809
815
|
rowKey: {
|
|
810
816
|
type: String,
|
|
811
|
-
default:
|
|
817
|
+
default: undefined
|
|
818
|
+
},
|
|
819
|
+
// 别名,兼容 v-data-table
|
|
820
|
+
itemKey: {
|
|
821
|
+
type: String,
|
|
822
|
+
default: undefined
|
|
812
823
|
},
|
|
813
824
|
size: {
|
|
814
825
|
type: String,
|
|
815
|
-
default: '
|
|
826
|
+
default: 'medium', // default / medium / compact
|
|
816
827
|
validator: (v) => ['default', 'medium', 'compact'].includes(v)
|
|
817
828
|
},
|
|
818
829
|
footerProps: {
|
|
@@ -845,6 +856,19 @@ export default {
|
|
|
845
856
|
type: Boolean,
|
|
846
857
|
default: false
|
|
847
858
|
},
|
|
859
|
+
// ========== v-data-table 透传 Props (补充) ==========
|
|
860
|
+
page: {
|
|
861
|
+
type: Number,
|
|
862
|
+
default: undefined
|
|
863
|
+
},
|
|
864
|
+
serverItemsLength: {
|
|
865
|
+
type: Number,
|
|
866
|
+
default: undefined
|
|
867
|
+
},
|
|
868
|
+
value: {
|
|
869
|
+
type: Array,
|
|
870
|
+
default: undefined
|
|
871
|
+
},
|
|
848
872
|
context: {
|
|
849
873
|
type: Object,
|
|
850
874
|
default: null
|
|
@@ -856,26 +880,28 @@ export default {
|
|
|
856
880
|
searchInputInternal: this.searchInput,
|
|
857
881
|
currentLoading: this.loading,
|
|
858
882
|
currentItems: this.items || [],
|
|
859
|
-
currentPage: this.pagination ? this.pagination.current || 1 : 1,
|
|
883
|
+
currentPage: this.page !== undefined ? this.page : (this.pagination ? this.pagination.current || 1 : 1),
|
|
860
884
|
currentItemsPerPage: this.itemsPerPage,
|
|
861
|
-
serverItemsLength: -1,
|
|
885
|
+
serverItemsLength: this.serverItemsLength !== undefined ? this.serverItemsLength : -1,
|
|
862
886
|
internalColumns: [],
|
|
863
887
|
currentDensity: this.size,
|
|
864
888
|
isFullscreen: false,
|
|
865
|
-
selectedItems: [],
|
|
889
|
+
selectedItems: this.value || [],
|
|
866
890
|
filterValues: {},
|
|
867
891
|
pollingTimer: null,
|
|
868
892
|
searchDebounceTimer: null,
|
|
869
893
|
internalSortBy: this.normalizeSortBy(this.sortBy),
|
|
870
894
|
internalSortDesc: this.normalizeSortDesc(this.sortDesc),
|
|
871
895
|
sortChangeTimer: null,
|
|
872
|
-
hasAppliedDefaultRowSelection: false
|
|
896
|
+
hasAppliedDefaultRowSelection: false,
|
|
897
|
+
// 最终使用的 item-key
|
|
898
|
+
computedRowKey: this.rowKey || this.itemKey || this.$attrs['item-key'] || 'id'
|
|
873
899
|
};
|
|
874
900
|
},
|
|
875
901
|
computed: {
|
|
876
902
|
// 选中的行 keys
|
|
877
903
|
selectedRowKeys() {
|
|
878
|
-
return this.selectedItems.map(item => item[this.
|
|
904
|
+
return this.selectedItems.map(item => item[this.computedRowKey]);
|
|
879
905
|
},
|
|
880
906
|
|
|
881
907
|
tableAlertScope() {
|
|
@@ -1000,6 +1026,24 @@ export default {
|
|
|
1000
1026
|
customHeaderSlots() {
|
|
1001
1027
|
return this.internalColumns.filter(h => this.$scopedSlots[`header.${h.value}`]);
|
|
1002
1028
|
},
|
|
1029
|
+
// 需要透传的原生插槽
|
|
1030
|
+
passthroughSlots() {
|
|
1031
|
+
const handledSlots = [
|
|
1032
|
+
'loading', 'no-data', 'no-results', 'footer.page-text',
|
|
1033
|
+
'toolbar-actions', 'toolbar-extra', 'header-title', 'table-extra', 'alert', 'alert-actions'
|
|
1034
|
+
];
|
|
1035
|
+
return Object.keys(this.$scopedSlots).filter(name => {
|
|
1036
|
+
// 排除已处理的 item.* 和 header.*
|
|
1037
|
+
if (name.startsWith('item.') || name.startsWith('header.')) {
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
// 排除筛选字段插槽
|
|
1041
|
+
if (name.startsWith('filter-field-')) {
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
return !handledSlots.includes(name);
|
|
1045
|
+
});
|
|
1046
|
+
},
|
|
1003
1047
|
// 表格样式类
|
|
1004
1048
|
tableClassComputed() {
|
|
1005
1049
|
return [
|
|
@@ -1085,7 +1129,7 @@ export default {
|
|
|
1085
1129
|
'filterSearchText', 'filterResetText', 'showCreateButton', 'showUpdateAction',
|
|
1086
1130
|
'showDeleteAction', 'actionColumn', 'columnsState', 'pagination',
|
|
1087
1131
|
'itemsPerPage', 'rowSelection', 'tableAlertRender', 'tableAlertOptionRender',
|
|
1088
|
-
'showSelect', 'singleSelect', 'rowKey', 'size', 'footerProps',
|
|
1132
|
+
'showSelect', 'singleSelect', 'rowKey', 'itemKey', 'size', 'footerProps',
|
|
1089
1133
|
'tableClass', 'polling', 'debounceTime', 'dataTableProps'
|
|
1090
1134
|
];
|
|
1091
1135
|
|
|
@@ -1134,6 +1178,26 @@ export default {
|
|
|
1134
1178
|
}
|
|
1135
1179
|
},
|
|
1136
1180
|
watch: {
|
|
1181
|
+
page(val) {
|
|
1182
|
+
if (val !== undefined && val !== this.currentPage) {
|
|
1183
|
+
this.currentPage = val;
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
serverItemsLength(val) {
|
|
1187
|
+
if (val !== undefined) {
|
|
1188
|
+
this.serverItemsLength = val;
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
value(val) {
|
|
1192
|
+
if (val !== undefined) {
|
|
1193
|
+
this.selectedItems = val;
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
itemsPerPage(val) {
|
|
1197
|
+
if (val !== undefined && val !== this.currentItemsPerPage) {
|
|
1198
|
+
this.currentItemsPerPage = val;
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1137
1201
|
headers: {
|
|
1138
1202
|
immediate: true,
|
|
1139
1203
|
handler(val) {
|
|
@@ -1182,6 +1246,12 @@ export default {
|
|
|
1182
1246
|
this.$nextTick(() => this.applySelectionState());
|
|
1183
1247
|
}
|
|
1184
1248
|
},
|
|
1249
|
+
rowKey(val) {
|
|
1250
|
+
this.computedRowKey = val || this.itemKey || this.$attrs['item-key'] || 'id';
|
|
1251
|
+
},
|
|
1252
|
+
itemKey(val) {
|
|
1253
|
+
this.computedRowKey = this.rowKey || val || this.$attrs['item-key'] || 'id';
|
|
1254
|
+
},
|
|
1185
1255
|
actionColumn: {
|
|
1186
1256
|
handler() {
|
|
1187
1257
|
this.initColumns(this.headers);
|
|
@@ -1585,7 +1655,7 @@ export default {
|
|
|
1585
1655
|
handleSelectionChange(selectedItems) {
|
|
1586
1656
|
this.selectedItems = selectedItems;
|
|
1587
1657
|
const payload = {
|
|
1588
|
-
selectedRowKeys: selectedItems.map(item => item[this.
|
|
1658
|
+
selectedRowKeys: selectedItems.map(item => item[this.computedRowKey]),
|
|
1589
1659
|
selectedRows: selectedItems
|
|
1590
1660
|
};
|
|
1591
1661
|
this.$emit('selection-change', payload);
|
|
@@ -1634,6 +1704,7 @@ export default {
|
|
|
1634
1704
|
const filters = this.transformFilterValues(queryData);
|
|
1635
1705
|
this.filterValues = filters;
|
|
1636
1706
|
this.currentPage = 1; // 重置到第一页
|
|
1707
|
+
this.$emit('update:page', 1);
|
|
1637
1708
|
this.$emit('filter-search', filters);
|
|
1638
1709
|
if (this.request) {
|
|
1639
1710
|
this.reload();
|
|
@@ -1643,6 +1714,7 @@ export default {
|
|
|
1643
1714
|
handleFilterReset() {
|
|
1644
1715
|
this.filterValues = {};
|
|
1645
1716
|
this.currentPage = 1; // 重置到第一页
|
|
1717
|
+
this.$emit('update:page', 1);
|
|
1646
1718
|
this.$emit('filter-reset');
|
|
1647
1719
|
if (this.request) {
|
|
1648
1720
|
this.reload();
|
|
@@ -1789,6 +1861,10 @@ export default {
|
|
|
1789
1861
|
if (!label) return '';
|
|
1790
1862
|
return type === 'select' ? `请选择${label}` : `请输入${label}`;
|
|
1791
1863
|
},
|
|
1864
|
+
// 获取原生 v-data-table 实例
|
|
1865
|
+
getVDataTable() {
|
|
1866
|
+
return this.$refs.dataTable;
|
|
1867
|
+
},
|
|
1792
1868
|
// 重新加载数据(服务端分页)
|
|
1793
1869
|
async reload() {
|
|
1794
1870
|
if (!this.request) return;
|
|
@@ -1820,6 +1896,7 @@ export default {
|
|
|
1820
1896
|
// 重置到第一页
|
|
1821
1897
|
reset() {
|
|
1822
1898
|
this.currentPage = 1;
|
|
1899
|
+
this.$emit('update:page', 1);
|
|
1823
1900
|
this.reload();
|
|
1824
1901
|
},
|
|
1825
1902
|
// 清空选择
|
|
@@ -2006,7 +2083,7 @@ export default {
|
|
|
2006
2083
|
syncSelectionByKeys(keys) {
|
|
2007
2084
|
if (!Array.isArray(keys)) return;
|
|
2008
2085
|
const keySet = new Set(keys);
|
|
2009
|
-
const matched = this.currentItems.filter(item => keySet.has(item[this.
|
|
2086
|
+
const matched = this.currentItems.filter(item => keySet.has(item[this.computedRowKey]));
|
|
2010
2087
|
this.selectedItems = matched;
|
|
2011
2088
|
this.$emit('input', matched);
|
|
2012
2089
|
},
|
|
@@ -2014,7 +2091,7 @@ export default {
|
|
|
2014
2091
|
if (this.hasAppliedDefaultRowSelection) return;
|
|
2015
2092
|
if (!this.rowSelection || !Array.isArray(this.rowSelection.defaultSelectedRowKeys)) return;
|
|
2016
2093
|
const keySet = new Set(this.rowSelection.defaultSelectedRowKeys);
|
|
2017
|
-
const matched = this.currentItems.filter(item => keySet.has(item[this.
|
|
2094
|
+
const matched = this.currentItems.filter(item => keySet.has(item[this.computedRowKey]));
|
|
2018
2095
|
if (matched.length) {
|
|
2019
2096
|
this.selectedItems = matched;
|
|
2020
2097
|
this.$emit('input', matched);
|
|
@@ -2045,7 +2122,7 @@ export default {
|
|
|
2045
2122
|
display: flex;
|
|
2046
2123
|
align-items: center;
|
|
2047
2124
|
justify-content: space-between;
|
|
2048
|
-
padding: 16px
|
|
2125
|
+
padding: 16px 0;
|
|
2049
2126
|
border-bottom: 1px solid #f0f0f0;
|
|
2050
2127
|
min-height: 64px;
|
|
2051
2128
|
}
|
|
@@ -2063,7 +2140,10 @@ export default {
|
|
|
2063
2140
|
height: 40px;
|
|
2064
2141
|
}
|
|
2065
2142
|
.jh-pro-table ::v-deep .jh-table-medium.v-data-table > .v-data-table__wrapper > table > tbody > tr > td {
|
|
2066
|
-
|
|
2143
|
+
min-height: 40px;
|
|
2144
|
+
height: 40px;
|
|
2145
|
+
color: #333C44;
|
|
2146
|
+
font-size: .8125rem;
|
|
2067
2147
|
}
|
|
2068
2148
|
|
|
2069
2149
|
|
|
@@ -2097,14 +2177,14 @@ export default {
|
|
|
2097
2177
|
align-items: center;
|
|
2098
2178
|
justify-content: space-between;
|
|
2099
2179
|
padding: 12px 24px;
|
|
2100
|
-
background: #
|
|
2101
|
-
border: 1px solid #91d5ff;
|
|
2180
|
+
background: #fbfbfb;
|
|
2102
2181
|
border-radius: 4px;
|
|
2103
2182
|
margin: 16px 0 0;
|
|
2104
2183
|
}
|
|
2105
2184
|
|
|
2106
2185
|
.jh-pro-table-alert-info {
|
|
2107
2186
|
display: flex;
|
|
2187
|
+
flex: 1;
|
|
2108
2188
|
align-items: center;
|
|
2109
2189
|
font-size: 14px;
|
|
2110
2190
|
color: rgba(0, 0, 0, 0.65);
|
|
@@ -2119,6 +2199,7 @@ export default {
|
|
|
2119
2199
|
/* 工具栏 */
|
|
2120
2200
|
.jh-pro-table-toolbar {
|
|
2121
2201
|
padding: 16px 0 !important;
|
|
2202
|
+
flex: inherit;
|
|
2122
2203
|
}
|
|
2123
2204
|
|
|
2124
2205
|
/* 表格额外内容区 */
|
|
@@ -2189,6 +2270,7 @@ export default {
|
|
|
2189
2270
|
.jh-table-default >>> .v-data-table > .v-data-table__wrapper > table > thead > tr > th {
|
|
2190
2271
|
height: 48px !important;
|
|
2191
2272
|
padding: 0 16px !important;
|
|
2273
|
+
|
|
2192
2274
|
}
|
|
2193
2275
|
|
|
2194
2276
|
.jh-table-medium >>> .v-data-table > .v-data-table__wrapper > table > tbody > tr > td,
|
|
@@ -2302,6 +2384,20 @@ export default {
|
|
|
2302
2384
|
cursor: pointer;
|
|
2303
2385
|
}
|
|
2304
2386
|
|
|
2387
|
+
/* 表格行选中样式 */
|
|
2388
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr.v-data-table__selected {
|
|
2389
|
+
background-color: #E6F7FF !important;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr.v-data-table__selected:hover {
|
|
2393
|
+
background-color: #D1EDFF !important;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
/* 优化选中行的过渡效果 */
|
|
2397
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr {
|
|
2398
|
+
transition: background-color 0.2s ease;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2305
2401
|
/* Flex 工具类(如果没有 Tailwind) */
|
|
2306
2402
|
.flex {
|
|
2307
2403
|
display: flex;
|
|
@@ -359,8 +359,7 @@
|
|
|
359
359
|
.v-data-table>.v-data-table__wrapper>table>tbody>tr>td,
|
|
360
360
|
.v-data-table>.v-data-table__wrapper>table>tfoot>tr>td,
|
|
361
361
|
.v-data-table>.v-data-table__wrapper>table>thead>tr>td {
|
|
362
|
-
|
|
363
|
-
height: 40px;
|
|
362
|
+
|
|
364
363
|
color: #333C44;
|
|
365
364
|
font-size: .8125rem;
|
|
366
365
|
}
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
# JhAddressSelect - 省市区选择组件
|
|
2
|
-
|
|
3
|
-
省市区三级联动选择组件,支持自定义显示层级、标签文本和数据源。
|
|
4
|
-
|
|
5
|
-
- ✅ **四级联动**: 支持省、市、区、镇四级联动选择
|
|
6
|
-
- ✅ **自定义层级**: 可配置显示 1-4 级(仅省份、省市、省市区、省市区镇)
|
|
7
|
-
- ✅ **自定义标签**: 支持自定义各级别的显示文本
|
|
8
|
-
- ✅ **自定义数据**: 支持传入自定义省市区数据源
|
|
9
|
-
- ✅ **样式透传**: 支持 Vuetify 的 outlined、dense、loading 等样式
|
|
10
|
-
- ✅ **双向绑定**: 支持 v-model 双向数据绑定
|
|
11
|
-
- ✅ **事件触发**: change 事件在值变化时触发
|
|
12
|
-
- ✅ **清除功能**: 支持清除已选值
|
|
13
|
-
- ✅ **返回完整信息**: 事件返回包�� code 和 name 的完整对象
|
|
14
|
-
- ✅ **多种模式**: 支持普通联动选择 (select) 和级联选择 (cascader) 两种模式
|
|
15
|
-
|
|
16
|
-
## 基本用法
|
|
17
|
-
|
|
18
|
-
```vue
|
|
19
|
-
<template>
|
|
20
|
-
<jh-address-select
|
|
21
|
-
v-model="address"
|
|
22
|
-
:data="addressData"
|
|
23
|
-
@change="handleAddressChange"
|
|
24
|
-
></jh-address-select>
|
|
25
|
-
</template>
|
|
26
|
-
|
|
27
|
-
<script>
|
|
28
|
-
export default {
|
|
29
|
-
data() {
|
|
30
|
-
return {
|
|
31
|
-
address: { province: null, city: null, district: null, town: null },
|
|
32
|
-
addressData: [
|
|
33
|
-
{ code: '110000', name: '北京市', children: [...] },
|
|
34
|
-
{ code: '440000', name: '广东省', children: [...] }
|
|
35
|
-
]
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
methods: {
|
|
39
|
-
handleAddressChange(value) {
|
|
40
|
-
console.log('省份:', value.province?.name, value.province?.code);
|
|
41
|
-
console.log('城市:', value.city?.name, value.city?.code);
|
|
42
|
-
console.log('区县:', value.district?.name, value.district?.code);
|
|
43
|
-
console.log('乡镇:', value.town?.name, value.town?.code);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
</script>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## API
|
|
51
|
-
|
|
52
|
-
### Props
|
|
53
|
-
|
|
54
|
-
| 参数 | 说明 | 类型 | 默认值 |
|
|
55
|
-
| --- | --- | --- | --- |
|
|
56
|
-
| value / v-model | 绑定值,返回包含 code 和 name 的对象 | object | { province: null, city: null, district: null } |
|
|
57
|
-
| level | 显示层级:1-仅省份,2-省市,3-省市区,4-省市区镇 | number | 3 |
|
|
58
|
-
| type | 显示模式:'select' (默认) 或 'cascader' | string | 'select' |
|
|
59
|
-
| label | 级联模式下的输入框标签 | string | '请选择地区' |
|
|
60
|
-
| outlined | 是否使用 outlined 样式 | boolean | true |
|
|
61
|
-
| dense | 是否使用紧凑模式 | boolean | false |
|
|
62
|
-
| loading | 是否显示加载状态 | boolean | false |
|
|
63
|
-
| labels | 自定义标签文本 | object | { province: '省份', city: '城市', district: '区/县', town: '乡镇' } |
|
|
64
|
-
| data | 省市区数据源 | array | 内置示例数据 |
|
|
65
|
-
|
|
66
|
-
### Events
|
|
67
|
-
|
|
68
|
-
| 事件名 | 说明 | 回调参数 |
|
|
69
|
-
| --- | --- | --- |
|
|
70
|
-
| input | 值变化时触发(v-model) | (value: object) |
|
|
71
|
-
| change | 值变化时触发 | (value: object) |
|
|
72
|
-
|
|
73
|
-
### 返回值格式
|
|
74
|
-
|
|
75
|
-
v-model 绑定值和事件回调参数的格式:
|
|
76
|
-
|
|
77
|
-
```javascript
|
|
78
|
-
{
|
|
79
|
-
province: { code: '440000', name: '广东省' },
|
|
80
|
-
city: { code: '440300', name: '深圳市' },
|
|
81
|
-
district: { code: '440305', name: '南山区' },
|
|
82
|
-
town: { code: '440305001', name: '南头街道' } // level=4 时存在
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
每个字段都是一个对象,包含 `code`(编码)和 `name`(名称)两个属性。未选择时为 `null`。
|
|
87
|
-
|
|
88
|
-
### 数据格式
|
|
89
|
-
|
|
90
|
-
data 属性需要符合以下格式:
|
|
91
|
-
|
|
92
|
-
```javascript
|
|
93
|
-
[
|
|
94
|
-
{
|
|
95
|
-
code: '110000', // 省份编码
|
|
96
|
-
name: '北京市', // 省份名称
|
|
97
|
-
children: [ // 城市列表
|
|
98
|
-
{
|
|
99
|
-
code: '110100', // 城市编码
|
|
100
|
-
name: '市辖区', // 城市名称
|
|
101
|
-
children: [ // 区县列表
|
|
102
|
-
{
|
|
103
|
-
code: '110101', // 区县编码
|
|
104
|
-
name: '东城区' // 区县名称
|
|
105
|
-
}
|
|
106
|
-
]
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
]
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## 使用示例
|
|
114
|
-
|
|
115
|
-
### 默认三级联动
|
|
116
|
-
|
|
117
|
-
```vue
|
|
118
|
-
<jh-address-select
|
|
119
|
-
v-model="address"
|
|
120
|
-
:data="addressData"
|
|
121
|
-
></jh-address-select>
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 级联选择模式
|
|
125
|
-
|
|
126
|
-
使用 `type="cascader"` 开启级联选择模式。
|
|
127
|
-
|
|
128
|
-
```vue
|
|
129
|
-
<jh-address-select
|
|
130
|
-
v-model="address"
|
|
131
|
-
type="cascader"
|
|
132
|
-
label="收货地址"
|
|
133
|
-
:data="addressData"
|
|
134
|
-
></jh-address-select>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### 仅选择到城市
|
|
138
|
-
|
|
139
|
-
```vue
|
|
140
|
-
<jh-address-select
|
|
141
|
-
v-model="address"
|
|
142
|
-
:level="2"
|
|
143
|
-
:data="addressData"
|
|
144
|
-
></jh-address-select>
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 仅选择省份
|
|
148
|
-
|
|
149
|
-
```vue
|
|
150
|
-
<jh-address-select
|
|
151
|
-
v-model="address"
|
|
152
|
-
:level="1"
|
|
153
|
-
:data="addressData"
|
|
154
|
-
></jh-address-select>
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### 四级联动(省市区镇)
|
|
158
|
-
|
|
159
|
-
```vue
|
|
160
|
-
<jh-address-select
|
|
161
|
-
v-model="address"
|
|
162
|
-
:level="4"
|
|
163
|
-
:data="addressData"
|
|
164
|
-
></jh-address-select>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### 四级联动(带初始值)
|
|
168
|
-
|
|
169
|
-
```vue
|
|
170
|
-
<jh-address-select
|
|
171
|
-
v-model="address"
|
|
172
|
-
:level="4"
|
|
173
|
-
:data="addressData"
|
|
174
|
-
></jh-address-select>
|
|
175
|
-
|
|
176
|
-
<script>
|
|
177
|
-
export default {
|
|
178
|
-
data() {
|
|
179
|
-
return {
|
|
180
|
-
address: {
|
|
181
|
-
province: { code: '440000', name: '广东省' },
|
|
182
|
-
city: { code: '440300', name: '深圳市' },
|
|
183
|
-
district: { code: '440305', name: '南山区' },
|
|
184
|
-
town: { code: '440305001', name: '南头街道' }
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
</script>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 紧凑模式
|
|
193
|
-
|
|
194
|
-
```vue
|
|
195
|
-
<jh-address-select
|
|
196
|
-
v-model="address"
|
|
197
|
-
dense
|
|
198
|
-
:data="addressData"
|
|
199
|
-
></jh-address-select>
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 自定义标签文本
|
|
203
|
-
|
|
204
|
-
```vue
|
|
205
|
-
<jh-address-select
|
|
206
|
-
v-model="address"
|
|
207
|
-
:labels="{ province: '所在省份', city: '所在城市', district: '所在区县' }"
|
|
208
|
-
:data="addressData"
|
|
209
|
-
></jh-address-select>
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### 加载状态
|
|
213
|
-
|
|
214
|
-
```vue
|
|
215
|
-
<jh-address-select
|
|
216
|
-
v-model="address"
|
|
217
|
-
:loading="isLoading"
|
|
218
|
-
:data="addressData"
|
|
219
|
-
></jh-address-select>
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### 使用 filled 样式
|
|
223
|
-
|
|
224
|
-
```vue
|
|
225
|
-
<jh-address-select
|
|
226
|
-
v-model="address"
|
|
227
|
-
:outlined="false"
|
|
228
|
-
:data="addressData"
|
|
229
|
-
></jh-address-select>
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### 监听变化
|
|
233
|
-
|
|
234
|
-
```vue
|
|
235
|
-
<template>
|
|
236
|
-
<jh-address-select
|
|
237
|
-
v-model="address"
|
|
238
|
-
:data="addressData"
|
|
239
|
-
@change="handleAddressChange"
|
|
240
|
-
></jh-address-select>
|
|
241
|
-
</template>
|
|
242
|
-
|
|
243
|
-
<script>
|
|
244
|
-
export default {
|
|
245
|
-
methods: {
|
|
246
|
-
handleAddressChange(value) {
|
|
247
|
-
console.log('地址变化:', value);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
</script>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## 注意事项
|
|
255
|
-
|
|
256
|
-
1. **数据格式**: data 属性必须符合指定的嵌套格式,包含 code 和 name 字段
|
|
257
|
-
2. **层级控制**: level 属性控制显示的级数,设置为 1 时只显示省份,2 时显示省市,3 时显示省市区,4 时显示省市区镇
|
|
258
|
-
3. **联动逻辑**: 选择省份后会清空城市、区县和乡镇,选择城市后会清空区县和乡镇,选择区县后会清空乡镇
|
|
259
|
-
4. **禁用状态**: 未选择上级时,下级选择器会自动禁用
|
|
260
|
-
5. **响应式布局**: 组件会根据 level 自动调整栅格布局(level=1 时占 12 列,level=2 时占 6 列,level=3 时占 4 列,level=4 时占 3 列)
|
|
261
|
-
6. **返回值格式**: v-model 和事件返回的值包含 code 和 name 两个字段,方便获取编码和名称
|
|
262
|
-
7. **级联模式**: 在 cascader 模式下,点击省份/城市/区县会展示下一级列表,直到选择完指定 level 级数后自动收起菜单
|
|
263
|
-
|
|
264
|
-
## 相关链接
|
|
265
|
-
|
|
266
|
-
- [Vuetify Autocomplete](https://vuetifyjs.com/en/components/autocompletes/)
|
|
267
|
-
- [中国行政区划代码](http://www.mca.gov.cn/article/sj/xzqh/)
|