github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/views/Dashboard/editor/TabCardItem.vue (about)

     1  <script setup lang="ts">
     2  import {computed, onMounted, onUnmounted, PropType, ref, watch} from 'vue'
     3  import {useI18n} from '@/hooks/web/useI18n'
     4  import {Card, CardItem, Core, eventBus, requestCurrentState} from "@/views/Dashboard/core";
     5  import {CardEditorName, CardItemList} from "@/views/Dashboard/card_items";
     6  import {JsonViewer} from "@/components/JsonViewer";
     7  import {
     8    ElButton,
     9    ElCascader,
    10    ElCol,
    11    ElCollapse,
    12    ElCollapseItem,
    13    ElDivider,
    14    ElEmpty,
    15    ElForm,
    16    ElFormItem,
    17    ElInput,
    18    ElMessage,
    19    ElPopconfirm,
    20    ElRow,
    21  } from 'element-plus'
    22  import {JsonEditor} from "@/components/JsonEditor";
    23  import {Dialog} from "@/components/Dialog";
    24  import {ApiDashboardCardItem} from "@/api/stub";
    25  
    26  const {t} = useI18n()
    27  
    28  const cardItem = ref<CardItem>(null)
    29  // const card = ref<Card>({} as Card)
    30  const itemTypes = CardItemList;
    31  const itemProps = {
    32    expandTrigger: 'hover' as const,
    33  }
    34  const cardItemType = computed(() => cardItem.value?.type)
    35  const handleTypeChanged = (value: string[]) => {
    36    cardItem.value.type = value[value.length - 1]
    37  }
    38  
    39  const props = defineProps({
    40    core: {
    41      type: Object as PropType<Core>,
    42    },
    43    card: {
    44      type: Object as PropType<Nullable<Card>>,
    45      default: () => null
    46    },
    47  })
    48  
    49  const activeCard = computed({
    50    get(): Card {
    51      return props.card as Card
    52    },
    53    set(val: Card) {
    54    }
    55  })
    56  
    57  const currentCore = computed({
    58    get(): Core {
    59      return props.core as Core
    60    },
    61    set(val: Core) {
    62    }
    63  })
    64  
    65  watch(
    66      () => props.card,
    67      (val?: Card) => {
    68        if (!val) return
    69        activeCard.value = val
    70        if (val?.selectedItem > -1) {
    71          cardItem.value = val?.items[val?.selectedItem] || null
    72        } else {
    73          cardItem.value = null
    74        }
    75  
    76      },
    77      {
    78        deep: true,
    79        immediate: true
    80      }
    81  )
    82  
    83  // ---------------------------------
    84  // import/export
    85  // ---------------------------------
    86  
    87  const addCardItem = () => {
    88    currentCore.value.createCardItem(undefined, 'text');
    89  }
    90  
    91  const removeCardItem = (index: number) => {
    92    currentCore.value.removeCardItem(index);
    93  }
    94  
    95  const duplicate = () => {
    96    activeCard.value.copyItem(activeCard.value.selectedItem);
    97  }
    98  
    99  const getCardEditorName = (name: string) => {
   100    return CardEditorName(name);
   101  }
   102  
   103  const updateCardItem = async () => {
   104    const {data} = await currentCore.value.updateCard();
   105  
   106    if (data) {
   107      ElMessage({
   108        title: t('Success'),
   109        message: t('message.updatedSuccessfully'),
   110        type: 'success',
   111        duration: 2000
   112      });
   113    }
   114  }
   115  
   116  const updateCurrentState = () => {
   117    if (cardItem.value.entityId) {
   118      requestCurrentState(cardItem.value?.entityId)
   119    }
   120  }
   121  
   122  const eventHandler = (event: string, args: any[]) => {
   123    switch (event) {
   124      case 'showCardItemImportDialog':
   125        cardIdForImport.value = args
   126        importDialogVisible.value = true
   127        break;
   128      case 'showCardItemExportDialog':
   129        showExportDialog(args)
   130        break;
   131    }
   132  }
   133  const cardIdForImport = ref<number>()
   134  onMounted(() => {
   135    eventBus.subscribe(['showCardItemImportDialog', 'showCardItemExportDialog'], eventHandler)
   136  })
   137  
   138  onUnmounted(() => {
   139    eventBus.unsubscribe(['showCardItemImportDialog', 'showCardItemExportDialog'], eventHandler)
   140  })
   141  
   142  // ---------------------------------
   143  // import/export
   144  // ---------------------------------
   145  
   146  const dialogSource = ref({})
   147  const importDialogVisible = ref(false)
   148  const exportDialogVisible = ref(false)
   149  const importedCardItem = ref(null)
   150  
   151  const prepareForExport = (cardItemId?: number) => {
   152    if (currentCore.value.activeCard == undefined) {
   153      return;
   154    }
   155    dialogSource.value = currentCore.value.serializeCardItem(cardItemId)
   156  }
   157  
   158  const showExportDialog = (cardItemId?: number) => {
   159    prepareForExport(cardItemId)
   160    exportDialogVisible.value = true
   161  }
   162  
   163  const importHandler = (val: any) => {
   164    if (importedCardItem.value == val) {
   165      return
   166    }
   167    importedCardItem.value = val
   168  }
   169  
   170  const importCardItem = async () => {
   171    let cardItem: ApiDashboardCardItem
   172    try {
   173      if (importedCardItem.value?.json) {
   174        cardItem = importedCardItem.value.json as ApiDashboardCardItem;
   175      } else if (importedCardItem.value.text) {
   176        cardItem = JSON.parse(importedCardItem.value.text) as ApiDashboardCardItem;
   177      }
   178    } catch {
   179      ElMessage({
   180        title: t('Error'),
   181        message: t('message.corruptedJsonFormat'),
   182        type: 'error',
   183        duration: 2000
   184      });
   185      return
   186    }
   187    const res = await currentCore.value.importCardItem(cardIdForImport.value, cardItem);
   188    if (res) {
   189      cardIdForImport.value = undefined
   190      ElMessage({
   191        title: t('Success'),
   192        message: t('message.importedSuccessful'),
   193        type: 'success',
   194        duration: 2000
   195      })
   196    }
   197    importDialogVisible.value = false
   198  }
   199  
   200  
   201  </script>
   202  
   203  <template>
   204  
   205    <ElRow class="mb-10px" v-if="activeCard.selectedItem !== -1">
   206      <ElCol>
   207        <ElDivider content-position="left">{{ $t('dashboard.cardItemOptions') }}</ElDivider>
   208      </ElCol>
   209    </ElRow>
   210  
   211    <ElForm
   212        v-if="cardItem"
   213        :model="cardItem"
   214        label-position="top"
   215        style="width: 100%"
   216        ref="cardItemForm"
   217    >
   218  
   219      <ElRow>
   220        <ElCol>
   221  
   222          <ElFormItem :label="$t('dashboard.editor.type')" prop="type">
   223            <ElCascader
   224                v-model="cardItemType"
   225                :options="itemTypes"
   226                :props="itemProps"
   227                :placeholder="$t('dashboard.editor.pleaseSelectType')"
   228                style="width: 100%"
   229                @change="handleTypeChanged"
   230            />
   231          </ElFormItem>
   232        </ElCol>
   233      </ElRow>
   234  
   235      <ElRow>
   236        <ElCol>
   237          <ElFormItem :label="$t('dashboard.editor.title')" prop="title">
   238            <ElInput v-model="cardItem.title"/>
   239          </ElFormItem>
   240        </ElCol>
   241  
   242      </ElRow>
   243  
   244      <component
   245          :is="getCardEditorName(cardItem.type)"
   246          :core="core"
   247          :item="cardItem"
   248      />
   249    </ElForm>
   250  
   251    <ElEmpty v-if="!activeCard.items.length || activeCard.selectedItem === -1" :rows="5" class="mt-20px mb-20px"
   252             description="Select card item or">
   253      <ElButton type="primary" @click="addCardItem()">
   254        {{ t('dashboard.editor.addNewCardItem') }}
   255      </ElButton>
   256      <ElButton type="primary" @click="importDialogVisible = true">
   257        {{ t('main.import') }}
   258      </ElButton>
   259    </ElEmpty>
   260  
   261    <ElRow class="mb-10px mt-10px" v-if="activeCard.selectedItem > -1 && cardItem.entity">
   262      <ElCol>
   263        <ElCollapse>
   264          <ElCollapseItem :title="$t('dashboard.editor.eventstateJSONobject')">
   265            <ElButton class="mb-10px w-[100%]" @click.prevent.stop="updateCurrentState()">
   266              <Icon icon="ep:refresh" class="mr-5px"/>
   267              {{ $t('dashboard.editor.getEvent') }}
   268            </ElButton>
   269            <JsonViewer v-model="cardItem.lastEvent"/>
   270          </ElCollapseItem>
   271        </ElCollapse>
   272      </ElCol>
   273    </ElRow>
   274  
   275    <ElRow v-if="activeCard.selectedItem > -1" class="mb-10px">
   276      <ElCol>
   277        <ElDivider class="mb-10px" content-position="left">{{ $t('main.actions') }}</ElDivider>
   278      </ElCol>
   279    </ElRow>
   280  
   281    <div v-if="activeCard.selectedItem > -1" class="text-right">
   282  
   283      <ElButton type="primary" @click.prevent.stop="updateCardItem" plain>{{
   284          $t('main.update')
   285        }}
   286      </ElButton>
   287  
   288      <ElButton @click.prevent.stop="duplicate">{{ $t('main.duplicate') }}</ElButton>
   289  
   290      <ElPopconfirm
   291          :confirm-button-text="$t('main.ok')"
   292          :cancel-button-text="$t('main.no')"
   293          width="250"
   294          style="margin-left: 10px;"
   295          :title="$t('main.are_you_sure_to_do_want_this?')"
   296          @confirm="removeCardItem(activeCard.selectedItem)"
   297      >
   298        <template #reference>
   299          <ElButton type="danger" plain>
   300            <Icon icon="ep:delete" class="mr-5px"/>
   301            {{ t('main.remove') }}
   302          </ElButton>
   303        </template>
   304      </ElPopconfirm>
   305    </div>
   306  
   307    <!-- export dialog -->
   308    <Dialog v-model="exportDialogVisible" :title="t('main.dialogExportTitle')" :maxHeight="400" width="80%">
   309      <JsonViewer v-model="dialogSource"/>
   310    </Dialog>
   311    <!-- /export dialog -->
   312  
   313    <!-- import dialog -->
   314    <Dialog v-model="importDialogVisible" :title="t('main.dialogImportTitle')" :maxHeight="400" width="80%"
   315            custom-class>
   316      <JsonEditor @change="importHandler"/>
   317      <template #footer>
   318        <ElButton type="primary" @click="importCardItem()" plain>{{ t('main.import') }}</ElButton>
   319        <ElButton @click="importDialogVisible = false">{{ t('main.closeDialog') }}</ElButton>
   320      </template>
   321    </Dialog>
   322    <!-- /import dialog -->
   323  
   324  </template>
   325  
   326  <style lang="less">
   327  
   328  </style>