Дерево страниц

Сравнение версий

Ключ

  • Эта строка добавлена.
  • Эта строка удалена.
  • Изменено форматирование.
Комментарий: Написан текст статьи

Соединение с портлетом
urlhttp://youtube.com/watch?v=ltiqwSGoqhU


Работа с экраном приветствия

Шаг 1.

Реализуйте получение информации о пользователях при монтировании компонента с помощью метода onInfo() модуля faceRecognizeService. Для этого добавьте следующий текст  в mounted() в defineComponent компонента Home.

Блок кода
languagejs
this.$robot.faceRecognizeService.onInfo(info => {


})

Шаг 2.

Реализуйте метод getCount() для подсчёта лиц в кадре. Для этого добавьте следующий текст в methods в defineComponent компонента Home.

Блок кода
languagejs
getCount (info: FaceStructure[]): number {
  return info.filter(user => user.id > FACE_RECOGNIZING_CODE).length
}

Метод возвращает количество элементов массива, где ID пользователя больше, чем -1.

FACE_RECOGNIZING_CODE — это константа, в которой хранится значение -1, оно означает состояние, когда пользователь появился в кадре, но еще не определен.

Шаг 3.

Добавьте константу, где будет храниться количество лиц в кадре. Вызовите метод getCount() и сохраните полученное значение в этой константе. Для этого добавьте следующий текст в реализацию метода onInfo() в defineComponent компонента Home.

Блок кода
languagejs
const count = this.getCount(info)

Шаг 4.

Добавьте переменную для хранения текущего количества лиц. Для этого добавьте следующий текст в defineComponent компонента Home.

Блок кода
languagejs
data () {
  return {
    count: 0
  }
},

Шаг 5.

Добавьте обновление переменной текущего количества лиц, если оно отличается от константы. Для этого добавьте следующий текст в реализацию метода onInfo() в defineComponent компонента Home.

Блок кода
languagejs
if (count !== this.count) {
  this.count = count
}

Шаг 6. 

Реализуйте метод getGeneralUser() для определения главного лица в кадре. Для этого добавьте следующий текст в methods в defineComponent компонента Home.

Блок кода
languagejs
getGeneralUser (info: FaceStructure[]): FaceStructure | undefined {
  return info.find(user => user.id > FACE_RECOGNIZING_CODE && user.is_tracking)
}

Метод возвращает объект FaceStructure, либо undefined.

Шаг 7.

Создайте файл user.ts в modules в store. Добавьте следующий текст в файл user.ts. Всё это нам понадобится для определения, знакомый ли пользователь.

Блок кода
languagejs
collapsetrue
import { FaceStructure } from '@pb/api/dist/src/services/face_recognize'
import { Commit } from 'vuex'

export const MODULE_NAME = 'user'

interface UserStore {
  user: FaceStructure | undefined
}

export const ACTIONS = {
  SET_FACE_INFO: 'SET_FACE_INFO'
}

export const MUTATIONS = {
  SET_FACE_INFO: 'SET_FACE_INFO'
}

export const GETTERS = {
  GET_USER: 'GET_USER'
}

export const user = {
  namespaced: true,
  state: (): UserStore => ({
    user: undefined
  }),
  actions: {
    [ACTIONS.SET_FACE_INFO]: ({ commit }: { commit: Commit }, payload?: FaceStructure): void => commit(MUTATIONS.SET_FACE_INFO, payload)
  },
  mutations: {
    [MUTATIONS.SET_FACE_INFO]: (state: UserStore, payload?: FaceStructure): void => {
      state.user = payload
    }
  },
  getters: {
    [GETTERS.GET_USER]: (status: UserStore): FaceStructure | undefined => status.user
  }
}

Шаг 8.

Добавьте computed-свойство, которое будет возвращать user из store. Для этого добавьте следующий текст в defineComponent компонента Home.

Блок кода
languagejs
computed: {
  ...mapGetters(USER, {
    person: USER_GETTERS.GET_USER
  })
}

Шаг 9.

Добавьте константу, где будет храниться главное лицо в кадре (объект FaceStructure). Вызовите метод getGeneralUser() и сохраните полученное значение в этой константе. Для этого добавьте следующий текст в реализацию метода onInfo() в defineComponent компонента Home.

Блок кода
languagejs
const general = this.getGeneralUser(info)

Шаг 10.

Добавьте проверку главного лица в кадре. Для этого добавьте следующий текст в реализацию метода onInfo() в defineComponent компонента Home.

