AddProductFoodkeeper

AddProductFoodkeeper

<template>

          <button class="btn btn-more" v-on:click="increment">+</button>
          <button class="btn btn-less" v-on:click="decrement">-</button>

          +
          <input id="product-item-count" value="0" v-model="form.quantity" number hidden>

{{ form.quantity }}

        <input v-model="form.name" type="text" placeholder="Nom du produit" />

        <input v-model="form.description" type="text" placeholder="Description du produit" />

        <input id="raw" class="filter" type="radio" value="true" v-model="form.type" checked>
        Produits Bruts
        <input id="homemade" class="filter" type="radio" value="false" v-model="form.type">
        Plats Préparés

        Dans quel(s) garde(s)-manger(s) proposez-vous ce produit ?

        <select name="foodkeepersSelect" class="foodkeeper-add-product-form-select" id="foodkeepersSelect" v-model="foodkeepersSelected" multiple >
          <template  v-for="foodkeeper in foodkeepers">
            <option value="{{foodkeeper.name}}">  {{ foodkeeper.name }}</option>
          </template>
        </select>

            <template v-for="item in foodkeepersSelected">

{{ item }}

            </template>

Sélectionnez...

À consommer sous

        {{ form.dlc }} jours
        <input type="range" name="product-dlc" value="1" max="10" min="1" step="1" v-model="form.dlc"/>

1 jour

10 jours

Ajouter

  <validation destination="Market" add="Product"></validation>
</template>

<template>

          <button class="btn btn-more" v-on:click="increment">+</button>
          <button class="btn btn-less" v-on:click="decrement">-</button>

          +
          <input id="product-item-count" value="0" v-model="form.quantity" number hidden>

{{ form.quantity }}

        <input v-model="form.name" type="text" placeholder="Nom du produit" />

        <input v-model="form.description" type="text" placeholder="Description du produit" />

        <input id="raw" class="filter" type="radio" value="true" v-model="form.type" checked>
        Produits Bruts
        <input id="homemade" class="filter" type="radio" value="false" v-model="form.type">
        Plats Préparés

        Dans quel(s) garde(s)-manger(s) proposez-vous ce produit ?

        <select name="foodkeepersSelect" class="foodkeeper-add-product-form-select" id="foodkeepersSelect" v-model="foodkeepersSelected" hidden multiple >
          <template  v-for="foodkeeper in foodkeepers">
            <option value="{{foodkeeper.name}}">  {{ foodkeeper.name }}</option>
          </template>
        </select>

            <template v-for="item in foodkeepersSelected">

{{ item }}

            </template>

Sélectionnez...

À consommer sous

        {{ form.dlc }} jours
        <input type="range" name="product-dlc" value="1" max="10" min="1" step="1" v-model="form.dlc"/>

1 jour

10 jours

Ajouter

  <validation destination="Market" add="Product"></validation>
</template>


ensure this line is on top

ensure this line is on top

<script lang="coffee">
import Records        from '@/shared/services/records'
import AbilityService from '@/shared/services/ability_service'
import RecordLoader   from '@/shared/services/record_loader'
import Session        from '@/shared/services/session'
import EventBus       from '@/shared/services/event_bus'
import {includes, some, compact, intersection, orderBy, slice, debounce, min, escapeRegExp, map} from 'lodash'
import LmoUrlService from '@/shared/services/lmo_url_service'
import { exact, approximate } from '@/shared/helpers/format_time'

export default
  data: ->
    loader: null
    group: null
    per: 100
    from: 0
    order: 'created_at desc'
    orders: [
      {text: @$t('members_panel.order_by_name'),  value:'users.name' }
      {text: @$t('members_panel.order_by_created'), value:'memberships.created_at' }
      {text: @$t('members_panel.order_by_created_desc'), value:'memberships.created_at desc' }
      {text: @$t('members_panel.order_by_admin_desc'), value:'admin desc' }
    ]
    memberships: []

  created: ->
    @onQueryInput = debounce (val) =>
      @$router.replace(@mergeQuery(q: val))
    , 500

    Records.groups.findOrFetch(@$route.params.key).then (group) =>
      @group = group

      EventBus.$emit 'currentComponent',
        page: 'groupPage'
        title: @group.name
        group: @group
        search:
          placeholder: @$t('navbar.search_members', name: @group.parentOrSelf().name)

      @loader = new RecordLoader
        collection: 'memberships'
        path: 'autocomplete'
        params:
          exclude_types: 'group'
          group_id: @group.id
          pending: true
          per: @per
          from: @from
          order: @order
          subgroups: 'all'

      @watchRecords
        collections: ['memberships', 'groups']
        query: @query

      @refresh()

  methods:
    exact: exact
    approximate: approximate

    query: ->
      chain = Records.memberships.collection.chain()
      switch @$route.query.subgroups
        when 'mine'
          chain = chain.find(groupId: {$in: intersection(@group.organisationIds(), Session.user().groupIds())})
        when 'none'
          chain = chain.find(groupId: @group.id)
        else
          chain = chain.find(groupId: {$in: @group.organisationIds()})
        # when 'all'
        #   chain = chain.find(groupId: {$in: @group.organisationIds()})

      if @$route.query.q
        users = Records.users.collection.find
          $or: [
            {name: {'$regex': ["^#{@$route.query.q}", "i"]}},
            {email: {'$regex': ["#{@$route.query.q}", "i"]}},
            {username: {'$regex': ["^#{@$route.query.q}", "i"]}},
            {name: {'$regex': [" #{@$route.query.q}", "i"]}}
          ]
        chain = chain.find(userId: {$in: map(users, 'id')})

      switch @$route.query.filter
        when 'admin'
          chain = chain.find(admin: true)
        when 'pending'
          chain = chain.find(acceptedAt: null)

      userIds = []
      membershipIds = chain.simplesort('groupId').data().filter (m) ->
        if userIds.includes(m.userId)
          false
        else
          userIds.push(m.userId)
          true
      .map (m) -> m.id

      chain = chain.find(id: {$in: membershipIds})

      if @$route.query.q
        chain = chain.sort (a,b) ->
          return -1 if a.user().name < b.user().name
          return 1  if a.user().name > b.user().name
          return 0
      else
        chain = chain.simplesort('createdAt', true)

      @memberships = chain.data()

    refresh: ->
      @fetch()
      @query()

    fetch: ->
      @loader.fetchRecords
        q: @$route.query.q
        from: @from
        order: @order
        filter: @$route.query.filter
        subgroups: @$route.query.subgroups

    invite: ->
      EventBus.$emit('openModal',
                      component: 'AnnouncementForm',
                      props:
                        announcement: Records.announcements.buildFromModel(@group))

  computed:
    membershipRequestsPath: -> LmoUrlService.membershipRequest(@group)
    showLoadMore: -> !@loader.exhausted
    totalRecords: ->
      if @pending
        @group.pendingMembershipsCount
      else
        @group.membershipsCount - @group.pendingMembershipsCount

    canAddMembers: ->
      AbilityService.canAddMembersToGroup(@group) && !@pending

    onlyOneAdminWithMultipleMembers: ->
      (@group.adminMembershipsCount < 2) && ((@group.membershipsCount - @group.adminMembershipsCount) > 0)

  watch:
    '$route.query': 'refresh'

</script>

<template lang="pug">
.members-panel
  loading(v-if="!group")
  div(v-if="group")
    v-alert.my-2(v-model="onlyOneAdminWithMultipleMembers" color="primary" type="warning")
      template(slot="default")
        span(v-t="'memberships_page.only_one_admin'")

    v-layout.py-2(align-center wrap)
      v-menu
        template(v-slot:activator="{ on, attrs }")
          v-btn.members-panel__filters.mr-2.text-lowercase(v-on="on" v-bind="attrs" text)
            span(v-if="$route.query.filter == 'admin'" v-t="'members_panel.order_by_admin_desc'")
            span(v-if="$route.query.filter == 'pending'" v-t="'members_panel.invitations'")
            span(v-if="!$route.query.filter" v-t="'members_panel.everyone'")
            v-icon mdi-menu-down
        v-list(dense)
          v-list-item.members-panel__filters-everyone(:to="mergeQuery({filter: null})")
            v-list-item-title(v-t="'members_panel.everyone'")
          v-list-item.members-panel__filters-admins(:to="mergeQuery({filter: 'admin'})")
            v-list-item-title(v-t="'members_panel.order_by_admin_desc'")
          v-list-item.members-panel__filters-invitations(:to="mergeQuery({filter: 'pending'})")
            v-list-item-title(v-t="'members_panel.invitations'")
      v-text-field.mr-2(clearable hide-details solo :value="$route.query.q" @input="onQueryInput" :placeholder="$t('navbar.search_members', {name: group.name})" append-icon="mdi-magnify")
      v-btn.membership-card__invite.mr-2(color="primary" v-if='canAddMembers' @click="invite()" v-t="'common.action.invite'")
      shareable-link-modal(v-if='canAddMembers' :group="group")
      v-btn.group-page__requests-tab(:to="urlFor(group, 'members/requests')" v-t="'members_panel.requests'")

    v-card(outlined)
      div(v-if="loader.status == 403")
        p.pa-4.text-center(v-t="'error_page.forbidden'")
      div(v-else)
        p.pa-4.text-center(v-if="!memberships.length" v-t="'common.no_results_found'")
        v-list(v-else three-line)
          v-list-item(v-for="membership in memberships" :key="membership.id")
            v-list-item-avatar(size='48')
              router-link(:to="urlFor(membership.user())")
                user-avatar(:user='membership.user()' size='48')
            v-list-item-content
              v-list-item-title
                router-link(:to="urlFor(membership.user())") {{ membership.user().name }}
                space
                span.caption(v-if="$route.query.subgroups") {{membership.group().name}}
                space
                span.title.caption {{membership.title}}
                space
                v-chip(v-if="membership.admin" small outlined label v-t="'members_panel.admin'")
              v-list-item-subtitle
                span(v-if="membership.groupId != group.id" v-t="{path: 'members_panel.only_in_subgroups', args: {name: membership.group().name}}")
                span(v-if="membership.acceptedAt") {{ (membership.user().shortBio || '').replace(/<\/?[^>]+(>|$)/g, "") }}
                span(v-if="!membership.acceptedAt && membership.inviter()" v-t="{path: 'members_panel.invited_by_name', args: {name: membership.inviter().name}}")
            v-list-item-action
              membership-dropdown(v-if="membership.groupId == group.id" :membership="membership")
        v-layout(justify-center)
          v-btn.my-2(outlined color='accent' v-if="showLoadMore" :loading="loader.loading" @click="loader.fetchRecords()" v-t="'common.action.load_more'")

</template>

<script lang="coffee">
import Records        from '@/shared/services/records'
import AbilityService from '@/shared/services/ability_service'
import RecordLoader   from '@/shared/services/record_loader'
import Session        from '@/shared/services/session'
import EventBus       from '@/shared/services/event_bus'
import {includes, some, compact, intersection, orderBy, slice, debounce, min, escapeRegExp, map} from 'lodash'
import LmoUrlService from '@/shared/services/lmo_url_service'
import { exact, approximate } from '@/shared/helpers/format_time'

