提交 dc09de1e 作者: vben

feat: multi-language component

上级 e5f8ce3f
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
### 🎫 Chores ### 🎫 Chores
- 更新 antdv 到`2.0.0-rc.2` - 更新 antdv 到`2.0.0-rc.2`
- 更新 vue 到`3.0.3`
- 更新 vite 到`1.0.0.rc10`
- 暂时删除 `@vueuse/core`.等稳定后在集成。目前不太稳定。 - 暂时删除 `@vueuse/core`.等稳定后在集成。目前不太稳定。
## 2.0.0-rc.11 (2020-11-18) ## 2.0.0-rc.11 (2020-11-18)
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"sortablejs": "^1.12.0", "sortablejs": "^1.12.0",
"vditor": "^3.6.6", "vditor": "^3.6.6",
"vue": "^3.0.2", "vue": "^3.0.3",
"vue-i18n": "^9.0.0-beta.8", "vue-i18n": "^9.0.0-beta.8",
"vue-router": "^4.0.0-rc.5", "vue-router": "^4.0.0-rc.5",
"vuex": "^4.0.0-rc.1", "vuex": "^4.0.0-rc.1",
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.8.2", "@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2", "@typescript-eslint/parser": "^4.8.2",
"@vue/compiler-sfc": "^3.0.2", "@vue/compiler-sfc": "^3.0.3",
"@vuedx/typecheck": "^0.2.4-0", "@vuedx/typecheck": "^0.2.4-0",
"@vuedx/typescript-plugin-vue": "^0.2.4-0", "@vuedx/typescript-plugin-vue": "^0.2.4-0",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
"tasksfile": "^5.1.1", "tasksfile": "^5.1.1",
"ts-node": "^9.0.0", "ts-node": "^9.0.0",
"typescript": "^4.1.2", "typescript": "^4.1.2",
"vite": "^1.0.0-rc.9", "vite": "^1.0.0-rc.10",
"vite-plugin-html": "^1.0.0-beta.2", "vite-plugin-html": "^1.0.0-beta.2",
"vite-plugin-mock": "^1.0.6", "vite-plugin-mock": "^1.0.6",
"vite-plugin-purge-icons": "^0.4.5", "vite-plugin-purge-icons": "^0.4.5",
......
import './index.less';
import type { DrawerInstance, DrawerProps } from './types'; import type { DrawerInstance, DrawerProps } from './types';
import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue'; import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue';
...@@ -13,8 +15,7 @@ import { getSlot } from '/@/utils/helper/tsxHelper'; ...@@ -13,8 +15,7 @@ import { getSlot } from '/@/utils/helper/tsxHelper';
import { isFunction, isNumber } from '/@/utils/is'; import { isFunction, isNumber } from '/@/utils/is';
import { buildUUID } from '/@/utils/uuid'; import { buildUUID } from '/@/utils/uuid';
import { deepMerge } from '/@/utils'; import { deepMerge } from '/@/utils';
import { useI18n } from '/@/hooks/web/useI18n';
import './index.less';
const prefixCls = 'basic-drawer'; const prefixCls = 'basic-drawer';
export default defineComponent({ export default defineComponent({
...@@ -27,6 +28,8 @@ export default defineComponent({ ...@@ -27,6 +28,8 @@ export default defineComponent({
const visibleRef = ref(false); const visibleRef = ref(false);
const propsRef = ref<Partial<DrawerProps> | null>(null); const propsRef = ref<Partial<DrawerProps> | null>(null);
const { t } = useI18n('component.drawer');
const getMergeProps = computed((): any => { const getMergeProps = computed((): any => {
return deepMerge(toRaw(props), unref(propsRef)); return deepMerge(toRaw(props), unref(propsRef));
}); });
...@@ -208,7 +211,7 @@ export default defineComponent({ ...@@ -208,7 +211,7 @@ export default defineComponent({
> >
<FullLoading <FullLoading
absolute absolute
tip="加载中..." tip={t('loadingText')}
class={[!unref(getProps).loading ? 'hidden' : '']} class={[!unref(getProps).loading ? 'hidden' : '']}
/> />
{getSlot(slots, 'default')} {getSlot(slots, 'default')}
......
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.drawer');
export const footerProps = { export const footerProps = {
confirmLoading: Boolean as PropType<boolean>, confirmLoading: Boolean as PropType<boolean>,
/** /**
...@@ -11,7 +15,7 @@ export const footerProps = { ...@@ -11,7 +15,7 @@ export const footerProps = {
cancelButtonProps: Object as PropType<any>, cancelButtonProps: Object as PropType<any>,
cancelText: { cancelText: {
type: String as PropType<string>, type: String as PropType<string>,
default: '关闭', default: t('cancelText'),
}, },
/** /**
* @description: Show confirmation button * @description: Show confirmation button
...@@ -23,7 +27,7 @@ export const footerProps = { ...@@ -23,7 +27,7 @@ export const footerProps = {
okButtonProps: Object as PropType<any>, okButtonProps: Object as PropType<any>,
okText: { okText: {
type: String as PropType<string>, type: String as PropType<string>,
default: '确认', default: t('okText'),
}, },
okType: { okType: {
type: String as PropType<string>, type: String as PropType<string>,
......
<template> <template>
<BasicModal v-bind="$attrs" title="导出数据" @ok="handleOk" @register="registerModal"> <BasicModal
v-bind="$attrs"
:title="t('exportModalTitle')"
@ok="handleOk"
@register="registerModal"
>
<BasicForm <BasicForm
:labelWidth="100" :labelWidth="100"
:schemas="schemas" :schemas="schemas"
...@@ -9,22 +14,26 @@ ...@@ -9,22 +14,26 @@
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { ExportModalResult } from './types';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index'; import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { ExportModalResult } from './types';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.excel');
const schemas: FormSchema[] = [ const schemas: FormSchema[] = [
{ {
field: 'filename', field: 'filename',
component: 'Input', component: 'Input',
label: '文件名', label: t('fileName'),
rules: [{ required: true }], rules: [{ required: true }],
}, },
{ {
field: 'bookType', field: 'bookType',
component: 'Select', component: 'Select',
label: '文件类型', label: t('fileType'),
defaultValue: 'xlsx', defaultValue: 'xlsx',
rules: [{ required: true }], rules: [{ required: true }],
componentProps: { componentProps: {
...@@ -76,6 +85,7 @@ ...@@ -76,6 +85,7 @@
handleOk, handleOk,
registerForm, registerForm,
registerModal, registerModal,
t,
}; };
}, },
}); });
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
/* DO SOMETHING WITH workbook HERE */ /* DO SOMETHING WITH workbook HERE */
const excelData = getExcelData(workbook); const excelData = getExcelData(workbook);
emit('success', excelData); emit('success', excelData);
resolve(); resolve('');
} catch (error) { } catch (error) {
reject(error); reject(error);
} finally { } finally {
......
...@@ -6,6 +6,9 @@ import Button from '/@/components/Button/index.vue'; ...@@ -6,6 +6,9 @@ import Button from '/@/components/Button/index.vue';
import { BasicArrow } from '/@/components/Basic/index'; import { BasicArrow } from '/@/components/Basic/index';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.form');
export default defineComponent({ export default defineComponent({
name: 'BasicFormAction', name: 'BasicFormAction',
...@@ -55,14 +58,14 @@ export default defineComponent({ ...@@ -55,14 +58,14 @@ export default defineComponent({
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const getResetBtnOptionsRef = computed(() => { const getResetBtnOptionsRef = computed(() => {
return { return {
text: '重置', text: t('resetButton'),
...props.resetButtonOptions, ...props.resetButtonOptions,
}; };
}); });
const getSubmitBtnOptionsRef = computed(() => { const getSubmitBtnOptionsRef = computed(() => {
return { return {
text: '查询', text: t('submitButton'),
// htmlType: 'submit', // htmlType: 'submit',
...props.submitButtonOptions, ...props.submitButtonOptions,
}; };
...@@ -108,7 +111,7 @@ export default defineComponent({ ...@@ -108,7 +111,7 @@ export default defineComponent({
<Button type="default" class="mr-2" onClick={toggleAdvanced}> <Button type="default" class="mr-2" onClick={toggleAdvanced}>
{() => ( {() => (
<> <>
{isAdvanced ? '收起' : '展开'} {isAdvanced ? t('putAway') : t('unfold')}
<BasicArrow expand={!isAdvanced} /> <BasicArrow expand={!isAdvanced} />
</> </>
)} )}
......
import type { ComponentType } from './types/index'; import type { ComponentType } from './types/index';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.form');
/** /**
* @description: 生成placeholder * @description: 生成placeholder
*/ */
export function createPlaceholderMessage(component: ComponentType) { export function createPlaceholderMessage(component: ComponentType) {
if (component.includes('Input') || component.includes('Complete')) { if (component.includes('Input') || component.includes('Complete')) {
return '请输入'; return t('input');
} }
if (component.includes('Picker')) { if (component.includes('Picker')) {
return '请选择'; return t('choose');
} }
if ( if (
component.includes('Select') || component.includes('Select') ||
...@@ -18,7 +21,7 @@ export function createPlaceholderMessage(component: ComponentType) { ...@@ -18,7 +21,7 @@ export function createPlaceholderMessage(component: ComponentType) {
component.includes('Switch') component.includes('Switch')
) { ) {
// return `请选择${label}`; // return `请选择${label}`;
return '请选择'; return t('choose');
} }
return ''; return '';
} }
......
<template> <template>
<section class="menu-search-input" @Click="handleClick" :class="searchClass"> <section class="menu-search-input" @Click="handleClick" :class="searchClass">
<a-input-search <a-input-search
placeholder="菜单搜索" :placeholder="t('search')"
class="menu-search-input__search" class="menu-search-input__search"
allowClear allowClear
@change="handleChange" @change="handleChange"
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
// hook // hook
import { useDebounce } from '/@/hooks/core/useDebounce'; import { useDebounce } from '/@/hooks/core/useDebounce';
import { useI18n } from '/@/hooks/web/useI18n';
// //
export default defineComponent({ export default defineComponent({
name: 'BasicMenuSearchInput', name: 'BasicMenuSearchInput',
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
}, },
}, },
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n('component.menu');
const [debounceEmitChange] = useDebounce(emitChange, 200); const [debounceEmitChange] = useDebounce(emitChange, 200);
function emitChange(value?: string): void { function emitChange(value?: string): void {
...@@ -52,7 +54,7 @@ ...@@ -52,7 +54,7 @@
return cls; return cls;
}); });
return { handleClick, searchClass, handleChange }; return { handleClick, searchClass, handleChange, t };
}, },
}); });
</script> </script>
......
import { ComputedRef } from 'vue';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { MenuModeEnum } from '/@/enums/menuEnum';
export interface MenuState { export interface MenuState {
// 默认选中的列表 // 默认选中的列表
defaultSelectedKeys: string[]; defaultSelectedKeys: string[];
......
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.modal');
export const modalProps = { export const modalProps = {
visible: Boolean as PropType<boolean>, visible: Boolean as PropType<boolean>,
// open drag // open drag
...@@ -13,11 +17,11 @@ export const modalProps = { ...@@ -13,11 +17,11 @@ export const modalProps = {
}, },
cancelText: { cancelText: {
type: String as PropType<string>, type: String as PropType<string>,
default: '关闭', default: t('cancelText'),
}, },
okText: { okText: {
type: String as PropType<string>, type: String as PropType<string>,
default: '确认', default: t('okText'),
}, },
closeFunc: Function as PropType<() => Promise<boolean>>, closeFunc: Function as PropType<() => Promise<boolean>>,
}; };
......
...@@ -4,27 +4,27 @@ ...@@ -4,27 +4,27 @@
<Tooltip placement="top" v-if="getSetting.redo"> <Tooltip placement="top" v-if="getSetting.redo">
<template #title> <template #title>
<span>刷新</span> <span>{{ t('settingRedo') }}</span>
</template> </template>
<RedoOutlined @click="redo" /> <RedoOutlined @click="redo" />
</Tooltip> </Tooltip>
<Tooltip placement="top" v-if="getSetting.size"> <Tooltip placement="top" v-if="getSetting.size">
<template #title> <template #title>
<span>密度</span> <span>{{ t('settingDens') }}</span>
</template> </template>
<Dropdown placement="bottomCenter" :trigger="['click']"> <Dropdown placement="bottomCenter" :trigger="['click']">
<ColumnHeightOutlined /> <ColumnHeightOutlined />
<template #overlay> <template #overlay>
<Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef"> <Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">
<MenuItem key="default"> <MenuItem key="default">
<span>默认</span> <span>{{ t('settingDensDefault') }}</span>
</MenuItem> </MenuItem>
<MenuItem key="middle"> <MenuItem key="middle">
<span>中等</span> <span>{{ t('settingDensMiddle') }}</span>
</MenuItem> </MenuItem>
<MenuItem key="small"> <MenuItem key="small">
<span>紧凑</span> <span>{{ t('settingDensSmall') }}</span>
</MenuItem> </MenuItem>
</Menu> </Menu>
</template> </template>
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<Tooltip placement="top" v-if="getSetting.setting"> <Tooltip placement="top" v-if="getSetting.setting">
<template #title> <template #title>
<span>列设置</span> <span>{{ t('settingColumn') }}</span>
</template> </template>
<Popover <Popover
placement="bottomLeft" placement="bottomLeft"
...@@ -58,9 +58,9 @@ ...@@ -58,9 +58,9 @@
v-model:checked="checkAll" v-model:checked="checkAll"
@change="onCheckAllChange" @change="onCheckAllChange"
> >
列展示 {{ t('settingColumnShow') }}
</Checkbox> </Checkbox>
<a-button size="small" type="link" @click="reset">重置</a-button> <a-button size="small" type="link" @click="reset"> {{ t('settingReset') }}</a-button>
</div> </div>
</template> </template>
<SettingOutlined /> <SettingOutlined />
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
<Tooltip placement="top" v-if="getSetting.fullScreen"> <Tooltip placement="top" v-if="getSetting.fullScreen">
<template #title> <template #title>
<span>全屏</span> <span>{{ t('settingFullScreen') }}</span>
</template> </template>
<FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" /> <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" />
<FullscreenExitOutlined @click="handleFullScreen" v-else /> <FullscreenExitOutlined @click="handleFullScreen" v-else />
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
import { useFullscreen } from '/@/hooks/web/useFullScreen'; import { useFullscreen } from '/@/hooks/web/useFullScreen';
import type { SizeType, TableSetting } from '../types/table'; import type { SizeType, TableSetting } from '../types/table';
import { useI18n } from '/@/hooks/web/useI18n';
interface Options { interface Options {
label: string; label: string;
...@@ -139,6 +140,8 @@ ...@@ -139,6 +140,8 @@
defaultCheckList: [], defaultCheckList: [],
}); });
const { t } = useI18n('component.table');
function init() { function init() {
let ret: Options[] = []; let ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => { table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
...@@ -217,6 +220,7 @@ ...@@ -217,6 +220,7 @@
reset, reset,
getSetting, getSetting,
...toRefs(state), ...toRefs(state),
t,
}; };
}, },
}); });
......
...@@ -4,7 +4,9 @@ import { unref, ComputedRef, Ref, computed, watchEffect, ref, toRaw } from 'vue' ...@@ -4,7 +4,9 @@ import { unref, ComputedRef, Ref, computed, watchEffect, ref, toRaw } from 'vue'
import { isBoolean, isArray, isObject } from '/@/utils/is'; import { isBoolean, isArray, isObject } from '/@/utils/is';
import { PAGE_SIZE } from '../const'; import { PAGE_SIZE } from '../const';
import { useProps } from './useProps'; import { useProps } from './useProps';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.table');
export function useColumns( export function useColumns(
refProps: ComputedRef<BasicTableProps>, refProps: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<false | PaginationProps> getPaginationRef: ComputedRef<false | PaginationProps>
...@@ -42,7 +44,7 @@ export function useColumns( ...@@ -42,7 +44,7 @@ export function useColumns(
columns.unshift({ columns.unshift({
flag: 'INDEX', flag: 'INDEX',
width: 50, width: 50,
title: '序号', title: t('index'),
align: 'center', align: 'center',
customRender: ({ index }) => { customRender: ({ index }) => {
const getPagination = unref(getPaginationRef); const getPagination = unref(getPaginationRef);
......
...@@ -8,6 +8,9 @@ import { isBoolean } from '/@/utils/is'; ...@@ -8,6 +8,9 @@ import { isBoolean } from '/@/utils/is';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const'; import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
import { useProps } from './useProps'; import { useProps } from './useProps';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.table');
export function usePagination(refProps: ComputedRef<BasicTableProps>) { export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const configRef = ref<PaginationProps>({}); const configRef = ref<PaginationProps>({});
const { propsRef } = useProps(refProps); const { propsRef } = useProps(refProps);
...@@ -22,7 +25,7 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { ...@@ -22,7 +25,7 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
size: 'small', size: 'small',
defaultPageSize: PAGE_SIZE, defaultPageSize: PAGE_SIZE,
showTotal: (total) => `共 ${total} 条数据`, showTotal: (total) => t('total', { total }),
showSizeChanger: true, showSizeChanger: true,
pageSizeOptions: PAGE_SIZE_OPTIONS, pageSizeOptions: PAGE_SIZE_OPTIONS,
itemRender: ({ page, type, originalElement }) => { itemRender: ({ page, type, originalElement }) => {
......
...@@ -13,7 +13,7 @@ const lineHeight = function (tinymce: any) { ...@@ -13,7 +13,7 @@ const lineHeight = function (tinymce: any) {
t.ui.registry.addMenuButton('lineheight', { t.ui.registry.addMenuButton('lineheight', {
icon: 'lineheight', icon: 'lineheight',
tooltip: '设置行高', tooltip: 'Line Height',
// fetch: function (callback: Fn) { // fetch: function (callback: Fn) {
// var dom = t.dom; // var dom = t.dom;
// var blocks = t.selection.getSelectedBlocks(); // var blocks = t.selection.getSelectedBlocks();
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
<div> <div>
<a-button-group> <a-button-group>
<a-button type="primary" @click="openUploadModal" preIcon="ant-design:cloud-upload-outlined"> <a-button type="primary" @click="openUploadModal" preIcon="ant-design:cloud-upload-outlined">
上传 {{ t('upload') }}
</a-button> </a-button>
<Tooltip placement="bottom" v-if="showPreview"> <Tooltip placement="bottom" v-if="showPreview">
<template #title> <template #title>
已上传 {{ t('uploaded') }}
<template v-if="fileListRef.length">{{ fileListRef.length }}</template> <template v-if="fileListRef.length">{{ fileListRef.length }}</template>
</template> </template>
<a-button @click="openPreviewModal"> <a-button @click="openPreviewModal">
...@@ -39,12 +39,14 @@ ...@@ -39,12 +39,14 @@
import { uploadContainerProps } from './props'; import { uploadContainerProps } from './props';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
name: 'BasicUpload', name: 'BasicUpload',
components: { UploadModal, UploadPreviewModal, Icon, Tooltip }, components: { UploadModal, UploadPreviewModal, Icon, Tooltip },
props: uploadContainerProps, props: uploadContainerProps,
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const { t } = useI18n('component.upload');
// 上传modal // 上传modal
const [registerUploadModal, { openModal: openUploadModal }] = useModal(); const [registerUploadModal, { openModal: openUploadModal }] = useModal();
...@@ -94,6 +96,7 @@ ...@@ -94,6 +96,7 @@
fileListRef, fileListRef,
showPreview, showPreview,
bindValue, bindValue,
t,
}; };
}, },
}); });
......
<template> <template>
<BasicModal <BasicModal
width="800px" width="800px"
title="上传" :title="t('upload')"
okText="保存" :okText="t('save')"
v-bind="$attrs" v-bind="$attrs"
@register="register" @register="register"
@ok="handleOk" @ok="handleOk"
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
:before-upload="beforeUpload" :before-upload="beforeUpload"
class="upload-modal-toolbar__btn" class="upload-modal-toolbar__btn"
> >
<a-button type="primary"> 选择文件 </a-button> <a-button type="primary"> {{ t('choose') }} </a-button>
</Upload> </Upload>
</div> </div>
<FileList :dataSource="fileListRef" :columns="columns" :actionColumn="actionColumn" /> <FileList :dataSource="fileListRef" :columns="columns" :actionColumn="actionColumn" />
...@@ -57,11 +57,15 @@ ...@@ -57,11 +57,15 @@
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log'; import { warn } from '/@/utils/log';
import FileList from './FileList'; import FileList from './FileList';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
components: { BasicModal, Upload, Alert, FileList }, components: { BasicModal, Upload, Alert, FileList },
props: basicProps, props: basicProps,
setup(props, { emit }) { setup(props, { emit }) {
// 是否正在上传 // 是否正在上传
const { t } = useI18n('component.upload');
const isUploadingRef = ref(false); const isUploadingRef = ref(false);
const fileListRef = ref<FileItem[]>([]); const fileListRef = ref<FileItem[]>([]);
const state = reactive<{ fileList: FileItem[] }>({ const state = reactive<{ fileList: FileItem[] }>({
...@@ -100,7 +104,11 @@ ...@@ -100,7 +104,11 @@
const someError = fileListRef.value.some( const someError = fileListRef.value.some(
(item) => item.status === UploadResultStatus.ERROR (item) => item.status === UploadResultStatus.ERROR
); );
return isUploadingRef.value ? '上传中' : someError ? '重新上传失败文件' : '开始上传'; return isUploadingRef.value
? t('uploading')
: someError
? t('reUploadFailed')
: t('startUpload');
}); });
// 上传前校验 // 上传前校验
...@@ -111,13 +119,13 @@ ...@@ -111,13 +119,13 @@
// 设置最大值,则判断 // 设置最大值,则判断
if (maxSize && file.size / 1024 / 1024 >= maxSize) { if (maxSize && file.size / 1024 / 1024 >= maxSize) {
createMessage.error(`只能上传不超过${maxSize}MB的文件!`); createMessage.error(t('maxSizeMultiple', [maxSize]));
return false; return false;
} }
// 设置类型,则判断 // 设置类型,则判断
if (accept.length > 0 && !checkFileType(file, accept)) { if (accept.length > 0 && !checkFileType(file, accept)) {
createMessage.error!(`只能上传${accept.join(',')}格式文件`); createMessage.error!(t('acceptUpload', [accept.join(',')]));
return false; return false;
} }
const commonItem = { const commonItem = {
...@@ -198,7 +206,7 @@ ...@@ -198,7 +206,7 @@
async function handleStartUpload() { async function handleStartUpload() {
const { maxNumber } = props; const { maxNumber } = props;
if (fileListRef.value.length > maxNumber) { if (fileListRef.value.length > maxNumber) {
return createMessage.warning(`最多只能上传${maxNumber}个文件`); return createMessage.warning(t('maxNumber', [maxNumber]));
} }
try { try {
isUploadingRef.value = true; isUploadingRef.value = true;
...@@ -225,10 +233,10 @@ ...@@ -225,10 +233,10 @@
const { maxNumber } = props; const { maxNumber } = props;
if (fileListRef.value.length > maxNumber) { if (fileListRef.value.length > maxNumber) {
return createMessage.warning(`最多只能上传${maxNumber}个文件`); return createMessage.warning(t('maxNumber', [maxNumber]));
} }
if (isUploadingRef.value) { if (isUploadingRef.value) {
return createMessage.warning('请等待文件上传后,保存'); return createMessage.warning(t('saveWarn'));
} }
const fileList: string[] = []; const fileList: string[] = [];
...@@ -240,7 +248,7 @@ ...@@ -240,7 +248,7 @@
} }
// 存在一个上传成功的即可保存 // 存在一个上传成功的即可保存
if (fileList.length <= 0) { if (fileList.length <= 0) {
return createMessage.warning('没有上传成功的文件,无法保存'); return createMessage.warning(t('saveError'));
} }
fileListRef.value = []; fileListRef.value = [];
closeModal(); closeModal();
...@@ -253,7 +261,7 @@ ...@@ -253,7 +261,7 @@
fileListRef.value = []; fileListRef.value = [];
return true; return true;
} else { } else {
createMessage.warning('请等待文件上传结束后操作'); createMessage.warning(t('uploadWait'));
return false; return false;
} }
} }
...@@ -285,6 +293,7 @@ ...@@ -285,6 +293,7 @@
handleCloseFunc, handleCloseFunc,
getIsSelectFile, getIsSelectFile,
getUploadBtnText, getUploadBtnText,
t,
}; };
}, },
}); });
......
<template> <template>
<BasicModal <BasicModal
width="800px" width="800px"
title="预览" :title="t('preview')"
wrapClassName="upload-preview-modal" wrapClassName="upload-preview-modal"
v-bind="$attrs" v-bind="$attrs"
@register="register" @register="register"
...@@ -23,11 +23,15 @@ ...@@ -23,11 +23,15 @@
import { downloadByUrl } from '/@/utils/file/download'; import { downloadByUrl } from '/@/utils/file/download';
import { createPreviewColumns, createPreviewActionColumn } from './data'; import { createPreviewColumns, createPreviewActionColumn } from './data';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
components: { BasicModal, FileList }, components: { BasicModal, FileList },
props: previewProps, props: previewProps,
setup(props, { emit }) { setup(props, { emit }) {
const [register, { closeModal }] = useModalInner(); const [register, { closeModal }] = useModalInner();
const { t } = useI18n('component.upload');
const fileListRef = ref<PreviewFileItem[]>([]); const fileListRef = ref<PreviewFileItem[]>([]);
watch( watch(
() => props.value, () => props.value,
...@@ -74,6 +78,7 @@ ...@@ -74,6 +78,7 @@
} }
return { return {
t,
register, register,
closeModal, closeModal,
fileListRef, fileListRef,
......
...@@ -6,12 +6,15 @@ import { Progress, Tag } from 'ant-design-vue'; ...@@ -6,12 +6,15 @@ import { Progress, Tag } from 'ant-design-vue';
import TableAction from '/@/components/Table/src/components/TableAction'; import TableAction from '/@/components/Table/src/components/TableAction';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.upload');
// 文件上传列表 // 文件上传列表
export function createTableColumns(): BasicColumn[] { export function createTableColumns(): BasicColumn[] {
return [ return [
{ {
dataIndex: 'thumbUrl', dataIndex: 'thumbUrl',
title: '图例', title: t('legend'),
width: 100, width: 100,
customRender: ({ record }) => { customRender: ({ record }) => {
const { thumbUrl, type } = (record as FileItem) || {}; const { thumbUrl, type } = (record as FileItem) || {};
...@@ -20,7 +23,7 @@ export function createTableColumns(): BasicColumn[] { ...@@ -20,7 +23,7 @@ export function createTableColumns(): BasicColumn[] {
}, },
{ {
dataIndex: 'name', dataIndex: 'name',
title: '文件名', title: t('fileName'),
align: 'left', align: 'left',
customRender: ({ text, record }) => { customRender: ({ text, record }) => {
const { percent, status: uploadStatus } = (record as FileItem) || {}; const { percent, status: uploadStatus } = (record as FileItem) || {};
...@@ -44,7 +47,7 @@ export function createTableColumns(): BasicColumn[] { ...@@ -44,7 +47,7 @@ export function createTableColumns(): BasicColumn[] {
}, },
{ {
dataIndex: 'size', dataIndex: 'size',
title: '文件大小', title: t('fileSize'),
width: 100, width: 100,
customRender: ({ text = 0 }) => { customRender: ({ text = 0 }) => {
return text && (text / 1024).toFixed(2) + 'KB'; return text && (text / 1024).toFixed(2) + 'KB';
...@@ -57,15 +60,15 @@ export function createTableColumns(): BasicColumn[] { ...@@ -57,15 +60,15 @@ export function createTableColumns(): BasicColumn[] {
// }, // },
{ {
dataIndex: 'status', dataIndex: 'status',
title: '状态', title: t('fileStatue'),
width: 100, width: 100,
customRender: ({ text }) => { customRender: ({ text }) => {
if (text === UploadResultStatus.SUCCESS) { if (text === UploadResultStatus.SUCCESS) {
return <Tag color="green">{() => '上传成功'}</Tag>; return <Tag color="green">{() => t('uploadSuccess')}</Tag>;
} else if (text === UploadResultStatus.ERROR) { } else if (text === UploadResultStatus.ERROR) {
return <Tag color="red">{() => '上传失败'}</Tag>; return <Tag color="red">{() => t('uploadError')}</Tag>;
} else if (text === UploadResultStatus.UPLOADING) { } else if (text === UploadResultStatus.UPLOADING) {
return <Tag color="blue">{() => '上传中'}</Tag>; return <Tag color="blue">{() => t('uploading')}</Tag>;
} }
return text; return text;
...@@ -76,20 +79,20 @@ export function createTableColumns(): BasicColumn[] { ...@@ -76,20 +79,20 @@ export function createTableColumns(): BasicColumn[] {
export function createActionColumn(handleRemove: Function, handlePreview: Function): BasicColumn { export function createActionColumn(handleRemove: Function, handlePreview: Function): BasicColumn {
return { return {
width: 120, width: 120,
title: '操作', title: t('operating'),
dataIndex: 'action', dataIndex: 'action',
fixed: false, fixed: false,
customRender: ({ record }) => { customRender: ({ record }) => {
const actions: ActionItem[] = [ const actions: ActionItem[] = [
{ {
label: '删除', label: t('del'),
color: 'error', color: 'error',
onClick: handleRemove.bind(null, record), onClick: handleRemove.bind(null, record),
}, },
]; ];
if (checkImgType(record)) { if (checkImgType(record)) {
actions.unshift({ actions.unshift({
label: '预览', label: t('preview'),
onClick: handlePreview.bind(null, record), onClick: handlePreview.bind(null, record),
}); });
} }
...@@ -102,7 +105,7 @@ export function createPreviewColumns(): BasicColumn[] { ...@@ -102,7 +105,7 @@ export function createPreviewColumns(): BasicColumn[] {
return [ return [
{ {
dataIndex: 'url', dataIndex: 'url',
title: '图例', title: t('legend'),
width: 100, width: 100,
customRender: ({ record }) => { customRender: ({ record }) => {
const { url, type } = (record as PreviewFileItem) || {}; const { url, type } = (record as PreviewFileItem) || {};
...@@ -113,7 +116,7 @@ export function createPreviewColumns(): BasicColumn[] { ...@@ -113,7 +116,7 @@ export function createPreviewColumns(): BasicColumn[] {
}, },
{ {
dataIndex: 'name', dataIndex: 'name',
title: '文件名', title: t('fileName'),
align: 'left', align: 'left',
}, },
]; ];
...@@ -130,7 +133,7 @@ export function createPreviewActionColumn({ ...@@ -130,7 +133,7 @@ export function createPreviewActionColumn({
}): BasicColumn { }): BasicColumn {
return { return {
width: 160, width: 160,
title: '操作', title: t('operating'),
dataIndex: 'action', dataIndex: 'action',
fixed: false, fixed: false,
customRender: ({ record }) => { customRender: ({ record }) => {
...@@ -138,18 +141,18 @@ export function createPreviewActionColumn({ ...@@ -138,18 +141,18 @@ export function createPreviewActionColumn({
const actions: ActionItem[] = [ const actions: ActionItem[] = [
{ {
label: '删除', label: t('del'),
color: 'error', color: 'error',
onClick: handleRemove.bind(null, record), onClick: handleRemove.bind(null, record),
}, },
{ {
label: '下载', label: t('download'),
onClick: handleDownload.bind(null, record), onClick: handleDownload.bind(null, record),
}, },
]; ];
if (isImgTypeByName(url)) { if (isImgTypeByName(url)) {
actions.unshift({ actions.unshift({
label: '预览', label: t('preview'),
onClick: handlePreview.bind(null, record), onClick: handlePreview.bind(null, record),
}); });
} }
......
import { Ref, unref, computed } from 'vue'; import { Ref, unref, computed } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.upload');
export function useUploadType({ export function useUploadType({
acceptRef, acceptRef,
// uploadTypeRef, // uploadTypeRef,
...@@ -37,17 +38,17 @@ export function useUploadType({ ...@@ -37,17 +38,17 @@ export function useUploadType({
const accept = unref(acceptRef); const accept = unref(acceptRef);
if (accept.length > 0) { if (accept.length > 0) {
helpTexts.push(`支持${accept.join(',')}格式`); helpTexts.push(t('accept', [accept.join(',')]));
} }
const maxSize = unref(maxSizeRef); const maxSize = unref(maxSizeRef);
if (maxSize) { if (maxSize) {
helpTexts.push(`单个文件不超过${maxSize}MB`); helpTexts.push(t('maxSize', [maxSize]));
} }
const maxNumber = unref(maxNumberRef); const maxNumber = unref(maxNumberRef);
if (maxNumber && maxNumber !== Infinity) { if (maxNumber && maxNumber !== Infinity) {
helpTexts.push(`最多只能上传${maxNumber}个文件`); helpTexts.push(t('maxNumber', [maxNumber]));
} }
return helpTexts.join(','); return helpTexts.join(',');
}); });
......
import './ImgRotate.less';
import type { MoveData, DragVerifyActionType } from './types'; import type { MoveData, DragVerifyActionType } from './types';
import { defineComponent, computed, unref, reactive, watch, ref, getCurrentInstance } from 'vue'; import { defineComponent, computed, unref, reactive, watch, ref, getCurrentInstance } from 'vue';
...@@ -8,7 +10,8 @@ import BasicDragVerify from './DragVerify'; ...@@ -8,7 +10,8 @@ import BasicDragVerify from './DragVerify';
import { hackCss } from '/@/utils/domUtils'; import { hackCss } from '/@/utils/domUtils';
import { rotateProps } from './props'; import { rotateProps } from './props';
import './ImgRotate.less'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
name: 'ImgRotateDargVerify', name: 'ImgRotateDargVerify',
inheritAttrs: false, inheritAttrs: false,
...@@ -27,6 +30,7 @@ export default defineComponent({ ...@@ -27,6 +30,7 @@ export default defineComponent({
endTime: 0, endTime: 0,
draged: false, draged: false,
}); });
const { t } = useI18n('component.verify');
watch( watch(
() => state.isPassing, () => state.isPassing,
...@@ -142,11 +146,11 @@ export default defineComponent({ ...@@ -142,11 +146,11 @@ export default defineComponent({
/> />
{state.showTip && ( {state.showTip && (
<span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}> <span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}>
{state.isPassing ? `校验成功,耗时${time.toFixed(1)}秒!` : '验证失败!'} {state.isPassing ? t('time', { time: time.toFixed(1) }) : t('error')}
</span> </span>
)} )}
{!state.showTip && !state.draged && ( {!state.showTip && !state.draged && (
<span class={[`ir-dv-img__tip`, 'normal']}>点击图片可刷新</span> <span class={[`ir-dv-img__tip`, 'normal']}>t('redoTip')</span>
)} )}
</div> </div>
<BasicDragVerify <BasicDragVerify
......
<script lang="tsx">
import { defineComponent, ref, unref } from 'vue';
import { BasicModal } from '/@/components/Modal/index';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { RotateDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
export default defineComponent({
name: 'VerifyModal',
setup(_, { attrs, emit }) {
const dragRef = ref<DragVerifyActionType | null>(null);
function handleSuccess() {
useTimeoutFn(() => {
emit('success');
const dragEl = unref(dragRef);
if (dragEl) {
dragEl.resume();
}
}, 500);
}
return () => (
<BasicModal
{...attrs}
title="安全校验"
keyboard={false}
maskClosable={false}
canFullscreen={false}
footer={null}
wrapperFooterOffset={60}
destroyOnClose={true}
>
<RotateDragVerify
imgWidth={210}
ref={dragRef}
text="请拖动滑块将图片摆正"
{...attrs}
onSuccess={handleSuccess}
/>
</BasicModal>
);
},
});
</script>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n('component.verify');
export const basicProps = { export const basicProps = {
value: { value: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
...@@ -13,11 +15,11 @@ export const basicProps = { ...@@ -13,11 +15,11 @@ export const basicProps = {
text: { text: {
type: [String] as PropType<string>, type: [String] as PropType<string>,
default: '请按住滑块拖动', default: t('dragText'),
}, },
successText: { successText: {
type: [String] as PropType<string>, type: [String] as PropType<string>,
default: '验证通过', default: t('successText'),
}, },
height: { height: {
type: [Number, String] as PropType<number | string>, type: [Number, String] as PropType<number | string>,
......
export default {
loadingText: 'Loading...',
cancelText: 'Close',
okText: 'Confirm',
};
export default {
exportModalTitle: 'Export data',
fileType: 'File type',
fileName: 'File name',
};
export default {
resetButton: 'Reset',
submitButton: 'Search',
putAway: 'Put away',
unfold: 'Unfold',
input: 'Please Input',
choose: 'Please Choose',
};
export default {
search: 'Menu search',
};
export default {
cancelText: 'Close',
okText: 'Confirm',
};
export default {
settingRedo: 'Refresh',
settingDens: 'Density',
settingDensDefault: 'Default',
settingDensMiddle: 'Middle',
settingDensSmall: 'Compact',
settingColumn: 'Column settings',
settingColumnShow: 'Column display',
settingReset: 'Reset',
settingFullScreen: 'Full Screen',
index: 'Index',
total: 'total of {total}',
};
export default {
save: 'Save',
upload: 'Upload',
uploaded: 'Uploaded',
operating: 'Operating',
del: 'Delete',
download: 'download',
saveWarn: 'Please wait for the file to upload and save!',
saveError: 'There is no file successfully uploaded and cannot be saved!',
preview: 'Preview',
choose: 'Select the file',
accept: 'Support {0} format',
acceptUpload: 'Only upload files in {0} format',
maxSize: 'A single file does not exceed {0}MB ',
maxSizeMultiple: 'Only upload files up to {0}MB!',
maxNumber: 'Only upload up to {0} files',
legend: 'Legend',
fileName: 'File name',
fileSize: 'File size',
fileStatue: 'File status',
startUpload: 'Start upload',
uploadSuccess: 'Upload successfully',
uploadError: 'Upload failed',
uploading: 'Uploading',
uploadWait: 'Please wait for the file upload to finish',
reUploadFailed: 'Re-upload failed files',
};
export default {
error: 'verification failed!',
time: 'The verification is successful and it takes {time} seconds!',
redoTip: 'Click the picture to refresh',
dragText: 'Hold down the slider and drag',
successText: 'Verified',
};
export default {
loadingText: '加载中...',
cancelText: '关闭',
okText: '确认',
};
export default {
exportModalTitle: '导出数据',
fileType: '文件类型',
fileName: '文件名',
};
export default {
resetButton: '重置',
submitButton: '查询',
putAway: '收起',
unfold: '展开',
input: '请输入',
choose: '请选择',
};
export default {
search: '菜单搜索',
};
export default {
loadingText: '加载中...',
cancelText: '关闭',
okText: '确认',
};
export default {
settingRedo: '刷新',
settingDens: '密度',
settingDensDefault: '默认',
settingDensMiddle: '中等',
settingDensSmall: '紧凑',
settingColumn: '列设置',
settingColumnShow: '列展示',
settingReset: '重置',
settingFullScreen: '全屏',
index: '序号',
total: '共 {total} 条数据',
};
export default {
save: '保存',
upload: '上传',
uploaded: '已上传',
operating: '操作',
del: '删除',
download: '下载',
saveWarn: '请等待文件上传后,保存!',
saveError: '没有上传成功的文件,无法保存!',
preview: '预览',
choose: '选择文件',
accept: '支持{0}格式',
acceptUpload: '只能上传{0}格式文件',
maxSize: '单个文件不超过{0}MB',
maxSizeMultiple: '只能上传不超过{0}MB的文件!',
maxNumber: '最多只能上传{0}个文件',
legend: '图例',
fileName: '文件名',
fileSize: '文件大小',
fileStatue: '状态',
startUpload: '开始上传',
uploadSuccess: '上传成功',
uploadError: '上传失败',
uploading: '上传中',
uploadWait: '请等待文件上传结束后操作',
reUploadFailed: '重新上传失败文件',
};
export default {
error: '验证失败!',
time: '验证校验成功,耗时{time}秒!',
redoTip: '点击图片可刷新',
dragText: '请按住滑块拖动',
successText: '验证通过',
};
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论