Блок кода
languagejs
if (general?.id !== this.person.id) {

}

Шаг 11.

Добавьте реализацию обновления главного лица в кадре. Для этого добавьте следующий текст в methods в defineComponent компонента Home.

Блок кода
languagejs
...mapActions(USER, {
  setFaceInfo: USER_ACTIONS.SET_FACE_INFO
}),

Шаг 12.

Добавьте открытие видеопотока при ненулевом количестве лиц. Для этого добавьте следующий текст в реализацию метода onInfo() в defineComponent компонента Home.

Блок кода
languagejs
if (this.count !== 1) {
  this.$router.push('/video')
}

Шаг 13.

Добавьте путь до компонента видеопотока в router\index.ts. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>.

Блок кода
languagejs
{
  path: '/video',
  name: 'Video',
  component: () => import('../views/VideoStream.vue')
},

Шаг 14.

Добавьте проверку, знакомый ли пользователь (для случая, когда количество лиц = 1). Добавьте файл checkPerson.ts в папку helpers. Реализуйте метод checkPerson(). Метод будет перенаправлять знакомого пользователя на страницу главного меню, а незнакомого пользователя — на страницу знакомства. Для этого добавьте следующий текст в файл checkPerson.ts.

Блок кода
languagejs
import store from '@/store'
import { GETTERS as USER_GETTERS, MODULE_NAME as USER } from '@/store/modules/user'
import router from '@/router'

export function checkPerson () {
  const personFromStorageId = store.getters[`${USER}/${USER_GETTERS.GET_USER}`]?.id ?? ''
  const personFromLocalStorage = JSON.parse(localStorage.getItem(personFromStorageId) || 'null')

  personFromLocalStorage == null
    ? router.push('/acquaintance')
    : router.push('/menu')
}

Шаг 15.

Добавьте вызов метода checkPerson() в реализацию метода onInfo() в defineComponent компонента Home. (строка 19)

Блок кода
languagejs
linenumberstrue
collapsetrue
  mounted () {
    this.$robot.faceRecognizeService.onInfo(info => {
      const count = this.getCount(info)

      if (count !== this.count) {
        this.count = count
      }

      const general = this.getGeneralUser(info)

      if (general?.id !== this.person.id) {
        this.setFaceInfo(general)
      }

      if (this.count !== 1) {
        this.$router.push('/video')
      }

      checkPerson()
    })
  },

Шаг 16.

Добавьте путь до страниц главного меню и знакомства в router\index.ts. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>.

Блок кода
languagejs
{
  path: '/acquaintance',
  name: 'Acquaintance',
  component: () => import('../views/AcquaintanceForm.vue')
},
{
  path: '/menu',
  name: 'MainMenu',
  component: () => import('../views/MainMenu.vue')
},

Работа с экраном видеопотока

Шаг 17.

Добавьте файл VideoStream.vue в папку views. Это экран видеопотока, на котором будет картинка с видеопотоком (получаемым в src) и кнопка Продолжить. Добавьте следующий текст в этот файл.

Блок кода
languagejs
collapsetrue
<template>
  <section class="face-request">
    <img
      id="video-stream"
      class="video-stream"
      src="http://localhost:8070/stream?topic=/facerecognition/image_raw&quality=60"
      alt="Видеопоток"
      width="768"
      height="432"
      @click.left="handleClick"
    />

    <pb-button
      class="_md"
      :disabled="disabled"
      @click="moveNext"
    >
      Продолжить
    </pb-button>
  </section>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'VideoStream'
})
</script>

<style lang="scss" scoped>
.face-request {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;

  height: 100%;
  padding-bottom: 20px;

  & button {
    margin-top: 30px;
  }
}
</style>

Шаг 18.

Создайте тип UnsubscribeFunction. Для этого добавьте следующий текст внутри тегов <script></script> файла VideoStream.vue.

Блок кода
languagejs
type UnsubscribeFunction = () => boolean

Шаг 19.

Создайте интерфейс Data в файле VideoStream.vue. Для этого добавьте следующий текст внутри тегов <script></script> файла VideoStream.vue.

Блок кода
languagejs
interface Data {
  faceEventSubscriber?: UnsubscribeFunction,
  disabled: boolean
}

Шаг 20.

Реализуйте функцию-подписчик FaceEventSubscriber. Для этого добавьте следующий текст в data() в defineComponent компонента VideoStream.

Блок кода
languagejs
data (): Data {
  return {
    faceEventSubscriber: undefined,
    disabled: true
  }
},

