jianghu-ui 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -55,6 +55,8 @@ npm run serve-storybook
55
55
 
56
56
  ## CDN 使用方式
57
57
 
58
+ ### 快速引入
59
+
58
60
  在你的 HTML 文件中引入必要的资源:
59
61
 
60
62
  ```html
@@ -66,50 +68,78 @@ npm run serve-storybook
66
68
  <title>JianghuJS UI Demo</title>
67
69
 
68
70
  <!-- Vuetify CSS -->
69
- <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
71
+ <link href="https://cdn.jsdelivr.net/npm/vuetify@2.7.0/dist/vuetify.min.css" rel="stylesheet">
70
72
  <!-- Material Design Icons -->
71
- <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
72
- <!-- Roboto 字体 -->
73
- <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
74
- <!-- JianghuJS UI 核心样式(锁定具体版本号,如 1.0.0) -->
75
- <link href="https://jianghujs.github.io/jianghu-ui/cdn/<版本号>/jianghu-ui.css" rel="stylesheet">
73
+ <link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
74
+ <!-- JianghuJS UI 核心样式(使用具体版本号,如 1.0.1) -->
75
+ <link href="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.css" rel="stylesheet">
76
76
  </head>
77
77
  <body>
78
78
  <div id="app">
79
79
  <v-app>
80
80
  <v-container>
81
- <jh-button label="点击我" color="primary" @click="handleClick" />
81
+ <jh-card title="示例卡片" bordered>
82
+ <div style="padding: 20px;">
83
+ <p>这是卡片内容</p>
84
+ </div>
85
+ </jh-card>
82
86
  </v-container>
83
87
  </v-app>
84
88
  </div>
85
89
 
86
90
  <!-- Vue 2 -->
87
- <script src="https://cdn.jsdelivr.net/npm/vue@2.7/dist/vue.js"></script>
91
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
88
92
  <!-- Vuetify -->
89
- <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.js"></script>
93
+ <script src="https://cdn.jsdelivr.net/npm/vuetify@2.7.0/dist/vuetify.js"></script>
90
94
  <!-- JianghuJS UI 运行时代码 -->
91
- <script src="https://jianghujs.github.io/jianghu-ui/cdn/<版本号>/jianghu-ui.js"></script>
95
+ <script src="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.js"></script>
92
96
 
93
97
  <script>
94
- Vue.component('jh-button', {
95
- // 引入你的组件定义
96
- });
97
-
98
98
  new Vue({
99
99
  el: '#app',
100
100
  vuetify: new Vuetify(),
101
- methods: {
102
- handleClick() {
103
- alert('按钮被点击了!');
104
- }
105
- }
101
+ data: () => ({
102
+ // 你的数据
103
+ })
106
104
  });
107
105
  </script>
108
106
  </body>
109
107
  </html>
110
108
  ```
111
109
 
112
- > CDN 产物会被发布到 `https://jianghujs.github.io/jianghu-ui/cdn/<版本号>/` 目录中,示例:`cdn/1.0.0/jianghu-ui.js`。生产环境务必固定具体版本,若仅需查看最新构建可临时使用 `cdn/latest/`。
110
+ ### CDN 版本选择
111
+
112
+ #### 使用最新版本
113
+ ```html
114
+ <link href="https://cdn.jsdelivr.net/npm/jianghu-ui@latest/dist/jianghu-ui.css" rel="stylesheet">
115
+ <script src="https://cdn.jsdelivr.net/npm/jianghu-ui@latest/dist/jianghu-ui.js"></script>
116
+ ```
117
+
118
+ #### 使用指定版本(推荐生产环境)
119
+ ```html
120
+ <link href="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.css" rel="stylesheet">
121
+ <script src="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.js"></script>
122
+ ```
123
+
124
+ ### 可用 CDN 源
125
+
126
+ #### jsDelivr(推荐)
127
+ ```html
128
+ <link href="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.css" rel="stylesheet">
129
+ <script src="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.js"></script>
130
+ ```
131
+
132
+ #### unpkg
133
+ ```html
134
+ <link href="https://unpkg.com/jianghu-ui@1.0.1/dist/jianghu-ui.css" rel="stylesheet">
135
+ <script src="https://unpkg.com/jianghu-ui@1.0.1/dist/jianghu-ui.js"></script>
136
+ ```
137
+
138
+ ### 查看完整示例
139
+
140
+ 查看 [examples/cdn-demo.html](examples/cdn-demo.html) 获取完整的使用示例和更多组件演示。
141
+
142
+ > ✅ CDN 产物会被发布到 npm,通过 jsDelivr/unpkg 提供 CDN 加速。生产环境务必固定具体版本,避免 `@latest` 导致的意外更新。
113
143
 
