Восьмой пункт из серии квалифицированных интерфейсов — создайте собственную библиотеку пользовательского интерфейса.

Vue.js Web Components

Колеса~ Мне нужно построить новое~

Введение в проект

vui: частная библиотека компонентов пользовательского интерфейса vue (в основном для мобильных устройств)

It's a A personal Vue UI component library .

Официальный сайт документации

Вход со стороны ПК - это непосредственно интерфейс документа. Если вы хотите просмотреть демонстрационный эффект, вы можете напрямую настроить браузер на режим мобильного телефона. Если вам это нравится, пожалуйста, не жалейте своей звезды~~

Предварительный просмотр эффекта онлайн

Пожалуйста, отсканируйте QR-код ниже

существующие компоненты

Установить

npm i x-vui -S

быстрый старт

Соберите проект (с помощью vue-cli)

# 全局安装 vue-cli
npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
vue init webpack my-vui-project
# 安装依赖,并下载x-vui
cd my-vui-project
npm install && npm install x-vui
# 项目启动 默认端口localhost:8080
npm run dev

полный импорт

import Vue from 'vue'
import vui from 'x-vui'
import 'x-vui/lib/vui-css/index.css';

Vue.use(vui)

Частичное введение

import Vue from 'vue'
import {
  Scroller,
  Select
  // ...
} from 'x-vui'
import 'x-vui/lib/vui-css/scroller.css';
import 'x-vui/lib/vui-css/select.css';

Vue.component(Scroller.name, Scroller)
Vue.component(Select.name, Select)

Внедрить плагины

Примечание: если vui полностью введен, нет необходимости регистрировать плагины.

import Vue from 'vue';
import { 
  $Toast, 
  $Dialog 
  // ...
} from 'x-vui';

Vue.prototype.$toast = $Toast
Vue.prototype.$dialog = $Dialog

Использование компонентов

1. swiper

В настоящее время по умолчанию используется карусель swiper. Если у вас есть особые потребности, вы также можете использовать swipe и swipe-item, чтобы объединить свой собственный swiper

Основное использование

<template>
  <div class="swiper-page">
    <p>正常swiper</p>
    <v-swiper :items='items' :styles="{height: '200px'}" @change="changeHandle"></v-swiper>
    <p>缩略swiper</p>
    <v-swiper type='thum' :items='items' :styles="{height: '240px'}"></v-swiper>
  </div>
</template>
<script>
export default {
  data () {
    return {
      items: [
        require('../assets/beauty_1.png'),
        require('../assets/beauty_2.png'),
        require('../assets/beauty_3.png'),
        require('../assets/beauty_4.png'),
        require('../assets/beauty_5.png')
      ],
    }
  },
  methods: {
    changeHandle (index) {
      console.log(index);
    }
  }
}
</script>

Attributes

параметр иллюстрировать Типы необязательное значение По умолчанию
type тип считывателя string swiper (обычный) / thum (сокращенно) swiper
auto Продолжительность автовоспроизведения number 5000
items Список дисплеев свайпера array []
showIndicators Показывают ли маленькие точки swiper boolean true
styles управление стилем swiper object {}
resetTitle сбросить содержимое заголовка string

Events

название события иллюстрировать параметр обратного вызова
change скользящий обратный вызов swiper текущий индекс элемента swiper

2. прокрутка (потяните вниз, чтобы обновить и вверх, чтобы загрузить)

Часто используется для функций пейджинга на мобильных телефонах, выпадающего обновления, подтягивающей загрузки.

Основное использование

<template>
  <div class="scroller-page">
    <v-scroller
      :on-refresh="refresh"
      :on-infinite="infinite"
    >
      <ul>
        <li v-for="(list, index) in lists" :key="index">{{list}}</li>
      </ul>
    </v-scroller>
  </div>
</template>
<script>
export default {
  data () {
    return {
      len: 6,
    }
  },
  computed: {
    lists () {
      let arr = []
      for (let i = 1; i < this.len + 1; i++) {
        arr.push('列表' + i)
      }
      return arr
    }
  },
  methods: {
    // 下拉刷新
    refresh (done) {
      setTimeout(() => {
        this.len = 6
        done()
      }, 1000)
    },
    // 上拉加载
    infinite (done) {
      setTimeout(() => {
        if (this.len >= 7) {
          done(true)
          return
        }
        this.len++
        done()
      }, 1000)
    }
  }
}
</script>

Attributes

параметр иллюстрировать Типы необязательное значение По умолчанию
onRefresh выпадающий обратный вызов function
onInfinite подтягивающий обратный вызов function
width ширина скроллера string 100%
height высота скроллера string 100%
isLoadMore Отображать ли подтягивающую загрузку boolean true
refreshText выпадающее текстовое содержимое string Потяните вниз, чтобы обновить
noDataText нет текста данных string больше нет данных
refreshLayerColor цвет выпадающего текста string #AAA
loadingLayerColor Подтянуть цвет текста string #AAA
animating Есть ли анимация boolean true
animationDuration интервал анимации number 250
bouncing Есть ли эффект отскока string true
cssClass content css class string