export default
  data: ->
    loader: null
    group: null
    per: 100
    from: 0
    order: 'created_at desc'
    orders: [
      {text: @$t('members_panel.order_by_name'),  value:'users.name' }
      {text: @$t('members_panel.order_by_created'), value:'memberships.created_at' }
      {text: @$t('members_panel.order_by_created_desc'), value:'memberships.created_at desc' }
      {text: @$t('members_panel.order_by_admin_desc'), value:'admin desc' }
    ]
    memberships: []

  created: ->
    @onQueryInput = debounce (val) =>
      @$router.replace(@mergeQuery(q: val))
    , 500

    Records.groups.findOrFetch(@$route.params.key).then (group) =>
      @group = group

      EventBus.$emit 'currentComponent',
        page: 'groupPage'
        title: @group.name
        group: @group
        search:
          placeholder: @$t('navbar.search_members', name: @group.parentOrSelf().name)

      @loader = new RecordLoader
        collection: 'memberships'
        path: 'autocomplete'
        params:
          exclude_types: 'group'
          group_id: @group.id
          pending: true
          per: @per
          from: @from
          order: @order
          subgroups: 'all'

      @watchRecords
        collections: ['memberships', 'groups']
        query: @query

      @refresh()

  methods:
    exact: exact
    approximate: approximate

    query: ->
      chain = Records.memberships.collection.chain()
      switch @$route.query.subgroups
        when 'mine'
          chain = chain.find(groupId: {$in: intersection(@group.organisationIds(), Session.user().groupIds())})
        when 'none'
          chain = chain.find(groupId: @group.id)
        else
          chain = chain.find(groupId: {$in: @group.organisationIds()})
        # when 'all'
        #   chain = chain.find(groupId: {$in: @group.organisationIds()})

      if @$route.query.q
        users = Records.users.collection.find
          $or: [
            {name: {'$regex': ["^#{@$route.query.q}", "i"]}},
            {email: {'$regex': ["#{@$route.query.q}", "i"]}},
            {username: {'$regex': ["^#{@$route.query.q}", "i"]}},
            {name: {'$regex': [" #{@$route.query.q}", "i"]}}
          ]
        chain = chain.find(userId: {$in: map(users, 'id')})

      switch @$route.query.filter
        when 'admin'
          chain = chain.find(admin: true)
        when 'pending'
          chain = chain.find(acceptedAt: null)

      userIds = []
      membershipIds = chain.simplesort('groupId').data().filter (m) ->
        if userIds.includes(m.userId)
          false
        else
          userIds.push(m.userId)
          true
      .map (m) -> m.id

      chain = chain.find(id: {$in: membershipIds})

      if @$route.query.q
        chain = chain.sort (a,b) ->
          return -1 if a.user().name < b.user().name
          return 1  if a.user().name > b.user().name
          return 0
      else
        chain = chain.simplesort('createdAt', true)

      @memberships = chain.data()

    refresh: ->
      @fetch()
      @query()

    fetch: ->
      @loader.fetchRecords
        q: @$route.query.q
        from: @from
        order: @order
        filter: @$route.query.filter
        subgroups: @$route.query.subgroups

    invite: ->
      EventBus.$emit('openModal',
                      component: 'AnnouncementForm',
                      props:
                        announcement: Records.announcements.buildFromModel(@group))

  computed:
    membershipRequestsPath: -> LmoUrlService.membershipRequest(@group)
    showLoadMore: -> !@loader.exhausted
    totalRecords: ->
      if @pending
        @group.pendingMembershipsCount
      else
        @group.membershipsCount - @group.pendingMembershipsCount

    canAddMembers: ->
      AbilityService.canAddMembersToGroup(@group) && !@pending

    onlyOneAdminWithMultipleMembers: ->
      (@group.adminMembershipsCount < 2) && ((@group.membershipsCount - @group.adminMembershipsCount) > 0)

  watch:
    '$route.query': 'refresh'

</script>

<template lang="pug">
.members-panel
  loading(v-if="!group")
  div(v-if="group")
    v-alert.my-2(v-model="onlyOneAdminWithMultipleMembers" color="primary" type="warning")
      template(slot="default")
        span(v-t="'memberships_page.only_one_admin'")

    v-layout.py-2(align-center wrap)
      v-menu
        template(v-slot:activator="{ on, attrs }")
          v-btn.members-panel__filters.mr-2.text-lowercase(v-on="on" v-bind="attrs" text)
            span(v-if="$route.query.filter == 'admin'" v-t="'members_panel.order_by_admin_desc'")
            span(v-if="$route.query.filter == 'pending'" v-t="'members_panel.invitations'")
            span(v-if="!$route.query.filter" v-t="'members_panel.everyone'")
            v-icon mdi-menu-down
        v-list(dense)
          v-list-item.members-panel__filters-everyone(:to="mergeQuery({filter: null})")
            v-list-item-title(v-t="'members_panel.everyone'")
          v-list-item.members-panel__filters-admins(:to="mergeQuery({filter: 'admin'})")
            v-list-item-title(v-t="'members_panel.order_by_admin_desc'")
          v-list-item.members-panel__filters-invitations(:to="mergeQuery({filter: 'pending'})")
            v-list-item-title(v-t="'members_panel.invitations'")
      v-text-field.mr-2(clearable hide-details solo :value="$route.query.q" @input="onQueryInput" :placeholder="$t('navbar.search_members', {name: group.name})" append-icon="mdi-magnify")
      v-btn.membership-card__invite.mr-2(color="primary" v-if='canAddMembers' @click="invite()" v-t="'common.action.invite'")
      shareable-link-modal(v-if='canAddMembers' :group="group")
      v-btn.group-page__requests-tab(:to="urlFor(group, 'members/requests')" v-t="'members_panel.requests'")

    v-card(outlined)
      div(v-if="loader.status == 403")
        p.pa-4.text-center(v-t="'error_page.forbidden'")
      div(v-else)
        p.pa-4.text-center(v-if="!memberships.length" v-t="'common.no_results_found'")
        v-list(v-else three-line)
          v-list-item(v-for="membership in memberships" :key="membership.id")
            v-list-item-avatar(size='48')
              router-link(:to="urlFor(membership.user())")
                user-avatar(:user='membership.user()' size='48')
            v-list-item-content
              v-list-item-title
                router-link(:to="urlFor(membership.user())") {{ membership.user().name }}
                space
                span.caption(v-if="$route.query.subgroups") {{membership.group().name}}
                space
                span.title.caption {{membership.title}}
                space
                v-chip(v-if="membership.admin" small outlined label v-t="'members_panel.admin'")
              v-list-item-subtitle
                span(v-if="membership.acceptedAt") {{ (membership.user().shortBio || '').replace(/<\/?[^>]+(>|$)/g, "") }}
                span(v-if="!membership.acceptedAt && membership.inviter()" v-t="{path: 'members_panel.invited_by_name', args: {name: membership.inviter().name}}")
                span(v-if="membership.groupId != group.id" v-t="{path: 'members_panel.only_in_subgroups', args: {name: membership.group().name}}")
            v-list-item-action
              membership-dropdown(v-if="membership.groupId == group.id" :membership="membership")
        v-layout(justify-center)
          v-btn.my-2(outlined color='accent' v-if="showLoadMore" :loading="loader.loading" @click="loader.fetchRecords()" v-t="'common.action.load_more'")

</template>


l10n: Add dot

l10n: Add dot

<template>
	<AppSettingsDialog
		:open.sync="showSettings"
		:show-navigation="true"
		first-selected-section="keyboard shortcuts"
		container="#content-vue">
		<AppSettingsSection :title="t('spreed', 'Choose devices')"
			class="app-settings-section">
			<MediaDevicesPreview />
		</AppSettingsSection>
		<AppSettingsSection v-if="!isGuest"
			:title="t('spreed', 'Attachments folder')"
			class="app-settings-section">

				{{ locationHint }}

			<input
				type="text"
				class="app-settings-section__input"
				:value="attachmentFolder"
				:disabled="attachmentFolderLoading"
				@click="selectAttachmentFolder">
		</AppSettingsSection>
		<AppSettingsSection v-if="!isGuest"
			:title="t('spreed', 'Privacy')"
			class="app-settings-section">
			<input id="read_status_privacy"
				:checked="readStatusPrivacyIsPublic"
				:disabled="privacyLoading"
				type="checkbox"
				name="read_status_privacy"
				class="checkbox"
				@change="toggleReadStatusPrivacy">
			{{ t('spreed', 'Share my read-status and show the read-status of others') }}
		</AppSettingsSection>
		<AppSettingsSection
			:title="t('spreed', 'Sounds')"
			class="app-settings-section">
			<input id="play_sounds"
				:checked="playSounds"
				:disabled="playSoundsLoading"
				type="checkbox"
				class="checkbox"
				@change="togglePlaySounds">
			{{ t('spreed', 'Play sounds when participants join or leave a call') }}
			{{ t('spreed', 'Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer.') }}
		</AppSettingsSection>
		<AppSettingsSection :title="t('spreed', 'Keyboard shortcuts')">

{{ t('spreed', 'Speed up your Talk experience with these quick shortcuts.') }}

C

						{{ t('spreed', 'Focus the chat input') }}

Esc

						{{ t('spreed', 'Unfocus the chat input to use shortcuts') }}

F

						{{ t('spreed', 'Fullscreen the chat or call') }}

Ctrl + F

						{{ t('spreed', 'Search') }}

{{ t('spreed', 'Shortcuts while in a call.') }}

V

						{{ t('spreed', 'Video on and off') }}

M

						{{ t('spreed', 'Microphone on and off') }}

{{ t('spreed', 'Space bar') }}

						{{ t('spreed', 'Push to talk or push to mute') }}

R

						{{ t('spreed', 'Raise or lower hand') }}

		</AppSettingsSection>
	</AppSettingsDialog>
</template>

