update AIChart, customer followup, media selector
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<t-image-viewer
|
||||
v-model:visible="viewerVisible"
|
||||
:images="images"
|
||||
show-index
|
||||
@close="handleClose"
|
||||
@index-change="handleIndexChange"
|
||||
>
|
||||
<template #image="params">
|
||||
<div
|
||||
v-if="isVideoViewerItem(params.index)"
|
||||
:class="[params.className, 'viewer-video-wrap']"
|
||||
:style="params.style"
|
||||
@click.stop
|
||||
@pointerdown.stop
|
||||
@pointermove.stop
|
||||
@pointerup.stop
|
||||
@touchstart.stop
|
||||
@touchmove.stop
|
||||
@touchend.stop
|
||||
>
|
||||
<video
|
||||
:src="getViewerMediaURL(params.index)"
|
||||
class="viewer-video"
|
||||
controls
|
||||
playsinline
|
||||
preload="metadata"
|
||||
@loadedmetadata="handleViewerVideoLoaded($event, params)"
|
||||
@transitionstart="params.onTransitionstart"
|
||||
@transitionend="params.onTransitionend"
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
v-else
|
||||
:src="params.src"
|
||||
:class="params.className"
|
||||
:style="params.style"
|
||||
:alt="getViewerAlt(params.index)"
|
||||
@load="params.onLoad"
|
||||
@transitionstart="params.onTransitionstart"
|
||||
@transitionend="params.onTransitionend"
|
||||
/>
|
||||
</template>
|
||||
<template #delete-btn>
|
||||
<div v-if="showActions" class="viewer-action" @click.stop>
|
||||
<t-popover
|
||||
v-model:visible="photoActionVisible"
|
||||
placement="bottom"
|
||||
theme="light"
|
||||
:show-arrow="false"
|
||||
>
|
||||
<t-button class="white" variant="text">
|
||||
<t-icon name="ellipsis" />
|
||||
</t-button>
|
||||
<template #content>
|
||||
<div class="actions" @click.stop>
|
||||
<div class="title">修改类型</div>
|
||||
<div class="type">
|
||||
<t-button
|
||||
v-for="item in attachTypeOptions"
|
||||
:key="item.value"
|
||||
:theme="item.value === currentPhotoItem?.attachType ? 'primary' : 'default'"
|
||||
variant="text"
|
||||
size="extra-small"
|
||||
:disabled="submitting"
|
||||
@click="changePhotoType(item.value)"
|
||||
>
|
||||
<span v-text="item.text" />
|
||||
</t-button>
|
||||
</div>
|
||||
<t-divider />
|
||||
<t-button
|
||||
class="button delete-action"
|
||||
theme="danger"
|
||||
size="extra-small"
|
||||
variant="text"
|
||||
:disabled="submitting"
|
||||
@click="removeCurrentPhoto"
|
||||
>
|
||||
<template #icon>
|
||||
<t-icon name="delete" />
|
||||
</template>
|
||||
删除
|
||||
</t-button>
|
||||
</div>
|
||||
</template>
|
||||
</t-popover>
|
||||
</div>
|
||||
</template>
|
||||
</t-image-viewer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ImageSlotParams } from "tdesign-mobile-vue";
|
||||
import type { MediaSelectorViewerItem } from "./types";
|
||||
import { getAttachTypeText, getMediaPreviewURL, isVideoItem } from "./utils";
|
||||
|
||||
interface Props {
|
||||
images: string[];
|
||||
items: readonly MediaSelectorViewerItem[];
|
||||
showActions: boolean;
|
||||
submitting: boolean;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", value: boolean): void;
|
||||
(event: "change-type", sourceIndex: number, attachType: string): void;
|
||||
(event: "remove", sourceIndex: number): void;
|
||||
}>();
|
||||
|
||||
const viewerVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: value => emit("update:visible", value)
|
||||
});
|
||||
const viewerIndex = ref(0);
|
||||
const photoActionVisible = ref(false);
|
||||
const attachTypeOptions = [
|
||||
{ text: "照片", value: "PHOTO" },
|
||||
{ text: "积分卡", value: "POINT_CARD" },
|
||||
{ text: "其他文件", value: "OTHER" }
|
||||
] as const;
|
||||
const currentPhotoItem = computed(() => props.items[viewerIndex.value]);
|
||||
|
||||
watch(() => props.visible, value => {
|
||||
if (value) {
|
||||
viewerIndex.value = 0;
|
||||
photoActionVisible.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function handleClose(): void {
|
||||
photoActionVisible.value = false;
|
||||
emit("update:visible", false);
|
||||
}
|
||||
|
||||
function handleIndexChange(index: number): void {
|
||||
viewerIndex.value = index;
|
||||
photoActionVisible.value = false;
|
||||
}
|
||||
|
||||
function isVideoViewerItem(index?: number): boolean {
|
||||
const item = getViewerItem(index);
|
||||
return !!item && isVideoItem(item);
|
||||
}
|
||||
|
||||
function getViewerItem(index?: number): MediaSelectorViewerItem | undefined {
|
||||
return index === undefined ? undefined : props.items[index];
|
||||
}
|
||||
|
||||
function getViewerMediaURL(index?: number): string {
|
||||
const item = getViewerItem(index);
|
||||
return item ? getMediaPreviewURL(item) : "";
|
||||
}
|
||||
|
||||
function getViewerAlt(index?: number): string {
|
||||
return getAttachTypeText(getViewerItem(index)?.attachType);
|
||||
}
|
||||
|
||||
function handleViewerVideoLoaded(event: Event, params: ImageSlotParams): void {
|
||||
const video = event.currentTarget as HTMLVideoElement;
|
||||
if (video.videoHeight > 0) {
|
||||
video.height = video.videoHeight;
|
||||
}
|
||||
params.onLoad?.(event);
|
||||
}
|
||||
|
||||
function changePhotoType(attachType: string): void {
|
||||
const currentItem = currentPhotoItem.value;
|
||||
photoActionVisible.value = false;
|
||||
if (!currentItem || currentItem.attachType === attachType) {
|
||||
return;
|
||||
}
|
||||
emit("change-type", currentItem.sourceIndex, attachType);
|
||||
}
|
||||
|
||||
function removeCurrentPhoto(): void {
|
||||
const currentItem = currentPhotoItem.value;
|
||||
photoActionVisible.value = false;
|
||||
if (currentItem) {
|
||||
emit("remove", currentItem.sourceIndex);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.viewer-action {
|
||||
color: #fff;
|
||||
margin-left: -1rem;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
min-width: 8rem;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
padding: 0 .75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.type {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
flex-direction: column;
|
||||
|
||||
.button {
|
||||
text-indent: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.viewer-video-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewer-video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
background: #0f172a;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user