jianghu-ui 1.0.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.
Files changed (112) hide show
  1. package/README.md +376 -0
  2. package/dist/jianghu-ui.css +2318 -0
  3. package/dist/jianghu-ui.js +2 -0
  4. package/dist/jianghu-ui.js.LICENSE.txt +1 -0
  5. package/package.json +56 -0
  6. package/src/Design.stories.mdx +195 -0
  7. package/src/Introduction.stories.mdx +148 -0
  8. package/src/components/JhAddressSelect/JhAddressSelect.md +250 -0
  9. package/src/components/JhAddressSelect/JhAddressSelect.stories.js +282 -0
  10. package/src/components/JhAddressSelect/JhAddressSelect.vue +261 -0
  11. package/src/components/JhCard/JhCard.md +246 -0
  12. package/src/components/JhCard/JhCard.stories.js +688 -0
  13. package/src/components/JhCard/JhCard.vue +604 -0
  14. package/src/components/JhCheckCard/JhCheckCard.md +245 -0
  15. package/src/components/JhCheckCard/JhCheckCard.stories.js +750 -0
  16. package/src/components/JhCheckCard/JhCheckCard.vue +476 -0
  17. package/src/components/JhConfirmDialog/JhConfirmDialog.md +70 -0
  18. package/src/components/JhConfirmDialog/JhConfirmDialog.stories.js +550 -0
  19. package/src/components/JhConfirmDialog/JhConfirmDialog.vue +181 -0
  20. package/src/components/JhDateRangePicker/JhDateRangePicker.md +56 -0
  21. package/src/components/JhDateRangePicker/JhDateRangePicker.stories.js +320 -0
  22. package/src/components/JhDateRangePicker/JhDateRangePicker.vue +307 -0
  23. package/src/components/JhDescriptions/JhDescriptions.md +724 -0
  24. package/src/components/JhDescriptions/JhDescriptions.stories.js +858 -0
  25. package/src/components/JhDescriptions/JhDescriptions.vue +933 -0
  26. package/src/components/JhDraggable/JhDraggable.md +66 -0
  27. package/src/components/JhDraggable/JhDraggable.stories.js +161 -0
  28. package/src/components/JhDraggable/JhDraggable.vue +254 -0
  29. package/src/components/JhDrawer/JhDrawer.md +68 -0
  30. package/src/components/JhDrawer/JhDrawer.stories.js +478 -0
  31. package/src/components/JhDrawer/JhDrawer.vue +281 -0
  32. package/src/components/JhDrawerForm/JhDrawerForm.md +69 -0
  33. package/src/components/JhDrawerForm/JhDrawerForm.stories.js +492 -0
  34. package/src/components/JhDrawerForm/JhDrawerForm.vue +297 -0
  35. package/src/components/JhEditableTable/JhEditableTable.md +507 -0
  36. package/src/components/JhEditableTable/JhEditableTable.stories.js +615 -0
  37. package/src/components/JhEditableTable/JhEditableTable.vue +685 -0
  38. package/src/components/JhFileInput/JhFileInput.md +56 -0
  39. package/src/components/JhFileInput/JhFileInput.stories.js +103 -0
  40. package/src/components/JhFileInput/JhFileInput.vue +253 -0
  41. package/src/components/JhForm/JhForm.md +676 -0
  42. package/src/components/JhForm/JhForm.stories.js +1375 -0
  43. package/src/components/JhForm/JhForm.vue +657 -0
  44. package/src/components/JhFormField/JhFormField.stories.js +217 -0
  45. package/src/components/JhFormField/JhFormField.vue +439 -0
  46. package/src/components/JhFormFields/JhFormFields.md +647 -0
  47. package/src/components/JhFormFields/JhFormFields.stories.js +922 -0
  48. package/src/components/JhFormFields/JhFormFields.vue +998 -0
  49. package/src/components/JhFormList/JhFormList.md +303 -0
  50. package/src/components/JhFormList/JhFormList.stories.js +661 -0
  51. package/src/components/JhFormList/JhFormList.vue +1127 -0
  52. package/src/components/JhJsonEditor/JhJsonEditor.md +54 -0
  53. package/src/components/JhJsonEditor/JhJsonEditor.stories.js +157 -0
  54. package/src/components/JhJsonEditor/JhJsonEditor.vue +178 -0
  55. package/src/components/JhLayout/JhLayout.md +580 -0
  56. package/src/components/JhLayout/JhLayout.stories.js +414 -0
  57. package/src/components/JhLayout/JhLayout.vue +387 -0
  58. package/src/components/JhList/JhList.md +441 -0
  59. package/src/components/JhList/JhList.stories.js +524 -0
  60. package/src/components/JhList/JhList.vue +571 -0
  61. package/src/components/JhMarkdownEditor/JhMarkdownEditor.md +56 -0
  62. package/src/components/JhMarkdownEditor/JhMarkdownEditor.stories.js +191 -0
  63. package/src/components/JhMarkdownEditor/JhMarkdownEditor.vue +188 -0
  64. package/src/components/JhMask/JhMask.md +62 -0
  65. package/src/components/JhMask/JhMask.stories.js +270 -0
  66. package/src/components/JhMask/JhMask.vue +123 -0
  67. package/src/components/JhMenu/JhMenu.md +85 -0
  68. package/src/components/JhMenu/JhMenu.stories.js +384 -0
  69. package/src/components/JhMenu/JhMenu.vue +545 -0
  70. package/src/components/JhModal/JhModal.md +68 -0
  71. package/src/components/JhModal/JhModal.stories.js +562 -0
  72. package/src/components/JhModal/JhModal.vue +235 -0
  73. package/src/components/JhModalForm/JhModalForm.md +69 -0
  74. package/src/components/JhModalForm/JhModalForm.stories.js +592 -0
  75. package/src/components/JhModalForm/JhModalForm.vue +298 -0
  76. package/src/components/JhPageContainer/JhPageContainer.md +409 -0
  77. package/src/components/JhPageContainer/JhPageContainer.stories.js +209 -0
  78. package/src/components/JhPageContainer/JhPageContainer.vue +72 -0
  79. package/src/components/JhQueryFilter/JhQueryFilter.md +77 -0
  80. package/src/components/JhQueryFilter/JhQueryFilter.stories.js +684 -0
  81. package/src/components/JhQueryFilter/JhQueryFilter.vue +429 -0
  82. package/src/components/JhScene/JhScene.md +64 -0
  83. package/src/components/JhScene/JhScene.stories.js +317 -0
  84. package/src/components/JhScene/JhScene.vue +376 -0
  85. package/src/components/JhStatisticCard/JhStatisticCard.md +363 -0
  86. package/src/components/JhStatisticCard/JhStatisticCard.stories.js +847 -0
  87. package/src/components/JhStatisticCard/JhStatisticCard.vue +459 -0
  88. package/src/components/JhStepsForm/JhStepsForm.md +666 -0
  89. package/src/components/JhStepsForm/JhStepsForm.stories.js +1224 -0
  90. package/src/components/JhStepsForm/JhStepsForm.vue +749 -0
  91. package/src/components/JhTable/JhTable.md +730 -0
  92. package/src/components/JhTable/JhTable.stories.js +1444 -0
  93. package/src/components/JhTable/JhTable.vue +2298 -0
  94. package/src/components/JhTableAttachment/JhTableAttachment.md +70 -0
  95. package/src/components/JhTableAttachment/JhTableAttachment.stories.js +198 -0
  96. package/src/components/JhTableAttachment/JhTableAttachment.vue +264 -0
  97. package/src/components/JhToast/JhToast.md +67 -0
  98. package/src/components/JhToast/JhToast.stories.js +386 -0
  99. package/src/components/JhToast/JhToast.vue +239 -0
  100. package/src/components/JhTreeSelect/JhTreeSelect.md +82 -0
  101. package/src/components/JhTreeSelect/JhTreeSelect.stories.js +391 -0
  102. package/src/components/JhTreeSelect/JhTreeSelect.vue +727 -0
  103. package/src/components/JhWaterMark/JhWaterMark.md +190 -0
  104. package/src/components/JhWaterMark/JhWaterMark.stories.js +675 -0
  105. package/src/components/JhWaterMark/JhWaterMark.vue +351 -0
  106. package/src/components/README.md +52 -0
  107. package/src/index.js +135 -0
  108. package/src/style/globalCSSJHV4.css +348 -0
  109. package/src/style/globalCSSVuetifyV4.css +637 -0
  110. package/src/style/storybook.css +4 -0
  111. package/src/tailwind.css +3 -0
  112. package/src/utils/vuetify.js +31 -0
