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",
|
||||
"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 Area from './area';
|
||||
import Badge from './badge';
|
||||
import BadgeGroup from './badge-group';
|
||||
import Button from './button';
|
||||
@ -53,6 +54,7 @@ import Waterfall from './waterfall';
|
||||
const version = '0.8.9';
|
||||
const components = [
|
||||
Actionsheet,
|
||||
Area,
|
||||
Badge,
|
||||
BadgeGroup,
|
||||
Button,
|
||||
@ -117,6 +119,7 @@ export {
|
||||
install,
|
||||
version,
|
||||
Actionsheet,
|
||||
Area,
|
||||
Badge,
|
||||
BadgeGroup,
|
||||
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