feat: batch table
This commit is contained in:
parent
d8d94a0c20
commit
51ac0e2f97
|
@ -1,6 +1,6 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import Footer from '@/components/footer/index.vue';
|
||||
import Footer from '@/components/pure/footer/index.vue';
|
||||
|
||||
describe('Footer', () => {
|
||||
test('renders the correct text', () => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import MsBaseTable from '@/components/ms-table/base-table.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { nextTick } from 'vue';
|
||||
import { MsTableColumn } from '@/components/ms-table/type';
|
||||
import useTable from '@/components/ms-table/useTable';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import { getTableList } from '@/api/modules/api-test/index';
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
|
@ -71,7 +71,6 @@ describe('MS-Table', () => {
|
|||
test('init table with useTable', async () => {
|
||||
const { propsRes, propsEvent, loadList, setProps } = useTable(getTableList, {
|
||||
columns,
|
||||
pagination: { current: 1, pageSize: 1 },
|
||||
});
|
||||
|
||||
const wrapper = mount(MsBaseTable, {
|
||||
|
@ -85,7 +84,7 @@ describe('MS-Table', () => {
|
|||
expect(propsRes.value.data.length).toBe(2);
|
||||
expect(content).toBe('e7bd7179-d63a-43a5-1a65-218473ee69ca');
|
||||
|
||||
setProps({ pagination: { current: 2, pageSize: 1 } });
|
||||
setProps({});
|
||||
loadList();
|
||||
|
||||
await nextTick();
|
|
@ -5,57 +5,57 @@
|
|||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {};
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
AAffix: typeof import('@arco-design/web-vue')['Affix'];
|
||||
AAlert: typeof import('@arco-design/web-vue')['Alert'];
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar'];
|
||||
ABadge: typeof import('@arco-design/web-vue')['Badge'];
|
||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb'];
|
||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem'];
|
||||
AButton: typeof import('@arco-design/web-vue')['Button'];
|
||||
ACard: typeof import('@arco-design/web-vue')['Card'];
|
||||
ACardMeta: typeof import('@arco-design/web-vue')['CardMeta'];
|
||||
ACol: typeof import('@arco-design/web-vue')['Col'];
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider'];
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider'];
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption'];
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer'];
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown'];
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'];
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout'];
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent'];
|
||||
ALayoutFooter: typeof import('@arco-design/web-vue')['LayoutFooter'];
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider'];
|
||||
ALink: typeof import('@arco-design/web-vue')['Link'];
|
||||
AList: typeof import('@arco-design/web-vue')['List'];
|
||||
AListItem: typeof import('@arco-design/web-vue')['ListItem'];
|
||||
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta'];
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu'];
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'];
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal'];
|
||||
AOption: typeof import('@arco-design/web-vue')['Option'];
|
||||
APagination: typeof import('@arco-design/web-vue')['Pagination'];
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover'];
|
||||
AResult: typeof import('@arco-design/web-vue')['Result'];
|
||||
ARow: typeof import('@arco-design/web-vue')['Row'];
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select'];
|
||||
ASkeleton: typeof import('@arco-design/web-vue')['Skeleton'];
|
||||
ASkeletonLine: typeof import('@arco-design/web-vue')['SkeletonLine'];
|
||||
ASkeletonShape: typeof import('@arco-design/web-vue')['SkeletonShape'];
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space'];
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin'];
|
||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'];
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch'];
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane'];
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs'];
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag'];
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip'];
|
||||
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph'];
|
||||
ATypographyText: typeof import('@arco-design/web-vue')['TypographyText'];
|
||||
RouterLink: typeof import('vue-router')['RouterLink'];
|
||||
RouterView: typeof import('vue-router')['RouterView'];
|
||||
AAffix: typeof import('@arco-design/web-vue')['Affix']
|
||||
AAlert: typeof import('@arco-design/web-vue')['Alert']
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
||||
ABadge: typeof import('@arco-design/web-vue')['Badge']
|
||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||
ACard: typeof import('@arco-design/web-vue')['Card']
|
||||
ACardMeta: typeof import('@arco-design/web-vue')['CardMeta']
|
||||
ACol: typeof import('@arco-design/web-vue')['Col']
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||
ALayoutFooter: typeof import('@arco-design/web-vue')['LayoutFooter']
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||
AList: typeof import('@arco-design/web-vue')['List']
|
||||
AListItem: typeof import('@arco-design/web-vue')['ListItem']
|
||||
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||
AOption: typeof import('@arco-design/web-vue')['Option']
|
||||
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||
AResult: typeof import('@arco-design/web-vue')['Result']
|
||||
ARow: typeof import('@arco-design/web-vue')['Row']
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||
ASkeleton: typeof import('@arco-design/web-vue')['Skeleton']
|
||||
ASkeletonLine: typeof import('@arco-design/web-vue')['SkeletonLine']
|
||||
ASkeletonShape: typeof import('@arco-design/web-vue')['SkeletonShape']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph']
|
||||
ATypographyText: typeof import('@arco-design/web-vue')['TypographyText']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<a-config-provider :locale="locale">
|
||||
<router-view />
|
||||
<global-setting />
|
||||
<ThemeBox />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
|
@ -10,9 +9,8 @@
|
|||
import { computed } from 'vue';
|
||||
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
|
||||
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||
import GlobalSetting from '@/components/global-setting/index.vue';
|
||||
import GlobalSetting from '@/components/pure/global-setting/index.vue';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
import ThemeBox from '@/components/theme-box/index.vue';
|
||||
|
||||
const { currentLocale } = useLocale();
|
||||
const locale = computed(() => {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import MSR from '@/api/http/index';
|
||||
import { GetApiTestList, GetApiTestListUrl } from '@/api/requrls/api-test';
|
||||
import { QueryParams } from '@/components/ms-table/useTable';
|
||||
import { QueryParams } from '@/models/common';
|
||||
import { ApiTestListI } from '@/models/api-test';
|
||||
|
||||
export function getTableList(params: QueryParams) {
|
||||
const { current, pageSize } = params;
|
||||
const { current, pageSize, sort, filter, keyword } = params;
|
||||
return MSR.post<ApiTestListI>({
|
||||
url: GetApiTestList,
|
||||
data: { current, pageSize, projectId: 'test-project-id' },
|
||||
data: { current, pageSize, sort, filter, keyword, projectId: 'test-project-id' },
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<template>
|
||||
<div class="ms-base-tale">
|
||||
<a-table v-bind="$attrs">
|
||||
<template v-for="(item, key, i) in slots" :key="i" #[key]="{ record, rowIndex, column }">
|
||||
<slot :name="key" v-bind="{ rowIndex, record, column }"></slot>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSlots } from 'vue';
|
||||
|
||||
const slots = useSlots();
|
||||
</script>
|
|
@ -1,3 +0,0 @@
|
|||
export default {
|
||||
msTable: {},
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
export default {
|
||||
msTable: {},
|
||||
};
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="ms-base-tale">
|
||||
<select-all
|
||||
v-if="attrs.showSelectAll"
|
||||
class="custom-action"
|
||||
:total="selectTotal"
|
||||
:current="selectCurrent"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<a-table v-bind="$attrs" :selected-keys="props.selectedKeys" @selection-change="(e) => selectionChange(e, true)">
|
||||
<template v-for="(item, key, i) in slots" :key="i" #[key]="{ record, rowIndex, column }">
|
||||
<slot :name="key" v-bind="{ rowIndex, record, column }"></slot>
|
||||
</template>
|
||||
<template v-if="selectCurrent > 0" #footer>
|
||||
<batch-action
|
||||
:select-row-count="selectCurrent"
|
||||
@batch-export="emit('batchExport')"
|
||||
@batch-edit="emit('batchEdit')"
|
||||
@batch-move="emit('batchMove')"
|
||||
@batch-related="emit('batchRelated')"
|
||||
@batch-delete="emit('batchDelete')"
|
||||
/>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// eslint-disable no-console
|
||||
import { useSlots, useAttrs, computed, ref, onMounted, watchEffect } from 'vue';
|
||||
import selectAll from './select-all.vue';
|
||||
import { MsTabelProps, SelectAllEnum, MsPaginationI } from './type';
|
||||
import BatchAction from './batchAction.vue';
|
||||
|
||||
const batchleft = ref('10px');
|
||||
const props = defineProps({
|
||||
selectedKeys: {
|
||||
type: Array as unknown as () => (string | number)[],
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emit = defineEmits<{
|
||||
(e: 'selectedChange', value: (string | number)[]): void;
|
||||
(e: 'batchExport'): void;
|
||||
(e: 'batchEdit'): void;
|
||||
(e: 'batchMove'): void;
|
||||
(e: 'batchRelated'): void;
|
||||
(e: 'batchDelete'): void;
|
||||
}>();
|
||||
const isSelectAll = ref(false);
|
||||
// 全选按钮-当前的条数
|
||||
const selectCurrent = ref(0);
|
||||
|
||||
const slots = useSlots();
|
||||
const attrs = useAttrs();
|
||||
|
||||
const { data, rowKey, pagination }: Partial<MsTabelProps> = attrs;
|
||||
|
||||
// 全选按钮-总条数
|
||||
const selectTotal = computed(() => {
|
||||
if (pagination) {
|
||||
const { pageSize } = pagination as MsPaginationI;
|
||||
return pageSize;
|
||||
}
|
||||
return data ? data.length : 20;
|
||||
});
|
||||
// 选择行change事件
|
||||
const selectionChange = (arr: (string | number)[], setCurrentSelect: boolean) => {
|
||||
emit('selectedChange', arr);
|
||||
if (setCurrentSelect) {
|
||||
selectCurrent.value = arr.length;
|
||||
}
|
||||
};
|
||||
|
||||
// 全选change事件
|
||||
const handleChange = (v: string) => {
|
||||
isSelectAll.value = v === SelectAllEnum.ALL;
|
||||
if (v === SelectAllEnum.NONE) {
|
||||
selectionChange([], true);
|
||||
}
|
||||
if (v === SelectAllEnum.CURRENT) {
|
||||
if (data && data.length > 0) {
|
||||
selectionChange(
|
||||
data.map((item: any) => item[rowKey || 'id']),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
if (v === SelectAllEnum.ALL) {
|
||||
const { total } = pagination as MsPaginationI;
|
||||
if (data && data.length > 0) {
|
||||
selectionChange(
|
||||
data.map((item: any) => item[rowKey || 'id']),
|
||||
false
|
||||
);
|
||||
selectCurrent.value = total;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 根据参数获取全选按钮的位置
|
||||
const getBatchLeft = () => {
|
||||
if (attrs.enableDrag) {
|
||||
return '30px';
|
||||
}
|
||||
switch (attrs.size) {
|
||||
case 'small':
|
||||
return '10px';
|
||||
case 'mini':
|
||||
return '10px';
|
||||
default:
|
||||
return '10px';
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
batchleft.value = getBatchLeft();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ms-base-tale {
|
||||
position: relative;
|
||||
.custom-action {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: v-bind(batchleft);
|
||||
z-index: 100;
|
||||
border-radius: 2px;
|
||||
line-height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<div class="ms-table__patch-action">
|
||||
<span>{{ t('msTable.batch.selected', { count: props.selectRowCount }) }}</span>
|
||||
<a-button @click="emit('batchExport')">{{ t('msTable.batch.export') }}</a-button>
|
||||
<a-button @click="emit('batchEdit')">{{ t('msTable.batch.edit') }}</a-button>
|
||||
<a-button @click="emit('batchExport')">{{ t('msTable.batch.moveTo') }}</a-button>
|
||||
<a-button @click="emit('batchExport')">{{ t('msTable.batch.copyTo') }}</a-button>
|
||||
<a-button @click="emit('batchExport')">{{ t('msTable.batch.related') }}</a-button>
|
||||
<a-button @click="emit('batchExport')">{{ t('msTable.batch.delete') }}</a-button>
|
||||
<a-button @click="emit('clear')">{{ t('msTable.batch.clear') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
selectRowCount: {
|
||||
type: Number,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits<{
|
||||
(e: 'batchExport'): void;
|
||||
(e: 'batchEdit'): void;
|
||||
(e: 'batchMove'): void;
|
||||
(e: 'batchRelated'): void;
|
||||
(e: 'batchDelete'): void;
|
||||
(e: 'clear'): void;
|
||||
}>();
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
export default {
|
||||
msTable: {
|
||||
current: 'Select Current Page',
|
||||
all: 'Select All Pages',
|
||||
batch: {
|
||||
title: '批量操作',
|
||||
selected: '已选择 {count} 项',
|
||||
export: '导出',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
moveTo: '移动到',
|
||||
copyTo: '复制到',
|
||||
related: '关联需求',
|
||||
generate: '生成依赖关系',
|
||||
add: '添加到公用用例库',
|
||||
clear: 'clear',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
export default {
|
||||
msTable: {
|
||||
current: '全选当前页',
|
||||
all: '全选所有页',
|
||||
batch: {
|
||||
title: '批量操作',
|
||||
selected: '已选择 {count} 项',
|
||||
export: '导出',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
moveTo: '移动到',
|
||||
copyTo: '复制到',
|
||||
related: '关联需求',
|
||||
generate: '生成依赖关系',
|
||||
add: '添加到公用用例库',
|
||||
clear: '清空',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="ms-table-select-all items-center text-base">
|
||||
<a-checkbox v-model="checked" class="text-base" :indeterminate="indeterminate" @change="handleCheckChange" />
|
||||
<a-dropdown position="bl" @select="handleSelect">
|
||||
<a-icon-down class="dropdown-icon ml-0.5" />
|
||||
<template #content>
|
||||
<a-doption :value="SelectAllEnum.CURRENT">{{ t('msTable.current') }}</a-doption>
|
||||
<a-doption :value="SelectAllEnum.ALL">{{ t('msTable.all') }}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { SelectAllEnum } from './type';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'change', value: string): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps({
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const checked = ref(false);
|
||||
const indeterminate = ref(false);
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.current === 0) {
|
||||
checked.value = false;
|
||||
indeterminate.value = false;
|
||||
} else if (props.current < props.total) {
|
||||
checked.value = false;
|
||||
indeterminate.value = true;
|
||||
} else if (props.current >= props.total) {
|
||||
checked.value = true;
|
||||
indeterminate.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
const handleSelect = (v: string | number | Record<string, any> | undefined) => {
|
||||
emit('change', v as string);
|
||||
};
|
||||
|
||||
const handleCheckChange = () => {
|
||||
if (checked.value) {
|
||||
handleSelect(SelectAllEnum.CURRENT);
|
||||
} else {
|
||||
handleSelect(SelectAllEnum.NONE);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ms-table-select-all {
|
||||
.dropdown-icon {
|
||||
color: rgb(var(--primary-6));
|
||||
}
|
||||
.dropdown-icon:hover {
|
||||
border-radius: 50%;
|
||||
background-color: rgb(var(--primary-3));
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,14 @@
|
|||
import { TableColumnData, TableData, TableDraggable } from '@arco-design/web-vue';
|
||||
import { TableColumnData, TableData, TableDraggable, TableChangeExtra } from '@arco-design/web-vue';
|
||||
|
||||
export interface MsPaginationI {
|
||||
pageSize?: number;
|
||||
total?: number;
|
||||
current?: number;
|
||||
current: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
showPageSize: boolean;
|
||||
}
|
||||
|
||||
// 表格属性
|
||||
export interface MsTabelProps extends MsPaginationI {
|
||||
export interface MsTabelProps {
|
||||
// 表格列 - 详见 TableColumn https://arco.design/web-vue/components/table-column;
|
||||
columns: TableColumnData[];
|
||||
// 表格数据 - 详见 TableData https://arco.design/web-vue/components/table-data;
|
||||
|
@ -20,6 +21,7 @@ export interface MsTabelProps extends MsPaginationI {
|
|||
y?: number | string;
|
||||
};
|
||||
// 表格是否可拖拽
|
||||
enableDrag?: boolean;
|
||||
draggable?: TableDraggable;
|
||||
// 表格是否可编辑
|
||||
editable?: boolean;
|
||||
|
@ -29,6 +31,8 @@ export interface MsTabelProps extends MsPaginationI {
|
|||
sortable?: boolean;
|
||||
// 表格是否可选中
|
||||
selectable?: boolean;
|
||||
// 展示自定义全选
|
||||
showSelectAll?: boolean;
|
||||
// 表格是否可展开
|
||||
expandable?: boolean;
|
||||
// 表格是否可固定表头
|
||||
|
@ -41,9 +45,28 @@ export interface MsTabelProps extends MsPaginationI {
|
|||
loading?: boolean;
|
||||
bordered?: boolean;
|
||||
// pagination
|
||||
pagination?: MsPaginationI;
|
||||
pagination: MsPaginationI | boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface MsTableSelectAll {
|
||||
value: boolean;
|
||||
total: number;
|
||||
current: number;
|
||||
type: 'all' | 'page';
|
||||
}
|
||||
|
||||
export type MsTableData = TableData[];
|
||||
export type MsTableColumn = TableColumnData[];
|
||||
export type MSTableChangeExtra = TableChangeExtra;
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
export enum SelectAllEnum {
|
||||
ALL = 'all',
|
||||
CURRENT = 'current',
|
||||
NONE = 'none',
|
||||
}
|
||||
|
||||
export interface SortItem {
|
||||
[key: string]: string;
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
// 核心的封装方法,详细参数看文档 https://arco.design/vue/component/table
|
||||
// hook/table-props.ts
|
||||
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { MsTabelProps, MsTableData, MsTableColumn } from './type';
|
||||
import { ApiTestListI } from '@/models/api-test';
|
||||
import { TableData } from '@arco-design/web-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { QueryParams } from '@/models/common';
|
||||
|
||||
export interface Pagination {
|
||||
current: number;
|
||||
|
@ -12,47 +15,62 @@ export interface Pagination {
|
|||
showPageSize: boolean;
|
||||
}
|
||||
|
||||
export interface QueryParams {
|
||||
current: number;
|
||||
pageSize: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type GetListFunc = (v: QueryParams) => Promise<ApiTestListI>;
|
||||
export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<MsTabelProps>) {
|
||||
// 行选择
|
||||
const rowSelection = reactive({
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
showCheckedAll: true,
|
||||
onlyCurrent: false,
|
||||
});
|
||||
showCheckedAll: false,
|
||||
};
|
||||
|
||||
const defaultProps: MsTabelProps = {
|
||||
'bordered': true,
|
||||
'size': 'small',
|
||||
'scroll': { y: '550px', x: '1400px' },
|
||||
'checkable': true,
|
||||
'expandable': false,
|
||||
'loading': true,
|
||||
'data': [] as MsTableData,
|
||||
'columns': [] as MsTableColumn,
|
||||
'pagination': {
|
||||
bordered: true,
|
||||
showPagination: true,
|
||||
size: 'small',
|
||||
scroll: { y: '550px', x: '1400px' },
|
||||
checkable: true,
|
||||
loading: true,
|
||||
data: [] as MsTableData,
|
||||
columns: [] as MsTableColumn,
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
showPageSize: true,
|
||||
} as Pagination,
|
||||
'draggable': { type: 'handle' },
|
||||
'row-key': 'id',
|
||||
rowKey: 'id',
|
||||
selectedKeys: [],
|
||||
selectedAll: false,
|
||||
enableDrag: false,
|
||||
showSelectAll: true,
|
||||
...props,
|
||||
};
|
||||
|
||||
// 属性组
|
||||
const propsRes = ref(defaultProps);
|
||||
|
||||
// 排序
|
||||
const sortItem = ref<object>({});
|
||||
|
||||
// 筛选
|
||||
const filterItem = ref<object>({});
|
||||
|
||||
// keyword
|
||||
const keyword = ref('');
|
||||
|
||||
// 是否分页
|
||||
if (!propsRes.value.showPagination) {
|
||||
propsRes.value.pagination = false;
|
||||
}
|
||||
|
||||
// 是否可选中
|
||||
if (propsRes.value.selectable) {
|
||||
propsRes.value['row-selection'] = rowSelection;
|
||||
propsRes.value.rowSelection = rowSelection;
|
||||
}
|
||||
|
||||
// 是否可拖拽
|
||||
if (propsRes.value.enableDrag) {
|
||||
propsRes.value.draggable = { type: 'handle' };
|
||||
}
|
||||
|
||||
// 加载效果
|
||||
|
@ -72,9 +90,11 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
}
|
||||
|
||||
const setPagination = ({ current, total }: SetPaginationPrams) => {
|
||||
if (propsRes.value.pagination) {
|
||||
if (propsRes.value.pagination && typeof propsRes.value.pagination === 'object') {
|
||||
propsRes.value.pagination.current = current;
|
||||
if (total) propsRes.value.pagination.total = total;
|
||||
if (total) {
|
||||
propsRes.value.pagination.total = total;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,6 +113,10 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
loadListParams.value = params || {};
|
||||
};
|
||||
|
||||
const setKeyword = (v: string) => {
|
||||
keyword.value = v;
|
||||
};
|
||||
|
||||
// 加载分页列表数据
|
||||
const loadList = async () => {
|
||||
const { current, pageSize } = propsRes.value.pagination as Pagination;
|
||||
|
@ -100,9 +124,20 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
const data = await loadListFunc({
|
||||
current,
|
||||
pageSize,
|
||||
...loadListParams.value,
|
||||
sort: sortItem.value,
|
||||
filter: filterItem.value,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
const tmpArr = data.list as unknown as MsTableData;
|
||||
propsRes.value.data = tmpArr.map((item: TableData) => {
|
||||
if (item.updateTime) {
|
||||
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
if (item.createTime) {
|
||||
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
return item;
|
||||
});
|
||||
propsRes.value.data = data.list as unknown as MsTableData;
|
||||
setPagination({ current: data.current, total: data.total });
|
||||
setLoading(false);
|
||||
return data;
|
||||
|
@ -113,7 +148,14 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
// 排序触发
|
||||
sorterChange: (dataIndex: string, direction: string) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(dataIndex, direction);
|
||||
sortItem.value = { [dataIndex]: direction };
|
||||
loadList();
|
||||
},
|
||||
|
||||
// 筛选触发
|
||||
filterChange: (dataIndex: string, filteredValues: string[]) => {
|
||||
filterItem.value = { [dataIndex]: filteredValues };
|
||||
loadList();
|
||||
},
|
||||
// 分页触发
|
||||
pageChange: (current: number) => {
|
||||
|
@ -122,13 +164,23 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
},
|
||||
// 修改每页显示条数
|
||||
pageSizeChange: (pageSize: number) => {
|
||||
if (propsRes.value.pagination) {
|
||||
if (propsRes.value.pagination && typeof propsRes.value.pagination === 'object') {
|
||||
propsRes.value.pagination.pageSize = pageSize;
|
||||
}
|
||||
loadList();
|
||||
},
|
||||
// 选择触发
|
||||
selectedChange: (arr: (string | number)[]) => {
|
||||
if (arr.length === 0) {
|
||||
propsRes.value.pagination = defaultProps.pagination;
|
||||
} else {
|
||||
propsRes.value.pagination = false;
|
||||
}
|
||||
|
||||
propsRes.value.selectedKeys = arr;
|
||||
},
|
||||
change: (_data: MsTableData) => {
|
||||
if (propsRes.value.draggable) {
|
||||
if (propsRes.value.draggable && _data instanceof Array) {
|
||||
// eslint-disable-next-line vue/require-explicit-emits
|
||||
propsRes.value.data = _data;
|
||||
}
|
||||
|
@ -143,5 +195,6 @@ export default function useTbleProps(loadListFunc: GetListFunc, props?: Partial<
|
|||
loadList,
|
||||
setPagination,
|
||||
setLoadListParams,
|
||||
setKeyword,
|
||||
};
|
||||
}
|
|
@ -142,9 +142,9 @@
|
|||
import { LOCALE_OPTIONS } from '@/locale';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
import useUser from '@/hooks/useUser';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import Menu from '@/components/pure/menu/index.vue';
|
||||
import MessageBox from '../message-box/index.vue';
|
||||
import ProjcetSelection from '@/components/project-selection/index.vue';
|
||||
import ProjcetSelection from '@/components/pure/project-selection/index.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
|
@ -1,275 +0,0 @@
|
|||
<template>
|
||||
<a-badge class="theme-badge bottom-[124px] right-[70px]" :count="theme ? 1 : 0" dot>
|
||||
<a-button
|
||||
class="theme-badge-button"
|
||||
:shape="hover ? 'round' : 'circle'"
|
||||
size="large"
|
||||
@click="modalVisible = true"
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
>
|
||||
<IconSkin />
|
||||
<span v-if="hover" style="margin-left: 8px">
|
||||
{{ t('themeBox.installTheme') }}
|
||||
</span>
|
||||
</a-button>
|
||||
</a-badge>
|
||||
<a-modal :visible="modalVisible" :width="900" @ok="modalVisible = false" @cancel="modalVisible = false">
|
||||
<template #title>
|
||||
<div class="theme-box-header pr-[24px]">
|
||||
<span>{{ t('themeBox.installTheme') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<a-row :gutter="[20, 20]">
|
||||
<template v-if="isLoading">
|
||||
<a-col v-for="(_, index) of loadingFillArray" :key="index" :span="8">
|
||||
<a-card v-if="isLoading" class="theme-box-card">
|
||||
<template #cover>
|
||||
<a-skeleton animation>
|
||||
<a-skeleton-shape style="width: 272px; height: 160px" />
|
||||
</a-skeleton>
|
||||
</template>
|
||||
<a-card-meta>
|
||||
<template #title>
|
||||
<a-skeleton animation>
|
||||
<a-skeleton-line :line-height="25" />
|
||||
</a-skeleton>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
<a-skeleton animation>
|
||||
<a-skeleton-shape style="margin-top: 20px; margin-left: auto; width: 100px; height: 24px" />
|
||||
</a-skeleton>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</template>
|
||||
<template v-else-if="themeList.length > 0">
|
||||
<a-col v-for="item of themeList" :key="item.themeId" :span="8">
|
||||
<a-card class="theme-box-card">
|
||||
<template #cover>
|
||||
<img :src="item.cover" style="height: 160px" alt="theme-cover" />
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-button
|
||||
class="theme-box-card-link"
|
||||
type="text"
|
||||
size="mini"
|
||||
:href="`https://arco.design/themes/design/${item.themeId}`"
|
||||
>
|
||||
<template #icon>
|
||||
<IconLink />
|
||||
</template>
|
||||
{{ t('themeBox.openInDesignLab') }}
|
||||
</a-button>
|
||||
<a-tag v-if="theme && theme.themeId === item.themeId" color="arcoblue">
|
||||
{{ t('themeBox.currentTheme') }}
|
||||
</a-tag>
|
||||
<a-button v-else type="primary" size="mini" @click="() => useTheme(item)">
|
||||
{{ t('themeBox.install') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<a-card-meta :title="item.themeName" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Empty style="margin: 200px 0">
|
||||
<template #description>
|
||||
{{ t('themeBox.noResult') }}
|
||||
<a-link :href="`https://arco.design/themes`">
|
||||
{{ t('themeBox.createTheme') }}
|
||||
</a-link>
|
||||
</template>
|
||||
</Empty>
|
||||
</template>
|
||||
</a-row>
|
||||
<div class="theme-box-bottom mt-[20px]">
|
||||
<a-pagination :total="total" :current="page" :page-size="6" @change="onPageChange" />
|
||||
</div>
|
||||
<template v-if="theme" #footer>
|
||||
<div class="theme-box-footer">
|
||||
<a-typography-text bold> {{ t('themeBox.currentTheme') }}: {{ theme.themeName }} </a-typography-text>
|
||||
<a-button type="primary" status="danger" size="small" @click="onResetClick">
|
||||
{{ t('themeBox.resetTheme') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import axios from 'axios';
|
||||
import { IconSkin, IconLink } from '@arco-design/web-vue/es/icon';
|
||||
import { getLocalStorage, removeLocalStorage, setLocalStorage } from '../../utils/local-storage';
|
||||
import { apiBasename } from '../../utils/api';
|
||||
import { ThemeData } from './interface';
|
||||
|
||||
const THEME_LINK_ID = 'arco-custom-theme';
|
||||
const loadingFillArray = Array(3).fill(1);
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ThemeBox',
|
||||
components: {
|
||||
IconSkin,
|
||||
IconLink,
|
||||
},
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
const theme = ref<ThemeData>();
|
||||
const themeList = ref<ThemeData[]>([]);
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
const modalVisible = ref(false);
|
||||
const searchValue = ref('');
|
||||
const isLoading = ref(false);
|
||||
const hover = ref(false);
|
||||
|
||||
const removeTheme = () => {
|
||||
const linkElement = document.getElementById(THEME_LINK_ID);
|
||||
if (linkElement) {
|
||||
document.body.removeChild(linkElement);
|
||||
}
|
||||
theme.value = undefined;
|
||||
};
|
||||
|
||||
const addTheme = (_theme: ThemeData, notice: boolean) => {
|
||||
const url = `${_theme.unpkgHost}${_theme.packageName}/css/arco.css`;
|
||||
axios
|
||||
.get(url)
|
||||
.then(() => {
|
||||
if (theme.value) {
|
||||
removeTheme();
|
||||
}
|
||||
const linkElement = document.createElement('link');
|
||||
linkElement.id = THEME_LINK_ID;
|
||||
linkElement.href = url;
|
||||
linkElement.type = 'text/css';
|
||||
linkElement.rel = 'stylesheet';
|
||||
document.body.appendChild(linkElement);
|
||||
theme.value = _theme;
|
||||
|
||||
if (notice) {
|
||||
Notification.success({
|
||||
id: 'theme',
|
||||
title: t('themeBox.installTheme'),
|
||||
content: t('themeBox.installThemeSuccess'),
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
Notification.error({
|
||||
id: 'theme',
|
||||
title: t('themeBox.installTheme'),
|
||||
content: t('themeBox.installThemeError'),
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const useTheme = (_theme: ThemeData, notice = true) => {
|
||||
addTheme(_theme, notice);
|
||||
setLocalStorage('vue-custom-theme', _theme);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// const _theme = getLocalStorage<ThemeData>('vue-custom-theme', true) as ThemeData;
|
||||
// if (_theme) {
|
||||
// useTheme(_theme, false);
|
||||
// }
|
||||
});
|
||||
|
||||
const fetchThemeList = async (current: number, search: string) => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const data = await axios.get(
|
||||
`${apiBasename}/themes/api/open/themes/list?pageSize=6¤tPage=${current}&depLibrary=@arco-design/web-vue&keyword=vue-ms-theme-${search}`
|
||||
);
|
||||
|
||||
themeList.value = data.data.list;
|
||||
total.value = data.data.total;
|
||||
} catch (e) {
|
||||
themeList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
isLoading.value = false;
|
||||
};
|
||||
|
||||
watch(modalVisible, (visible) => {
|
||||
if (visible) {
|
||||
fetchThemeList(page.value, searchValue.value);
|
||||
}
|
||||
});
|
||||
|
||||
const onResetClick = () => {
|
||||
removeTheme();
|
||||
removeLocalStorage('vue-custom-theme');
|
||||
};
|
||||
|
||||
const onSearchInput = (value: string) => {
|
||||
searchValue.value = value;
|
||||
page.value = 1;
|
||||
fetchThemeList(1, value);
|
||||
};
|
||||
|
||||
const onPageChange = (_page: number) => {
|
||||
page.value = _page;
|
||||
fetchThemeList(_page, searchValue.value);
|
||||
};
|
||||
|
||||
return {
|
||||
hover,
|
||||
modalVisible,
|
||||
themeList,
|
||||
theme,
|
||||
total,
|
||||
page,
|
||||
isLoading,
|
||||
loadingFillArray,
|
||||
searchValue,
|
||||
useTheme,
|
||||
onSearchInput,
|
||||
onPageChange,
|
||||
onResetClick,
|
||||
t,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.theme-box {
|
||||
&-header {
|
||||
@apply flex w-full items-center justify-between;
|
||||
}
|
||||
&-card {
|
||||
&-link {
|
||||
@apply opacity-0;
|
||||
|
||||
transition: opacity 100ms;
|
||||
}
|
||||
&:hover &-link {
|
||||
@apply opacity-100;
|
||||
}
|
||||
:deep(.arco-card-meta-title) {
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
&-bottom {
|
||||
@apply flex justify-end;
|
||||
}
|
||||
&-footer {
|
||||
@apply flex items-center justify-between;
|
||||
}
|
||||
}
|
||||
.theme-badge {
|
||||
@apply fixed;
|
||||
&-button {
|
||||
border: 1px solid var(--color-fill-3) !important;
|
||||
background: var(--color-bg-5) !important;
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,7 +0,0 @@
|
|||
export interface ThemeData {
|
||||
themeId: number;
|
||||
themeName: string;
|
||||
cover: string;
|
||||
packageName: string;
|
||||
unpkgHost: string;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
export default {
|
||||
themeBox: {
|
||||
currentTheme: 'Current theme',
|
||||
autoUseTheme: 'Automatically use theme',
|
||||
install: 'Install',
|
||||
installTheme: 'Install theme',
|
||||
search: 'Search',
|
||||
installingTheme: 'Installing theme...',
|
||||
installThemeSuccess: 'Install theme successfully! ',
|
||||
installThemeError: 'Install theme failed, please try again! ',
|
||||
resetTheme: 'Reset theme',
|
||||
resetThemeSuccess: 'Reset theme successfully! ',
|
||||
openInDesignLab: 'Open in the Design Lab',
|
||||
noResult: 'No related themes',
|
||||
createTheme: 'Go to the Design Lab to create',
|
||||
},
|
||||
};
|
|
@ -1,17 +0,0 @@
|
|||
export default {
|
||||
themeBox: {
|
||||
currentTheme: '当前主题',
|
||||
autoUseTheme: '自动应用主题',
|
||||
install: '安装',
|
||||
installTheme: '安装主题',
|
||||
search: '搜索',
|
||||
installingTheme: '正在安装主题...',
|
||||
installThemeSuccess: '主题安装成功!',
|
||||
installThemeError: '主题安装失败,请重试!',
|
||||
resetTheme: '重置主题',
|
||||
resetThemeSuccess: '重置主题成功!',
|
||||
openInDesignLab: '在主题商店打开',
|
||||
noResult: '没有相关主题',
|
||||
createTheme: '前往主题商店创建',
|
||||
},
|
||||
};
|
|
@ -48,10 +48,10 @@
|
|||
import { ref, computed, watch, provide, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import NavBar from '@/components/navbar/index.vue';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import Footer from '@/components/footer/index.vue';
|
||||
import TabBar from '@/components/tab-bar/index.vue';
|
||||
import NavBar from '@/components/pure/navbar/index.vue';
|
||||
import Menu from '@/components/pure/menu/index.vue';
|
||||
import Footer from '@/components/pure/footer/index.vue';
|
||||
import TabBar from '@/components/pure/tab-bar/index.vue';
|
||||
import usePermission from '@/hooks/usePermission';
|
||||
import useResponsive from '@/hooks/useResponsive';
|
||||
import PageLayout from './page-layout.vue';
|
||||
|
|
|
@ -1,38 +1,22 @@
|
|||
import dayjsLocale from 'dayjs/locale/en';
|
||||
import localeSettings from './settings';
|
||||
import sys from './sys';
|
||||
import localeMessageBox from '@/components/message-box/locale/en-US';
|
||||
import minder from '@/components/minder-editor/locale/en-US';
|
||||
import localeMessageBox from '@/components/pure/message-box/locale/en-US';
|
||||
import minder from '@/components/pure/minder-editor/locale/en-US';
|
||||
import localeLogin from '@/views/login/locale/en-US';
|
||||
import localeWorkplace from '@/views/dashboard/workplace/locale/en-US';
|
||||
import localeThemebox from '@/components/theme-box/locale/en-US';
|
||||
import localeTable from '@/components/pure/ms-table/locale/en-US';
|
||||
import localeApiTest from '@/views/api-test/locale/en-US';
|
||||
|
||||
export default {
|
||||
message: {
|
||||
'menu.component': 'component hub',
|
||||
'menu.component.demo': 'component demo',
|
||||
'menu.apitest': 'Api Test',
|
||||
'menu.dashboard': 'Dashboard',
|
||||
'menu.minder': 'Minder',
|
||||
'menu.server.dashboard': 'Dashboard-Server',
|
||||
'menu.server.workplace': 'Workplace-Server',
|
||||
'menu.server.monitor': 'Monitor-Server',
|
||||
'menu.list': 'List',
|
||||
'menu.result': 'Result',
|
||||
'menu.exception': 'Exception',
|
||||
'menu.form': 'Form',
|
||||
'menu.profile': 'Profile',
|
||||
'menu.visualization': 'Data Visualization',
|
||||
'menu.user': 'User Center',
|
||||
'menu.apiTest': 'Api Test',
|
||||
'navbar.action.locale': 'Switch to English',
|
||||
...sys,
|
||||
...localeSettings,
|
||||
...localeMessageBox,
|
||||
...localeLogin,
|
||||
...localeWorkplace,
|
||||
...minder,
|
||||
...localeThemebox,
|
||||
...localeTable,
|
||||
...localeApiTest,
|
||||
},
|
||||
dayjsLocale,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue