mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
Merge remote-tracking branch 'main/dev' into dev
This commit is contained in:
commit
594b0e5bb1
133
docs/examples-docs/area.md
Normal file
133
docs/examples-docs/area.md
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<script>
|
||||||
|
import AreaList from '../mock/area.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
areaList: AreaList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## Area 省市县选择组件
|
||||||
|
|
||||||
|
### 使用指南
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
import { Area } from 'vant';
|
||||||
|
|
||||||
|
Vue.component(Area.name, Area);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码演示
|
||||||
|
|
||||||
|
#### 基础用法
|
||||||
|
|
||||||
|
要初始化一个`Area`组件,你需要传入一个`areaList`属性,`areaList`数据格式具体可看下面数据格式章节。
|
||||||
|
|
||||||
|
:::demo 基础用法
|
||||||
|
```html
|
||||||
|
<van-area :area-list="areaList"></van-area>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AreaList from '../mock/area.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
areaList: AreaList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### 选中省市县
|
||||||
|
|
||||||
|
如果想选中某个省市县,需要传入一个`value`属性,绑定对应的省市县`code`。
|
||||||
|
|
||||||
|
:::demo 选中省市县
|
||||||
|
```html
|
||||||
|
<van-area :area-list="areaList" value="110101"></van-area>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### 配置显示列
|
||||||
|
|
||||||
|
可以通过`columnsNum`属性配置省市县显示的列数,默认情况下会显示省市县,当你设置为`2`,则只会显示省市选择。
|
||||||
|
|
||||||
|
:::demo 配置显示列
|
||||||
|
```html
|
||||||
|
<van-area :area-list="areaList" :columns-num="2"></van-area>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|
||||||
|
|-----------|-----------|-----------|-------------|-------------|
|
||||||
|
| value | 当前选中的省市区`code` | `String` | - | |
|
||||||
|
| areaList | 省市县数据,必须与`province_list`、`city_list`和`county_list`为key | `Object` | | |
|
||||||
|
| columnsNum | 省市县显示列数,3-省市县,2-省市,1-省 | `String`,`Number` | 3 | |
|
||||||
|
|
||||||
|
### Event
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 回调参数 |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| confirm | 点击右上方完成按钮 | 一个数组参数,具体格式看下方数据格式章节 |
|
||||||
|
| cancel | 点击取消按钮时 | - |
|
||||||
|
|
||||||
|
### 数据格式
|
||||||
|
|
||||||
|
#### 省市县列表数据格式
|
||||||
|
|
||||||
|
整体是一个Object,包含 `province_list`, `city_list`, `county_list` 三个key。
|
||||||
|
|
||||||
|
每项以省市区编码作为key,省市区名字作为value。编码为6位数字,前两位代表省份,中间两位代表城市,后两位代表区县,以0补足6位。如北京编码为 `11`,以零补足6位,为 `110000`。
|
||||||
|
|
||||||
|
`AreaList`具体格式如下:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
province_list: {
|
||||||
|
110000: '北京市',
|
||||||
|
120000: '天津市'
|
||||||
|
},
|
||||||
|
city_list: {
|
||||||
|
110100: '北京市',
|
||||||
|
110200: '县',
|
||||||
|
120100: '天津市',
|
||||||
|
120200: '县'
|
||||||
|
},
|
||||||
|
county_list: {
|
||||||
|
110101: '东城区',
|
||||||
|
110102: '西城区',
|
||||||
|
110105: '朝阳区',
|
||||||
|
110106: '丰台区'
|
||||||
|
120101: '和平区',
|
||||||
|
120102: '河东区',
|
||||||
|
120103: '河西区',
|
||||||
|
120104: '南开区',
|
||||||
|
120105: '河北区',
|
||||||
|
// ....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 点击完成时返回的数据格式
|
||||||
|
返回的数据整体为一个数组,数组内包含 `columnsNum` 个数据, 每个数据对应一列选项中被选中的数据。
|
||||||
|
|
||||||
|
`code` 代表被选中的地区编码, `name` 代表被选中的地区名称
|
||||||
|
```javascript
|
||||||
|
[{
|
||||||
|
code: '110000',
|
||||||
|
name: '北京市'
|
||||||
|
}, {
|
||||||
|
code: '110100',
|
||||||
|
name: '北京市'
|
||||||
|
},{
|
||||||
|
code: '110101',
|
||||||
|
name: '东城区'
|
||||||
|
}]
|
||||||
|
```
|
3607
docs/mock/area.json
Normal file
3607
docs/mock/area.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -220,6 +220,10 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
"path": "/switch-cell",
|
"path": "/switch-cell",
|
||||||
"title": "SwitchCell 开关单元格"
|
"title": "SwitchCell 开关单元格"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/area",
|
||||||
|
"title": "Area 省市区选择"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
159
packages/area/index.vue
Normal file
159
packages/area/index.vue
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="van-area">
|
||||||
|
<van-picker ref="picker" :columns="areaColumns" value-key="name" show-toolbar @change="handleAreaChange" @confirm="handleAreaConfirm" @cancel="handleAreaCancel"></van-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Picker from '../picker';
|
||||||
|
|
||||||
|
const DEFAULT_PROVINCE = {
|
||||||
|
code: '-1',
|
||||||
|
name: '选择省份'
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_CITY = {
|
||||||
|
code: '-1',
|
||||||
|
name: '选择城市'
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_COUNTY = {
|
||||||
|
code: '-1',
|
||||||
|
name: '选择地区'
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROVINCE_TYPE = 'provice';
|
||||||
|
const CITY_TYPE = 'city';
|
||||||
|
const COUNTY_TYPE = 'county';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'van-area',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
[Picker.name]: Picker
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {},
|
||||||
|
areaList: Object,
|
||||||
|
/**
|
||||||
|
* 省市县显示列数,3-省市县,2-省市,1-省
|
||||||
|
*/
|
||||||
|
columnsNum: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
areaColumns() {
|
||||||
|
const areaList = this.areaList;
|
||||||
|
|
||||||
|
if (!areaList || (areaList && typeof areaList.province_list !== 'object')) return [];
|
||||||
|
|
||||||
|
const columns = [];
|
||||||
|
const curValue = this.value || '';
|
||||||
|
|
||||||
|
columns.push({
|
||||||
|
values: [DEFAULT_PROVINCE].concat(this.computedAreaList(PROVINCE_TYPE)),
|
||||||
|
className: 'van-area__province',
|
||||||
|
defaultIndex: this.getAreaIndex(PROVINCE_TYPE, curValue)
|
||||||
|
});
|
||||||
|
|
||||||
|
const columnsNum = this.columnsNum;
|
||||||
|
if (+columnsNum > 1) {
|
||||||
|
columns.push({
|
||||||
|
values: [DEFAULT_CITY].concat(this.computedAreaList(CITY_TYPE, curValue.slice(0, 2))),
|
||||||
|
className: 'van-area__city',
|
||||||
|
defaultIndex: this.getAreaIndex(CITY_TYPE, curValue)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (+columnsNum > 2) {
|
||||||
|
columns.push({
|
||||||
|
values: [DEFAULT_COUNTY].concat(this.computedAreaList(COUNTY_TYPE, curValue.slice(0, 4))),
|
||||||
|
className: 'van-area__county',
|
||||||
|
defaultIndex: this.getAreaIndex(COUNTY_TYPE, curValue)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 根据省市县类型和对应的`code`获取对应列表
|
||||||
|
*
|
||||||
|
* @param {string} type 省市县类型
|
||||||
|
* @param {string} code 对应code
|
||||||
|
*/
|
||||||
|
computedAreaList(type, code) {
|
||||||
|
const result = [];
|
||||||
|
const curAreaList = this.areaList;
|
||||||
|
const areaList = type === PROVINCE_TYPE
|
||||||
|
? curAreaList.province_list
|
||||||
|
: (type === CITY_TYPE ? curAreaList.city_list : curAreaList.county_list);
|
||||||
|
|
||||||
|
for (const i in areaList) {
|
||||||
|
// 如果为省类型直接插入,因为省那一列是全部显示的
|
||||||
|
// 其他类型需要找到前缀相同的
|
||||||
|
if (type === PROVINCE_TYPE || (code && i.slice(0, code.length) === code)) {
|
||||||
|
result.push({
|
||||||
|
code: i,
|
||||||
|
name: areaList[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对应省市县在列表中的索引
|
||||||
|
*/
|
||||||
|
getAreaIndex(type, code) {
|
||||||
|
const compareNum = type === PROVINCE_TYPE
|
||||||
|
? 2
|
||||||
|
: (type === CITY_TYPE ? 4 : 6);
|
||||||
|
const areaList = this.computedAreaList(type, code.slice(0, compareNum - 2));
|
||||||
|
|
||||||
|
for (let i = 0; i < areaList.length; i++) {
|
||||||
|
if (+areaList[i].code.slice(0, compareNum) === +code.slice(0, compareNum)) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAreaChange(picker, values, index) {
|
||||||
|
const code = values[index].code;
|
||||||
|
// 处理省变化
|
||||||
|
if (index === 0) {
|
||||||
|
picker.setColumnValues(
|
||||||
|
1,
|
||||||
|
[DEFAULT_CITY].concat(this.computedAreaList(CITY_TYPE, code.slice(0, 2)))
|
||||||
|
);
|
||||||
|
picker.setColumnValues(
|
||||||
|
2,
|
||||||
|
[DEFAULT_COUNTY].concat(this.computedAreaList(COUNTY_TYPE, code.slice(0, 4)))
|
||||||
|
);
|
||||||
|
} else if (index === 1) {
|
||||||
|
picker.setColumnValues(
|
||||||
|
2,
|
||||||
|
[DEFAULT_COUNTY].concat(this.computedAreaList(COUNTY_TYPE, code.slice(0, 4)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAreaConfirm(values) {
|
||||||
|
this.$emit('confirm', values);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAreaCancel() {
|
||||||
|
this.$emit('cancel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,4 +1,5 @@
|
|||||||
import Actionsheet from './actionsheet';
|
import Actionsheet from './actionsheet';
|
||||||
|
import Area from './area';
|
||||||
import Badge from './badge';
|
import Badge from './badge';
|
||||||
import BadgeGroup from './badge-group';
|
import BadgeGroup from './badge-group';
|
||||||
import Button from './button';
|
import Button from './button';
|
||||||
@ -53,6 +54,7 @@ import Waterfall from './waterfall';
|
|||||||
const version = '0.8.9';
|
const version = '0.8.9';
|
||||||
const components = [
|
const components = [
|
||||||
Actionsheet,
|
Actionsheet,
|
||||||
|
Area,
|
||||||
Badge,
|
Badge,
|
||||||
BadgeGroup,
|
BadgeGroup,
|
||||||
Button,
|
Button,
|
||||||
@ -117,6 +119,7 @@ export {
|
|||||||
install,
|
install,
|
||||||
version,
|
version,
|
||||||
Actionsheet,
|
Actionsheet,
|
||||||
|
Area,
|
||||||
Badge,
|
Badge,
|
||||||
BadgeGroup,
|
BadgeGroup,
|
||||||
Button,
|
Button,
|
||||||
|
3607
test/unit/mock/area.json
Normal file
3607
test/unit/mock/area.json
Normal file
File diff suppressed because it is too large
Load Diff
117
test/unit/specs/area.spec.js
Normal file
117
test/unit/specs/area.spec.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import Area from 'packages/area';
|
||||||
|
import { mount } from 'avoriaz';
|
||||||
|
import AreaList from '../mock/area.json';
|
||||||
|
|
||||||
|
describe('Area', () => {
|
||||||
|
let wrapper;
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper && wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area', () => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area with default value', (done) => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList,
|
||||||
|
value: '110101'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
|
||||||
|
const confirmBtn = wrapper.find('.van-picker__confirm')[0];
|
||||||
|
const eventStub = sinon.stub(wrapper.vm, '$emit');
|
||||||
|
|
||||||
|
confirmBtn.trigger('click');
|
||||||
|
wrapper.vm.$nextTick(() => {
|
||||||
|
expect(eventStub.calledOnce).to.be.true;
|
||||||
|
expect(eventStub.calledWith('confirm'));
|
||||||
|
expect(wrapper.vm.$refs.picker.getColumnValue(2).code).to.equal('110101');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area and set value', (done) => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList,
|
||||||
|
value: '110101'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
expect(wrapper.vm.$refs.picker.getColumnValue(2).code).to.equal('110101');
|
||||||
|
|
||||||
|
wrapper.setProps({
|
||||||
|
value: '110102'
|
||||||
|
});
|
||||||
|
wrapper.vm.$nextTick(() => {
|
||||||
|
expect(wrapper.vm.$refs.picker.getColumnValue(2).code).to.equal('110102');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area with invalid areaList', () => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
expect(wrapper.vm.areaColumns.length).to.equal(0);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area with columnsNum equal 2', () => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList,
|
||||||
|
columnsNum: 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
expect(wrapper.vm.areaColumns.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area with columnsNum equal 1', () => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList,
|
||||||
|
columnsNum: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
expect(wrapper.vm.areaColumns.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an area and click cancel', (done) => {
|
||||||
|
wrapper = mount(Area, {
|
||||||
|
propsData: {
|
||||||
|
areaList: AreaList
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.hasClass('van-area')).to.be.true;
|
||||||
|
const cancelBtn = wrapper.find('.van-picker__cancel')[0];
|
||||||
|
const eventStub = sinon.stub(wrapper.vm, '$emit');
|
||||||
|
|
||||||
|
cancelBtn.trigger('click');
|
||||||
|
wrapper.vm.$nextTick(() => {
|
||||||
|
expect(eventStub.calledOnce).to.be.true;
|
||||||
|
expect(eventStub.calledWith('cancel'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user