<template>
	<AppSettingsDialog
		:open.sync="showSettings"
		:show-navigation="true"
		first-selected-section="keyboard shortcuts"
		container="#content-vue">
		<AppSettingsSection :title="t('spreed', 'Choose devices')"
			class="app-settings-section">
			<MediaDevicesPreview />
		</AppSettingsSection>
		<AppSettingsSection v-if="!isGuest"
			:title="t('spreed', 'Attachments folder')"
			class="app-settings-section">

				{{ locationHint }}

			<input
				type="text"
				class="app-settings-section__input"
				:value="attachmentFolder"
				:disabled="attachmentFolderLoading"
				@click="selectAttachmentFolder">
		</AppSettingsSection>
		<AppSettingsSection v-if="!isGuest"
			:title="t('spreed', 'Privacy')"
			class="app-settings-section">
			<input id="read_status_privacy"
				:checked="readStatusPrivacyIsPublic"
				:disabled="privacyLoading"
				type="checkbox"
				name="read_status_privacy"
				class="checkbox"
				@change="toggleReadStatusPrivacy">
			{{ t('spreed', 'Share my read-status and show the read-status of others') }}
		</AppSettingsSection>
		<AppSettingsSection
			:title="t('spreed', 'Sounds')"
			class="app-settings-section">
			<input id="play_sounds"
				:checked="playSounds"
				:disabled="playSoundsLoading"
				type="checkbox"
				class="checkbox"
				@change="togglePlaySounds">
			{{ t('spreed', 'Play sounds when participants join or leave a call') }}
			{{ t('spreed', 'Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer.') }}
		</AppSettingsSection>
		<AppSettingsSection :title="t('spreed', 'Keyboard shortcuts')">

{{ t('spreed', 'Speed up your Talk experience with these quick shortcuts.') }}

C

						{{ t('spreed', 'Focus the chat input') }}

Esc

						{{ t('spreed', 'Unfocus the chat input to use shortcuts') }}

F

						{{ t('spreed', 'Fullscreen the chat or call') }}

Ctrl + F

						{{ t('spreed', 'Search') }}

{{ t('spreed', 'Shortcuts while in a call') }}

V

						{{ t('spreed', 'Video on and off') }}

M

						{{ t('spreed', 'Microphone on and off') }}

{{ t('spreed', 'Space bar') }}

						{{ t('spreed', 'Push to talk or push to mute') }}

R

						{{ t('spreed', 'Raise or lower hand') }}

		</AppSettingsSection>
	</AppSettingsDialog>
</template>


- Animate on tagKnobs wrapper

- Animate on tagKnobs wrapper

<template>

          <cardTotal
            title="Total Posts"
            background-color="bg-teal-9"
            icon-name="local_post_office"
            :total="totalPosts">
          </cardTotal>

          <cardTotal
            title="Total comments"
            background-color="bg-teal-7"
            icon-name="comment"
            :total="totalComments">
          </cardTotal>

          <cardTotal
            title="Static total"
            background-color="bg-teal-5"
            icon-name="repeat_one"
            :total="50004">
          </cardTotal>

          <card-chart
            card-title="Total Graph"
            :data="dataForGraph"
          ></card-chart>

            <knob-statistics
              card-title="General statistics">
            </knob-statistics>

          <card-todo
            card-title="Generic todos"
            api="todos">
          </card-todo>

</template>


docs(examples): remove v- from custom sheet footer

<template>
  <v-container>
    <v-row class="flex-child">
      <v-col
        class="d-flex"
        cols="12"
        md="4"
      >
        <v-sheet
          class="d-flex"
          color="grey lighten-3"
          height="424"
        >
          <sheet-footer>
            #1: (3r x 2c)
          </sheet-footer>
        </v-sheet>
      </v-col>
      <v-col
        class="d-flex"
        cols="12"
        md="4"
      >
        <v-row class="ma-n3">
          <v-col cols="6">
            <v-sheet
              class="d-flex"
              color="green lighten-3"
              height="150"
            >
              <sheet-footer>
                #2: (1r x 1c)
              </sheet-footer>
            </v-sheet>
          </v-col>

          <v-col cols="6">
            <v-sheet
              class="d-flex"
              color="yellow lighten-3"
              height="150"
            >
              <sheet-footer>
                #3: (1r x 1c)
              </sheet-footer>
            </v-sheet>
          </v-col>

          <v-col cols="12">
            <v-sheet
              class="d-flex"
              color="red lighten-3"
              height="250"
            >
              <sheet-footer>
                #5: (2r x 2c)
              </sheet-footer>
            </v-sheet>
          </v-col>
        </v-row>
      </v-col>

      <v-col
        cols="6"
        md="2"
      >
        <v-sheet
          class="d-flex"
          color="teal lighten-3"
          height="300"
        >
          <sheet-footer>
            #4: (2r x 1c)
          </sheet-footer>
        </v-sheet>
      </v-col>

      <v-col
        class="d-flex"
        cols="6"
        md="2"
      >
        <v-sheet
          class="d-flex mt-auto"
          color="purple lighten-3"
          height="300"
        >
          <sheet-footer>
            #6: (2r x 1c)
          </sheet-footer>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  export default {
    components: {
      // A simple helper component
      SheetFooter: {
        functional: true,

        render (h, { children }) {
          return h('v-sheet', {
            staticClass: 'mt-auto align-center justify-center d-flex px-2',
            props: {
              color: 'rgba(0, 0, 0, .36)',
              dark: true,
              height: 50,
            },
          }, children)
        },
      },
    },
  }
</script>

<template>
  <v-container>
    <v-row class="flex-child">
      <v-col
        class="d-flex"
        cols="12"
        md="4"
      >
        <v-sheet
          class="d-flex"
          color="grey lighten-3"
          height="424"
        >
          <v-sheet-footer>
            #1: (3r x 2c)
          </v-sheet-footer>
        </v-sheet>
      </v-col>

      <v-col
        class="d-flex"
        cols="12"
        md="4"
      >
        <v-row class="ma-n3">
          <v-col cols="6">
            <v-sheet
              class="d-flex"
              color="green lighten-3"
              height="150"
            >
              <v-sheet-footer>
                #2: (1r x 1c)
              </v-sheet-footer>
            </v-sheet>
          </v-col>

          <v-col cols="6">
            <v-sheet
              class="d-flex"
              color="yellow lighten-3"
              height="150"
            >
              <v-sheet-footer>
                #3: (1r x 1c)
              </v-sheet-footer>
            </v-sheet>
          </v-col>

          <v-col cols="12">
            <v-sheet
              class="d-flex"
              color="red lighten-3"
              height="250"
            >
              <v-sheet-footer>
                #5: (2r x 2c)
              </v-sheet-footer>
            </v-sheet>
          </v-col>
        </v-row>
      </v-col>

      <v-col
        cols="6"
        md="2"
      >
        <v-sheet
          class="d-flex"
          color="teal lighten-3"
          height="300"
        >
          <v-sheet-footer>
            #4: (2r x 1c)
          </v-sheet-footer>
        </v-sheet>
      </v-col>

      <v-col
        class="d-flex"
        cols="6"
        md="2"
      >
        <v-sheet
          class="d-flex mt-auto"
          color="purple lighten-3"
          height="300"
        >
          <v-sheet-footer>
            #6: (2r x 1c)
          </v-sheet-footer>
        </v-sheet>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  export default {
    components: {
      // A simple helper component
      VSheetFooter: {
        functional: true,

        render (h, { children }) {
          return h('v-sheet', {
            staticClass: 'mt-auto align-center justify-center d-flex px-2',
            props: {
              color: 'rgba(0, 0, 0, .36)',
              dark: true,
              height: 50,
            },
          }, children)
        },
      },
    },
  }
</script>


메인 최신 글자수 제한 교정

메인 최신 글자수 제한 교정

<template>

 {{$t('common.news')}}

{{$t('failed.loading')}}

      <template v-if="latest">

 {{$t('home.posts.latest')}}

              <router-link :to="{name: 'board.view', params: {name: article.board.toLowerCase(), seq: article.seq}}" v-for="article in latest.articles" :key="article.seq" class="item">

                        {{convertBoardName(article.board)}}
{{categories[article.board][article.category].name}}

                      {{article.status.delete ? $t('board.msg.deleted') : article.subject}}

{{truncate(article.shortContent)}}

                      {{article.writer.username}} · {{article.id | IdToRegDate('LL')}}

              </router-link>

 {{$t('home.comments.latest')}}

              <router-link :to="{name: 'board.view', params: {name: comment.article.board.toLowerCase(), seq: comment.article.seq}}" v-for="comment in latest.comments" :key="comment.id" class="item">

{{comment.content}}

{{comment.article.subject}}

                    {{comment.writer.username}} &middot {{comment.id | IdToRegDate('LL')}}

              </router-link>

      </template>

          <router-link to="/gallery" class="black-link">
             {{$t('home.pictures.latest')}}
          </router-link>

                <router-link :to="{name: 'gallery.view', params: {id: image.id}}" class="ui rounded bordered image">

                     {{image.writer.username}}

                </router-link>

{{$t('failed.loading')}}

 {{$t('popular.search.words')}}

          <router-link :to="{name: 'search', query: {q: word.key, w: 'ARTICLE;COMMENT;GALLERY', from: 0, size: 3}}" v-for="(word, index) in popularSearchWords" :key="word.key" :class="indexedColor(index, true)" class="ui label search-keyword">
            {{word.key}}
          </router-link>

{{$t('failed.loading')}}

 {{$t('home.members.registered.latest')}}

{{user.username}}

{{user.about}}

{{$t('failed.loading')}}

 {{$t('home.encyclopedia')}}

            {{encyclopedia.subject}}

{{encyclopediaSummary}}

{{$t('failed.loading')}}

</template>

<template>

 {{$t('common.news')}}

{{$t('failed.loading')}}

      <template v-if="latest">

 {{$t('home.posts.latest')}}

              <router-link :to="{name: 'board.view', params: {name: article.board.toLowerCase(), seq: article.seq}}" v-for="article in latest.articles" :key="article.seq" class="item">

                        {{convertBoardName(article.board)}}
{{categories[article.board][article.category].name}}

                      {{article.status.delete ? $t('board.msg.deleted') : article.subject}}

{{article.shortContent}}...

                      {{article.writer.username}} · {{article.id | IdToRegDate('LL')}}

              </router-link>

 {{$t('home.comments.latest')}}

              <router-link :to="{name: 'board.view', params: {name: comment.article.board.toLowerCase(), seq: comment.article.seq}}" v-for="comment in latest.comments" :key="comment.id" class="item">

{{comment.content}}

{{comment.article.subject}}

                    {{comment.writer.username}} &middot {{comment.id | IdToRegDate('LL')}}

              </router-link>

      </template>

          <router-link to="/gallery" class="black-link">
             {{$t('home.pictures.latest')}}
          </router-link>

                <router-link :to="{name: 'gallery.view', params: {id: image.id}}" class="ui rounded bordered image">

                     {{image.writer.username}}

                </router-link>

{{$t('failed.loading')}}

 {{$t('popular.search.words')}}

          <router-link :to="{name: 'search', query: {q: word.key, w: 'ARTICLE;COMMENT;GALLERY', from: 0, size: 3}}" v-for="(word, index) in popularSearchWords" :key="word.key" :class="indexedColor(index, true)" class="ui label search-keyword">
            {{word.key}}
          </router-link>

{{$t('failed.loading')}}

 {{$t('home.members.registered.latest')}}

{{user.username}}

{{user.about}}

{{$t('failed.loading')}}

 {{$t('home.encyclopedia')}}

            {{encyclopedia.subject}}

{{encyclopediaSummary}}

{{$t('failed.loading')}}

</template>


shorten meeting type

