mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
[Improvement] add BEM helper mixin (#904)
This commit is contained in:
parent
f92caf0e5c
commit
051116df4c
@ -1,32 +1,30 @@
|
||||
<template>
|
||||
<transition name="van-slide-bottom">
|
||||
<div class="van-actionsheet" :class="{ 'van-actionsheet--withtitle': title }" v-show="value">
|
||||
<div class="van-actionsheet__header van-hairline--top-bottom" v-if="title">
|
||||
<div v-show="value" :class="b({ 'withtitle': title })">
|
||||
<div v-if="title" class="van-hairline--top-bottom" :class="b('header')">
|
||||
<div v-text="title" />
|
||||
<icon name="close" @click="handleCancel" />
|
||||
<icon name="close" @click="onCancel" />
|
||||
</div>
|
||||
<ul v-else class="van-hairline--bottom">
|
||||
<li
|
||||
v-for="(item, index) in actions"
|
||||
:key="index"
|
||||
class="van-actionsheet__item van-hairline--top"
|
||||
:class="[item.className, { 'van-actionsheet__item--loading': item.loading }]"
|
||||
v-for="item in actions"
|
||||
:class="[b('item'), item.className, 'van-hairline--top']"
|
||||
@click.stop="onClickItem(item)"
|
||||
>
|
||||
<template v-if="!item.loading">
|
||||
<span class="van-actionsheet__name">{{ item.name }}</span>
|
||||
<span class="van-actionsheet__subname" v-if="item.subname">{{ item.subname }}</span>
|
||||
<span :class="b('name')">{{ item.name }}</span>
|
||||
<span :class="b('subname')" v-if="item.subname">{{ item.subname }}</span>
|
||||
</template>
|
||||
<loading v-else class="van-actionsheet__loading" size="20px" />
|
||||
<loading v-else :class="b('loading')" size="20px" />
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
v-if="cancelText"
|
||||
v-text="cancelText"
|
||||
class="van-actionsheet__item van-actionsheet__cancel van-hairline--top"
|
||||
@click="handleCancel"
|
||||
:class="[b('cancel'), 'van-hairline--top']"
|
||||
@click="onCancel"
|
||||
/>
|
||||
<div v-else class="van-actionsheet__content">
|
||||
<div v-else :class="b('content')">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@ -67,7 +65,7 @@ export default create({
|
||||
}
|
||||
},
|
||||
|
||||
handleCancel() {
|
||||
onCancel() {
|
||||
this.$emit('input', false);
|
||||
this.$emit('cancel');
|
||||
}
|
||||
|
@ -9,31 +9,26 @@
|
||||
rows="1"
|
||||
:value="value"
|
||||
:error="isError"
|
||||
:on-icon-click="onIconClick"
|
||||
@click-icon="onIconClick"
|
||||
@input="$emit('input', $event)"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
>
|
||||
<div slot="icon">
|
||||
<span v-if="showIcon && isAndroid" class="van-address-edit-detail__finish-edit">{{ $t('complete') }}</span>
|
||||
<span v-if="showIcon && isAndroid" :class="b('finish')">{{ $t('complete') }}</span>
|
||||
<icon v-else-if="showIcon" name="clear" />
|
||||
</div>
|
||||
</field>
|
||||
|
||||
<cell-group class="van-address-edit-detail__suggest-list" v-if="showSearchList">
|
||||
<cell-group :border="false" v-if="showSearchList">
|
||||
<cell
|
||||
v-for="express in searchResult"
|
||||
:key="express.name + express.address"
|
||||
class="van-address-edit-detail__suggest-item"
|
||||
:title="express.name"
|
||||
:label="express.address"
|
||||
icon="location"
|
||||
clickable
|
||||
@click="onSuggestSelect(express)"
|
||||
>
|
||||
<icon name="location" class="van-address-edit-detail__location" />
|
||||
<div class="van-address-edit-detail__item-info">
|
||||
<p class="van-address-edit-detail__title" v-if="isString(express.name)">{{ express.name }}</p>
|
||||
<p class="van-address-edit-detail__subtitle" v-if="isString(express.address)">{{ express.address }}</p>
|
||||
</div>
|
||||
</cell>
|
||||
@click="onSelect(express)"
|
||||
/>
|
||||
</cell-group>
|
||||
</div>
|
||||
</template>
|
||||
@ -97,7 +92,7 @@ export default create({
|
||||
}
|
||||
},
|
||||
|
||||
onSuggestSelect(express) {
|
||||
onSelect(express) {
|
||||
this.$emit('input', `${express.address || ''} ${express.name || ''}`.trim());
|
||||
this.$emit('select-search', express);
|
||||
},
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="van-address-edit">
|
||||
<div :class="b()">
|
||||
<cell-group>
|
||||
<field
|
||||
v-model="data.name"
|
||||
maxlength="15"
|
||||
:placeholder="$t('name')"
|
||||
:label="$t('label.name')"
|
||||
v-model="data.name"
|
||||
:error="errorInfo.name"
|
||||
@focus="onFocus('name')"
|
||||
/>
|
||||
@ -19,7 +19,7 @@
|
||||
/>
|
||||
<cell
|
||||
clickable
|
||||
class="van-address-edit__area"
|
||||
:class="b('area')"
|
||||
:title="$t('area')"
|
||||
@click="showArea = true"
|
||||
>
|
||||
@ -57,7 +57,7 @@
|
||||
:title="$t('defaultAddress')"
|
||||
/>
|
||||
</cell-group>
|
||||
<div v-show="!hideBottomFields" class="van-address-edit__buttons">
|
||||
<div v-show="!hideBottomFields" :class="b('buttons')">
|
||||
<van-button block :loading="isSaving" @click="onSave" type="primary">
|
||||
{{ $t('save') }}
|
||||
</van-button>
|
||||
|
@ -1,20 +1,26 @@
|
||||
<template>
|
||||
<div class="van-address-list">
|
||||
<radio-group :value="value" @input="$emit('input', $event)" class="van-address-list__group">
|
||||
<div :class="b()">
|
||||
<radio-group :value="value" :class="b('group')" @input="$emit('input', $event)">
|
||||
<cell-group>
|
||||
<cell v-for="(item, index) in list" :key="item.id" is-link>
|
||||
<radio :name="item.id" @click="$emit('select', item, index)">
|
||||
<div class="van-address-list__name">{{ item.name }},{{ item.tel }}</div>
|
||||
<div class="van-address-list__address">{{ $t('address') }}:{{ item.address }}</div>
|
||||
<div :class="b('name')">{{ item.name }},{{ item.tel }}</div>
|
||||
<div :class="b('address')">{{ $t('address') }}:{{ item.address }}</div>
|
||||
</radio>
|
||||
<icon slot="right-icon" name="edit" class="van-address-list__edit" @click="$emit('edit', item, index)" />
|
||||
<icon
|
||||
slot="right-icon"
|
||||
name="edit"
|
||||
:class="b('edit')"
|
||||
@click="$emit('edit', item, index)"
|
||||
/>
|
||||
</cell>
|
||||
</cell-group>
|
||||
</radio-group>
|
||||
<cell
|
||||
icon="add"
|
||||
is-link
|
||||
class="van-address-list__add van-hairline--top"
|
||||
:class="b('add')"
|
||||
class="van-hairline--top"
|
||||
:title="addButtonText || $t('add')"
|
||||
@click="$emit('add')"
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<picker
|
||||
class="van-area"
|
||||
:class="b()"
|
||||
ref="picker"
|
||||
show-toolbar
|
||||
value-key="name"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="van-badge-group van-hairline--top-bottom">
|
||||
<div :class="b()" class="van-hairline--top-bottom">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<a class="van-badge van-hairline" :class="{ 'van-badge--select': isSelect }" :href="url" @click="onClick">
|
||||
<div v-if="isDef(info)" class="van-badge__info">{{ info }}</div>
|
||||
<a
|
||||
:class="[b({ select }), 'van-hairline']"
|
||||
:href="url"
|
||||
@click="onClick"
|
||||
>
|
||||
<div v-if="isDef(info)" :class="b('info')">{{ info }}</div>
|
||||
{{ title }}
|
||||
</a>
|
||||
</template>
|
||||
@ -23,7 +27,7 @@ export default create({
|
||||
},
|
||||
|
||||
computed: {
|
||||
isSelect() {
|
||||
select() {
|
||||
return this.$parent.badges.indexOf(this) === this.$parent.activeKey;
|
||||
}
|
||||
},
|
||||
|
@ -3,22 +3,21 @@
|
||||
:is="tag"
|
||||
:type="nativeType"
|
||||
:disabled="disabled"
|
||||
class="van-button"
|
||||
:class="[
|
||||
'van-button--' + type,
|
||||
'van-button--' + size,
|
||||
:class="b([
|
||||
type,
|
||||
size,
|
||||
{
|
||||
'van-button--disabled': disabled,
|
||||
'van-button--loading': loading,
|
||||
'van-button--block': block,
|
||||
'van-button--bottom-action': bottomAction,
|
||||
'van-button--unclickable': disabled || loading
|
||||
block,
|
||||
loading,
|
||||
disabled,
|
||||
unclickable: disabled || loading,
|
||||
'bottom-action': bottomAction
|
||||
}
|
||||
]"
|
||||
])"
|
||||
@click="onClick"
|
||||
>
|
||||
<loading v-if="loading" size="20px" :color="type === 'default' ? 'black' : 'white'" />
|
||||
<span class="van-button__text">
|
||||
<span :class="b('text')">
|
||||
<slot>{{ text }}</slot>
|
||||
</span>
|
||||
</component>
|
||||
|
@ -1,32 +1,33 @@
|
||||
<template>
|
||||
<div class="van-card" :class="{ 'van-card--center': centered }">
|
||||
<div class="van-card__thumb">
|
||||
<div :class="b({ center: centered })">
|
||||
<div :class="b('thumb')">
|
||||
<slot name="thumb">
|
||||
<img :src="thumb" class="van-card__img" >
|
||||
<img :src="thumb" :class="b('img')" >
|
||||
</slot>
|
||||
</div>
|
||||
<div class="van-card__content">
|
||||
<div :class="b('content')">
|
||||
<slot name="title">
|
||||
<div class="van-card__row" v-if="title || price !== undefined">
|
||||
<div v-if="title" class="van-card__title">{{ title }}</div>
|
||||
<div v-if="price !== undefined" class="van-card__price">{{ currency }} {{ price }}</div>
|
||||
<div :class="b('row')" v-if="title || isDef(price)">
|
||||
<div v-if="title" :class="b('title')">{{ title }}</div>
|
||||
<div v-if="isDef(price)" :class="b('price')">{{ currency }} {{ price }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="desc">
|
||||
<div class="van-card__row" v-if="desc || num !== undefined">
|
||||
<div v-if="desc" class="van-card__desc">{{ desc }}</div>
|
||||
<div v-if="num !== undefined" class="van-card__num">x {{ num }}</div>
|
||||
<div :class="b('row')" v-if="desc || isDef(num)">
|
||||
<div v-if="desc" :class="b('desc')">{{ desc }}</div>
|
||||
<div v-if="isDef(num)" :class="b('num')">x {{ num }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="tags" />
|
||||
</div>
|
||||
<div class="van-card__footer" v-if="$slots.footer">
|
||||
<div :class="b('footer')" v-if="$slots.footer">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isDef } from '../utils';
|
||||
import create from '../utils/create';
|
||||
|
||||
export default create({
|
||||
@ -43,6 +44,10 @@ export default create({
|
||||
type: String,
|
||||
default: '¥'
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
isDef
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
45
packages/mixins/bem.js
Normal file
45
packages/mixins/bem.js
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* bem helper
|
||||
* b() // 'button'
|
||||
* b('text') // 'button__text'
|
||||
* b({ disabled }) // 'button button--disabled'
|
||||
* b('text', { disabled }) // 'button__text button__text--disabled'
|
||||
* b(['disabled', 'primary']) // 'button button--disabled button--primary'
|
||||
*/
|
||||
|
||||
const ELEMENT = '__';
|
||||
const MODS = '--';
|
||||
|
||||
const join = (name, el, symbol) => el ? name + symbol + el : name;
|
||||
|
||||
const prefix = (name, mods) => {
|
||||
if (typeof mods === 'string') {
|
||||
return join(name, mods, MODS);
|
||||
}
|
||||
|
||||
if (Array.isArray(mods)) {
|
||||
return mods.map(item => prefix(name, item));
|
||||
}
|
||||
|
||||
const ret = {};
|
||||
Object.keys(mods).forEach(key => {
|
||||
ret[name + MODS + key] = mods[key];
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
b(el, mods) {
|
||||
const { name } = this.$options;
|
||||
|
||||
if (el && typeof el !== 'string') {
|
||||
mods = el;
|
||||
el = '';
|
||||
}
|
||||
el = join(name, el, ELEMENT);
|
||||
|
||||
return mods ? [el, prefix(name, mods)] : el;
|
||||
}
|
||||
}
|
||||
};
|
@ -10,7 +10,6 @@
|
||||
<li
|
||||
v-if="isMultiMode"
|
||||
v-for="(page, index) in pages"
|
||||
:key="index"
|
||||
class="van-pagination__item van-pagination__page van-hairline"
|
||||
:class="{ 'van-pagination--active': page.active }"
|
||||
@click="selectPage(page.number)"
|
||||
|
@ -23,7 +23,6 @@
|
||||
<!-- 已有的图片,图片右上角显示删除按钮 -->
|
||||
<div
|
||||
v-for="(img, index) in imgList"
|
||||
:key="index"
|
||||
class="van-sku-img-uploader__img"
|
||||
>
|
||||
<img :src="img">
|
||||
|
@ -12,7 +12,6 @@
|
||||
<div v-if="type === 'line'" class="van-tabs__nav-bar" :style="navBarStyle" />
|
||||
<div
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
ref="tabs"
|
||||
class="van-tab"
|
||||
:class="{
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Create a basic component with common options
|
||||
*/
|
||||
import '../locale';
|
||||
import bem from '../mixins/bem';
|
||||
import i18n from '../mixins/i18n';
|
||||
|
||||
const install = function(Vue) {
|
||||
@ -12,7 +13,7 @@ export default function(sfc) {
|
||||
sfc.name = 'van-' + sfc.name;
|
||||
sfc.install = sfc.install || install;
|
||||
sfc.mixins = sfc.mixins || [];
|
||||
sfc.mixins.push(i18n);
|
||||
sfc.mixins.push(i18n, bem);
|
||||
|
||||
return sfc;
|
||||
};
|
||||
|
@ -15,7 +15,8 @@
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
&__item {
|
||||
&__item,
|
||||
&__cancel {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
|
@ -3,6 +3,10 @@
|
||||
.van-address-edit {
|
||||
&__buttons {
|
||||
padding: 20px 10px;
|
||||
|
||||
.van-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__area {
|
||||
@ -19,46 +23,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.van-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.van-icon-clear {
|
||||
color: $gray-dark;
|
||||
}
|
||||
|
||||
&-detail {
|
||||
&__finish-edit {
|
||||
color: $blue;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__suggest-list {
|
||||
&::after {
|
||||
border-top-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__location {
|
||||
float: left;
|
||||
font-size: 18px;
|
||||
color: $gray-darker;
|
||||
}
|
||||
|
||||
&__item-info {
|
||||
margin-left: 26px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 14px;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: $gray-darker;
|
||||
}
|
||||
&-detail__finish {
|
||||
color: $blue;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
@ -192,21 +192,21 @@ describe('AddressEdit', () => {
|
||||
|
||||
wrapper.find('.van-field__control')[2].trigger('focus');
|
||||
wrapper.vm.$nextTick(() => {
|
||||
const items = wrapper.find('.van-address-edit-detail__suggest-item');
|
||||
const items = wrapper.find('.van-icon-location');
|
||||
expect(items.length).to.equal(3);
|
||||
|
||||
items[0].trigger('click');
|
||||
items[0].element.parentNode.click();
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.find('.van-field__control')[2].element.value).to.equal(
|
||||
'杭州市西湖区 黄龙万科中心'
|
||||
);
|
||||
|
||||
items[1].trigger('click');
|
||||
items[1].element.parentNode.click();
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.find('.van-field__control')[2].element.value).to.equal(
|
||||
'黄龙万科中心H座'
|
||||
);
|
||||
items[2].trigger('click');
|
||||
items[2].element.parentNode.click();
|
||||
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(
|
||||
|
Loading…
x
Reference in New Issue
Block a user