Files
xmc-Assets/web/src/components/ImportExportButton.vue
caopeng a254aae503 feat(web): 引入 Vite 前端应用并扩展仓库忽略规则
将整套 web 源码纳入仓库,并为 web/node_modules、构建产物及本地环境文件配置 .gitignore,同时移除占位用的 assets/.gitkeep。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-17 15:22:29 +08:00

248 lines
6.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 导入/导出公共按钮下拉下载导入模板批量导入导出 -->
<template>
<div class="import-export-wrap" :class="{ 'locale-en': isEn }" ref="importExportWrapRef">
<div class="table-down-box" @click="toggleDropdown">
<div class="downIcon">
<img src="../images/download/file.png" alt="">
</div>
<div class="downName">{{ $t('importExport.importExport') }}</div>
<div class="downIconRight">
<img src="../images/download/down.png" alt="">
</div>
</div>
<Transition name="dropdown">
<div v-show="dropdownVisible" class="import-export-dropdown">
<div v-if="!exportOnly" class="import-export-item" @click="onDownloadTemplate">
<div class="item-icon-wrap">
<img src="../images/download/download.png" class="item-icon item-icon-default" alt="">
<img src="../images/download/downloadSelect.png" class="item-icon item-icon-hover" alt="">
</div>
<span>{{ $t('importExport.downloadTemplate') }}</span>
</div>
<div v-if="!exportOnly" class="import-export-item" @click="onBatchImportClick">
<div class="item-icon-wrap">
<img src="../images/download/allIn.png" class="item-icon item-icon-default" alt="">
<img src="../images/download/allInSelect.png" class="item-icon item-icon-hover" alt="">
</div>
<span>{{ $t('importExport.batchImport') }}</span>
</div>
<div class="import-export-item" @click="onExport">
<div class="item-icon-wrap">
<img src="../images/download/export.png" class="item-icon item-icon-default" alt="">
<img src="../images/download/exportSelect.png" class="item-icon item-icon-hover" alt="">
</div>
<span>{{ $t('importExport.export') }}</span>
</div>
</div>
</Transition>
<input v-if="!exportOnly" ref="batchImportInputRef" type="file" accept=".xlsx,.xls" class="batch-import-input"
@change="onFileChange" />
</div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const isEn = computed(() => (locale.value || "").startsWith("en"));
const props = defineProps({
/** 为 true 时点击「批量导入」只触发事件不打开文件选择,由父级用弹框处理 */
useBatchImportDialog: { type: Boolean, default: false },
/** 为 true 时下拉只显示「导出」选项,隐藏「下载导入模板」「批量导入」 */
exportOnly: { type: Boolean, default: false },
});
const emit = defineEmits(["download-template", "batch-import", "export"]);
const importExportWrapRef = ref(null);
const dropdownVisible = ref(false);
const batchImportInputRef = ref(null);
const toggleDropdown = () => {
dropdownVisible.value = !dropdownVisible.value;
};
const closeDropdown = (e) => {
if (importExportWrapRef.value && !importExportWrapRef.value.contains(e.target)) {
dropdownVisible.value = false;
}
};
const onDownloadTemplate = () => {
dropdownVisible.value = false;
emit("download-template");
};
const onBatchImportClick = () => {
dropdownVisible.value = false;
if (props.useBatchImportDialog) {
emit("batch-import");
return;
}
batchImportInputRef.value?.click();
};
const onFileChange = (e) => {
emit("batch-import", e);
e.target.value = "";
};
const onExport = () => {
dropdownVisible.value = false;
emit("export");
};
onMounted(() => {
document.addEventListener("click", closeDropdown);
});
onBeforeUnmount(() => {
document.removeEventListener("click", closeDropdown);
});
</script>
<style scoped>
.import-export-wrap {
position: relative;
margin-left: 10px;
}
.table-down-box {
cursor: pointer;
width: 135px;
height: 34px;
background: #3067E5;
border-radius: 4px 4px 4px 4px;
display: flex;
align-items: center;
justify-content: center;
}
/* 英文时左右 padding 固定为 15px与中文/泰文一致有左右留白;避免 padding 压缩内容区导致图标变形 */
.import-export-wrap.locale-en .table-down-box {
padding: 0 10px;
box-sizing: content-box;
}
/* 英文时禁止左右图标被 flex 压缩,保持 16x16 不变形 */
.import-export-wrap.locale-en .table-down-box .downIcon,
.import-export-wrap.locale-en .table-down-box .downIconRight {
flex-shrink: 0;
}
.table-down-box .downIcon {
margin-right: 5px;
width: 16px;
height: 16px;
}
.table-down-box .downIcon img {
width: 100%;
height: 100%;
}
.table-down-box .downName {
font-family: Source Han Sans SC, Source Han Sans SC;
font-weight: 400;
font-size: 14px;
color: #FFFFFF;
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.table-down-box .downIconRight {
margin-left: 5px;
width: 16px;
height: 16px;
}
.table-down-box .downIconRight img {
width: 100%;
height: 100%;
}
.import-export-dropdown {
/* width: 165px; */
width: 175px;
position: absolute;
top: calc(100% + 4px);
left: 0;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
padding: 8px;
z-index: 1000;
}
.import-export-item {
width: 160px;
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
font-size: 14px;
color: #333;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.import-export-item:hover {
background: #EFF4FF;
border-radius: 4px 4px 4px 4px;
margin-right: 8px;
color: #3067E5;
}
.import-export-item .item-icon-wrap {
position: relative;
width: 16px;
height: 16px;
flex-shrink: 0;
}
.import-export-item .item-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
transition: opacity 0.15s ease;
}
.import-export-item .item-icon-hover {
position: absolute;
left: 0;
top: 0;
opacity: 0;
transition: opacity 0.15s ease;
}
.import-export-item:hover .item-icon-default {
opacity: 0;
}
.import-export-item:hover .item-icon-hover {
opacity: 1;
}
.batch-import-input {
position: absolute;
width: 0;
height: 0;
opacity: 0;
overflow: hidden;
}
.dropdown-enter-active,
.dropdown-leave-active {
transition: opacity 0.15s ease, transform 0.15s ease;
}
.dropdown-enter-from,
.dropdown-leave-to {
opacity: 0;
transform: translateY(-4px);
}
</style>