Events

название события иллюстрировать параметр обратного вызова
onRefresh выпадающий обратный вызов В нем есть обратный вызов done, чтобы завершить эффект загрузки
onInfinite подтягивающий обратный вызов В нем есть обратный вызов done, чтобы завершить эффект загрузки

3. search

Обычно используется для ключевых слов поиска мобильного телефона, а иногда и для выделения соответствия.

Основное использование

1. Только поле поиска

<template>
  <v-search
    placeholder="请输入搜索关键字"
    @search="searchFn"
    @enter="searchEnter"
  ></v-search>
</template>
<script>
export default {
  methods: {
    searchFn (query) {
      console.log('search', query)
    },
    searchEnter (query) {
      console.log('enter', query)
    }
  }
}
</script>

2. Со списком результатов поиска SearchList

<template>
  <v-search
    placeholder="请输入搜索关键字"
    :async="false"
    @search="searchFn"
  >
    <v-search-list :result="filterResult" @listSearch="listSearch" v-show="visible"></v-search-list>
  </v-search>
</template>
<script>
export default {
  data () {
    return {
      keyword: '',
      visible: false, // 点击列表,列表是否消失
      defaultResult: [
        'Apple',
        'Banana',
        'Orange',
        'Durian',
        'Lemon',
        'Peach',
        'Cherry',
        'Berry',
        'Core',
        'Fig',
        'Haw',
        'Melon',
        'Plum',
        'Pear',
        'Peanut',
        'Other'
      ]
    }
  },
  watch: {
    keyword (val) {
      if (!val) {
        this.visible = false;
      }
    }
  },
  methods: {
    searchFn (query) {
      this.keyword = query;
      this.visible = true;
    }
  },
  computed: {
    filterResult() {
      return this.defaultResult.filter(item => new RegExp(this.keyword, 'i').test(item));
    }
  }
}
</script>

Расширенное использование

3. Индивидуальный список результатов, выделение и сопоставление ключевых слов.

<template>
  <v-search
    placeholder="请输入搜索关键字"
    :async="false"
    @search="searchFn"
  >
    <v-search-list :result="filterResult" @listSearch="listSearch" v-show="visible">
      <div class="search-result" slot="list-item" slot-scope="props">
        <p class="l" v-html="props.slotValue.name"></p>
        <p class="gray" v-show="props.slotValue.price">¥{{props.slotValue.price}}/斤</p>
        <div class="gray r" v-show="props.slotValue.amount">剩余{{props.slotValue.amount}}斤</div>
      </div>
    </v-search-list>
  </v-search>
</template>
<script>
export default {
  data () {
    return {
      keyword: '',
      visible: false,
      defaultResult: [
        {name: 'Apple', price: 5, amount: 20},
        {name: 'Banana', price: 5, amount: 30},
        {name: 'Orange', price: 3, amount: 10},
        {name: 'Durian', price: 10, amount: 25},
        {name: 'Lemon', price: 4, amount: 30},
        {name: 'Peach', price: 5, amount: 40},
        {name: 'Cherry', price: 20, amount: 50},
        {name: 'Berry', price: 15, amount: 60},
        {name: 'Core', price: 10, amount: 21},
        {name: 'Fig', price: 10, amount: 22},
        {name: 'Haw', price: 10, amount: 23},
        {name: 'Melon', price: 10, amount: 24},
        {name: 'Plum', price: 10, amount: 25},
        {name: 'Pear', price: 10, amount: 26},
        {name: 'Peanut', price: 10, amount: 27},
        {name: 'Other'}
      ],
      // 防止defaultResult值被污染
      copy: []
    }
  },
  watch: {
    keyword (val) {
      if (!val) {
        this.visible = false;
      }
    }
  },
  methods: {
    searchFn (query) {
      this.keyword = query;
      this.visible = true;
    },
    listSearch (index) {
      this.visible = false;
      console.log(index, this.defaultResult[index].name)
    }
  },
  computed: {
    filterResult() {
      // i 忽略大小写
      let result = this.defaultResult.filter(item => new RegExp(this.keyword, 'i').test(item.name));
      // 关键字高亮匹配
      this.copy = JSON.parse(JSON.stringify(result))
      this.copy.forEach((item, index) => {
        let name = item.name, word = this.keyword;
        name = name.toLowerCase();
        word = word.toLowerCase();

        if (word && name.indexOf(word) !== -1) {
          let arr    = item.name.split('')
          let i      = name.indexOf(word);
          let len    = word.length;
          let active = '<span class="price">' + arr.splice(i, len).join('') + '</span>';
          arr.splice(i, 0, active);
          item.name  = arr.join('');
        }
      })
      return this.copy;
    }
  }
}
</script>