shorten meeting type

<template>

            Meeting Date(s)

            Meeting Day(s)

            Meeting Time

            Meeting Location

            Meeting Type

            {{ formatEos(meeting) }}

            Class does not meet

            Days and times to be arranged

            No classroom meeting: online learning

          <template v-else-if="meeting.start_time && meeting.end_time">

              M
              T
              W

              Th

              F
              Sa

              Su

              {{ meeting.start_time.format('h:mm A') }} –
              {{ meeting.end_time.format('h:mm A') }}

                Remote

                Room to be arranged

                <a
                  v-if="locationUrl(meeting)"
                  :href="locationUrl(meeting)"
                  target="_blank"
                  :title="`Map ${meeting.building}`"
                >
                  {{ meeting.building }}

                  {{ meeting.room }}

                  {{ meeting.room }}

          </template>

              {{ shortenMtgType(meeting.type) }}

</template>

<template>

            Meeting Date(s)

            Meeting Day(s)

            Meeting Time

            Meeting Location

            Meeting Type

            {{ formatEos(meeting) }}

            Class does not meet

            Days and times to be arranged

            No classroom meeting: online learning

          <template v-else-if="meeting.start_time && meeting.end_time">

              M
              T
              W

              Th

              F
              Sa

              Su

              {{ meeting.start_time.format('h:mm A') }} –
              {{ meeting.end_time.format('h:mm A') }}

                Remote

                Room to be arranged

                <a
                  v-if="locationUrl(meeting)"
                  :href="locationUrl(meeting)"
                  target="_blank"
                  :title="`Map ${meeting.building}`"
                >
                  {{ meeting.building }}

                  {{ meeting.room }}

                  {{ meeting.room }}

          </template>

              {{ meeting.type }}

</template>


add list manipulation animatino

add list manipulation animatino

<template>

Messenger

        connected to {{serverName}}

        <transition name="fade">

            <transition-group name="list">
              <buddy-view v-for="buddy in buddyList" :key="buddy.id" :buddy="buddy" @click.native="buddyList.splice(buddyList.indexOf(buddy), 1)"></buddy-view>
            </transition-group>

        </transition>

        <button @click="centerPageIndex++">change</button>

        2017 © Jack Q

      <transition name="fade">

          <instruction-view></instruction-view>

Audio View

Message View

          <setting-view></setting-view>

      </transition>

</template>

<template>

Messenger

        connected to {{serverName}}

        <transition name="fade">

            <buddy-view v-for="buddy in buddyList" :buddy="buddy" @click.native="buddyList.splice(buddyList.indexOf(buddy), 1)"></buddy-view>

        </transition>

        <button @click="centerPageIndex++">change</button>

        2017 © Jack Q

      <transition name="fade">

          <instruction-view></instruction-view>

Audio View

Message View

          <setting-view></setting-view>

      </transition>

</template>


解决用户编辑看不到按钮问题

解决用户编辑看不到按钮问题

<template>

    <template slot="title">

        {{ title }}

    </template>

        <template v-if="!model.id">

        </template>

              {{ role.roleName }}

上传

            男
            女

</template>


Refactor code and remove console.log

Refactor code and remove console.log

<template>
  <div>
    <userInputs
      v-on:changeViewMode="changeViewMode"
      v-on:execute="executeAction"
    ></userInputs>
    <documents
      :view-mode="viewMode"
      :docs="docs"
    ></documents>
  </div>
</template>

<script>
import fileManager from 'src/logic/filemanager';
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      viewMode: 'listView',
      docs: {}
    };
  },
  watch: {
  },
  methods: {
    changeViewMode(viewMode) {
      this.viewMode = viewMode;
    },
    executeAction(action) {
      if (action === 'createFolder') {
        this.docs.createFolder('Folder');
      } else if (action === 'createFile') {
        this.docs.createFile('File');
      }
    }
  },
  mounted() {
    fileManager
    .start()
    .then((rootFolder) => {
      this.docs = rootFolder;
    })
    .catch((error) => {
      console.log(error);
    });
  }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

</style>

<template>
  <div>
    <userInputs
      v-on:changeViewMode="changeViewMode"
      v-on:execute="executeAction"
    ></userInputs>
    <documents
      :view-mode="viewMode"
      :docs="docs"
    ></documents>
  </div>
</template>

<script>
import fileManager from 'src/logic/filemanager';
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      viewMode: 'listView',
      docs: ''
    };
  },
  watch: {
  },
  methods: {
    changeViewMode(viewMode) {
      this.viewMode = viewMode;
    },
    executeAction(action) {
      if (action === 'createFolder') {
        this.docs.createFolder('Folder');
      } else if (action === 'createFile') {
        this.docs.createFile('File');
      }
    }
  },
  mounted() {
    const startPromise = fileManager.start();

    startPromise
    .then((rootFolder) => {
      console.log(rootFolder);
      this.docs = rootFolder;
    })
    .catch((error) => {
      console.log(error);
    });
  }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

</style>


red color when api is offline

red color when api is offline

<template lang="jade">

span(:style='style') {{updateInfo}}

</template>

<script>
import ms from 'pretty-ms';

export default {
  data() {
    return {
      updateInfo: 'aktualizowanie',
      style: {
        color: '',
      },
    };
  },

  ready() {
    this.$parent.$parent.api.basic
      .on('nextUpdate', time => this.nextUpdate(time));

    this.$parent.$parent.api.basic
      .on('offline', ::this.offline);
  },

  methods: {
    nextUpdate(time) {
      if (time) {
        this.thread ? clearInterval(this.thread) : null;
        this.style.color = '';

        this.thread = setInterval(_ => this.parse(--time), 1000);
      } else this.offline();

      return time;
    },

    offline() {
      clearInterval(this.thread);
      this.updateInfo = 'Stacja jest offline';
      this.style.color = 'red';
    },

    parse(time) {
      if (time == 260) {
        this.updateInfo = `aktualizacja za 4:20`;
        return time;
      }

      this.updateInfo = time > 0
        ? `aktualizacja za ${ ms(time * 1000) }`
        : `aktualizowanie w trakcie ${time ? `(${ms(-time * 1000)})` : ''}`;
    },
  },
};

</script>

<template lang="jade">

span {{updateInfo}}

</template>

<script>
import ms from 'pretty-ms';

export default {
  data() {
    return {
      updateInfo: 'aktualizowanie',
    };
  },

  ready() {
    this.$parent.$parent.api.basic
      .on('nextUpdate', time => this.nextUpdate(time));

    this.$parent.$parent.api.basic
      .on('offline', ::this.offline);
  },

  methods: {
    nextUpdate(time) {
      if (time) {
        this.thread ? clearInterval(this.thread) : null;

        this.thread = setInterval(_ => this.parse(--time), 1000);
      } else this.offline();

      return time;
    },

    offline() {
      clearInterval(this.thread);
      this.updateInfo = 'Stacja jest offline';
    },

    parse(time) {
      if (time == 260) {
        this.updateInfo = `aktualizacja za 4:20`;
        return time;
      }

      this.updateInfo = time > 0
        ? `aktualizacja za ${ ms(time * 1000) }`
        : `aktualizowanie w trakcie ${time ? `(${ms(-time * 1000)})` : ''}`;
    },
  },
};

</script>


Sending array of ids for regions and topics.

<template>
    <v-layout row justify-center>
        <v-dialog v-model="dialog" scrollable persistent max-width="650px">
            <website-preview v-if="showPage === 1" @close-dialog="$emit('close-dialog')" @next="showPage = 2">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </website-preview>
            <commitment-content v-else-if="showPage === 2" @close-dialog="$emit('close-dialog')"
                                @finish="finishCommitmentData" :action-button-text="$t('common:button.next')"
                                :init-commitment="$store.getters['createCommitment/getCommitmentCopy']">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </commitment-content>
            <region v-else-if="showPage === 3" @close-dialog="$emit('close-dialog')" @finish="finishRegion"
                    :action-button-text="$t('common:button.next')"
                    :description="$t('pages:commitment.createDialog.regionDescription')">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </region>
            <topics v-else-if="showPage === 4" @close-dialog="$emit('close-dialog')" @finish="finishTopics"
                    :action-button-text="$t('pages:commitment.createDialog.createCommitmentButton')"
                    :description="$t('pages:commitment.createDialog.topicDescription')" :loading="loading">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </topics>
        </v-dialog>
        <v-snackbar top v-model="showError" color="error" :timeout="0">{{$t("common:error.unknown")}}
            <v-btn dark flat @click="showError = false">{{$t("common:button.close")}}</v-btn>
        </v-snackbar>
    </v-layout>
</template>

<script>
    import WebsitePreview from './WebsitePreview';
    import CommitmentContent from './Commitment';
    import Topics from '~/components/topic/dialog/Topics';
    import Region from '~/components/region/dialog/Region';
    import Stepper from './Stepper';

    export default {
        data() {
            return {dialog: true, showPage: 1, loading: false, showError: false}
        },
        components: {WebsitePreview, CommitmentContent, Topics, Region, Stepper},
        mounted() {
            this.$store.commit('createCommitment/RESET');
        },
        methods: {
            finishCommitmentData({commitment}) {
                this.$store.commit('createCommitment/SET_COMMITMENT', commitment);
                this.showPage = 3;
            },
            finishRegion(regions) {
                this.$store.commit('createCommitment/SET_REGIONS', regions.map(region => region.id));
                this.showPage = 4;
            },
            async finishTopics(topics) {
                try {
                    this.$store.commit('createCommitment/SET_TOPICS', topics.map(topic => topic.id));
                    this.loading = true;
                    let response = await this.$store.dispatch('createCommitment/createCommitment');
                    this.$emit('close-dialog');
                    this.$router.push({
                        name: 'commitment-commitmentId-slug',
                        params: {commitmentId: response.commitmentId, slug: response.slug}
                    });
                }
                catch (e) {
                    this.showError = true;
                } finally {
                    this.loading = false;
                }
            }
        }
    }
</script>

<style lang="scss">

</style>

<template>
    <v-layout row justify-center>
        <v-dialog v-model="dialog" scrollable persistent max-width="650px">
            <website-preview v-if="showPage === 1" @close-dialog="$emit('close-dialog')" @next="showPage = 2">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </website-preview>
            <commitment-content v-else-if="showPage === 2" @close-dialog="$emit('close-dialog')"
                                @finish="finishCommitmentData" :action-button-text="$t('common:button.next')"
                                :init-commitment="$store.getters['createCommitment/getCommitmentCopy']">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </commitment-content>
            <region v-else-if="showPage === 3" @close-dialog="$emit('close-dialog')" @finish="finishRegion"
                    :action-button-text="$t('common:button.next')"
                    :description="$t('pages:commitment.createDialog.regionDescription')">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </region>
            <topics v-else-if="showPage === 4" @close-dialog="$emit('close-dialog')" @finish="finishTopics"
                    :action-button-text="$t('pages:commitment.createDialog.createCommitmentButton')"
                    :description="$t('pages:commitment.createDialog.topicDescription')" :loading="loading">
                <stepper slot="header" :selected-step="showPage"></stepper>
            </topics>
        </v-dialog>
        <v-snackbar top v-model="showError" color="error" :timeout="0">{{$t("common:error.unknown")}}
            <v-btn dark flat @click="showError = false">{{$t("common:button.close")}}</v-btn>
        </v-snackbar>
    </v-layout>