@@ -0,0 +1,282 @@
1
+ import JhAddressSelect from './JhAddressSelect.vue';
2
+ import { ref, watch } from 'vue';
3
+
4
+ export default {
5
+ title: '数据录入/JhAddressSelect - 省市区选择',
6
+ component: JhAddressSelect,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: '省市区三级联动选择组件,支持自定义显示层级、标签文本和数据源。这是一个封装了多个 `v-select` 的便利组件,用于快速构建地址输入功能。<br/>' +
12
+ '在 https://github.com/modood/Administrative-divisions-of-China 中获取省市区数据。',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ value: {
18
+ control: 'object',
19
+ description: 'v-model 绑定值,包含 `province`, `city`, `district`',
20
+ table: {
21
+ type: { summary: 'object' },
22
+ defaultValue: { summary: '{ province: null, city: null, district: null }' },
23
+ },
24
+ },
25
+ level: {
26
+ control: { type: 'select' },
27
+ options: [1, 2, 3, 4],
28
+ description: '显示层级:1-仅省份,2-省市,3-省市区,4-省市区镇',
29
+ table: {
30
+ type: { summary: 'number' },
31
+ defaultValue: { summary: '3' },
32
+ },
33
+ },
34
+ outlined: {
35
+ control: 'boolean',
36
+ description: '是否使用 `outlined` 样式',
37
+ table: {
38
+ type: { summary: 'boolean' },
39
+ defaultValue: { summary: 'false' },
40
+ },
41
+ },
42
+ dense: {
43
+ control: 'boolean',
44
+ description: '是否使用紧凑模式 `dense`',
45
+ table: {
46
+ type: { summary: 'boolean' },
47
+ defaultValue: { summary: 'true' },
48
+ },
49
+ },
50
+ loading: {
51
+ control: 'boolean',
52
+ description: '是否显示加载状态',
53
+ table: {
54
+ type: { summary: 'boolean' },
55
+ defaultValue: { summary: 'false' },
56
+ },
57
+ },
58
+ labels: {
59
+ control: 'object',
60
+ description: '自定义下拉框的标签文本',
61
+ table: {
62
+ type: { summary: 'object' },
63
+ defaultValue: { summary: "{ province: '省份', city: '城市', district: '区/县' }" },
64
+ },
65
+ },
66
+ data: {
67
+ control: 'object',
68
+ description: '省市区数据源,默认为内置数据',
69
+ table: {
70
+ type: { summary: 'array' },
71
+ },
72
+ },
73
+ onChange: {
74
+ action: 'changed',
75
+ description: '值变化时触发的事件',
76
+ }
77
+ },
78
+ };
79
+
80
+ // 模拟的省市区数据
81
+ const defaultData = [
82
+ { code: '110000', name: '北京市', children: [{ code: '110100', name: '市辖区', children: [{ code: '110101', name: '东城区', children: [{ code: '110101001', name: '东华门街道' }] }, { code: '110102', name: '西城区', children: [{ code: '110102001', name: '西长安街街道' }] }] }] },
83
+ {
84
+ code: '440000', name: '广东省',
85
+ children: [
86
+ { code: '440100', name: '广州市', children: [{ code: '440106', name: '天河区', children: [{ code: '440106001', name: '天河南街道' }] }, { code: '440103', name: '荔湾区', children: [{ code: '440103001', name: '金花街道' }] }] },
87
+ { code: '440300', name: '深圳市', children: [{ code: '440305', name: '南山区', children: [{ code: '440305001', name: '南头街道' }] }, { code: '440304', name: '福田区', children: [{ code: '440304001', name: '园岭街道' }] }] }
88
+ ]
89
+ }
90
+ ];
91
+
92
+ // 统一的渲染模板
93
+ const Template = (args) => ({
94
+ components: { JhAddressSelect },
95
+ setup() {
96
+ const value = ref(args.value);
97
+ watch(() => args.value, (newValue) => {
98
+ value.value = newValue;
99
+ }, { deep: true });
100
+
101
+ return { args, value };
102
+ },
103
+ template: `
104
+ <div style="background: rgb(240, 242, 245); padding: 30px; min-height: 350px;">
105
+ <jh-address-select
106
+ v-model="value"
107
+ :level="args.level"
108
+ :outlined="args.outlined"
109
+ :dense="args.dense"
110
+ :loading="args.loading"
111
+ :labels="args.labels"
112
+ :data="args.data"
113
+ @change="args.onChange"
114
+ />
115
+ <div style="margin-top: 20px; background: white; padding: 16px; border-radius: 4px;">
116
+ <strong>v-model value:</strong>
117
+ <pre style="margin-top: 8px;">{{ value }}</pre>
118
+ </div>
119
+ </div>
120
+ `,
121
+ });
122
+
123
+ const baseArgs = {
124
+ value: { province: null, city: null, district: null, town: null },
125
+ level: 3,
126
+ outlined: true,
127
+ dense: true,
128
+ loading: false,
129
+ labels: { province: '省份', city: '城市', district: '区/县', town: '乡镇' },
130
+ data: defaultData,
131
+ };
132
+
133
+ // 故事导出
134
+ export const Default = Template.bind({});
135
+ Default.storyName = "基础用法";
136
+ Default.args = {
137
+ ...baseArgs,
138
+ };
139
+ Default.parameters = {
140
+ docs: {
141
+ description: {
142
+ story: '默认情况下,组件会渲染一个三级(省-市-区)地址选择器。',
143
+ },
144
+ },
145
+ };
146
+
147
+ export const WithInitialValue = Template.bind({});
148
+ WithInitialValue.storyName = "带初始值";
149
+ WithInitialValue.args = {
150
+ ...baseArgs,
151
+ value: {
152
+ province: { code: '440000', name: '广东省' },
153
+ city: { code: '440300', name: '深圳市' },
154
+ district: { code: '440305', name: '南山区' }
155
+ },
156
+ };
157
+ WithInitialValue.parameters = {
158
+ docs: {
159
+ description: {
160
+ story: '设置 `v-model` 的初始值可以使组件在加载时就显示已选定的地址。返回值包含 `code` 和 `name` 两个字段。',
161
+ },
162
+ },
163
+ };
164
+
165
+ export const Level2CityOnly = Template.bind({});
166
+ Level2CityOnly.storyName = "二级联动 (省-市)";
167
+ Level2CityOnly.args = {
168
+ ...baseArgs,
169
+ level: 2,
170
+ };
171
+ Level2CityOnly.parameters = {
172
+ docs: {
173
+ description: {
174
+ story: '通过设置 `level: 2`,可以将组件配置为只显示省和市两级选择。',
175
+ },
176
+ },
177
+ };
178
+
179
+ export const Level1ProvinceOnly = Template.bind({});
180
+ Level1ProvinceOnly.storyName = "一级联动 (仅省)";
181
+ Level1ProvinceOnly.args = {
182
+ ...baseArgs,
183
+ level: 1,
184
+ };
185
+ Level1ProvinceOnly.parameters = {
186
+ docs: {
187
+ description: {
188
+ story: '通过设置 `level: 1`,可以将组件配置为只显示省份一级选择。',
189
+ },
190
+ },
191
+ };
192
+
193
+ export const DenseMode = Template.bind({});
194
+ DenseMode.storyName = "紧凑模式";
195
+ DenseMode.args = {
196
+ ...baseArgs,
197
+ dense: true,
198
+ };
199
+ DenseMode.parameters = {
200
+ docs: {
201
+ description: {
202
+ story: '设置 `dense: true` 可以减小组件的垂直间距,适用于空间有限的场景。',
203
+ },
204
+ },
205
+ };
206
+
207
+ export const FilledStyle = Template.bind({});
208
+ FilledStyle.storyName = "填充样式";
209
+ FilledStyle.args = {
210
+ ...baseArgs,
211
+ outlined: true,
212
+ };
213
+ FilledStyle.parameters = {
214
+ docs: {
215
+ description: {
216
+ story: '通过设置 `outlined: false`,可以切换到 `filled` 样式的输入框。',
217
+ },
218
+ },
219
+ };
220
+
221
+ export const LoadingState = Template.bind({});
222
+ LoadingState.storyName = "加载状态";
223
+ LoadingState.args = {
224
+ ...baseArgs,
225
+ loading: true,
226
+ };
227
+ LoadingState.parameters = {
228
+ docs: {
229
+ description: {
230
+ story: '设置 `loading: true` 会禁用所有下拉框并显示加载指示器,适用于数据正在从后端异步加载的场景。',
231
+ },
232
+ },
233
+ };
234
+
235
+ export const CustomLabels = Template.bind({});
236
+ CustomLabels.storyName = "自定义标签";
237
+ CustomLabels.args = {
238
+ ...baseArgs,
239
+ labels: { province: '选择省', city: '选择市', district: '选择区', town: '选择乡镇' },
240
+ };
241
+ CustomLabels.parameters = {
242
+ docs: {
243
+ description: {
244
+ story: '通过 `labels` 属性,可以自定义每个下拉框的标签文本,以适应不同的业务术语。',
245
+ },
246
+ },
247
+ };
248
+
249
+ export const Level4Full = Template.bind({});
250
+ Level4Full.storyName = "四级联动 (省-市-区-镇)";
251
+ Level4Full.args = {
252
+ ...baseArgs,
253
+ level: 4,
254
+ };
255
+ Level4Full.parameters = {
256
+ docs: {
257
+ description: {
258
+ story: '通过设置 `level: 4`,可以将组件配置为显示省、市、区、镇四级选择,适用于需要精确到乡镇的场景。',
259
+ },
260
+ },
261
+ };
262
+
263
+ export const Level4WithInitialValue = Template.bind({});
264
+ Level4WithInitialValue.storyName = "四级联动 (带初始值)";
265
+ Level4WithInitialValue.args = {
266
+ ...baseArgs,
267
+ level: 4,
268
+ value: {
269
+ province: { code: '440000', name: '广东省' },
270
+ city: { code: '440300', name: '深圳市' },
271
+ district: { code: '440305', name: '南山区' },
272
+ town: { code: '440305001', name: '南头街道' }
273
+ },
274
+ };
275
+ Level4WithInitialValue.parameters = {
276
+ docs: {
277
+ description: {
278
+ story: '四级联动并设置初始值,展示完整的省市区镇选择功能。',
279
+ },
280
+ },
281
+ };
282
+
@@ -0,0 +1,261 @@
1
+ <template>
2
+ <v-row dense>
3
+ <v-col cols="12" :md="gridCol">
4
+ <v-autocomplete
5
+ class="jh-v-input"
6
+ v-model="internalValue.province"
7
+ :items="provinces"
8
+ :label="labels.province"
9
+ :outlined="outlined"
10
+ :dense="dense"
11
+ :filled="filled"
12
+ :single-line="singleLine"
13
+ :loading="loading"
14
+ item-text="name"
15
+ item-value="code"
16
+ clearable
17
+ hide-details
18
+ hide-no-data
19
+ prepend-inner-icon="mdi-map-outline"
20
+ @change="handleProvinceChange"
21
+ ></v-autocomplete>
22
+ </v-col>
23
+
24
+ <v-col v-if="level >= 2" cols="12" :md="gridCol">
25
+ <v-autocomplete
26
+ class="jh-v-input"
27
+ v-model="internalValue.city"
28
+ :items="cities"
29
+ :label="labels.city"
30
+ :disabled="!internalValue.province"
31
+ :outlined="outlined"
32
+ :dense="dense"
33
+ :filled="filled"
34
+ :single-line="singleLine"
35
+ :loading="loading"
36
+ item-text="name"
37
+ item-value="code"
38
+ clearable
39
+ prepend-inner-icon="mdi-city-variant-outline"
40
+ @change="handleCityChange"
41
+ ></v-autocomplete>
42
+ </v-col>
43
+
44
+ <v-col v-if="level >= 3" cols="12" :md="gridCol">
45
+ <v-autocomplete
46
+ class="jh-v-input"
47
+ v-model="internalValue.district"
48
+ :items="districts"
49
+ :label="labels.district"
50
+ :disabled="!internalValue.city"
51
+ :outlined="outlined"
52
+ :dense="dense"
53
+ :filled="filled"
54
+ :single-line="singleLine"
55
+ :loading="loading"
56
+ item-text="name"
57
+ item-value="code"
58
+ clearable
59
+ prepend-inner-icon="mdi-home-city-outline"
60
+ @change="handleDistrictChange"
61
+ ></v-autocomplete>
62
+ </v-col>
63
+
64
+ <v-col v-if="level >= 4" cols="12" :md="gridCol">
65
+ <v-autocomplete
66
+ class="jh-v-input"
67
+ v-model="internalValue.town"
68
+ :items="towns"
69
+ :label="labels.town"
70
+ :disabled="!internalValue.district"
71
+ :outlined="outlined"
72
+ :dense="dense"
73
+ :filled="filled"
74
+ :single-line="singleLine"
75
+ :loading="loading"
76
+ item-text="name"
77
+ item-value="code"
78
+ clearable
79
+ prepend-inner-icon="mdi-home-variant-outline"
80
+ @change="emitChange"
81
+ ></v-autocomplete>
82
+ </v-col>
83
+ </v-row>
84
+ </template>
85
+
86
+ <script>
87
+ export default {
88
+ name: 'JhAddressSelect',
89
+ props: {
90
+ value: {
91
+ type: Object,
92
+ default: () => ({ province: null, city: null, district: null, town: null })
93
+ },
94
+ level: { type: Number, default: 3 },
95
+ outlined: { type: Boolean, default: true },
96
+ dense: { type: Boolean, default: true },
97
+ filled: { type: Boolean, default: true },
98
+ singleLine: { type: Boolean, default: true },
99
+
100
+ loading: { type: Boolean, default: false },
101
+ labels: {
102
+ type: Object,
103
+ default: () => ({ province: '省份', city: '城市', district: '区/县', town: '乡镇' })
104
+ },
105
+ data: {
106
+ type: Array,
107
+ default: () => [
108
+ { code: '110000', name: '北京市', children: [{ code: '110100', name: '市辖区', children: [{ code: '110101', name: '东城区', children: [{ code: '110101001', name: '东华门街道' }] }] }] },
109
+ {
110
+ code: '440000', name: '广东省',
111
+ children: [
112
+ { code: '440100', name: '广州市', children: [{ code: '440106', name: '天河区', children: [{ code: '440106001', name: '天河南街道' }] }] },
113
+ { code: '440300', name: '深圳市', children: [{ code: '440305', name: '南山区', children: [{ code: '440305001', name: '南头街道' }] }] }
114
+ ]
115
+ }
116
+ ]
117
+ }
118
+ },
119
+ data() {
120
+ return {
121
+ internalValue: { ...this.value },
122
+ cities: [],
123
+ districts: [],
124
+ towns: []
125
+ };
126
+ },
127
+ computed: {
128
+ provinces() {
129
+ return this.data.map(i => ({ code: i.code, name: i.name }));
130
+ },
131
+ gridCol() {
132
+ if (this.level === 1) return 12;
133
+ if (this.level === 2) return 6;
134
+ if (this.level === 3) return 4;
135
+ return 3;
136
+ },
137
+ fullValue() {
138
+ const result = {
139
+ province: null,
140
+ city: null,
141
+ district: null,
142
+ town: null
143
+ };
144
+
145
+ if (this.internalValue.province) {
146
+ const province = this.provinces.find(p => p.code === this.internalValue.province);
147
+ if (province) {
148
+ result.province = { code: province.code, name: province.name };
149
+ }
150
+ }
151
+
152
+ if (this.internalValue.city) {
153
+ const city = this.cities.find(c => c.code === this.internalValue.city);
154
+ if (city) {
155
+ result.city = { code: city.code, name: city.name };
156
+ }
157
+ }
158
+
159
+ if (this.internalValue.district) {
160
+ const district = this.districts.find(d => d.code === this.internalValue.district);
161
+ if (district) {
162
+ result.district = { code: district.code, name: district.name };
163
+ }
164
+ }
165
+
166
+ if (this.internalValue.town) {
167
+ const town = this.towns.find(t => t.code === this.internalValue.town);
168
+ if (town) {
169
+ result.town = { code: town.code, name: town.name };
170
+ }
171
+ }
172
+
173
+ return result;
174
+ }
175
+ },
176
+ watch: {
177
+ value: {
178
+ deep: true,
179
+ immediate: true,
180
+ handler(val) {
181
+ const newValue = { province: null, city: null, district: null, town: null };
182
+
183
+ if (val.province) {
184
+ newValue.province = typeof val.province === 'object' ? val.province.code : val.province;
185
+ }
186
+ if (val.city) {
187
+ newValue.city = typeof val.city === 'object' ? val.city.code : val.city;
188
+ }
189
+ if (val.district) {
190
+ newValue.district = typeof val.district === 'object' ? val.district.code : val.district;
191
+ }
192
+ if (val.town) {
193
+ newValue.town = typeof val.town === 'object' ? val.town.code : val.town;
194
+ }
195
+
196
+ this.internalValue = newValue;
197
+
198
+ if (newValue.province) {
199
+ const provinceMatch = this.data.find(i => i.code === newValue.province);
200
+ this.cities = provinceMatch ? provinceMatch.children : [];
201
+
202
+ if (newValue.city) {
203
+ const cityMatch = this.cities.find(i => i.code === newValue.city);
204
+ this.districts = cityMatch ? cityMatch.children : [];
205
+
206
+ if (newValue.district) {
207
+ const districtMatch = this.districts.find(i => i.code === newValue.district);
208
+ this.towns = districtMatch ? districtMatch.children : [];
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ },
215
+ methods: {
216
+ handleProvinceChange(code) {
217
+ this.internalValue.city = null;
218
+ this.internalValue.district = null;
219
+ this.internalValue.town = null;
220
+ this.cities = [];
221
+ this.districts = [];
222
+ this.towns = [];
223
+ if (code) {
224
+ const match = this.data.find(i => i.code === code);
225
+ this.cities = match ? match.children : [];
226
+ }
227
+ this.emitChange();
228
+ },
229
+ handleCityChange(code) {
230
+ this.internalValue.district = null;
231
+ this.internalValue.town = null;
232
+ this.districts = [];
233
+ this.towns = [];
234
+ if (code) {
235
+ const provinceMatch = this.data.find(i => i.code === this.internalValue.province);
236
+ const cityMatch = provinceMatch?.children.find(i => i.code === code);
237
+ this.districts = cityMatch ? cityMatch.children : [];
238
+ }
239
+ this.emitChange();
240
+ },
241
+ handleDistrictChange(code) {
242
+ this.internalValue.town = null;
243
+ this.towns = [];
244
+ if (code) {
245
+ const provinceMatch = this.data.find(i => i.code === this.internalValue.province);
246
+ const cityMatch = provinceMatch?.children.find(i => i.code === this.internalValue.city);
247
+ const districtMatch = cityMatch?.children.find(i => i.code === code);
248
+ this.towns = districtMatch ? districtMatch.children : [];
249
+ }
250
+ this.emitChange();
251
+ },
252
+ emitChange() {
253
+ this.$emit('input', { ...this.fullValue });
254
+ this.$emit('change', { ...this.fullValue });
255
+ }
256
+ }
257
+ };
258
+ </script>
259
+
260
+ <style scoped>
261
+ </style>