Attributes

параметр иллюстрировать Типы необязательное значение По умолчанию
async Следует ли дросселировать boolean true
timeout Продолжительность дроссельной заслонки поиска number 100
styles стиль поиска object
placeholder placeholder string 'поиск'
autofocus Следует ли автофокусироваться (автофокусировка на iOS недействительна) boolean
clear Очищать ли содержимое окна поиска при поиске boolean false

Events

название события иллюстрировать параметр обратного вызова
search обратный поиск поиска текст поиска
enter Обратный поиск при входе текст поиска
close Нажмите кнопку закрытия поиска, чтобы перезвонить ''

4. Всплывающее диалоговое окно

Основное использование

<template>
  <example-block title="基础用法">
    <button @click="showSimpleDialog">普通 Dialog</button>
  </example-block>
</template>
<script>
export default {
  methods: {
    showSimpleDialog () {
      this.$dialog({
        title: '普通 Dialog',
        cancelText: '取消',
        okText: '确定',
        content: '测试 Dialog,测试 Dialog,测试 Dialog~~~'
      })
    }
  }
}
</script>

пользовательский HTML

<template>
  <example-block title="自定义 HTML">
    <button @click="showHtmlDialog">HTML Dialog</button>
  </example-block>
</template>
<script>
export default {
  methods: {
    showHtmlDialog () {
      this.$dialog({
        title: '自定义 HTML',
        cancelText: '取消',
        okText: '确定',
        content: '<strong style="color: green">测试 Dialog,测试 Dialog,测试 Dialog~~~</strong style="color: green">'
      })
    }
  }
}
</script>

Использование компонента Dialog высшего порядка

<template>
  <div>
    <example-block title="Dialog 模板">
      <button @click="showDialogTpl">Dialog Template</button>
    </example-block>
    <v-dialog
    title="Dialog 模板"
    cancelText="取消"
    okText="确认"
    content="测试 Dialog,测试 Dialog,测试 Dialog~~~"
    :show="showDialog"
    :onCancel="close"
    :onOk="close"
    >
      <p class="modal-text">Dialog Template slot !!!</p>
    </v-dialog>
  </div>
</template>

<script>
export default {
  data () {
    return {
      showDialog: false
    }
  },
  methods: {
    showDialogTpl () {
      this.showDialog = true
    },
    close () {
      this.showDialog = false
    }
  }
}
</script>

Attributes(modal)

параметр иллюстрировать Типы необязательное значение По умолчанию
show Отображается ли модальное окно boolean
title модальный заголовок string
content модальное содержание string
onOk Обратный вызов кнопки ОК function
onCancel кнопка отмены обратного вызова function
okText Содержимое кнопки ОК string
cancelText Содержимое кнопки отмены string
showCloseIcon отображать ли значок закрытия boolean true

5. Напоминание о тосте

Основное использование

<template>
  <example-block title="基础用法">
    <button @click="showSimpleToast">普通文字提示</button>
  </example-block>
</template>
<script>
export default {
  methods: {
    showSimpleToast () {
      this.$toast({msg: '我是文字提示~'});
    }
  }
}
</script>

пользовательский HTML

<template>
  <example-block title="自定义HTML">
    <button @click="showHtmlToast">自定义HTML文本提示</button>
  </example-block>
</template>
<script>
export default {
  methods: {
    showHtmlToast () {
      this.$toast('<strong style="font-size: 20px;">HTML文字提示~</strong>');
    }
  }
}
</script>

Attributes

параметр иллюстрировать Типы необязательное значение По умолчанию
msg текст сообщения string
timeout продолжительность отображения сообщения number 2000
callback Перезвоните function
icon специальный значок string

6. Селектор выбора

Содержит 4 часто используемых селектора: время, дату, время и дату, а также общие селекторы.

Основное использование

1. Селектор типа времени (вы можете переключать тип)

<template>
  <x-picker 
    title="选择日期"
    placeholder="请选择日期" 
    v-model="now_date" 
    type="date"
  ></x-picker>
</template>
<script>
export default {
  data() {
    return {
      now_date: null // new Date().getTime()/1000
    };
  }
};
</script>

2, пользовательский общий селектор

<template>
  <x-picker 
    v-model="gender.value" 
    placeholder="请选择性别" 
    :default="gender.default" 
    title="选择性别" 
    type="custom"
  ></x-picker>
</template>
<script>
export default {
  data() {
    return {
     gender: {
        default: -1,
        value: [
          { name: "保密", value: 0 },
          { name: "男", value: 1 },
          { name: "女", value: 2 }
        ]
      }
    };
  }
};
</script>

Расширенное использование