</template>

<script>
    import WebsitePreview from './WebsitePreview';
    import CommitmentContent from './Commitment';
    import Topics from '~/components/topic/dialog/Topics';
    import Region from '~/components/region/dialog/Region';
    import Stepper from './Stepper';

    export default {
        data() {
            return {dialog: true, showPage: 1, loading: false, showError: false}
        },
        components: {WebsitePreview, CommitmentContent, Topics, Region, Stepper},
        mounted() {
            this.$store.commit('createCommitment/RESET');
        },
        methods: {
            finishCommitmentData({commitment}) {
                this.$store.commit('createCommitment/SET_COMMITMENT', commitment);
                this.showPage = 3;
            },
            finishRegion(regions) {
                this.$store.commit('createCommitment/SET_REGIONS', regions);
                this.showPage = 4;
            },
            async finishTopics(topics) {
                try {
                    this.$store.commit('createCommitment/SET_TOPICS', topics);
                    this.loading = true;
                    let response = await this.$store.dispatch('createCommitment/createCommitment');
                    this.$emit('close-dialog');
                    this.$router.push({
                        name: 'commitment-commitmentId-slug',
                        params: {commitmentId: response.commitmentId, slug: response.slug}
                    });
                }
                catch (e) {
                    this.showError = true;
                } finally {
                    this.loading = false;
                }
            }
        }
    }
</script>

<style lang="scss">

</style>


Add number of messages to each tab

Add number of messages to each tab

<template>

    <template v-if="adminMode">

        <sms-actions
          :bus="bus"
          :checked-rows="checkedRows"
          :hidden="{
            'markAsRead': !isInbox,
          }"
          :disabled="{
            'clearChecked': checkedRows.length === 0,
            'delete': checkedRows.length === 0,
            'markAsRead': checkedRows.length === 0,
            'import': true,
          }">
        </sms-actions>

          <sms-box
            :bus="buses[idx]"
            :box-type="tab.boxType"
            @update:checked-rows="val => updateCheckedRows(val, idx)"
          ></sms-box>

        <sms-dialog></sms-dialog>

    </template>
    <template v-else>

          {{ 'sms_admin_only' | $i18n}}

    </template>

</template>


Move notifications above the channel grid

<template>

    <template v-if="isSuperuser">
      <component v-if="pageState.wizardState.shown" :is="wizardComponent"/>

        <task-status
          :type="pageState.taskList[0].type"
          :status="pageState.taskList[0].status"
          :percentage="pageState.taskList[0].percentage"
          :id="pageState.taskList[0].id"
          @importsuccess="notification=notificationTypes.CHANNEL_IMPORT_SUCCESS"
        />

      <notifications
        v-bind="{notification}"
        @dismiss="notification=null"
      />

{{$tr('title')}}

            <icon-button
              :text="$tr('import')"
              class="button"
              @click="startImportWizard"
              :primary="true">
              <mat-svg category="content" name="add"/>
            </icon-button>
            <icon-button
              :text="$tr('export')"
              class="button"
              :primary="true"
              @click="startExportWizard">
              <ion-svg name="ios-upload-outline"/>
            </icon-button>

        <channels-grid
          @deletesuccess="notification=notificationTypes.CHANNEL_DELETE_SUCCESS"
          @deletefailure="notification=notificationTypes.CHANNEL_DELETE_FAILURE"
        />

    </template>
    <auth-message v-else :header="$tr('notAdminHeader')" :details="$tr('notAdminDetails')" />

</template>

<template>

    <template v-if="isSuperuser">
      <component v-if="pageState.wizardState.shown" :is="wizardComponent"/>

        <task-status
          :type="pageState.taskList[0].type"
          :status="pageState.taskList[0].status"
          :percentage="pageState.taskList[0].percentage"
          :id="pageState.taskList[0].id"
          @importsuccess="notification=notificationTypes.CHANNEL_IMPORT_SUCCESS"
        />

{{$tr('title')}}

            <icon-button
              :text="$tr('import')"
              class="button"
              @click="startImportWizard"
              :primary="true">
              <mat-svg category="content" name="add"/>
            </icon-button>
            <icon-button
              :text="$tr('export')"
              class="button"
              :primary="true"
              @click="startExportWizard">
              <ion-svg name="ios-upload-outline"/>
            </icon-button>

        <channels-grid
          @deletesuccess="notification=notificationTypes.CHANNEL_DELETE_SUCCESS"
          @deletefailure="notification=notificationTypes.CHANNEL_DELETE_FAILURE"
        />

        <notifications
          v-bind="{notification}"
          @dismiss="notification=null"
        />

    </template>
    <auth-message v-else :header="$tr('notAdminHeader')" :details="$tr('notAdminDetails')" />

</template>


extended the width of the user to 100% so they display as a column

<template>

                {{this.$route.params.community}}

                edit

                 --

              <drop-down
              v-for="user in this.$store.state.users"
              :key="user.username"
              v-if="user.status.isAdmin"
              :class="{admin: user.status.isAdmin}"
              :user="user.username"/>

              <drop-down
              v-for="user in this.$store.state.users"
              :key="user.username"
              v-if="!user.status.isAdmin"
              :class="{user: !user.status.isAdmin}"
              :user="user.username"/>

                Connect

                Disconnect

              SEND

</template>


Linted

Linted

<template>
  <v-menu
    v-model="showEntryInfo"
    :activator="selectedElement"
    :open-on-click="false"
    :close-on-click="false"
    :close-on-content-click="false"
    offset-y>
    <v-card v-if="scheduleEntry">
      <v-form>
        <v-card-text>
          <e-text-field v-model="scheduleEntry.activity().title" no-label
                        class="font-weight-bold" placeholder="Aktivitätsname"
                        hide-details="auto" />
          <v-row no-gutters class="my-4">
            <e-time-picker label="Start"
                           :icon="null" class="flex-full"
                           :value="toTimeString(scheduleEntry.startTime)" />
            <e-time-picker width="100" label="Ende"
                           :icon="null" class="flex-full mt-0"
                           :value="toTimeString(scheduleEntry.endTime)" />
          </v-row>
          <v-row no-gutters class="my-4" style="gap: 12px">
            <e-select v-model="scheduleEntry.activity().activityCategory" label="Aktivitätstyp"
                      :items="activityCategories.items" item-value="id"
                      :return-object="true" required
                      item-text="name">
              <template #item="{item, on, attrs}">
                <v-list-item :key="item.id" v-bind="attrs" v-on="on">
                  <v-list-item-avatar>
                    <v-chip :color="item.color">{{ item.short }}</v-chip>
                  </v-list-item-avatar>
                  <v-list-item-content>
                    {{ item.name }}
                  </v-list-item-content>
                </v-list-item>
              </template>
              <template #selection="{item}">

                    {{ item.name }}

                  <v-chip x-small :color="item.color">{{ item.short }}</v-chip>

              </template>
            </e-select>
            <e-text-field v-model="scheduleEntry.activity().location" :label="$tc('entity.activity.fields.location')"
                          hide-details="auto"
                          class="mt-0" />
          </v-row>
          <e-select label="Verantwortliche Leitende" multiple
                    chips deletable-chips
                    :items="[{text:'Leitende1'},{text:'Leitende2'}]" />
        </v-card-text>
        <v-card-actions class="px-4 pb-4 flex-wrap">
          <v-alert
            v-if="createdError"
            dense
            class="w-100"
            border="left"
            outlined
            type="error">
            {{ createdError }}
          </v-alert>
          <v-btn v-if="!scheduleEntry.tmpEvent"
                 color="primary" :to="scheduleEntryRoute(scheduleEntry.activity().camp(), scheduleEntry)">
            {{ $tc('global.button.open') }}
          </v-btn>
          <v-btn text color="secondary"
                 class="ml-auto"
                 @click="cancel">
            {{ $tc('global.button.cancel') }}
          </v-btn>
          <v-btn v-if="scheduleEntry.tmpEvent" color="success" @click="create">
            <v-icon v-if="isPersisting" class="mdi-spin">mdi-loading</v-icon>
            {{ createdError ? $tc('global.button.tryagain') : $tc('global.button.create') }}
          </v-btn>
          <v-btn v-else color="success" @click="save">Speichern</v-btn>
        </v-card-actions>
      </v-form>
    </v-card>
  </v-menu>
</template>


报表地址优化

报表地址优化

<template>
    <iframe :id="id" :src="url" frameborder="0" width="100%" :height="height" scrolling="auto"></iframe>
</template>

<script>
  import Vue from 'vue'
  import { ACCESS_TOKEN } from "@/store/mutation-types"
  import {mixinDevice} from '@/utils/mixin.js'

  export default {
    name: "IframePageContent",
    inject:['closeCurrent'],
    mixins: [mixinDevice],
    data () {
      return {
        url: "",
        id:"",
        height: ""
      }
    },
    created () {
      this.goUrl()
    },
    updated () {
      this.goUrl()
    },
    watch: {
      $route(to, from) {
        this.goUrl();
      }
    },
    methods: {
      goUrl () {
        let url = this.$route.meta.url
        this.id = this.$route.path
        if (this.isMobile()) {
          this.height = 800
        } else {
          this.height = document.documentElement.clientHeight-130
        }
        console.log("------url------"+url)
        console.log("------token------"+Vue.ls.get(ACCESS_TOKEN))
        if (url !== null && url !== undefined) {
          //外部url加入token
          let token = Vue.ls.get(ACCESS_TOKEN);
          if(url) {
            url = url.replace('/system','')
            url = document.location.protocol + '//' + window.location.host + url
          }
          this.url = url + '?token=' + token;
        }
      }
    }
  }
</script>

<style>
</style>

<template>
    <iframe :id="id" :src="url" frameborder="0" width="100%" :height="height" scrolling="auto"></iframe>
</template>

<script>
  import Vue from 'vue'
  import { ACCESS_TOKEN } from "@/store/mutation-types"
  import {mixinDevice} from '@/utils/mixin.js'

  export default {
    name: "IframePageContent",
    inject:['closeCurrent'],
    mixins: [mixinDevice],
    data () {
      return {
        url: "",
        id:"",
        height: ""
      }
    },
    created () {
      this.goUrl()
    },
    updated () {
      this.goUrl()
    },
    watch: {
      $route(to, from) {
        this.goUrl();
      }
    },
    methods: {
      goUrl () {
        let url = this.$route.meta.url
        this.id = this.$route.path
        if (this.isMobile()) {
          this.height = 800
        } else {
          this.height = document.documentElement.clientHeight-130
        }
        console.log("------url------"+url)
        console.log("------token------"+Vue.ls.get(ACCESS_TOKEN))
        if (url !== null && url !== undefined) {
          //外部url加入token
          let token = Vue.ls.get(ACCESS_TOKEN);
          if(url) {
            url = url.replace('/report','')
            url = 'http://' + window.location.host + url
          }
          this.url = url + '?token=' + token;
        }
      }
    }
  }
