Issue 菜单列表显示图标

Issue 菜单列表显示图标

<template>

      <el-button type="success" size="mini" icon="el-icon-plus"  @click.native="add" v-permission="['/menu/add']">{{ $t('button.add') }}</el-button>

    <el-table
      :data="data"
      style="width: 100%;margin-bottom: 20px;"
      row-key="id"
      border
      :default-expand-all="false"
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
      <el-table-column label="名称" >
        <template slot-scope="scope">
          <el-button type="text" @click="edit(scope.row)">{{scope.row.name}}</el-button>
        </template>
      </el-table-column>
      <el-table-column label="编码" >
        <template slot-scope="scope">
          {{scope.row.code}}
        </template>
      </el-table-column>
      <el-table-column label="图标">
        <template slot-scope="scope">
          <svg-icon :icon-class="scope.row.icon" />
        </template>
      </el-table-column>
      <el-table-column label="组件" >
        <template slot-scope="scope">
          {{scope.row.component}}
        </template>
      </el-table-column>
      <el-table-column label="类型" >
        <template slot-scope="scope">
          {{scope.row.ismenu==1?'菜单':'按钮'}}
        </template>
      </el-table-column>
      <el-table-column label="URL">
        <template slot-scope="scope">
          {{scope.row.url}}
        </template>
      </el-table-column>
      <el-table-column label="是否启用">
        <template slot-scope="scope">
          {{scope.row.statusName}}
        </template>
      </el-table-column>
      <el-table-column label="是否隐藏">
        <template slot-scope="scope">
          {{scope.row.hidden}}
        </template>
      </el-table-column>
      <el-table-column label="顺序">
        <template slot-scope="scope">
          {{scope.row.num}}
        </template>
      </el-table-column>
      <el-table-column label="操作" >
        <template slot-scope="scope">
          <el-button type="text" size="mini" icon="el-icon-edit" @click="edit(scope.row)" v-permission="['/menu/edit']">{{ $t('button.edit') }}</el-button>
          <el-button type="text" size="mini" icon="el-icon-delete" @click="remove(scope.row)" v-permission="['/menu/remove']">{{ $t('button.delete') }}</el-button>
        </template>
      </el-table-column>
    </el-table>

      <el-dialog
        :title="formTitle"
        :visible.sync="formVisible"
        width="70%">
        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
          <el-row>
            <el-col :span="12">
              <el-form-item label="名称" prop="name">
                <el-input v-model="form.name" minlength=1></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="请求地址" prop="url">
                <el-input v-model="form.url"  minlength=1></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="12">
              <el-form-item label="是否是菜单">
                <el-radio-group v-model="form.ismenu">
                  <el-radio :label="1">是</el-radio>
                  <el-radio :label="0">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="是否启用">
                <el-radio-group v-model="form.status">
                  <el-radio :label="1">是</el-radio>
                  <el-radio :label="0">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单编号" prop="code">
                <el-input v-model="form.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="组件" prop="num">
                <el-input v-model="form.component"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="是否隐藏">
                <el-radio-group v-model="form.hidden">
                  <el-radio :label="true">是</el-radio>
                  <el-radio :label="false">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="图标" >
                <el-input v-model="form.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="排序" prop="num">
                <el-input type="number" v-model="form.num"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="父菜单" >
                <el-input
                  placeholder="请选择父菜单"
                  v-model="form.pname"
                  readonly="readonly"
                  @click.native="showTree = !showTree">
                </el-input>
                <el-tree v-if="showTree"
                         empty-text="暂无数据"
                         :expand-on-click-node="false"
                         :data="data"
                         :props="defaultProps"
                         @node-click="handleNodeClick"
                         class="input-tree">
                </el-tree>

              </el-form-item>
            </el-col>

          </el-row>
          <el-form-item>
            <el-button type="primary" @click="save">{{ $t('button.submit') }}</el-button>
            <el-button @click.native="formVisible = false">{{ $t('button.cancel') }}</el-button>
          </el-form-item>
        </el-form>
      </el-dialog>

</template>

<template>

      <el-button type="success" size="mini" icon="el-icon-plus"  @click.native="add" v-permission="['/menu/add']">{{ $t('button.add') }}</el-button>

    <el-table
      :data="data"
      style="width: 100%;margin-bottom: 20px;"
      row-key="id"
      border
      :default-expand-all="false"
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
      <el-table-column label="名称" >
        <template slot-scope="scope">
          <el-button type="text" @click="edit(scope.row)">{{scope.row.name}}</el-button>
        </template>
      </el-table-column>
      <el-table-column label="编码" >
        <template slot-scope="scope">
          {{scope.row.code}}
        </template>
      </el-table-column>
      <el-table-column label="组件" >
        <template slot-scope="scope">
          {{scope.row.component}}
        </template>
      </el-table-column>
      <el-table-column label="类型" >
        <template slot-scope="scope">
          {{scope.row.ismenu==1?'菜单':'按钮'}}
        </template>
      </el-table-column>
      <el-table-column label="URL">
        <template slot-scope="scope">
          {{scope.row.url}}
        </template>
      </el-table-column>
      <el-table-column label="是否启用">
        <template slot-scope="scope">
          {{scope.row.statusName}}
        </template>
      </el-table-column>
      <el-table-column label="是否隐藏">
        <template slot-scope="scope">
          {{scope.row.hidden}}
        </template>
      </el-table-column>
      <el-table-column label="顺序">
        <template slot-scope="scope">
          {{scope.row.num}}
        </template>
      </el-table-column>
      <el-table-column label="操作" >
        <template slot-scope="scope">
          <el-button type="text" size="mini" icon="el-icon-edit" @click="edit(scope.row)" v-permission="['/menu/edit']">{{ $t('button.edit') }}</el-button>
          <el-button type="text" size="mini" icon="el-icon-delete" @click="remove(scope.row)" v-permission="['/menu/remove']">{{ $t('button.delete') }}</el-button>
        </template>
      </el-table-column>
    </el-table>

      <el-dialog
        :title="formTitle"
        :visible.sync="formVisible"
        width="70%">
        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
          <el-row>
            <el-col :span="12">
              <el-form-item label="名称" prop="name">
                <el-input v-model="form.name" minlength=1></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="请求地址" prop="url">
                <el-input v-model="form.url"  minlength=1></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="12">
              <el-form-item label="是否是菜单">
                <el-radio-group v-model="form.ismenu">
                  <el-radio :label="1">是</el-radio>
                  <el-radio :label="0">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="是否启用">
                <el-radio-group v-model="form.status">
                  <el-radio :label="1">是</el-radio>
                  <el-radio :label="0">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单编号" prop="code">
                <el-input v-model="form.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="组件" prop="num">
                <el-input v-model="form.component"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="是否隐藏">
                <el-radio-group v-model="form.hidden">
                  <el-radio :label="true">是</el-radio>
                  <el-radio :label="false">否</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="图标" >
                <el-input v-model="form.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="排序" prop="num">
                <el-input type="number" v-model="form.num"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="父菜单" >
                <el-input
                  placeholder="请选择父菜单"
                  v-model="form.pname"
                  readonly="readonly"
                  @click.native="showTree = !showTree">
                </el-input>
                <el-tree v-if="showTree"
                         empty-text="暂无数据"
                         :expand-on-click-node="false"
                         :data="data"
                         :props="defaultProps"
                         @node-click="handleNodeClick"
                         class="input-tree">
                </el-tree>

              </el-form-item>
            </el-col>

          </el-row>
          <el-form-item>
            <el-button type="primary" @click="save">{{ $t('button.submit') }}</el-button>
            <el-button @click.native="formVisible = false">{{ $t('button.cancel') }}</el-button>
          </el-form-item>
        </el-form>
      </el-dialog>

</template>


expand root instance by default

expand root instance by default

<template>

        <{{ instance.name }}>

        router-view{{ instance.matchedRouteSegment ? ': ' + instance.matchedRouteSegment : null }}

        fragment

        inactive

      <component-instance
        v-for="child in instance.children | orderBy 'inactive'"
        track-by="id"
        :instance="child"
        :depth="depth + 1">
      </component-instance>

</template>


added validation

added validation

<template>
  <v-app id="inspire">
    <v-container grid-list-md text-xs-center>
      <v-layout row wrap>
        <v-flex xs6>
          <v-container class="dack">
            <drop
              class="drop"
              @drop="handleDrop">
              <v-layout row wrap>
                <v-flex xs4 v-for="img in imgs" :key="imgs.indexOf(img)">
                  <drag
                  :transferData="imgs.indexOf(img)"
                  :key="imgs.indexOf(img)"
                  class="list-img"
                  >

                  </drag>
                </v-flex>
              </v-layout>
            </drop>
          </v-container>
        </v-flex>
        <v-flex xs6>
          <drop
          @drop="removeItems"
          >
            <v-container elevation-4 class="app">
              <v-list two-line class="list">
                <template v-for="item in items">
                  <v-subheader v-if="item.header">{{ item.header }}</v-subheader>
                  <v-divider v-else-if="item.divider" v-bind:inset="item.inset"></v-divider>
                  <v-list-tile avatar v-else v-bind:key="item.title" @click="">
                    <v-list-tile-avatar>
                      <drag
                        :transferData="{img: item.avatar, text: item.title}"
                        :key="item.avater">

                      </drag>
                    </v-list-tile-avatar>
                    <v-list-tile-content>
                      <v-list-tile-title v-html="item.title"></v-list-tile-title>
                      <v-list-tile-sub-title v-html="item.subtitle"></v-list-tile-sub-title>
                    </v-list-tile-content>
                  </v-list-tile>
                </template>
              </v-list>
            </v-container>
          </drop>
        </v-flex>
      </v-layout>
    </v-container>
    <simplert :useRadius="true"
          :useIcon="true"
          ref="simplert">
    </simplert>
  </v-app>
