refalactor:国际化前端

This commit is contained in:
PZ688 2025-02-28 18:19:00 +08:00
parent f4f1c5c4e6
commit 04951cefbd
5 changed files with 60 additions and 44 deletions

View File

@ -4,18 +4,18 @@
<template #header> <template #header>
<div style="color: #fff"> <div style="color: #fff">
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UploadFilled /> </el-icon> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UploadFilled /> </el-icon>
<span> 数据导入 </span> <span> {{ t('list.dataImport') }} </span>
</div> </div>
</template> </template>
<el-row :gutter="15" v-loading="state.loading"> <el-row :gutter="15" v-loading="state.loading">
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12"> <el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<el-button class="ml10" type="info" icon="ele-Download" v-reclick="3000" @click="() => download()" :disabled="state.loading">模板</el-button> <el-button class="ml10" type="info" icon="ele-Download" v-reclick="3000" @click="() => download()" :disabled="state.loading">{{ t('list.template') }}</el-button>
</el-col> </el-col>
<el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12"> <el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
<el-upload :limit="1" :show-file-list="false" :on-exceed="handleExceed" :http-request="handleImportData" ref="uploadRef"> <el-upload :limit="1" :show-file-list="false" :on-exceed="handleExceed" :http-request="handleImportData" ref="uploadRef">
<template #trigger> <template #trigger>
<el-button type="primary" icon="ele-MostlyCloudy" v-reclick="3000" :disabled="state.loading">导入</el-button> <el-button type="primary" icon="ele-MostlyCloudy" v-reclick="3000" :disabled="state.loading">{{ t('list.import') }}</el-button>
</template> </template>
</el-upload> </el-upload>
</el-col> </el-col>
@ -23,7 +23,7 @@
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button icon="ele-CircleCloseFilled" @click="() => (state.isShowDialog = false)" :disabled="state.loading"> </el-button> <el-button icon="ele-CircleCloseFilled" @click="() => (state.isShowDialog = false)" :disabled="state.loading">{{ t('list.cancelButtonText') }}</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@ -35,7 +35,9 @@ import type { UploadInstance, UploadProps, UploadRawFile, UploadRequestOptions }
import { ElUpload, ElMessage, genFileId } from 'element-plus'; import { ElUpload, ElMessage, genFileId } from 'element-plus';
import { downloadStreamFile } from '/@/utils/download'; import { downloadStreamFile } from '/@/utils/download';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const uploadRef = ref<UploadInstance>(); const uploadRef = ref<UploadInstance>();
const state = reactive({ const state = reactive({
isShowDialog: false, isShowDialog: false,
@ -80,7 +82,7 @@ const download = () => {
props props
.download() .download()
.then((res: any) => downloadStreamFile(res)) .then((res: any) => downloadStreamFile(res))
.catch((res: any) => ElMessage.error('下载错误: ' + res)); .catch((res: any) => ElMessage.error(`${t('list.downloadError')}: ${res}`));
}; };
// //

View File

@ -5,7 +5,7 @@
<slot name="command"></slot> <slot name="command"></slot>
</div> </div>
<div v-loading="state.exportLoading" class="table-footer-tool"> <div v-loading="state.exportLoading" class="table-footer-tool">
<SvgIcon v-if="!config.hideRefresh" name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" /> <SvgIcon v-if="!config.hideRefresh" name="iconfont icon-shuaxin" :size="22" :title="t('message.list.refresh')" @click="onRefreshTable" />
<el-tooltip effect="light" :content="state.switchFixedContent" placement="bottom-start" :show-after="200" v-if="state.haveFixed"> <el-tooltip effect="light" :content="state.switchFixedContent" placement="bottom-start" :show-after="200" v-if="state.haveFixed">
<el-icon :style="{ color: state.fixedIconColor }" @click="switchFixed"><ele-Switch /></el-icon> <el-icon :style="{ color: state.fixedIconColor }" @click="switchFixed"><ele-Switch /></el-icon>
</el-tooltip> </el-tooltip>
@ -13,24 +13,24 @@
<SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" /> <SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" />
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="onExportTable">导出本页数据</el-dropdown-item> <el-dropdown-item @click="onExportTable">{{ t('message.list.exportCurrentPage') }}</el-dropdown-item>
<el-dropdown-item @click="onExportTableAll">导出全部数据</el-dropdown-item> <el-dropdown-item @click="onExportTableAll">{{ t('message.list.exportAll') }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<SvgIcon v-if="!config.hidePrint" name="iconfont icon-dayin" :size="19" title="打印" @click="onPrintTable" /> <SvgIcon v-if="!config.hidePrint" name="iconfont icon-dayin" :size="19" :title="t('message.list.print')" @click="onPrintTable" />
<el-popover v-if="!config.hideSet" placement="bottom-end" trigger="click" transition="el-zoom-in-top" popper-class="table-tool-popper" :width="300" :persistent="false" @show="onSetTable"> <el-popover v-if="!config.hideSet" placement="bottom-end" trigger="click" transition="el-zoom-in-top" popper-class="table-tool-popper" :width="300" :persistent="false" @show="onSetTable">
<template #reference> <template #reference>
<SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" /> <SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" :title="t('message.list.settings')" />
</template> </template>
<template #default> <template #default>
<div class="tool-box"> <div class="tool-box">
<el-tooltip content="拖动进行排序" placement="top-start"> <el-tooltip :content="t('message.list.dragToSort')" placement="top-start">
<SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" /> <SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
</el-tooltip> </el-tooltip>
<el-checkbox v-model="state.checkListAll" :indeterminate="state.checkListIndeterminate" class="ml10 mr1" label="列显示" @change="onCheckAllChange" /> <el-checkbox v-model="state.checkListAll" :indeterminate="state.checkListIndeterminate" class="ml10 mr1" :label="t('message.list.columnDisplay')" @change="onCheckAllChange" />
<el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" /> <el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" :label="t('message.list.seq')" />
<el-checkbox v-if="getConfig.showSelection" v-model="getConfig.isSelection" class="ml12 mr1" label="多选" /> <el-checkbox v-if="getConfig.showSelection" v-model="getConfig.isSelection" class="ml12 mr1" :label="t('message.list.multiSelect')" />
</div> </div>
<el-scrollbar> <el-scrollbar>
<div ref="toolSetRef" class="tool-sortable"> <div ref="toolSetRef" class="tool-sortable">
@ -110,7 +110,7 @@
</el-table-column> </el-table-column>
</el-table-column> </el-table-column>
<template #empty> <template #empty>
<el-empty description="暂无数据" /> <el-empty :description="t('message.list.noData')" />
</template> </template>
</el-table> </el-table>
<div v-if="!config.hidePagination && state.showPagination" class="table-footer mt15"> <div v-if="!config.hidePagination && state.showPagination" class="table-footer mt15">
@ -141,6 +141,7 @@ import { exportExcel } from '/@/utils/exportExcel';
// import '/@/theme/tableTool.scss'; // import '/@/theme/tableTool.scss';
import printJs from 'print-js'; import printJs from 'print-js';
import formatter from '/@/components/table/formatter.vue'; import formatter from '/@/components/table/formatter.vue';
import { useI18n } from 'vue-i18n';
// //
const props = defineProps({ const props = defineProps({
@ -188,6 +189,7 @@ const toolSetRef = ref();
const tableRef = ref(); const tableRef = ref();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const { t } = useI18n();
const state = reactive({ const state = reactive({
data: [] as Array<EmptyObjectType>, data: [] as Array<EmptyObjectType>,
loading: false, loading: false,
@ -208,7 +210,7 @@ const state = reactive({
haveFixed: false, haveFixed: false,
currentFixed: false, currentFixed: false,
serialNoFixed: false, serialNoFixed: false,
switchFixedContent: '取消固定列', switchFixedContent: t('message.list.unfixColumn'),
fixedIconColor: themeConfig.value.primary, fixedIconColor: themeConfig.value.primary,
}); });
@ -284,12 +286,12 @@ const pageReset = () => {
}; };
// //
const onExportTable = () => { const onExportTable = () => {
if (setHeader.value.length <= 0) return ElMessage.error('没有勾选要导出的列'); if (setHeader.value.length <= 0) return ElMessage.error(t('message.list.noSelectedColumns'));
exportData(state.data); exportData(state.data);
}; };
// //
const onExportTableAll = async () => { const onExportTableAll = async () => {
if (setHeader.value.length <= 0) return ElMessage.error('没有勾选要导出的列'); if (setHeader.value.length <= 0) return ElMessage.error(t('message.list.noSelectedColumns'));
state.exportLoading = true; state.exportLoading = true;
const param = Object.assign({}, props.param, { page: 1, pageSize: 9999999 }); const param = Object.assign({}, props.param, { page: 1, pageSize: 9999999 });
const res = await props.getData(param); const res = await props.getData(param);
@ -299,7 +301,7 @@ const onExportTableAll = async () => {
}; };
// //
const exportData = (data: Array<EmptyObjectType>) => { const exportData = (data: Array<EmptyObjectType>) => {
if (data.length <= 0) return ElMessage.error('没有数据可以导出'); if (data.length <= 0) return ElMessage.error(t('message.list.noDataToExport'));
state.exportLoading = true; state.exportLoading = true;
let exportData = JSON.parse(JSON.stringify(data)); let exportData = JSON.parse(JSON.stringify(data));
if (props.exportChangeData) { if (props.exportChangeData) {
@ -311,7 +313,7 @@ const exportData = (data: Array<EmptyObjectType>) => {
setHeader.value.filter((item) => { setHeader.value.filter((item) => {
return item.type != 'action'; return item.type != 'action';
}), }),
'导出数据' t('message.list.exportData')
); );
state.exportLoading = false; state.exportLoading = false;
}; };
@ -434,7 +436,7 @@ const clearFixed = () => {
const switchFixed = () => { const switchFixed = () => {
state.currentFixed = !state.currentFixed; state.currentFixed = !state.currentFixed;
state.switchFixedContent = state.currentFixed ? '取消固定列' : '启用固定列'; state.switchFixedContent = state.currentFixed ? t('message.list.unfixColumn') : t('message.list.fixColumn');
if (state.currentFixed) { if (state.currentFixed) {
state.fixedIconColor = themeConfig.value.primary; state.fixedIconColor = themeConfig.value.primary;
state.columns = JSON.parse(JSON.stringify(state.oldColumns)); state.columns = JSON.parse(JSON.stringify(state.oldColumns));

View File

@ -2,55 +2,58 @@
<el-popover placement="bottom" width="300" trigger="hover"> <el-popover placement="bottom" width="300" trigger="hover">
<template #reference> <template #reference>
<el-text type="primary" class="cursor-default"> <el-text type="primary" class="cursor-default">
<el-icon><ele-InfoFilled /></el-icon> <el-icon><ele-InfoFilled /></el-icon>{{ t('message.list.detail') }}
</el-text> </el-text>
</template> </template>
<el-descriptions direction="vertical" :column="2" border> <el-descriptions direction="vertical" :column="2" border>
<el-descriptions-item width="140"> <el-descriptions-item width="140">
<template #label> <template #label>
<el-text> <el-text>
<el-icon><ele-UserFilled /></el-icon> <el-icon><ele-UserFilled /></el-icon>{{ t('message.list.creator') }}
</el-text> </el-text>
</template> </template>
<el-tag>{{ props.data.createUserName ?? '无' }}</el-tag> <el-tag>{{ props.data.createUserName ?? t('message.list.none') }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-text> <el-text>
<el-icon><ele-Calendar /></el-icon> <el-icon><ele-Calendar /></el-icon>{{ t('message.list.createTime') }}
</el-text> </el-text>
</template> </template>
<el-tag>{{ props.data.createTime ?? '无' }}</el-tag> <el-tag>{{ props.data.createTime ?? t('message.list.none') }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-text> <el-text>
<el-icon><ele-UserFilled /></el-icon> <el-icon><ele-UserFilled /></el-icon>{{ t('message.list.modifier') }}
</el-text> </el-text>
</template> </template>
<el-tag>{{ props.data.updateUserName ?? '无' }}</el-tag> <el-tag>{{ props.data.updateUserName ?? t('message.list.none') }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-text> <el-text>
<el-icon><ele-Calendar /></el-icon> <el-icon><ele-Calendar /></el-icon>{{ t('message.list.modifyTime') }}
</el-text> </el-text>
</template> </template>
<el-tag>{{ props.data.updateTime ?? '无' }}</el-tag> <el-tag>{{ props.data.updateTime ?? t('message.list.none') }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item v-if="'remark' in props.data"> <el-descriptions-item v-if="'remark' in props.data">
<template #label> <template #label>
<el-text> <el-text>
<el-icon><ele-Tickets /></el-icon> <el-icon><ele-Tickets /></el-icon>{{ t('message.list.remark') }}
</el-text> </el-text>
</template> </template>
{{ props.data.remark ?? '无' }} {{ props.data.remark ?? t('message.list.none') }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</el-popover> </el-popover>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
data: ModifyRecord; data: ModifyRecord;
}>(); }>();

View File

@ -9,7 +9,7 @@
label-width="auto" label-width="auto"
:label="val.label" :label="val.label"
:prop="val.prop" :prop="val.prop"
:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: val.type === 'input' ? 'blur' : 'change' }]" :rules="[{ required: val.required, message: `${val.label}${t('message.list.required')}`, trigger: val.type === 'input' ? 'blur' : 'change' }]"
> >
<el-input <el-input
v-model="state.innerModelValue[val.prop]" v-model="state.innerModelValue[val.prop]"
@ -47,9 +47,9 @@
v-bind="val.comProps" v-bind="val.comProps"
type="daterange" type="daterange"
value-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
range-separator="" :range-separator="t('message.list.to')"
start-placeholder="开始日期" :start-placeholder="t('message.list.startDate')"
end-placeholder="结束日期" :end-placeholder="t('message.list.endDate')"
:clearable="!val.required" :clearable="!val.required"
:shortcuts="shortcuts" :shortcuts="shortcuts"
:default-time="defaultTime" :default-time="defaultTime"
@ -89,8 +89,8 @@
<div> <div>
<!-- 使用el-button-group会导致具有type属性的按钮的右边框无法显示 --> <!-- 使用el-button-group会导致具有type属性的按钮的右边框无法显示 -->
<!-- <el-button-group> --> <!-- <el-button-group> -->
<el-button plain type="primary" icon="ele-Search" @click="onSearch(tableSearchRef)"> 查询 </el-button> <el-button plain type="primary" icon="ele-Search" @click="onSearch(tableSearchRef)"> {{ t('message.list.query') }} </el-button>
<el-button icon="ele-Refresh" @click="onReset(tableSearchRef)" style="margin-left: 12px"> 重置 </el-button> <el-button icon="ele-Refresh" @click="onReset(tableSearchRef)" style="margin-left: 12px"> {{ t('message.list.reset') }} </el-button>
<!-- </el-button-group> --> <!-- </el-button-group> -->
</div> </div>
</el-form-item> </el-form-item>
@ -107,6 +107,9 @@
import { reactive, ref, watch } from 'vue'; import { reactive, ref, watch } from 'vue';
import type { FormInstance } from 'element-plus'; import type { FormInstance } from 'element-plus';
import { dayjs } from 'element-plus'; import { dayjs } from 'element-plus';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
// //
const props = defineProps({ const props = defineProps({
@ -174,7 +177,7 @@ const defaultTime = ref<[Date, Date]>([new Date(2000, 1, 1, 0, 0, 0), new Date(2
/** 时间范围快捷选择 */ /** 时间范围快捷选择 */
const shortcuts = [ const shortcuts = [
{ {
text: '7天内', text: t('message.list.last7Days'),
value: () => { value: () => {
const end = dayjs().endOf('day').toDate(); const end = dayjs().endOf('day').toDate();
const start = dayjs().startOf('day').add(-7, 'day').toDate(); const start = dayjs().startOf('day').add(-7, 'day').toDate();
@ -182,7 +185,7 @@ const shortcuts = [
}, },
}, },
{ {
text: '1个月内', text: t('message.list.lastMonth'),
value: () => { value: () => {
const end = dayjs().endOf('day').toDate(); const end = dayjs().endOf('day').toDate();
const start = dayjs().startOf('day').add(-1, 'month').toDate(); const start = dayjs().startOf('day').add(-1, 'month').toDate();
@ -190,7 +193,7 @@ const shortcuts = [
}, },
}, },
{ {
text: '3个月内', text: t('message.list.last3Months'),
value: () => { value: () => {
const end = dayjs().endOf('day').toDate(); const end = dayjs().endOf('day').toDate();
const start = dayjs().startOf('day').add(-3, 'month').toDate(); const start = dayjs().startOf('day').add(-3, 'month').toDate();

View File

@ -6,10 +6,11 @@
<th v-for="(citem, ci) in $props.columns" :key="ci" v-show="citem.ifShow == undefined ? true : citem.ifShow" style="text-align: center"> <th v-for="(citem, ci) in $props.columns" :key="ci" v-show="citem.ifShow == undefined ? true : citem.ifShow" style="text-align: center">
{{ citem.label }} {{ citem.label }}
</th> </th>
<th style="text-align: center" v-show="!$props.disabled">操作</th> <th style="text-align: center" v-show="!$props.disabled">{{ t('message.list.operation') }}</th>
</tr> </tr>
</thead> </thead>
<tbody class="el-table-tbody"> <tbody class="el-table-tbody">
<tr v-for="(item, index) in vm.value" :key="index"> <tr v-for="(item, index) in vm.value" :key="index">
<td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow"> <td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow">
<component v-if="citem.component != 'el-select'" :is="citem.component" v-model="item[citem.field]" v-bind="renderComponentProp(citem)" :disabled="$props.disabled" /> <component v-if="citem.component != 'el-select'" :is="citem.component" v-model="item[citem.field]" v-bind="renderComponentProp(citem)" :disabled="$props.disabled" />
@ -18,10 +19,11 @@
</el-select> </el-select>
</td> </td>
<td style="text-align: center" v-show="!$props.disabled"> <td style="text-align: center" v-show="!$props.disabled">
<el-button type="danger" @click="del(item, index)">删除</el-button> <el-button type="danger" @click="del(item, index)">{{ t('message.list.delete') }}</el-button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow"> <td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow">
@ -31,10 +33,11 @@
</el-select> </el-select>
</td> </td>
<td class="el-table-cell el-table-cell-ellipsis" style="text-align: center" v-show="!$props.disabled"> <td class="el-table-cell el-table-cell-ellipsis" style="text-align: center" v-show="!$props.disabled">
<el-button type="primary" @click="add">添加</el-button> <el-button type="primary" @click="add">{{ t('message.list.add') }}</el-button>
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</template> </template>
@ -44,6 +47,9 @@ import { reactive } from 'vue';
import { ElMessage, dayjs } from 'element-plus'; import { ElMessage, dayjs } from 'element-plus';
import AsyncValidator from 'async-validator'; import AsyncValidator from 'async-validator';
import { isDate, isString } from 'lodash-es'; import { isDate, isString } from 'lodash-es';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const props = defineProps({ const props = defineProps({
value: { value: {