This commit is contained in:
Viet An
2026-05-07 10:53:09 +07:00
parent 6e10dffd22
commit 56cfcd09bf
15 changed files with 439 additions and 1951 deletions

View File

@@ -1,11 +1,13 @@
<template>
<div>
<div
class="file is-primary"
id="ignore"
v-if="position === 'left'"
class="file is-primary"
>
<label class="file-label">
<label
class="file-label"
v-bind="$attrs"
>
<input
class="file-input"
type="file"
@@ -14,117 +16,106 @@
name="resume"
@change="doChange"
/>
<span class="file-cta px-2">
<span class="icon-text is-clickable">
<SvgIcon v-bind="{ name: 'attach-file.svg', type: 'white', size: 22 }"></SvgIcon>
<!--<span class="has-text-white">File</span>-->
<span class="file-cta px-3 py-1.5">
<span class="icon-text is-gap-0.5 is-align-items-center">
<slot name="icon">
<Icon
name="material-symbols:attach-file-rounded"
:size="20"
/>
</slot>
<slot />
</span>
</span>
</label>
</div>
<div
class="field is-grouped is-grouped-right"
id="ignore"
v-else
>
<div class="control">
<div class="file is-primary">
<label class="file-label">
<input
class="file-input"
type="file"
:id="docid"
multiple
name="resume"
@change="doChange"
/>
<span class="file-cta is-primary px-1">
<span class="icon-text is-clickable">
<SvgIcon v-bind="{ name: 'attach-file.svg', type: 'white', size: 22 }"></SvgIcon>
</span>
</span>
</label>
</div>
</div>
</div>
<Modal
@close="showmodal = undefined"
v-bind="showmodal"
v-if="showmodal"
@close="showmodal = undefined"
@files="getFiles"
></Modal>
/>
</div>
</template>
<script>
export default {
props: ["type", "position", "convert", "quality"],
data() {
return {
loading: false,
files: undefined,
dataFiles: [],
vtype: this.type || ["image"],
showmodal: undefined,
docid: this.$id(),
};
},
methods: {
getFileExtension(fileName) {
if (!fileName || typeof fileName !== "string") return "";
const parts = fileName.split(".");
return parts.length > 1 ? parts.pop().toLowerCase() : "";
},
getType(ext) {
// copied from 01-common.js
const imageFormat = ["png", "jpg", "jpeg", "bmp", "gif", "svg", "webp"];
const videoFormat = ["wmv", "avi", "mp4", "flv", "mov", "mpg", "amv", "rm"];
<script setup>
defineOptions({
inheritAttrs: false,
});
if (ext === "pdf") return "pdf";
if (imageFormat.includes(ext)) return "image";
if (videoFormat.includes(ext)) return "video";
return "file";
},
doChange() {
this.dataFiles = [];
const fileList = document.getElementById(this.docid).files;
this.files = Array.from(fileList);
if (this.files.length === 0) return;
const props = defineProps({
type: Array,
convert: String,
quality: Number,
});
// Xác định giá trị convert: "1" nếu convert được bật, "0" nếu không
const convertValue = this.convert ? "1" : "0";
const qualityValue = this.convert && this.quality ? this.quality : null;
const emit = defineEmits(["files"]);
const { $id, $snackbar, $upload } = useNuxtApp();
this.files.map((v) => {
const ext = this.getFileExtension(v.name);
const type = this.getType(ext);
const vtype = props.type || ["image"];
const files = ref();
const dataFiles = ref();
const showmodal = ref();
const docid = $id();
if (!this.vtype.includes(type)) {
this.$snackbar(`Định dạng file "${ext}" không hợp lệ`);
return;
}
function getFileExtension(fileName) {
if (!fileName || typeof fileName !== "string") return "";
const parts = fileName.split(".");
return parts.length > 1 ? parts.pop().toLowerCase() : "";
}
let file = this.$upload(v, type, 1, convertValue, qualityValue);
this.dataFiles.push(file);
});
function getType(ext) {
// copied from 01-common.js
const imageFormat = ["png", "jpg", "jpeg", "bmp", "gif", "svg", "webp"];
const videoFormat = ["wmv", "avi", "mp4", "flv", "mov", "mpg", "amv", "rm"];
this.showmodal = {
component: "media/UploadProgress",
title: "Upload files",
width: "700px",
height: "200px",
vbind: { files: this.dataFiles },
};
this.clearFileList();
},
clearFileList() {
const fileInput = document.getElementById(this.docid);
const dt = new DataTransfer();
fileInput.files = dt.files;
},
getFiles(files) {
this.$emit("files", files);
setTimeout(() => (this.showmodal = undefined), 3000);
},
},
};
if (ext === "pdf") return "pdf";
if (imageFormat.includes(ext)) return "image";
if (videoFormat.includes(ext)) return "video";
return "file";
}
function doChange() {
dataFiles.value = [];
const fileList = document.getElementById(docid).files;
files.value = Array.from(fileList);
if (files.value.length === 0) return;
// Xác định giá trị convert: "1" nếu convert được bật, "0" nếu không
const convertValue = props.convert ? "1" : "0";
const qualityValue = props.convert && props.quality ? props.quality : null;
files.value.map((v) => {
const ext = getFileExtension(v.name);
const type = getType(ext);
if (!vtype.includes(type)) {
$snackbar(`Định dạng file "${ext}" không hợp lệ`);
return;
}
const file = $upload(v, type, 1, convertValue, qualityValue);
dataFiles.value.push(file);
});
showmodal.value = {
component: "media/UploadProgress",
title: "Upload files",
width: "700px",
height: "200px",
vbind: { files: dataFiles.value },
};
clearFileList();
}
function clearFileList() {
const fileInput = document.getElementById(docid);
const dt = new DataTransfer();
fileInput.files = dt.files;
}
function getFiles(files) {
emit("files", files);
setTimeout(() => {
showmodal.value = undefined;
}, 3000);
}
</script>