</script>

<style>
</style>


Add insufficientQuantity

Add insufficientQuantity

18 - Project Stock Trader/src/components/portfolio/Stock.vue


Hide comment count

Hide comment count

It requires more work to be completed.

<template>

      {{ playlist.name }}

      <combobox-styled
        :options="forClientOptions"
        v-model="forClient"
      />

    <button-simple
      @click="showEditModal"
      class="edit-button playlist-button flexrow-item"
      icon="edit"
      v-if="isCurrentUserManager"
    />
    <button-simple
      @click="showDeleteModal"
      class="delete-button playlist-button flexrow-item"
      icon="delete"
      v-if="isCurrentUserManager"
    />

    <raw-video-player
      class="raw-player"
      ref="raw-player"
      :shots="shotList"
      @shot-change="onShotChange"
      @time-update="onTimeUpdate"
      @max-duration-update="onMaxDurationUpdate"
    />
    <raw-video-player
      class="raw-player"
      ref="raw-player-comparison"
      :shots="shotListToCompare"
      v-if="isComparing"
    />

      <canvas
        id="annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
      >
      </canvas>

    <task-info
      ref="task-info"
      :class="{
        'flexrow-item': true,
        'task-info-column': true,
        'hidden': isCommentsHidden
      }"
      :task="task"
      :is-preview="false"
    />

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressBarClicked"
      >

      </progress>

  <annotation-bar
    class="playlist-annotations"
    ref="playlist-annotation"
    :annotations="annotations"
    :max-duration-raw="maxDurationRaw"
    @select-annotation="loadAnnotation"
    v-if="playlist.id"
  />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayClicked"
      icon="play"
      v-if="!isPlaying"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayClicked"
      icon="pause"
      v-else
    />

      {{ currentTime }}

    /

      {{ maxDuration }}

      ({{ currentFrame }})

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPreviousFrameClicked"
      icon="left"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onNextFrameClicked"
      icon="right"
    />

      {{ shotList.length > 0 ? playingShotIndex + 1 : 0 }}

    /

      {{ shotList.length }}

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayPreviousShotClicked"
      icon="back"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onPlayNextShotClicked"
      icon="forward"
    />
    <button-simple
      :class="{
        'comparison-button': true,
        'flexrow-item': true,
        'playlist-button': true,
        active: isComparing
      }"
      icon="compare"
      @click="onCompareClicked"
      v-if="taskTypeOptions"
    />
    <combobox
      class="playlist-button flexrow-item comparison-list"
      :options="taskTypeOptions"
      v-model="taskTypeToCompare"
      v-if="isComparing"
    />

    <button-simple
      :class="{
        'playlist-button': true,
        'flexrow-item': true,
        active: isDrawing
      }"
      @click="onAnnotateClicked"
      icon="pencil"
    />
    <button-simple
      class="playlist-button flexrow-item"
      icon="remove"
      @click="onDeleteClicked"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onCommentClicked"
      icon="comment"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onFilmClicked"
      icon="film"
    />

        {{ $t('playlists.download_zip') }}

        {{ $t('playlists.build_mp4') }}

        {{ $t('playlists.no_build') }}

            {{ $t('playlists.available_build') }}

              {{ formatDate(job.created_at) }}

            <spinner
              class="build-spinner"
              v-if="job.status === 'running'"
            />
            <button
              class="delete-job-button"
              @click="onRemoveBuildJob(job)"
              v-else
            >
              x
            </button>

      <button-simple
        class="playlist-button"
        icon="download"
        @click="toggleDlButtons"
      />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onFullscreenClicked"
      icon="maximize"
      v-if="isFullScreenEnabled"
    />

    <spinner class="spinner" v-if="isLoading" />

      <playlisted-shot
        :ref="'shot-' + index"
        :index="index"
        :shot="shot"
        :is-playing="playingShotIndex === index"
        @play-click="playShot"
        @remove-shot="removeShot"
        @preview-changed="onPreviewChanged"
        @shot-dropped="onShotDropped"
      />

  <edit-playlist-modal
    :active="modals.edit"
    :is-loading="loading.editPlaylist"
    :is-error="errors.editError"
    :playlist-to-edit="playlist"
    @cancel="hideEditModal"
    @confirm="confirmEditPlaylist"
  />

  <delete-modal
    :active="modals.delete"
    :is-loading="loading.deletePlaylist"
    :is-error="errors.deletePlaylist"
    :text="deleteText"
    :error-text="$t('playlists.delete_error')"
    @confirm="confirmRemovePlaylist"
    @cancel="hideDeleteModal"
  />

</template>

<template>

      {{ playlist.name }}

      <combobox-styled
        :options="forClientOptions"
        v-model="forClient"
      />

    <button-simple
      @click="showEditModal"
      class="edit-button playlist-button flexrow-item"
      icon="edit"
      v-if="isCurrentUserManager"
    />
    <button-simple
      @click="showDeleteModal"
      class="delete-button playlist-button flexrow-item"
      icon="delete"
      v-if="isCurrentUserManager"
    />

      {{ nbCommentedShots }}
      /
      {{ shotList.length }}

 0"
    >

    <raw-video-player
      class="raw-player"
      ref="raw-player"
      :shots="shotList"
      @shot-change="onShotChange"
      @time-update="onTimeUpdate"
      @max-duration-update="onMaxDurationUpdate"
    />
    <raw-video-player
      class="raw-player"
      ref="raw-player-comparison"
      :shots="shotListToCompare"
      v-if="isComparing"
    />

      <canvas
        id="annotation-canvas"
        ref="annotation-canvas"
        class="canvas"
      >
      </canvas>

    <task-info
      ref="task-info"
      :class="{
        'flexrow-item': true,
        'task-info-column': true,
        'hidden': isCommentsHidden
      }"
      :task="task"
      :is-preview="false"
    />

      <progress
        ref="progress"
        value="0"
        min="0"
        @click="onProgressBarClicked"
      >

      </progress>

  <annotation-bar
    class="playlist-annotations"
    ref="playlist-annotation"
    :annotations="annotations"
    :max-duration-raw="maxDurationRaw"
    @select-annotation="loadAnnotation"
    v-if="playlist.id"
  />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayClicked"
      icon="play"
      v-if="!isPlaying"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayClicked"
      icon="pause"
      v-else
    />

      {{ currentTime }}

    /

      {{ maxDuration }}

      ({{ currentFrame }})

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPreviousFrameClicked"
      icon="left"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onNextFrameClicked"
      icon="right"
    />

      {{ shotList.length > 0 ? playingShotIndex + 1 : 0 }}

    /

      {{ shotList.length }}

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayPreviousShotClicked"
      icon="back"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onPlayNextShotClicked"
      icon="forward"
    />
    <button-simple
      :class="{
        'comparison-button': true,
        'flexrow-item': true,
        'playlist-button': true,
        active: isComparing
      }"
      icon="compare"
      @click="onCompareClicked"
      v-if="taskTypeOptions"
    />
    <combobox
      class="playlist-button flexrow-item comparison-list"
      :options="taskTypeOptions"
      v-model="taskTypeToCompare"
      v-if="isComparing"
    />

    <button-simple
      :class="{
        'playlist-button': true,
        'flexrow-item': true,
        active: isDrawing
      }"
      @click="onAnnotateClicked"
      icon="pencil"
    />
    <button-simple
      class="playlist-button flexrow-item"
      icon="remove"
      @click="onDeleteClicked"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      @click="onCommentClicked"
      icon="comment"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onFilmClicked"
      icon="film"
    />

        {{ $t('playlists.download_zip') }}

        {{ $t('playlists.build_mp4') }}

        {{ $t('playlists.no_build') }}

            {{ $t('playlists.available_build') }}

              {{ formatDate(job.created_at) }}

            <spinner
              class="build-spinner"
              v-if="job.status === 'running'"
            />
            <button
              class="delete-job-button"
              @click="onRemoveBuildJob(job)"
              v-else
            >
              x
            </button>

      <button-simple
        class="playlist-button"
        icon="download"
        @click="toggleDlButtons"
      />

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onFullscreenClicked"
      icon="maximize"
      v-if="isFullScreenEnabled"
    />

    <spinner class="spinner" v-if="isLoading" />

      <playlisted-shot
        :ref="'shot-' + index"
        :index="index"
        :shot="shot"
        :is-playing="playingShotIndex === index"
        @play-click="playShot"
        @remove-shot="removeShot"
        @preview-changed="onPreviewChanged"
        @shot-dropped="onShotDropped"
      />

  <edit-playlist-modal
    :active="modals.edit"
    :is-loading="loading.editPlaylist"
    :is-error="errors.editError"
    :playlist-to-edit="playlist"
    @cancel="hideEditModal"
    @confirm="confirmEditPlaylist"
  />

  <delete-modal
    :active="modals.delete"
    :is-loading="loading.deletePlaylist"
    :is-error="errors.deletePlaylist"
    :text="deleteText"
    :error-text="$t('playlists.delete_error')"
    @confirm="confirmRemovePlaylist"
    @cancel="hideDeleteModal"
  />

</template>


Fixed runtime error due to LoadingComponent mismatch

<template>

		<LoadingComponent :loading="loading"></LoadingComponent>

			<NavBarComponent></NavBarComponent>
			<md-content>
				<MapComponent @mapLoaded="onMapLoaded"></MapComponent>
			</md-content>

</template>

<template>

		<Loading :loading="loading"></Loading>

			<NavBarComponent></NavBarComponent>
			<md-content>
				<MapComponent @mapLoaded="onMapLoaded"></MapComponent>
			</md-content>

</template>


Missing prop typing in TypeCell

Missing prop typing in TypeCell

<!-- Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
  <td>
    <v-chip class="ma-2" color="white" label>
      <v-icon left>{{ iconName() }}</v-icon>
      {{ recommenderSubtype }}
    </v-chip>
  </td>
</template>
<script lang="ts">
import Vue, { PropType } from "vue";
import { Component } from "vue-property-decorator";
import { RecommendationExtra } from "../store/model";

const TypeCellProps = Vue.extend({
  props: {
    rowRecommendation: {
      type: Object as PropType<RecommendationExtra>,
      required: true
    }
  }
});

