mirror of
https://gitee.com/beecue/fastbee.git
synced 2026-05-07 08:14:39 +08:00
feat(国际化): 去除冗余代码,增加国际化配置页面
This commit is contained in:
548
vue/src/views/system/app/lang.vue
Normal file
548
vue/src/views/system/app/lang.vue
Normal file
@@ -0,0 +1,548 @@
|
||||
<template>
|
||||
<div class="system-app-lang">
|
||||
<el-card v-show="showSearch" class="search-card">
|
||||
<el-form
|
||||
@submit.native.prevent
|
||||
:model="queryParams"
|
||||
ref="queryForm"
|
||||
:inline="true"
|
||||
label-width="46px"
|
||||
class="search-form"
|
||||
>
|
||||
<el-form-item prop="langName">
|
||||
<el-input
|
||||
v-model="queryParams.langName"
|
||||
:placeholder="$t('app.lang.755172-14')"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="country">
|
||||
<el-input
|
||||
v-model="queryParams.country"
|
||||
:placeholder="$t('app.lang.755172-12')"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div style="float: right">
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">{{ $t('search') }}</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">{{ $t('reset') }}</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-row :gutter="10" style="margin-bottom: 16px">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="small"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['app:language:add']"
|
||||
>
|
||||
{{ $t('add') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="small"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['app:language:remove']"
|
||||
>
|
||||
{{ $t('del') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
v-hasPermi="['app:language:export']"
|
||||
plain
|
||||
size="small"
|
||||
:loading="exportLoading"
|
||||
@click="handleExport"
|
||||
>
|
||||
{{ $t('app.start.891644-43') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-upload v-hasRole="['admin']" :show-file-list="false" ref="upload" action="" :http-request="handleImport">
|
||||
<el-button slot="trigger" :loading="importLoading" size="small" plain>
|
||||
{{ $t('app.start.891644-44') }}
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown v-hasRole="['admin']" @command="handleExportBackendMenu($event, true)">
|
||||
<el-button plain size="small">
|
||||
{{ $t('app.start.891644-47') }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="dict in dict.type.international_configuration_template"
|
||||
:key="dict.value"
|
||||
:command="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown v-hasRole="['admin']" @command="handleExportBackendMenu($event, false)">
|
||||
<el-button plain size="small">
|
||||
{{ $t('app.start.891644-45') }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="dict in dict.type.international_configuration_template"
|
||||
:key="dict.value"
|
||||
:command="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown v-hasRole="['admin']" @command="handleImportTranslate">
|
||||
<el-upload
|
||||
v-hasRole="['admin']"
|
||||
:show-file-list="false"
|
||||
ref="upload"
|
||||
action=""
|
||||
:http-request="handleImportBackendMenu"
|
||||
:disabled="true"
|
||||
>
|
||||
<el-button slot="trigger" size="small" plain>
|
||||
{{ $t('app.start.891644-46') }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="dict in dict.type.international_configuration_template"
|
||||
:key="dict.value"
|
||||
:command="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="languageList" :border="false" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column :label="$t('app.lang.755172-4')" align="center" prop="id" min-width="80" />
|
||||
<el-table-column :label="$t('app.lang.755172-8')" align="left" prop="langName" min-width="180" />
|
||||
<el-table-column :label="$t('app.lang.755172-5')" align="center" prop="language" min-width="100" />
|
||||
<el-table-column :label="$t('app.lang.755172-6')" align="center" prop="country" min-width="120" />
|
||||
<el-table-column :label="$t('app.lang.755172-7')" align="center" prop="timeZone" min-width="100" />
|
||||
<el-table-column fixed="right" :label="$t('opation')" align="center" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="small"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['app:language:edit']"
|
||||
>
|
||||
{{ $t('update') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['app:language:remove']"
|
||||
>
|
||||
{{ $t('del') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
style="margin-bottom: 20px"
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改app语言对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="560px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="75px">
|
||||
<el-form-item :label="$t('app.lang.755172-8')" prop="langName">
|
||||
<el-input v-model="form.langName" :placeholder="$t('app.lang.755172-14')" style="width: 390px" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.lang.755172-5')" prop="language">
|
||||
<el-input v-model="form.language" :placeholder="$t('app.lang.755172-11')" style="width: 390px" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.lang.755172-6')" prop="country">
|
||||
<el-input v-model="form.country" :placeholder="$t('app.lang.755172-12')" style="width: 390px" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.lang.755172-7')" prop="timeZone">
|
||||
<el-input v-model="form.timeZone" :placeholder="$t('app.lang.755172-13')" style="width: 390px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">
|
||||
{{ $t('confirm') }}
|
||||
</el-button>
|
||||
<el-button @click="cancel">
|
||||
{{ $t('cancel') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="$t('app.lang.755172-22')" :visible.sync="productModelVisible" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item :label="$t('app.lang.755172-23')" prop="productId">
|
||||
<el-select v-model="productId" :placeholder="$t('pleaseSelect')">
|
||||
<el-option v-for="item in prodcutModels" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitProdcutModel">
|
||||
{{ $t('confirm') }}
|
||||
</el-button>
|
||||
<el-button @click="closeProductModelDialog">
|
||||
{{ $t('cancel') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listLanguage,
|
||||
getLanguage,
|
||||
delLanguage,
|
||||
addLanguage,
|
||||
updateLanguage,
|
||||
exportTranslate,
|
||||
importTranslate,
|
||||
} from '@/api/system/language';
|
||||
import * as langTransformer from './script/langTransformer';
|
||||
import * as xlsxHandler from './script/xlsx';
|
||||
import * as jszip from './script/jszip';
|
||||
import { downFileByBlob } from '@/utils/common.js';
|
||||
import { listShortProduct } from '@/api/iot/product';
|
||||
|
||||
export default {
|
||||
name: 'AppLang',
|
||||
dicts: ['international_configuration_template'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// app语言表格数据
|
||||
languageList: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
langName: '',
|
||||
country: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
langName: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('app.lang.755172-14'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
language: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('app.lang.755172-11'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
country: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('app.lang.755172-12'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
// 导出语言包loading
|
||||
exportLoading: false,
|
||||
// 导入excel生成语言包loading
|
||||
importLoading: false,
|
||||
currentLanguage: '',
|
||||
currentTranslateModule: '',
|
||||
productModelVisible: false,
|
||||
prodcutModels: [],
|
||||
productId: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getProductModels();
|
||||
},
|
||||
methods: {
|
||||
/** 查询app语言列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listLanguage(this.queryParams).then((response) => {
|
||||
this.languageList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
id: null,
|
||||
language: null,
|
||||
country: null,
|
||||
timeZone: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
langName: null,
|
||||
};
|
||||
this.resetForm('form');
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm');
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map((item) => item.id);
|
||||
this.single = selection.length !== 1;
|
||||
this.multiple = !selection.length;
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = this.$t('app.lang.755172-17');
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id || this.ids;
|
||||
getLanguage(id).then((response) => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = this.$t('app.lang.755172-18');
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.form.id != null) {
|
||||
updateLanguage(this.form).then((response) => {
|
||||
this.$modal.msgSuccess(this.$t('updateSuccess'));
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
addLanguage(this.form).then((response) => {
|
||||
this.$modal.msgSuccess(this.$t('addSuccess'));
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal
|
||||
.confirm(this.$t('app.lang.755172-21', [ids]))
|
||||
.then(function () {
|
||||
return delLanguage(ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess(this.$t('delSuccess'));
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
// 导出语言包
|
||||
handleExport() {
|
||||
if (this.languageList.length === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.exportLoading = true;
|
||||
// 中英文映射
|
||||
const langs = this.languageList.reduce((obj, item) => {
|
||||
obj[item.language] = item.country;
|
||||
return obj;
|
||||
}, {});
|
||||
// 获取json数据
|
||||
const jsonMap = langTransformer.getLangJson();
|
||||
// 转换为excel导出需要的数据格式
|
||||
const excelData = langTransformer.transoformToExcel(jsonMap, langs);
|
||||
// 导出为excel
|
||||
xlsxHandler.exportExcel(excelData, 'lang.xlsx');
|
||||
} finally {
|
||||
this.exportLoading = false;
|
||||
}
|
||||
},
|
||||
// 导入语言包excel直接导出转换后的压缩包
|
||||
async handleImport(fileInfo) {
|
||||
try {
|
||||
this.importLoading = true;
|
||||
// 中英文映射
|
||||
const langs = this.languageList.reduce((obj, item) => {
|
||||
obj[item.country] = item.language;
|
||||
return obj;
|
||||
}, {});
|
||||
// 读取excel文件,解析为json数据
|
||||
const data = await xlsxHandler.parseJson(fileInfo.file);
|
||||
// 将json文件转换为以模块维度的数据,用于进一步处理成压缩包文件数据
|
||||
const jsonData = jszip.parseJsonZipData(data, langs);
|
||||
// 生成zip的文件列表
|
||||
const files = jszip.generateJsonZipFiles(jsonData);
|
||||
// 下载为压缩包
|
||||
jszip.downloadFiles2Zip({
|
||||
zipName: 'lang',
|
||||
files: files,
|
||||
});
|
||||
} finally {
|
||||
this.importLoading = false;
|
||||
}
|
||||
},
|
||||
// 导出菜单名称翻译列表
|
||||
async handleExportBackendMenu(value, isSource = false) {
|
||||
this.currentTranslateModule = value;
|
||||
const exportFn = () => {
|
||||
const sourceData = this.dict.type.international_configuration_template;
|
||||
const isThingsModel = this.currentTranslateModule === 'things_model';
|
||||
exportTranslate(value, isSource, isThingsModel ? this.productId : null).then((response) => {
|
||||
let name = sourceData.find((item) => item.value === value).name;
|
||||
if (isThingsModel) {
|
||||
name += '_' + this.prodcutModels.find((item) => item.id === this.productId).name;
|
||||
}
|
||||
if (response.type === 'application/json') {
|
||||
this.$modal.msgError(`导出异常`);
|
||||
return;
|
||||
}
|
||||
const fileName = isSource ? `${name}原表数据.xlsx` : `${name}翻译数据.xlsx`;
|
||||
downFileByBlob(response, fileName);
|
||||
isThingsModel && this.closeProductModelDialog();
|
||||
});
|
||||
};
|
||||
if (value === 'things_model') {
|
||||
this.productModelVisible = true;
|
||||
this.callback = () => {
|
||||
exportFn();
|
||||
};
|
||||
} else {
|
||||
exportFn();
|
||||
}
|
||||
},
|
||||
async handleImportBackendMenu(fileInfo) {
|
||||
let formData = new FormData();
|
||||
formData.append('file', fileInfo.file);
|
||||
const isThingsModel = this.currentTranslateModule === 'things_model';
|
||||
const productId = isThingsModel && this.productId ? this.productId : '';
|
||||
importTranslate(formData, this.currentTranslateModule, productId).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$modal.msgSuccess('导入成功');
|
||||
} else {
|
||||
this.$modal.msgError(res.msg);
|
||||
}
|
||||
isThingsModel && this.closeProductModelDialog();
|
||||
});
|
||||
},
|
||||
handleImportTranslate(value) {
|
||||
this.currentTranslateModule = value;
|
||||
if (value === 'things_model') {
|
||||
this.productModelVisible = true;
|
||||
this.callback = () => {
|
||||
this.$refs.upload.$el.querySelector('input').click();
|
||||
};
|
||||
} else {
|
||||
this.$refs.upload.$el.querySelector('input').click();
|
||||
}
|
||||
},
|
||||
async getProductModels() {
|
||||
const params = {
|
||||
pageSize: 999,
|
||||
showSenior: true,
|
||||
};
|
||||
const res = await listShortProduct(params);
|
||||
if (res.code === 200) {
|
||||
this.prodcutModels = res.data || [];
|
||||
}
|
||||
},
|
||||
closeProductModelDialog() {
|
||||
this.productModelVisible = false;
|
||||
this.productId = '';
|
||||
},
|
||||
submitProdcutModel() {
|
||||
if (!this.productId) {
|
||||
this.$message.warning('请选择产品后再确认');
|
||||
return;
|
||||
}
|
||||
this.callback && this.callback(this.productId);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.system-app-lang {
|
||||
padding: 20px;
|
||||
|
||||
.search-card {
|
||||
margin-bottom: 15px;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: -22.5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
95
vue/src/views/system/app/script/jszip.js
Normal file
95
vue/src/views/system/app/script/jszip.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import JsZip from 'jszip';
|
||||
import { saveAs } from 'file-saver';
|
||||
/**
|
||||
* 根据提供的JSON 数据生成 JSON 格式的 zip 文件列表。
|
||||
* @param {Object} jsonData - 包含模块和其对应多语言数据的对象。
|
||||
* @returns {Array} files - 包含每个文件信息的数组,每个文件信息包含文件夹名、文件名、文件类型和文件数据。
|
||||
*/
|
||||
export const generateJsonZipFiles = (jsonData) => {
|
||||
const files = [];
|
||||
// 遍历 jsonData 中的每个模块,为每个模块和语言组合生成一个文件对象
|
||||
for (const [module, objects] of jsonData.entries()) {
|
||||
for (const [lang, jsonObject] of Object.entries(objects)) {
|
||||
// 将生成的文件信息添加到 files 数组中
|
||||
files.push({
|
||||
folderName: lang, //文件夹名
|
||||
fileName: module, // 文件名
|
||||
fileType: 'json', // 文件类型
|
||||
fileData: convertToJsonBlob(jsonObject), // 文件数据
|
||||
});
|
||||
}
|
||||
}
|
||||
return files;
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析JSON Zip数据
|
||||
* @param jsonMap 包含模块名对应数据的JSON映射对象
|
||||
* @param langFileNameEnum 语言文件名枚举对象,将实际语言文件名映射到枚举值
|
||||
* @returns 返回一个Map对象,其中键为模块名,值为一个对象,该对象的键为语言文件名(根据langFileNameEnum转换),值为对应语言文件的内容(键为文件中的键,值为文件中的值)
|
||||
*/
|
||||
export const parseJsonZipData = (jsonMap, langFileNameEnum) => {
|
||||
const dataMap = new Map();
|
||||
for (const [module, rows] of jsonMap.entries()) {
|
||||
const fileObjects = {};
|
||||
const cellIndexMap = {};
|
||||
rows.forEach((row, rowNumber) => {
|
||||
if (rowNumber === 0) {
|
||||
row.forEach((cell, colNumber) => {
|
||||
if (colNumber !== 0) {
|
||||
fileObjects[langFileNameEnum[cell]] = {};
|
||||
cellIndexMap[colNumber] = langFileNameEnum[cell];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.keys(cellIndexMap)
|
||||
.map((item) => Number(item))
|
||||
.map((colNumber) => {
|
||||
const key = row[0];
|
||||
const value = row[colNumber];
|
||||
const lang = cellIndexMap[colNumber];
|
||||
fileObjects[lang][key] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
dataMap.set(module, fileObjects);
|
||||
}
|
||||
return dataMap;
|
||||
};
|
||||
|
||||
export const convertToJsonBlob = (jsonObject) => {
|
||||
const jsonString = JSON.stringify(jsonObject, null, 2);
|
||||
return new Blob([jsonString], { type: 'application/json' });
|
||||
};
|
||||
|
||||
/**
|
||||
* 将多个文件下载并打包成一个 zip 文件。
|
||||
* @param {Object} params 参数对象,包含待下载文件信息和 zip 文件名。
|
||||
* @param {Array} params.files 待下载的文件数组。
|
||||
* @param {string} params.zipName 打包后的 zip 文件名。
|
||||
*/
|
||||
export function downloadFiles2Zip(params) {
|
||||
const zip = new JsZip();
|
||||
// 待每个文件都写入完之后再生成 zip 文件
|
||||
params.files.map((file) => handleEachFile(file, zip));
|
||||
zip.generateAsync({ type: 'blob' }).then((blob) => {
|
||||
saveAs(blob, `${params.zipName}.zip`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理每个文件,将其添加到zip文件中。
|
||||
* @param {Object} param0 包含文件相关信息的对象
|
||||
* @param {string} param0.folderName 文件所属的文件夹名称(可选)
|
||||
* @param {string} param0.fileName 文件名
|
||||
* @param {string} param0.fileType 文件类型
|
||||
* @param {Blob|Uint8Array} param0.fileData 文件数据
|
||||
* @param {JSZip} zip JSZip对象,用于构建zip文件
|
||||
*/
|
||||
export const handleEachFile = ({ folderName, fileName, fileType, fileData }, zip) => {
|
||||
if (folderName) {
|
||||
zip.folder(folderName)?.file(`${fileName}.${fileType}`, fileData);
|
||||
} else {
|
||||
zip.file(`${filename}.${fileType}`, blob);
|
||||
}
|
||||
};
|
||||
41
vue/src/views/system/app/script/langTransformer.js
Normal file
41
vue/src/views/system/app/script/langTransformer.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// 获取语言包数据, new Map({fileName,{key:{ [lang]:value}}})
|
||||
export const getLangJson = () => {
|
||||
const all = require.context('@/lang', true, /^((?!index).)*\.json$/);
|
||||
const modules = new Map();
|
||||
all.keys().forEach((key) => {
|
||||
const lang = key.match(/(?<=\/)[^\/]+(?=\/|$)/)[0];
|
||||
const fileName = key.match(/(?<=\.\/[^\/]+\/)[^\.]+(?=\.\w+$)/)[0];
|
||||
const obj = modules.get(fileName) || {};
|
||||
Object.entries(all(key)).forEach(([key, value]) => {
|
||||
const item = obj[key] || {};
|
||||
item[lang] = value;
|
||||
obj[key] = item;
|
||||
});
|
||||
modules.set(fileName, obj);
|
||||
});
|
||||
return modules;
|
||||
};
|
||||
|
||||
// 转换为excel数据: [sheeNames:表,sheeData:单表数据]
|
||||
export const transoformToExcel = (array2, langs) => {
|
||||
const sheetNames = [];
|
||||
const sheetData = [];
|
||||
for (const [sheetName, arr] of array2.entries()) {
|
||||
sheetNames.push(sheetName);
|
||||
sheetData.push(transformToExcelSheet(arr, langs));
|
||||
}
|
||||
return [sheetNames, sheetData];
|
||||
};
|
||||
|
||||
// 转换为excel单表数据:[{'键值':'key','中文':'中文','英文':'English'}]
|
||||
export const transformToExcelSheet = (jsonData, langs) => {
|
||||
let data = [];
|
||||
for (const [key, obj] of Object.entries(jsonData)) {
|
||||
let item = { 键值: key };
|
||||
for (const [lang, label] of Object.entries(langs)) {
|
||||
item[label] = obj[lang] || '';
|
||||
}
|
||||
data.push(item);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
98
vue/src/views/system/app/script/xlsx.js
Normal file
98
vue/src/views/system/app/script/xlsx.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import * as XLSX from 'xlsx';
|
||||
import { saveAs } from 'file-saver';
|
||||
/**
|
||||
* 导出excel
|
||||
*/
|
||||
export const exportExcel = ([sheetNames, sheetsData], filename = '多语言包.xlsx') => {
|
||||
// 创建workbook对象
|
||||
let wb = XLSX.utils.book_new();
|
||||
sheetNames.forEach((sheetName, index) => {
|
||||
const sheeData = sheetsData[index];
|
||||
// 把json转为worksheet对象
|
||||
let ws = XLSX.utils.json_to_sheet(sheeData);
|
||||
// 计算每列最大字符数作为列宽
|
||||
const colWidths = getColumnWidths(sheeData);
|
||||
// 设置列宽
|
||||
setColumnWidths(ws, colWidths);
|
||||
// 添加worksheet 到 workbook
|
||||
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||
});
|
||||
// 写出 arraybuffer 数据
|
||||
let wb_out = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||
// 构建Blob对象
|
||||
let _blob = new Blob([wb_out], { type: 'application/octet-stream' });
|
||||
//下载
|
||||
saveAs(_blob, filename);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取表格列的宽度数组。
|
||||
*/
|
||||
export const getColumnWidths = (data) => {
|
||||
const headers = Object.keys(data[0]);
|
||||
// 计算列宽
|
||||
const colWidths = [];
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let maxWidth = getCellWidth(headers[i]);
|
||||
let key = headers[i];
|
||||
let cellWidth = 0;
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
cellWidth = getCellWidth(data[j][key]);
|
||||
if (data[j][key] && cellWidth > maxWidth) {
|
||||
maxWidth = cellWidth;
|
||||
}
|
||||
}
|
||||
colWidths.push(maxWidth);
|
||||
}
|
||||
return colWidths;
|
||||
};
|
||||
/**
|
||||
* 计算单元格宽度
|
||||
*/
|
||||
const getCellWidth = (value) => {
|
||||
if (/.*[\u4e00-\u9fa5]+.*$/.test(value)) {
|
||||
return parseFloat(value.toString().length * 2.1);
|
||||
} else {
|
||||
return parseFloat(value.toString().length * 1.1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置工作表的列宽
|
||||
*/
|
||||
export const setColumnWidths = (ws, columnWidths) => {
|
||||
ws['!cols'] = columnWidths.map((_, i) => ({
|
||||
wch: columnWidths[i] || 30,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析文件为 JSON
|
||||
* @param file 文件
|
||||
* @returns 解析后的 JSON 数据
|
||||
*/
|
||||
export const parseJson = async (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
try {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
const workbook = XLSX.read(data, { type: 'array' });
|
||||
const jsonMap = new Map();
|
||||
for (let index = 0; index < workbook.SheetNames.length; index++) {
|
||||
const sheetName = workbook.SheetNames[index];
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||
jsonMap.set(sheetName, jsonData);
|
||||
}
|
||||
resolve(jsonMap);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
reader.onerror = () => {
|
||||
reject(new Error('文件读取失败'));
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user