init
This commit is contained in:
300
src/views/lowdesign/i18nConfig/ImportI18n.vue
Normal file
300
src/views/lowdesign/i18nConfig/ImportI18n.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="import-data-box pb-50px!">
|
||||
<div class="top-steps">
|
||||
<el-steps :active="stepValue" align-center finish-status="finish" process-status="finish">
|
||||
<el-step
|
||||
v-for="(title, index) in stepList"
|
||||
:key="title"
|
||||
:class="{ success: stepValue > index, active: stepValue == index }"
|
||||
>
|
||||
<template #title>
|
||||
<div class="text-14px"> {{ title }} </div>
|
||||
</template>
|
||||
<template #icon>
|
||||
<div class="step-icon"></div>
|
||||
</template>
|
||||
</el-step>
|
||||
</el-steps>
|
||||
</div>
|
||||
<div class="import-content">
|
||||
<div v-if="stepValue == 0">
|
||||
<div class="file-item flex" v-for="(item, key) in uploadList" :key="key">
|
||||
<div class="item-icon flex-basis-100px flex-shrink-0 bg-[var(--el-fill-color-light)]">
|
||||
<Icon color="#999" :icon="item.icon" :size="60"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 pl-20px pt-10px pb-10px">
|
||||
<div class="text-16px">{{ item.title }}</div>
|
||||
<div class="text-14px pt-10px pb-10px">{{ item.tip }}</div>
|
||||
<div v-if="key == 'upload' && !files?.length">
|
||||
<el-button type="primary" :loading="item.loading" link @click="open()">
|
||||
{{ item.btnText }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="key == 'upload' && files?.length" class="flex items-center">
|
||||
<Icon :size="14" icon="ep:link"></Icon>
|
||||
<div class="c-#409EFF pl-2px">{{ files[0].name }}</div>
|
||||
<div class="c-#999">({{ fileSizeFormatter(files[0], 'size', files[0].size) }})</div>
|
||||
<Icon :size="14" icon="ep:close" @click="reset" class="cursor-pointer"></Icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="stepValue == 1" class="w-80% m-auto mt-40px mb-80px">
|
||||
<el-progress
|
||||
:text-inside="true"
|
||||
:stroke-width="18"
|
||||
:percentage="progress"
|
||||
:format="() => progress + '%'"
|
||||
/>
|
||||
<div class="text-center text-16px mt-10px">
|
||||
<span>
|
||||
{{ t('Avue.crud.importStep_3.playTip') }}
|
||||
({{ importInfo.handleCou }}/{{ importInfo.totalCou }})...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="stepValue == 2" class="mt-20px pb-40px">
|
||||
<div class="flex flex-col items-center">
|
||||
<Icon color="#67C23A" icon="mdi:success-circle-outline" :size="60"></Icon>
|
||||
<div class="text-20px pt-10px pb-10px">{{ t('Avue.crud.importStep_4.tip') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="pos-absolute left-0 bottom-0 w-100% box-border px-20px py-10px flex justify-end bg-[var(--el-bg-color)] b-solid b-0px b-t-1px b-#f1f1f1 dark:b-[var(--el-border-color-dark)]"
|
||||
>
|
||||
<template v-if="stepValue == 0">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!files || !files?.length"
|
||||
:loading="loading"
|
||||
@click="importData()"
|
||||
>
|
||||
<Icon v-if="!loading" icon="ep:view" :size="16"> </Icon>
|
||||
<span>开始导入数据</span>
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template v-if="stepValue == 2">
|
||||
<el-button type="default" @click="closeImport">
|
||||
<Icon icon="ep:close" :size="14" class="mr-3px"> </Icon>
|
||||
{{ t('dialog.close') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as I18nApi from '@/api/design/i18n/index'
|
||||
import { fileSizeFormatter } from '@/utils/index'
|
||||
import { useFileDialog } from '@vueuse/core'
|
||||
defineOptions({ name: 'ImportData' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
localeMap: object[]
|
||||
isFull?: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits(['close-popup', 'reset-change'])
|
||||
const { t } = useI18n()
|
||||
|
||||
const stepList = [
|
||||
t('Avue.crud.importStep_1.title'),
|
||||
t('Avue.crud.importStep_3.title'),
|
||||
t('Avue.crud.importStep_4.title')
|
||||
]
|
||||
const uploadList = ref({
|
||||
upload: {
|
||||
icon: 'uiw:cloud-upload',
|
||||
title: '上传已经完善国际化的信息表',
|
||||
tip: t('Avue.crud.importStep_1.tip_2'),
|
||||
btnText: t('Avue.crud.importStep_1.btn_2'),
|
||||
loading: false
|
||||
}
|
||||
})
|
||||
const stepValue = ref(0)
|
||||
const loading = ref(false)
|
||||
const importInfo = ref({ batchCode: 0, handleCou: 0, totalCou: 0 })
|
||||
const timer = ref<any>(null)
|
||||
const isStop = ref(false)
|
||||
const viewRef = ref()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { files, open, reset } = useFileDialog({
|
||||
multiple: false, //可选:是否可以多选文件
|
||||
accept: '.xls,.xlsx', //可选:自定义上传文件类型
|
||||
reset: true //可选:再次选择时是否把之前选的文件清除
|
||||
})
|
||||
|
||||
const progress = computed(() => {
|
||||
return Number(((importInfo.value.handleCou / importInfo.value.totalCou) * 100).toFixed(2))
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
stepValue.value = 0
|
||||
isStop.value = false
|
||||
for (let key in importInfo.value) importInfo.value[key] = 0
|
||||
reset()
|
||||
}
|
||||
|
||||
const rollPoling = async (done?, type?) => {
|
||||
if (timer.value) clearTimeout(timer.value)
|
||||
const data = await I18nApi.getImportProgress(importInfo.value.batchCode).catch(() => false)
|
||||
console.log(data)
|
||||
if (data !== false) {
|
||||
importInfo.value.handleCou = data.handleCou
|
||||
importInfo.value.totalCou = data.totalCou
|
||||
if (type == 'init') {
|
||||
done()
|
||||
stepValue.value = 1
|
||||
}
|
||||
}
|
||||
|
||||
if (data.handleCou < data.totalCou) {
|
||||
timer.value = setTimeout(() => {
|
||||
rollPoling()
|
||||
}, 2000)
|
||||
} else stepValue.value = 2
|
||||
}
|
||||
|
||||
const importData = async () => {
|
||||
await message.confirm(t('Avue.crud.importTips.importDataBtnTip'))
|
||||
loading.value = true
|
||||
const file = files.value ? files.value[0] : ''
|
||||
const uploadRes = await I18nApi.uploadFile({ updateSupport: 1, file })
|
||||
const res = await I18nApi.uploadExcelData({
|
||||
fileUrl: uploadRes.data.fileUrl,
|
||||
exportLangParam: props.localeMap.map((item) => `${item['lang']}=${item['name']}`).join(','),
|
||||
file
|
||||
}).catch(() => false)
|
||||
const done = () => (loading.value = false)
|
||||
if (res) {
|
||||
importInfo.value.batchCode = res.data
|
||||
rollPoling(done, 'init')
|
||||
} else done()
|
||||
}
|
||||
|
||||
const handleClose = async (done) => {
|
||||
if (timer.value) clearTimeout(timer.value)
|
||||
emit('reset-change')
|
||||
done()
|
||||
}
|
||||
|
||||
const closeImport = () => {
|
||||
emit('reset-change')
|
||||
emit('close-popup')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val && stepValue.value !== 1) {
|
||||
init()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props?.isFull,
|
||||
(val) => {
|
||||
viewRef.value?.setCalcHeight(val ? 80 : 215)
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({ handleClose })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.import-data-box {
|
||||
padding: 20px;
|
||||
|
||||
::v-deep(.el-steps) {
|
||||
.el-step__icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.el-step__line {
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.el-step__head {
|
||||
&.is-success {
|
||||
.step-icon {
|
||||
background-color: var(--el-color-success);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-finish {
|
||||
.step-icon {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-wait {
|
||||
.step-icon {
|
||||
background-color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-step {
|
||||
&.active {
|
||||
.step-icon {
|
||||
background-color: var(--el-bg-color);
|
||||
border: 2px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-step__line::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 50%;
|
||||
height: 2px;
|
||||
background-color: var(--el-color-primary);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&.success {
|
||||
.el-step__line {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.import-content {
|
||||
padding: 20px 40px;
|
||||
|
||||
.file-item {
|
||||
margin-bottom: 20px;
|
||||
border: var(--el-border);
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100px;
|
||||
border-right: var(--el-border);
|
||||
}
|
||||
}
|
||||
|
||||
.error-list {
|
||||
padding: 15px 20px;
|
||||
border: 1px solid #ece9e9;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
180
src/views/lowdesign/i18nConfig/index.vue
Normal file
180
src/views/lowdesign/i18nConfig/index.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:table-loading="loading"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
:size="size"
|
||||
@click="importDialog = true"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['jeelowcode:i18n:import']"
|
||||
>
|
||||
<Icon icon="clarity:import-line"></Icon>
|
||||
<span>导入国际化配置</span>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
:size="size"
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['jeelowcode:i18n:export']"
|
||||
>
|
||||
<Icon icon="clarity:export-line"></Icon>
|
||||
<span>导出国际化配置</span>
|
||||
</el-button>
|
||||
</template>
|
||||
<template #fileName="{ row }">
|
||||
<div
|
||||
class="cursor-pointer hover:c-#409EFF"
|
||||
@click="downloadByUrl({ url: row.fileUrl, fileName: row.fileName })"
|
||||
>
|
||||
{{ row.fileName }}
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<!-- 导入 -->
|
||||
<DesignPopup
|
||||
v-model="importDialog"
|
||||
title="导入国际化配置"
|
||||
width="900px"
|
||||
:dialog-params="{ alignCenter: true }"
|
||||
:handleClose="importRef?.handleClose"
|
||||
>
|
||||
<template #default="{ isFull }">
|
||||
<ImportI18n
|
||||
ref="importRef"
|
||||
:show="importDialog"
|
||||
:isFull="isFull"
|
||||
:locale-map="localeStore.localeMap"
|
||||
@close-popup="importDialog = false"
|
||||
@reset-change="resetChange"
|
||||
></ImportI18n>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { downloadByUrl } from '@/utils/filt'
|
||||
import * as I18nApi from '@/api/design/i18n'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import ImportI18n from './ImportI18n.vue'
|
||||
import { useLocaleStoreWithOut } from '@/store/modules/locale'
|
||||
|
||||
defineOptions({ name: 'SystemPost' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const { getCurrPermi } = useCrudPermi()
|
||||
const localeStore = useLocaleStoreWithOut()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const tableOption = reactive({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
addBtn: false,
|
||||
menu: false,
|
||||
column: {
|
||||
fileName: {
|
||||
label: '导入文件'
|
||||
},
|
||||
userName: {
|
||||
label: '操作人',
|
||||
dicData: getIntDictOptions(DICT_TYPE.COMMON_STATUS),
|
||||
rules: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
value: CommonStatusEnum.ENABLE
|
||||
},
|
||||
createTime: {
|
||||
label: '导入时间',
|
||||
display: false,
|
||||
type: 'datetime',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间',
|
||||
formatter: (row, val, value, column) => {
|
||||
return dateFormatter(row, column, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<{ id?: number }>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const crudRef = ref()
|
||||
|
||||
const importDialog = ref(false)
|
||||
const importRef = ref()
|
||||
|
||||
useCrudHeight(crudRef)
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize
|
||||
}
|
||||
try {
|
||||
const data = await I18nApi.getImportList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const resetChange = () => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await I18nApi.exportExcelData({ param: localeStore.localeMap })
|
||||
download.excel(data, '国际化配置.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTableData()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user