@Component
export default class TypeCell extends TypeCellProps {
  get recommenderSubtype(): string {
    return this.rowRecommendation.typeCol;
  }
  iconName(): string {
    switch (this.recommenderSubtype) {
      case "CHANGE_MACHINE_TYPE":
        return "mdi-move-resize-variant";
      case "STOP_VM":
        return "mdi-monitor-off";
      case "INCREASE_PERFORMANCE":
        return "mdi-monitor-screenshot";
      case "SNAPSHOT_AND_DELETE_DISK":
        return "mdi-harddisk-remove";
      default:
        return "mdi-cloud-question";
    }
  }
}
</script>

<!-- Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
  <td>
    <v-chip class="ma-2" color="white" label>
      <v-icon left>{{ iconName() }}</v-icon>
      {{ recommenderSubtype() }}
    </v-chip>
  </td>
</template>
<script lang="ts">
import Vue from "vue";
import { Component } from "vue-property-decorator";
import { getRecommendationType } from "../store/model";

const TypeCellProps = Vue.extend({
  props: ["rowRecommendation"]
});

@Component
export default class TypeCell extends TypeCellProps {
  recommenderSubtype(): string {
    return getRecommendationType(this.rowRecommendation);
  }
  iconName(): string {
    switch (this.recommenderSubtype()) {
      case "CHANGE_MACHINE_TYPE":
        return "mdi-move-resize-variant";
      case "STOP_VM":
        return "mdi-monitor-off";
      case "INCREASE_PERFORMANCE":
        return "mdi-monitor-screenshot";
      case "SNAPSHOT_AND_DELETE_DISK":
        return "mdi-harddisk-remove";
      default:
        return "mdi-cloud-question";
    }
  }
}
</script>


Encapsulate USSD data, clear USSD content on command change

<template>

  <form v-on:submit.prevent v-if="$adminMode">

     0" label="USSD Presets">

        <option value="">--</option>
        <option
          v-for="command in ussd.commands"
          :value="command.Command"
          :key="command.Command"
          >{{ command.Name }}</option>

    {{ error }}

 0 || ussdOptionsExist">

{{ ussd.result }}

      <template v-for="(option, key) in ussd.options">

            {{ key + '. ' + option }}

      </template>

    <button @click="send" class="button is-primary" :class="{'is-loading': loading}">Send</button>
  </form>
  <template v-else>

      You must be in administrator mode to access USSD services

  </template>

</template>

<template>

  <form v-on:submit.prevent v-if="$adminMode">

     0">

        <option
          v-for="command in ussdCommands"
          :value="command.Command"
          :key="command.Command"
          >{{ command.Name }}</option>

    {{ error }}

 0 || Object.keys(ussdOptions).length > 0">

{{ ussdResult }}

      <template v-for="(option, key) in ussdOptions">

            {{ key + '. ' + option }}

      </template>

    <button @click="send" class="button is-primary" :class="{'is-loading': loading}">Send</button>
  </form>
  <template v-else>

      You must be in administrator mode to access USSD services

  </template>

</template>


Fix for vue: Button "Next" and "Complete" doesn't work if end-user has fixed an error in the the focused question #2267

<template>
  <div :class="css.footer">
    <input
      type="button"
      :value="survey.pagePrevText"
      v-show="!survey.isFirstPage && survey.isShowPrevButton"
      :class="survey.cssNavigationPrev"
      @mousedown="buttonMouseDown"
      @click="prevPage"
    />
    <input
      type="button"
      :value="survey.pageNextText"
      v-show="!survey.isLastPage"
      :class="survey.cssNavigationNext"
      @mousedown="nextButtonMouseDown"
      @click="nextPage"
    />
    <input
      v-if="survey.isPreviewButtonVisible"
      type="button"
      :value="survey.previewText"
      v-show="survey.isLastPage"
      :class="survey.cssNavigationPreview"
      @mousedown="buttonMouseDown"
      @click="showPreview"
    />
    <input
      v-if="survey.isCompleteButtonVisible"
      type="button"
      :value="survey.completeText"
      v-show="survey.isLastPage"
      :class="survey.cssNavigationComplete"
      @mousedown="buttonMouseDown"
      @click="completeLastPage"
    />
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";
import { SurveyModel } from "../survey";
import { PageModel } from "../page";

@Component
export class Navigation extends Vue {
  private mouseDownPage: PageModel;
  @Prop survey: SurveyModel;
  @Prop css: any;
  nextButtonMouseDown() {
    this.mouseDownPage = this.survey.currentPage;
    return this.survey.navigationMouseDown();
  }
  buttonMouseDown() {
    return this.survey.navigationMouseDown();
  }
  prevPage() {
    this.survey.prevPage();
  }
  nextPage() {
    if (!!this.mouseDownPage && this.mouseDownPage !== this.survey.currentPage)
      return;
    this.mouseDownPage = null;
    this.survey.nextPage();
  }
  completeLastPage() {
    this.survey.completeLastPage();
  }
  showPreview() {
    this.survey.showPreview();
  }
}
Vue.component("survey-navigation", Navigation);
export default Navigation;
</script>

<template>
  <div :class="css.footer">
    <input
      type="button"
      :value="survey.pagePrevText"
      v-show="!survey.isFirstPage && survey.isShowPrevButton"
      :class="survey.cssNavigationPrev"
      @click="prevPage"
    />
    <input
      type="button"
      :value="survey.pageNextText"
      v-show="!survey.isLastPage"
      :class="survey.cssNavigationNext"
      @click="nextPage"
    />
    <input
      v-if="survey.isPreviewButtonVisible"
      type="button"
      :value="survey.previewText"
      v-show="survey.isLastPage"
      :class="survey.cssNavigationPreview"
      @click="showPreview"
    />
    <input
      v-if="survey.isCompleteButtonVisible"
      type="button"
      :value="survey.completeText"
      v-show="survey.isLastPage"
      :class="survey.cssNavigationComplete"
      @click="completeLastPage"
    />
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";
import { SurveyModel } from "../survey";

@Component
export class Navigation extends Vue {
  @Prop survey: SurveyModel;
  @Prop css: any;
  prevPage() {
    this.survey.prevPage();
  }
  nextPage() {
    this.survey.nextPage();
  }
  completeLastPage() {
    this.survey.completeLastPage();
  }
  showPreview() {
    this.survey.showPreview();
  }
}
Vue.component("survey-navigation", Navigation);
export default Navigation;
</script>