</template>


fix this

fix this

<script lang="coffee">
import EventBus from '@/shared/services/event_bus'
import { iconFor }          from '@/shared/helpers/poll'
import PollCommonDirective from '@/components/poll/common/directive.vue'
import Flash   from '@/shared/services/flash'
import _sortBy from 'lodash/sortBy'

export default
  props:
    stance: Object
    close: Function
  data: ->
    isEditing: true
  components:
    PollCommonDirective: PollCommonDirective

  methods:
    submit: ->
      actionName = if @stance.isNew() then 'created' else 'updated'
      @stance.save().then =>
        @stance.poll().clearStaleStances()
        Flash.success "poll_#{stance.poll().pollType}_vote_form.stance_#{actionName}"
        @close()

    toggleCreation: ->
      @isEditing = false
  computed:
    icon: ->
      iconFor(@stance.poll())
    orderedStanceChoices: ->
      _sortBy(@stance.stanceChoices(), 'rank')
</script>
<template lang="pug">
v-card.poll-common-edit-vote-modal
  submit-overlay(:value="stance.processing")
  v-card-title
    h1.headline
      span(v-if="stance.isNew()", v-t="'poll_common.your_response'")
      span(v-if="!stance.isNew()", v-t="'poll_common.change_your_response'")
    v-spacer
    dismiss-modal-button(:close="close")

  v-card-text(v-if="!isEditing")
    poll-common-directive(name="vote-form", :stance="stance")

  div(v-if="isEditing")
    v-card-text
      v-layout.mb-3(align-center wrap)
        poll-common-directive(:size="48" :stance-choice="choice", name="stance-choice", v-if="choice.id && choice.score > 0", v-for="choice in orderedStanceChoices" :key="choice.id")
        v-btn.poll-common-edit-vote__button(color="accent" outlined @click="toggleCreation()", v-t="'poll_common.change_vote'")
      .poll-common-stance-reason
        lmo-textarea.poll-common-vote-form__reason(:model='stance', field='reason', :label="$t('poll_common.reason')", :placeholder="$t('poll_common.reason_placeholder')", maxlength="500")
    v-card-actions
      v-spacer
      v-btn(color="primary" @click="submit()", v-t="'poll_common.save_changes'")
</template>

<script lang="coffee">
import EventBus from '@/shared/services/event_bus'
import { iconFor }          from '@/shared/helpers/poll'
import PollCommonDirective from '@/components/poll/common/directive.vue'
import Flash   from '@/shared/services/flash'
import _sortBy from 'lodash/sortBy'

export default
  props:
    stance: Object
    close: Function
  data: ->
    isEditing: true
  components:
    PollCommonDirective: PollCommonDirective

  methods:
    submit: ->
      actionName = if stance.isNew() then 'created' else 'updated'
      @stance.save().then =>
        @stance.poll().clearStaleStances()
        Flash.success "poll_#{stance.poll().pollType}_vote_form.stance_#{actionName}"
        @close()

    toggleCreation: ->
      @isEditing = false
  computed:
    icon: ->
      iconFor(@stance.poll())
    orderedStanceChoices: ->
      _sortBy(@stance.stanceChoices(), 'rank')
</script>
<template lang="pug">
v-card.poll-common-edit-vote-modal
  submit-overlay(:value="stance.processing")
  v-card-title
    h1.headline
      span(v-if="stance.isNew()", v-t="'poll_common.your_response'")
      span(v-if="!stance.isNew()", v-t="'poll_common.change_your_response'")
    v-spacer
    dismiss-modal-button(:close="close")

  v-card-text(v-if="!isEditing")
    poll-common-directive(name="vote-form", :stance="stance")

  div(v-if="isEditing")
    v-card-text
      v-layout.mb-3(align-center wrap)
        poll-common-directive(:size="48" :stance-choice="choice", name="stance-choice", v-if="choice.id && choice.score > 0", v-for="choice in orderedStanceChoices" :key="choice.id")
        v-btn.poll-common-edit-vote__button(color="accent" outlined @click="toggleCreation()", v-t="'poll_common.change_vote'")
      .poll-common-stance-reason
        lmo-textarea.poll-common-vote-form__reason(:model='stance', field='reason', :label="$t('poll_common.reason')", :placeholder="$t('poll_common.reason_placeholder')", maxlength="500")
    v-card-actions
      v-spacer
      v-btn(color="primary" @click="submit()", v-t="'poll_common.save_changes'")
</template>


AssessmentWrapper: move star icon down to align with message

<template v-if="ready">

    <UiAlert v-if="itemError" :dismissible="false" type="error">
      {{ $tr('itemError') }}
      <KButton
        appearance="basic-link"
        :text="$tr('tryDifferentQuestion')"
        @click="nextQuestion"
      />
    </UiAlert>

      <ContentRenderer
        ref="contentRenderer"
        :kind="kind"
        :lang="lang"
        :files="files"
        :available="available"
        :extraFields="extraFields"
        :assessment="true"
        :itemId="itemId"
        :initSession="initSession"
        @answerGiven="answerGiven"
        @hintTaken="hintTaken"
        @sessionInitialized="sessionInitialized"
        @itemError="handleItemError"
        @startTracking="startTracking"
        @stopTracking="stopTracking"
        @updateProgress="updateProgress"
        @updateContentState="updateContentState"
      />

    <KBottomAppBar
      class="attempts-container"
      :class="{ 'mobile': windowIsSmall }"
    >

        <mat-svg
          name="stars"
          category="action"
          :style="{
            fill: success ? $themeTokens.mastered : $themeColors.palette.grey.v_200,
            marginBottom: '-6px',
          }"
        />

            {{ $tr('completed') }}

            {{ $tr('goal', {count: totalCorrectRequiredM}) }}

            <transition mode="out-in">
              <KButton
                v-if="!complete"
                appearance="raised-button"
                class="question-btn"
                :text="$tr('check')"
                :primary="true"
                :class="{shaking: shake}"
                :disabled="checkingAnswer"
                @click="checkAnswer"
              />
              <KButton
                v-else
                appearance="raised-button"
                class="question-btn"
                :text="$tr('next')"
                :primary="true"
                @click="nextQuestion"
              />
            </transition>

            <ExerciseAttempts
              :waitingForAttempt="firstAttemptAtQuestion || itemError"
              :numSpaces="attemptsWindowN"
              :log="recentAttempts"
            />

              {{ currentStatus }}

    </KBottomAppBar>

</template>

<template v-if="ready">

    <UiAlert v-if="itemError" :dismissible="false" type="error">
      {{ $tr('itemError') }}
      <KButton
        appearance="basic-link"
        :text="$tr('tryDifferentQuestion')"
        @click="nextQuestion"
      />
    </UiAlert>

      <ContentRenderer
        ref="contentRenderer"
        :kind="kind"
        :lang="lang"
        :files="files"
        :available="available"
        :extraFields="extraFields"
        :assessment="true"
        :itemId="itemId"
        :initSession="initSession"
        @answerGiven="answerGiven"
        @hintTaken="hintTaken"
        @sessionInitialized="sessionInitialized"
        @itemError="handleItemError"
        @startTracking="startTracking"
        @stopTracking="stopTracking"
        @updateProgress="updateProgress"
        @updateContentState="updateContentState"
      />

    <KBottomAppBar
      class="attempts-container"
      :class="{ 'mobile': windowIsSmall }"
    >

        <mat-svg
          name="stars"
          category="action"
          :style="{
            fill: success ? $themeTokens.mastered : $themeColors.palette.grey.v_200,
            verticalAlign: 0,
          }"
        />

            {{ $tr('completed') }}

            {{ $tr('goal', {count: totalCorrectRequiredM}) }}

            <transition mode="out-in">
              <KButton
                v-if="!complete"
                appearance="raised-button"
                class="question-btn"
                :text="$tr('check')"
                :primary="true"
                :class="{shaking: shake}"
                :disabled="checkingAnswer"
                @click="checkAnswer"
              />
              <KButton
                v-else
                appearance="raised-button"
                class="question-btn"
                :text="$tr('next')"
                :primary="true"
                @click="nextQuestion"
              />
            </transition>

            <ExerciseAttempts
              :waitingForAttempt="firstAttemptAtQuestion || itemError"
              :numSpaces="attemptsWindowN"
              :log="recentAttempts"
            />

              {{ currentStatus }}

    </KBottomAppBar>

</template>


Make navbar semi-translucent

Make navbar semi-translucent

<template>
  <header>
    <nav>
      <h1>{{ $t('hibari') }}</h1>
      <router-link to='/'>{{ $t('navigation.home') }}</router-link>
      <git-hub-corner></git-hub-corner>
    </nav>
  </header>
</template>