Детальный выбор минут с использованием timeStep

<template>
  <x-picker 
    title="选择日期"
    placeholder="请选择日期" 
    v-model="now_date" 
    type="date"
    :timeStep="20"
  ></x-picker>
</template>
<script>
export default {
  data() {
    return {
      now_date: null // new Date().getTime()/1000
    };
  }
};
</script>

Используйте startYear, endYear, startDate, endDate, startMinute, endMinute для выбора диапазона времени. (Больше можно подобрать в зависимости от реальной ситуации)

<template>
  <x-picker 
    title="选择日期"
    placeholder="请选择日期" 
    v-model="now_date" 
    type="date"
    startMinute="2" endMinute="30"
  ></x-picker>
</template>
<script>
export default {
  data() {
    return {
      now_date: null // new Date().getTime()/1000
    };
  }
};
</script>

Attributes

параметр иллюстрировать Типы необязательное значение По умолчанию
default Значение, выбранное средством выбора по умолчанию string/number
type тип сборщика string date/time/datetime/custom datetime
title заголовок всплывающего окна выбора string
placeholder placeholder string Пожалуйста, выберите время
timeStep Детализация выбора времени (с селектором минут) number 1
startYear год начала number/string Этот год
endYear конец года number/string 10-летний диапазон
startDate Дата начала string
endDate Дата окончания string
startHour время начала number/string 0
endHour Время окончания number/string 23
startMinute стартовая минута number/string 0
endMinute конечная минута number/string 59
yearFormat Форматирование "Год" string {значение} лет
monthFormat форматирование "месяц" string
dayFormat string
hourFormat string
minuteFormat string

<template>
  <div>
    <x-select
    title="LIST ONE"
    defaultValue="0"
    :selectData="selectData"
    :alwaysShowTitle="false"
    @search="searchFn"
    ></x-select>
  </div>
</template>
<script>
export default {
  data() {
    return {
      selectData: [
        { id: 1, name: "LIST ONE 1" },
        { id: 2, name: "LIST ONE 2" },
        { id: 3, name: "LIST ONE 3" },
        { id: 4, name: "LIST ONE 4" },
        { id: 5, name: "LIST ONE 5" }
      ],
    };
  },
  methods: {
    searchFn(index, id) {
      console.log(index, id);
    }
  }
};
</script>

<template>
  <div>
    <!-- first -->
    <v-select
    title="LIST ONE"
    width="50%"
    defaultValue="0"
    @search="searchFn"
    :selectData="selectData"
    :alwaysShowTitle="false"
    ></v-select>
    <!-- second -->
    <v-select
    title="LIST TWO"
    width="50%"
    ellipsisWidth="65px"
    defaultValue="1"
    @search="searchFn1"
    :selectData="selectData1"
    ></v-select>
  </div>
</template>
<script>
export default {
  data() {
    return {
      selectData: [
        { id: 1, name: "LIST ONE 1" },
        { id: 2, name: "LIST ONE 2" },
        { id: 3, name: "LIST ONE 3" },
        { id: 4, name: "LIST ONE 4" },
        { id: 5, name: "LIST ONE 5" }
      ],
      selectData1: [
        { id: 1, name: "LIST TWO 1" },
        { id: 2, name: "LIST TWO 2" },
        { id: 3, name: "LIST TWO 3" },
        { id: 4, name: "LIST TWO 4" },
        { id: 5, name: "LIST TWO 5" }
      ]
    };
  },
  methods: {
    searchFn(index, id) {
      console.log(index, id);
    },
    searchFn1(index, id) {
      console.log(index, id);
    }
  }
};
</script>

Attributes

selectData array []
title string
alwaysShowTitle boolean false
defaultValue number/string 0
width string 100%
ellipsisWidth string 120px

Events

search

8. switch

<template>
  <ul class='v-list'>
    <li><label>默认switch,值:{{val1}}</label><v-switch v-model="val1"></v-switch></li>
    <li><label>设置宽高,默认选中,值:{{val2}}</label><v-switch @change="handleChange" v-model="val2" width="50" height="30"></v-switch></li>
    <li><label>禁止点击,值:{{val3}}</label><v-switch :disabled="true"></v-switch></li>
    <li><label>禁止点击,默认选中,值:{{val4}}</label><v-switch :disabled="true" v-model="val4"></v-switch></li>
  </ul>
</template>
<script>
export default {
  data () {
    return {
      val1: false,
      val2: true,
      val3: false,
      val4: true
    }
  },
  methods: {
    handleChange (val, oldVal) {
      console.log(val, oldVal);
    }
  }
}
</script>

Attributes

width number/string 60
height number/string
disabled boolean false
value boolean/number/string 0
activeClass string avtive
inactiveClass string inactive

Events

change

vui

vui

vuibrickies.github.io/vui

vuistar

Issue

PR