114
144
  ## 组件列表
115
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jianghu-ui",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "JianghuJS UI Component Library with Storybook, Vue 2, and Vuetify 2",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -24,13 +24,13 @@ import { Meta } from '@storybook/blocks';
24
24
  <!DOCTYPE html>
25
25
  <html>
26
26
  <head>
27
- <link href="https://jianghujs.github.io/jianghu-ui/cdn/jianghu-ui.css" rel="stylesheet">
27
+ <link href="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.css" rel="stylesheet">
28
28
  </head>
29
29
  <body>
30
30
  <div id="app">
31
31
  <!-- 你的应用内容 -->
32
32
  </div>
33
- <script src="https://jianghujs.github.io/jianghu-ui/cdn/jianghu-ui.js"></script>
33
+ <script src="https://cdn.jsdelivr.net/npm/jianghu-ui@1.0.1/dist/jianghu-ui.js"></script>
34
34
  </body>
35
35
  </html>
36
36
  ```
@@ -10,7 +10,8 @@
10
10
  - ✅ **双向绑定**: 支持 v-model 双向数据绑定
11
11
  - ✅ **事件触发**: change 事件在值变化时触发
12
12
  - ✅ **清除功能**: 支持清除已选值
13
- - ✅ **返回完整信息**: 事件返回包含 code 和 name 的完整对象
13
+ - ✅ **返回完整信息**: 事件返回包�� code 和 name 的完整对象
14
+ - ✅ **多种模式**: 支持普通联动选择 (select) 和级联选择 (cascader) 两种模式
14
15
 
15
16
  ## 基本用法
16
17
 
@@ -54,6 +55,8 @@ export default {
54
55
  | --- | --- | --- | --- |
55
56
  | value / v-model | 绑定值,返回包含 code 和 name 的对象 | object | { province: null, city: null, district: null } |
56
57
  | level | 显示层级:1-仅省份,2-省市,3-省市区,4-省市区镇 | number | 3 |
58
+ | type | 显示模式:'select' (默认) 或 'cascader' | string | 'select' |
59
+ | label | 级联模式下的输入框标签 | string | '请选择地区' |
57
60
  | outlined | 是否使用 outlined 样式 | boolean | true |
58
61
  | dense | 是否使用紧凑模式 | boolean | false |
59
62
  | loading | 是否显示加载状态 | boolean | false |
@@ -118,6 +121,19 @@ data 属性需要符合以下格式:
118
121
  ></jh-address-select>
119
122
  ```
120
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
+
121
137
  ### 仅选择到城市
122
138
 