feat(docs): Fix rotation of Quasar logo button (#3176)

<template lang="pug">
q-layout.doc-layout(view="hHh LpR lff", @scroll="onScroll")
  q-header.header(elevated)
    q-toolbar
      q-btn.q-mr-sm(flat, dense, round, @click="leftDrawerState = !leftDrawerState", aria-label="Menu")
        q-icon(name="menu")

      q-btn.quasar-logo.text-bold(key="logo", flat, no-caps, no-wrap, stretch, to="/")
        q-avatar
          img(src="https://cdn.quasar-framework.org/img/quasar-logo.png")
        q-toolbar-title(shrink) Quasar

      q-space

      header-menu.self-stretch.row.no-wrap(v-if="$q.screen.gt.xs")

      q-btn.q-ml-xs(flat, dense, round, @click="rightDrawerState = !rightDrawerState", aria-label="Menu")
        q-icon(name="assignment")

  q-footer.text-white.text-center.footer
    div.footer__icons.row.flex-center
      a(href="https://github.com/quasarframework/quasar", target="_blank")
        q-icon(name="fab fa-github")

      a(href="https://twitter.com/quasarframework", target="_blank")
        q-icon(name="fab fa-twitter")

      a(href="https://medium.com/quasar-framework", target="_blank")
        q-icon(name="fab fa-medium")

      a(href="https://discord.gg/5TDhbDg", target="_blank")
        q-icon(name="fab fa-discord")

      a(href="https://forum.quasar-framework.org/", target="_blank")
        q-icon(name="fas fa-comments")
    div
      | Released under the <doc-link to="https://github.com/quasarframework/quasar/blob/dev/LICENSE">MIT LICENSE</doc-link>

    div Copyright © 2015 - {{ year }} PULSARDEV SRL, Razvan Stoenescu

  q-drawer(
    v-model="leftDrawerState"
    bordered
    show-if-above
  )
    q-scroll-area.fit
      .flex.justify-center
        q-btn.q-mt-lg(
          type="a"
          href="https://www.patreon.com/quasarframework"
          target="_blank"
          size="13px"
          color="red"
          icon="fab fa-patreon"
          label="Become a Patron"
        )

        q-input.q-my-lg(v-model="search", filled, dense)
          template(v-slot:append)
            q-icon(name="search")

      app-menu.q-mb-lg

  q-drawer(
    v-model="rightDrawerState"
    side="right"
    content-class="bg-grey-2"
    :width="180"
    show-if-above
    @on-layout="updateRightDrawerOnLayout"
  )
    q-scroll-area.fit
      header-menu.q-mt-sm.text-primary.column(v-if="$q.screen.lt.sm", align="right")

      q-list.doc-toc.q-my-lg.text-grey-8
        q-item(
          v-for="toc in $store.state.toc",
          :key="toc.id",
          clickable,
          v-ripple,
          dense,
          @click="scrollTo(toc.id)",
          :active="activeToc === toc.id"
        )
          q-item-section(v-if="toc.sub === true", side) •
          q-item-section {{ toc.title }}

  q-page-container
    transition(
      enter-active-class="animated fadeIn"
      leave-active-class="animated fadeOut"
      mode="out-in"
      :duration="200"
      @leave="resetScroll"
    )
      router-view

  q-page-scroller
    q-btn(fab, color="red", icon="keyboard_arrow_up")
</template>

<script>
import { scroll } from 'quasar'
import AppMenu from 'components/AppMenu'
import HeaderMenu from 'components/HeaderMenu'

export default {
  name: 'Layout',

  meta: {
    title: 'Welcome!',
    titleTemplate: title => `${title} | Quasar Framework`
  },

  components: {
    AppMenu,
    HeaderMenu
  },

  watch: {
    $route () {
      this.leftDrawerState = true
      this.rightDrawerState = true
      this.$nextTick(() => {
        this.updateActiveToc(document.documentElement.scrollTop || document.body.scrollTop)
      })
    }
  },

  data () {
    return {
      year: (new Date()).getFullYear(),
      search: '',
      rightDrawerOnLayout: false,
      activeToc: void 0
    }
  },

  computed: {
    leftDrawerState: {
      get () {
        return this.$store.state.leftDrawerState
      },
      set (val) {
        this.$store.commit('updateLeftDrawerState', val)
      }
    },

    rightDrawerState: {
      get () {
        return this.$store.state.rightDrawerState
      },
      set (val) {
        this.$store.commit('updateRightDrawerState', val)
      }
    }
  },

  methods: {
    resetScroll (el, done) {
      document.documentElement.scrollTop = 0
      document.body.scrollTop = 0
      done()
    },

    scrollTo (id) {
      const el = document.getElementById(id)
      clearTimeout(this.scrollTimer)

      if (el) {
        if (this.rightDrawerOnLayout !== true) {
          this.rightDrawerState = false
          this.scrollTimer = setTimeout(() => {
            this.scrollPage(el)
          }, 300)
        }
        else {
          this.scrollPage(el)
        }
      }
    },

    scrollPage (el) {
      const
        target = scroll.getScrollTarget(el),
        offset = el.offsetTop - el.scrollHeight

      this.scrollingPage = true
      this.scrollTimer = setTimeout(() => {
        this.scrollingPage = false
      }, 510)
      scroll.setScrollPosition(target, offset, 500)
    },

    updateRightDrawerOnLayout (state) {
      this.rightDrawerOnLayout = state
    },

    onScroll ({ position }) {
      if (this.scrollingPage !== true) {
        this.updateActiveToc(position)
      }
    },

    updateActiveToc (position) {
      const toc = this.$store.state.toc
      let last

      for (let i in toc) {
        const section = toc[i]
        const item = document.getElementById(section.id)

        if (item === null) {
          continue
        }

        if (item.offsetTop >= position + 155) {
          if (last === void 0) {
            last = section.id
          }
          break
        }
        else {
          last = section.id
        }
      }

      if (last !== void 0) {
        this.activeToc = last
      }
    }
  },

  beforeDestroy () {
    clearTimeout(this.scrollTimer)
  }
}
</script>

<style lang="stylus">
@import '~quasar-variables'

.header, .footer
  background linear-gradient(145deg, $primary 11%, $dark-primary 45%)

.header-logo
  width 25px
  height 25px

.footer
  font-size 11px
  padding 16px 0

  &__icons
    font-size 28px
    a
      margin 0 8px 8px
      text-decoration none
      outline 0
      color inherit
    .q-icon:hover
      color lighten($primary, 50%)
  .doc-link
    color inherit

.q-drawer--standard .doc-toc
  .q-item
    border-radius 5px 0 0 5px
    border-left 3px solid transparent
  .q-item--active
    border-left 3px solid $primary
.q-drawer--mobile .doc-toc
  .q-item--active
    font-weight 600

.quasar-logo
  img
    transform rotate(0deg)
    transition transform .8s ease-in-out
  &:hover img
    transform rotate(-360deg)
</style>

<template lang="pug">
q-layout.doc-layout(view="hHh LpR lff", @scroll="onScroll")
  q-header.header(elevated)
    q-toolbar
      q-btn.q-mr-sm(flat, dense, round, @click="leftDrawerState = !leftDrawerState", aria-label="Menu")
        q-icon(name="menu")

      q-btn.quasar-logo.text-bold(key="logo", flat, no-caps, no-wrap, stretch, to="/")
        q-avatar
          img(src="https://cdn.quasar-framework.org/img/quasar-logo.png")
        q-toolbar-title(shrink) Quasar

      q-space

      header-menu.self-stretch.row.no-wrap(v-if="$q.screen.gt.xs")

      q-btn.q-ml-xs(flat, dense, round, @click="rightDrawerState = !rightDrawerState", aria-label="Menu")
        q-icon(name="assignment")

  q-footer.text-white.text-center.footer
    div.footer__icons.row.flex-center
      a(href="https://github.com/quasarframework/quasar", target="_blank")
        q-icon(name="fab fa-github")

      a(href="https://twitter.com/quasarframework", target="_blank")
        q-icon(name="fab fa-twitter")

      a(href="https://medium.com/quasar-framework", target="_blank")
        q-icon(name="fab fa-medium")

      a(href="https://discord.gg/5TDhbDg", target="_blank")
        q-icon(name="fab fa-discord")

      a(href="https://forum.quasar-framework.org/", target="_blank")
        q-icon(name="fas fa-comments")
    div
      | Released under the <doc-link to="https://github.com/quasarframework/quasar/blob/dev/LICENSE">MIT LICENSE</doc-link>

    div Copyright © 2015 - {{ year }} PULSARDEV SRL, Razvan Stoenescu

  q-drawer(
    v-model="leftDrawerState"
    bordered
    show-if-above
  )
    q-scroll-area.fit
      .flex.justify-center
        q-btn.q-mt-lg(
          type="a"
          href="https://www.patreon.com/quasarframework"
          target="_blank"
          size="13px"
          color="red"
          icon="fab fa-patreon"
          label="Become a Patron"
        )

        q-input.q-my-lg(v-model="search", filled, dense)
          template(v-slot:append)
            q-icon(name="search")

      app-menu.q-mb-lg

  q-drawer(
    v-model="rightDrawerState"
    side="right"
    content-class="bg-grey-2"
    :width="180"
    show-if-above
    @on-layout="updateRightDrawerOnLayout"
  )
    q-scroll-area.fit
      header-menu.q-mt-sm.text-primary.column(v-if="$q.screen.lt.sm", align="right")

      q-list.doc-toc.q-my-lg.text-grey-8
        q-item(
          v-for="toc in $store.state.toc",
          :key="toc.id",
          clickable,
          v-ripple,
          dense,
          @click="scrollTo(toc.id)",
          :active="activeToc === toc.id"
        )
          q-item-section(v-if="toc.sub === true", side) •
          q-item-section {{ toc.title }}

  q-page-container
    transition(
      enter-active-class="animated fadeIn"
      leave-active-class="animated fadeOut"
      mode="out-in"
      :duration="200"
      @leave="resetScroll"
    )
      router-view

  q-page-scroller
    q-btn(fab, color="red", icon="keyboard_arrow_up")
</template>

<script>
import { scroll } from 'quasar'
import AppMenu from 'components/AppMenu'
import HeaderMenu from 'components/HeaderMenu'

export default {
  name: 'Layout',

  meta: {
    title: 'Welcome!',
    titleTemplate: title => `${title} | Quasar Framework`
  },

  components: {
    AppMenu,
    HeaderMenu
  },

  watch: {
    $route () {
      this.leftDrawerState = true
      this.rightDrawerState = true
      this.$nextTick(() => {
        this.updateActiveToc(document.documentElement.scrollTop || document.body.scrollTop)
      })
    }
  },

  data () {
    return {
      year: (new Date()).getFullYear(),
      search: '',
      rightDrawerOnLayout: false,
      activeToc: void 0
    }
  },

  computed: {
    leftDrawerState: {
      get () {
        return this.$store.state.leftDrawerState
      },
      set (val) {
        this.$store.commit('updateLeftDrawerState', val)
      }
    },

    rightDrawerState: {
      get () {
        return this.$store.state.rightDrawerState
      },
      set (val) {
        this.$store.commit('updateRightDrawerState', val)
      }
    }
  },

  methods: {
    resetScroll (el, done) {
      document.documentElement.scrollTop = 0
      document.body.scrollTop = 0
      done()
    },

    scrollTo (id) {
      const el = document.getElementById(id)
      clearTimeout(this.scrollTimer)

      if (el) {
        if (this.rightDrawerOnLayout !== true) {
          this.rightDrawerState = false
          this.scrollTimer = setTimeout(() => {
            this.scrollPage(el)
          }, 300)
        }
        else {
          this.scrollPage(el)
        }
      }
    },

    scrollPage (el) {
      const
        target = scroll.getScrollTarget(el),
        offset = el.offsetTop - el.scrollHeight

      this.scrollingPage = true
      this.scrollTimer = setTimeout(() => {
        this.scrollingPage = false
      }, 510)
      scroll.setScrollPosition(target, offset, 500)
    },

    updateRightDrawerOnLayout (state) {
      this.rightDrawerOnLayout = state
    },

    onScroll ({ position }) {
      if (this.scrollingPage !== true) {
        this.updateActiveToc(position)
      }
    },

    updateActiveToc (position) {
      const toc = this.$store.state.toc
      let last

      for (let i in toc) {
        const section = toc[i]
        const item = document.getElementById(section.id)

        if (item === null) {
          continue
        }

        if (item.offsetTop >= position + 155) {
          if (last === void 0) {
            last = section.id
          }
          break
        }
        else {
          last = section.id
        }
      }

      if (last !== void 0) {
        this.activeToc = last
      }
    }
  },

  beforeDestroy () {
    clearTimeout(this.scrollTimer)
  }
}
</script>

<style lang="stylus">
@import '~quasar-variables'

.header, .footer
  background linear-gradient(145deg, $primary 11%, $dark-primary 45%)

.header-logo
  width 25px
  height 25px

.footer
  font-size 11px
  padding 16px 0

  &__icons
    font-size 28px
    a
      margin 0 8px 8px
      text-decoration none
      outline 0
      color inherit
    .q-icon:hover
      color lighten($primary, 50%)
  .doc-link
    color inherit

.q-drawer--standard .doc-toc
  .q-item
    border-radius 5px 0 0 5px
    border-left 3px solid transparent
  .q-item--active
    border-left 3px solid $primary
.q-drawer--mobile .doc-toc
  .q-item--active
    font-weight 600

.quasar-logo
  img
    transition transform .8s ease-in-out
  &:hover img
    transform rotate(360deg)
</style>


fix(fab): Only destroy ripple if not undefined


Hide Browse and Create a document button if the collection is realtime only

<template>

        <router-link :to="{name: 'DataDocumentsList', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Browse
        </router-link>

        <router-link href="#!" :to="{name: 'DataCollectionWatch', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Watch
        </router-link>

        <router-link v-if="canCreateDocument($store.state.route.params.index, $store.state.route.params.collection) && !$store.state.collection.isRealtimeOnly"
          :to="{name: 'DataCreateDocument', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Create a document
        </router-link>

          Create a document

</template>

<template>

        <router-link :to="{name: 'DataDocumentsList', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Browse
        </router-link>

        <router-link href="#!" :to="{name: 'DataCollectionWatch', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Watch
        </router-link>

        <router-link v-if="canCreateDocument($store.state.route.params.index, $store.state.route.params.collection)"
          :to="{name: 'DataCreateDocument', params: {index: $store.state.route.params.index, collection: $store.state.route.params.collection}}">
          Create a document
        </router-link>

          Create a document

</template>


日期单元格的data-date非当月的日期会计算成当月，例如当月是2016-08，日历7月份那最后几天的data-date字段会显示为2016-08-xx，会造成replaceText时同一个月出现两个today。

<template>

        {{year}}

        {{months[month]}}

          {{week}}

            {{replaceText(child.day, formatDate(year, month, child))}}
            {{{customSlotFn(k1, k2, child)}}}

</template>


fix: change treepicker v-for key

fix: change treepicker v-for key

<template>

      <template v-if="multiple&&objects.length">

您总共选择{{valuebak.length}}项

{{obj[param.titleName]}}

      </template>

{{object[param.titleName]}}

{{placeholder}}

        <Tree ref="tree" :toggleOnSelect="toggleOnSelect" :option="option" :multiple="multiple" v-model="valuebak" :chooseMode="chooseMode" @select="select" @choose="choose" :filterable="filterable" :config="config"></Tree>

        <button class="h-btn h-btn-text h-btn-s"
                @click="clear">清除</button>
        <button class="h-btn h-btn-primary h-btn-s"
                @click="confirm">确定</button>

</template>