Jonathan
No notes.
Jonathan
<script setup> import SelectInput from '@/components/form/SelectInput.vue'; const optionList = [ { text: '姓名', value: 'name' } ] const searchParams = ref({ type: 'name' input: '' }); </script> <template> <SelectInput v-model="params" :option-list="optionList" @change="changeHandler" /> </template>
<script setup> import SelectInput from '@/components/form/SelectInput.vue'; const optionList = [ { text: '姓名', value: 'name' } ] const searchParams = ref({ type: 'name' input: '' }); </script> <template> <SelectInput v-model="params" :option-list="optionList" @change="changeHandler" /> </template>
SelectInput ┣ Select ┗ Input
SelectInput ┣ Select ┗ Input
<template> <div class="d-flex custom-select-input" > <Select class="custom-select-input--select" :value="props.value.type" :items="props.optionList" item-text="text" item-value="value" :hide-details="true" @change="(value) => handleSelectChange(value)" /> <Input :value="props.value.input" type="text" class="custom-select-input--text" clear-icon="mdi-close-circle" clearable :hide-details="true" :placeholder="$t('TEAMMATE.PLEASE_SEARCH')" @change="(value) => handleChange({ key:'input', value })" /> </div> </template> <style lang="scss" scoped> .custom-select-input { height: 32px; border: 1px solid $neutral-cold-053; border-radius: 4px; box-sizing: border-box; &:has(:focus) { transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); border-color: $primary-001; box-shadow: 0px 0px 0px 2px rgba(64, 143, 255, 0.25); } } ... </style>
<template> <div class="d-flex custom-select-input" > <Select class="custom-select-input--select" :value="props.value.type" :items="props.optionList" item-text="text" item-value="value" :hide-details="true" @change="(value) => handleSelectChange(value)" /> <Input :value="props.value.input" type="text" class="custom-select-input--text" clear-icon="mdi-close-circle" clearable :hide-details="true" :placeholder="$t('TEAMMATE.PLEASE_SEARCH')" @change="(value) => handleChange({ key:'input', value })" /> </div> </template> <style lang="scss" scoped> .custom-select-input { height: 32px; border: 1px solid $neutral-cold-053; border-radius: 4px; box-sizing: border-box; &:has(:focus) { transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); border-color: $primary-001; box-shadow: 0px 0px 0px 2px rgba(64, 143, 255, 0.25); } } ... </style>
<script setup> import FilePreviewer from '@/components/filePreviewer/FilePreviewer.vue'; const filePath = ref('') const height = ref(200) const showDownloadButton = ref(false) </script> <template> <FilePreviewer :file-path="filePath" :height="height" :show-download-button="showDownloadButton" /> </template>
<script setup> import FilePreviewer from '@/components/filePreviewer/FilePreviewer.vue'; const filePath = ref('') const height = ref(200) const showDownloadButton = ref(false) </script> <template> <FilePreviewer :file-path="filePath" :height="height" :show-download-button="showDownloadButton" /> </template>
FilePreviewer ┣ ImagePreviewer.vue ┃ ┣ ViewerContainer ┃ ┗ LightboxDialog ┗ PdfPreviewer.vue ┗ ViewerContainer
FilePreviewer ┣ ImagePreviewer.vue ┃ ┣ ViewerContainer ┃ ┗ LightboxDialog ┗ PdfPreviewer.vue ┗ ViewerContainer
<script setup> // ... const isPdfFile = computed(() => props.filePath.endsWith('.pdf')); </script> <template> <div> <PdfPreviewer v-if="isPdfFile" v-bind="$props" /> <ImagePreviewer v-else v-on="$listeners" v-bind="$props" /> </div> </template>
<script setup> // ... const isPdfFile = computed(() => props.filePath.endsWith('.pdf')); </script> <template> <div> <PdfPreviewer v-if="isPdfFile" v-bind="$props" /> <ImagePreviewer v-else v-on="$listeners" v-bind="$props" /> </div> </template>
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
<script setup> const showLightbox = ref(false); const actionList = computed(() => ([ { type: 'expand', icon: 'expand', action: () => { showLightbox.value = true; } }, { type: 'download', icon: 'export_download', action: () => { downloadImageFromUrl(props.filePath); } } ])); </script> <template> <ViewerContainer :action-list="actionList"> <template #previewPanel> <img :src="props.filePath"> </template> </ViewerContainer> <LightboxDialog :file-path="props.filePath" :display="showLightbox" @close="showLightbox = false" /> </template>
<script setup> const showLightbox = ref(false); const actionList = computed(() => ([ { type: 'expand', icon: 'expand', action: () => { showLightbox.value = true; } }, { type: 'download', icon: 'export_download', action: () => { downloadImageFromUrl(props.filePath); } } ])); </script> <template> <ViewerContainer :action-list="actionList"> <template #previewPanel> <img :src="props.filePath"> </template> </ViewerContainer> <LightboxDialog :file-path="props.filePath" :display="showLightbox" @close="showLightbox = false" /> </template>
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
<template> <v-sheet class="viewer-container" :style="{'--height': `${props.height}px`}" width="100%" height="100%" > <div class="action-panel" v-if="hasActionList" > <SvgIcon v-for="action in props.actionList" class="pointer-cursor" :key="action.type" :icon-class="action.icon" size="32" color="#FFFFFF" @click="action.action" /> </div> <div class="preview-panel"> <slot name="previewPanel" /> </div> </v-sheet> </template>
<template> <v-sheet class="viewer-container" :style="{'--height': `${props.height}px`}" width="100%" height="100%" > <div class="action-panel" v-if="hasActionList" > <SvgIcon v-for="action in props.actionList" class="pointer-cursor" :key="action.type" :icon-class="action.icon" size="32" color="#FFFFFF" @click="action.action" /> </div> <div class="preview-panel"> <slot name="previewPanel" /> </div> </v-sheet> </template>
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
FilePreviewer ┗ ImagePreviewer.vue ┣ ViewerContainer ┗ LightboxDialog
<template> <v-dialog class="lightbox-dialog" :value="props.display" fullscreen hide-overlay @keydown="close" > <div class="mask" /> <div class="action-container"> <div class="action-btn" @click="downloadImageFromUrl(props.filePath)" > <SvgIcon v-if="props.showDownloadButton" icon-class="export_download" size="40" color="#FFFFFF" /> </div> <div class="action-btn" @click="close" > <SvgIcon class="pointer-cursor" icon-class="cancel" size="40" color="#FFFFFF" /> </div> </div> <div class="preview-img"> <img :src="props.filePath" alt="preview" width="100%" height="100%" > </div> </v-dialog> </template>
<template> <v-dialog class="lightbox-dialog" :value="props.display" fullscreen hide-overlay @keydown="close" > <div class="mask" /> <div class="action-container"> <div class="action-btn" @click="downloadImageFromUrl(props.filePath)" > <SvgIcon v-if="props.showDownloadButton" icon-class="export_download" size="40" color="#FFFFFF" /> </div> <div class="action-btn" @click="close" > <SvgIcon class="pointer-cursor" icon-class="cancel" size="40" color="#FFFFFF" /> </div> </div> <div class="preview-img"> <img :src="props.filePath" alt="preview" width="100%" height="100%" > </div> </v-dialog> </template>
FilePreviewer ┗ PdfPreviewer.vue ┗ ViewerContainer
FilePreviewer ┗ PdfPreviewer.vue ┗ ViewerContainer
<script setup> import { pdfjs } from '@/utils/pdfjs.js'; const getDocument = async () => { try { if (!props.filePath) { return; } isLoading.value = true; const file = await pdfjs.getDocument(props.filePath).promise; pdfDocument.value = file; const page = await file.getPage(1); const viewport = page.getViewport({ scale: 1 }); const context = pdfContainer.value.getContext('2d'); pdfContainer.value.height = viewport.height; pdfContainer.value.width = viewport.width; page.render({ canvasContext: context, viewport }); } catch (error) { console.error('pdf read error:', error); } finally { isLoading.value = false; } }; </script> <template> <div> <ViewerContainer :action-list="actionList"> <template #previewPanel> <canvas ref="pdfContainer"/> </template> </ViewerContainer> </div> </template>
<script setup> import { pdfjs } from '@/utils/pdfjs.js'; const getDocument = async () => { try { if (!props.filePath) { return; } isLoading.value = true; const file = await pdfjs.getDocument(props.filePath).promise; pdfDocument.value = file; const page = await file.getPage(1); const viewport = page.getViewport({ scale: 1 }); const context = pdfContainer.value.getContext('2d'); pdfContainer.value.height = viewport.height; pdfContainer.value.width = viewport.width; page.render({ canvasContext: context, viewport }); } catch (error) { console.error('pdf read error:', error); } finally { isLoading.value = false; } }; </script> <template> <div> <ViewerContainer :action-list="actionList"> <template #previewPanel> <canvas ref="pdfContainer"/> </template> </ViewerContainer> </div> </template>
import * as pdfjs from 'pdfjs-dist'; pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.js', import.meta.url ); export { pdfjs };
import * as pdfjs from 'pdfjs-dist'; pdfjs.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.js', import.meta.url ); export { pdfjs };
import { computed } from 'vue'; export function useRouteHash() { const route = useRoute(); const hash = computed(() => route.hash); const hashValue = computed(() => route.hash.replace('#', '')); const updateUrlHash = (value) => { window.history.pushState( null, null, `${route.path}#${value}` ); }; return { hash, hashValue, updateUrlHash }; }
import { computed } from 'vue'; export function useRouteHash() { const route = useRoute(); const hash = computed(() => route.hash); const hashValue = computed(() => route.hash.replace('#', '')); const updateUrlHash = (value) => { window.history.pushState( null, null, `${route.path}#${value}` ); }; return { hash, hashValue, updateUrlHash }; }
<script setup> import { useRouteHash } from '@/components/composables/useRouteHash'; const { hash, updateUrlHash } = useRouteHash(); const breadcrumbs = computed(() => [ { text: 'WhatsApp Flows', to: { name: 'whatsAppFlows', hash: hash.value } // ... } ]); const handleWabaIdChange = (id) => { updateUrlHash(id) } </script>
<script setup> import { useRouteHash } from '@/components/composables/useRouteHash'; const { hash, updateUrlHash } = useRouteHash(); const breadcrumbs = computed(() => [ { text: 'WhatsApp Flows', to: { name: 'whatsAppFlows', hash: hash.value } // ... } ]); const handleWabaIdChange = (id) => { updateUrlHash(id) } </script>
yarn lang:sync-cli
yarn lang:sync-cli
✔ Pick a tab › 通訊渠道|Channels
✔ Pick a tab › 通訊渠道|Channels
✔ Pick a feature › WhatsApp Flow
✔ Pick a feature › WhatsApp Flow
✔ Input i18n key … flow
✔ Input i18n key … flow
✔ Ready to sync i18n? … no / yes
✔ Ready to sync i18n? … no / yes
{ "FLOW": { "KEY0": "測試用0", "KEY1": "測試用1", "KEY2": "測試用2", "KEY3": "測試用3", "KEY4": "測試用4", "KEY5": "測試用5" } }
{ "FLOW": { "KEY0": "測試用0", "KEY1": "測試用1", "KEY2": "測試用2", "KEY3": "測試用3", "KEY4": "測試用4", "KEY5": "測試用5" } }