|
| 1 | +<template> |
| 2 | + <nut-form-item :prop="prop"> |
| 3 | + <template #label> |
| 4 | + <span class="image-fit-label"> |
| 5 | + {{ labelText }} |
| 6 | + <nut-icon |
| 7 | + name="tips" |
| 8 | + size="14" |
| 9 | + role="button" |
| 10 | + :aria-label="$t(`imageFit.tips.title`)" |
| 11 | + @click.stop="openTips" |
| 12 | + /> |
| 13 | + </span> |
| 14 | + </template> |
| 15 | + <nut-input |
| 16 | + :model-value="currentText" |
| 17 | + class="nut-input-text" |
| 18 | + :border="false" |
| 19 | + input-align="right" |
| 20 | + readonly |
| 21 | + right-icon="rect-right" |
| 22 | + @click="openPicker" |
| 23 | + @click-right-icon="openPicker" |
| 24 | + /> |
| 25 | + </nut-form-item> |
| 26 | + <DesktopPicker |
| 27 | + v-model="pickerValue" |
| 28 | + v-model:visible="showPicker" |
| 29 | + :columns="columns" |
| 30 | + :title="labelText" |
| 31 | + :cancel-text="$t(`editorPage.subConfig.sourceNamePicker.cancel`)" |
| 32 | + :ok-text="$t(`editorPage.subConfig.sourceNamePicker.confirm`)" |
| 33 | + @confirm="confirm" |
| 34 | + /> |
| 35 | +</template> |
| 36 | + |
| 37 | +<script setup lang="ts"> |
| 38 | +import { Dialog } from "@nutui/nutui"; |
| 39 | +import { computed, ref, watch } from "vue"; |
| 40 | +import { useI18n } from "vue-i18n"; |
| 41 | +
|
| 42 | +import DesktopPicker from "@/components/DesktopPicker.vue"; |
| 43 | +import { IMAGE_FIT_OPTIONS, isImageFit, type ImageFit } from "@/utils/iconFit"; |
| 44 | +
|
| 45 | +const INHERIT_IMAGE_FIT = "__inherit__"; |
| 46 | +type ImageFitPickerValue = ImageFit | typeof INHERIT_IMAGE_FIT; |
| 47 | +
|
| 48 | +const props = withDefaults( |
| 49 | + defineProps<{ |
| 50 | + modelValue?: ImageFit | string | null; |
| 51 | + fallbackValue?: ImageFit | string | null; |
| 52 | + label?: string; |
| 53 | + prop?: string; |
| 54 | + }>(), |
| 55 | + { |
| 56 | + label: "", |
| 57 | + prop: "iconFit", |
| 58 | + }, |
| 59 | +); |
| 60 | +
|
| 61 | +const emit = defineEmits<{ |
| 62 | + (event: "update:modelValue", value: ImageFit | undefined): void; |
| 63 | + (event: "change", value: ImageFit | undefined): void; |
| 64 | +}>(); |
| 65 | +
|
| 66 | +const { t } = useI18n(); |
| 67 | +const showPicker = ref(false); |
| 68 | +const labelText = computed(() => props.label || t("editorPage.subConfig.basic.iconFit.label")); |
| 69 | +const currentPickerValue = computed<ImageFitPickerValue>(() => { |
| 70 | + return isImageFit(props.modelValue) ? props.modelValue : INHERIT_IMAGE_FIT; |
| 71 | +}); |
| 72 | +const currentText = computed(() => { |
| 73 | + return currentPickerValue.value === INHERIT_IMAGE_FIT |
| 74 | + ? t("imageFit.inherit") |
| 75 | + : t(`imageFit.${currentPickerValue.value}`); |
| 76 | +}); |
| 77 | +const columns = computed(() => { |
| 78 | + return [ |
| 79 | + { |
| 80 | + text: t("imageFit.inherit"), |
| 81 | + value: INHERIT_IMAGE_FIT, |
| 82 | + }, |
| 83 | + ...IMAGE_FIT_OPTIONS.map((value) => ({ |
| 84 | + text: t(`imageFit.${value}`), |
| 85 | + value, |
| 86 | + })), |
| 87 | + ]; |
| 88 | +}); |
| 89 | +const pickerValue = ref<ImageFitPickerValue[]>([currentPickerValue.value]); |
| 90 | +watch(currentPickerValue, (value) => { |
| 91 | + if (!showPicker.value) pickerValue.value = [value]; |
| 92 | +}); |
| 93 | +watch(showPicker, (visible) => { |
| 94 | + if (visible) pickerValue.value = [currentPickerValue.value]; |
| 95 | +}); |
| 96 | +const openPicker = () => { |
| 97 | + pickerValue.value = [currentPickerValue.value]; |
| 98 | + showPicker.value = true; |
| 99 | +}; |
| 100 | +const confirm = ({ selectedValue }: { selectedValue?: unknown[] }) => { |
| 101 | + const selected = selectedValue?.[0]; |
| 102 | + const next = isImageFit(selected) ? selected : undefined; |
| 103 | + emit("update:modelValue", next); |
| 104 | + emit("change", next); |
| 105 | +}; |
| 106 | +const openTips = () => { |
| 107 | + Dialog({ |
| 108 | + title: t("imageFit.tips.title"), |
| 109 | + content: t("imageFit.tips.content"), |
| 110 | + popClass: "auto-dialog image-fit-tips-dialog", |
| 111 | + noCancelBtn: true, |
| 112 | + okText: t("imageFit.tips.close"), |
| 113 | + closeOnClickOverlay: true, |
| 114 | + closeOnPopstate: true, |
| 115 | + }); |
| 116 | +}; |
| 117 | +</script> |
| 118 | + |
| 119 | +<style scoped lang="scss"> |
| 120 | +.image-fit-label { |
| 121 | + display: inline-flex; |
| 122 | + align-items: center; |
| 123 | + gap: 4px; |
| 124 | +
|
| 125 | + :deep(.nut-icon-tips) { |
| 126 | + color: var(--second-text-color); |
| 127 | + } |
| 128 | +} |
| 129 | +</style> |
0 commit comments