<script>
  import GitHubCorner from 'components/GitHubCorner'
  export default {
    components: {
      GitHubCorner
    }
  }
</script>

<style lang='scss'>
  @import '~styles/main.scss';

  header {
    @extend .navbar;
    @extend .navbar-light;
    @extend .fixed-top;
    background: rgba(white, .8);
    transition: background 300ms ease-out;
    backdrop-filter: blur(2px);
    border-bottom: 1px solid rgba(black, .05);

    &:hover {
      background: white;
      transition: background 200ms ease-in;
    }

    nav {
      @extend .nav;
      @extend .container;
    }

    h1 {
      @extend .navbar-brand;
      @extend .mb-0;
      padding-left: 15px;
    }

    a {
      @extend .nav-link;
      color: black;
      transition: color 200ms ease-out;

      &:hover {
        color: $primary;
        transition: color 100ms ease-in;
      }
    }
  }
</style>

<template>
  <header>
    <nav>
      <h1>{{ $t('hibari') }}</h1>
      <router-link to='/'>{{ $t('navigation.home') }}</router-link>
      <git-hub-corner></git-hub-corner>
    </nav>
  </header>
</template>

<script>
  import GitHubCorner from 'components/GitHubCorner'
  export default {
    components: {
      GitHubCorner
    }
  }
</script>

<style lang='scss'>
  @import '~styles/main.scss';

  header {
    @extend .navbar;
    @extend .navbar-light;
    @extend .fixed-top;

    background: white;
    box-shadow: 0 0 1px 0 rgba(black, .25);

    nav {
      @extend .nav;
      @extend .container;
    }

    h1 {
      @extend .navbar-brand;
      @extend .mb-0;
      padding-left: 15px;
    }

    a {
      @extend .nav-link;
    }
  }
</style>


Hide auto completion menu when channel changes

<template>
	<form id="form" method="post" action="" @submit.prevent="onSubmit">
		{{ network.nick }}
		<textarea
			id="input"
			ref="input"
			dir="auto"
			:value="channel.pendingMessage"
			:placeholder="getInputPlaceholder(channel)"
			:aria-label="getInputPlaceholder(channel)"
			@input="setPendingMessage"
			@keypress.enter.exact.prevent="onSubmit"
		/>

			<input
				id="upload-input"
				ref="uploadInput"
				type="file"
				multiple
				@change="onUploadInputChange"
			/>
			<button
				id="upload"
				type="button"
				aria-label="Upload file"
				:disabled="!$store.state.isConnected"
			/>

			<button
				id="submit"
				type="submit"
				aria-label="Send message"
				:disabled="!$store.state.isConnected"
			/>

	</form>
</template>


handle null in handleResize

handle null in handleResize

<template>
  <ul class="photo-list" ref="photoList" :style="{ height: `${height}px` }">
    <transition-group name="slide-fade">

        <dropzone :group-id="groupId" :width="width" :columns="columns" />

        <photo :photo-id="photo.photoId" :url="photo.link" :voted="photo.voted" :votes="photo.votes" :width="width" />

    </transition-group>
    <resize-observer @notify="handleResize" />

</template>


fix changing the feed's category

fix changing the feed's category

<template>

    <transition name="modal">

Add new feed

Add new category

Delete category

Rename category

Delete feed

Rename feed

Categories

                <button
                  type="button"
                  class="close"
                  data-dismiss="modal"
                  aria-label="Close"
                  @click="closeModal"
                >
                  ×
                </button>

 0">
                  <input
                    class="form-control form-control-lg"
                    type="text"
                    placeholder="Enter feed or website url..."
                    v-model="url"
                  >

                    Category

                      <select
                        class="form-control"
                        id="category"
                        v-model="store.currentSelection.categoryId"
                      >
                        <option
                          v-for="category in this.store.categories"
                          :value="category.id"
                          :key="category.id"
                          v-bind:id="category.id"
                        >{{ category.name }}</option>
                      </select>

No categories exist at this moment.

First create a new category before adding a new feed.

                <button
                  v-if="this.store.categories.length > 0"
                  type="submit"
                  class="btn btn-primary mb-2"
                  @click="checkWebsite"
                >Validate feed</button>

                Please Wait ...

                {{ error_msg }}

                    Feed name

                      <input
                        type="text"
                        class="form-control"
                        v-model="feed.feedName"
                        placeholder="Feed name"
                      >

                    Feed description

                      <input
                        type="text"
                        class="form-control"
                        v-model="feed.feedDesc"
                        placeholder="Feed description"
                      >

                <input
                  class="form-control form-control-lg"
                  type="text"
                  placeholder="Enter new category name.."
                  v-model="category.name"
                >

Are you sure to delete this category?

                <input
                  class="form-control form-control-lg"
                  type="text"
                  placeholder="Enter new category name.."
                  v-model="category.name"
                >

Are you sure to delete this feed?

                  Feed name

                    <input
                      class="form-control"
                      type="text"
                      id="feed_name"
                      placeholder="Feed name"
                      v-model="feed.feedName"
                    >

 10">
                  Feed url

                    <input
                      class="form-control red"
                      type="text"
                      id="rssUrl"
                      placeholder="Feed RSS Url"
                      v-model="feed.rssUrl"
                    >

 0">

                    Feed description

                      <input
                        class="form-control"
                        type="text"
                        id="feed_desc"
                        placeholder="Feed description"
                        v-model="feed.feedDesc"
                      >

                    Category

                      <select class="form-control" id="category" v-model="selectedCategory">
                        <option
                          v-for="category in this.categories"
                          :selected="selectedCategory == feed.categoryId"
                          :value="category.id"
                          :key="category.id"
                          v-bind:id="category.id"
                        >{{ category.name }}</option>
                      </select>

Select which category you want to display

                    Show all categories

                    {{ category.name }}

Select how the articles should be displayed

                <button
                  @click="store.filter = 'full'"
                  type="button"
                  class="btn btn-primary content"
                >Full content</button>
                <button
                  @click="store.filter = 'minimal'"
                  type="button"
                  class="btn btn-primary content"
                >Minimal content</button>

Click the button below to add a new feed

                <button
                  @click="modal = 'newfeed'"
                  type="button"
                  class="btn btn-success"
                >Add new feed</button>

                <button
                  v-if="feed.feedName && modal==='newfeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="newFeed"
                >Save changes</button>
                <button
                  v-if="modal==='newcategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="saveCategory"
                >Add new category</button>
                <button
                  v-if="modal==='deletecategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="deleteCategory"
                >Delete category</button>
                <button
                  v-if="modal==='renamecategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="renameCategory"
                >Update category</button>
                <button
                  v-if="modal==='deletefeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="deleteFeed"
                >Delete feed</button>
                <button
                  v-if="modal==='renamefeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="renameFeed"
                >Update feed</button>
                <button
                  type="button"
                  class="btn btn-secondary"
                  data-dismiss="modal"
                  @click="emitClickEvent('modal',null)"
                >Cancel</button>

    </transition>

</template>

<template>

    <transition name="modal">

Add new feed

Add new category

Delete category

Rename category

Delete feed

Rename feed

Categories

                <button
                  type="button"
                  class="close"
                  data-dismiss="modal"
                  aria-label="Close"
                  @click="closeModal"
                >
                  ×
                </button>

 0">
                  <input
                    class="form-control form-control-lg"
                    type="text"
                    placeholder="Enter feed or website url..."
                    v-model="url"
                  >

                    Category

                      <select
                        class="form-control"
                        id="category"
                        v-model="store.currentSelection.categoryId"
                      >
                        <option
                          v-for="category in this.store.categories"
                          :value="category.id"
                          :key="category.id"
                          v-bind:id="category.id"
                        >{{ category.name }}</option>
                      </select>

No categories exist at this moment.

First create a new category before adding a new feed.

                <button
                  v-if="this.store.categories.length > 0"
                  type="submit"
                  class="btn btn-primary mb-2"
                  @click="checkWebsite"
                >Validate feed</button>

                Please Wait ...

                {{ error_msg }}

                    Feed name

                      <input
                        type="text"
                        class="form-control"
                        v-model="feed.feedName"
                        placeholder="Feed name"
                      >

                    Feed description

                      <input
                        type="text"
                        class="form-control"
                        v-model="feed.feedDesc"
                        placeholder="Feed description"
                      >

                <input
                  class="form-control form-control-lg"
                  type="text"
                  placeholder="Enter new category name.."
                  v-model="category.name"
                >

Are you sure to delete this category?

                <input
                  class="form-control form-control-lg"
                  type="text"
                  placeholder="Enter new category name.."
                  v-model="category.name"
                >

Are you sure to delete this feed?

                  Feed name

                    <input
                      class="form-control"
                      type="text"
                      id="feed_name"
                      placeholder="Feed name"
                      v-model="feed.feedName"
                    >

 10">
                  Feed url

                    <input
                      class="form-control red"
                      type="text"
                      id="rssUrl"
                      placeholder="Feed RSS Url"
                      v-model="feed.rssUrl"
                    >

 0">

                    Feed description

                      <input
                        class="form-control"
                        type="text"
                        id="feed_desc"
                        placeholder="Feed description"
                        v-model="feed.feedDesc"
                      >

                    Category

                      <select
                        class="form-control"
                        id="category"
                        v-model="store.currentSelection.categoryId"
                      >
                        <option
                          v-for="category in this.store.categories"
                          :value="category.id"
                          :key="category.id"
                          v-bind:id="category.id"
                        >{{ category.name }}</option>
                      </select>