Шаг 21.

Реализуйте монтирование компонента. Это нужно для подписки на события от робота, которые будут сигнализировать о том, что установлен главный пользователь. Когда установлен главный пользователь, разблокируется кнопка Продолжить (disabled станет false). Для этого добавьте следующий текст в defineComponent компонента VideoStream.

Блок кода
languagejs
mounted () {
  this.faceEventSubscriber = this.$robot.faceRecognizeService.onGeneralSetted(() => {
    this.disabled = false
  })
}

Шаг 22.

Реализуйте обработку кликов по видеопотоку. В этом обработчике вызовите метод Promobot API faceRecognizeService() для установки главного пользователя. Для этого добавьте следующий текст в methods в defineComponent компонента VideoStream.

Блок кода
languagejs
handleClick: function (event: MouseEvent & { target: HTMLImageElement}) {
  if (event.target) {
    const coordinates = {
      x: event.offsetX / event.target.clientWidth,
      y: event.offsetY / event.target.clientHeight
    }

    this.$robot.faceRecognizeService.touchGeneral(coordinates)
  }
}

Шаг 23.

Реализуйте обработку кликов по кнопке Продолжить. При нажатии кнопки должна происходить проверка пользователя (знакомый или незнакомый) и в зависимости от этого, перенаправление на страницу главного меню или страницу знакомства. Метод для такой проверки уже реализован ранее, это checkPerson().Вызовите этот метод в обработчике кнопки. Для этого добавьте следующий текст в methods в defineComponent компонента VideoStream.

Блок кода
languagejs
moveNext (): void {
  checkPerson()
}

Шаг 24.

Реализуйте метод beforeUnmount(). При размонтировании компонента нужно очистить src картинки и вызвать метод faceEventSubscriber(). Для этого добавьте следующий текст в methods в defineComponent компонента VideoStream.

Блок кода
languagejs
beforeUnmount () {
  const image = document.getElementById('video-stream') as HTMLImageElement
  image.src = ''

  this.faceEventSubscriber?.()
}

Работа с экраном заполнения данных

Шаг 25.

Добавьте страницу AcquaintanceForm.vue в папку views. Это экран, на котором есть поле для ввода имени и кнопка сохранения. В этом проекте использована наша библиотека компонентов Promobot Library, но можно использовать любую другую. Добавьте следующий текст в этот файл.

Блок кода
languagejs
collapsetrue
<template>
  <Header title="Как вас зовут?"/>

  <section class="acquaintance">
    <pb-input
      class="acquaintance__input"
      v-model="name"
      :input-options="{
        label: 'Ваше имя',
        placeholder: 'Введите имя',
        enableErase: true
      }"
    />

    <pb-button
      class="_lg"
      :disabled="!isNotEmptyName"
      @click="saveUser"
    >
      Продолжить
    </pb-button>
  </section>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import Header from '@/components/Header.vue'
import { AcquaintanceCore, AcquaintanceProcessSteps } from '@/helpers/AcquintanceCore'
import { mapActions, mapGetters } from 'vuex'
import { ACTIONS as USER_ACTIONS, GETTERS as USER_GETTERS, MODULE_NAME as USER } from '@/store/modules/user'

export default defineComponent({
  name: 'AcquaintanceForm',
  components: {
    Header
  }
})
</script>

<style lang="scss" scoped>
.acquaintance {
  display: flex;
  flex-direction: column;
  align-items: center;

  padding: 30px;

  &__input {
    width: 720px;
    margin-bottom: 60px;
  }
}
</style>

Шаг 26.

Реализуйте метод сохранения пользователя в базу данных и в localStorage. Реализуйте его в data() с помощью Promobot API, создав instance ядра знакомства AcquaintanceCore. Для этого добавьте следующий текст в defineComponent компонента AcquaintanceForm.

Блок кода
languagejs
data () {
  return {
    name: '',
    core: new AcquaintanceCore(this.$robot)
  }
}

Шаг 27.

Добавьте computed-свойство, которое будет возвращать user из store (аналогично шагу 8). Для этого добавьте следующий текст в defineComponent компонента AcquaintanceForm.

Блок кода
languagejs
computed: {
  ...mapGetters(USER, {
    person: USER_GETTERS.GET_USER
  })
}

Шаг 28.

Реализуйте проверку на заполненность поля name. Для этого добавьте следующий текст в computed в defineComponent компонента AcquaintanceForm.

Блок кода
languagejs
isNotEmptyName (): boolean {
  return Boolean(this.name.length)
}