123
139
  ```vue
@@ -243,8 +259,9 @@ export default {
243
259
  4. **禁用状态**: 未选择上级时,下级选择器会自动禁用
244
260
  5. **响应式布局**: 组件会根据 level 自动调整栅格布局(level=1 时占 12 列,level=2 时占 6 列,level=3 时占 4 列,level=4 时占 3 列)
245
261
  6. **返回值格式**: v-model 和事件返回的值包含 code 和 name 两个字段,方便获取编码和名称
262
+ 7. **级联模式**: 在 cascader 模式下,点击省份/城市/区县会展示下一级列表,直到选择完指定 level 级数后自动收起菜单
246
263
 
247
264
  ## 相关链接
248
265
 
249
266
  - [Vuetify Autocomplete](https://vuetifyjs.com/en/components/autocompletes/)
250
- - [中国行政区划代码](http://www.mca.gov.cn/article/sj/xzqh/)
267
+ - [中国行政区划代码](http://www.mca.gov.cn/article/sj/xzqh/)
@@ -31,6 +31,23 @@ export default {
31
31
  defaultValue: { summary: '3' },
32
32
  },
33
33
  },
34
+ type: {
35
+ control: { type: 'select' },
36
+ options: ['select', 'cascader'],
37
+ description: '显示模式:select-普通联动,cascader-级联选择',
38
+ table: {
39
+ type: { summary: 'string' },
40
+ defaultValue: { summary: 'select' },
41
+ },
42
+ },
43
+ label: {
44
+ control: 'text',
45
+ description: '级联模式下的输入框标签',
46
+ table: {
47
+ type: { summary: 'string' },
48
+ defaultValue: { summary: '请选择地区' },
49
+ },
50
+ },
34
51
  outlined: {
35
52
  control: 'boolean',
36
53
  description: '是否使用 `outlined` 样式',
@@ -105,6 +122,8 @@ const Template = (args) => ({
105
122
  <jh-address-select
106
123
  v-model="value"
107
124
  :level="args.level"
125
+ :type="args.type"
126
+ :label="args.label"
108
127
  :outlined="args.outlined"
109
128
  :dense="args.dense"
110
129
  :loading="args.loading"
@@ -123,6 +142,8 @@ const Template = (args) => ({
123
142
  const baseArgs = {
124
143
  value: { province: null, city: null, district: null, town: null },
125
144
  level: 3,
145
+ type: 'select',
146
+ label: '请选择地区',
126
147
  outlined: true,
127
148
  dense: true,
128
149
  loading: false,
@@ -144,6 +165,21 @@ Default.parameters = {
144
165
  },
145
166
  };
146
167
 
168
+ export const CascaderMode = Template.bind({});
169
+ CascaderMode.storyName = "级联选择模式";
170
+ CascaderMode.args = {
171
+ ...baseArgs,
172
+ type: 'cascader',
173
+ label: '请选择收货地址',
174
+ };
175
+ CascaderMode.parameters = {
176
+ docs: {
177
+ description: {
178
+ story: '使用 `type="cascader"` 开启级联选择模式,在一个下拉菜单中完成多级选择。',
179
+ },
180
+ },
181
+ };
182
+
147
183
  export const WithInitialValue = Template.bind({});
148
184
  WithInitialValue.storyName = "带初始值";
149
185
  WithInitialValue.args = {
@@ -157,7 +193,7 @@ WithInitialValue.args = {
157
193
  WithInitialValue.parameters = {
158
194
  docs: {
159
195
  description: {
160
- story: '设置 `v-model` 的初始值可以使组件在加载时就显示已选定的地址。返回值包含 `code` 和 `name` 两个字段。',
196
+ story: '设置 `v-model` 的初始值可以使组件在加载时就显示已选定的地址。返回值包含 `code` 和 `name` ���个字段。',
161
197
  },
162
198
  },
163
199
  };
@@ -278,5 +314,4 @@ Level4WithInitialValue.parameters = {
278
314
  story: '四级联动并设置初始值,展示完整的省市区镇选择功能。',
279
315
  },
280
316
  },
281
- };
282
-
317
+ };
@@ -1,86 +1,158 @@
1
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>
2
+ <div class="jh-address-select">
3
+ <template v-if="type === 'select'">
4
+ <v-row dense>
5
+ <v-col cols="12" :md="gridCol">
6
+ <v-autocomplete
7
+ class="jh-v-input"
8
+ v-model="internalValue.province"
9
+ :items="provinces"
10
+ :label="labels.province"
11
+ :outlined="outlined"
12
+ :dense="dense"
13
+ :filled="filled"
14
+ :single-line="singleLine"
15
+ :loading="loading"
16
+ item-text="name"
17
+ item-value="code"
18
+ clearable
19
+ hide-details
20
+ hide-no-data
21
+ prepend-inner-icon="mdi-map-outline"
22
+ @change="handleProvinceChange"
23
+ ></v-autocomplete>
24
+ </v-col>
25
+
26
+ <v-col v-if="level >= 2" cols="12" :md="gridCol">
27
+ <v-autocomplete
28
+ class="jh-v-input"
29
+ v-model="internalValue.city"
30
+ :items="cities"
31
+ :label="labels.city"
32
+ :disabled="!internalValue.province"
33
+ :outlined="outlined"
34
+ :dense="dense"
35
+ :filled="filled"
36
+ :single-line="singleLine"
37
+ :loading="loading"
38
+ item-text="name"
39
+ item-value="code"
40
+ clearable
41
+ prepend-inner-icon="mdi-city-variant-outline"
42
+ @change="handleCityChange"
43
+ ></v-autocomplete>
44
+ </v-col>
45
+
46
+ <v-col v-if="level >= 3" cols="12" :md="gridCol">
47
+ <v-autocomplete
48
+ class="jh-v-input"
49
+ v-model="internalValue.district"
50
+ :items="districts"
51
+ :label="labels.district"
52
+ :disabled="!internalValue.city"
53
+ :outlined="outlined"
54
+ :dense="dense"
55
+ :filled="filled"
56
+ :single-line="singleLine"
57
+ :loading="loading"
58
+ item-text="name"
59
+ item-value="code"
60
+ clearable
61
+ prepend-inner-icon="mdi-home-city-outline"
62
+ @change="handleDistrictChange"
63
+ ></v-autocomplete>
64
+ </v-col>
65
+
66
+ <v-col v-if="level >= 4" cols="12" :md="gridCol">
67
+ <v-autocomplete
68
+ class="jh-v-input"
69
+ v-model="internalValue.town"
70
+ :items="towns"
71
+ :label="labels.town"
72
+ :disabled="!internalValue.district"
73
+ :outlined="outlined"
74
+ :dense="dense"
75
+ :filled="filled"
76
+ :single-line="singleLine"
77
+ :loading="loading"
78
+ item-text="name"
79
+ item-value="code"
80
+ clearable
81
+ prepend-inner-icon="mdi-home-variant-outline"
82
+ @change="emitChange"
83
+ ></v-autocomplete>
84
+ </v-col>
85
+ </v-row>
86
+ </template>
43
87
 
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>
88
+ <template v-else-if="type === 'cascader'">
89
+ <v-menu
90
+ v-model="menuVisible"
91
+ :close-on-content-click="false"
92
+ offset-y
93
+ max-width="100%"
94
+ transition="scale-transition"
95
+ >
96
+ <template v-slot:activator="{ on, attrs }">
97
+ <v-text-field
98
+ class="jh-v-input"
99
+ v-bind="attrs"
100
+ v-on="on"
101
+ :value="displayText"
102
+ :label="label"
103
+ :outlined="outlined"
104
+ :dense="dense"
105
+ :filled="filled"
106
+ :single-line="singleLine"
107
+ :loading="loading"
108
+ readonly
109
+ append-icon="mdi-menu-down"
110
+ clearable
111
+ hide-details
112
+ @click:clear="clearValue"
113
+ ></v-text-field>
114
+ </template>
115
+ <v-card class="jh-cascader-card">
116
+ <div class="jh-cascader-container">
117
+ <div class="jh-cascader-column" v-if="provinces.length">
118
+ <v-list dense class="pa-0">
119
+ <v-list-item v-for="item in provinces" :key="item.code" @click="onProvinceClick(item)" :class="{'v-item--active v-list-item--active primary--text': internalValue.province === item.code}">
120
+ <v-list-item-content><v-list-item-title :title="item.name">{{ item.name }}</v-list-item-title></v-list-item-content>
121
+ <v-list-item-action v-if="level > 1"><v-icon small>mdi-chevron-right</v-icon></v-list-item-action>
122
+ </v-list-item>
123
+ </v-list>
124
+ </div>
63
125
 
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>
126
+ <div v-if="level >= 2" class="jh-cascader-column">
127
+ <v-list dense class="pa-0" v-if="cities.length">
128
+ <v-list-item v-for="item in cities" :key="item.code" @click="onCityClick(item)" :class="{'v-item--active v-list-item--active primary--text': internalValue.city === item.code}">
129
+ <v-list-item-content><v-list-item-title :title="item.name">{{ item.name }}</v-list-item-title></v-list-item-content>
130
+ <v-list-item-action v-if="level > 2"><v-icon small>mdi-chevron-right</v-icon></v-list-item-action>
131
+ </v-list-item>
132
+ </v-list>
133
+ </div>
134
+
135
+ <div v-if="level >= 3" class="jh-cascader-column">
136
+ <v-list dense class="pa-0" v-if="districts.length">
137
+ <v-list-item v-for="item in districts" :key="item.code" @click="onDistrictClick(item)" :class="{'v-item--active v-list-item--active primary--text': internalValue.district === item.code}">
138
+ <v-list-item-content><v-list-item-title :title="item.name">{{ item.name }}</v-list-item-title></v-list-item-content>
139
+ <v-list-item-action v-if="level > 3"><v-icon small>mdi-chevron-right</v-icon></v-list-item-action>
140
+ </v-list-item>
141
+ </v-list>
142
+ </div>
143
+
144
+ <div v-if="level >= 4" class="jh-cascader-column">
145
+ <v-list dense class="pa-0" v-if="towns.length">
146
+ <v-list-item v-for="item in towns" :key="item.code" @click="onTownClick(item)" :class="{'v-item--active v-list-item--active primary--text': internalValue.town === item.code}">
147
+ <v-list-item-content><v-list-item-title :title="item.name">{{ item.name }}</v-list-item-title></v-list-item-content>
148
+ </v-list-item>
149
+ </v-list>
150
+ </div>
151
+ </div>
152
+ </v-card>
153
+ </v-menu>
154
+ </template>
155
+ </div>
84
156
  </template>
85
157
 
86
158
  <script>
@@ -96,6 +168,8 @@ export default {
96
168
  dense: { type: Boolean, default: true },
97
169
  filled: { type: Boolean, default: true },
98
170
  singleLine: { type: Boolean, default: true },
171
+ type: { type: String, default: 'select' }, // select | cascader
172
+ label: { type: String, default: '请选择地区' },
99
173
 
100
174
  loading: { type: Boolean, default: false },
101
175
  labels: {
@@ -121,7 +195,8 @@ export default {
121
195
  internalValue: { ...this.value },
122
196
  cities: [],
123
197
  districts: [],
124
- towns: []
198
+ towns: [],
199
+ menuVisible: false,
125
200
  };
126
201
  },
127
202
  computed: {
@@ -134,6 +209,28 @@ export default {
134
209
  if (this.level === 3) return 4;
135
210
  return 3;
136
211
  },
212
+ displayText() {
213
+ if (!this.internalValue.province) return '';
214
+ const p = this.provinces.find(x => x.code === this.internalValue.province);
215
+ let text = p ? p.name : '';
216
+
217
+ if (this.level >= 2 && this.internalValue.city) {
218
+ const c = this.cities.find(x => x.code === this.internalValue.city);
219
+ if (c) text += ` / ${c.name}`;
220
+ }
221
+
222
+ if (this.level >= 3 && this.internalValue.district) {
223
+ const d = this.districts.find(x => x.code === this.internalValue.district);
224
+ if (d) text += ` / ${d.name}`;
225
+ }
226
+
227
+ if (this.level >= 4 && this.internalValue.town) {
228
+ const t = this.towns.find(x => x.code === this.internalValue.town);
229
+ if (t) text += ` / ${t.name}`;
230
+ }
231
+
232
+ return text;
233
+ },
137
234
  fullValue() {
138
235
  const result = {
139
236
  province: null,
@@ -252,10 +349,61 @@ export default {
252
349
  emitChange() {
253
350
  this.$emit('input', { ...this.fullValue });
254
351
  this.$emit('change', { ...this.fullValue });
352
+ },
353
+ onProvinceClick(item) {
354
+ this.internalValue.province = item.code;
355
+ this.handleProvinceChange(item.code);
356
+ if (this.level === 1) this.menuVisible = false;
357
+ },
358
+ onCityClick(item) {
359
+ this.internalValue.city = item.code;
360
+ this.handleCityChange(item.code);
361
+ if (this.level === 2) this.menuVisible = false;
362
+ },
363
+ onDistrictClick(item) {
364
+ this.internalValue.district = item.code;
365
+ this.handleDistrictChange(item.code);
366
+ if (this.level === 3) this.menuVisible = false;
367
+ },
368
+ onTownClick(item) {
369
+ this.internalValue.town = item.code;
370
+ this.emitChange();
371
+ if (this.level === 4) this.menuVisible = false;
372
+ },
373
+ clearValue() {
374
+ this.internalValue = { province: null, city: null, district: null, town: null };
375
+ this.cities = [];
376
+ this.districts = [];
377
+ this.towns = [];
378
+ this.emitChange();
255
379
  }
256
380
  }
257
381
  };
258
382
  </script>
259
383
 
260
384
  <style scoped>
385
+ .jh-cascader-container {
386
+ display: flex;
387
+ overflow-x: auto;
388
+ white-space: nowrap;
389
+ }
390
+ .jh-cascader-column {
391
+ min-width: 180px;
392
+ max-width: 250px;
393
+ max-height: 400px;
394
+ overflow-y: auto;
395
+ border-right: 1px solid #eee;
396
+ }
397
+ .jh-cascader-column:last-child {
398
+ border-right: none;
399
+ }
400
+ /* 优化列表项内容防止过早截断 */
401
+ .jh-cascader-column .v-list-item__content {
402
+ overflow: visible;
403
+ }
404
+ .jh-cascader-column .v-list-item__title {
405
+ white-space: nowrap;
406
+ overflow: hidden;
407
+ text-overflow: ellipsis;
408
+ }
261
409
  </style>