Select which category you want to display

                    Show all categories

                    {{ category.name }}

Select how the articles should be displayed

                <button
                  @click="store.filter = 'full'"
                  type="button"
                  class="btn btn-primary content"
                >Full content</button>
                <button
                  @click="store.filter = 'minimal'"
                  type="button"
                  class="btn btn-primary content"
                >Minimal content</button>

Click the button below to add a new feed

                <button
                  @click="modal = 'newfeed'"
                  type="button"
                  class="btn btn-success"
                >Add new feed</button>

                <button
                  v-if="feed.feedName && modal==='newfeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="newFeed"
                >Save changes</button>
                <button
                  v-if="modal==='newcategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="saveCategory"
                >Add new category</button>
                <button
                  v-if="modal==='deletecategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="deleteCategory"
                >Delete category</button>
                <button
                  v-if="modal==='renamecategory'"
                  type="button"
                  class="btn btn-primary"
                  @click="renameCategory"
                >Update category</button>
                <button
                  v-if="modal==='deletefeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="deleteFeed"
                >Delete feed</button>
                <button
                  v-if="modal==='renamefeed'"
                  type="button"
                  class="btn btn-primary"
                  @click="renameFeed"
                >Update feed</button>
                <button
                  type="button"
                  class="btn btn-secondary"
                  data-dismiss="modal"
                  @click="emitClickEvent('modal',null)"
                >Cancel</button>

    </transition>

</template>


just a /

just a /

<template>

                    {{ trans('screengroup.model', 2) }}

                                     {{ trans('general.archive') }}

                                     {{ trans('general.new') }}

0 ">

                    {{ trans('screengroup.edit') }}

                                     {{ trans('screen.add') }}

0 ">

                                     {{ trans('ticker.add') }}

                                     {{ trans('client.add') }}

                        <router-view></router-view>

</template>


Upgrade progressbar using proper upgrade function

<template lang="jade">
.mdl-progress.mdl-js-progress(v-bind:class='{ "mdl-progress__indeterminate": indeterminate }')
</template>

<script lang="coffee">
module.exports =
  data: ->
    unwatch: []
  props:
    progress:
      required: false
    buffer:
      required: false
    indeterminate:
      type: Boolean
      default: false
  ready: ->
    componentHandler.upgradeElement @$el, 'MaterialProgress'

    if @progress?
      @$el.MaterialProgress.setProgress @progress
      @unwatch.push @$watch 'progress', (val) ->
        @$el.MaterialProgress.setProgress val

    if @buffer?
      @$el.MaterialProgress.setBuffer @buffer
      @unwatch.push @$watch 'buffer', (val) ->
        @$el.MaterialProgress.setBuffer val
  beforeDestroy: ->
    @unwatch.forEach (unwatch) -> unwatch()
</script>

<template lang="jade">
.mdl-progress.mdl-js-progress(v-bind:class='{ "mdl-progress__indeterminate": indeterminate }')
</template>

<script lang="coffee">
module.exports =
  data: ->
    unwatch: []
  props:
    progress:
      required: false
    buffer:
      required: false
    indeterminate:
      type: Boolean
      default: false
  ready: ->
    componentHandler.upgradeElement @$el

    if @progress?
      @$el.MaterialProgress.setProgress @progress
      @unwatch.push @$watch 'progress', (val) ->
        @$el.MaterialProgress.setProgress val

    if @buffer?
      @$el.MaterialProgress.setBuffer @buffer
      @unwatch.push @$watch 'buffer', (val) ->
        @$el.MaterialProgress.setBuffer val
  beforeDestroy: ->
    @unwatch.forEach (unwatch) -> unwatch()
</script>


Address some more accessibility concerns

Signed-off-by: Julius Härtl <bf353fa4999f2f148afcc6d8ee6cb1ee74cc07c3@bitgrid.net>

<template>

				 0 }"
					:aria-label="appLabel(app)"
					:aria-current="app.active ? 'page' : false">

						{{ app.name }}
						 0" class="hidden-visually unread-counter">{{ app.unread }}

		<NcActions class="app-menu-more" :aria-label="t('core', 'More apps')">
			<NcActionLink v-for="app in popoverAppList"
				:key="app.id"
				:aria-label="appLabel(app)"
				:aria-current="app.active ? 'page' : false"
				:href="app.href"
				class="app-menu-popover-entry">
				<template #icon>

 0 }">

				</template>
				{{ app.name }}
				 0" class="hidden-visually unread-counter">{{ app.unread }}
			</NcActionLink>
		</NcActions>

</template>

<template>

				 0 }" :aria-label="appLabel(app)">

						{{ app.name }}
						 0" class="hidden-visually unread-counter">{{ app.unread }}

		<NcActions class="app-menu-more" :aria-label="t('core', 'More apps')">
			<NcActionLink v-for="app in popoverAppList"
				:key="app.id"
				:aria-label="appLabel(app)"
				:href="app.href"
				class="app-menu-popover-entry">
				<template #icon>

 0 }">

				</template>
				{{ app.name }}
				 0" class="hidden-visually unread-counter">{{ app.unread }}
			</NcActionLink>
		</NcActions>

</template>


Alterando Layout: duvidas

Alterando Layout: duvidas

ALterando o layout de avaliação na página dúvidas

<template>

           <router-link class="btn btn-outline-secondary" role="button" aria-pressed="true" to="/inicio"></router-link>

Envie-nos a sua dúvida

Pergunte o que quiser, respondemos rapidinho!

        <form id="duvidas-form">

                    Máximo de 900 caracteres

Avalie o Aplicativo

                        <input type="radio" class="option-input radio" id="ruim" v-model="radioselected" name="rate" value="Ruim">
                        Ruim

                        <input type="radio" class="option-input radio"  id="razoavel" v-model="radioselected" name="rate" value="Razoável">
                        Razoável

                        <input type="radio" class="option-input radio" id="bom" v-model="radioselected" name="rate" value="Bom">
                        Bom

                        <input type="radio" class="option-input radio" id="otimo" v-model="radioselected" name="rate" value="Ótimo">
                        Ótimo

                        <input type="radio" class="option-input radio" id="incrivel" v-model="radioselected" name="rate" value="Incrível">
                        Incrível

            {{radioselected}}

                Confirmar

        </form>

</template>

<template>

           <router-link class="btn btn-outline-secondary" role="button" aria-pressed="true" to="/inicio"></router-link>

Envie-nos a sua dúvida

Pergunte o que quiser, respondemos rapidinho!

        <form id="duvidas-form">

                    Máximo de 900 caracteres

Avalie o Aplicativo

                    Ruim

                    Razoável

                    Bom

                    Ótimo

                    Incrível

                Confirmar

        </form>

</template>


Fix sorting rules on UI to avoid infinite loop

<template>

              Error {{ error }}

    <div class="row" v-if="!loading && !error && (dataMapByNs == null || Object.keys(dataMapByNs).length <= 0)">

                No Rules Defined

Rules: {{ ns }}

                Name
                Weight
                Criteria
                Rule Actions
                Action

                  <obj-with-icon :obj="d"/>

                  {{d.weight}}

                      {{cType}}
                      {{ c }}

                        [+] label
                        {{ k }} = {{ v }}

                        [-] label
                        {{ k }}

                        {{ aType }}
                        {{ aData }}

                        Unknown dependency action

                      Unknown rule type

                  <button type="button" class="btn btn-default btn-xs" @click="editYaml(d)">Edit</button>

</template>


ad more features

ad more features

<template>

    <md-layout flex="100" style="padding: 2%; padding-top: 0px;">
      <md-layout flex="100">
        <md-input-container>
          HERO
          <md-input v-model="hero" v-bind:readonly="isFindingAvatar"></md-input>
        </md-input-container>
      </md-layout>
      <md-layout md-align="center">
        <md-button class="md-raised md-primary large-button" v-on:click.native="find" v-bind:disabled="isFindingAvatar">
          {{ !isFindingAvatar ? 'Start Battle' : 'Battle in progress'}}
        </md-button>
      </md-layout>
      <md-layout>
        <md-input-container flex="100">
          ENEMY
          <md-input v-model="enemy" v-bind:readonly="isFindingAvatar"></md-input>
        </md-input-container>
      </md-layout>
    </md-layout>

</template>


Add transitions

Add transitions

<template>

      <transition name="table__module">

 0">

          Code
          Title
          Credits
          Exam Timing

      </transition>

      <transition name="table__row">

          Please refresh the page to try again. We could not connect to the database.

        Click the button to add a module!

      </transition>

    <transition name="table__module">

    </transition>

</template>

<template>

      <transition name="table__module">

 0">

          Code
          Title
          Credits
          Exam Timing

      </transition>

          Please refresh the page to try again. We could not connect to the database.

      <transition name="table__row">>

          Click the button to add a module!

      </transition>

    <transition name="table__module">

    </transition>

</template>


Space out vote options

Space out vote options

<template>

    <q-card-title>

{{title}}

    </q-card-title>

by {{sponsor}}

voteID: {{voteID}}

