文件
OmniCard/mpuniapp/components/image-uploader/image-uploader.uvue

179 行
3.6 KiB
Plaintext

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<view class="uploader-container">
<view class="uploader-grid">
<!-- 已选图片列表 -->
<view
v-for="(url, index) in state.images"
:key="index"
class="uploader-item"
>
<image
class="uploader-img"
:src="url"
mode="aspectFill"
@click="handlePreview(index)"
/>
<view
v-if="!disabled"
class="uploader-delete-btn"
@click="handleDelete(index)"
>
<text class="uploader-delete-icon">×</text>
</view>
</view>
<!-- 上传卡片触发区 -->
<view
v-if="canAdd && !disabled"
class="uploader-trigger"
@click="handleChoose"
>
<text class="uploader-trigger-plus">+</text>
<text class="uploader-trigger-text">{{ state.uploading ? '上传中' : '添加图片' }}</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import {
createImageUploaderState,
removeImageByIndex,
previewImageAt,
chooseAndUploadImages,
ImageUploaderState,
} from './useImageUploader.uts';
const props = withDefaults(
defineProps<{
modelValue?: string[];
multiple?: boolean;
maxCount?: number;
disabled?: boolean;
}>(),
{
modelValue: () => [] as string[],
multiple: false,
maxCount: 9,
disabled: false,
}
);
const emit = defineEmits<{
(e: 'update:modelValue', value: string[]): void;
(e: 'change', value: string[]): void;
}>();
const state = reactive(createImageUploaderState(props.modelValue));
watch(
() => props.modelValue,
(newVal: string[]) => {
state.images = newVal != null ? newVal.slice() : [];
}
);
const canAdd = computed(() => {
const limit = props.multiple ? props.maxCount : 1;
return state.images.length < limit;
});
function handlePreview(index: number): void {
previewImageAt(state, index);
}
function handleDelete(index: number): void {
removeImageByIndex(state, index);
emit('update:modelValue', state.images);
emit('change', state.images);
}
async function handleChoose(): Promise<void> {
if (state.uploading || props.disabled) {
return;
}
const ok = await chooseAndUploadImages(state, {
multiple: props.multiple,
maxCount: props.multiple ? props.maxCount : 1,
});
if (ok) {
emit('update:modelValue', state.images);
emit('change', state.images);
}
}
</script>
<style scoped>
.uploader-container {
display: flex;
flex-direction: column;
width: 100%;
}
.uploader-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.uploader-item {
position: relative;
width: 80px;
height: 80px;
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--oc-border, #E3E7ED);
background-color: var(--oc-surface, #F7F8FA);
}
.uploader-img {
width: 100%;
height: 100%;
}
.uploader-delete-btn {
position: absolute;
top: 4px;
right: 4px;
width: 24px;
height: 24px;
border-radius: 12px;
background-color: rgba(16, 24, 40, 0.65);
display: flex;
align-items: center;
justify-content: center;
}
.uploader-delete-icon {
color: #FFFFFF;
font-size: 14px;
font-weight: 700;
line-height: 1;
}
.uploader-trigger {
width: 80px;
height: 80px;
border-radius: 12px;
border: 1px dashed var(--oc-border, #C9CDD4);
background-color: var(--oc-surface, #F7F8FA);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.uploader-trigger-plus {
font-size: 24px;
color: var(--oc-text-2, #667085);
line-height: 1;
margin-bottom: 2px;
}
.uploader-trigger-text {
font-size: 11px;
color: var(--oc-text-2, #667085);
}
</style>