[new feature] add i18n support (#310)
* fix: Tabbar icon line-height * [new feature] progress add showPivot prop * [new feature] TabItem support vue-router * [new feature] update document header style * [Doc] add toast english ducoment * [new feature] add i18n support * feat: Extract demos from markdown * feat: Base components demos * [new feature] complete demo extract & translate * [fix] text cases * fix: add deepAssign test cases * fix: changelog detail * [new feature] AddressEdit support i18n
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div ref="root">
|
||||
<van-field
|
||||
label="详细地址"
|
||||
placeholder="如街道、楼层、门牌号等"
|
||||
:label="$t('label.address')"
|
||||
:placeholder="$t('placeholder.address')"
|
||||
maxlength="200"
|
||||
type="textarea"
|
||||
autosize
|
||||
@@ -15,7 +15,7 @@
|
||||
@blur="handleBlur"
|
||||
>
|
||||
<div slot="icon">
|
||||
<span v-if="showIcon && isAndroid" class="van-address-edit-detail__finish-edit">完成</span>
|
||||
<span v-if="showIcon && isAndroid" class="van-address-edit-detail__finish-edit">{{ $t('complete') }}</span>
|
||||
<van-icon v-else-if="showIcon" name="clear" />
|
||||
</div>
|
||||
</van-field>
|
||||
@@ -42,10 +42,13 @@ import Field from '../field';
|
||||
import Cell from '../cell';
|
||||
import CellGroup from '../cell-group';
|
||||
import isAndroid from '../utils/env/is-android';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-address-edit-detail',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Field.name]: Field,
|
||||
[Icon.name]: Icon,
|
||||
@@ -85,7 +88,7 @@ export default {
|
||||
},
|
||||
|
||||
handleBlur(e) {
|
||||
// 等待其他地方点击事件完了以后,再触发
|
||||
// wait for click event finished
|
||||
setTimeout(() => {
|
||||
this.isFocused = false;
|
||||
this.$emit('blur', e);
|
||||
|
||||
@@ -3,24 +3,24 @@
|
||||
<van-cell-group>
|
||||
<van-field
|
||||
maxlength="15"
|
||||
placeholder="名字"
|
||||
:label="addressText + '人'"
|
||||
:placeholder="$t('placeholder.name')"
|
||||
:label="$t('label.name', computedAddressText)"
|
||||
v-model="currentInfo.name"
|
||||
:error="errorInfo.name"
|
||||
@focus="onFocus('name')"
|
||||
/>
|
||||
<van-field
|
||||
type="tel"
|
||||
label="联系电话"
|
||||
placeholder="手机或固定电话"
|
||||
:label="$t('label.tel')"
|
||||
:placeholder="$t('placeholder.tel')"
|
||||
v-model="currentInfo.tel"
|
||||
:error="errorInfo.tel"
|
||||
@focus="onFocus('tel')"
|
||||
/>
|
||||
<van-cell class="van-address-edit__area" title="收件地区" @click="showAreaSelect = true">
|
||||
<span>{{ currentInfo.province || '选择省' }}</span>
|
||||
<span>{{ currentInfo.city || '选择市' }}</span>
|
||||
<span>{{ currentInfo.county || '选择区' }}</span>
|
||||
<van-cell class="van-address-edit__area" :title="$t('areaTitle')" @click="showAreaSelect = true">
|
||||
<span>{{ currentInfo.province || $t('placeholder.province') }}</span>
|
||||
<span>{{ currentInfo.city || $t('placeholder.city') }}</span>
|
||||
<span>{{ currentInfo.county || $t('placeholder.county') }}</span>
|
||||
</van-cell>
|
||||
<van-address-edit-detail
|
||||
:value="currentInfo.address_detail"
|
||||
@@ -35,8 +35,8 @@
|
||||
v-if="showPostal"
|
||||
v-show="!hideBottomFields"
|
||||
type="tel"
|
||||
label="邮政编码"
|
||||
placeholder="邮政编码(选填)"
|
||||
:label="$t('label.postal')"
|
||||
:placeholder="$t('placeholder.postal')"
|
||||
v-model="currentInfo.postal_code"
|
||||
maxlength="6"
|
||||
class="van-hairline--top"
|
||||
@@ -47,12 +47,16 @@
|
||||
v-if="showSetDefault"
|
||||
v-show="!hideBottomFields"
|
||||
v-model="currentInfo.is_default"
|
||||
:title="`设为默认${addressText}地址`"
|
||||
:title="$t('defaultAddress', computedAddressText)"
|
||||
/>
|
||||
</van-cell-group>
|
||||
<div v-show="!hideBottomFields" class="van-address-edit__buttons">
|
||||
<van-button block :loading="isSaving" @click="onSaveAddress" type="primary">保存</van-button>
|
||||
<van-button block :loading="isDeleting" @click="onDeleteAddress" v-if="isEdit">删除{{ addressText }}地址</van-button>
|
||||
<van-button block :loading="isSaving" @click="onSaveAddress" type="primary">
|
||||
{{ $t('save') }}
|
||||
</van-button>
|
||||
<van-button block :loading="isDeleting" @click="onDeleteAddress" v-if="isEdit">
|
||||
{{ $t('deleteAddress', computedAddressText) }}
|
||||
</van-button>
|
||||
</div>
|
||||
<van-popup v-model="showAreaSelect" position="bottom">
|
||||
<van-area
|
||||
@@ -77,10 +81,13 @@ import Area from '../area';
|
||||
import Detail from './Detail';
|
||||
import SwitchCell from '../switch-cell';
|
||||
import validateMobile from '../utils/validate/mobile';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-address-edit',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Field.name]: Field,
|
||||
[Cell.name]: Cell,
|
||||
@@ -99,10 +106,7 @@ export default {
|
||||
showPostal: Boolean,
|
||||
showSetDefault: Boolean,
|
||||
showSearchResult: Boolean,
|
||||
addressText: {
|
||||
type: String,
|
||||
default: '收货'
|
||||
},
|
||||
addressText: String,
|
||||
addressInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
@@ -145,13 +149,16 @@ export default {
|
||||
this.isEdit = !!val.id;
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
// 当地址输入框开启了搜索,并且开始搜索后,隐藏所有地址详情以外的输入框
|
||||
// hide bottom field when use search && detail get focused
|
||||
hideBottomFields() {
|
||||
return this.searchResult.length && this.detailFocused;
|
||||
},
|
||||
computedAddressText() {
|
||||
return this.addressText || this.$t('addressText');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -172,13 +179,13 @@ export default {
|
||||
|
||||
onAreaConfirm(values) {
|
||||
if (values.length !== 3 || +values[0].code === -1 || +values[1].code === -1 || +values[2].code === -1) {
|
||||
return Toast('请选择正确的收件地区');
|
||||
return Toast(this.$t('areaWrong'));
|
||||
}
|
||||
Object.assign(this.currentInfo, {
|
||||
province: values[0].name,
|
||||
city: values[1].name,
|
||||
county: values[2].name,
|
||||
'area_code': values[2].code
|
||||
area_code: values[2].code
|
||||
});
|
||||
this.showAreaSelect = false;
|
||||
},
|
||||
@@ -211,18 +218,19 @@ export default {
|
||||
|
||||
getErrorMessageByKey(key) {
|
||||
const value = this.currentInfo[key];
|
||||
const { $t } = this;
|
||||
|
||||
switch (key) {
|
||||
case 'name':
|
||||
return value ? value.length <= 15 ? '' : '名字过长,请重新输入' : '请填写名字';
|
||||
return value ? value.length <= 15 ? '' : $t('nameOverlimit') : $t('nameEmpty');
|
||||
case 'tel':
|
||||
return validateMobile(value) ? '' : '请填写正确的手机号码或电话号码';
|
||||
return validateMobile(value) ? '' : $t('telWrong');
|
||||
case 'area_code':
|
||||
return value ? +value !== -1 ? '' : '请选择正确的收件地区' : '请选择收件地区';
|
||||
return value ? +value !== -1 ? '' : $t('areaWrong') : $t('areaEmpty');
|
||||
case 'address_detail':
|
||||
return value ? value.length <= 200 ? '' : '详细地址不能超过200个字符' : '请填写详细地址';
|
||||
return value ? value.length <= 200 ? '' : $t('addressOverlimit') : $t('addressEmpty');
|
||||
case 'postal_code':
|
||||
return value && !/^\d{6}$/.test(value) ? '邮政编码格式不正确' : '';
|
||||
return value && !/^\d{6}$/.test(value) ? $t('postalEmpty') : '';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -232,7 +240,7 @@ export default {
|
||||
}
|
||||
|
||||
Dialog.confirm({
|
||||
message: `确定要删除这个${this.addressText}地址么`
|
||||
message: this.$t('confirmDelete', this.computedAddressText)
|
||||
}).then(() => {
|
||||
this.$emit('delete', this.currentInfo);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
## 定制主题
|
||||
|
||||
`Vant`提供了一套默认主题,CSS 命名采用 BEM 的风格,方便使用者覆盖样式。如果你想完全替换主题色或者其他样式,可以使用下面的方法:
|
||||
|
||||
### 方案一. PostCSS 插件
|
||||
在项目中直接引入组件对应的 postcss 源代码,并通过 postcss 插件 [postcss-theme-variables](https://www.npmjs.com/package/postcss-theme-variables) 替换颜色变量,步骤如下:
|
||||
|
||||
```javascript
|
||||
// 引入基础样式
|
||||
import 'vant/packages/vant-css/src/base.css';
|
||||
|
||||
// 引入组价对应的样式
|
||||
import 'vant/packages/vant-css/src/button.css';
|
||||
import 'vant/packages/vant-css/src/checkbox.css';
|
||||
```
|
||||
|
||||
接着在 postcss.config.js 中引入所需的 postcss 插件,并根据项目需求配置颜色变量,所有可用的颜色变量请参考 [配置文件](https://github.com/youzan/vant/blob/dev/packages/vant-css/src/common/var.css)
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('postcss-easy-import')({
|
||||
extensions: ['pcss', 'css']
|
||||
}),
|
||||
require('postcss-theme-variables')({
|
||||
vars: {
|
||||
red: '#F60',
|
||||
gray: '#CCC',
|
||||
blue: '#03A9F4'
|
||||
},
|
||||
prefix: '$'
|
||||
}),
|
||||
require('precss')(),
|
||||
require('postcss-calc')(),
|
||||
require('autoprefixer')({
|
||||
browsers: ['Android >= 4.0', 'iOS >= 7']
|
||||
})
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### 方案二. 本地构建
|
||||
可以通过在本地构建 vant-css 的方式生成所需的主题
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone git@github.com:youzan/vant.git
|
||||
cd packages/vant-css
|
||||
```
|
||||
|
||||
在本地 vant-css 仓库中,修改 src/common/var.css 中的颜色变量,然后执行以下构建命令,即可生成对应的样式文件
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
@@ -36,9 +36,7 @@ export default {
|
||||
props: {
|
||||
value: {},
|
||||
areaList: Object,
|
||||
/**
|
||||
* 省市县显示列数,3-省市县,2-省市,1-省
|
||||
*/
|
||||
// 省市县显示列数,3-省市县,2-省市,1-省
|
||||
columnsNum: {
|
||||
type: [String, Number],
|
||||
default: 3
|
||||
|
||||
@@ -9,7 +9,6 @@ export default {
|
||||
name: 'van-badge-group',
|
||||
|
||||
props: {
|
||||
// 当前激活 tab 面板的 key
|
||||
activeKey: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
|
||||
@@ -57,8 +57,8 @@ export default {
|
||||
|
||||
methods: {
|
||||
resetSwipeStatus() {
|
||||
this.swiping = false; // 是否正在拖动
|
||||
this.opened = true; // 记录是否滑动左右 或者 注册
|
||||
this.swiping = false;
|
||||
this.opened = true;
|
||||
},
|
||||
|
||||
swipeMove(offset = 0) {
|
||||
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
// checkbox 是否在 van-checkbox-group 中
|
||||
// whether is in van-checkbox-group
|
||||
isGroup() {
|
||||
return !!this.findParentByComponentName('van-checkbox-group');
|
||||
},
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<div class="van-contact-card__content">
|
||||
<template v-if="type === 'add'">
|
||||
<van-icon class="van-contact-card__icon" name="add2" />
|
||||
<div class="van-contact-card__text">{{ addText }}</div>
|
||||
<div class="van-contact-card__text">{{ addText || $t('addText') }}</div>
|
||||
</template>
|
||||
<template v-else-if="type === 'edit'">
|
||||
<van-icon class="van-contact-card__icon" name="contact" />
|
||||
<div class="van-contact-card__text">
|
||||
<div>联系人:{{ name }}</div>
|
||||
<div>联系电话:{{ tel }}</div>
|
||||
<div>{{ $t('name') }}:{{ name }}</div>
|
||||
<div>{{ $t('tel') }}:{{ tel }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -19,15 +19,19 @@
|
||||
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-contact-card',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Icon.name]: Icon
|
||||
},
|
||||
|
||||
props: {
|
||||
addText: String,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'add'
|
||||
@@ -37,10 +41,6 @@ export default {
|
||||
},
|
||||
tel: {
|
||||
type: String
|
||||
},
|
||||
addText: {
|
||||
type: String,
|
||||
default: '添加订单联系人信息'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
<div class="van-contact-edit">
|
||||
<van-cell-group>
|
||||
<van-field
|
||||
label="联系人"
|
||||
maxlength="30"
|
||||
placeholder="名字"
|
||||
v-model="currentInfo.name"
|
||||
maxlength="30"
|
||||
:label="$t('name')"
|
||||
:placeholder="$t('namePlaceholder')"
|
||||
:error="errorInfo.name"
|
||||
@focus="onFocus('name')">
|
||||
</van-field>
|
||||
<van-field
|
||||
type="tel"
|
||||
label="联系电话"
|
||||
placeholder="手机或固定电话"
|
||||
v-model="currentInfo.tel"
|
||||
type="tel"
|
||||
:label="$t('tel')"
|
||||
:placeholder="$t('telPlaceholder')"
|
||||
:error="errorInfo.tel"
|
||||
@focus="onFocus('tel')">
|
||||
</van-field>
|
||||
</van-cell-group>
|
||||
<div class="van-contact-edit__buttons">
|
||||
<van-button block :loading="isSaving" @click="onSaveContact" type="primary">保存</van-button>
|
||||
<van-button block :loading="isDeleting" @click="onDeleteContact" v-if="isEdit">删除联系人</van-button>
|
||||
<van-button block :loading="isSaving" @click="onSaveContact" type="primary">{{ $t('save') }}</van-button>
|
||||
<van-button block :loading="isDeleting" @click="onDeleteContact" v-if="isEdit">{{ $t('delete') }}</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -32,10 +32,13 @@ import CellGroup from '../cell-group';
|
||||
import Dialog from '../dialog';
|
||||
import Toast from '../toast';
|
||||
import validateMobile from '../utils/validate/mobile';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-contact-edit',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Field.name]: Field,
|
||||
[Button.name]: Button,
|
||||
@@ -81,9 +84,9 @@ export default {
|
||||
const value = this.currentInfo[key];
|
||||
switch (key) {
|
||||
case 'name':
|
||||
return value ? value.length <= 15 ? '' : '名字过长,请重新输入' : '请填写名字';
|
||||
return value ? value.length <= 15 ? '' : this.$t('nameOverlimit') : this.$t('nameEmpty');
|
||||
case 'tel':
|
||||
return validateMobile(value) ? '' : '请填写正确的手机号码或电话号码';
|
||||
return validateMobile(value) ? '' : this.$t('telInvalid');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -113,7 +116,7 @@ export default {
|
||||
}
|
||||
|
||||
Dialog.confirm({
|
||||
message: `确定要删除这个联系人么`
|
||||
message: this.$t('confirmDelete')
|
||||
}).then(() => {
|
||||
this.$emit('delete', this.currentInfo);
|
||||
});
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<van-cell-group>
|
||||
<van-cell v-for="(item, index) in list" :key="item.id">
|
||||
<van-radio :name="item.id" @click="$emit('select', item, index)">
|
||||
<p class="van-contact-list__text">联系人:{{ item.name }}</p>
|
||||
<p class="van-contact-list__text">联系电话:{{ item.tel }}</p>
|
||||
<p class="van-contact-list__text">{{ $t('name') }}:{{ item.name }}</p>
|
||||
<p class="van-contact-list__text">{{ $t('tel') }}:{{ item.tel }}</p>
|
||||
</van-radio>
|
||||
<van-icon name="edit" class="van-contact-list__edit" @click="$emit('edit', item, index)" />
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</van-radio-group>
|
||||
<van-cell icon="add" class="van-contact-list__add van-hairline--top" @click="$emit('add')" :title="addText" isLink />
|
||||
<van-cell icon="add" class="van-contact-list__add van-hairline--top" @click="$emit('add')" :title="addText || $t('addText')" isLink />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -21,10 +21,13 @@ import Cell from '../cell';
|
||||
import Radio from '../radio';
|
||||
import CellGroup from '../cell-group';
|
||||
import RadioGroup from '../radio-group';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-contact-list',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Icon.name]: Icon,
|
||||
[Cell.name]: Cell,
|
||||
@@ -35,10 +38,7 @@ export default {
|
||||
|
||||
props: {
|
||||
value: {},
|
||||
addText: {
|
||||
type: String,
|
||||
default: '新建联系人'
|
||||
},
|
||||
addText: String,
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
v-show="showCancelButton"
|
||||
@click="handleAction('cancel')"
|
||||
>
|
||||
{{ cancelButtonText }}
|
||||
{{ cancelButtonText || $t('cancel') }}
|
||||
</van-button>
|
||||
<van-button
|
||||
size="large"
|
||||
@@ -22,7 +22,7 @@
|
||||
v-show="showConfirmButton"
|
||||
@click="handleAction('confirm')"
|
||||
>
|
||||
{{ confirmButtonText }}
|
||||
{{ confirmButtonText || $t('confirm') }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,6 +32,7 @@
|
||||
<script>
|
||||
import Button from '../button';
|
||||
import Popup from '../mixins/popup';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-dialog',
|
||||
@@ -40,9 +41,11 @@ export default {
|
||||
[Button.name]: Button
|
||||
},
|
||||
|
||||
mixins: [Popup],
|
||||
mixins: [Popup, i18n],
|
||||
|
||||
props: {
|
||||
confirmButtonText: String,
|
||||
cancelButtonText: String,
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
@@ -59,14 +62,6 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmButtonText: {
|
||||
type: String,
|
||||
default: '确认'
|
||||
},
|
||||
cancelButtonText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
callback: {
|
||||
type: Function
|
||||
},
|
||||
|
||||
@@ -7,8 +7,8 @@ const defaultConfig = {
|
||||
value: true,
|
||||
title: '',
|
||||
message: '',
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonText: '',
|
||||
cancelButtonText: '',
|
||||
showCancelButton: false,
|
||||
closeOnClickOverlay: false,
|
||||
callback: action => {
|
||||
|
||||
+5
-4
@@ -1,3 +1,4 @@
|
||||
// This file is auto gererated by build/bin/build-entry.js
|
||||
import Actionsheet from './actionsheet';
|
||||
import AddressEdit from './address-edit';
|
||||
import AddressList from './address-list';
|
||||
@@ -27,6 +28,7 @@ import Icon from './icon';
|
||||
import ImagePreview from './image-preview';
|
||||
import Lazyload from './lazyload';
|
||||
import Loading from './loading';
|
||||
import Locale from './locale';
|
||||
import NavBar from './nav-bar';
|
||||
import NoticeBar from './notice-bar';
|
||||
import NumberKeyboard from './number-keyboard';
|
||||
@@ -118,15 +120,12 @@ const components = [
|
||||
Uploader
|
||||
];
|
||||
|
||||
const install = function(Vue) {
|
||||
if (install.installed) return;
|
||||
|
||||
const install = Vue => {
|
||||
components.forEach(component => {
|
||||
Vue.component(component.name, component);
|
||||
});
|
||||
};
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
install(window.Vue);
|
||||
}
|
||||
@@ -163,6 +162,7 @@ export {
|
||||
ImagePreview,
|
||||
Lazyload,
|
||||
Loading,
|
||||
Locale,
|
||||
NavBar,
|
||||
NoticeBar,
|
||||
NumberKeyboard,
|
||||
@@ -195,6 +195,7 @@ export {
|
||||
Uploader,
|
||||
Waterfall
|
||||
};
|
||||
|
||||
export default {
|
||||
install,
|
||||
version
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import Vue from 'vue';
|
||||
import get from '../utils/get';
|
||||
import camelize from '../utils/camelize';
|
||||
import deepAssign from '../utils/deep-assign';
|
||||
import defaultMessages from './lang/zh-CN';
|
||||
|
||||
// component mixin
|
||||
const i18n = {
|
||||
computed: {
|
||||
$t() {
|
||||
const { name } = this.$options;
|
||||
const prefix = name ? camelize(name) + '.' : '';
|
||||
const messages = this.$vantMessages[this.$vantLang];
|
||||
|
||||
return (path, ...args) => {
|
||||
const message = get(messages, prefix + path) || get(messages, path);
|
||||
return typeof message === 'function' ? message.apply(null, args) : message;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const proto = Vue.prototype;
|
||||
const defaultLang = 'zh-CN';
|
||||
const locale = {
|
||||
i18n,
|
||||
|
||||
init() {
|
||||
Vue.util.defineReactive(proto, '$vantLang', defaultLang);
|
||||
Vue.util.defineReactive(proto, '$vantMessages', { [defaultLang]: defaultMessages });
|
||||
},
|
||||
|
||||
use(lang, messages) {
|
||||
proto.$vantLang = lang;
|
||||
this.add({ [lang]: messages });
|
||||
},
|
||||
|
||||
add(messages = {}) {
|
||||
deepAssign(proto.$vantMessages, messages);
|
||||
}
|
||||
};
|
||||
|
||||
locale.init();
|
||||
export default locale;
|
||||
export { i18n };
|
||||
@@ -0,0 +1,68 @@
|
||||
export default {
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
complete: 'Complete',
|
||||
vanAddressEdit: {
|
||||
areaTitle: 'Area',
|
||||
areaWrong: 'Please select the correct receiving area',
|
||||
areaEmpty: 'Please select a receiving area',
|
||||
nameEmpty: 'Name can not be empty',
|
||||
nameOverlimit: 'Name length exceeds limit',
|
||||
telWrong: 'Wrong format of phone number',
|
||||
addressOverlimit: 'The length of the address can not exceed 200 characters',
|
||||
addressEmpty: 'Address can not be empty',
|
||||
postalEmpty: 'Wrong postal code',
|
||||
defaultAddress: 'Set as the default address',
|
||||
deleteAddress: 'Delete the address',
|
||||
confirmDelete: 'Are you sure you want to delete this address?',
|
||||
label: {
|
||||
name: 'Receiver',
|
||||
tel: 'Phone',
|
||||
postal: 'Postal'
|
||||
},
|
||||
placeholder: {
|
||||
name: 'Receiver name',
|
||||
tel: 'Phone',
|
||||
postal: 'Postal code (optional)',
|
||||
province: 'Province',
|
||||
city: 'City',
|
||||
county: 'County'
|
||||
}
|
||||
},
|
||||
vanAddressEditDetail: {
|
||||
label: {
|
||||
address: 'Address'
|
||||
},
|
||||
placeholder: {
|
||||
address: 'Address'
|
||||
}
|
||||
},
|
||||
vanContactCard: {
|
||||
name: 'Name',
|
||||
tel: 'Phone',
|
||||
addText: 'Add contact info'
|
||||
},
|
||||
vanContactList: {
|
||||
name: 'Name',
|
||||
tel: 'Phone',
|
||||
addText: 'Add new contact'
|
||||
},
|
||||
vanContactEdit: {
|
||||
name: 'Name',
|
||||
namePlaceholder: 'Name',
|
||||
nameEmpty: 'Name can not be empty',
|
||||
nameOverlimit: 'Name length exceeds limit',
|
||||
tel: 'Phone',
|
||||
telPlaceholder: 'Phone',
|
||||
telInvalid: 'Malformed phone number',
|
||||
save: 'Save',
|
||||
delete: 'Delete',
|
||||
confirmDelete: 'Are you sure you want to delete this contact?'
|
||||
},
|
||||
vanPullRefresh: {
|
||||
pullingText: 'Pull to refresh...',
|
||||
loosingText: 'Loose to refresh...',
|
||||
loadingText: 'Loading...'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
export default {
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
save: '保存',
|
||||
complete: '完成',
|
||||
vanAddressEdit: {
|
||||
areaTitle: '收件地区',
|
||||
addressText: '收货',
|
||||
areaWrong: '请选择正确的收件地区',
|
||||
areaEmpty: '请选择收件地区',
|
||||
nameEmpty: '请填写名字',
|
||||
nameOverlimit: '名字过长,请重新输入',
|
||||
telWrong: '请填写正确的手机号码或电话号码',
|
||||
addressOverlimit: '详细地址不能超过200个字符',
|
||||
addressEmpty: '请填写详细地址',
|
||||
postalEmpty: '邮政编码格式不正确',
|
||||
defaultAddress: text => `设为默认${text}地址`,
|
||||
deleteAddress: text => `删除${text}地址`,
|
||||
confirmDelete: text => `确定要删除这个${text}地址么`,
|
||||
label: {
|
||||
name: text => `${text}人`,
|
||||
tel: '联系电话',
|
||||
postal: '邮政编码'
|
||||
},
|
||||
placeholder: {
|
||||
name: '名字',
|
||||
tel: '手机或固定电话',
|
||||
postal: '邮政编码(选填)',
|
||||
province: '选择省',
|
||||
city: '选择市',
|
||||
county: '选择区'
|
||||
}
|
||||
},
|
||||
vanAddressEditDetail: {
|
||||
label: {
|
||||
address: '详细地址'
|
||||
},
|
||||
placeholder: {
|
||||
address: '如街道、楼层、门牌号等'
|
||||
}
|
||||
},
|
||||
vanContactCard: {
|
||||
name: '联系人',
|
||||
tel: '联系电话',
|
||||
addText: '添加订单联系人信息'
|
||||
},
|
||||
vanContactList: {
|
||||
name: '联系人',
|
||||
tel: '联系电话',
|
||||
addText: '新建联系人'
|
||||
},
|
||||
vanContactEdit: {
|
||||
name: '联系人',
|
||||
namePlaceholder: '名字',
|
||||
nameEmpty: '请填写名字',
|
||||
nameOverlimit: '名字过长,请重新输入',
|
||||
tel: '联系电话',
|
||||
telPlaceholder: '手机或固定电话',
|
||||
telInvalid: '请填写正确的手机号码或电话号码',
|
||||
save: '保存',
|
||||
delete: '删除联系人',
|
||||
confirmDelete: '确定要删除这个联系人么'
|
||||
},
|
||||
vanPicker: {
|
||||
confirm: '完成'
|
||||
},
|
||||
vanPullRefresh: {
|
||||
pullingText: '下拉即可刷新...',
|
||||
loosingText: '释放即可刷新...',
|
||||
loadingText: '加载中...'
|
||||
}
|
||||
};
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="van-picker">
|
||||
<div class="van-picker__toolbar van-hairline--top-bottom" v-show="showToolbar">
|
||||
<slot>
|
||||
<a href="javascript:void(0)" class="van-picker__cancel" @click="handlePickerCancel">取消</a>
|
||||
<a href="javascript:void(0)" class="van-picker__confirm" @click="handlePickerConfirm">完成</a>
|
||||
<a href="javascript:void(0)" class="van-picker__cancel" @click="handlePickerCancel">{{ $t('cancel') }}</a>
|
||||
<a href="javascript:void(0)" class="van-picker__confirm" @click="handlePickerConfirm">{{ $t('confirm') }}</a>
|
||||
<div v-if="title" class="van-picker__title">{{ title }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
@@ -25,13 +25,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PickerColumn from './picker-column';
|
||||
import PickerColumn from './PickerColumn';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
const DEFAULT_ITEM_HEIGHT = 44;
|
||||
|
||||
export default {
|
||||
name: 'van-picker',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[PickerColumn.name]: PickerColumn
|
||||
},
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
<div class="van-pull-refresh__head">
|
||||
<slot name="normal" v-if="status === 'normal'"></slot>
|
||||
<slot name="pulling" v-if="status === 'pulling'">
|
||||
<span class="van-pull-refresh__text">{{ pullingText }}</span>
|
||||
<span class="van-pull-refresh__text">{{ pullingText || $t('pullingText') }}</span>
|
||||
</slot>
|
||||
<slot name="loosing" v-if="status === 'loosing'">
|
||||
<span class="van-pull-refresh__text">{{ loosingText }}</span>
|
||||
<span class="van-pull-refresh__text">{{ loosingText || $t('loosingText') }}</span>
|
||||
</slot>
|
||||
<slot name="loading" v-if="status === 'loading'">
|
||||
<div class="van-pull-refresh__loading">
|
||||
<van-loading />
|
||||
<span>{{ loadingText }}</span>
|
||||
<span>{{ loadingText || $t('loadingText') }}</span>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
@@ -29,27 +29,21 @@
|
||||
<script>
|
||||
import Loading from '../loading';
|
||||
import scrollUtils from '../utils/scroll';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-pull-refresh',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
props: {
|
||||
pullingText: String,
|
||||
loosingText: String,
|
||||
loadingText: String,
|
||||
value: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
pullingText: {
|
||||
type: String,
|
||||
default: '下拉即可刷新...'
|
||||
},
|
||||
loosingText: {
|
||||
type: String,
|
||||
default: '释放即可刷新...'
|
||||
},
|
||||
loadingText: {
|
||||
type: String,
|
||||
default: '加载中...'
|
||||
},
|
||||
animationDuration: {
|
||||
type: Number,
|
||||
default: 300
|
||||
|
||||
+22
-29
@@ -3,22 +3,23 @@
|
||||
class="van-search"
|
||||
:class="{ 'van-search--show-action': showAction }"
|
||||
:style="{ 'background-color': background }">
|
||||
<div class="van-search__input-wrap" v-clickoutside="handleClickoutside">
|
||||
<van-icon name="search"></van-icon>
|
||||
<div class="van-search__input-wrap" v-clickoutside="onClickoutside">
|
||||
<van-icon name="search" />
|
||||
<input
|
||||
type="search"
|
||||
class="van-search__input"
|
||||
v-refocus="focusStatus"
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@keypress.enter.prevent="handleSearch">
|
||||
<van-icon name="clear" @click="handleClean" v-show="isFocus"></van-icon>
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@keypress.enter.prevent="onSearch"
|
||||
>
|
||||
<van-icon name="clear" @click="onClean" v-show="isFocus" />
|
||||
</div>
|
||||
<div class="van-search__action" v-if="showAction">
|
||||
<slot name="action">
|
||||
<div class="van-search__action-text" @click="handleBack">取消</div>
|
||||
<div class="van-search__action-text" @click="onBack">{{ $t('cancel') }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,17 +28,20 @@
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import Clickoutside from '../utils/clickoutside';
|
||||
import { i18n } from '../locale';
|
||||
|
||||
export default {
|
||||
name: 'van-search',
|
||||
|
||||
mixins: [i18n],
|
||||
|
||||
components: {
|
||||
[Icon.name]: Icon
|
||||
},
|
||||
|
||||
props: {
|
||||
placeholder: String,
|
||||
value: String,
|
||||
placeholder: String,
|
||||
showAction: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -50,8 +54,8 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
focusStatus: false,
|
||||
isFocus: false
|
||||
isFocus: false,
|
||||
focusStatus: false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -67,48 +71,37 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 进入input焦点,出现close和取消
|
||||
*/
|
||||
handleFocus() {
|
||||
onFocus() {
|
||||
this.isFocus = true;
|
||||
},
|
||||
|
||||
handleInput(event) {
|
||||
onInput(event) {
|
||||
this.$emit('input', event.target.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击close后清空value后,再聚焦input框
|
||||
*/
|
||||
handleClean() {
|
||||
// refocus after click close icon
|
||||
onClean() {
|
||||
this.$emit('input', '');
|
||||
this.focusStatus = true;
|
||||
|
||||
// 保证多次点击 clean 后,仍能触发 refocus
|
||||
// ensure refocus can work after click clean icon
|
||||
this.$nextTick(() => {
|
||||
this.focusStatus = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击取消后,清空所有回复最初状态
|
||||
*/
|
||||
handleBack() {
|
||||
onBack() {
|
||||
this.$emit('input', '');
|
||||
this.$emit('cancel');
|
||||
},
|
||||
|
||||
/**
|
||||
* input输入回车后,发送回调
|
||||
*/
|
||||
handleSearch(e) {
|
||||
onSearch(e) {
|
||||
e.preventDefault();
|
||||
this.$emit('search', this.value);
|
||||
return false;
|
||||
},
|
||||
|
||||
handleClickoutside() {
|
||||
onClickoutside() {
|
||||
this.isFocus = false;
|
||||
this.focusStatus = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
const camelizeRE = /-(\w)/g;
|
||||
export default function(str) {
|
||||
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '');
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
function isObj(x) {
|
||||
const type = typeof x;
|
||||
return x !== null && (type === 'object' || type === 'function');
|
||||
}
|
||||
|
||||
function assignKey(to, from, key) {
|
||||
const val = from[key];
|
||||
|
||||
if (
|
||||
val === undefined ||
|
||||
val === null ||
|
||||
(hasOwnProperty.call(to, key) &&
|
||||
(to[key] === undefined || to[key] === null))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasOwnProperty.call(to, key) || !isObj(val)) {
|
||||
to[key] = val;
|
||||
} else {
|
||||
to[key] = assign(Object(to[key]), from[key]);
|
||||
}
|
||||
}
|
||||
|
||||
export default function assign(to, from) {
|
||||
for (const key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
assignKey(to, from, key);
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default function(object, path) {
|
||||
const keys = path.split('.');
|
||||
let result = object;
|
||||
|
||||
keys.forEach(key => {
|
||||
result = result[key] || '';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
line-height: 40px;
|
||||
|
||||
.van-contact-card__icon {
|
||||
width: 40px;
|
||||
color: $blue;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
|
||||
&__pivot {
|
||||
top: 50%;
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
padding: 0 5px;
|
||||
font-size: 8px;
|
||||
margin-top: -6px;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
background-color: $gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user