Vote ends in {{timeRemaining}}

    <template v-for="(item, index) in questions.answers">

        <q-btn v-if="isChosenAnswerIndex(index)" v-on:click="choseAnswer(index)" color="blue">{{item}}</q-btn>
        <q-btn v-else="isChosenAnswerIndex(index)" v-on:click="chooseAnswer(index)">{{item}}</q-btn>

    </template>

      <q-btn class="mt3" v-on:click="vote()">Vote</q-btn>

</template>


Ajout des icones de l'application

Ajout des icones de l'application


fix(sidebar): wrong active parent element

<template>

       <router-link :to="item.path" @click="toggleMenuItem(item)" v-if="item.path">
         {{item.meta.title}}
       </router-link>

         {{item.meta.title}}

           <router-link :to="childItem.path">
             {{childItem.meta.title}}
           </router-link>

</template>

<template>

       <router-link :to="item.path || ''" @click.native="toggleMenuItem(item)">
         {{item.meta.title}}
       </router-link>

           <router-link :to="childItem.path">
             {{childItem.meta.title}}
           </router-link>

</template>


修改图层移出文件夹时，删除图层bug

修改图层移出文件夹时，删除图层bug

<template>

    <mdl-snackbar display-on="mailSent"></mdl-snackbar>

{{styleObj.name}}

            delete

            删除

            content_copy

            复制

            create

            新建

            keyboard_arrow_right
            keyboard_arrow_down
            folder

            {{layer.id}}

              {{item.id}}

{{curPanelLayer.id}}

          样式
          数据

{{translate[name.replace(curPanelLayer.type+'-','')]}}

              <input class="color" type="text" v-model="value" v-if="name.indexOf('color')!=-1" v-on:change='propertyChange' v-on:click="colorPickerClick" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>

              <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

{{translate[name.replace(curPanelLayer.type+'-','')]}}

              <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout'></mdl-switch>
              <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout'></mdl-switch>

            布局
            注记
            符号

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                  <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

                  <input class="color" type="text" v-on:change='propertyChange' v-on:click="colorPickerClick" v-model="value" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>

                  <input type="text" :value="value" v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                  <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='layout'>
                    <option value="{{'{'+field+'}'}}" type="{{type}}" v-for="(field,type) in layerFields">{{'{'+field+'}'}}</option>
                  </select>
                  <input type="text" v-if="value.indexOf('{')==-1" v-model="value" name="{{name}}" data-type='layout' v-on:change='propertyChange' @focus="fieldInputFocus" @blur="fieldInputBlur">
                  <input type="text" v-model="" name="{{name}}" data-type='layout' v-on:change='propertyChange' @focus="fieldInputFocus" @blur="fieldInputBlur" v-else>

                  <input type="text" :value="value" name="{{name}}" v-if="name==='text-font'" v-on:change='propertyChange' v-on:click='onShowFontPanel' data-type='layout'/>
                  <input type="text" :value="value" name="{{name}}" v-else v-on:change='propertyChange' data-type='layout'/>

                  <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='layout'>
                    <option value="center">中心</option>
                    <option value="left">右</option>
                    <option value="right">左</option>
                    <option value="top">下</option>
                    <option value="bottom">上</option>
                    <option value="top-left">右下</option>
                    <option value="top-right">左下</option>
                    <option value="bottom-left">右上</option>
                    <option value="bottom-right">左上</option>
                  </select>

                  <mdl-switch :checked.sync="true" v-if="value==true" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                  <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

                timeline

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                  <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

                  <input type="text" :value="value" v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                  <input class="color" type="text" v-on:change='propertyChange' v-on:click="colorPickerClick" v-model="value" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>

                timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                  <input type="text" :value="value" name="{{name}}" v-if="name==='icon-image'" v-on:change='propertyChange' v-on:click='onShowIconPanel' data-type='layout'/>
                  <input type="text" :value="value" name="{{name}}" v-else v-on:change='propertyChange' data-type='layout'/>

                  <mdl-switch :checked.sync="true" v-if="value==true" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                  <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

                timeline

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                  <input type="text" :value="value" name="{{name}}" v-on:change='propertyChange' data-type='layout'/>

                  <mdl-switch :checked.sync="true" v-if="value==true" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                  <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

                  <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                  <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

                  <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='layout'>
                    <option value="point">点</option>
                    <option value="line">线</option>
                  </select>

标注盾牌

                  <mdl-switch :checked.sync="true" v-if="styleObj.metadata.shield&&styleObj.metadata.shield.indexOf(curPanelLayer.id)!==-1" v-on:change='shieldChange' data-name="shield" data-type='layout' ></mdl-switch>
                  <mdl-switch :checked.sync="false" v-else v-on:change='shieldChange' data-name="shield" data-type='layout' ></mdl-switch>

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <input class="color" type="text" v-model="value" v-if="name.indexOf('color')!=-1" v-on:change='propertyChange' v-on:click="colorPickerClick" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>
                <input type="text" :value="value" v-else v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

                <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='paint'>
                  <option value="map">地图</option>
                  <option value="viewport">视图窗口</option>
                </select>

                <mdl-switch :checked.sync="true" v-if="value==true" v-on:change='propertyChange' data-name="{{name}}" data-type='paint' ></mdl-switch>
                <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='paint' ></mdl-switch>

              timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

              timeline

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <input class="color" type="text" v-model="value" v-if="name.indexOf('color')!=-1" v-on:change='propertyChange' v-on:click="colorPickerClick" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>
                <input type="text" :value="value" v-else v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

                <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='paint'>
                  <option value="map">地图</option>
                  <option value="viewport">视图窗口</option>
                </select>

              timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <input type="text" :value="value" v-on:change='propertyChange' name="{{name}}" data-type='layout' />

                <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

                <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='layout'>
                  <option value="butt">粗</option>
                  <option value="round">圆</option>
                  <option value="square">方</option>
                </select>

                <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='layout'>
                  <option value="bevel">斜交叉</option>
                  <option value="miter">切线交叉</option>
                  <option value="round">圆交叉</option>
                </select>

                <input type="text" :value="value" v-on:change='propertyChange' v-if="curPanelLayer.layout['line-join']=='miter' || curPanelLayer.layout['line-join']=='bevel'" disabled name="{{name}}" data-type='layout'/>
                <input type="text" :value="value" v-on:change='propertyChange' v-else name="{{name}}" data-type='layout'/>

                <input type="text" :value="value" v-on:change='propertyChange' v-if="curPanelLayer.layout['line-join']=='round' || curPanelLayer.layout['line-join']=='bevel'" disabled name="{{name}}" data-type='layout'/>
                <input type="text" :value="value" v-on:change='propertyChange' v-else name="{{name}}" data-type='layout'/>

              timeline

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <input class="color" type="text" v-model="value" v-if="name.indexOf('color')!=-1" v-on:change='propertyChange' v-on:click="colorPickerClick" name="{{name}}" data-type='paint' :style = "'background-color:'+value" lazy/>
                <input type="text" :value="value" v-else v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

                <select v-model="value" v-on:change='propertyChange' name="{{name}}" data-type='paint'>
                  <option value="map">地图</option>
                  <option value="viewport">视图窗口</option>
                </select>

              timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

              timeline

绘图属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <input type="text" :value="value" v-on:change='propertyChange' name="{{name}}" data-type='paint' />

                <mdl-slider :value.sync="value" min="0" max="1" step="0.05" name="{{name}}" @input='propertyChange' data-type='paint'></mdl-slider>

              timeline

输出属性

{{translate[name.replace(curPanelLayer.type+'-','')]}}

                <mdl-switch :checked.sync="true" v-if="value=='visible'" v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>
                <mdl-switch :checked.sync="false" v-else v-on:change='propertyChange' data-name="{{name}}" data-type='layout' ></mdl-switch>

              timeline

          <foxgis-filter-data :selecteddata="selectedData" :layerfields="layerFields" :folders="Folders" :types="types"></foxgis-filter-data>

      clear

新建图层

        <foxgis-filter-data :selecteddata="selectedData" :layerfields="layerFields" :folders="Folders" :types="types"></foxgis-filter-data>

      <mdl-button colored raised id="btn-createLayer" @click="createNewLayer">创建图层</mdl-button>
      <mdl-button colored raised id="btn-cancel" @click="createPanelClose">关闭</mdl-button>

        字体详情

            folder
            {{family}}({{fonts.length}})

            title

    <foxgis-icon-panel id="icon-select-panel" class="panel" :dataset="spriteObj"></foxgis-icon-panel>
    <foxgis-stops-panel id="stops-panel" class="panel" :stopsdata="stopsData" :name="stopsData.property.name" :layerfields="layerFields"></foxgis-stops-panel>
    <foxgis-dialog-single id="copy-layer-panel" @dialog-action="copyLayerAction" :dialog="dialogcontent"></foxgis-dialog-single>
    <foxgis-dialog-prompt id="layer-folder-dialog" class='modal' :dialog="dialogcontent" v-on:dialog-action="saveAction"></foxgis-dialog-prompt>

</template>


fix: canvas setTransform and transform pixelRatio question/144676

<template>
  <uni-canvas
    :canvas-id="canvasId"
    :disable-scroll="disableScroll"
    v-on="_listeners"
  >
    <canvas
      ref="canvas"
      width="300"
      height="150"
    />
    <div
      style="
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
      "
    >
      <slot />
    </div>
    <v-uni-resize-sensor
      ref="sensor"
      @resize="_resize"
    />
  </uni-canvas>