Шаг 29.

Реализуйте метод сохранения пользователя и обновления store новыми данными.

В методе в первую очередь происходит проверка на наличие person и на валидность ID. Далее из функции startAcquaintance() происходит получение нового ID и шага. Если шаг "локальное знакомство", т.е. удалось познакомиться, но запись не добавлена в глобальную базу, то вызывается метод ядра delete (т.е. delLocalUser) по новому ID. Иначе происходит обновление store через метод setFaceInfo(), куда передается текущий person и ID. В конце происходит сохранение пользователя в localStorage и переход в главное меню.

Чтобы это сделать добавьте следующий текст в defineComponent компонента AcquaintanceForm.

Блок кода
languagejs
methods: {
  ...mapActions(USER, {
    setFaceInfo: USER_ACTIONS.SET_FACE_INFO
  }),

  async saveUser (): Promise<void> {
    const minValidId = 1000
    if (this.person && this.person.id < minValidId) {
      const { newId, step } = await this.core.startAcquaintance(this.person, undefined, this.name)

      if (step === AcquaintanceProcessSteps.LocalAcquainted) {
        this.core.delLocalUser(newId)
      } else {
        this.setFaceInfo({ ...this.person, id: newId })
      }
    }

    localStorage.setItem(`${this.person?.id}`, JSON.stringify(this.person))

    this.$router.push('/menu')
  }
}

Работа с экраном главного меню

Шаг 30.

Добавьте путь до компонента главного меню в router\index.ts. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>.

Блок кода
languagejs
{
  path: '/menu',
  name: 'MainMenu',
  component: () => import('../views/MainMenu.vue')
}

Шаг 31.

Добавьте страницу MainMenu.vue в папку views. Главное меню состоит из двух кнопок: Об отеле и Поболтать. Добавьте следующий текст в этот файл.

Блок кода
languagejs
collapsetrue
<template>
  <section class="main-menu">
    <button
      class="main-menu__button"
      @click="aboutHotel"
    >
      Об отеле
    </button>
    <button
      class="main-menu__button"
      @click="toChat"
    >
      Поболтать
    </button>
    <button
      class="main-menu__button"
      @click="toData"
    >
      Персональные<br>данные
    </button>
  </section>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'MainMenu',
})
</script>

<style lang="scss" scoped>
.main-menu {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;

  height: 100%;
  padding: 100px 20px;

  &__button {
    font-family: inherit;

    font-weight: 600;
    font-size: 32px;
    line-height: 42px;

    background-color: #ffffff;
    box-shadow: 0px 10px 15px -8px rgba(29, 29, 47, 0.2);
    border: none;
    border-radius: 10px;
  }
}
</style>

Шаг 32.

Реализуйте метод для кнопки Об отеле. Для этого добавьте следующий текст в methods в defineComponent компонента MainMenu.

Блок кода
languagejs
aboutHotel (): void {
  this.$robot.dialogService.sayReplicByName('ABOUT-HOTEL')
}

Шаг 33.

Реализуйте метод для кнопки Поболтать. Для этого добавьте следующий текст в methods в defineComponent компонента MainMenu.

Блок кода
languagejs
toChat (): void {
  this.$router.push('/chat')
}

Шаг 34.

Добавьте путь до компонента кнопки Поболтать в router\index.ts. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>.

Блок кода
languagejs
{
  path: '/chat',
  name: 'Chat',
  component: () => import('../views/Chat.vue')
}

Работа с экраном Поболтать

Шаг 35.

Добавьте страницу Chat.vue в папку views. Добавьте следующий текст в этот файл.

Блок кода
languagejs
collapsetrue
<template>
  <Header show-back-button/>
  <section class="chat">
    <h2>Задайте ваш вопрос!</h2>
    <img src="@/assets/chat-robot.png" width="600" height="320" alt="chat-robot">
  </section>
  <Footer/>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'

export default defineComponent({
  name: 'Chat',
  components: {
    Header,
    Footer
  }
})
</script>

<style lang="scss" scoped>
.chat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;

  height: 565px;
  padding: 20px;

  & h2 {
    margin-bottom: 64px;

    font-size: 40px;
    font-weight: 400;
    line-height: 48px;
  }
}
</style>

Шаг 36.

Добавьте вызов метода, который установит Лингвокейс при монтировании компонента Chat. Для этого добавьте следующий текст в defineComponent компонента Chat.

Блок кода
languagejs
mounted () {
  this.$robot.dialogService.setCase('chat')
}