211 lines
4.0 KiB
Vue
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>
|