Files
gao-customer-h5/src/components/PhotoField.vue
T

167 lines
2.8 KiB
Vue

<template>
<div class="photo-field" :class="{ compact: size === 'compact' }">
<t-button
v-if="!modelValue"
theme="primary"
variant="dashed"
class="add"
:loading="loading"
:disabled="loading"
@click="$emit('add')"
>
<span v-text="addText" />
</t-button>
<div v-else class="card">
<t-button variant="text" class="preview-trigger" @click="handlePhotoClick">
<t-image
class="image"
:src="modelValue"
:alt="altText"
fit="cover"
/>
</t-button>
<div v-if="loading" class="mask">
<t-loading size="1.5rem" text="上传中" />
</div>
<t-button
v-if="showRemove && !loading"
class="remove"
theme="danger"
shape="circle"
size="extra-small"
@click="clearPhoto"
>
<t-icon name="close" size="1rem" />
</t-button>
</div>
<teleport to="body">
<t-image-viewer
v-model:visible="photoViewerVisible"
:images="photoViewerImages"
show-index
/>
</teleport>
</div>
</template>
<script setup lang="ts">
interface Props {
addText?: string;
altText?: string;
modelValue: string;
loading?: boolean;
preview?: boolean;
showRemove?: boolean;
size?: "normal" | "compact";
}
const props = withDefaults(defineProps<Props>(), {
addText: "拍照",
altText: "照片",
loading: false,
preview: true,
showRemove: true,
size: "normal"
});
defineOptions({
name: "PhotoField"
});
const emit = defineEmits<{
add: [];
clear: [];
"update:modelValue": [value: string];
}>();
const photoViewerVisible = ref(false);
const photoViewerImages = computed(() => props.modelValue ? [props.modelValue] : []);
function handlePhotoClick(): void {
if (!props.preview) {
emit("add");
return;
}
photoViewerVisible.value = true;
}
function clearPhoto(): void {
emit("update:modelValue", "");
emit("clear");
photoViewerVisible.value = false;
}
</script>
<style scoped lang="less">
.photo-field {
gap: .75rem;
display: grid;
.add {
--td-button-border-radius: .875rem;
width: 5rem;
height: 5rem;
padding: 0;
}
.card {
width: 5rem;
height: 5rem;
position: relative;
overflow: hidden;
border-radius: .875rem;
}
.preview-trigger {
padding: 0;
border: 0;
width: 100%;
height: 100%;
display: block;
background: transparent;
border-radius: .875rem;
}
.image {
width: 100%;
height: 100%;
display: block;
border-radius: .875rem;
}
.remove {
top: .375rem;
right: .375rem;
position: absolute;
:deep(.t-button__content) {
display: flex;
align-items: center;
}
}
.mask {
inset: 0;
gap: .375rem;
z-index: 1;
color: #fff;
display: flex;
position: absolute;
align-items: center;
flex-direction: column;
justify-content: center;
background: rgba(15, 23, 42, .45);
}
&.compact {
.add,
.card {
width: 4rem;
height: 4rem;
}
}
}
</style>