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 +50 -20
- package/package.json +1 -1
- package/src/Introduction.stories.mdx +2 -2
- package/src/components/JhAddressSelect/JhAddressSelect.md +19 -2
- package/src/components/JhAddressSelect/JhAddressSelect.stories.js +38 -3
- package/src/components/JhAddressSelect/JhAddressSelect.vue +229 -81
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.
|
|
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@
|
|
72
|
-
<!--
|
|
73
|
-
<link href="https://
|
|
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-
|
|
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.
|
|
93
|
+
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.7.0/dist/vuetify.js"></script>
|
|
90
94
|
<!-- JianghuJS UI 运行时代码 -->
|
|
91
|
-
<script src="https://
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
101
|
+
data: () => ({
|
|
102
|
+
// 你的数据
|
|
103
|
+
})
|
|
106
104
|
});
|
|
107
105
|
</script>
|
|
108
106
|
</body>
|
|
109
107
|
</html>
|
|
110
108
|
```
|
|
111
109
|
|
|
112
|
-
|
|
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
|
@@ -24,13 +24,13 @@ import { Meta } from '@storybook/blocks';
|
|
|
24
24
|
<!DOCTYPE html>
|
|
25
25
|
<html>
|
|
26
26
|
<head>
|
|
27
|
-
<link href="https://
|
|
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://
|
|
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
|
-
- ✅ **返回完整信息**:
|
|
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
|
-
<
|
|
3
|
-
<v-
|
|
4
|
-
<v-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
<
|
|
45
|
-
<v-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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>
|