</template>
<script>
import {
  subscriber
} from 'uni-mixins'

import {
  pixelRatio,
  wrapper
} from 'uni-helpers/hidpi'

import saveImage from 'uni-platform/helpers/save-image'
import { getSameOriginUrl } from 'uni-platform/helpers/file'

function resolveColor (color) {
  color = color.slice(0)
  color[3] = color[3] / 255
  return 'rgba(' + color.join(',') + ')'
}

function processTouches (target, touches) {
  return ([]).map.call(touches, (touch) => {
    var boundingClientRect = target.getBoundingClientRect()
    return {
      identifier: touch.identifier,
      x: touch.clientX - boundingClientRect.left,
      y: touch.clientY - boundingClientRect.top
    }
  })
}

var tempCanvas
function getTempCanvas (width = 0, height = 0) {
  if (!tempCanvas) {
    tempCanvas = document.createElement('canvas')
  }
  tempCanvas.width = width
  tempCanvas.height = height
  return tempCanvas
}

export default {
  name: 'Canvas',
  mixins: [subscriber],
  props: {
    canvasId: {
      type: String,
      default: ''
    },
    disableScroll: {
      type: [Boolean, String],
      default: false
    },
    hidpi: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      actionsWaiting: false
    }
  },
  computed: {
    id () {
      return this.canvasId
    },
    _listeners () {
      var $listeners = Object.assign({}, this.$listeners)
      var events = ['touchstart', 'touchmove', 'touchend']
      events.forEach(event => {
        var existing = $listeners[event]
        var eventHandler = []
        if (existing) {
          eventHandler.push(($event) => {
            this.$trigger(event, Object.assign({}, $event, {
              touches: processTouches($event.currentTarget, $event.touches),
              changedTouches: processTouches($event.currentTarget, $event
                .changedTouches)
            }))
          })
        }
        if (this.disableScroll && event === 'touchmove') {
          eventHandler.push(this._touchmove)
        }
        $listeners[event] = eventHandler
      })
      return $listeners
    },
    pixelRatio () {
      return this.hidpi ? pixelRatio : 1
    }
  },
  created () {
    this._actionsDefer = []
    this._images = {}
  },
  mounted () {
    this._resize()
  },
  beforeDestroy () {
    const canvas = this.$refs.canvas
    canvas.height = canvas.width = 0
  },
  methods: {
    _handleSubscribe ({
      type,
      data = {}
    }) {
      var method = this[type]
      if (type.indexOf('_') !== 0 && typeof method === 'function') {
        method(data)
      }
    },
    _resize (size) {
      var canvas = this.$refs.canvas
      var hasChanged = !size || (canvas.width !== Math.floor(size.width * this.pixelRatio) || canvas.height !== Math.floor(size.height * this.pixelRatio))
      if (!hasChanged) return
      if (canvas.width > 0 && canvas.height > 0) {
        var context = canvas.getContext('2d')
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height)
        wrapper(canvas, this.hidpi)
        context.putImageData(imageData, 0, 0)
      } else {
        wrapper(canvas, this.hidpi)
      }
    },
    _touchmove (event) {
      event.preventDefault()
    },
    actionsChanged ({
      actions,
      reserve,
      callbackId
    }) {
      var self = this
      if (!actions) {
        return
      }
      if (this.actionsWaiting) {
        this._actionsDefer.push([actions, reserve, callbackId])
        return
      }
      var canvas = this.$refs.canvas
      var c2d = canvas.getContext('2d')
      if (!reserve) {
        c2d.fillStyle = '#000000'
        c2d.strokeStyle = '#000000'
        c2d.shadowColor = '#000000'
        c2d.shadowBlur = 0
        c2d.shadowOffsetX = 0
        c2d.shadowOffsetY = 0
        c2d.setTransform(1, 0, 0, 1, 0, 0)
        c2d.clearRect(0, 0, canvas.width, canvas.height)
      }
      this.preloadImage(actions)
      for (let index = 0; index < actions.length; index++) {
        const action = actions[index]
        let method = action.method
        const data = action.data
        if (/^set/.test(method) && method !== 'setTransform') {
          const method1 = method[3].toLowerCase() + method.slice(4)
          let color
          if (method1 === 'fillStyle' || method1 === 'strokeStyle') {
            if (data[0] === 'normal') {
              color = resolveColor(data[1])
            } else if (data[0] === 'linear') {
              const LinearGradient = c2d.createLinearGradient(...data[1])
              data[2].forEach(function (data2) {
                const offset = data2[0]
                const color = resolveColor(data2[1])
                LinearGradient.addColorStop(offset, color)
              })
              color = LinearGradient
            } else if (data[0] === 'radial') {
              const x = data[1][0]
              const y = data[1][1]
              const r = data[1][2]
              const LinearGradient = c2d.createRadialGradient(x, y, 0, x, y, r)
              data[2].forEach(function (data2) {
                const offset = data2[0]
                const color = resolveColor(data2[1])
                LinearGradient.addColorStop(offset, color)
              })
              color = LinearGradient
            } else if (data[0] === 'pattern') {
              const loaded = this.checkImageLoaded(data[1], actions.slice(index + 1), callbackId,
                function (image) {
                  if (image) {
                    c2d[method1] = c2d.createPattern(image, data[2])
                  }
                })
              if (!loaded) {
                break
              }
              continue
            }
            c2d[method1] = color
          } else if (method1 === 'globalAlpha') {
            c2d[method1] = data[0] / 255
          } else if (method1 === 'shadow') {
            var _ = ['shadowOffsetX', 'shadowOffsetY', 'shadowBlur', 'shadowColor']
            data.forEach(function (color_, method_) {
              c2d[_[method_]] = _[method_] === 'shadowColor' ? resolveColor(color_) : color_
            })
          } else if (method1 === 'fontSize') {
            const font = c2d.__font__ || c2d.font
            c2d.__font__ = c2d.font = font.replace(/\d+\.?\d*px/, data[0] + 'px')
          } else if (method1 === 'lineDash') {
            c2d.setLineDash(data[0])
            c2d.lineDashOffset = data[1] || 0
          } else if (method1 === 'textBaseline') {
            if (data[0] === 'normal') {
              data[0] = 'alphabetic'
            }
            c2d[method1] = data[0]
          } else if (method1 === 'font') {
            c2d.__font__ = c2d.font = data[0]
          } else {
            c2d[method1] = data[0]
          }
        } else if (method === 'fillPath' || method === 'strokePath') {
          method = method.replace(/Path/, '')
          c2d.beginPath()
          data.forEach(function (data_) {
            c2d[data_.method].apply(c2d, data_.data)
          })
          c2d[method]()
        } else if (method === 'fillText') {
          c2d.fillText.apply(c2d, data)
        } else if (method === 'drawImage') {
          var A = (function () {
            var dataArray = [...data]
            var url = dataArray[0]
            var otherData = dataArray.slice(1)
            self._images = self._images || {}
            if (!self.checkImageLoaded(url, actions.slice(index + 1), callbackId, function (
              image) {
              if (image) {
                c2d.drawImage.apply(c2d, [image].concat([...otherData.slice(4, 8)],
                  [...otherData.slice(0, 4)]))
              }
            })) return 'break'
          }())
          if (A === 'break') {
            break
          }
        } else {
          if(method === 'setTransform' || method === 'transform'){
            data[4] && (data[4] *= this.pixelRatio)
            data[5] && (data[5] *= this.pixelRatio)
            c2d[method].apply(c2d, data)
          } else if (method === 'clip') {
            data.forEach(function (data_) {
              c2d[data_.method].apply(c2d, data_.data)
            })
            c2d.clip()
          } else {
            c2d[method].apply(c2d, data)
          }
        }
      }
      if (!this.actionsWaiting && callbackId) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: 'drawCanvas:ok'
          }
        }, this.$page.id)
      }
    },
    preloadImage: function (actions) {
      var self = this
      actions.forEach(function (action) {
        var method = action.method
        var data = action.data
        var src = ''
        if (method === 'drawImage') {
          src = data[0]
          src = self.$getRealPath(src)
          data[0] = src
        } else if (method === 'setFillStyle' && data[0] === 'pattern') {
          src = data[1]
          src = self.$getRealPath(src)
          data[1] = src
        }
        if (src && !self._images[src]) {
          loadImage()
        }
        /**
         * 加载图像
         */
        function loadImage () {
          const image = self._images[src] = new Image()
          image.onload = function () {
            image.ready = true
          }

          // 安卓 WebView 除本地路径无跨域问题
          if (__PLATFORM__ === 'app-plus' && navigator.vendor === 'Google Inc.') {
            if (src.indexOf('file://') === 0) {
              image.crossOrigin = 'anonymous'
            }
            image.src = src
            return
          }
          getSameOriginUrl(src).then(src => {
            image.src = src
          }).catch(() => {
            image.src = src
          })
        }
      })
    },
    checkImageLoaded: function (src, actions, callbackId, fn) {
      var self = this
      var image = this._images[src]
      if (image.ready) {
        fn(image)
        return true
      } else {
        this._actionsDefer.unshift([actions, true])
        this.actionsWaiting = true
        image.onload = function () {
          image.ready = true
          fn(image)
          self.actionsWaiting = false
          var actions = self._actionsDefer.slice(0)
          self._actionsDefer = []
          for (var action = actions.shift(); action;) {
            self.actionsChanged({
              actions: action[0],
              reserve: action[1],
              callbackId
            })
            action = actions.shift()
          }
        }
        return false
      }
    },
    getImageData ({
      x = 0,
      y = 0,
      width,
      height,
      destWidth,
      destHeight,
      hidpi = true,
      dataType,
      quality = 1,
      type = 'png',
      callbackId
    }) {
      const canvas = this.$refs.canvas
      let data
      const maxWidth = canvas.offsetWidth - x
      width = width ? Math.min(width, maxWidth) : maxWidth
      const maxHeight = canvas.offsetHeight - y
      height = height ? Math.min(height, maxHeight) : maxHeight
      if (!hidpi) {
        if (!destWidth && !destHeight) {
          destWidth = Math.round(width * this.pixelRatio)
          destHeight = Math.round(height * this.pixelRatio)
        } else if (!destWidth) {
          destWidth = Math.round(width / height * destHeight)
        } else if (!destHeight) {
          destHeight = Math.round(height / width * destWidth)
        }
      } else {
        destWidth = width
        destHeight = height
      }
      const newCanvas = getTempCanvas(destWidth, destHeight)
      const context = newCanvas.getContext('2d')
      if (type === 'jpeg' || type === 'jpg') {
        type = 'jpeg'
        context.fillStyle = '#fff'
        context.fillRect(0, 0, destWidth, destHeight)
      }
      context.__hidpi__ = true
      context.drawImageByCanvas(canvas, x, y, width, height, 0, 0, destWidth, destHeight, false)
      let result
      try {
        let compressed
        if (dataType === 'base64') {
          data = newCanvas.toDataURL(`image/${type}`, quality)
        } else {
          const imgData = context.getImageData(0, 0, destWidth, destHeight)
          if (__PLATFORM__ === 'app-plus') {
            const pako = require('pako')
            data = pako.deflateRaw(imgData.data, { to: 'string' })
            compressed = true
          } else {
            // fix [...]展开TypedArray在低版本手机报错的问题，使用Array.prototype.slice
            data = Array.prototype.slice.call(imgData.data)
          }
        }
        result = {
          errMsg: 'canvasGetImageData:ok',
          data,
          compressed,
          width: destWidth,
          height: destHeight
        }
      } catch (error) {
        result = {
          errMsg: `canvasGetImageData:fail ${error}`
        }
      }
      newCanvas.height = newCanvas.width = 0
      context.__hidpi__ = false
      if (!callbackId) {
        return result
      } else {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: result
        }, this.$page.id)
      }
    },
    putImageData ({
      data,
      x,
      y,
      width,
      height,
      compressed,
      callbackId
    }) {
      try {
        if (__PLATFORM__ === 'app-plus' && compressed) {
          const pako = require('pako')
          data = pako.inflateRaw(data)
        }
        if (!height) {
          height = Math.round(data.length / 4 / width)
        }
        const canvas = getTempCanvas(width, height)
        const context = canvas.getContext('2d')
        context.putImageData(new ImageData(new Uint8ClampedArray(data), width, height), 0, 0)
        this.$refs.canvas.getContext('2d').drawImage(canvas, x, y, width, height)
        canvas.height = canvas.width = 0
      } catch (error) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: 'canvasPutImageData:fail'
          }
        }, this.$page.id)
        return
      }
      UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
        callbackId,
        data: {
          errMsg: 'canvasPutImageData:ok'
        }
      }, this.$page.id)
    },
    toTempFilePath ({
      x = 0,
      y = 0,
      width,
      height,
      destWidth,
      destHeight,
      fileType,
      quality,
      dirname,
      callbackId
    }) {
      const res = this.getImageData({
        x,
        y,
        width,
        height,
        destWidth,
        destHeight,
        hidpi: false,
        dataType: 'base64',
        type: fileType,
        quality
      })
      if (!res.data || !res.data.length) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: res.errMsg.replace('canvasPutImageData', 'toTempFilePath')
          }
        }, this.$page.id)
        return
      }
      saveImage(res.data, dirname, (error, tempFilePath) => {
        let errMsg = `toTempFilePath:${error ? 'fail' : 'ok'}`
        if (error) {
          errMsg += ` ${error.message}`
        }
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg,
            tempFilePath: tempFilePath
          }
        }, this.$page.id)
      })
    }
  }
}
</script>
<style>
uni-canvas {
  width: 300px;
  height: 150px;
  display: block;
  position: relative;
}

