Files
gao-customer-h5/src/components/mediaSelector/MediaSelectorAdd.vue
T
2026-09-19 13:08:44 +08:00

211 lines
4.0 KiB
Vue

<template>
<div class="add-trigger">
<t-popover
v-model:visible="capturePopoverVisible"
placement="left"
theme="light"
>
<t-button
class="capture"
theme="primary"
variant="dashed"
:loading="busy"
:disabled="busy"
>
<template #icon>
<t-icon name="camera" />
</template>
拍摄
</t-button>
<template #content>
<div class="add-actions">
<t-button
v-for="(action, index) in captureActionList"
:key="`${action.emit}-${index}`"
variant="text"
:theme="action.theme"
class="add-action"
:disabled="busy"
@click="handleCaptureAction(action)"
>
<template #icon v-if="action.icon">
<t-icon :name="action.icon" />
</template>
<span v-text="action.text" />
</t-button>
</div>
</template>
</t-popover>
<t-button
class="file"
theme="primary"
variant="dashed"
:loading="busy"
:disabled="busy"
@click="handleFileAction"
>
<template #icon>
<t-icon name="file-attachment" />
</template>
<span v-text="addText" />
</t-button>
</div>
<input
v-if="uploadEnabled"
ref="fileInput"
class="file-input"
type="file"
accept="*/*"
multiple
@change="handleFileChange"
/>
<input
v-if="uploadEnabled"
ref="photoFileInput"
class="file-input"
type="file"
accept="image/*"
capture="environment"
@change="handleFileChange"
/>
<input
v-if="uploadEnabled"
ref="videoFileInput"
class="file-input"
type="file"
accept="video/*"
capture="environment"
@change="handleFileChange"
/>
</template>
<script setup lang="ts">
import type { MediaSelectorAddAction } from "./types";
interface Props {
addActions: readonly MediaSelectorAddAction[];
addText: string;
busy: boolean;
uploadEnabled: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
add: [];
action: [value: MediaSelectorAddAction];
"capture-photo": [];
"capture-video": [];
"file-change": [value: Event];
}>();
const capturePopoverVisible = ref(false);
const fileInput = ref<HTMLInputElement>();
const photoFileInput = ref<HTMLInputElement>();
const videoFileInput = ref<HTMLInputElement>();
const captureActionList = computed<readonly MediaSelectorAddAction[]>(() => [{
icon: "camera",
emit: "capture-photo",
text: "拍照片",
theme: "primary"
}, {
icon: "video-camera-1",
emit: "capture-video",
text: "录视频",
theme: "primary"
}, ...props.addActions]);
function handleFileAction(): void {
capturePopoverVisible.value = false;
if (props.uploadEnabled) {
fileInput.value?.click();
return;
}
emit("add");
}
function handleCaptureAction(action: MediaSelectorAddAction): void {
capturePopoverVisible.value = false;
if (action.emit === "capture-photo") {
if (props.uploadEnabled) {
photoFileInput.value?.click();
return;
}
emit("capture-photo");
return;
}
if (action.emit === "capture-video") {
if (props.uploadEnabled) {
videoFileInput.value?.click();
return;
}
emit("capture-video");
return;
}
emit("action", action);
}
function handleFileChange(event: Event): void {
emit("file-change", event);
}
</script>
<style scoped lang="less">
.add-trigger {
--td-popover-padding: 0 .5rem;
gap: .375rem;
width: 5.5rem;
height: 5.5rem;
display: flex;
flex-direction: column;
:deep(.t-popover__content) {
border-radius: .875rem;
}
.capture,
.file {
--td-button-border-radius: .875rem;
width: 5.5rem;
height: 2.5625rem;
min-height: 0;
display: flex;
align-items: center;
justify-content: center;
}
.add-actions {
gap: .25rem;
display: grid;
padding: .5rem 0;
.add-action {
--td-button-medium-height: 32px;
--td-button-medium-padding-horizontal: 10px;
gap: .5rem;
width: 100%;
position: relative;
font-weight: unset;
justify-content: start;
}
}
}
.file-input {
display: none;
}
:global(html.theme-dark) {
.capture,
.file {
background: rgba(37, 99, 235, .16);
border-color: rgba(96, 165, 250, .28);
span {
color: #93c5fd;
}
}
}
</style>