View File

@@ -1,33 +1,43 @@
<template>
<div class="has-text-left">
<div class="fs-14 has-text-left is-flex is-flex-direction-column is-gap-1">
<p
class="py-1 border-bottom"
v-for="v in vfiles"
v-for="(v, i) in vfiles"
:key="i"
>
<span class="icon-text">
<span class="mr-5">{{ v.name }}</span>
<SvgIcon
v-bind="{ name: 'check2.svg', type: 'primary', size: 22 }"
v-if="v.status === 'success'"
></SvgIcon>
<SvgIcon
v-bind="{ name: 'error.svg', type: 'danger', size: 22 }"
v-else-if="v.status === 'error'"
></SvgIcon>
<span class="icon-text is-gap-1 is-align-items-center">
<span class="icon">
<Icon
name="svg-spinners:90-ring"
:size="22"
class="has-text-primary"
v-if="v.status === 'uploading'"
/>
<Icon
name="material-symbols:check-circle-rounded"
:size="22"
class="has-text-primary"
v-else-if="v.status === 'success'"
/>
<Icon
name="material-symbols:cancel-rounded"
:size="22"
class="has-text-danger"
v-else-if="v.status === 'error'"
/>
</span>
<span>{{ v.name }}</span>
</span>
<span
class="icon-text has-text-danger ml-6"
class="icon-text is-gap-1 has-text-danger ml-6"
v-if="v.error"
>
<SvgIcon v-bind="{ name: 'error.svg', type: 'danger', size: 22 }"></SvgIcon>
<span class="ml-1">{{ v.text }}</span>
<Icon
name="material-symbols:cancel-rounded"
:size="22"
class="has-text-danger"
/>
<span>{{ v.text }}</span>
</span>
<button
class="button is-small is-white is-loading px-0 ml-4"
v-if="v.status === 'uploading'"
>
Loading
</button>
</p>
</div>
</template>