uni-canvas > canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

<template>
  <uni-canvas
    :canvas-id="canvasId"
    :disable-scroll="disableScroll"
    v-on="_listeners"
  >
    <canvas
      ref="canvas"
      width="300"
      height="150"
    />
    <div
      style="
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
      "
    >
      <slot />
    </div>
    <v-uni-resize-sensor
      ref="sensor"
      @resize="_resize"
    />
  </uni-canvas>
</template>
<script>
import {
  subscriber
} from 'uni-mixins'

import {
  pixelRatio,
  wrapper
} from 'uni-helpers/hidpi'

import saveImage from 'uni-platform/helpers/save-image'
import { getSameOriginUrl } from 'uni-platform/helpers/file'

function resolveColor (color) {
  color = color.slice(0)
  color[3] = color[3] / 255
  return 'rgba(' + color.join(',') + ')'
}

function processTouches (target, touches) {
  return ([]).map.call(touches, (touch) => {
    var boundingClientRect = target.getBoundingClientRect()
    return {
      identifier: touch.identifier,
      x: touch.clientX - boundingClientRect.left,
      y: touch.clientY - boundingClientRect.top
    }
  })
}

var tempCanvas
function getTempCanvas (width = 0, height = 0) {
  if (!tempCanvas) {
    tempCanvas = document.createElement('canvas')
  }
  tempCanvas.width = width
  tempCanvas.height = height
  return tempCanvas
}

export default {
  name: 'Canvas',
  mixins: [subscriber],
  props: {
    canvasId: {
      type: String,
      default: ''
    },
    disableScroll: {
      type: [Boolean, String],
      default: false
    },
    hidpi: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      actionsWaiting: false
    }
  },
  computed: {
    id () {
      return this.canvasId
    },
    _listeners () {
      var $listeners = Object.assign({}, this.$listeners)
      var events = ['touchstart', 'touchmove', 'touchend']
      events.forEach(event => {
        var existing = $listeners[event]
        var eventHandler = []
        if (existing) {
          eventHandler.push(($event) => {
            this.$trigger(event, Object.assign({}, $event, {
              touches: processTouches($event.currentTarget, $event.touches),
              changedTouches: processTouches($event.currentTarget, $event
                .changedTouches)
            }))
          })
        }
        if (this.disableScroll && event === 'touchmove') {
          eventHandler.push(this._touchmove)
        }
        $listeners[event] = eventHandler
      })
      return $listeners
    },
    pixelRatio () {
      return this.hidpi ? pixelRatio : 1
    }
  },
  created () {
    this._actionsDefer = []
    this._images = {}
  },
  mounted () {
    this._resize()
  },
  beforeDestroy () {
    const canvas = this.$refs.canvas
    canvas.height = canvas.width = 0
  },
  methods: {
    _handleSubscribe ({
      type,
      data = {}
    }) {
      var method = this[type]
      if (type.indexOf('_') !== 0 && typeof method === 'function') {
        method(data)
      }
    },
    _resize (size) {
      var canvas = this.$refs.canvas
      var hasChanged = !size || (canvas.width !== Math.floor(size.width * this.pixelRatio) || canvas.height !== Math.floor(size.height * this.pixelRatio))
      if (!hasChanged) return
      if (canvas.width > 0 && canvas.height > 0) {
        var context = canvas.getContext('2d')
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height)
        wrapper(canvas, this.hidpi)
        context.putImageData(imageData, 0, 0)
      } else {
        wrapper(canvas, this.hidpi)
      }
    },
    _touchmove (event) {
      event.preventDefault()
    },
    actionsChanged ({
      actions,
      reserve,
      callbackId
    }) {
      var self = this
      if (!actions) {
        return
      }
      if (this.actionsWaiting) {
        this._actionsDefer.push([actions, reserve, callbackId])
        return
      }
      var canvas = this.$refs.canvas
      var c2d = canvas.getContext('2d')
      if (!reserve) {
        c2d.fillStyle = '#000000'
        c2d.strokeStyle = '#000000'
        c2d.shadowColor = '#000000'
        c2d.shadowBlur = 0
        c2d.shadowOffsetX = 0
        c2d.shadowOffsetY = 0
        c2d.setTransform(1, 0, 0, 1, 0, 0)
        c2d.clearRect(0, 0, canvas.width, canvas.height)
      }
      this.preloadImage(actions)
      for (let index = 0; index < actions.length; index++) {
        const action = actions[index]
        let method = action.method
        const data = action.data
        if (/^set/.test(method) && method !== 'setTransform') {
          const method1 = method[3].toLowerCase() + method.slice(4)
          let color
          if (method1 === 'fillStyle' || method1 === 'strokeStyle') {
            if (data[0] === 'normal') {
              color = resolveColor(data[1])
            } else if (data[0] === 'linear') {
              const LinearGradient = c2d.createLinearGradient(...data[1])
              data[2].forEach(function (data2) {
                const offset = data2[0]
                const color = resolveColor(data2[1])
                LinearGradient.addColorStop(offset, color)
              })
              color = LinearGradient
            } else if (data[0] === 'radial') {
              const x = data[1][0]
              const y = data[1][1]
              const r = data[1][2]
              const LinearGradient = c2d.createRadialGradient(x, y, 0, x, y, r)
              data[2].forEach(function (data2) {
                const offset = data2[0]
                const color = resolveColor(data2[1])
                LinearGradient.addColorStop(offset, color)
              })
              color = LinearGradient
            } else if (data[0] === 'pattern') {
              const loaded = this.checkImageLoaded(data[1], actions.slice(index + 1), callbackId,
                function (image) {
                  if (image) {
                    c2d[method1] = c2d.createPattern(image, data[2])
                  }
                })
              if (!loaded) {
                break
              }
              continue
            }
            c2d[method1] = color
          } else if (method1 === 'globalAlpha') {
            c2d[method1] = data[0] / 255
          } else if (method1 === 'shadow') {
            var _ = ['shadowOffsetX', 'shadowOffsetY', 'shadowBlur', 'shadowColor']
            data.forEach(function (color_, method_) {
              c2d[_[method_]] = _[method_] === 'shadowColor' ? resolveColor(color_) : color_
            })
          } else if (method1 === 'fontSize') {
            const font = c2d.__font__ || c2d.font
            c2d.__font__ = c2d.font = font.replace(/\d+\.?\d*px/, data[0] + 'px')
          } else if (method1 === 'lineDash') {
            c2d.setLineDash(data[0])
            c2d.lineDashOffset = data[1] || 0
          } else if (method1 === 'textBaseline') {
            if (data[0] === 'normal') {
              data[0] = 'alphabetic'
            }
            c2d[method1] = data[0]
          } else if (method1 === 'font') {
            c2d.__font__ = c2d.font = data[0]
          } else {
            c2d[method1] = data[0]
          }
        } else if (method === 'fillPath' || method === 'strokePath') {
          method = method.replace(/Path/, '')
          c2d.beginPath()
          data.forEach(function (data_) {
            c2d[data_.method].apply(c2d, data_.data)
          })
          c2d[method]()
        } else if (method === 'fillText') {
          c2d.fillText.apply(c2d, data)
        } else if (method === 'drawImage') {
          var A = (function () {
            var dataArray = [...data]
            var url = dataArray[0]
            var otherData = dataArray.slice(1)
            self._images = self._images || {}
            if (!self.checkImageLoaded(url, actions.slice(index + 1), callbackId, function (
              image) {
              if (image) {
                c2d.drawImage.apply(c2d, [image].concat([...otherData.slice(4, 8)],
                  [...otherData.slice(0, 4)]))
              }
            })) return 'break'
          }())
          if (A === 'break') {
            break
          }
        } else {
          if (method === 'clip') {
            data.forEach(function (data_) {
              c2d[data_.method].apply(c2d, data_.data)
            })
            c2d.clip()
          } else {
            c2d[method].apply(c2d, data)
          }
        }
      }
      if (!this.actionsWaiting && callbackId) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: 'drawCanvas:ok'
          }
        }, this.$page.id)
      }
    },
    preloadImage: function (actions) {
      var self = this
      actions.forEach(function (action) {
        var method = action.method
        var data = action.data
        var src = ''
        if (method === 'drawImage') {
          src = data[0]
          src = self.$getRealPath(src)
          data[0] = src
        } else if (method === 'setFillStyle' && data[0] === 'pattern') {
          src = data[1]
          src = self.$getRealPath(src)
          data[1] = src
        }
        if (src && !self._images[src]) {
          loadImage()
        }
        /**
         * 加载图像
         */
        function loadImage () {
          const image = self._images[src] = new Image()
          image.onload = function () {
            image.ready = true
          }

          // 安卓 WebView 除本地路径无跨域问题
          if (__PLATFORM__ === 'app-plus' && navigator.vendor === 'Google Inc.') {
            if (src.indexOf('file://') === 0) {
              image.crossOrigin = 'anonymous'
            }
            image.src = src
            return
          }
          getSameOriginUrl(src).then(src => {
            image.src = src
          }).catch(() => {
            image.src = src
          })
        }
      })
    },
    checkImageLoaded: function (src, actions, callbackId, fn) {
      var self = this
      var image = this._images[src]
      if (image.ready) {
        fn(image)
        return true
      } else {
        this._actionsDefer.unshift([actions, true])
        this.actionsWaiting = true
        image.onload = function () {
          image.ready = true
          fn(image)
          self.actionsWaiting = false
          var actions = self._actionsDefer.slice(0)
          self._actionsDefer = []
          for (var action = actions.shift(); action;) {
            self.actionsChanged({
              actions: action[0],
              reserve: action[1],
              callbackId
            })
            action = actions.shift()
          }
        }
        return false
      }
    },
    getImageData ({
      x = 0,
      y = 0,
      width,
      height,
      destWidth,
      destHeight,
      hidpi = true,
      dataType,
      quality = 1,
      type = 'png',
      callbackId
    }) {
      const canvas = this.$refs.canvas
      let data
      const maxWidth = canvas.offsetWidth - x
      width = width ? Math.min(width, maxWidth) : maxWidth
      const maxHeight = canvas.offsetHeight - y
      height = height ? Math.min(height, maxHeight) : maxHeight
      if (!hidpi) {
        if (!destWidth && !destHeight) {
          destWidth = Math.round(width * this.pixelRatio)
          destHeight = Math.round(height * this.pixelRatio)
        } else if (!destWidth) {
          destWidth = Math.round(width / height * destHeight)
        } else if (!destHeight) {
          destHeight = Math.round(height / width * destWidth)
        }
      } else {
        destWidth = width
        destHeight = height
      }
      const newCanvas = getTempCanvas(destWidth, destHeight)
      const context = newCanvas.getContext('2d')
      if (type === 'jpeg' || type === 'jpg') {
        type = 'jpeg'
        context.fillStyle = '#fff'
        context.fillRect(0, 0, destWidth, destHeight)
      }
      context.__hidpi__ = true
      context.drawImageByCanvas(canvas, x, y, width, height, 0, 0, destWidth, destHeight, false)
      let result
      try {
        let compressed
        if (dataType === 'base64') {
          data = newCanvas.toDataURL(`image/${type}`, quality)
        } else {
          const imgData = context.getImageData(0, 0, destWidth, destHeight)
          if (__PLATFORM__ === 'app-plus') {
            const pako = require('pako')
            data = pako.deflateRaw(imgData.data, { to: 'string' })
            compressed = true
          } else {
            // fix [...]展开TypedArray在低版本手机报错的问题，使用Array.prototype.slice
            data = Array.prototype.slice.call(imgData.data)
          }
        }
        result = {
          errMsg: 'canvasGetImageData:ok',
          data,
          compressed,
          width: destWidth,
          height: destHeight
        }
      } catch (error) {
        result = {
          errMsg: `canvasGetImageData:fail ${error}`
        }
      }
      newCanvas.height = newCanvas.width = 0
      context.__hidpi__ = false
      if (!callbackId) {
        return result
      } else {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: result
        }, this.$page.id)
      }
    },
    putImageData ({
      data,
      x,
      y,
      width,
      height,
      compressed,
      callbackId
    }) {
      try {
        if (__PLATFORM__ === 'app-plus' && compressed) {
          const pako = require('pako')
          data = pako.inflateRaw(data)
        }
        if (!height) {
          height = Math.round(data.length / 4 / width)
        }
        const canvas = getTempCanvas(width, height)
        const context = canvas.getContext('2d')
        context.putImageData(new ImageData(new Uint8ClampedArray(data), width, height), 0, 0)
        this.$refs.canvas.getContext('2d').drawImage(canvas, x, y, width, height)
        canvas.height = canvas.width = 0
      } catch (error) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: 'canvasPutImageData:fail'
          }
        }, this.$page.id)
        return
      }
      UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
        callbackId,
        data: {
          errMsg: 'canvasPutImageData:ok'
        }
      }, this.$page.id)
    },
    toTempFilePath ({
      x = 0,
      y = 0,
      width,
      height,
      destWidth,
      destHeight,
      fileType,
      quality,
      dirname,
      callbackId
    }) {
      const res = this.getImageData({
        x,
        y,
        width,
        height,
        destWidth,
        destHeight,
        hidpi: false,
        dataType: 'base64',
        type: fileType,
        quality
      })
      if (!res.data || !res.data.length) {
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg: res.errMsg.replace('canvasPutImageData', 'toTempFilePath')
          }
        }, this.$page.id)
        return
      }
      saveImage(res.data, dirname, (error, tempFilePath) => {
        let errMsg = `toTempFilePath:${error ? 'fail' : 'ok'}`
        if (error) {
          errMsg += ` ${error.message}`
        }
        UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
          callbackId,
          data: {
            errMsg,
            tempFilePath: tempFilePath
          }
        }, this.$page.id)
      })
    }
  }
}
</script>
<style>
uni-canvas {
  width: 300px;
  height: 150px;
  display: block;
  position: relative;
}

uni-canvas > canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>