Browse Source

Merge pull request #957 from colinin/backlogg-update

backlogs update
pull/953/merge
yx lin 2 years ago
committed by GitHub
parent
commit
17d34737b2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 30
      apps/vue/.env.development
  2. 53
      apps/vue/src/components/Table/src/BasicTable.vue
  3. 107
      apps/vue/src/components/Table/src/components/AdvancedSearch.vue
  4. 14
      apps/vue/src/components/Table/src/components/EditTableHeaderIcon.vue
  5. 39
      apps/vue/src/components/Table/src/components/HeaderCell.vue
  6. 30
      apps/vue/src/components/Table/src/components/TableAction.vue
  7. 68
      apps/vue/src/components/Table/src/components/TableFooter.vue
  8. 54
      apps/vue/src/components/Table/src/components/TableHeader.vue
  9. 35
      apps/vue/src/components/Table/src/components/TableImg.vue
  10. 54
      apps/vue/src/components/Table/src/components/TableSelectionBar.vue
  11. 27
      apps/vue/src/components/Table/src/components/TableTitle.vue
  12. 94
      apps/vue/src/components/Table/src/components/editable/EditableCell.vue
  13. 6
      apps/vue/src/components/Table/src/components/editable/index.ts
  14. 721
      apps/vue/src/components/Table/src/components/settings/ColumnSetting.vue
  15. 20
      apps/vue/src/components/Table/src/components/settings/FullScreenSetting.vue
  16. 16
      apps/vue/src/components/Table/src/components/settings/RedoSetting.vue
  17. 57
      apps/vue/src/components/Table/src/components/settings/SizeSetting.vue
  18. 39
      apps/vue/src/components/Table/src/components/settings/TableExport.vue
  19. 36
      apps/vue/src/components/Table/src/components/settings/index.vue
  20. 28
      apps/vue/src/components/Table/src/helper.ts
  21. 58
      apps/vue/src/components/Table/src/hooks/useColumns.ts
  22. 53
      apps/vue/src/components/Table/src/hooks/useCustomRow.ts
  23. 84
      apps/vue/src/components/Table/src/hooks/useDataSource.ts
  24. 89
      apps/vue/src/components/Table/src/hooks/useRowSelection.ts
  25. 32
      apps/vue/src/components/Table/src/hooks/useTable.ts
  26. 107
      apps/vue/src/components/Table/src/hooks/useTableExpand.ts
  27. 10
      apps/vue/src/components/Table/src/hooks/useTableFooter.ts
  28. 2
      apps/vue/src/components/Table/src/hooks/useTableForm.ts
  29. 5
      apps/vue/src/components/Table/src/hooks/useTableHeader.ts
  30. 261
      apps/vue/src/components/Table/src/hooks/useTableScroll.ts
  31. 77
      apps/vue/src/components/Table/src/types/table.ts
  32. 3
      apps/vue/src/enums/cacheEnum.ts
  33. 27
      apps/vue/src/hooks/web/useSignalR.ts
  34. 9
      apps/vue/src/settings/designSetting.ts
  35. 126
      apps/vue/src/store/modules/tableSetting.ts
  36. 4
      apps/vue/src/utils/cache/persistent.ts
  37. 112
      apps/vue/src/utils/is.ts
  38. 2
      apps/vue/src/views/account/center/Cloud.vue
  39. 2
      apps/vue/src/views/account/center/FileList.vue
  40. 18
      apps/vue/src/views/oss-management/objects/components/FileList.vue
  41. 16
      apps/vue/src/views/sys/login/TwoFactorLoginForm.vue
  42. 15
      apps/vue/src/views/task-management/background-jobs/components/JobTable.vue
  43. 20
      apps/vue/src/views/webhooks/send-attempts/components/SendAttemptTable.vue
  44. 17
      apps/vue/src/views/webhooks/subscriptions/components/SubscriptionTable.vue
  45. 8
      apps/vue/types/store.d.ts
  46. 22
      aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/HttpOverrides/AbpAspNetCoreHttpOverridesModule.cs
  47. 36
      aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/HttpOverrides/Forwarded/AbpForwardedHeadersOptions.cs
  48. 27
      aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/WebClientInfo/RequestForwardedHeaderWebClientInfoProvider.cs
  49. 67
      aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/Microsoft/AspNetCore/Builder/ForwardedHeadersOptionsExtensions.cs
  50. 3
      aspnet-core/services/LY.MicroService.Applications.Single/Dockerfile
  51. 14
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.Configure.cs
  52. 9
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.cs
  53. 3
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/Dockerfile
  54. 3
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/LY.MicroService.AuthServer.HttpApi.Host.csproj
  55. 3
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/appsettings.Development.json
  56. 3
      aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/appsettings.json
  57. 21
      aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs
  58. 8
      aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.cs
  59. 3
      aspnet-core/services/LY.MicroService.AuthServer/Dockerfile
  60. 1
      aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj
  61. 1
      aspnet-core/services/LY.MicroService.AuthServer/appsettings.Development.json
  62. 3
      aspnet-core/services/LY.MicroService.AuthServer/appsettings.json
  63. 14
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs
  64. 10
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs
  65. 3
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Dockerfile
  66. 1
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/LY.MicroService.BackendAdmin.HttpApi.Host.csproj
  67. 6
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json
  68. 3
      aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.json
  69. 3
      aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/Dockerfile
  70. 14
      aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs
  71. 9
      aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs
  72. 1
      aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj
  73. 3
      aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/appsettings.json
  74. 3
      aspnet-core/services/LY.MicroService.IdentityServer/Dockerfile
  75. 21
      aspnet-core/services/LY.MicroService.IdentityServer/IdentityServerModule.Configure.cs
  76. 8
      aspnet-core/services/LY.MicroService.IdentityServer/IdentityServerModule.cs
  77. 1
      aspnet-core/services/LY.MicroService.IdentityServer/LY.MicroService.IdentityServer.csproj
  78. 3
      aspnet-core/services/LY.MicroService.IdentityServer/appsettings.json
  79. 3
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/Dockerfile
  80. 1
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj
  81. 14
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs
  82. 22
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs
  83. 3
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json
  84. 3
      aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.json
  85. 3
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Dockerfile
  86. 1
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj
  87. 14
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs
  88. 11
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs
  89. 3
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json
  90. 3
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.json
  91. 3
      aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/Dockerfile
  92. 1
      aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj
  93. 14
      aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs
  94. 11
      aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs
  95. 3
      aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.json
  96. 25
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile
  97. 1
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj
  98. 14
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs
  99. 11
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs
  100. 3
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json

30
apps/vue/.env.development

@ -1,30 +0,0 @@
# Whether to open mock
VITE_USE_MOCK=false
# public path
VITE_PUBLIC_PATH=/
# Cross-domain proxy, you can configure multiple
# Please note that no line breaks
VITE_PROXY=[["/connect","http://127.0.0.1:30000"],["/api","http://127.0.0.1:30000"],["/signalr-hubs","ws://127.0.0.1:30000"]]
# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
# Delete console
VITE_DROP_CONSOLE=false
# Basic interface address SPA
VITE_GLOB_API_URL=/api
# File upload address, optional
VITE_GLOB_UPLOAD_URL=/upload
# Interface prefix
VITE_GLOB_API_URL_PREFIX=
# Multi-tenancy key
VITE_GLOB_MULTITENANCY_KEY='__tenant'
# STS Connect
VITE_GLOB_AUTHORITY='http://127.0.0.1:30000'
VITE_GLOB_CLIENT_ID='vue-admin-client'
VITE_GLOB_CLIENT_SECRET='1q2w3e*'

53
apps/vue/src/components/Table/src/BasicTable.vue

@ -33,13 +33,17 @@
v-show="getEmptyDataIsShowTable" v-show="getEmptyDataIsShowTable"
@change="handleTableChange" @change="handleTableChange"
@resizeColumn="handleResizeColumn" @resizeColumn="handleResizeColumn"
@expand="handleTableExpand"
> >
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item"> <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<slot name="headerCell" v-bind="{ column }">
<HeaderCell :column="column" /> <HeaderCell :column="column" />
</slot>
</template> </template>
<!-- 增加对antdv3.x兼容 -->
<template #bodyCell="data"> <template #bodyCell="data">
<slot name="bodyCell" v-bind="data || {}"></slot> <slot name="bodyCell" v-bind="data || {}"></slot>
</template> </template>
@ -62,19 +66,10 @@
TableActionType, TableActionType,
SizeType, SizeType,
ColumnChangeParam, ColumnChangeParam,
InnerMethods,
} from './types/table'; } from './types/table';
import { import { defineComponent, ref, reactive, computed, unref, toRaw, inject, watchEffect, nextTick } from 'vue';
defineComponent,
ref,
reactive,
computed,
unref,
toRaw,
inject,
watchEffect,
nextTick,
} from 'vue';
import { Button, Table } from 'ant-design-vue'; import { Button, Table } from 'ant-design-vue';
import { BasicForm, useForm } from '/@/components/Form/index'; import { BasicForm, useForm } from '/@/components/Form/index';
import { useModal } from '/@/components/Modal/index'; import { useModal } from '/@/components/Modal/index';
@ -209,12 +204,12 @@
emit, emit,
); );
function handleTableChange(...args) { function handleTableChange(pagination: any, filters: any, sorter: any, extra: any) {
onTableChange.call(undefined, ...args); onTableChange(pagination, filters, sorter);
emit('change', ...args); emit('change', pagination, filters, sorter);
// useTableonChange // useTableonChange
const { onChange } = unref(getProps); const { onChange } = unref(getProps);
onChange && isFunction(onChange) && onChange.call(undefined, ...args); onChange && isFunction(onChange) && onChange(pagination, filters, sorter, extra);
} }
const { const {
@ -248,7 +243,7 @@
const { getRowClassName } = useTableStyle(getProps, prefixCls); const { getRowClassName } = useTableStyle(getProps, prefixCls);
const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand( const { getExpandOption, expandAll, expandRows, collapseRows, collapseAll, handleTableExpand } = useTableExpand(
getProps, getProps,
tableData, tableData,
emit, emit,
@ -265,15 +260,14 @@
}, },
}; };
const methods: InnerMethods = {
clearSelectedRowKeys,
getSelectRowKeys,
};
const { getAlertEnabled, getAlertMessage } = useTableAlert(getProps, getRowSelectionRef); const { getAlertEnabled, getAlertMessage } = useTableAlert(getProps, getRowSelectionRef);
const { getHeaderProps } = useTableHeader( const { getHeaderProps } = useTableHeader(getProps, slots, handlers, methods, getAlertEnabled, getAlertMessage);
getProps,
slots,
handlers,
getAlertEnabled,
getAlertMessage,
);
const { getFooterProps } = useTableFooter( const { getFooterProps } = useTableFooter(
getProps, getProps,
@ -289,8 +283,9 @@
getFormSlotKeys, getFormSlotKeys,
handleSearchInfoChange, handleSearchInfoChange,
handleAdvanceSearchChange, handleAdvanceSearchChange,
handleAdvanceSearchInfoChange, handleAdvanceSearchInfoChange
} = useTableForm(getProps, slots, fetch, getLoading, formActions.setFieldsValue); } =
useTableForm(getProps, slots, fetch, getLoading, formActions.setFieldsValue);
const getBindValues = computed(() => { const getBindValues = computed(() => {
const dataSource = unref(getDataSourceRef); const dataSource = unref(getDataSourceRef);
@ -310,9 +305,9 @@
footer: unref(getFooterProps), footer: unref(getFooterProps),
...unref(getExpandOption), ...unref(getExpandOption),
}; };
if (slots.expandedRowRender) { // if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll'); // propsData = omit(propsData, 'scroll');
} // }
propsData = omit(propsData, ['class', 'onChange']); propsData = omit(propsData, ['class', 'onChange']);
return propsData; return propsData;
@ -373,6 +368,7 @@
setCacheColumnsByField, setCacheColumnsByField,
expandAll, expandAll,
expandRows, expandRows,
collapseRows,
collapseAll, collapseAll,
scrollTo, scrollTo,
getSize: () => { getSize: () => {
@ -427,6 +423,7 @@
getWrapperClass, getWrapperClass,
columns: getViewColumns, columns: getViewColumns,
handleResizeColumn, handleResizeColumn,
handleTableExpand,
}; };
}, },
}); });

107
apps/vue/src/components/Table/src/components/AdvancedSearch.vue

@ -8,12 +8,8 @@
> >
<Card :title="t('component.table.advancedSearch.conditions')"> <Card :title="t('component.table.advancedSearch.conditions')">
<template #extra> <template #extra>
<Button @click="resetFields" danger>{{ <Button @click="resetFields" danger>{{ t('component.table.advancedSearch.clearCondition') }}</Button>
t('component.table.advancedSearch.clearCondition') <Button @click="handleAddField" type="primary" style="margin-left: 20px;">{{ t('component.table.advancedSearch.addCondition') }}</Button>
}}</Button>
<Button @click="handleAddField" type="primary" style="margin-left: 20px">{{
t('component.table.advancedSearch.addCondition')
}}</Button>
</template> </template>
<Table <Table
size="small" size="small"
@ -23,9 +19,9 @@
:pagination="false" :pagination="false"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'field'"> <template v-if="column.dataIndex==='field'">
<Select <Select
style="width: 100%" style="width: 100%;"
show-search show-search
v-model:value="record.field" v-model:value="record.field"
:options="getAvailableOptions" :options="getAvailableOptions"
@ -33,39 +29,30 @@
@change="(field) => handleFieldChange(field, record)" @change="(field) => handleFieldChange(field, record)"
/> />
</template> </template>
<template v-else-if="column.dataIndex === 'comparison'"> <template v-else-if="column.dataIndex==='comparison'">
<Select <Select
style="width: 100%" style="width: 100%;"
v-model:value="record.comparison" v-model:value="record.comparison"
:options="getAvailableComparisonOptions(record)" :options="getAvailableComparisonOptions(record)"
/> />
</template> </template>
<template v-else-if="column.dataIndex === 'value'"> <template v-else-if="column.dataIndex==='value'">
<Input v-if="record.javaScriptType === 'string'" v-model:value="record.value" /> <Input v-if="record.javaScriptType==='string'" v-model:value="record.value" />
<Select <Select
v-else-if=" v-else-if="record.javaScriptType==='number' && record.options && record.options.length > 0"
record.javaScriptType === 'number' && record.options && record.options.length > 0 style="width: 100%;"
"
style="width: 100%"
v-model:value="record.value" v-model:value="record.value"
:options="record.options" :options="record.options"
:field-names="{ :field-names="{
label: 'key', label: 'key',
value: 'value', value: 'value'
}" }"
/> />
<InputNumber <InputNumber v-else-if="record.javaScriptType==='number'" style="width: 100%;" v-model:value="record.value" />
v-else-if="record.javaScriptType === 'number'" <Switch v-else-if="record.javaScriptType==='boolean'" v-model:checked="record.value" />
style="width: 100%"
v-model:value="record.value"
/>
<Switch
v-else-if="record.javaScriptType === 'boolean'"
v-model:checked="record.value"
/>
<DatePicker <DatePicker
v-else-if="record.javaScriptType === 'Date'" v-else-if="record.javaScriptType==='Date'"
style="width: 100%" style="width: 100%;"
v-model:value="record.value" v-model:value="record.value"
format="YYYY-MM-DD 00:00:00" format="YYYY-MM-DD 00:00:00"
value-format="YYYY-MM-DDT00:00:00" value-format="YYYY-MM-DDT00:00:00"
@ -77,10 +64,14 @@
v-model="record.value" v-model="record.value"
/> />
</template> </template>
<template v-else-if="column.dataIndex === 'logic'"> <template v-else-if="column.dataIndex==='logic'">
<Select style="width: 100%" v-model:value="record.logic" :options="logicOptions" /> <Select
style="width: 100%;"
v-model:value="record.logic"
:options="logicOptions"
/>
</template> </template>
<template v-else-if="column.dataIndex === 'actions'"> <template v-else-if="column.dataIndex==='actions'">
<Popconfirm <Popconfirm
v-if="formMdel.paramters.length" v-if="formMdel.paramters.length"
:title="t('table.sureToDelete')" :title="t('table.sureToDelete')"
@ -108,18 +99,12 @@
Popconfirm, Popconfirm,
Select, Select,
Switch, Switch,
Table, Table
} from 'ant-design-vue'; } from 'ant-design-vue';
import { CodeEditorX, MODE } from '/@/components/CodeEditor'; import { CodeEditorX, MODE } from '/@/components/CodeEditor';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { import { DefineParamter, DynamicLogic, DynamicComparison, DynamicQueryable, DynamicParamter } from '../types/advancedSearch';
DefineParamter,
DynamicLogic,
DynamicComparison,
DynamicQueryable,
DynamicParamter,
} from '../types/advancedSearch';
import { isNullOrWhiteSpace } from '/@/utils/strings'; import { isNullOrWhiteSpace } from '/@/utils/strings';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { get } from 'lodash-es'; import { get } from 'lodash-es';
@ -135,14 +120,14 @@
default: true, default: true,
}, },
defineFieldApi: { defineFieldApi: {
type: Function as PropType<() => Promise<any>>, type: Function as PropType<() => Promise<any>>
}, },
defineFieldReplace: { defineFieldReplace: {
type: Function as PropType<(response: any) => DefineParamter[]>, type: Function as PropType<(response: any) => DefineParamter[]>,
}, },
listField: { listField: {
type: String, type: String,
}, }
}); });
const { t } = useI18n(); const { t } = useI18n();
const loadingRef = ref(false); const loadingRef = ref(false);
@ -261,26 +246,27 @@
} }
// //
const defineParams = unref(defineParamsRef); const defineParams = unref(defineParamsRef);
if (!defineParams.length) return []; if (!defineParams.length) return[];
return defineParams.filter((dp) => !formMdel.paramters.some((fp) => fp.field === dp.name)); return defineParams.filter(dp => !formMdel.paramters.some(fp => fp.field === dp.name));
}); });
const getAvailableOptions = computed(() => { const getAvailableOptions = computed(() => {
const availableParams = unref(getAvailableParams); const availableParams = unref(getAvailableParams);
if (!availableParams.length) return []; if (!availableParams.length) return[];
return availableParams.map((item) => { return availableParams
.map((item) => {
return { return {
label: item.description, label: item.description,
value: item.name, value: item.name,
children: [], children: [],
}; }
}); });
}); });
const getAvailableComparisonOptions = computed(() => { const getAvailableComparisonOptions = computed(() => {
return (paramter: DynamicParamter) => { return (paramter: DynamicParamter) => {
const defineParams = unref(defineParamsRef); const defineParams = unref(defineParamsRef);
const defineParam = defineParams.find((p) => p.name === paramter.field); const defineParam = defineParams.find(p => p.name === paramter.field);
if (!defineParam) { if (!defineParam) {
return comparisonOptions; return comparisonOptions;
} }
@ -289,16 +275,15 @@
return comparisonOptions; return comparisonOptions;
} }
// //
return comparisonOptions.filter((c) => availableComparator.includes(c.value)); return comparisonOptions
}; .filter(c => availableComparator.includes(c.value));
}
}); });
const filterOption = (input: string, option: any) => { const filterOption = (input: string, option: any) => {
if (isNullOrWhiteSpace(option.label) && isNullOrWhiteSpace(option.value)) return false; if (isNullOrWhiteSpace(option.label) && isNullOrWhiteSpace(option.value)) return false;
return ( return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 ;
option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
);
}; };
onMounted(fetch); onMounted(fetch);
@ -310,8 +295,7 @@
if (!useAdvancedSearch) return; if (!useAdvancedSearch) return;
if (!defineFieldApi || !isFunction(defineFieldApi)) return; if (!defineFieldApi || !isFunction(defineFieldApi)) return;
setLoading(true); setLoading(true);
defineFieldApi() defineFieldApi().then((res) => {
.then((res) => {
let resultItems: DefineParamter[] = []; let resultItems: DefineParamter[] = [];
if (defineFieldReplace && isFunction(defineFieldReplace)) { if (defineFieldReplace && isFunction(defineFieldReplace)) {
resultItems = defineFieldReplace(res); resultItems = defineFieldReplace(res);
@ -320,8 +304,7 @@
resultItems = isArrayResult ? res : get(res, listField || 'items'); resultItems = isArrayResult ? res : get(res, listField || 'items');
} }
defineParamsRef.value = resultItems; defineParamsRef.value = resultItems;
}) }).finally(() => {
.finally(() => {
setLoading(false); setLoading(false);
}); });
} }
@ -345,14 +328,14 @@
} }
function handleDelField(paramter) { function handleDelField(paramter) {
const index = formMdel.paramters.findIndex((p) => p.field === paramter.field); const index = formMdel.paramters.findIndex(p => p.field === paramter.field);
formMdel.paramters.splice(index, 1); formMdel.paramters.splice(index, 1);
emits('change', getSearchInput()); emits('change', getSearchInput());
} }
function handleFieldChange(field, record) { function handleFieldChange(field, record) {
const defineParams = unref(defineParamsRef); const defineParams = unref(defineParamsRef);
const defineParam = defineParams.find((dp) => dp.name === field); const defineParam = defineParams.find(dp => dp.name === field);
if (defineParam) { if (defineParam) {
record.field = defineParam.name; record.field = defineParam.name;
record.javaScriptType = defineParam.javaScriptType; record.javaScriptType = defineParam.javaScriptType;
@ -378,7 +361,7 @@
function getSearchInput() { function getSearchInput() {
const searchInput = { const searchInput = {
// //
paramters: formMdel.paramters.filter((p) => p.value !== undefined), paramters: formMdel.paramters.filter(p => p.value !== undefined)
}; };
return searchInput; return searchInput;
} }
@ -390,4 +373,6 @@
defineExpose({ resetFields }); defineExpose({ resetFields });
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped>
</style>

14
apps/vue/src/components/Table/src/components/EditTableHeaderIcon.vue

@ -1,16 +1,16 @@
<template> <template>
<span> <span class="edit-header-cell">
<slot></slot> <slot></slot>
{{ title }} {{ title }}
<FormOutlined /> <FormOutlined />
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import { FormOutlined } from '@ant-design/icons-vue'; import { FormOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'EditTableHeaderIcon', //defineOptions({ name: 'EditTableHeaderIcon' });
components: { FormOutlined },
props: { title: { type: String, default: '' } }, defineProps({
title: { type: String, default: '' },
}); });
</script> </script>

39
apps/vue/src/components/Table/src/components/HeaderCell.vue

@ -1,17 +1,11 @@
<template> <script lang="tsx">
<EditTableHeaderCell v-if="getIsEdit">
{{ getTitle }}
</EditTableHeaderCell>
<span v-else>{{ getTitle }}</span>
<BasicHelp v-if="getHelpMessage" :text="getHelpMessage" :class="`${prefixCls}__help`" />
</template>
<script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { BasicColumn } from '../types/table'; import type { BasicColumn } from '../types/table';
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import BasicHelp from '/@/components/Basic/src/BasicHelp.vue'; import BasicHelp from '/@/components/Basic/src/BasicHelp.vue';
import EditTableHeaderCell from './EditTableHeaderIcon.vue'; import EditTableHeaderCell from './EditTableHeaderIcon.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { ColumnType } from 'ant-design-vue/lib/table/interface';
export default defineComponent({ export default defineComponent({
name: 'TableHeaderCell', name: 'TableHeaderCell',
@ -21,18 +15,37 @@
}, },
props: { props: {
column: { column: {
type: Object as PropType<BasicColumn>, type: Object as PropType<ColumnType<any>>,
default: () => ({}), default: () => ({}),
}, },
}, },
setup(props) { setup(props) {
const { prefixCls } = useDesign('basic-table-header-cell'); const { prefixCls } = useDesign('basic-table-header-cell');
const getIsEdit = computed(() => !!props.column?.edit); const getIsEdit = computed(() => !!(props.column as BasicColumn)?.edit);
const getTitle = computed(() => props.column?.customTitle || props.column?.title); const getTitle = computed(() => {
const getHelpMessage = computed(() => props.column?.helpMessage); const column = props.column as BasicColumn;
if (typeof column.customHeaderRender === 'function') {
return column.customHeaderRender(column);
}
return column?.customTitle || props.column?.title;
});
const getHelpMessage = computed(() => (props.column as BasicColumn)?.helpMessage);
return { prefixCls, getIsEdit, getTitle, getHelpMessage }; return () => {
return (
<div>
{getIsEdit.value ? (
<EditTableHeaderCell>{getTitle.value}</EditTableHeaderCell>
) : (
<span class="default-header-cell">{getTitle.value}</span>
)}
{getHelpMessage.value && (
<BasicHelp text={getHelpMessage.value} class={`${prefixCls}__help`} />
)}
</div>
);
};
}, },
}); });
</script> </script>

30
apps/vue/src/components/Table/src/components/TableAction.vue

@ -2,12 +2,12 @@
<div :class="[prefixCls, getAlign]" @click="onCellClick"> <div :class="[prefixCls, getAlign]" @click="onCellClick">
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`"> <template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)"> <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action"> <PopConfirmButton v-bind="omit(action, 'icon')">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template> <template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton> </PopConfirmButton>
</Tooltip> </Tooltip>
<PopConfirmButton v-else v-bind="action"> <PopConfirmButton v-else v-bind="omit(action, 'icon')">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template> <template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton> </PopConfirmButton>
@ -30,11 +30,11 @@
</Dropdown> </Dropdown>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, PropType, computed, toRaw, unref } from 'vue'; import { PropType, computed, toRaw, unref } from 'vue';
import { MoreOutlined } from '@ant-design/icons-vue'; import { MoreOutlined } from '@ant-design/icons-vue';
import { Divider, Tooltip, TooltipProps } from 'ant-design-vue'; import { Divider, Tooltip, TooltipProps } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import { Icon } from '/@/components/Icon';
import { ActionItem, TableActionType } from '/@/components/Table'; import { ActionItem, TableActionType } from '/@/components/Table';
import { PopConfirmButton } from '/@/components/Button'; import { PopConfirmButton } from '/@/components/Button';
import { Dropdown } from '/@/components/Dropdown'; import { Dropdown } from '/@/components/Dropdown';
@ -44,11 +44,11 @@
import { isBoolean, isFunction, isString } from '/@/utils/is'; import { isBoolean, isFunction, isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { ACTION_COLUMN_FLAG } from '../const'; import { ACTION_COLUMN_FLAG } from '../const';
import { omit } from 'lodash-es';
export default defineComponent({ //defineOptions({ name: 'TableAction' });
name: 'TableAction',
components: { Icon, PopConfirmButton, Divider, Dropdown, MoreOutlined, Tooltip }, const props = defineProps({
props: {
actions: { actions: {
type: Array as PropType<ActionItem[]>, type: Array as PropType<ActionItem[]>,
default: null, default: null,
@ -60,8 +60,8 @@
divider: propTypes.bool.def(true), divider: propTypes.bool.def(true),
outside: propTypes.bool, outside: propTypes.bool,
stopButtonPropagation: propTypes.bool.def(false), stopButtonPropagation: propTypes.bool.def(false),
}, });
setup(props) {
const { prefixCls } = useDesign('basic-table-action'); const { prefixCls } = useDesign('basic-table-action');
let table: Partial<TableActionType> = {}; let table: Partial<TableActionType> = {};
if (!props.outside) { if (!props.outside) {
@ -91,7 +91,7 @@
.map((action) => { .map((action) => {
const { popConfirm } = action; const { popConfirm } = action;
return { return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body, getPopupContainer: () => unref((table as any)?.wrapRef) ?? document.body,
type: 'link', type: 'link',
size: 'small', size: 'small',
...action, ...action,
@ -128,7 +128,7 @@
function getTooltip(data: string | TooltipProps): TooltipProps { function getTooltip(data: string | TooltipProps): TooltipProps {
return { return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body, getPopupContainer: () => unref((table as any)?.wrapRef) ?? document.body,
placement: 'bottom', placement: 'bottom',
...(isString(data) ? { title: data } : data), ...(isString(data) ? { title: data } : data),
}; };
@ -142,10 +142,6 @@
}); });
isInButton && e.stopPropagation(); isInButton && e.stopPropagation();
} }
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-action'; @prefix-cls: ~'@{namespace}-basic-table-action';

68
apps/vue/src/components/Table/src/components/TableFooter.vue

@ -1,60 +1,61 @@
<template> <template>
<Table <Table
v-if="summaryFunc || summaryData" v-if="!!props.summaryFunc || props.summaryData"
:showHeader="false" :showHeader="false"
:bordered="false" :bordered="false"
:pagination="false" :pagination="false"
:dataSource="getDataSource" :dataSource="getDataSource"
:rowKey="(r) => r[rowKey]" :rowKey="props.rowKey"
:columns="getColumns" :columns="getColumns"
tableLayout="fixed" tableLayout="fixed"
:scroll="scroll" :scroll="props.scroll"
/> />
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue'; import { unref, computed, toRaw } from 'vue';
import { defineComponent, unref, computed, toRaw } from 'vue';
import { Table } from 'ant-design-vue'; import { Table } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import type { BasicColumn } from '../types/table'; import type { BasicColumn, BasicTableProps } from '../types/table';
import { INDEX_COLUMN_FLAG } from '../const'; import { INDEX_COLUMN_FLAG } from '../const';
import { propTypes } from '/@/utils/propTypes';
import { useTableContext } from '../hooks/useTableContext'; import { useTableContext } from '../hooks/useTableContext';
import { ColumnType } from 'ant-design-vue/es/table/interface';
import { parseRowKey } from '../helper';
//defineOptions({ name: 'BasicTableFooter' });
const props = withDefaults(
defineProps<{
summaryFunc?: Fn | null;
summaryData?: Recordable[] | null;
scroll?: BasicTableProps['scroll'];
rowKey?: BasicTableProps['rowKey'];
}>(),
{
summaryFunc: null,
summaryData: null,
rowKey: '',
},
);
const SUMMARY_ROW_KEY = '_row'; const SUMMARY_ROW_KEY = '_row';
const SUMMARY_INDEX_KEY = '_index'; const SUMMARY_INDEX_KEY = '_index';
export default defineComponent({
name: 'BasicTableFooter',
components: { Table },
props: {
summaryFunc: {
type: Function as PropType<Fn>,
},
summaryData: {
type: Array as PropType<Recordable[]>,
},
scroll: {
type: Object as PropType<Recordable>,
},
rowKey: propTypes.string.def('key'),
},
setup(props) {
const table = useTableContext(); const table = useTableContext();
const getDataSource = computed((): Recordable[] => { const getDataSource = computed((): Recordable[] => {
const { summaryFunc, summaryData } = props; if (props.summaryData?.length) {
if (summaryData?.length) { props.summaryData.forEach((item, i) => {
summaryData.forEach((item, i) => (item[props.rowKey] = `${i}`)); item[parseRowKey(props.rowKey, item)] = `${i}`;
return summaryData; });
return props.summaryData;
} }
if (!isFunction(summaryFunc)) { if (!isFunction(props.summaryFunc)) {
return []; return [];
} }
let dataSource = toRaw(unref(table.getDataSource())); let dataSource = toRaw(unref(table.getDataSource()));
dataSource = summaryFunc(dataSource); dataSource = props.summaryFunc(dataSource);
dataSource.forEach((item, i) => { dataSource.forEach((item, i) => {
item[props.rowKey] = `${i}`; item[parseRowKey(props.rowKey, item)] = `${i}`;
}); });
return dataSource; return dataSource;
}); });
@ -86,9 +87,6 @@
customRender: ({ record }) => record[SUMMARY_ROW_KEY], customRender: ({ record }) => record[SUMMARY_ROW_KEY],
}); });
} }
return columns; return columns as unknown as ColumnType[];
});
return { getColumns, getDataSource };
},
}); });
</script> </script>

54
apps/vue/src/components/Table/src/components/TableHeader.vue

@ -3,6 +3,9 @@
<div v-if="$slots.headerTop" style="margin: 5px"> <div v-if="$slots.headerTop" style="margin: 5px">
<slot name="headerTop"></slot> <slot name="headerTop"></slot>
</div> </div>
<div v-if="showSelectionBar" style="margin: 5px">
<TableSelectionBar :clearSelectedRowKeys="props.clearSelectedRowKeys!" :count="props.count" />
</div>
<div class="flex items-center"> <div class="flex items-center">
<slot name="tableTitle" v-if="$slots.tableTitle"></slot> <slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle <TableTitle
@ -13,39 +16,29 @@
<div :class="`${prefixCls}__toolbar`"> <div :class="`${prefixCls}__toolbar`">
<slot name="toolbar"></slot> <slot name="toolbar"></slot>
<Divider type="vertical" v-if="$slots.toolbar && showTableSetting" /> <Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
<TableSetting <TableSettingComponent
:setting="tableSetting" :setting="tableSetting"
v-if="showTableSetting" v-if="showTableSetting"
@columns-change="handleColumnChange" @columns-change="handleColumnChange"
/> />
</div> </div>
</div> </div>
<div v-if="showTableAlert" :class="`${prefixCls}__alert`">
<TableAlert :message="tableAlertMessage" @de-select="$emit('de-select')" />
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { TableSetting, ColumnChangeParam } from '../types/table'; import type { TableSetting, ColumnChangeParam, TableActionType } from '../types/table';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { Divider } from 'ant-design-vue'; import { Divider } from 'ant-design-vue';
import TableSettingComponent from './settings/index.vue'; import TableSettingComponent from './settings/index.vue';
import TableTitle from './TableTitle.vue'; import TableTitle from './TableTitle.vue';
import TableAlert from './TableAlert.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import TableSelectionBar from '../components/TableSelectionBar.vue';
export default defineComponent({ //defineOptions({ name: 'BasicTableHeader' });
name: 'BasicTableHeader',
components: { const props = defineProps({
Divider,
TableAlert,
TableTitle,
TableSetting: TableSettingComponent,
},
props: {
title: { title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>, type: [Function, String] as PropType<string | ((data) => string)>,
}, },
tableSetting: { tableSetting: {
type: Object as PropType<TableSetting>, type: Object as PropType<TableSetting>,
@ -57,32 +50,33 @@
type: [String, Array] as PropType<string | string[]>, type: [String, Array] as PropType<string | string[]>,
default: '', default: '',
}, },
showTableAlert: { //
type: Boolean, clearSelectedRowKeys: {
default: false, type: Function as PropType<TableActionType['clearSelectedRowKeys']>,
}, },
tableAlertMessage: { count: {
type: String, type: Number,
default: '', default: 0,
}, },
showSelectionBar: {
type: Boolean,
default: false,
}, },
emits: ['columns-change', 'de-select'], });
setup(_, { emit }) {
const emit = defineEmits(['columns-change']);
const { prefixCls } = useDesign('basic-table-header'); const { prefixCls } = useDesign('basic-table-header');
function handleColumnChange(data: ColumnChangeParam[]) { function handleColumnChange(data: ColumnChangeParam[]) {
emit('columns-change', data); emit('columns-change', data);
} }
return { prefixCls, handleColumnChange };
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-header'; @prefix-cls: ~'@{namespace}-basic-table-header';
.@{prefix-cls} { .@{prefix-cls} {
&__toolbar { &__toolbar {
flex: 1;
display: flex; display: flex;
flex: 1;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;

35
apps/vue/src/components/Table/src/components/TableImg.vue

@ -7,41 +7,42 @@
> >
<Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow"> <Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow">
<div class="img-div"> <div class="img-div">
<PreviewGroup> <Image.PreviewGroup>
<template v-for="(img, index) in imgList" :key="img"> <template v-for="(img, index) in imgList" :key="img">
<AImage <Image
:width="size" :width="size"
:style="{ :style="{
display: index === 0 ? '' : 'none !important', display: index === 0 ? '' : 'none !important',
}" }"
:src="srcPrefix + img" :src="srcPrefix + img"
:fallback="fallback"
/> />
</template> </template>
</PreviewGroup> </Image.PreviewGroup>
</div> </div>
</Badge> </Badge>
<PreviewGroup v-else> <Image.PreviewGroup v-else>
<template v-for="(img, index) in imgList" :key="img"> <template v-for="(img, index) in imgList" :key="img">
<AImage <Image
:width="size" :width="size"
:style="{ marginLeft: index === 0 ? 0 : margin + 'px' }" :style="{ marginLeft: index === 0 ? 0 : margin + 'px' }"
:src="srcPrefix + img" :src="srcPrefix + img"
:fallback="fallback"
/> />
</template> </template>
</PreviewGroup> </Image.PreviewGroup>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import { defineComponent, computed } from 'vue'; import { computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { Image, Badge } from 'ant-design-vue'; import { Image, Badge } from 'ant-design-vue';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
export default defineComponent({ //defineOptions({ name: 'TableImage' });
name: 'TableImage',
components: { AImage: Image, PreviewGroup: Image.PreviewGroup, Badge }, const props = defineProps({
props: {
imgList: propTypes.arrayOf(propTypes.string), imgList: propTypes.arrayOf(propTypes.string),
size: propTypes.number.def(40), size: propTypes.number.def(40),
// //
@ -56,8 +57,7 @@
fallback: propTypes.string.def( fallback: propTypes.string.def(
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==',
), ),
}, });
setup(props) {
const getWrapStyle = computed((): CSSProperties => { const getWrapStyle = computed((): CSSProperties => {
const { size } = props; const { size } = props;
const s = `${size}px`; const s = `${size}px`;
@ -65,9 +65,6 @@
}); });
const { prefixCls } = useDesign('basic-table-img'); const { prefixCls } = useDesign('basic-table-img');
return { prefixCls, getWrapStyle };
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-img'; @prefix-cls: ~'@{namespace}-basic-table-img';
@ -84,6 +81,10 @@
.img-div { .img-div {
display: inline-grid; display: inline-grid;
> .ant-image:nth-of-type(n + 2) {
display: none;
}
} }
} }
</style> </style>

54
apps/vue/src/components/Table/src/components/TableSelectionBar.vue

@ -0,0 +1,54 @@
<template>
<a-alert type="info" showIcon :class="[prefixCls]">
<template #message>
<span v-if="props.count > 0">
{{ t('component.table.selectionBarTips', { count: props.count }) }}
</span>
<span v-else>
{{ t('component.table.selectionBarEmpty') }}
</span>
<a-button type="link" @click="clearSelectedRowKeys" size="small" v-show="props.count > 0">
{{ t('component.table.selectionBarClear') }}
</a-button>
</template>
</a-alert>
</template>
<script lang="ts" setup>
import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
import type { TableActionType } from '../types/table';
import { Alert as AAlert } from 'ant-design-vue';
const { t } = useI18n();
const { prefixCls } = useDesign('table-select-bar');
//defineOptions({ name: 'TableSelectBar' });
const props = withDefaults(
defineProps<{
count?: number;
//
clearSelectedRowKeys: TableActionType['clearSelectedRowKeys'];
}>(),
{
count: () => 0,
},
);
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-table-select-bar';
.@{prefix-cls} {
flex-grow: 1;
padding: 2px 8px;
:deep(.ant-btn-link) {
height: 20px;
line-height: 20px;
}
}
</style>

27
apps/vue/src/components/Table/src/components/TableTitle.vue

@ -3,27 +3,26 @@
{{ getTitle }} {{ getTitle }}
</BasicTitle> </BasicTitle>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, PropType } from 'vue'; import { computed, PropType } from 'vue';
import { BasicTitle } from '/@/components/Basic/index'; import { BasicTitle } from '/@/components/Basic';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
export default defineComponent({ //defineOptions({ name: 'BasicTableTitle' });
name: 'BasicTableTitle',
components: { BasicTitle }, const props = defineProps({
props: {
title: { title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>, type: [Function, String] as PropType<string | ((data) => string)>,
}, },
getSelectRows: { getSelectRows: {
type: Function as PropType<() => Recordable[]>, type: Function as PropType<() => any[]>,
}, },
helpMessage: { helpMessage: {
type: [String, Array] as PropType<string | string[]>, type: [String, Array] as PropType<string | string[]>,
}, },
}, });
setup(props) {
const { prefixCls } = useDesign('basic-table-title'); const { prefixCls } = useDesign('basic-table-title');
const getTitle = computed(() => { const getTitle = computed(() => {
@ -37,17 +36,13 @@
} }
return tit; return tit;
}); });
return { getTitle, prefixCls };
},
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-title'; @prefix-cls: ~'@{namespace}-basic-table-title';
.@{prefix-cls} { .@{prefix-cls} {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
} }
</style> </style>

94
apps/vue/src/components/Table/src/components/editable/EditableCell.vue

@ -2,7 +2,6 @@
import type { CSSProperties, PropType } from 'vue'; import type { CSSProperties, PropType } from 'vue';
import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue'; import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
import type { BasicColumn } from '../../types/table'; import type { BasicColumn } from '../../types/table';
import type { EditRecordRow } from './index';
import { CheckOutlined, CloseOutlined, FormOutlined } from '@ant-design/icons-vue'; import { CheckOutlined, CloseOutlined, FormOutlined } from '@ant-design/icons-vue';
import { CellComponent } from './CellComponent'; import { CellComponent } from './CellComponent';
@ -14,9 +13,10 @@
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is'; import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
import { createPlaceholderMessage } from './helper'; import { createPlaceholderMessage } from './helper';
import { omit, pick, set } from 'lodash-es'; import { pick, set } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper'; import { treeToList } from '/@/utils/helper/treeHelper';
import { Spin } from 'ant-design-vue'; import { Spin } from 'ant-design-vue';
import { parseRowKey } from '../../helper';
export default defineComponent({ export default defineComponent({
name: 'EditableCell', name: 'EditableCell',
@ -26,11 +26,13 @@
}, },
props: { props: {
value: { value: {
type: [String, Number, Boolean, Object] as PropType<string | number | boolean | Recordable>, type: [String, Number, Boolean, Object] as PropType<
string | number | boolean | Record<string, any>
>,
default: '', default: '',
}, },
record: { record: {
type: Object as PropType<EditRecordRow>, type: Object as any,
}, },
column: { column: {
type: Object as PropType<BasicColumn>, type: Object as PropType<BasicColumn>,
@ -44,7 +46,7 @@
const elRef = ref(); const elRef = ref();
const ruleVisible = ref(false); const ruleVisible = ref(false);
const ruleMessage = ref(''); const ruleMessage = ref('');
const optionsRef = ref<LabelValueOptions>([]); const optionsRef = ref([]);
const currentValueRef = ref<any>(props.value); const currentValueRef = ref<any>(props.value);
const defaultValueRef = ref<any>(props.value); const defaultValueRef = ref<any>(props.value);
const spinning = ref<boolean>(false); const spinning = ref<boolean>(false);
@ -65,13 +67,23 @@
const getComponentProps = computed(() => { const getComponentProps = computed(() => {
const isCheckValue = unref(getIsCheckComp); const isCheckValue = unref(getIsCheckComp);
let compProps = props.column?.editComponentProps ?? ({} as any);
const { checkedValue, unCheckedValue } = compProps;
const valueField = isCheckValue ? 'checked' : 'value'; const valueField = isCheckValue ? 'checked' : 'value';
const val = unref(currentValueRef); const val = unref(currentValueRef);
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; let value = val;
if (isCheckValue) {
if (typeof checkedValue !== 'undefined') {
value = val === checkedValue ? checkedValue : unCheckedValue;
} else if (typeof unCheckedValue !== 'undefined') {
value = val === unCheckedValue ? unCheckedValue : checkedValue;
} else {
value = isNumber(val) || isBoolean(val) ? val : !!val;
}
}
let compProps = props.column?.editComponentProps ?? {};
const { record, column, index } = props; const { record, column, index } = props;
if (isFunction(compProps)) { if (isFunction(compProps)) {
@ -83,7 +95,7 @@
delete compProps.onChange; delete compProps.onChange;
const component = unref(getComponent); const component = unref(getComponent);
const apiSelectProps: Recordable = {}; const apiSelectProps: Record<string, any> = {};
if (component === 'ApiSelect') { if (component === 'ApiSelect') {
apiSelectProps.cache = true; apiSelectProps.cache = true;
} }
@ -93,8 +105,9 @@
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body, getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
placeholder: createPlaceholderMessage(unref(getComponent)), placeholder: createPlaceholderMessage(unref(getComponent)),
...apiSelectProps, ...apiSelectProps,
...omit(compProps, 'onChange'), ...compProps,
[valueField]: value, [valueField]: value,
disabled: unref(getDisable),
} as any; } as any;
}); });
function upEditDynamicDisabled(record, column, value) { function upEditDynamicDisabled(record, column, value) {
@ -112,7 +125,7 @@
} }
if (isFunction(editDynamicDisabled)) { if (isFunction(editDynamicDisabled)) {
const { record } = props; const { record } = props;
disabled = editDynamicDisabled({ record }); disabled = editDynamicDisabled({ record, currentValue: currentValueRef.value });
} }
return disabled; return disabled;
}); });
@ -130,8 +143,7 @@
return value; return value;
} }
const options: LabelValueOptions = const options = unref(getComponentProps)?.options ?? (unref(optionsRef) || []);
unref(getComponentProps)?.options ?? (unref(optionsRef) || []);
const option = options.find((item) => `${item.value}` === `${value}`); const option = options.find((item) => `${item.value}` === `${value}`);
return option?.label ?? value; return option?.label ?? value;
@ -157,7 +169,7 @@
}); });
watchEffect(() => { watchEffect(() => {
//defaultValueRef.value = props.value; // defaultValueRef.value = props.value;
currentValueRef.value = props.value; currentValueRef.value = props.value;
}); });
@ -168,8 +180,9 @@
} }
}); });
function handleEdit() { function handleEdit(e) {
if (unref(getRowEditable) || unref(props.column?.editRow)) return; e.stopPropagation();
if (unref(getRowEditable) || unref(props.column?.editRow) || unref(getDisable)) return;
ruleMessage.value = ''; ruleMessage.value = '';
isEdit.value = true; isEdit.value = true;
nextTick(() => { nextTick(() => {
@ -183,11 +196,11 @@
if (!e) { if (!e) {
currentValueRef.value = e; currentValueRef.value = e;
} else if (component === 'Checkbox') { } else if (component === 'Checkbox') {
currentValueRef.value = (e as ChangeEvent).target.checked; currentValueRef.value = e.target.checked;
} else if (component === 'Switch') { } else if (component === 'Switch') {
currentValueRef.value = e; currentValueRef.value = e;
} else if (e?.target && Reflect.has(e.target, 'value')) { } else if (e?.target && Reflect.has(e.target, 'value')) {
currentValueRef.value = (e as ChangeEvent).target.value; currentValueRef.value = e.target.value;
} else if (isString(e) || isBoolean(e) || isNumber(e) || isArray(e)) { } else if (isString(e) || isBoolean(e) || isNumber(e) || isArray(e)) {
currentValueRef.value = e; currentValueRef.value = e;
} }
@ -215,8 +228,8 @@
return false; return false;
} }
if (isFunction(editRule)) { if (isFunction(editRule)) {
const res = await editRule(currentValue, record as Recordable); const res = await editRule(currentValue, record);
if (!!res) { if (res) {
ruleMessage.value = res; ruleMessage.value = res;
ruleVisible.value = true; ruleVisible.value = true;
return false; return false;
@ -247,17 +260,20 @@
if (!record.editable) { if (!record.editable) {
const { getBindValues } = table; const { getBindValues } = table;
const { beforeEditSubmit, columns } = unref(getBindValues); const { beforeEditSubmit, columns, rowKey } = unref(getBindValues);
const rowKeyParsed = parseRowKey(rowKey, record);
if (beforeEditSubmit && isFunction(beforeEditSubmit)) { if (beforeEditSubmit && isFunction(beforeEditSubmit)) {
spinning.value = true; spinning.value = true;
const keys: string[] = columns const keys: string[] = columns
.map((_column) => _column.dataIndex) .map((_column) => _column.dataIndex)
.filter((field) => !!field) as string[]; .filter((field) => !!field) as string[];
let result: any = true; let result: any = true;
try { try {
result = await beforeEditSubmit({ result = await beforeEditSubmit({
record: pick(record, keys), record: pick(record, [rowKeyParsed, ...keys]),
index, index,
key: dataKey as string, key: dataKey as string,
value, value,
@ -272,8 +288,8 @@
} }
} }
} }
set(record, dataKey, value); set(record, dataKey, value);
defaultValueRef.value = value;
//const record = await table.updateTableData(index, dataKey, value); //const record = await table.updateTableData(index, dataKey, value);
needEmit && table.emit?.('edit-end', { record, index, key: dataKey, value }); needEmit && table.emit?.('edit-end', { record, index, key: dataKey, value });
isEdit.value = false; isEdit.value = false;
@ -315,25 +331,25 @@
} }
// only ApiSelect or TreeSelect // only ApiSelect or TreeSelect
function handleOptionsChange(options: LabelValueOptions) { function handleOptionsChange(options) {
const { replaceFields } = unref(getComponentProps); const { replaceFields } = unref(getComponentProps);
const component = unref(getComponent); const component = unref(getComponent);
if (component === 'ApiTreeSelect') { if (component === 'ApiTreeSelect') {
const { title = 'title', value = 'value', children = 'children' } = replaceFields || {}; const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
let listOptions: Recordable[] = treeToList(options, { children }); let listOptions = treeToList(options, { children });
listOptions = listOptions.map((item) => { listOptions = listOptions.map((item) => {
return { return {
label: item[title], label: item[title],
value: item[value], value: item[value],
}; };
}); });
optionsRef.value = listOptions as LabelValueOptions; optionsRef.value = listOptions;
} else { } else {
optionsRef.value = options; optionsRef.value = options;
} }
} }
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) { function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle) {
if (props.record) { if (props.record) {
/* eslint-disable */ /* eslint-disable */
isArray(props.record[cbs]) isArray(props.record[cbs])
@ -378,7 +394,6 @@
elRef, elRef,
getComponent, getComponent,
getRule, getRule,
getDisable,
onClickOutside, onClickOutside,
ruleMessage, ruleMessage,
getRuleVisible, getRuleVisible,
@ -391,6 +406,7 @@
handleEnter, handleEnter,
handleSubmitClick, handleSubmitClick,
spinning, spinning,
getDisable,
}; };
}, },
render() { render() {
@ -408,14 +424,21 @@
record: this.record as Recordable, record: this.record as Recordable,
column: this.column, column: this.column,
index: this.index, index: this.index,
currentValue: this.currentValueRef,
}) })
: this.getValues ?? '\u00A0'} : this.getValues ?? '\u00A0'}
</div> </div>
{!this.column.editRow && <FormOutlined class={`${this.prefixCls}__normal-icon`} />} {!this.column.editRow && !this.getDisable && (
<FormOutlined class={`${this.prefixCls}__normal-icon`} />
)}
</div> </div>
{this.isEdit && ( {this.isEdit && (
<Spin spinning={this.spinning}> <Spin spinning={this.spinning} onClick={(e) => e.stopPropagation()}>
<div class={`${this.prefixCls}__wrapper`} v-click-outside={this.onClickOutside}> <div
class={`${this.prefixCls}__wrapper`}
v-click-outside={this.onClickOutside}
onClick={(e) => e.stopPropagation()}
>
<CellComponent <CellComponent
{...this.getComponentProps} {...this.getComponentProps}
component={this.getComponent} component={this.getComponent}
@ -476,13 +499,14 @@
.edit-cell-rule-popover { .edit-cell-rule-popover {
.ant-popover-inner-content { .ant-popover-inner-content {
padding: 4px 8px; padding: 4px 8px;
color: @error-color;
// border: 1px solid @error-color; // border: 1px solid @error-color;
border-radius: 2px; border-radius: 2px;
color: @error-color;
} }
} }
.@{prefix-cls} { .@{prefix-cls} {
position: relative; position: relative;
min-height: 24px; //hover
&__wrapper { &__wrapper {
display: flex; display: flex;
@ -506,20 +530,20 @@
.ellipsis-cell { .ellipsis-cell {
.cell-content { .cell-content {
overflow-wrap: break-word;
word-break: break-word;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
word-break: break-word;
white-space: nowrap;
overflow-wrap: break-word;
} }
} }
&__normal { &__normal {
&-icon { &-icon {
display: none;
position: absolute; position: absolute;
top: 4px; top: 4px;
right: 0; right: 0;
display: none;
width: 20px; width: 20px;
cursor: pointer; cursor: pointer;
} }

6
apps/vue/src/components/Table/src/components/editable/index.ts

@ -1,6 +1,6 @@
import type { BasicColumn } from '/@/components/Table/src/types/table'; import type { BasicColumn } from '/@/components/Table/src/types/table';
import { h, Ref } from 'vue'; import { h, Ref, toRaw } from 'vue';
import EditableCell from './EditableCell.vue'; import EditableCell from './EditableCell.vue';
import { isArray } from '/@/utils/is'; import { isArray } from '/@/utils/is';
@ -13,7 +13,7 @@ interface Params {
export function renderEditCell(column: BasicColumn) { export function renderEditCell(column: BasicColumn) {
return ({ text: value, record, index }: Params) => { return ({ text: value, record, index }: Params) => {
record.onValid = async () => { toRaw(record).onValid = async () => {
if (isArray(record?.validCbs)) { if (isArray(record?.validCbs)) {
const validFns = (record?.validCbs || []).map((fn) => fn()); const validFns = (record?.validCbs || []).map((fn) => fn());
const res = await Promise.all(validFns); const res = await Promise.all(validFns);
@ -23,7 +23,7 @@ export function renderEditCell(column: BasicColumn) {
} }
}; };
record.onEdit = async (edit: boolean, submit = false) => { toRaw(record).onEdit = async (edit: boolean, submit = false) => {
if (!submit) { if (!submit) {
record.editable = edit; record.editable = edit;
} }

721
apps/vue/src/components/Table/src/components/settings/ColumnSetting.vue

@ -6,37 +6,38 @@
<Popover <Popover
placement="bottomLeft" placement="bottomLeft"
trigger="click" trigger="click"
@visible-change="handleVisibleChange" @open-change="onOpenChange"
:overlayClassName="`${prefixCls}__cloumn-list`" :overlayClassName="`${prefixCls}__column-list`"
:getPopupContainer="getPopupContainer" :getPopupContainer="getPopupContainer"
> >
<template #title> <template #title>
<div :class="`${prefixCls}__popover-title`"> <div :class="`${prefixCls}__popover-title`">
<Checkbox <Checkbox
:indeterminate="indeterminate" :indeterminate="indeterminate"
v-model:checked="state.checkAll" v-model:checked="isColumnAllSelected"
@change="onCheckAllChange" @change="onColumnAllSelectChange"
> >
{{ t('component.table.settingColumnShow') }} {{ t('component.table.settingColumnShow') }}
</Checkbox> </Checkbox>
<Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange"> <Checkbox v-model:checked="isIndexColumnShow" @change="onIndexColumnShowChange">
{{ t('component.table.settingIndexColumnShow') }} {{ t('component.table.settingIndexColumnShow') }}
</Checkbox> </Checkbox>
<!-- 设置了 rowSelection 才出现 -->
<Checkbox <Checkbox
v-model:checked="checkSelect" v-model:checked="isRowSelectionShow"
@change="handleSelectCheckChange" @change="onRowSelectionShowChange"
:disabled="!defaultRowSelection" v-if="defaultIsRowSelectionShow"
> >
{{ t('component.table.settingSelectColumnShow') }} {{ t('component.table.settingSelectColumnShow') }}
</Checkbox> </Checkbox>
<Checkbox v-model:checked="checkDrag" @change="handleDragChange"> <Checkbox v-model:checked="isAllowResizeColumn" @change="onColumnAllowResizeChange">
{{ t('component.table.settingDragColumnShow') }} {{ t('component.table.settingDragColumnShow') }}
</Checkbox> </Checkbox>
<a-button size="small" type="link" @click="reset">
<a-button size="small" type="link" @click="onReset">
{{ t('common.resetText') }} {{ t('common.resetText') }}
</a-button> </a-button>
</div> </div>
@ -44,12 +45,12 @@
<template #content> <template #content>
<ScrollContainer> <ScrollContainer>
<CheckboxGroup v-model:value="state.checkedList" @change="onChange" ref="columnListRef"> <Checkbox.Group v-model:value="columnCheckedOptions" ref="columnOptionsRef">
<template v-for="item in plainOptions" :key="item.value"> <template v-for="opt in columnOptions" :key="opt.value">
<div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)"> <div :class="`${prefixCls}__check-item`" :data-no="opt.value">
<DragOutlined class="table-column-drag-icon" /> <DragOutlined class="table-column-drag-icon" />
<Checkbox :value="item.value"> <Checkbox :value="opt.value">
{{ item.label }} {{ opt.label }}
</Checkbox> </Checkbox>
<Tooltip <Tooltip
@ -65,11 +66,11 @@
:class="[ :class="[
`${prefixCls}__fixed-left`, `${prefixCls}__fixed-left`,
{ {
active: item.fixed === 'left', active: opt.fixed === 'left',
disabled: !state.checkedList.includes(item.value), disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true,
}, },
]" ]"
@click="handleColumnFixed(item, 'left')" @click="onColumnFixedChange(opt, 'left')"
/> />
</Tooltip> </Tooltip>
<Divider type="vertical" /> <Divider type="vertical" />
@ -86,16 +87,16 @@
:class="[ :class="[
`${prefixCls}__fixed-right`, `${prefixCls}__fixed-right`,
{ {
active: item.fixed === 'right', active: opt.fixed === 'right',
disabled: !state.checkedList.includes(item.value), disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true,
}, },
]" ]"
@click="handleColumnFixed(item, 'right')" @click="onColumnFixedChange(opt, 'right')"
/> />
</Tooltip> </Tooltip>
</div> </div>
</template> </template>
</CheckboxGroup> </Checkbox.Group>
</ScrollContainer> </ScrollContainer>
</template> </template>
<SettingOutlined /> <SettingOutlined />
@ -103,8 +104,8 @@
</Tooltip> </Tooltip>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { BasicColumn, ColumnChangeParam } from '../../types/table'; import type { BasicColumn, ColumnOptionsType, ColumnChangeParam } from '../../types/table';
import { useAttrs, ref, reactive, watchEffect, nextTick, unref, computed } from 'vue'; import { ref, nextTick, unref, computed, useAttrs, watch, onMounted } from 'vue';
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'; import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'; import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue'; import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
@ -113,269 +114,526 @@
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
// import { useSortable } from '/@/hooks/web/useSortable'; import { isFunction, isNil, isNumber } from '/@/utils/is';
import { isFunction, isNullAndUnDef, isNumber } from '/@/utils/is';
import { getPopupContainer as getParentContainer } from '/@/utils'; import { getPopupContainer as getParentContainer } from '/@/utils';
import { cloneDeep, omit } from 'lodash-es'; import { cloneDeep, omit } from 'lodash-es';
import Sortablejs from 'sortablejs'; import Sortablejs from 'sortablejs';
import type Sortable from 'sortablejs'; import { INDEX_COLUMN_FLAG } from '/@/components/Table/src/const';
interface State {
checkAll: boolean;
isInit?: boolean;
checkedList: string[];
defaultCheckList: string[];
}
interface Options {
label: string;
value: string;
fixed?: boolean | 'left' | 'right';
}
const CheckboxGroup = Checkbox.Group;
const emits = defineEmits(['columns-change']);
const { t } = useI18n();
const attrs = useAttrs();
const table = useTableContext();
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys'); //
let inited = false; import { useTableSettingStore } from '/@/store/modules/tableSetting';
import { useRoute } from 'vue-router';
import { TableRowSelection } from '/@/components/Table/src/types/table';
const cachePlainOptions = ref<Options[]>([]); const tableSettingStore = useTableSettingStore();
const plainOptions = ref<Options[] | any>([]);
const plainSortOptions = ref<Options[]>([]); // defineOptions({ name: 'ColumnSetting' });
const emit = defineEmits(['columns-change']);
const columnListRef = ref<ComponentRef>(null); const route = useRoute();
const state = reactive<State>({
checkAll: true,
checkedList: [],
defaultCheckList: [],
});
const checkIndex = ref(false);
const checkSelect = ref(false);
const checkDrag = ref(false);
const { t } = useI18n();
const { prefixCls } = useDesign('basic-column-setting'); const { prefixCls } = useDesign('basic-column-setting');
const getValues = computed(() => { const attrs = useAttrs();
return unref(table?.getBindValues) || {}; const table = useTableContext();
});
watchEffect(() => {
setTimeout(() => {
const columns = table.getColumns();
if (columns.length && !state.isInit) {
init();
}
}, 10);
});
watchEffect(() => { const props = withDefaults(
const values = unref(getValues); defineProps<{
checkIndex.value = !!values.showIndexColumn; /**
checkSelect.value = !!values.rowSelection; * 是否缓存列的设置
}); */
cache?: boolean;
}>(),
{
cache: () => false,
},
);
function getColumns() { const getPopupContainer = () => {
const ret: Options[] = []; return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => { };
ret.push({
label: (item.title as string) || (item.customTitle as string), //
value: (item.dataIndex || item.title) as string, let isRestored = false;
...item, let isInnerChange = false;
//
const columnOptions = ref<ColumnOptionsType[]>([]);
const columnOptionsRef = ref(null);
//
const columnCheckedOptions = ref<string[]>([]);
//
watch(columnCheckedOptions, () => {
//
if (isRestored) {
//
columnOptions.value
.filter((o) => columnCheckedOptions.value.includes(o.value))
.forEach((o) => {
o.column.defaultHidden = false;
}); });
//
columnOptions.value
.filter((o) => !columnCheckedOptions.value.includes(o.value))
.forEach((o) => {
o.column.defaultHidden = true;
o.fixed = undefined;
}); });
return ret; //
} isColumnAllSelectedUpdate();
function init() {
const columns = getColumns();
const checkList = table //
.getColumns({ ignoreAction: true, ignoreIndex: true }) tableColumnsUpdate();
.map((item) => { //
if (item.defaultHidden) { props.cache && columnOptionsSave();
return '';
}
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
if (!plainOptions.value.length) {
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
state.defaultCheckList = checkList;
} else {
// const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed')
// ) as BasicColumn[];
unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
if (findItem) {
item.fixed = findItem.fixed;
} }
}); });
}
state.isInit = true;
state.checkedList = checkList;
}
// checkAll change //
function onCheckAllChange(e: CheckboxChangeEvent) { const isColumnAllSelected = ref<boolean>(false);
const checkList = plainOptions.value.map((item) => item.value); const onColumnAllSelectChange = () => {
if (e.target.checked) { if (columnCheckedOptions.value.length < columnOptions.value.length) {
state.checkedList = checkList; columnCheckedOptions.value = columnOptions.value.map((o) => o.value);
setColumns(checkList);
} else { } else {
state.checkedList = []; columnCheckedOptions.value = [];
setColumns([]);
}
} }
};
//
const indeterminate = computed(() => { const indeterminate = computed(() => {
const len = plainOptions.value.length; return (
let checkedLen = state.checkedList.length; columnCheckedOptions.value.length > 0 &&
unref(checkIndex) && checkedLen--; columnCheckedOptions.value.length < columnOptions.value.length
return checkedLen > 0 && checkedLen < len; );
}); });
// Trigger when check/uncheck a column //
function onChange(checkedList: string[]) { const isIndexColumnShow = ref<boolean>(false);
const len = plainSortOptions.value.length; //
state.checkAll = checkedList.length === len; const onIndexColumnShowChange = (e: CheckboxChangeEvent) => {
const sortList = unref(plainSortOptions).map((item) => item.value); // showIndexColumn
checkedList.sort((prev, next) => { showIndexColumnUpdate(e.target.checked);
return sortList.indexOf(prev) - sortList.indexOf(next); // showIndexColumn
props.cache &&
typeof route.name === 'string' &&
tableSettingStore.setShowIndexColumn(route.name, e.target.checked);
};
//
const isRowSelectionShow = ref<boolean>(false);
//
const onRowSelectionShowChange = (e: CheckboxChangeEvent) => {
// showRowSelection
showRowSelectionUpdate(e.target.checked);
// showRowSelection
props.cache &&
typeof route.name === 'string' &&
tableSettingStore.setShowRowSelection(route.name, e.target.checked);
};
//
const isAllowResizeColumn = ref<boolean>(false);
//
const onColumnAllowResizeChange = (e: CheckboxChangeEvent) => {
const columns = getTableColumns();
columns.forEach((col) => {
if (isNumber(col.width)) {
col.resizable = e.target.checked;
}
}); });
setColumns(checkedList); props.cache &&
} typeof route.name === 'string' &&
tableSettingStore.setAllowResizeColumn(route.name, e.target.checked);
let sortable: Sortable; }
let sortableOrder: string[] = [];
// reset columns //
function reset() { const columnOptionsSave = () => {
state.checkedList = [...state.defaultCheckList]; if (typeof route.name === 'string') {
state.checkAll = true; // name keykey
plainOptions.value = unref(cachePlainOptions); tableSettingStore.setColumns(route.name, columnOptions.value);
plainSortOptions.value = unref(cachePlainOptions); }
setColumns(table.getCacheColumns()); };
sortable.sort(sortableOrder);
} //
const onReset = () => {
// Open the pop-up window for drag and drop initialization //
function handleVisibleChange() { isIndexColumnShow.value = defaultIsIndexColumnShow;
if (inited) return; //
nextTick(() => { onIndexColumnShowChange({
const columnListEl = unref(columnListRef); target: { checked: defaultIsIndexColumnShow },
if (!columnListEl) return; } as CheckboxChangeEvent);
const el = columnListEl.$el as any; //
if (!el) return; isRowSelectionShow.value = defaultIsRowSelectionShow;
// Drag and drop sort //
sortable = Sortablejs.create(unref(el), { onRowSelectionShowChange({
target: { checked: defaultIsRowSelectionShow },
} as CheckboxChangeEvent);
//
columnOptions.value = cloneDeep(defaultColumnOptions);
//
formUpdate();
};
// fixed
const onColumnFixedChange = (opt: ColumnOptionsType, type: 'left' | 'right') => {
if (type === 'left') {
if (!opt.fixed || opt.fixed === 'right') {
opt.fixed = 'left';
} else {
opt.fixed = undefined;
}
} else if (type === 'right') {
if (!opt.fixed || opt.fixed === 'left') {
opt.fixed = 'right';
} else {
opt.fixed = undefined;
}
}
//
tableColumnsUpdate();
//
props.cache && columnOptionsSave();
};
// 沿
const sortableFix = async () => {
// Sortablejsbugel appendchildNodechildNode
//
if (columnOptionsRef.value) {
const el = (columnOptionsRef.value as InstanceType<typeof Checkbox.Group>).$el;
Array.from(el.children).forEach((item) => el.removeChild(item));
}
await nextTick();
};
//
const columnIfShow = (column?: Partial<Omit<BasicColumn, 'children'>>) => {
if (column) {
if ('ifShow' in column) {
if (typeof column.ifShow === 'boolean') {
return column.ifShow;
} else if (column.ifShow) {
return column.ifShow(column);
}
}
return true;
}
return false;
};
//
const getTableColumns = () => {
return table
.getColumns({ ignoreIndex: true, ignoreAction: true })
.filter((col) => columnIfShow(col));
};
//
const tableColumnsSet = (columns: BasicColumn[]) => {
isInnerChange = true;
table?.setColumns(columns);
// 沿
const columnChangeParams: ColumnChangeParam[] = columns.map((col) => ({
dataIndex: col.dataIndex ? col.dataIndex.toString() : '',
fixed: col.fixed,
visible: !col.defaultHidden,
}));
emit('columns-change', columnChangeParams);
};
//
const tableColumnsUpdate = () => {
//
const columns = cloneDeep(table.getColumns());
// fixed
let count = columns.filter(
(o) => o.flag !== INDEX_COLUMN_FLAG && (o.fixed === 'left' || o.fixed === true),
).length;
//
if (isIndexColumnShow.value) {
count++;
}
// columnOptions table.getColumns()
for (const opt of columnOptions.value) {
const colIdx = columns.findIndex((o) => o.dataIndex === opt.value);
//
if (colIdx > -1) {
const target = columns[colIdx];
target.defaultHidden = opt.column?.defaultHidden;
target.fixed = opt.fixed;
columns.splice(colIdx, 1);
columns.splice(count++, 0, target); //
}
}
// action
const actionIndex = columns.findIndex((o) => o.dataIndex === 'action');
if (actionIndex > -1) {
const actionCol = columns.splice(actionIndex, 1);
columns.push(actionCol[0]);
}
//
tableColumnsSet(columns);
};
//
const onOpenChange = async () => {
await nextTick();
if (columnOptionsRef.value) {
//
const el = (columnOptionsRef.value as InstanceType<typeof Checkbox.Group>).$el;
Sortablejs.create(unref(el), {
animation: 500, animation: 500,
delay: 400, delay: 400,
delayOnTouchOnly: true, delayOnTouchOnly: true,
handle: '.table-column-drag-icon ', handle: '.table-column-drag-icon ',
dataIdAttr: 'data-no',
onEnd: (evt) => { onEnd: (evt) => {
const { oldIndex, newIndex } = evt; const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex) {
return; return;
} }
// Sort column
const columns = cloneDeep(plainSortOptions.value);
const options = cloneDeep(columnOptions.value);
//
if (oldIndex > newIndex) { if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]); options.splice(newIndex, 0, options[oldIndex]);
columns.splice(oldIndex + 1, 1); options.splice(oldIndex + 1, 1);
} else { } else {
columns.splice(newIndex + 1, 0, columns[oldIndex]); options.splice(newIndex + 1, 0, options[oldIndex]);
columns.splice(oldIndex, 1); options.splice(oldIndex, 1);
} }
plainSortOptions.value = columns; //
// fix: ColumnSettingbug (#1931) columnOptions.value = options;
// https://github.com/vbenjs/vue-vben-admin/commit/50468e9581c93e95df21447bec30b6148541c46b
setColumns( //
columns tableColumnsUpdate();
.map((col: Options) => col.value) //
.filter((value: string) => state.checkedList.includes(value)), props.cache && columnOptionsSave();
);
}, },
}); });
// order }
sortableOrder = sortable.toArray(); };
inited = true;
// removepush
const diff = () => {
if (typeof route.name === 'string') {
let cache = tableSettingStore.getColumns(route.name);
if (cache) {
// valuelabel
if (
JSON.stringify(columnOptions.value.map((o) => ({ value: o.value, label: o.label }))) !==
JSON.stringify(cache.map((o) => ({ value: o.value, label: o.label })))
) {
const map = columnOptions.value.reduce((map, item) => {
map[item.value] = item.label;
return map;
}, {});
if (Array.isArray(cache)) {
// remove
cache = cache.filter((o) => map[o.value]);
// label
cache.forEach((o) => {
o.label = map[o.value];
}); });
const cacheKeys = cache.map((o) => o.value);
// push
cache = cache.concat(columnOptions.value.filter((o) => !cacheKeys.includes(o.value)));
//
tableSettingStore.setColumns(route.name, cache);
} }
}
}
}
};
// Control whether the serial number column is displayed //
function handleIndexCheckChange(e: CheckboxChangeEvent) { const restore = () => {
table.setProps({ if (typeof route.name === 'string') {
showIndexColumn: e.target.checked, const isIndexColumnShowCache = tableSettingStore.getShowIndexColumn(route.name);
}); //
if (typeof isIndexColumnShowCache === 'boolean') {
isIndexColumnShow.value = defaultIsIndexColumnShow && isIndexColumnShowCache;
} }
// Control whether the check box is displayed const isRowSelectionShowCache = tableSettingStore.getShowRowSelection(route.name);
function handleSelectCheckChange(e: CheckboxChangeEvent) { //
table.setProps({ if (typeof isRowSelectionShowCache === 'boolean') {
rowSelection: e.target.checked ? defaultRowSelection : undefined, isRowSelectionShow.value = defaultIsRowSelectionShow && isRowSelectionShowCache;
});
} }
function handleDragChange(e: CheckboxChangeEvent) { const allowResizeColumnCache = tableSettingStore.getAllowResizeColumn(route.name);
const columns = getColumns() as BasicColumn[]; //
columns.forEach((col) => { if (typeof allowResizeColumnCache === 'boolean') {
if (isNumber(col.width)) { isAllowResizeColumn.value = defaultIsAllowResizeColumn && allowResizeColumnCache;
col.resizable = e.target.checked;
} }
});
setColumns(columns);
} }
//
onIndexColumnShowChange({
target: { checked: isIndexColumnShow.value },
} as CheckboxChangeEvent);
//
onRowSelectionShowChange({
target: { checked: isRowSelectionShow.value },
} as CheckboxChangeEvent);
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') { if (typeof route.name === 'string') {
if (!state.checkedList.includes(item.dataIndex as string)) return; const cache = tableSettingStore.getColumns(route.name);
//
const columns = getColumns().filter((c: BasicColumn) => if (Array.isArray(cache)) {
state.checkedList.includes(c.dataIndex as string), columnOptions.value = cache;
) as BasicColumn[]; }
const isFixed = item.fixed === fixed ? false : fixed;
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
if (index !== -1) {
columns[index].fixed = isFixed;
} }
item.fixed = isFixed; };
if (isFixed && !item.width) { //
item.width = 100; const columnCheckedOptionsUpdate = () => {
columnCheckedOptions.value = columnOptions.value
.filter((o) => !o.column?.defaultHidden)
.map((o) => o.value);
};
//
const isColumnAllSelectedUpdate = () => {
isColumnAllSelected.value = columnOptions.value.length === columnCheckedOptions.value.length;
};
// showIndexColumn
const showIndexColumnUpdate = (showIndexColumn) => {
isInnerChange = true;
table.setProps({
showIndexColumn,
});
};
// rowSelection
const showRowSelectionUpdate = (showRowSelection) => {
isInnerChange = true;
table.setProps({
rowSelection: showRowSelection
? {
...omit(defaultRowSelection, ['selectedRowKeys']),
fixed: true,
} }
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed }); : undefined,
setColumns(columns); });
};
//
const formUpdate = () => {
//
columnCheckedOptionsUpdate();
//
isColumnAllSelectedUpdate();
// showIndexColumn
showIndexColumnUpdate(isIndexColumnShow.value);
// showRowSelection
showRowSelectionUpdate(isRowSelectionShow.value);
//
tableColumnsUpdate();
};
//
let defaultIsIndexColumnShow: boolean = false;
let defaultIsRowSelectionShow: boolean = false;
let defaultIsAllowResizeColumn: boolean = false;
let defaultRowSelection: TableRowSelection<Recordable<any>>;
let defaultColumnOptions: ColumnOptionsType[] = [];
const init = async () => {
if (!isRestored) {
//
const columns = getTableColumns();
// 沿
table.setCacheColumns?.(columns);
//
const options: ColumnOptionsType[] = [];
for (const col of columns) {
// string
options.push({
label:
typeof col.title === 'string'
? col.title
: col.customTitle === 'string'
? col.customTitle
: '',
value:
typeof col.dataIndex === 'string'
? col.dataIndex
: col.title === 'string'
? col.title
: '',
column: {
defaultHidden: col.defaultHidden,
},
fixed: col.fixed,
});
} }
function setColumns(columns: BasicColumn[] | string[]) { //
table.setColumns(columns); defaultIsIndexColumnShow = table.getBindValues.value.showIndexColumn || false;
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => { defaultRowSelection = table.getRowSelection();
const visible = defaultIsRowSelectionShow = !!defaultRowSelection; // rowSelection
columns.findIndex( defaultColumnOptions = options;
(c: BasicColumn | string) =>
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value), //
) !== -1; isIndexColumnShow.value = defaultIsIndexColumnShow;
return { dataIndex: col.value, fixed: col.fixed, visible }; isRowSelectionShow.value = defaultIsRowSelectionShow;
}); columnOptions.value = cloneDeep(options);
// removepush
props.cache && diff();
emits('columns-change', data); //
props.cache && restore();
//
formUpdate();
isRestored = true;
} }
};
function getPopupContainer() { //
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer(); const once = async () => {
//
await sortableFix();
init();
};
once();
//
const getColumns = computed(() => {
return table?.getColumns();
});
const getValues = computed(() => {
return table?.getBindValues;
});
onMounted(() => {
watch([getColumns, getValues], () => {
if (!isInnerChange) {
isRestored = false;
console.log('onMounted isRestored');
init();
} else {
isInnerChange = false;
} }
});
});
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting'; @prefix-cls: ~'@{namespace}-basic-column-setting';
@ -387,8 +645,8 @@
.@{prefix-cls} { .@{prefix-cls} {
&__popover-title { &__popover-title {
position: relative;
display: flex; display: flex;
position: relative;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
@ -428,7 +686,7 @@
transform: rotate(180deg); transform: rotate(180deg);
} }
&__cloumn-list { &__column-list {
svg { svg {
width: 1em !important; width: 1em !important;
height: 1em !important; height: 1em !important;
@ -442,6 +700,7 @@
} }
.ant-checkbox-group { .ant-checkbox-group {
display: inline-block;
width: 100%; width: 100%;
min-width: 260px; min-width: 260px;
// flex-wrap: wrap; // flex-wrap: wrap;

20
apps/vue/src/components/Table/src/components/settings/FullScreenSetting.vue

@ -7,32 +7,16 @@
<FullscreenExitOutlined @click="toggle" v-else /> <FullscreenExitOutlined @click="toggle" v-else />
</Tooltip> </Tooltip>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue'; import { Tooltip } from 'ant-design-vue';
import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue'; import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue';
import { useFullscreen } from '@vueuse/core'; import { useFullscreen } from '@vueuse/core';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({ // defineOptions({ name: 'FullScreenSetting' });
name: 'FullScreenSetting',
components: {
FullscreenExitOutlined,
FullscreenOutlined,
Tooltip,
},
setup() {
const table = useTableContext(); const table = useTableContext();
const { t } = useI18n(); const { t } = useI18n();
const { toggle, isFullscreen } = useFullscreen(table.wrapRef); const { toggle, isFullscreen } = useFullscreen(table.wrapRef);
return {
toggle,
isFullscreen,
t,
};
},
});
</script> </script>

16
apps/vue/src/components/Table/src/components/settings/RedoSetting.vue

@ -6,28 +6,18 @@
<RedoOutlined @click="redo" /> <RedoOutlined @click="redo" />
</Tooltip> </Tooltip>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue'; import { Tooltip } from 'ant-design-vue';
import { RedoOutlined } from '@ant-design/icons-vue'; import { RedoOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({ // defineOptions({ name: 'RedoSetting' });
name: 'RedoSetting',
components: {
RedoOutlined,
Tooltip,
},
setup() {
const table = useTableContext(); const table = useTableContext();
const { t } = useI18n(); const { t } = useI18n();
function redo() { function redo() {
table.reload(); table.reload();
} }
return { redo, t };
},
});
</script> </script>

57
apps/vue/src/components/Table/src/components/settings/SizeSetting.vue

@ -8,57 +8,54 @@
<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"> <Menu.Item key="default">
<span>{{ t('component.table.settingDensDefault') }}</span> <span>{{ t('component.table.settingDensDefault') }}</span>
</MenuItem> </Menu.Item>
<MenuItem key="middle"> <Menu.Item key="middle">
<span>{{ t('component.table.settingDensMiddle') }}</span> <span>{{ t('component.table.settingDensMiddle') }}</span>
</MenuItem> </Menu.Item>
<MenuItem key="small"> <Menu.Item key="small">
<span>{{ t('component.table.settingDensSmall') }}</span> <span>{{ t('component.table.settingDensSmall') }}</span>
</MenuItem> </Menu.Item>
</Menu> </Menu>
</template> </template>
</Dropdown> </Dropdown>
</Tooltip> </Tooltip>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { SizeType } from '../../types/table'; import type { SizeType } from '../../types/table';
import { defineComponent, ref } from 'vue'; import { ref, onMounted } from 'vue';
import { Tooltip, Dropdown, Menu } from 'ant-design-vue'; import { Tooltip, Dropdown, Menu, type MenuProps } from 'ant-design-vue';
import { ColumnHeightOutlined } from '@ant-design/icons-vue'; import { ColumnHeightOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
import { getPopupContainer } from '/@/utils'; import { getPopupContainer } from '/@/utils';
export default defineComponent({ import { useTableSettingStore } from '/@/store/modules/tableSetting';
name: 'SizeSetting',
components: { const tableSettingStore = useTableSettingStore();
ColumnHeightOutlined,
Tooltip, //defineOptions({ name: 'SizeSetting' });
Dropdown,
Menu,
MenuItem: Menu.Item,
},
setup() {
const table = useTableContext(); const table = useTableContext();
const { t } = useI18n(); const { t } = useI18n();
const selectedKeysRef = ref<SizeType[]>([table.getSize()]); const selectedKeysRef = ref<SizeType[]>([table.getSize()]);
function handleTitleClick({ key }: { key: SizeType }) { const handleTitleClick: MenuProps['onClick'] = ({ key }) => {
selectedKeysRef.value = [key]; selectedKeysRef.value = [key as SizeType];
tableSettingStore.setTableSize(key as SizeType);
table.setProps({ table.setProps({
size: key, size: key as SizeType,
}); });
}
return {
handleTitleClick,
selectedKeysRef,
getPopupContainer,
t,
}; };
},
onMounted(() => {
selectedKeysRef.value = [tableSettingStore.getTableSize];
table.setProps({
size: selectedKeysRef.value[0],
});
}); });
</script> </script>

39
apps/vue/src/components/Table/src/components/settings/TableExport.vue

@ -15,6 +15,7 @@
import { ExpExcelModal, jsonToSheetXlsx } from '/@/components/Excel'; import { ExpExcelModal, jsonToSheetXlsx } from '/@/components/Excel';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { BasicColumn } from '../../types/table';
const table = useTableContext(); const table = useTableContext();
const { t } = useI18n(); const { t } = useI18n();
@ -33,22 +34,8 @@
} }
} }
function exportDataToExcel(options) { function fillDataRows(columns: BasicColumn[], data: any, rows: any[]) {
const dataSource = table.getDataSource(); const row: {[key:string]: string} = {};
//
const columns = table
.getColumns()
.filter((col) => !col.flag || col.flag === 'DEFAULT')
.filter((col) => isString(col.title) && isString(col.dataIndex));
//
const header: { [key: string]: string } = {};
columns.forEach((col) => {
header[String(col.dataIndex)] = String(col.title);
});
//
const rows: { [key: string]: string }[] = [];
dataSource.forEach((data) => {
const row: { [key: string]: string } = {};
columns.forEach((col) => { columns.forEach((col) => {
const colName = String(col.dataIndex); const colName = String(col.dataIndex);
if (Reflect.has(data, colName)) { if (Reflect.has(data, colName)) {
@ -58,7 +45,27 @@
if (Object.keys(row).length > 0) { if (Object.keys(row).length > 0) {
rows.push(row); rows.push(row);
} }
if (Reflect.has(data, 'children') && Array.isArray(data.children)) {
data.children.forEach((d) => {
fillDataRows(columns, d, rows);
}); });
}
}
function exportDataToExcel(options) {
const dataSource = table.getDataSource();
//
const columns = table.getColumns()
.filter((col) => !col.flag || col.flag === 'DEFAULT')
.filter((col) => isString(col.title) && isString(col.dataIndex));
//
const header: {[key:string]: string} = {};
columns.forEach((col) => {
header[String(col.dataIndex)] = String(col.title);
});
//
const rows: {[key:string]: string}[] = [];
dataSource.forEach((data) => fillDataRows(columns, data, rows));
// excel // excel
jsonToSheetXlsx({ jsonToSheetXlsx({
data: rows, data: rows,

36
apps/vue/src/components/Table/src/components/settings/index.vue

@ -7,40 +7,33 @@
v-if="getSetting.setting" v-if="getSetting.setting"
@columns-change="handleColumnChange" @columns-change="handleColumnChange"
:getPopupContainer="getTableContainer" :getPopupContainer="getTableContainer"
:cache="getSetting.settingCache"
/> />
<FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" /> <FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { TableSetting, ColumnChangeParam } from '../../types/table'; import type { TableSetting, ColumnChangeParam } from '../../types/table';
import { defineComponent, computed, unref } from 'vue'; import { computed, unref } from 'vue';
import ColumnSetting from './ColumnSetting.vue'; import ColumnSetting from './ColumnSetting.vue';
import TableExport from './TableExport.vue';
import SizeSetting from './SizeSetting.vue'; import SizeSetting from './SizeSetting.vue';
import RedoSetting from './RedoSetting.vue'; import RedoSetting from './RedoSetting.vue';
import FullScreenSetting from './FullScreenSetting.vue'; import FullScreenSetting from './FullScreenSetting.vue';
import TableExport from './TableExport.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({ //defineOptions({ name: 'TableSetting' });
name: 'TableSetting',
components: { const props = defineProps({
ColumnSetting,
SizeSetting,
RedoSetting,
FullScreenSetting,
TableExport,
},
props: {
setting: { setting: {
type: Object as PropType<TableSetting>, type: Object as PropType<TableSetting>,
default: () => ({}), default: () => ({}),
}, },
}, });
emits: ['columns-change'],
setup(props, { emit }) { const emit = defineEmits(['columns-change']);
const { t } = useI18n();
const table = useTableContext(); const table = useTableContext();
const getSetting = computed((): TableSetting => { const getSetting = computed((): TableSetting => {
@ -48,8 +41,9 @@
redo: true, redo: true,
size: true, size: true,
setting: true, setting: true,
fullScreen: false,
export: false, export: false,
settingCache: false,
fullScreen: false,
...props.setting, ...props.setting,
}; };
}); });
@ -61,10 +55,6 @@
function getTableContainer() { function getTableContainer() {
return table ? unref(table.wrapRef) : document.body; return table ? unref(table.wrapRef) : document.body;
} }
return { getSetting, t, handleColumnChange, getTableContainer };
},
});
</script> </script>
<style lang="less"> <style lang="less">
.table-settings { .table-settings {

28
apps/vue/src/components/Table/src/helper.ts

@ -0,0 +1,28 @@
import { ROW_KEY } from './const';
import type { BasicTableProps } from './types/table';
export function parseRowKey<RecordType = any>(
rowKey: BasicTableProps['rowKey'],
record: RecordType,
autoCreateKey?: boolean,
): number | string {
if (autoCreateKey) {
return ROW_KEY;
} else {
if (typeof rowKey === 'string') {
return rowKey;
} else if (rowKey) {
return rowKey(record);
} else {
return ROW_KEY;
}
}
}
export function parseRowKeyValue<RecordType = any>(
rowKey: BasicTableProps['rowKey'],
record: RecordType,
autoCreateKey?: boolean,
): number | string {
return record[parseRowKey(rowKey, record, autoCreateKey)];
}

58
apps/vue/src/components/Table/src/hooks/useColumns.ts

@ -9,13 +9,14 @@ import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
import { cloneDeep, isEqual } from 'lodash-es'; import { cloneDeep, isEqual } from 'lodash-es';
import { formatToDate } from '/@/utils/dateUtil'; import { formatToDate } from '/@/utils/dateUtil';
import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const'; import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
import { ColumnType } from 'ant-design-vue/es/table';
function handleItem(item: BasicColumn, ellipsis: boolean) { function handleItem(item: BasicColumn, ellipsis: boolean) {
const { key, dataIndex, children } = item; const { key, dataIndex, children } = item;
item.align = item.align || DEFAULT_ALIGN; item.align = item.align || DEFAULT_ALIGN;
if (ellipsis) { if (ellipsis) {
if (!key) { if (!key) {
item.key = dataIndex; item.key = typeof dataIndex == 'object' ? dataIndex.join('-') : dataIndex;
} }
if (!isBoolean(item.ellipsis)) { if (!isBoolean(item.ellipsis)) {
Object.assign(item, { Object.assign(item, {
@ -65,7 +66,7 @@ function handleIndexColumn(
columns.unshift({ columns.unshift({
flag: INDEX_COLUMN_FLAG, flag: INDEX_COLUMN_FLAG,
width: 50, width: 60,
title: t('component.table.index'), title: t('component.table.index'),
align: 'center', align: 'center',
customRender: ({ index }) => { customRender: ({ index }) => {
@ -146,18 +147,11 @@ export function useColumns(
const getViewColumns = computed(() => { const getViewColumns = computed(() => {
const viewColumns = sortFixedColumn(unref(getColumnsRef)); const viewColumns = sortFixedColumn(unref(getColumnsRef));
const columns = cloneDeep(viewColumns); const mapFn = (column) => {
return columns
.filter((column) => {
return hasPermission(column.auth) && isIfShow(column);
})
.map((column) => {
const { slots, customRender, format, edit, editRow, flag } = column; const { slots, customRender, format, edit, editRow, flag } = column;
if (!slots || !slots?.title) { if (!slots || !slots?.title) {
// column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
column.customTitle = column.title; column.customTitle = column.title;
Reflect.deleteProperty(column, 'title');
} }
const isDefaultAction = [INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG].includes(flag!); const isDefaultAction = [INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG].includes(flag!);
if (!customRender && format && !edit && !isDefaultAction) { if (!customRender && format && !edit && !isDefaultAction) {
@ -171,6 +165,18 @@ export function useColumns(
column.customRender = renderEditCell(column); column.customRender = renderEditCell(column);
} }
return reactive(column); return reactive(column);
};
const columns = cloneDeep(viewColumns);
return columns
.filter((column) => hasPermission(column.auth) && isIfShow(column))
.map((column) => {
// Support table multiple header editable
if (column.children?.length) {
column.children = column.children.map(mapFn);
}
return mapFn(column);
}); });
}); });
@ -253,14 +259,26 @@ export function useColumns(
function getCacheColumns() { function getCacheColumns() {
return cacheColumns; return cacheColumns;
} }
function setCacheColumns(columns: BasicColumn[]) {
if (!isArray(columns)) return;
cacheColumns = columns.filter((item) => !item.flag);
}
/**
*
*/
function setColumnWidth(w: number, col: ColumnType<BasicColumn>) {
col.width = w;
}
return { return {
getColumnsRef, getColumnsRef,
getCacheColumns, getCacheColumns,
getColumns, getColumns,
setColumns, setColumns,
setColumnWidth,
getViewColumns, getViewColumns,
setCacheColumnsByField, setCacheColumnsByField,
setCacheColumns,
}; };
} }
@ -279,9 +297,23 @@ function sortFixedColumn(columns: BasicColumn[]) {
} }
defColumns.push(column); defColumns.push(column);
} }
return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter( // 筛选逻辑
(item) => !item.defaultHidden, const filterFunc = (item) => !item.defaultHidden;
); // 筛选首层显示列(1级表头)
const viewColumns = [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(filterFunc);
// 筛选>=2级表头(深度优先)
const list = [...viewColumns];
while (list.length) {
const current = list[0];
if (Array.isArray(current.children)) {
current.children = current.children.filter(filterFunc);
list.shift();
list.unshift(...current.children);
} else {
list.shift();
}
}
return viewColumns;
} }
// format cell // format cell

53
apps/vue/src/components/Table/src/hooks/useCustomRow.ts

@ -1,34 +1,18 @@
import type { ComputedRef } from 'vue'; import type { ComputedRef } from 'vue';
import type { BasicTableProps } from '../types/table'; import type { BasicTableProps } from '../types/table';
import { unref } from 'vue'; import { unref } from 'vue';
import { ROW_KEY } from '../const'; import type { Key } from 'ant-design-vue/lib/table/interface';
import { isString, isFunction } from '/@/utils/is';
import { parseRowKeyValue } from '../helper';
interface Options { interface Options {
setSelectedRowKeys: (keys: string[]) => void; setSelectedRowKeys: (keyValues: Key[]) => void;
getSelectRowKeys: () => string[]; getSelectRowKeys: () => Key[];
clearSelectedRowKeys: () => void; clearSelectedRowKeys: () => void;
emit: EmitType; emit: EmitType;
getAutoCreateKey: ComputedRef<boolean | undefined>; getAutoCreateKey: ComputedRef<boolean | undefined>;
} }
function getKey(
record: Recordable,
rowKey: string | ((record: Record<string, any>) => string) | undefined,
autoCreateKey?: boolean,
) {
if (!rowKey || autoCreateKey) {
return record[ROW_KEY];
}
if (isString(rowKey)) {
return record[rowKey];
}
if (isFunction(rowKey)) {
return record[rowKey(record)];
}
return null;
}
export function useCustomRow( export function useCustomRow(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
{ setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options, { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options,
@ -40,37 +24,38 @@ export function useCustomRow(
function handleClick() { function handleClick() {
const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef); const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef);
if (!rowSelection || !clickToRowSelect) return; if (!rowSelection || !clickToRowSelect) return;
const keys = getSelectRowKeys() || []; const keyValues = getSelectRowKeys() || [];
const key = getKey(record, rowKey, unref(getAutoCreateKey)); const keyValue = parseRowKeyValue(rowKey, record, unref(getAutoCreateKey));
if (!key) return; if (!keyValue) return;
const isCheckbox = rowSelection.type === 'checkbox'; const isCheckbox = rowSelection.type === 'checkbox';
if (isCheckbox) { if (isCheckbox) {
// 找到tr // 找到tr
const tr: HTMLElement = (e as MouseEvent) const tr = (e as MouseEvent)
.composedPath?.() .composedPath?.()
.find((dom: HTMLElement) => dom.tagName === 'TR') as HTMLElement; .find((dom) => (dom as HTMLElement).tagName === 'TR') as HTMLElement;
if (!tr) return; if (!tr) return;
// 找到Checkbox,检查是否为disabled // 找到Checkbox,检查是否为disabled
const checkBox = tr.querySelector('input[type=checkbox]'); const checkBox = tr.querySelector('input[type=checkbox]');
if (!checkBox || checkBox.hasAttribute('disabled')) return; if (!checkBox || checkBox.hasAttribute('disabled')) return;
if (!keys.includes(key)) { if (!keyValues.includes(keyValue)) {
setSelectedRowKeys([...keys, key]); keyValues.push(keyValue);
setSelectedRowKeys(keyValues);
return; return;
} }
const keyIndex = keys.findIndex((item) => item === key); const keyIndex = keyValues.findIndex((item) => item === keyValue);
keys.splice(keyIndex, 1); keyValues.splice(keyIndex, 1);
setSelectedRowKeys(keys); setSelectedRowKeys(keyValues);
return; return;
} }
const isRadio = rowSelection.type === 'radio'; const isRadio = rowSelection.type === 'radio';
if (isRadio) { if (isRadio) {
if (!keys.includes(key)) { if (!keyValues.includes(keyValue)) {
if (keys.length) { if (keyValues.length) {
clearSelectedRowKeys(); clearSelectedRowKeys();
} }
setSelectedRowKeys([key]); setSelectedRowKeys([keyValue]);
return; return;
} }
clearSelectedRowKeys(); clearSelectedRowKeys();

84
apps/vue/src/components/Table/src/hooks/useDataSource.ts

@ -16,6 +16,8 @@ import { buildUUID } from '/@/utils/uuid';
import { isFunction, isBoolean, isObject } from '/@/utils/is'; import { isFunction, isBoolean, isObject } from '/@/utils/is';
import { get, cloneDeep, merge } from 'lodash-es'; import { get, cloneDeep, merge } from 'lodash-es';
import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const'; import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const';
import { parseRowKeyValue } from '../helper';
import type { Key } from 'ant-design-vue/lib/table/interface';
interface ActionType { interface ActionType {
getPaginationInfo: ComputedRef<boolean | PaginationProps>; getPaginationInfo: ComputedRef<boolean | PaginationProps>;
@ -69,10 +71,7 @@ export function useDataSource(
filters: Partial<Recordable<string[]>>, filters: Partial<Recordable<string[]>>,
sorter: SorterResult, sorter: SorterResult,
) { ) {
const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef); const { sortFn, filterFn } = unref(propsRef);
if (clearSelectOnPageChange) {
clearSelectedRowKeys();
}
setPagination(pagination); setPagination(pagination);
const params: Recordable = {}; const params: Recordable = {};
@ -138,7 +137,7 @@ export function useDataSource(
return unref(dataSourceRef); return unref(dataSourceRef);
}); });
async function updateTableData(index: number, key: string, value: any) { async function updateTableData(index: number, key: Key, value: any) {
const record = dataSourceRef.value[index]; const record = dataSourceRef.value[index];
if (record) { if (record) {
dataSourceRef.value[index][key] = value; dataSourceRef.value[index][key] = value;
@ -146,11 +145,8 @@ export function useDataSource(
return dataSourceRef.value[index]; return dataSourceRef.value[index];
} }
function updateTableDataRecord( function updateTableDataRecord(keyValue: Key, record: Recordable): Recordable | undefined {
rowKey: string | number, const row = findTableDataRecord(keyValue);
record: Recordable,
): Recordable | undefined {
const row = findTableDataRecord(rowKey);
if (row) { if (row) {
for (const field in row) { for (const field in row) {
@ -160,34 +156,28 @@ export function useDataSource(
} }
} }
function deleteTableDataRecord(rowKey: string | number | string[] | number[]) { function deleteTableDataRecord(keyValues: Key | Key[]) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const rowKeyName = unref(getRowKey); const delKeyValues = !Array.isArray(keyValues) ? [keyValues] : keyValues;
if (!rowKeyName) return;
const rowKeys = !Array.isArray(rowKey) ? [rowKey] : rowKey;
function deleteRow(data?: Recordable<any>, key?: string | number | string[] | number[]) { function deleteRow(data, keyValue) {
const row: { index: number; data: [] } = findRow(data, key); const row: { index: number; data: [] } = findRow(data, keyValue);
if (row === null || row.index === -1) { if (row === null || row.index === -1) {
return; return;
} }
row.data.splice(row.index, 1); row.data.splice(row.index, 1);
function findRow(data, key) { function findRow(data, keyValue) {
if (data === null || data === undefined) { if (data === null || data === undefined) {
return null; return null;
} }
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const row = data[i]; const row = data[i];
let targetKeyName: string = rowKeyName as string; if (parseRowKeyValue(unref(getRowKey), row) === keyValue) {
if (isFunction(rowKeyName)) {
targetKeyName = rowKeyName(row);
}
if (row[targetKeyName] === key) {
return { index: i, data }; return { index: i, data };
} }
if (row.children?.length > 0) { if (row.children?.length > 0) {
const result = findRow(row.children, key); const result = findRow(row.children, keyValue);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -197,16 +187,19 @@ export function useDataSource(
} }
} }
for (const key of rowKeys) { for (const keyValue of delKeyValues) {
deleteRow(dataSourceRef.value, key); deleteRow(dataSourceRef.value, keyValue);
deleteRow(unref(propsRef).dataSource, key); deleteRow(unref(propsRef).dataSource, keyValue);
} }
setPagination({ setPagination({
total: unref(propsRef).dataSource?.length, total: unref(propsRef).dataSource?.length,
}); });
} }
function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable[] | undefined { function insertTableDataRecord(
record: Recordable | Recordable[],
index?: number,
): Recordable[] | undefined {
// if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
index = index ?? dataSourceRef.value?.length; index = index ?? dataSourceRef.value?.length;
const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]); const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]);
@ -214,40 +207,22 @@ export function useDataSource(
return unref(dataSourceRef); return unref(dataSourceRef);
} }
function findTableDataRecord(rowKey: string | number) { function findTableDataRecord(keyValue: Key) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const rowKeyName = unref(getRowKey);
if (!rowKeyName) return;
const { childrenColumnName = 'children' } = unref(propsRef); const { childrenColumnName = 'children' } = unref(propsRef);
const findRow = (array: any[]) => { const findRow = (array: any[]) => {
let ret; let ret;
array.some(function iter(r) { array.some(function iter(r) {
if (typeof rowKeyName === 'function') { if (parseRowKeyValue(unref(getRowKey), r) === keyValue) {
if ((rowKeyName(r) as string) === rowKey) {
ret = r; ret = r;
return true; return true;
} }
} else {
if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) {
ret = r;
return true;
}
}
return r[childrenColumnName] && r[childrenColumnName].some(iter); return r[childrenColumnName] && r[childrenColumnName].some(iter);
}); });
return ret; return ret;
}; };
// const row = dataSourceRef.value.find(r => {
// if (typeof rowKeyName === 'function') {
// return (rowKeyName(r) as string) === rowKey
// } else {
// return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey
// }
// })
return findRow(dataSourceRef.value); return findRow(dataSourceRef.value);
} }
@ -257,12 +232,13 @@ export function useDataSource(
searchInfo, searchInfo,
defSort, defSort,
fetchSetting, fetchSetting,
useSearchForm,
beforeFetch, beforeFetch,
beforeResponse, beforeResponse,
afterFetch, afterFetch,
useSearchForm,
pagination, pagination,
advancedSearchConfig, advancedSearchConfig,
clearSelectOnPageChange,
} = unref(propsRef); } = unref(propsRef);
let fetchApi = api; let fetchApi = api;
// 高级查询条件支持 // 高级查询条件支持
@ -275,6 +251,9 @@ export function useDataSource(
} }
if (!fetchApi || !isFunction(fetchApi)) return; if (!fetchApi || !isFunction(fetchApi)) return;
try { try {
if (clearSelectOnPageChange) {
clearSelectedRowKeys();
}
setLoading(true); setLoading(true);
const { pageField, sizeField, listField, totalField } = Object.assign( const { pageField, sizeField, listField, totalField } = Object.assign(
{}, {},
@ -310,7 +289,6 @@ export function useDataSource(
} }
let res = await fetchApi(params); let res = await fetchApi(params);
// 增加用户自定义的返回数据处理函数 // 增加用户自定义的返回数据处理函数
if (beforeResponse && isFunction(beforeResponse)) { if (beforeResponse && isFunction(beforeResponse)) {
res = beforeResponse(res); res = beforeResponse(res);
@ -321,10 +299,10 @@ export function useDataSource(
const isArrayResult = Array.isArray(res); const isArrayResult = Array.isArray(res);
let resultItems: Recordable[] = isArrayResult ? res : get(res, listField); let resultItems: Recordable[] = isArrayResult ? res : get(res, listField);
const resultTotal: number = isArrayResult ? res.length : Number(get(res, totalField)); const resultTotal: number = isArrayResult ? res.length : get(res, totalField);
// 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行
if (resultTotal) { if (Number(resultTotal)) {
const currentTotalPage = Math.ceil(resultTotal / pageSize); const currentTotalPage = Math.ceil(resultTotal / pageSize);
if (current > currentTotalPage) { if (current > currentTotalPage) {
setPagination({ setPagination({
@ -362,8 +340,8 @@ export function useDataSource(
} }
} }
function setTableData(values: any[]) { function setTableData<T = Recordable>(values: T[]) {
dataSourceRef.value = values; dataSourceRef.value = values as Recordable[];
} }
function getDataSource<T = Recordable>() { function getDataSource<T = Recordable>() {

89
apps/vue/src/components/Table/src/hooks/useRowSelection.ts

@ -4,13 +4,15 @@ import { computed, ComputedRef, nextTick, Ref, ref, toRaw, unref, watch } from '
import { ROW_KEY } from '../const'; import { ROW_KEY } from '../const';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { findNodeAll } from '/@/utils/helper/treeHelper'; import { findNodeAll } from '/@/utils/helper/treeHelper';
import type { Key } from 'ant-design-vue/lib/table/interface';
import { parseRowKey, parseRowKeyValue } from '../helper';
export function useRowSelection( export function useRowSelection(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
tableData: Ref<Recordable[]>, tableData: Ref<Recordable[]>,
emit: EmitType, emit: EmitType,
) { ) {
const selectedRowKeysRef = ref<string[]>([]); const selectedRowKeysRef = ref<Key[]>([]);
const selectedRowRef = ref<Recordable[]>([]); const selectedRowRef = ref<Recordable[]>([]);
const getRowSelectionRef = computed((): TableRowSelection | null => { const getRowSelectionRef = computed((): TableRowSelection | null => {
@ -21,8 +23,55 @@ export function useRowSelection(
return { return {
selectedRowKeys: unref(selectedRowKeysRef), selectedRowKeys: unref(selectedRowKeysRef),
onChange: (selectedRowKeys: string[]) => { onChange: (selectedRowKeys: Key[], selectedRows: any[], isClickCustomRow?: boolean) => {
setSelectedRowKeys(selectedRowKeys); if (isClickCustomRow) {
// 点击行触发
// 维持外部定义的 onChange 回调
rowSelection.onChange?.(selectedRowKeys, selectedRows);
} else {
// 点击 checkbox/radiobox 触发
// 取出【当前页】所有 keyValues
const currentPageKeys = tableData.value.map((o) => parseRowKeyValue(unref(getRowKey), o));
// 从【所有分页】已选的 keyValues,且属于【当前页】的部分
for (const selectedKey of selectedRowKeysRef.value.filter((k) =>
currentPageKeys.includes(k),
)) {
// 判断是否已经不存在于【当前页】
if (selectedRowKeys.findIndex((k) => k === selectedKey) < 0) {
// 不存在 = 取消勾选
const removeIndex = selectedRowKeysRef.value.findIndex((k) => k === selectedKey);
if (removeIndex > -1) {
// 取消勾选
selectedRowKeysRef.value.splice(removeIndex, 1);
selectedRowRef.value.splice(removeIndex, 1);
}
}
}
// 存在于【当前页】,但不存在于【所有分页】,则认为是新增的
for (const selectedKey of selectedRowKeys) {
const existIndex = selectedRowKeysRef.value.findIndex((k) => k === selectedKey);
if (existIndex < 0) {
// 新增勾选
selectedRowKeysRef.value.push(selectedKey);
const record = selectedRows.find(
(o) => parseRowKeyValue(unref(getRowKey), o) === selectedKey,
);
if (record) {
selectedRowRef.value.push(record);
}
}
}
// 赋值调整过的值
setSelectedRowKeys(selectedRowKeysRef.value);
// 维持外部定义的onChange回调
rowSelection.onChange?.(selectedRowKeysRef.value, selectedRowRef.value);
}
}, },
...omit(rowSelection, ['onChange']), ...omit(rowSelection, ['onChange']),
}; };
@ -30,7 +79,7 @@ export function useRowSelection(
watch( watch(
() => unref(propsRef).rowSelection?.selectedRowKeys, () => unref(propsRef).rowSelection?.selectedRowKeys,
(v: string[]) => { (v?: Key[]) => {
setSelectedRowKeys(v); setSelectedRowKeys(v);
}, },
); );
@ -42,7 +91,7 @@ export function useRowSelection(
const { rowSelection } = unref(propsRef); const { rowSelection } = unref(propsRef);
if (rowSelection) { if (rowSelection) {
const { onChange } = rowSelection; const { onChange } = rowSelection;
if (onChange && isFunction(onChange)) onChange(getSelectRowKeys(), getSelectRows()); if (onChange && isFunction(onChange)) onChange(getSelectRowKeys(), getSelectRows(), true);
} }
emit('selection-change', { emit('selection-change', {
keys: getSelectRowKeys(), keys: getSelectRowKeys(),
@ -62,25 +111,39 @@ export function useRowSelection(
return unref(getAutoCreateKey) ? ROW_KEY : rowKey; return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
}); });
function setSelectedRowKeys(rowKeys: string[]) { function setSelectedRowKeys(keyValues?: Key[]) {
selectedRowKeysRef.value = rowKeys; selectedRowKeysRef.value = keyValues || [];
const rows = toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef)));
const allSelectedRows = findNodeAll( const allSelectedRows = findNodeAll(
toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))), rows,
(item) => rowKeys?.includes(item[unref(getRowKey) as string]), (item) => keyValues?.includes(parseRowKeyValue(unref(getRowKey), item)),
{ {
children: propsRef.value.childrenColumnName ?? 'children', children: propsRef.value.childrenColumnName ?? 'children',
}, },
); );
const trueSelectedRows: any[] = []; const trueSelectedRows: any[] = [];
rowKeys?.forEach((key: string) => { keyValues?.forEach((keyValue: Key) => {
const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key); const found = allSelectedRows.find(
found && trueSelectedRows.push(found); (item) => parseRowKeyValue(unref(getRowKey), item) === keyValue,
);
if (found) {
trueSelectedRows.push(found);
} else {
// 跨页的时候,非本页数据无法得到,暂如此处理
// tableData or selectedRowRef 总有数据
if (rows[0]) {
trueSelectedRows.push({ [parseRowKey(unref(getRowKey), rows[0])]: keyValue });
}
}
}); });
selectedRowRef.value = trueSelectedRows; selectedRowRef.value = trueSelectedRows;
} }
function setSelectedRows(rows: Recordable[]) { function setSelectedRows(rows: Recordable[]) {
selectedRowRef.value = rows; selectedRowRef.value = rows;
selectedRowKeysRef.value = selectedRowRef.value.map((o) =>
parseRowKeyValue(unref(getRowKey), o),
);
} }
function clearSelectedRowKeys() { function clearSelectedRowKeys() {
@ -88,7 +151,7 @@ export function useRowSelection(
selectedRowKeysRef.value = []; selectedRowKeysRef.value = [];
} }
function deleteSelectRowByKey(key: string) { function deleteSelectRowByKey(key: Key) {
const selectedRowKeys = unref(selectedRowKeysRef); const selectedRowKeys = unref(selectedRowKeysRef);
const index = selectedRowKeys.findIndex((item) => item === key); const index = selectedRowKeys.findIndex((item) => item === key);
if (index !== -1) { if (index !== -1) {

32
apps/vue/src/components/Table/src/hooks/useTable.ts

@ -7,6 +7,7 @@ import { getDynamicProps } from '/@/utils';
import { ref, onUnmounted, unref, watch, toRaw } from 'vue'; import { ref, onUnmounted, unref, watch, toRaw } from 'vue';
import { isProdMode } from '/@/utils/env'; import { isProdMode } from '/@/utils/env';
import { error } from '/@/utils/log'; import { error } from '/@/utils/log';
import type { Key } from 'ant-design-vue/lib/table/interface';
type Props = Partial<DynamicProps<BasicTableProps>>; type Props = Partial<DynamicProps<BasicTableProps>>;
@ -92,7 +93,7 @@ export function useTable(tableProps?: Props): [
const columns = getTableInstance().getColumns({ ignoreIndex }) || []; const columns = getTableInstance().getColumns({ ignoreIndex }) || [];
return toRaw(columns); return toRaw(columns);
}, },
setColumns: (columns: BasicColumn[]) => { setColumns: (columns: BasicColumn[] | string[]) => {
getTableInstance().setColumns(columns); getTableInstance().setColumns(columns);
}, },
setTableData: (values: any[]) => { setTableData: (values: any[]) => {
@ -101,8 +102,8 @@ export function useTable(tableProps?: Props): [
setPagination: (info: Partial<PaginationProps>) => { setPagination: (info: Partial<PaginationProps>) => {
return getTableInstance().setPagination(info); return getTableInstance().setPagination(info);
}, },
deleteSelectRowByKey: (key: string) => { deleteSelectRowByKey: (keyValue: Key) => {
getTableInstance().deleteSelectRowByKey(key); getTableInstance().deleteSelectRowByKey(keyValue);
}, },
getSelectRowKeys: () => { getSelectRowKeys: () => {
return toRaw(getTableInstance().getSelectRowKeys()); return toRaw(getTableInstance().getSelectRowKeys());
@ -113,8 +114,8 @@ export function useTable(tableProps?: Props): [
clearSelectedRowKeys: () => { clearSelectedRowKeys: () => {
getTableInstance().clearSelectedRowKeys(); getTableInstance().clearSelectedRowKeys();
}, },
setSelectedRowKeys: (keys: string[] | number[]) => { setSelectedRowKeys: (keyValues: Key[]) => {
getTableInstance().setSelectedRowKeys(keys); getTableInstance().setSelectedRowKeys(keyValues);
}, },
getPaginationRef: () => { getPaginationRef: () => {
return getTableInstance().getPaginationRef(); return getTableInstance().getPaginationRef();
@ -125,17 +126,17 @@ export function useTable(tableProps?: Props): [
updateTableData: (index: number, key: string, value: any) => { updateTableData: (index: number, key: string, value: any) => {
return getTableInstance().updateTableData(index, key, value); return getTableInstance().updateTableData(index, key, value);
}, },
deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => { deleteTableDataRecord: (keyValues: Key | Key[]) => {
return getTableInstance().deleteTableDataRecord(rowKey); return getTableInstance().deleteTableDataRecord(keyValues);
}, },
insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => { insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => {
return getTableInstance().insertTableDataRecord(record, index); return getTableInstance().insertTableDataRecord(record, index);
}, },
updateTableDataRecord: (rowKey: string | number, record: Recordable) => { updateTableDataRecord: (keyValue: Key, record: Recordable) => {
return getTableInstance().updateTableDataRecord(rowKey, record); return getTableInstance().updateTableDataRecord(keyValue, record);
}, },
findTableDataRecord: (rowKey: string | number) => { findTableDataRecord: (keyValue: Key) => {
return getTableInstance().findTableDataRecord(rowKey); return getTableInstance().findTableDataRecord(keyValue);
}, },
getRowSelection: () => { getRowSelection: () => {
return toRaw(getTableInstance().getRowSelection()); return toRaw(getTableInstance().getRowSelection());
@ -155,12 +156,15 @@ export function useTable(tableProps?: Props): [
expandAll: () => { expandAll: () => {
getTableInstance().expandAll(); getTableInstance().expandAll();
}, },
expandRows: (keys: string[]) => {
getTableInstance().expandRows(keys);
},
collapseAll: () => { collapseAll: () => {
getTableInstance().collapseAll(); getTableInstance().collapseAll();
}, },
expandRows: (keyValues: Key[]) => {
getTableInstance().expandRows(keyValues);
},
collapseRows: (keyValues: Key[]) => {
getTableInstance().collapseRows(keyValues);
},
scrollTo: (pos: string) => { scrollTo: (pos: string) => {
getTableInstance().scrollTo(pos); getTableInstance().scrollTo(pos);
}, },

107
apps/vue/src/components/Table/src/hooks/useTableExpand.ts

@ -1,14 +1,16 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import type { BasicTableProps } from '../types/table'; import type { BasicTableProps } from '../types/table';
import { computed, unref, ref, toRaw } from 'vue'; import { computed, unref, ref, toRaw, nextTick } from 'vue';
import { ROW_KEY } from '../const'; import { ROW_KEY } from '../const';
import { parseRowKeyValue } from '../helper';
import type { Key } from 'ant-design-vue/lib/table/interface';
export function useTableExpand( export function useTableExpand(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
tableData: Ref<Recordable[]>, tableData: Ref<Recordable[]>,
emit: EmitType, emit: EmitType,
) { ) {
const expandedRowKeys = ref<string[]>([]); const expandedRowKeys = ref<Key[]>([]);
const getAutoCreateKey = computed(() => { const getAutoCreateKey = computed(() => {
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey; return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
@ -20,46 +22,111 @@ export function useTableExpand(
}); });
const getExpandOption = computed(() => { const getExpandOption = computed(() => {
const { isTreeTable } = unref(propsRef); const { isTreeTable, expandRowByClick } = unref(propsRef);
if (!isTreeTable) return {}; if (!isTreeTable && !expandRowByClick) return {};
return { return {
expandedRowKeys: unref(expandedRowKeys), expandedRowKeys: unref(expandedRowKeys),
onExpandedRowsChange: (keys: string[]) => { onExpandedRowsChange: (keyValues: string[]) => {
expandedRowKeys.value = keys; expandedRowKeys.value = keyValues;
emit('expanded-rows-change', keys); emit('expanded-rows-change', keyValues);
}, },
}; };
}); });
function expandAll() { function expandAll() {
const keys = getAllKeys(); const keyValues = getAllKeys();
expandedRowKeys.value = keys; expandedRowKeys.value = keyValues;
} }
function expandRows(keys: string[]) { function collapseAll() {
expandedRowKeys.value = [];
}
function expandRows(keyValues: Key[]) {
// use row ID expands the specified table row // use row ID expands the specified table row
const { isTreeTable } = unref(propsRef); const { isTreeTable, expandRowByClick } = unref(propsRef);
if (!isTreeTable) return; if (!isTreeTable && !expandRowByClick) return;
expandedRowKeys.value = [...expandedRowKeys.value, ...keys]; expandedRowKeys.value = [...expandedRowKeys.value, ...keyValues];
}
function collapseRows(keyValues: Key[]) {
// use row ID collapses the specified table row
const { isTreeTable, expandRowByClick } = unref(propsRef);
if (!isTreeTable && !expandRowByClick) return;
expandedRowKeys.value = unref(expandedRowKeys).filter(
(keyValue) => !keyValues.includes(keyValue),
);
} }
function getAllKeys(data?: Recordable[]) { function getAllKeys(data?: Recordable[]) {
const keys: string[] = []; const keyValues: Array<number | string> = [];
const { childrenColumnName } = unref(propsRef); const { childrenColumnName } = unref(propsRef);
toRaw(data || unref(tableData)).forEach((item) => { toRaw(data || unref(tableData)).forEach((item) => {
keys.push(item[unref(getRowKey) as string]); keyValues.push(parseRowKeyValue(unref(getRowKey), item));
const children = item[childrenColumnName || 'children']; const children = item[childrenColumnName || 'children'];
if (children?.length) { if (children?.length) {
keys.push(...getAllKeys(children)); keyValues.push(...getAllKeys(children));
} }
}); });
return keys; return keyValues;
} }
function collapseAll() { // 获取展开路径 keyValues
expandedRowKeys.value = []; function getKeyPaths(
records: Recordable[],
childrenColumnName: string,
keyValue: Key,
paths: Array<Key>,
): boolean {
if (
records.findIndex((record) => parseRowKeyValue(unref(getRowKey), record) === keyValue) > -1
) {
paths.push(keyValue);
return true;
} else {
for (const record of records) {
const children = record[childrenColumnName];
if (Array.isArray(children) && getKeyPaths(children, childrenColumnName, keyValue, paths)) {
paths.push(parseRowKeyValue(unref(getRowKey), record));
return true;
}
}
}
return false;
}
// 手风琴展开
function expandRowAccordion(keyValue: Key) {
const { childrenColumnName } = unref(propsRef);
const paths: Array<Key> = [];
getKeyPaths(tableData.value, childrenColumnName || 'children', keyValue, paths);
expandedRowKeys.value = paths;
}
// 监听展开事件,用于支持手风琴展开效果
function handleTableExpand(expanded, record) {
// 手风琴开关
// isTreeTable 或 expandRowByClick 时支持
// 展开操作
if (
propsRef.value.accordion &&
(propsRef.value.isTreeTable || propsRef.value.expandRowByClick) &&
expanded
) {
nextTick(() => {
expandRowAccordion(parseRowKeyValue(unref(getRowKey), record));
});
}
} }
return { getExpandOption, expandAll, expandRows, collapseAll }; return {
getExpandOption,
expandAll,
collapseAll,
expandRows,
collapseRows,
expandRowAccordion,
handleTableExpand,
};
} }

10
apps/vue/src/components/Table/src/hooks/useTableFooter.ts

@ -6,11 +6,7 @@ import { useEventListener } from '/@/hooks/event/useEventListener';
export function useTableFooter( export function useTableFooter(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
scrollRef: ComputedRef<{ scrollRef: ComputedRef<BasicTableProps['scroll']>,
x: string | number | true;
y: string | number | null;
scrollToFirstRowOnChange: boolean;
}>,
tableElRef: Ref<ComponentRef>, tableElRef: Ref<ComponentRef>,
getDataSourceRef: ComputedRef<Recordable>, getDataSourceRef: ComputedRef<Recordable>,
) { ) {
@ -36,13 +32,13 @@ export function useTableFooter(
nextTick(() => { nextTick(() => {
const tableEl = unref(tableElRef); const tableEl = unref(tableElRef);
if (!tableEl) return; if (!tableEl) return;
const bodyDom = tableEl.$el.querySelector('.ant-table-content'); const bodyDom = tableEl.$el.querySelector(' .ant-table-content, .ant-table-body');
useEventListener({ useEventListener({
el: bodyDom, el: bodyDom,
name: 'scroll', name: 'scroll',
listener: () => { listener: () => {
const footerBodyDom = tableEl.$el.querySelector( const footerBodyDom = tableEl.$el.querySelector(
'.ant-table-footer .ant-table-content', '.ant-table-footer .ant-table-container [class^="ant-table-"]',
) as HTMLDivElement; ) as HTMLDivElement;
if (!footerBodyDom || !bodyDom) return; if (!footerBodyDom || !bodyDom) return;
footerBodyDom.scrollLeft = bodyDom.scrollLeft; footerBodyDom.scrollLeft = bodyDom.scrollLeft;

2
apps/vue/src/components/Table/src/hooks/useTableForm.ts

@ -9,7 +9,7 @@ import { isFunction } from '/@/utils/is';
export function useTableForm( export function useTableForm(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
slots: Slots, slots: Slots,
fetch: (opt?: FetchParams | undefined) => Promise<Recordable<any>[] | undefined | void>, fetch: (opt?: FetchParams) => Promise<Recordable<any>[] | undefined>,
getLoading: ComputedRef<boolean | undefined>, getLoading: ComputedRef<boolean | undefined>,
setFieldsValue: (values: Recordable) => Promise<void>, setFieldsValue: (values: Recordable) => Promise<void>,
) { ) {

5
apps/vue/src/components/Table/src/hooks/useTableHeader.ts

@ -1,5 +1,5 @@
import type { ComputedRef, Slots } from 'vue'; import type { ComputedRef, Slots } from 'vue';
import type { BasicTableProps, InnerHandlers } from '../types/table'; import type { BasicTableProps, InnerHandlers, InnerMethods } from '../types/table';
import { unref, computed, h } from 'vue'; import { unref, computed, h } from 'vue';
import TableHeader from '../components/TableHeader.vue'; import TableHeader from '../components/TableHeader.vue';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
@ -9,6 +9,7 @@ export function useTableHeader(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
slots: Slots, slots: Slots,
handlers: InnerHandlers, handlers: InnerHandlers,
methods: InnerMethods,
useTableAlert?: ComputedRef<Boolean>, useTableAlert?: ComputedRef<Boolean>,
tableAlertMessage?: ComputedRef<String>, tableAlertMessage?: ComputedRef<String>,
) { ) {
@ -33,6 +34,8 @@ export function useTableHeader(
showTableAlert: unref(useTableAlert), showTableAlert: unref(useTableAlert),
tableAlertMessage: unref(tableAlertMessage), tableAlertMessage: unref(tableAlertMessage),
onColumnsChange: handlers.onColumnsChange, onColumnsChange: handlers.onColumnsChange,
clearSelectedRowKeys: methods.clearSelectedRowKeys,
count: methods.getSelectRowKeys().length,
onDeSelect: handlers.deSelect, onDeSelect: handlers.deSelect,
} as Recordable, } as Recordable,
{ {

261
apps/vue/src/components/Table/src/hooks/useTableScroll.ts

@ -1,12 +1,20 @@
import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table'; import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table';
import { Ref, ComputedRef, ref } from 'vue'; import { Ref, ComputedRef, ref, computed, unref, nextTick, watch } from 'vue';
import { computed, unref, nextTick, watch } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils'; import { getViewportOffset } from '/@/utils/domUtils';
import { isBoolean } from '/@/utils/is'; import { isBoolean } from '/@/utils/is';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { useModalContext } from '/@/components/Modal';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useDebounceFn } from '@vueuse/core'; import { useModalContext } from '/@/components/Modal';
import { useDebounceFn, promiseTimeout } from '@vueuse/core';
import {
footerHeight as layoutFooterHeight,
layoutMultipleHeadePlaceholderTime,
} from '/@/settings/designSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
const { getShowFooter, getFullContent } = useRootSetting();
export function useTableScroll( export function useTableScroll(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
@ -29,7 +37,7 @@ export function useTableScroll(
}); });
watch( watch(
() => [unref(getCanResize), unref(getDataSourceRef)?.length], () => [unref(getCanResize), unref(getDataSourceRef)?.length, unref(getShowFooter)],
() => { () => {
debounceRedoHeight(); debounceRedoHeight();
}, },
@ -38,6 +46,18 @@ export function useTableScroll(
}, },
); );
watch(
() => [unref(getFullContent)],
async () => {
// 等待动画结束后200毫秒
await promiseTimeout(layoutMultipleHeadePlaceholderTime * 1000 + 200);
debounceRedoHeight();
},
{
flush: 'post',
},
);
function redoHeight() { function redoHeight() {
nextTick(() => { nextTick(() => {
calcTableHeight(); calcTableHeight();
@ -55,22 +75,13 @@ export function useTableScroll(
let footerEl: HTMLElement | null; let footerEl: HTMLElement | null;
let bodyEl: HTMLElement | null; let bodyEl: HTMLElement | null;
async function calcTableHeight() { /**
const { resizeHeightOffset, pagination, maxHeight, isCanResizeParent, useSearchForm } = * table wrapper padding
unref(propsRef); * @description .vben-basic-table .ant-table-wrapper
const tableData = unref(getDataSourceRef); */
const tableWrapperPadding = 6;
const table = unref(tableElRef);
if (!table) return;
const tableEl: Element = table.$el;
if (!tableEl) return;
if (!bodyEl) {
bodyEl = tableEl.querySelector('.ant-table-body');
if (!bodyEl) return;
}
function handleScrollBar(bodyEl: HTMLElement, tableEl: Element) {
const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight; const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth; const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth;
@ -87,35 +98,41 @@ export function useTableScroll(
} else { } else {
!tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x'); !tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x');
} }
}
bodyEl!.style.height = 'unset'; /**
*
if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return; * @param tableEl table element
* @returns number
await nextTick(); */
// Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight function caclPaginationHeight(tableEl: Element): number {
const { pagination } = unref(propsRef);
const headEl = tableEl.querySelector('.ant-table-thead ');
if (!headEl) return;
// Table height from bottom height-custom offset let paginationHeight = 0;
let paddingHeight = 32;
// Pager height
let paginationHeight = 2;
if (!isBoolean(pagination)) { if (!isBoolean(pagination)) {
// 从 Dom 获取
if (!paginationEl) {
paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement; paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement;
}
if (paginationEl) { if (paginationEl) {
// 分页 margin-top
const paginationElMarginTop = parseInt(getComputedStyle(paginationEl).marginTop);
// 分页高度
const offsetHeight = paginationEl.offsetHeight; const offsetHeight = paginationEl.offsetHeight;
paginationHeight += offsetHeight || 0; paginationHeight = offsetHeight + paginationElMarginTop;
} else { } else {
// TODO First fix 24 // 找不到分页组件,缺省给予默认分页 margin-top + 高度
paginationHeight += 24; paginationHeight = 10 + 24;
} }
} else { } else {
paginationHeight = -8; // 不显示分页,pagination 为 false 的时候
paginationHeight = 0;
}
return paginationHeight;
} }
function caclFooterHeight(tableEl: Element): number {
const { pagination } = unref(propsRef);
let footerHeight = 0; let footerHeight = 0;
if (!isBoolean(pagination)) { if (!isBoolean(pagination)) {
if (!footerEl) { if (!footerEl) {
@ -125,48 +142,172 @@ export function useTableScroll(
footerHeight += offsetHeight || 0; footerHeight += offsetHeight || 0;
} }
} }
return footerHeight;
}
function calcHeaderHeight(headEl: Element): number {
let headerHeight = 0; let headerHeight = 0;
if (headEl) { if (headEl) {
headerHeight = (headEl as HTMLElement).offsetHeight; headerHeight = (headEl as HTMLElement).offsetHeight;
} }
return headerHeight;
}
/**
* body底部的总高度
* @param tableEl table element
* @param headEl table element
* @returns number
*/
function calcBottomAndPaddingHeight(tableEl: Element, headEl: Element) {
const { isCanResizeParent } = unref(propsRef);
let bottomIncludeBody = 0; let bottomIncludeBody = 0;
if (unref(wrapRef) && isCanResizeParent) { if (unref(wrapRef) && isCanResizeParent) {
const tablePadding = 12; // 继承父元素高度
const formMargin = 16;
let paginationMargin = 10;
const wrapHeight = unref(wrapRef)?.offsetHeight ?? 0; const wrapHeight = unref(wrapRef)?.offsetHeight ?? 0;
let formHeight = unref(formRef)?.$el.offsetHeight ?? 0; let formHeight = unref(formRef)?.$el.offsetHeight ?? 0;
if (formHeight) { if (formHeight) {
formHeight += formMargin; // 来自于 .vben-basic-table-form-container .ant-form 以及 .vben-basic-table-form-container
formHeight += 16 + 16 * 2;
} }
if (isBoolean(pagination) && !pagination) {
paginationMargin = 0; bottomIncludeBody = wrapHeight - tableWrapperPadding - formHeight;
} else {
// 缺省 wrapRef 情况下
bottomIncludeBody = getViewportOffset(headEl).bottomIncludeBody;
} }
if (isBoolean(useSearchForm) && !useSearchForm) {
paddingHeight = 0; return bottomIncludeBody;
} }
const headerCellHeight = /**
(tableEl.querySelector('.ant-table-title') as HTMLElement)?.offsetHeight ?? 0; * table modal modal
* @param tableEl table element
* @returns number
*/
function calcModalHeight(tableEl: Element) {
// 找一下 table 是否在 modal 内,获得 modal、wrap、footer,并考虑 fullscreen 的情况
let modalEl: Nullable<HTMLElement> = null;
let modalWrapEl: Nullable<HTMLElement> = null;
let modalFooterEl: Nullable<HTMLElement> = null;
let modalElIterator: HTMLElement = tableEl.parentElement!;
let modalIsFullscreen = false;
while (modalElIterator !== document.body) {
if (modalElIterator.classList.contains('ant-modal')) {
modalEl = modalElIterator;
modalWrapEl = modalEl.parentElement;
modalFooterEl = modalElIterator.querySelector('.ant-modal-content>.ant-modal-footer');
modalIsFullscreen = modalWrapEl?.classList.contains('fullscreen-modal') ?? false;
break;
}
modalElIterator = modalElIterator.parentElement!;
}
console.log(wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin); if (modalEl) {
bottomIncludeBody = // table 在 modal 内
wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin;
} else { // modal top
// Table height from bottom const { top: modalTop = 0 } = modalEl ? getViewportOffset(modalEl) : {};
bottomIncludeBody = getViewportOffset(headEl).bottomIncludeBody;
// 来自于 .ant-modal,非全屏为 24,全屏为 0
const modalBottom = modalIsFullscreen ? 0 : 24;
// modal footer 高度
const modalFooterHeight = modalFooterEl?.offsetHeight ?? 0;
// modal footer 边距,来自于 .ant-modal .ant-modal-footer
const modalFooterMarginTop = modalFooterEl
? modalIsFullscreen
? 0
: parseInt(getComputedStyle(modalFooterEl).marginTop)
: 0;
// 来自于 .ant-modal .ant-modal-body > .scrollbar
const modalScrollBarHeight = 14;
return (
(modalTop > modalBottom ? modalTop : modalBottom) +
modalFooterHeight +
modalFooterMarginTop +
modalScrollBarHeight
);
}
// table 不住 modal 内
return 0;
}
/**
*
* @returns number
*/
function getMarginPaddingHeight() {
const { isCanResizeParent } = unref(propsRef);
if (unref(wrapRef) && isCanResizeParent) {
// 继承父元素高度
return tableWrapperPadding;
}
return (
tableWrapperPadding + 16 // 来自于 .vben-basic-table-form-container 或是 .p-4
);
}
async function calcTableHeight() {
const { resizeHeightOffset, maxHeight } = unref(propsRef);
const tableData = unref(getDataSourceRef);
const table = unref(tableElRef);
if (!table) return;
const tableEl: Element = table.$el;
if (!tableEl) return;
if (!bodyEl) {
bodyEl = tableEl.querySelector('.ant-table-body');
if (!bodyEl) return;
} }
let height = handleScrollBar(bodyEl, tableEl);
bodyEl!.style.height = 'unset';
if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return;
await nextTick();
// Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
const headEl = tableEl.querySelector('.ant-table-thead ');
if (!headEl) return;
const paginationHeight = caclPaginationHeight(tableEl);
const footerHeight = caclFooterHeight(tableEl);
const headerHeight = calcHeaderHeight(headEl);
const bottomIncludeBody = calcBottomAndPaddingHeight(tableEl, headEl);
const modalHeight = calcModalHeight(tableEl);
const marginPaddingHeight = getMarginPaddingHeight();
// Math.floor 宁愿小1px,也不溢出
let height = Math.floor(
bottomIncludeBody - bottomIncludeBody -
(resizeHeightOffset || 0) - (resizeHeightOffset || 0) -
paddingHeight -
paginationHeight - paginationHeight -
footerHeight - footerHeight -
headerHeight; headerHeight -
// 弹窗(如果有)相关高度
modalHeight -
// 页面 footer 高度(非弹窗的时候)
(getShowFooter.value && modalHeight <= 0 ? layoutFooterHeight : 0) -
// 样式间距高度
marginPaddingHeight -
// 预留非整数高度溢出(如实际高度为100.5,offsetHeight 的值为101)
1,
);
height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
setHeight(height); setHeight(height);
@ -193,7 +334,9 @@ export function useTableScroll(
columns.forEach((item) => { columns.forEach((item) => {
width += Number.parseFloat(item.width as string) || 0; width += Number.parseFloat(item.width as string) || 0;
}); });
const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width')); const unsetWidthColumns = columns.filter(
(item) => !Reflect.has(item, 'width') && item.ifShow !== false,
);
const len = unsetWidthColumns.length; const len = unsetWidthColumns.length;
if (len !== 0) { if (len !== 0) {
@ -213,7 +356,7 @@ export function useTableScroll(
y: canResize ? tableHeight : null, y: canResize ? tableHeight : null,
scrollToFirstRowOnChange: false, scrollToFirstRowOnChange: false,
...scroll, ...scroll,
}; } as BasicTableProps['scroll'];
}); });
return { getScrollRef, redoHeight }; return { getScrollRef, redoHeight };

77
apps/vue/src/components/Table/src/types/table.ts

@ -1,13 +1,20 @@
import type { VNodeChild } from 'vue'; import type { VNodeChild } from 'vue';
import type { PaginationProps } from './pagination'; import type { PaginationProps } from './pagination';
import type { FormProps } from '/@/components/Form'; import type { FormProps } from '/@/components/Form';
import type { ColumnProps } from 'ant-design-vue/lib/table'; import type {
import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface'; TableRowSelection as ITableRowSelection,
Key,
} from 'ant-design-vue/lib/table/interface';
import type { AdvanceSearchProps } from './advancedSearch'; import type { AdvanceSearchProps } from './advancedSearch';
import type { ColumnProps } from 'ant-design-vue/lib/table';
import { ComponentType } from './componentType'; import { ComponentType } from './componentType';
import { VueNode } from '/@/utils/propTypes'; import { VueNode } from '/@/utils/propTypes';
import { RoleEnum } from '/@/enums/roleEnum'; import { RoleEnum } from '/@/enums/roleEnum';
import { FixedType } from 'ant-design-vue/es/vc-table/interface';
import AntDesignVueTable from 'ant-design-vue/es/table';
export declare type SortOrder = 'ascend' | 'descend'; export declare type SortOrder = 'ascend' | 'descend';
@ -18,9 +25,12 @@ export interface TableCurrentDataSource<T = Recordable> {
export interface TableRowSelection<T = any> extends ITableRowSelection { export interface TableRowSelection<T = any> extends ITableRowSelection {
/** /**
* Callback executed when selected rows change * Callback executed when selected rows change
* @type Function * @param selectedRowKeys keyValues
* @param selectedRows records
* @param isClickCustomRow checkbox/radiobox
* @returns void
*/ */
onChange?: (selectedRowKeys: string[] | number[], selectedRows: T[]) => any; onChange?: (selectedRowKeys: Key[], selectedRows: T[], isClickCustomRow?: boolean) => void;
/** /**
* Callback executed when select/deselect one row * Callback executed when select/deselect one row
@ -38,7 +48,7 @@ export interface TableRowSelection<T = any> extends ITableRowSelection {
* Callback executed when row selection is inverted * Callback executed when row selection is inverted
* @type Function * @type Function
*/ */
onSelectInvert?: (selectedRows: string[] | number[]) => any; onSelectInvert?: (selectedRows: Key[]) => any;
} }
export interface TableCustomRecord<T> { export interface TableCustomRecord<T> {
@ -84,22 +94,23 @@ export interface GetColumnsParams {
export type SizeType = 'default' | 'middle' | 'small' | 'large'; export type SizeType = 'default' | 'middle' | 'small' | 'large';
export interface TableActionType { export interface TableActionType {
reload: (opt?: FetchParams) => Promise<Recordable<any>[] | undefined | void>; reload: (opt?: FetchParams) => Promise<Recordable<any>[] | undefined>;
setSelectedRows: (rows: Recordable[]) => void; setSelectedRows: (rows: Recordable[]) => void;
getSelectRows: <T = Recordable>() => T[]; getSelectRows: <T = Recordable>() => T[];
clearSelectedRowKeys: () => void; clearSelectedRowKeys: () => void;
expandAll: () => void; expandAll: () => void;
expandRows: (keys: string[] | number[]) => void;
collapseAll: () => void; collapseAll: () => void;
expandRows: (keyValues: Key[]) => void;
collapseRows: (keyValues: Key[]) => void;
scrollTo: (pos: string) => void; // pos: id | "top" | "bottom" scrollTo: (pos: string) => void; // pos: id | "top" | "bottom"
getSelectRowKeys: () => string[]; getSelectRowKeys: () => Key[];
deleteSelectRowByKey: (key: string) => void; deleteSelectRowByKey: (keyValue: Key) => void;
setPagination: (info: Partial<PaginationProps>) => void; setPagination: (info: Partial<PaginationProps>) => void;
setTableData: <T = Recordable>(values: T[]) => void; setTableData: <T = Recordable>(values: T[]) => void;
updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void; updateTableDataRecord: (keyValue: Key, record: Recordable) => Recordable | void;
deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => void; deleteTableDataRecord: (keyValues: Key | Key[]) => void;
insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable[] | void; insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable[] | void;
findTableDataRecord: (rowKey: string | number) => Recordable | void; findTableDataRecord: (keyValue: Key) => Recordable | void;
getColumns: (opt?: GetColumnsParams) => BasicColumn[]; getColumns: (opt?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void; setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: <T = Recordable>() => T[]; getDataSource: <T = Recordable>() => T[];
@ -107,7 +118,7 @@ export interface TableActionType {
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
setProps: (props: Partial<BasicTableProps>) => void; setProps: (props: Partial<BasicTableProps>) => void;
redoHeight: () => void; redoHeight: () => void;
setSelectedRowKeys: (rowKeys: string[] | number[]) => void; setSelectedRowKeys: (keyValues: Key[]) => void;
getPaginationRef: () => PaginationProps | boolean; getPaginationRef: () => PaginationProps | boolean;
getSize: () => SizeType; getSize: () => SizeType;
getRowSelection: () => TableRowSelection<Recordable>; getRowSelection: () => TableRowSelection<Recordable>;
@ -117,6 +128,7 @@ export interface TableActionType {
setShowPagination: (show: boolean) => Promise<void>; setShowPagination: (show: boolean) => Promise<void>;
getShowPagination: () => boolean; getShowPagination: () => boolean;
setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void; setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void;
setCacheColumns?: (columns: BasicColumn[]) => void;
} }
export interface FetchSetting { export interface FetchSetting {
@ -133,15 +145,17 @@ export interface FetchSetting {
export interface TableSetting { export interface TableSetting {
redo?: boolean; redo?: boolean;
size?: boolean; size?: boolean;
export?: boolean;
setting?: boolean; setting?: boolean;
settingCache?: boolean;
fullScreen?: boolean; fullScreen?: boolean;
export?: boolean;
} }
export interface BasicTableProps<T = any> { export interface BasicTableProps<T = any> {
// 点击行选中 // 点击行选中
clickToRowSelect?: boolean; clickToRowSelect?: boolean;
isTreeTable?: boolean; isTreeTable?: boolean;
accordion?: boolean; // isTreeTable 或 expandRowByClick 时支持
// 自定义排序方法 // 自定义排序方法
sortFn?: (sortInfo: SorterResult) => any; sortFn?: (sortInfo: SorterResult) => any;
// 排序方法 // 排序方法
@ -210,7 +224,7 @@ export interface BasicTableProps<T = any> {
// 在分页改变的时候清空选项 // 在分页改变的时候清空选项
clearSelectOnPageChange?: boolean; clearSelectOnPageChange?: boolean;
// //
rowKey?: string | ((record: Recordable) => string); rowKey?: InstanceType<typeof AntDesignVueTable>['$props']['rowKey'];
// 数据 // 数据
dataSource?: Recordable[]; dataSource?: Recordable[];
// 标题右侧提示 // 标题右侧提示
@ -312,13 +326,19 @@ export interface BasicTableProps<T = any> {
*/ */
rowSelection?: TableRowSelection; rowSelection?: TableRowSelection;
/**
* Show table selection bar
* @type boolean
*/
showSelectionBar?: boolean;
/** /**
* Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area. * Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area.
* It is recommended to set a number for x, if you want to set it to true, * It is recommended to set a number for x, if you want to set it to true,
* you need to add style .ant-table td { white-space: nowrap; }. * you need to add style .ant-table td { white-space: nowrap; }.
* @type object * @type object
*/ */
scroll?: { x?: string | number | true; y?: string | number }; scroll?: InstanceType<typeof AntDesignVueTable>['$props']['scroll'];
/** /**
* Whether to show table header * Whether to show table header
@ -385,7 +405,7 @@ export interface BasicTableProps<T = any> {
beforeEditSubmit?: (data: { beforeEditSubmit?: (data: {
record: Recordable; record: Recordable;
index: number; index: number;
key: string | number; key: Key;
value: any; value: any;
}) => Promise<any>; }) => Promise<any>;
@ -404,7 +424,7 @@ export interface BasicTableProps<T = any> {
* @param expanded * @param expanded
* @param record * @param record
*/ */
onExpand?: (expande: boolean, record: T) => void; onExpand?: (expanded: boolean, record: T) => void;
/** /**
* Callback executed when the expanded rows change * Callback executed when the expanded rows change
@ -437,11 +457,13 @@ export interface BasicColumn extends ColumnProps<Recordable> {
slots?: Recordable; slots?: Recordable;
// 自定义header渲染
customHeaderRender?: (column: BasicColumn) => string | VNodeChild | JSX.Element;
// Whether to hide the column by default, it can be displayed in the column configuration // Whether to hide the column by default, it can be displayed in the column configuration
defaultHidden?: boolean; defaultHidden?: boolean;
// Help text for table column header // Help text for table column header
helpMessage?: string | string[]; helpMessage?: string | string[] | VNodeChild | JSX.Element;
format?: CellFormat; format?: CellFormat;
@ -471,6 +493,7 @@ export interface BasicColumn extends ColumnProps<Recordable> {
record: Recordable; record: Recordable;
column: BasicColumn; column: BasicColumn;
index: number; index: number;
currentValue: string | number | boolean | Recordable;
}) => VNodeChild | JSX.Element; }) => VNodeChild | JSX.Element;
// 动态 Disabled // 动态 Disabled
editDynamicDisabled?: boolean | ((record: Recordable) => boolean); editDynamicDisabled?: boolean | ((record: Recordable) => boolean);
@ -486,3 +509,19 @@ export interface InnerHandlers {
onColumnsChange: (data: ColumnChangeParam[]) => void; onColumnsChange: (data: ColumnChangeParam[]) => void;
deSelect: () => void; deSelect: () => void;
} }
export interface InnerMethods {
clearSelectedRowKeys: TableActionType['clearSelectedRowKeys'];
getSelectRowKeys: TableActionType['getSelectRowKeys'];
}
export interface ColumnOptionsType {
value: string;
label: string;
//
column: {
defaultHidden?: boolean;
};
//
fixed?: FixedType;
}

3
apps/vue/src/enums/cacheEnum.ts

@ -30,6 +30,9 @@ export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
// base global session key // base global session key
export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__'; export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
// table 列设置
export const TABLE_SETTING_KEY = 'TABLE__SETTING__KEY__';
export enum CacheTypeEnum { export enum CacheTypeEnum {
SESSION, SESSION,
LOCAL, LOCAL,

27
apps/vue/src/hooks/web/useSignalR.ts

@ -10,7 +10,7 @@ import { useUserStoreWithOut } from '/@/store/modules/user';
import mitt from '/@/utils/mitt'; import mitt from '/@/utils/mitt';
interface UseSignalR { interface SignalROptions {
serverUrl: string; serverUrl: string;
autoStart?: boolean; autoStart?: boolean;
useAccessToken?: boolean; useAccessToken?: boolean;
@ -18,18 +18,16 @@ interface UseSignalR {
nextRetryDelayInMilliseconds?: number; nextRetryDelayInMilliseconds?: number;
} }
export function useSignalR({ interface UseSignalR {
serverUrl, lazyInit?: boolean;
autoStart = false, }
useAccessToken = true,
automaticReconnect = true, export function useSignalR(options: UseSignalR & SignalROptions) {
nextRetryDelayInMilliseconds = 60000,
}: UseSignalR) {
const emitter = mitt(); const emitter = mitt();
let connection: HubConnection | null = null; let connection: HubConnection | null = null;
onMounted(() => { onMounted(() => {
_initlizaConnection(); !options.lazyInit && init(options);
}); });
onUnmounted(() => { onUnmounted(() => {
@ -38,7 +36,13 @@ export function useSignalR({
} }
}); });
function _initlizaConnection() { function init({
serverUrl,
autoStart = false,
useAccessToken = true,
automaticReconnect = true,
nextRetryDelayInMilliseconds = 60000,
}: SignalROptions) {
const httpOptions: IHttpConnectionOptions = {}; const httpOptions: IHttpConnectionOptions = {};
if (useAccessToken) { if (useAccessToken) {
const userStore = useUserStoreWithOut(); const userStore = useUserStoreWithOut();
@ -49,7 +53,7 @@ export function useSignalR({
var connectionBuilder = new HubConnectionBuilder() var connectionBuilder = new HubConnectionBuilder()
.withUrl(serverUrl, httpOptions) .withUrl(serverUrl, httpOptions)
.configureLogging(LogLevel.Warning); .configureLogging(LogLevel.Warning);
if (automaticReconnect) { if (automaticReconnect && nextRetryDelayInMilliseconds) {
connectionBuilder.withAutomaticReconnect({ connectionBuilder.withAutomaticReconnect({
nextRetryDelayInMilliseconds: () => nextRetryDelayInMilliseconds, nextRetryDelayInMilliseconds: () => nextRetryDelayInMilliseconds,
}); });
@ -123,6 +127,7 @@ export function useSignalR({
return { return {
on, on,
off, off,
init,
onclose, onclose,
beforeStart, beforeStart,
onStart, onStart,

9
apps/vue/src/settings/designSetting.ts

@ -2,8 +2,17 @@ import { ThemeEnum } from '../enums/appEnum';
export const prefixCls = 'vben'; export const prefixCls = 'vben';
export const multipleTabHeight = 30;
export const darkMode = ThemeEnum.LIGHT; export const darkMode = ThemeEnum.LIGHT;
// 页脚固定高度
export const footerHeight = 75;
// .@{namespace}-layout-multiple-header__placeholder
// 全屏页头动画时长
export const layoutMultipleHeadePlaceholderTime = 0.6;
// app theme preset color // app theme preset color
export const APP_PRESET_COLOR_LIST: string[] = [ export const APP_PRESET_COLOR_LIST: string[] = [
'#0960bd', '#0960bd',

126
apps/vue/src/store/modules/tableSetting.ts

@ -0,0 +1,126 @@
import { defineStore } from 'pinia';
import { TABLE_SETTING_KEY } from '/@/enums/cacheEnum';
import { Persistent } from '/@/utils/cache/persistent';
import type { TableSetting } from '/#/store';
import type { SizeType, ColumnOptionsType } from '/@/components/Table/src/types/table';
interface TableSettingState {
setting: Nullable<Partial<TableSetting>>;
}
export const useTableSettingStore = defineStore({
id: 'table-setting',
state: (): TableSettingState => ({
setting: Persistent.getLocal(TABLE_SETTING_KEY),
}),
getters: {
getTableSetting(state): Nullable<Partial<TableSetting>> {
return state.setting;
},
//
getTableSize(state) {
return state.setting?.size || 'middle';
},
//
getShowIndexColumn(state) {
return (routerName: string) => {
return state.setting?.showIndexColumn?.[routerName];
};
},
//
getShowRowSelection(state) {
return (routerName: string) => {
return state.setting?.showRowSelection?.[routerName];
};
},
//
getAllowResizeColumn(state) {
return (routerName: string) => {
return state.setting?.allowResizeColumn?.[routerName];
};
},
//
getColumns(state) {
return (routerName: string) => {
return state.setting?.columns && state.setting?.columns[routerName]
? state.setting?.columns[routerName]
: null;
};
},
},
actions: {
setTableSetting(setting: Partial<TableSetting>) {
this.setting = Object.assign({}, this.setting, setting);
Persistent.setLocal(TABLE_SETTING_KEY, this.setting, true);
},
resetTableSetting() {
Persistent.removeLocal(TABLE_SETTING_KEY, true);
this.setting = null;
},
//
setTableSize(size: SizeType) {
this.setTableSetting(
Object.assign({}, this.setting, {
size,
}),
);
},
//
setShowIndexColumn(routerName: string, show: boolean) {
this.setTableSetting(
Object.assign({}, this.setting, {
showIndexColumn: {
...this.setting?.showIndexColumn,
[routerName]: show,
},
}),
);
},
//
setAllowResizeColumn(routerName: string, resize: boolean) {
this.setTableSetting(
Object.assign({}, this.setting, {
allowResizeColumn: {
...this.setting?.allowResizeColumn,
[routerName]: resize,
},
}),
);
},
//
setShowRowSelection(routerName: string, show: boolean) {
this.setTableSetting(
Object.assign({}, this.setting, {
showRowSelection: {
...this.setting?.showRowSelection,
[routerName]: show,
},
}),
);
},
//
setColumns(routerName: string, columns: Array<ColumnOptionsType>) {
this.setTableSetting(
Object.assign({}, this.setting, {
columns: {
...this.setting?.columns,
[routerName]: columns,
},
}),
);
},
clearColumns(routerName: string) {
this.setTableSetting(
Object.assign({}, this.setting, {
columns: {
...this.setting?.columns,
[routerName]: undefined,
},
}),
);
},
},
});

4
apps/vue/src/utils/cache/persistent.ts

@ -1,4 +1,4 @@
import type { LockInfo, UserInfo } from '/#/store'; import type { LockInfo, UserInfo, TableSetting } from '/#/store';
import type { ProjectConfig } from '/#/config'; import type { ProjectConfig } from '/#/config';
import type { RouteLocationNormalized } from 'vue-router'; import type { RouteLocationNormalized } from 'vue-router';
@ -13,6 +13,7 @@ import {
APP_LOCAL_CACHE_KEY, APP_LOCAL_CACHE_KEY,
APP_SESSION_CACHE_KEY, APP_SESSION_CACHE_KEY,
MULTIPLE_TABS_KEY, MULTIPLE_TABS_KEY,
TABLE_SETTING_KEY,
} from '/@/enums/cacheEnum'; } from '/@/enums/cacheEnum';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
import { toRaw } from 'vue'; import { toRaw } from 'vue';
@ -25,6 +26,7 @@ interface BasicStore {
[LOCK_INFO_KEY]: LockInfo; [LOCK_INFO_KEY]: LockInfo;
[PROJ_CFG_KEY]: ProjectConfig; [PROJ_CFG_KEY]: ProjectConfig;
[MULTIPLE_TABS_KEY]: RouteLocationNormalized[]; [MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
[TABLE_SETTING_KEY]: Partial<TableSetting>;
} }
type LocalStore = BasicStore; type LocalStore = BasicStore;

112
apps/vue/src/utils/is.ts

@ -1,3 +1,41 @@
import { isNull, isFunction } from 'lodash-es';
export { isNull, isFunction };
export {
isArguments,
isArrayBuffer,
isArrayLike,
isArrayLikeObject,
isBuffer,
isBoolean,
isDate,
isElement,
isEmpty,
isEqual,
isEqualWith,
isError,
isFinite,
isLength,
isMap,
isMatch,
isMatchWith,
isNative,
isNil,
isNumber,
isObjectLike,
isPlainObject,
isRegExp,
isSafeInteger,
isSet,
isString,
isSymbol,
isTypedArray,
isUndefined,
isWeakMap,
isWeakSet,
} from 'lodash-es';
const toString = Object.prototype.toString; const toString = Object.prototype.toString;
export function is(val: unknown, type: string) { export function is(val: unknown, type: string) {
@ -16,28 +54,8 @@ export function isObject(val: any): val is Record<any, any> {
return val !== null && is(val, 'Object'); return val !== null && is(val, 'Object');
} }
export function isEmpty<T = unknown>(val: T): val is T { export function isRegMatch(reg: RegExp, val: string) {
if (isArray(val) || isString(val)) { return reg.test(val);
return val.length === 0;
}
if (val instanceof Map || val instanceof Set) {
return val.size === 0;
}
if (isObject(val)) {
return Object.keys(val).length === 0;
}
return false;
}
export function isDate(val: unknown): val is Date {
return is(val, 'Date');
}
export function isNull(val: unknown): val is null {
return val === null;
} }
export function isNullAndUnDef(val: unknown): val is null | undefined { export function isNullAndUnDef(val: unknown): val is null | undefined {
@ -48,30 +66,10 @@ export function isNullOrUnDef(val: unknown): val is null | undefined {
return isUnDef(val) || isNull(val); return isUnDef(val) || isNull(val);
} }
export function isNumber(val: unknown): val is number {
return is(val, 'Number');
}
export function isPromise<T = any>(val: unknown): val is Promise<T> { export function isPromise<T = any>(val: unknown): val is Promise<T> {
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch); return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch);
} }
export function isString(val: unknown): val is string {
return is(val, 'String');
}
export function isFunction(val: unknown): val is Function {
return typeof val === 'function';
}
export function isBoolean(val: unknown): val is boolean {
return is(val, 'Boolean');
}
export function isRegExp(val: unknown): val is RegExp {
return is(val, 'RegExp');
}
export function isArray(val: any): val is Array<any> { export function isArray(val: any): val is Array<any> {
return val && Array.isArray(val); return val && Array.isArray(val);
} }
@ -80,47 +78,35 @@ export function isWindow(val: any): val is Window {
return typeof window !== 'undefined' && is(val, 'Window'); return typeof window !== 'undefined' && is(val, 'Window');
} }
export function isElement(val: unknown): val is Element {
return isObject(val) && !!val.tagName;
}
export function isMap(val: unknown): val is Map<any, any> {
return is(val, 'Map');
}
export const isServer = typeof window === 'undefined'; export const isServer = typeof window === 'undefined';
export const isClient = !isServer; export const isClient = !isServer;
export function isMatch(reg: RegExp, val: string) {
return reg.test(val);
}
export function isEmail(val: string) { export function isEmail(val: string) {
const reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/; const reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
return isMatch(reg, val); return isRegMatch(reg, val);
} }
export function isPhone(val: string) { export function isPhone(val: string) {
const reg = /^(13[0-9]|14[5|7]|15[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$/; const reg = /^(13[0-9]|14[5|7]|15[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$/;
return isMatch(reg, val); return isRegMatch(reg, val);
} }
export function isUrl(path: string): boolean { export function isUrl(path: string): boolean {
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/; const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
return isMatch(reg, path); return isRegMatch(reg, path);
} }
export function isSortUrl(path: string) { export function isSortUrl(path: string) {
const reg1 = /^[a-zA-Z]+:\d{1,5}$/; const reg1 = /^[a-zA-Z]+:\d{1,5}$/;
const reg2 = /^([a-zA-Z\-\d+]+\.){1,}[a-z\-\d]+:\d{1,5}$/; const reg2 = /^([a-zA-Z\-\d+]+\.){1,}[a-z\-\d]+:\d{1,5}$/;
return isMatch(reg1, path) || isMatch(reg2, path); return isRegMatch(reg1, path) || isRegMatch(reg2, path);
} }
export function isIpPort(path: string): boolean { export function isIpPort(path: string): boolean {
const reg = const reg =
/^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])(\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){3}:([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/; /^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])(\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){3}:([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/;
return isMatch(reg, path); return isRegMatch(reg, path);
} }
export function isDigit(val: string) { export function isDigit(val: string) {
@ -141,18 +127,18 @@ export function isLetterOrDigit(val: string) {
} }
function _isDigit(char: string) { function _isDigit(char: string) {
return isMatch(/[0-9]/g, char); return isRegMatch(/[0-9]/g, char);
} }
function _isLower(char: string) { function _isLower(char: string) {
return isMatch(/[a-z]/g, char); return isRegMatch(/[a-z]/g, char);
} }
function _isUpper(char: string) { function _isUpper(char: string) {
return isMatch(/[A-Z]/g, char); return isRegMatch(/[A-Z]/g, char);
} }
function _isLetterOrDigit(char: string) { function _isLetterOrDigit(char: string) {
return isMatch(/[^ A-Za-z0-9_]/g, char); return isRegMatch(/[^ A-Za-z0-9_]/g, char);
} }

2
apps/vue/src/views/account/center/Cloud.vue

@ -32,7 +32,7 @@
import { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; import { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { usePermission } from '/@/hooks/web/usePermission'; import { usePermission } from '/@/hooks/web/usePermission';
import { OssObject } from '/@/api/oss-management/model/ossModel'; import { OssObject } from '/@/api/oss-management/objects/model';
import FileList from './FileList.vue'; import FileList from './FileList.vue';
import ShareList from './ShareList.vue'; import ShareList from './ShareList.vue';

2
apps/vue/src/views/account/center/FileList.vue

@ -42,7 +42,7 @@
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { getDataColumns } from './data'; import { getDataColumns } from './data';
import { OssObject } from '/@/api/oss-management/model/ossModel'; import { OssObject } from '/@/api/oss-management/objects/model';
import { getList as getPrivates } from '/@/api/oss-management/files/private'; import { getList as getPrivates } from '/@/api/oss-management/files/private';
import { getList as getPublices } from '/@/api/oss-management/files/public'; import { getList as getPublices } from '/@/api/oss-management/files/public';
import { generateOssUrl, deleteObject } from '/@/api/oss-management/objects'; import { generateOssUrl, deleteObject } from '/@/api/oss-management/objects';

18
apps/vue/src/views/oss-management/objects/components/FileList.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="file-list-wrap"> <div class="file-list-wrap">
<BasicTable @register="registerTable"> <BasicTable @register="registerTable" @selection-change="handleSelectRows">
<template #toolbar> <template #toolbar>
<Button <Button
v-if="hasPermission('AbpOssManagement.OssObject.Create')" v-if="hasPermission('AbpOssManagement.OssObject.Create')"
@ -54,7 +54,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch, nextTick } from 'vue'; import { computed, watch, nextTick, ref } from 'vue';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
@ -88,7 +88,7 @@
const { L } = useLocalization(['AbpOssManagement', 'AbpUi']); const { L } = useLocalization(['AbpOssManagement', 'AbpUi']);
const [registerUploadModal, { openModal: openUploadModal }] = useModal(); const [registerUploadModal, { openModal: openUploadModal }] = useModal();
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
const [registerTable, { reload, getSelectRowKeys, setTableData, setPagination }] = useTable({ const [registerTable, { reload, setTableData, setPagination }] = useTable({
rowKey: 'name', rowKey: 'name',
title: L('DisplayName:OssObject'), title: L('DisplayName:OssObject'),
columns: getDataColumns(), columns: getDataColumns(),
@ -119,8 +119,9 @@
dataIndex: 'action', dataIndex: 'action',
}, },
}); });
const selectionKeys = ref<string[]>([]);
const selectCount = computed(() => { const selectCount = computed(() => {
return getSelectRowKeys().length; return selectionKeys.value.length;
}); });
watch( watch(
@ -147,6 +148,10 @@
}, },
); );
function handleSelectRows(e: { keys: string[] }) {
selectionKeys.value = e.keys;
}
function beforeFetch(request: any) { function beforeFetch(request: any) {
request.bucket = props.bucket; request.bucket = props.bucket;
request.prefix = props.path; request.prefix = props.path;
@ -180,15 +185,14 @@
onOk: async () => { onOk: async () => {
const bucket = props.bucket; const bucket = props.bucket;
const path = props.path; const path = props.path;
const objects = getSelectRowKeys();
await bulkDeleteObject({ await bulkDeleteObject({
bucket: bucket, bucket: bucket,
path: path, path: path,
objects: objects, objects: selectionKeys.value,
}); });
createMessage.success(L('SuccessfullyDeleted')); createMessage.success(L('SuccessfullyDeleted'));
await reload(); await reload();
emits('oss:delete', bucket, path, objects); emits('oss:delete', bucket, path, selectionKeys.value);
}, },
}); });
} }

16
apps/vue/src/views/sys/login/TwoFactorLoginForm.vue

@ -23,7 +23,7 @@
</FormItem> </FormItem>
<template v-if="formData.type === 'Email'"> <template v-if="formData.type === 'Email'">
<FormItem <FormItem
name="email" name="emailAddress"
class="enter-x" class="enter-x"
:label="L('DisplayName:EmailAddress')" :label="L('DisplayName:EmailAddress')"
:rules="[ :rules="[
@ -43,7 +43,7 @@
size="large" size="large"
class="fix-auto-fill" class="fix-auto-fill"
autocomplete="off" autocomplete="off"
v-model:value="formData.email" v-model:value="formData.emailAddress"
/> />
</FormItem> </FormItem>
<FormItem name="code" class="enter-x" :label="L('DisplayName:EmailVerifyCode')" required> <FormItem name="code" class="enter-x" :label="L('DisplayName:EmailVerifyCode')" required>
@ -52,7 +52,7 @@
class="fix-auto-fill" class="fix-auto-fill"
autocomplete="off" autocomplete="off"
v-model:value="formData.code" v-model:value="formData.code"
:sendCodeApi="() => handleSendCode('email', sendEmailSigninCode)" :sendCodeApi="() => handleSendCode('emailAddress', sendEmailSigninCode)"
/> />
</FormItem> </FormItem>
</template> </template>
@ -78,7 +78,7 @@
size="large" size="large"
class="fix-auto-fill" class="fix-auto-fill"
autocomplete="off" autocomplete="off"
v-model:value="formData.email" v-model:value="formData.emailAddress"
/> />
</FormItem> </FormItem>
<FormItem name="code" class="enter-x" :label="L('DisplayName:SmsVerifyCode')" required> <FormItem name="code" class="enter-x" :label="L('DisplayName:SmsVerifyCode')" required>
@ -152,7 +152,7 @@
const formData = reactive({ const formData = reactive({
type: '', type: '',
email: '', emailAddress: '',
phoneNumber: '', phoneNumber: '',
code: '', code: '',
}); });
@ -166,14 +166,16 @@
function handleResetValidate() { function handleResetValidate() {
formRef.value?.clearValidate(); formRef.value?.clearValidate();
formData.code = ''; formData.code = '';
formData.email = ''; formData.emailAddress = '';
formData.phoneNumber = ''; formData.phoneNumber = '';
} }
function handleSendCode(field: string, sendCodeApi: (...args) => Promise<void>) { function handleSendCode(field: string, sendCodeApi: (...args) => Promise<void>) {
return validFormFields([field]) return validFormFields([field])
.then(() => { .then(() => {
return sendCodeApi(formData[field]) return sendCodeApi({
[field]: formData[field],
})
.then(() => { .then(() => {
return Promise.resolve(true); return Promise.resolve(true);
}) })

15
apps/vue/src/views/task-management/background-jobs/components/JobTable.vue

@ -149,7 +149,7 @@
const { L } = useLocalization(['TaskManagement', 'AbpUi']); const { L } = useLocalization(['TaskManagement', 'AbpUi']);
const { hasPermission } = usePermission(); const { hasPermission } = usePermission();
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ const [registerTable, { reload, clearSelectedRowKeys }] = useTable({
rowKey: 'id', rowKey: 'id',
title: L('BackgroundJobs'), title: L('BackgroundJobs'),
columns: getDataColumns(), columns: getDataColumns(),
@ -182,7 +182,7 @@
}); });
const selectedRowKeys = ref<string[]>([]); const selectedRowKeys = ref<string[]>([]);
const isMultiSelected = computed(() => { const isMultiSelected = computed(() => {
return selectedRowKeys.value.length > 0; return selectedRowKeys.value.length;
}); });
function handleSelectChange(keys) { function handleSelectChange(keys) {
@ -223,24 +223,21 @@
} }
function handleStart() { function handleStart() {
const selectKeys = getSelectRowKeys(); bulkStart(selectedRowKeys.value).then(() => {
bulkStart(selectKeys).then(() => {
createMessage.success(L('Successful')); createMessage.success(L('Successful'));
reloadTable(); reloadTable();
}); });
} }
function handleStop() { function handleStop() {
const selectKeys = getSelectRowKeys(); bulkStop(selectedRowKeys.value).then(() => {
bulkStop(selectKeys).then(() => {
createMessage.success(L('Successful')); createMessage.success(L('Successful'));
reloadTable(); reloadTable();
}); });
} }
function handleDeleteMany() { function handleDeleteMany() {
const selectKeys = getSelectRowKeys(); if (selectedRowKeys.value.length <= 0) {
if (selectKeys.length <= 0) {
return; return;
} }
createConfirm({ createConfirm({
@ -249,7 +246,7 @@
content: L('MultipleSelectJobsWillBeDeletedMessage'), content: L('MultipleSelectJobsWillBeDeletedMessage'),
okCancel: true, okCancel: true,
onOk: () => { onOk: () => {
return bulkDelete(selectKeys).then(() => { return bulkDelete(selectedRowKeys.value).then(() => {
reloadTable(); reloadTable();
}); });
}, },

20
apps/vue/src/views/webhooks/send-attempts/components/SendAttemptTable.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="content"> <div class="content">
<BasicTable @register="registerTable"> <BasicTable @register="registerTable" @selection-change="handleSelectionChange">
<template #toolbar> <template #toolbar>
<Button <Button
v-if="isManyRecordSelected" v-if="isManyRecordSelected"
@ -59,7 +59,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import { Button, Tag } from 'ant-design-vue'; import { Button, Tag } from 'ant-design-vue';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
@ -78,10 +78,11 @@
} from '/@/api/webhooks/send-attempts'; } from '/@/api/webhooks/send-attempts';
import SendAttemptModal from './SendAttemptModal.vue'; import SendAttemptModal from './SendAttemptModal.vue';
const selectionKeys = ref<string[]>([]);
const { createConfirm, createMessage } = useMessage(); const { createConfirm, createMessage } = useMessage();
const { L } = useLocalization(['WebhooksManagement', 'AbpUi']); const { L } = useLocalization(['WebhooksManagement', 'AbpUi']);
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, setLoading, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ const [registerTable, { reload, setLoading, clearSelectedRowKeys }] = useTable({
rowKey: 'id', rowKey: 'id',
title: L('SendAttempts'), title: L('SendAttempts'),
columns: getDataColumns(), columns: getDataColumns(),
@ -107,10 +108,13 @@
}, },
}); });
const isManyRecordSelected = computed(() => { const isManyRecordSelected = computed(() => {
const selectedKeys = getSelectRowKeys(); return selectionKeys.value.length;
return selectedKeys.length > 0;
}); });
function handleSelectionChange(e: { keys: string[] }) {
selectionKeys.value = e.keys;
}
function handleEdit(record) { function handleEdit(record) {
openModal(true, record); openModal(true, record);
} }
@ -122,10 +126,9 @@
content: L('ItemWillBeDeletedMessageWithFormat', { 0: L('SelectedItems') }), content: L('ItemWillBeDeletedMessageWithFormat', { 0: L('SelectedItems') }),
okCancel: true, okCancel: true,
onOk: () => { onOk: () => {
const selectKeys = getSelectRowKeys();
setLoading(true); setLoading(true);
return DeleteManyAsyncByInput({ return DeleteManyAsyncByInput({
recordIds: selectKeys, recordIds: selectionKeys.value,
}) })
.then(() => { .then(() => {
createMessage.success(L('SuccessfullyDeleted')); createMessage.success(L('SuccessfullyDeleted'));
@ -188,10 +191,9 @@
content: L('ItemWillBeResendMessageWithFormat', { 0: L('SelectedItems') }), content: L('ItemWillBeResendMessageWithFormat', { 0: L('SelectedItems') }),
okCancel: true, okCancel: true,
onOk: () => { onOk: () => {
const selectKeys = getSelectRowKeys();
setLoading(true); setLoading(true);
return ResendManyAsyncByInput({ return ResendManyAsyncByInput({
recordIds: selectKeys, recordIds: selectionKeys.value,
}) })
.then(() => { .then(() => {
createMessage.success(L('Successful')); createMessage.success(L('Successful'));

17
apps/vue/src/views/webhooks/subscriptions/components/SubscriptionTable.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="content"> <div class="content">
<BasicTable @register="registerTable"> <BasicTable @register="registerTable" @selection-change="handleSelectionChange">
<template #toolbar> <template #toolbar>
<Button v-auth="['AbpWebhooks.Subscriptions.Create']" type="primary" @click="handleAddNew"> <Button v-auth="['AbpWebhooks.Subscriptions.Create']" type="primary" @click="handleAddNew">
{{ L('Subscriptions:AddNew') }} {{ L('Subscriptions:AddNew') }}
@ -49,7 +49,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import { Button, Tag } from 'ant-design-vue'; import { Button, Tag } from 'ant-design-vue';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue'; import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
@ -69,7 +69,7 @@
const { createConfirm, createMessage } = useMessage(); const { createConfirm, createMessage } = useMessage();
const { L } = useLocalization(['WebhooksManagement', 'AbpUi']); const { L } = useLocalization(['WebhooksManagement', 'AbpUi']);
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, setLoading, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ const [registerTable, { reload, setLoading, clearSelectedRowKeys }] = useTable({
rowKey: 'id', rowKey: 'id',
title: L('Subscriptions'), title: L('Subscriptions'),
columns: getDataColumns(), columns: getDataColumns(),
@ -94,11 +94,15 @@
type: 'checkbox', type: 'checkbox',
}, },
}); });
const selectionKeys = ref<string[]>([]);
const deleteManyEnabled = computed(() => { const deleteManyEnabled = computed(() => {
const selectKeys = getSelectRowKeys(); return selectionKeys.value.length;
return selectKeys.length > 0;
}); });
function handleSelectionChange(e: { keys: string[] }) {
selectionKeys.value = e.keys;
}
function handleAddNew() { function handleAddNew() {
openModal(true, { id: null }); openModal(true, { id: null });
} }
@ -114,10 +118,9 @@
content: L('ItemWillBeDeletedMessageWithFormat', { 0: L('SelectedItems') }), content: L('ItemWillBeDeletedMessageWithFormat', { 0: L('SelectedItems') }),
okCancel: true, okCancel: true,
onOk: () => { onOk: () => {
const selectKeys = getSelectRowKeys();
setLoading(true); setLoading(true);
return DeleteManyAsyncByInput({ return DeleteManyAsyncByInput({
recordIds: selectKeys, recordIds: selectionKeys.value,
}) })
.then(() => { .then(() => {
createMessage.success(L('SuccessfullyDeleted')); createMessage.success(L('SuccessfullyDeleted'));

8
apps/vue/types/store.d.ts

@ -50,3 +50,11 @@ export interface BeforeMiniState {
menuMode?: MenuModeEnum; menuMode?: MenuModeEnum;
menuType?: MenuTypeEnum; menuType?: MenuTypeEnum;
} }
export interface TableSetting {
size: Nullable<SizeType>;
showIndexColumn: Recordable<Nullable<boolean>>;
columns: Recordable<Nullable<Array<ColumnOptionsType>>>;
showRowSelection: Recordable<Nullable<boolean>>;
allowResizeColumn: Recordable<Nullable<boolean>>;
}

22
aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/HttpOverrides/AbpAspNetCoreHttpOverridesModule.cs

@ -1,25 +1,27 @@
using LINGYUN.Abp.AspNetCore.HttpOverrides.Forwarded; using LINGYUN.Abp.AspNetCore.WebClientInfo;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Collections.Generic;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace LINGYUN.Abp.AspNetCore.HttpOverrides namespace LINGYUN.Abp.AspNetCore.HttpOverrides;
[DependsOn(typeof(AbpAspNetCoreModule))]
public class AbpAspNetCoreHttpOverridesModule : AbpModule
{ {
public class AbpAspNetCoreHttpOverridesModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
var forwardOptions = new AbpForwardedHeadersOptions();
configuration.GetSection("Forwarded:Headers").Bind(forwardOptions);
context.Services.ExecutePreConfiguredActions(forwardOptions);
Configure<ForwardedHeadersOptions>(options => Configure<ForwardedHeadersOptions>(options =>
{ {
options.Configure(forwardOptions); configuration.GetSection("Forwarded").Bind(options);
}); });
}
context.Services.Replace(ServiceDescriptor.Transient<IWebClientInfoProvider, RequestForwardedHeaderWebClientInfoProvider>());
} }
} }

36
aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/HttpOverrides/Forwarded/AbpForwardedHeadersOptions.cs

@ -1,36 +0,0 @@
using Microsoft.AspNetCore.HttpOverrides;
using System.Collections.Generic;
namespace LINGYUN.Abp.AspNetCore.HttpOverrides.Forwarded
{
public class AbpForwardedHeadersOptions
{
public string ForwardedForHeaderName { get; set; }
public string ForwardedHostHeaderName { get; set; }
public string ForwardedProtoHeaderName { get; set; }
public string OriginalForHeaderName { get; set; }
public string OriginalHostHeaderName { get; set; }
public string OriginalProtoHeaderName { get; set; }
public ForwardedHeaders ForwardedHeaders { get; set; }
public int? ForwardLimit { get; set; }
public bool RequireHeaderSymmetry { get; set; }
public List<string> AllowedHosts { get; set; }
public List<string> KnownNetworks { get; set; }
public List<string> KnownProxies { get; set; }
public AbpForwardedHeadersOptions()
{
ForwardedForHeaderName = ForwardedHeadersDefaults.XForwardedForHeaderName;
ForwardedHostHeaderName = ForwardedHeadersDefaults.XForwardedHostHeaderName;
ForwardedProtoHeaderName = ForwardedHeadersDefaults.XForwardedProtoHeaderName;
OriginalForHeaderName = ForwardedHeadersDefaults.XOriginalForHeaderName;
OriginalHostHeaderName = ForwardedHeadersDefaults.XOriginalHostHeaderName;
OriginalProtoHeaderName = ForwardedHeadersDefaults.XOriginalProtoHeaderName;
ForwardedHeaders = ForwardedHeaders.None;
ForwardLimit = 1;
RequireHeaderSymmetry = false;
AllowedHosts = new List<string>();
KnownProxies = new List<string>();
KnownNetworks = new List<string>();
}
}
}

27
aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/LINGYUN/Abp/AspNetCore/WebClientInfo/RequestForwardedHeaderWebClientInfoProvider.cs

@ -1,21 +1,17 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using System;
using System.Linq; using System.Linq;
using Volo.Abp.AspNetCore.WebClientInfo; using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.AspNetCore.WebClientInfo namespace LINGYUN.Abp.AspNetCore.WebClientInfo;
public class RequestForwardedHeaderWebClientInfoProvider : HttpContextWebClientInfoProvider
{ {
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(
typeof(IWebClientInfoProvider),
typeof(HttpContextWebClientInfoProvider))]
public class RequestForwardedHeaderWebClientInfoProvider : HttpContextWebClientInfoProvider
{
protected ForwardedHeadersOptions Options { get; } protected ForwardedHeadersOptions Options { get; }
public RequestForwardedHeaderWebClientInfoProvider( public RequestForwardedHeaderWebClientInfoProvider(
ILogger<HttpContextWebClientInfoProvider> logger, ILogger<HttpContextWebClientInfoProvider> logger,
@ -28,16 +24,19 @@ namespace LINGYUN.Abp.AspNetCore.WebClientInfo
protected override string GetClientIpAddress() protected override string GetClientIpAddress()
{ {
IHeaderDictionary requestHeaders = HttpContextAccessor.HttpContext?.Request?.Headers; string forwardedForHeader = null;
var requestHeaders = HttpContextAccessor.HttpContext?.Request?.Headers;
if (requestHeaders != null && if (requestHeaders != null &&
Options.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor) &&
requestHeaders.TryGetValue(Options.ForwardedForHeaderName, out StringValues headers) == true) requestHeaders.TryGetValue(Options.ForwardedForHeaderName, out StringValues headers) == true)
{ {
if (!StringValues.IsNullOrEmpty(headers)) var headerStr = headers.ToString();
if (!headerStr.IsNullOrWhiteSpace())
{ {
return headers.Last(); // 原始客户端IP永远是第一个
} forwardedForHeader = headerStr.Split(",").First();
} }
return base.GetClientIpAddress();
} }
return forwardedForHeader.IsNullOrWhiteSpace() ? base.GetClientIpAddress() : forwardedForHeader;
} }
} }

67
aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.HttpOverrides/Microsoft/AspNetCore/Builder/ForwardedHeadersOptionsExtensions.cs

@ -1,67 +0,0 @@
using LINGYUN.Abp.AspNetCore.HttpOverrides.Forwarded;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
namespace Microsoft.AspNetCore.Builder
{
public static class ForwardedHeadersOptionsExtensions
{
public static void Configure(
this ForwardedHeadersOptions options,
AbpForwardedHeadersOptions abpOptions)
{
options.ForwardedForHeaderName = abpOptions.ForwardedForHeaderName;
options.ForwardedHeaders = abpOptions.ForwardedHeaders;
options.ForwardedHostHeaderName = abpOptions.ForwardedHostHeaderName;
options.ForwardedProtoHeaderName = abpOptions.ForwardedProtoHeaderName;
options.ForwardLimit = abpOptions.ForwardLimit;
options.OriginalForHeaderName = abpOptions.OriginalForHeaderName;
options.OriginalHostHeaderName = abpOptions.OriginalHostHeaderName;
options.OriginalProtoHeaderName = abpOptions.OriginalProtoHeaderName;
options.RequireHeaderSymmetry = abpOptions.RequireHeaderSymmetry;
if (abpOptions.AllowedHosts.Any())
{
options.AllowedHosts = abpOptions.AllowedHosts;
}
AddProxies(options.KnownProxies, abpOptions.KnownProxies);
if (abpOptions.KnownNetworks.Any())
{
options.KnownNetworks.Clear();
foreach (var proxy in abpOptions.KnownNetworks)
{
// 192.168.1.0/24
var spiltProxies = proxy.Split("/");
if (spiltProxies.Length != 2)
{
continue;
}
if (int.TryParse(spiltProxies[1], out int prefixLength) &&
IPAddress.TryParse(spiltProxies[0], out IPAddress prefixIpAddress))
{
options.KnownNetworks.Add(new IPNetwork(prefixIpAddress, prefixLength));
}
}
}
}
private static void AddProxies(IList<IPAddress> addresses, IList<string> proxiesAddress)
{
if (proxiesAddress.Any())
{
addresses.Clear();
foreach (var proxy in proxiesAddress)
{
if (IPAddress.TryParse(proxy, out IPAddress iPAddress))
{
addresses.Add(iPAddress);
}
}
}
}
}
}

3
aspnet-core/services/LY.MicroService.Applications.Single/Dockerfile

@ -12,4 +12,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.Applications.Single.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.Applications.Single.dll"]

14
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.Configure.cs

@ -8,9 +8,11 @@ using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -295,12 +297,22 @@ public partial class AuthServerHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

9
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/AuthServerHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.Account; using LINGYUN.Abp.Account;
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authorization.OrganizationUnits; using LINGYUN.Abp.Authorization.OrganizationUnits;
@ -59,6 +60,7 @@ namespace LY.MicroService.AuthServer;
typeof(AbpAliyunSmsModule), typeof(AbpAliyunSmsModule),
typeof(AbpCachingStackExchangeRedisModule), typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class AuthServerHttpApiHostModule : AbpModule public partial class AuthServerHttpApiHostModule : AbpModule
@ -79,7 +81,6 @@ public partial class AuthServerHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureIdentity(); ConfigureIdentity();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
@ -94,6 +95,7 @@ public partial class AuthServerHttpApiHostModule : AbpModule
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -103,10 +105,7 @@ public partial class AuthServerHttpApiHostModule : AbpModule
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链

3
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.AuthServer.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.AuthServer.HttpApi.Host.dll"]

3
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/LY.MicroService.AuthServer.HttpApi.Host.csproj

@ -33,7 +33,7 @@
<PackageReference Include="Serilog.Enrichers.Process" /> <PackageReference Include="Serilog.Enrichers.Process" />
<PackageReference Include="Serilog.Enrichers.Thread" /> <PackageReference Include="Serilog.Enrichers.Thread" />
<PackageReference Include="Serilog.Settings.Configuration" /> <PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Elasticsearch"/> <PackageReference Include="Serilog.Sinks.Elasticsearch" />
<PackageReference Include="Serilog.Sinks.File" /> <PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Swashbuckle.AspNetCore" /> <PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" /> <PackageReference Include="Volo.Abp.AspNetCore.Serilog" />
@ -54,6 +54,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Sms.Aliyun\LINGYUN.Abp.Sms.Aliyun.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Sms.Aliyun\LINGYUN.Abp.Sms.Aliyun.csproj" />

3
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/appsettings.Development.json

@ -51,7 +51,8 @@
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None" "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpTextTemplating": "Server=127.0.0.1;Database=Platform-V70;User Id=root;Password=123456;SslMode=None"
}, },
"CAP": { "CAP": {
"EventBus": { "EventBus": {

3
aspnet-core/services/LY.MicroService.AuthServer.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",

21
aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs

@ -4,11 +4,13 @@ using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -26,6 +28,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Unicode; using System.Text.Unicode;
using Volo.Abp.Account.Localization; using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
@ -175,6 +178,24 @@ public partial class AuthServerModule
} }
} }
private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ExposeIntegrationServices = true;
});
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
}
private void ConfigureDbContext() private void ConfigureDbContext()
{ {
Configure<AbpDbContextOptions>(options => Configure<AbpDbContextOptions>(options =>

8
aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.cs

@ -1,5 +1,6 @@
using DotNetCore.CAP; using DotNetCore.CAP;
using LINGYUN.Abp.Account; using LINGYUN.Abp.Account;
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authentication.QQ; using LINGYUN.Abp.Authentication.QQ;
using LINGYUN.Abp.Authentication.WeChat; using LINGYUN.Abp.Authentication.WeChat;
@ -77,6 +78,7 @@ namespace LY.MicroService.AuthServer;
typeof(AbpDataDbMigratorModule), typeof(AbpDataDbMigratorModule),
typeof(AbpAuditLoggingElasticsearchModule), // 放在 AbpIdentity 模块之后,避免被覆盖 typeof(AbpAuditLoggingElasticsearchModule), // 放在 AbpIdentity 模块之后,避免被覆盖
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpCAPEventBusModule), typeof(AbpCAPEventBusModule),
typeof(AbpAliyunSmsModule) typeof(AbpAliyunSmsModule)
)] )]
@ -114,6 +116,7 @@ public partial class AuthServerModule : AbpModule
ConfigureAuditing(configuration); ConfigureAuditing(configuration);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -126,10 +129,7 @@ public partial class AuthServerModule : AbpModule
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
var env = context.GetEnvironment(); var env = context.GetEnvironment();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {

3
aspnet-core/services/LY.MicroService.AuthServer/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.AuthServer.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.AuthServer.dll"]

1
aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj

@ -59,6 +59,7 @@
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.QQ\LINGYUN.Abp.Authentication.QQ.csproj" /> <ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.QQ\LINGYUN.Abp.Authentication.QQ.csproj" />
<ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.WeChat\LINGYUN.Abp.Authentication.WeChat.csproj" /> <ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.WeChat\LINGYUN.Abp.Authentication.WeChat.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Sms.Aliyun\LINGYUN.Abp.Sms.Aliyun.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Sms.Aliyun\LINGYUN.Abp.Sms.Aliyun.csproj" />

1
aspnet-core/services/LY.MicroService.AuthServer/appsettings.Development.json

@ -52,6 +52,7 @@
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpTextTemplating": "Server=127.0.0.1;Database=Platform-V70;User Id=root;Password=123456;SslMode=None",
"AppPlatform": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None" "AppPlatform": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None"
}, },
"CAP": { "CAP": {

3
aspnet-core/services/LY.MicroService.AuthServer/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",

14
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.Configure.cs

@ -10,8 +10,10 @@ using LINGYUN.Abp.TextTemplating;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -297,12 +299,22 @@ public partial class BackendAdminHttpApiHostModule
} }
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

10
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/BackendAdminHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.Aliyun.SettingManagement; using LINGYUN.Abp.Aliyun.SettingManagement;
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.Auditing; using LINGYUN.Abp.Auditing;
@ -35,7 +36,6 @@ using LINGYUN.Abp.WxPusher.SettingManagement;
using LY.MicroService.BackendAdmin.EntityFrameworkCore; using LY.MicroService.BackendAdmin.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Volo.Abp; using Volo.Abp;
@ -115,6 +115,7 @@ namespace LY.MicroService.BackendAdmin;
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class BackendAdminHttpApiHostModule : AbpModule public partial class BackendAdminHttpApiHostModule : AbpModule
@ -133,7 +134,6 @@ public partial class BackendAdminHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
ConfigureExceptionHandling(); ConfigureExceptionHandling();
@ -150,6 +150,7 @@ public partial class BackendAdminHttpApiHostModule : AbpModule
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -160,10 +161,7 @@ public partial class BackendAdminHttpApiHostModule : AbpModule
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链

3
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.BackendAdmin.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.BackendAdmin.HttpApi.Host.dll"]

1
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/LY.MicroService.BackendAdmin.HttpApi.Host.csproj

@ -61,6 +61,7 @@
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\cloud-aliyun\LINGYUN.Abp.Aliyun.SettingManagement\LINGYUN.Abp.Aliyun.SettingManagement.csproj" /> <ProjectReference Include="..\..\framework\cloud-aliyun\LINGYUN.Abp.Aliyun.SettingManagement\LINGYUN.Abp.Aliyun.SettingManagement.csproj" />
<ProjectReference Include="..\..\framework\cloud-tencent\LINGYUN.Abp.Tencent.SettingManagement\LINGYUN.Abp.Tencent.SettingManagement.csproj" /> <ProjectReference Include="..\..\framework\cloud-tencent\LINGYUN.Abp.Tencent.SettingManagement\LINGYUN.Abp.Tencent.SettingManagement.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />

6
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.Development.json

@ -9,12 +9,6 @@
"tag": "BackendAdmin" "tag": "BackendAdmin"
}, },
"App": { "App": {
"Forwarded": {
"ForwardedHeaders": 5,
"KnownProxies": [
"127.0.0.1"
]
},
"CorsOrigins": "http://127.0.0.1:3100", "CorsOrigins": "http://127.0.0.1:3100",
"ShowPii": true, "ShowPii": true,
"RefreshClaimsUrl": "http://127.0.0.1:30015" "RefreshClaimsUrl": "http://127.0.0.1:30015"

3
aspnet-core/services/LY.MicroService.BackendAdmin.HttpApi.Host/appsettings.json

@ -7,6 +7,9 @@
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",
"DefaultSalt": "sf&5)s3#" "DefaultSalt": "sf&5)s3#"
}, },
"Forwarded": {
"Headers": "XForwardedFor,XForwardedProto"
},
"Json": { "Json": {
"OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss", "OutputDateTimeFormat": "yyyy-MM-dd HH:mm:ss",
"InputDateTimeFormats": [ "InputDateTimeFormats": [

3
aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.IdentityServer.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.IdentityServer.HttpApi.Host.dll"]

14
aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/IdentityServerHttpApiHostModule.Configure.cs

@ -7,9 +7,11 @@ using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -295,12 +297,22 @@ public partial class IdentityServerHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

9
aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/IdentityServerHttpApiHostModule.cs

@ -1,3 +1,4 @@
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
@ -59,6 +60,7 @@ namespace LY.MicroService.IdentityServer;
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class IdentityServerHttpApiHostModule : AbpModule public partial class IdentityServerHttpApiHostModule : AbpModule
@ -79,7 +81,6 @@ public partial class IdentityServerHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureIdentity(); ConfigureIdentity();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
@ -94,6 +95,7 @@ public partial class IdentityServerHttpApiHostModule : AbpModule
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -103,10 +105,7 @@ public partial class IdentityServerHttpApiHostModule : AbpModule
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链

1
aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/LY.MicroService.identityServer.HttpApi.Host.csproj

@ -61,6 +61,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj" />

3
aspnet-core/services/LY.MicroService.IdentityServer.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",

3
aspnet-core/services/LY.MicroService.IdentityServer/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.IdentityServer.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.IdentityServer.dll"]

21
aspnet-core/services/LY.MicroService.IdentityServer/IdentityServerModule.Configure.cs

@ -7,10 +7,12 @@ using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LY.MicroService.IdentityServer.IdentityResources; using LY.MicroService.IdentityServer.IdentityResources;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -28,6 +30,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Unicode; using System.Text.Unicode;
using Volo.Abp.Account.Localization; using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.Caching; using Volo.Abp.Caching;
@ -124,6 +127,24 @@ public partial class IdentityServerModule
} }
} }
private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ExposeIntegrationServices = true;
});
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
}
private void ConfigureDbContext() private void ConfigureDbContext()
{ {
Configure<AbpDbContextOptions>(options => Configure<AbpDbContextOptions>(options =>

8
aspnet-core/services/LY.MicroService.IdentityServer/IdentityServerModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.Account; using LINGYUN.Abp.Account;
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authentication.QQ; using LINGYUN.Abp.Authentication.QQ;
@ -79,6 +80,7 @@ namespace LY.MicroService.IdentityServer;
typeof(AbpCAPEventBusModule), typeof(AbpCAPEventBusModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAliyunSmsModule) typeof(AbpAliyunSmsModule)
)] )]
public partial class IdentityServerModule : AbpModule public partial class IdentityServerModule : AbpModule
@ -114,6 +116,7 @@ public partial class IdentityServerModule : AbpModule
ConfigureUrls(configuration); ConfigureUrls(configuration);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -126,10 +129,7 @@ public partial class IdentityServerModule : AbpModule
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
var env = context.GetEnvironment(); var env = context.GetEnvironment();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {

1
aspnet-core/services/LY.MicroService.IdentityServer/LY.MicroService.IdentityServer.csproj

@ -58,6 +58,7 @@
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.QQ\LINGYUN.Abp.Authentication.QQ.csproj" /> <ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.QQ\LINGYUN.Abp.Authentication.QQ.csproj" />
<ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.WeChat\LINGYUN.Abp.Authentication.WeChat.csproj" /> <ProjectReference Include="..\..\framework\authentication\LINGYUN.Abp.Authentication.WeChat\LINGYUN.Abp.Authentication.WeChat.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj" />

3
aspnet-core/services/LY.MicroService.IdentityServer/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",

3
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.LocalizationManagement.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.LocalizationManagement.HttpApi.Host.dll"]

1
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LY.MicroService.LocalizationManagement.HttpApi.Host.csproj

@ -49,6 +49,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />

14
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.Configure.cs

@ -8,8 +8,10 @@ using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -248,12 +250,22 @@ public partial class LocalizationManagementHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

22
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/LocalizationManagementHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authorization.OrganizationUnits; using LINGYUN.Abp.Authorization.OrganizationUnits;
using LINGYUN.Abp.Data.DbMigrator; using LINGYUN.Abp.Data.DbMigrator;
@ -28,9 +29,9 @@ using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore;
namespace LY.MicroService.LocalizationManagement namespace LY.MicroService.LocalizationManagement;
{
[DependsOn( [DependsOn(
typeof(AbpSerilogEnrichersApplicationModule), typeof(AbpSerilogEnrichersApplicationModule),
typeof(AbpSerilogEnrichersUniqueIdModule), typeof(AbpSerilogEnrichersUniqueIdModule),
typeof(AbpAspNetCoreSerilogModule), typeof(AbpAspNetCoreSerilogModule),
@ -54,10 +55,11 @@ namespace LY.MicroService.LocalizationManagement
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class LocalizationManagementHttpApiHostModule : AbpModule public partial class LocalizationManagementHttpApiHostModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
@ -73,7 +75,6 @@ namespace LY.MicroService.LocalizationManagement
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
ConfigureExceptionHandling(); ConfigureExceptionHandling();
@ -86,6 +87,7 @@ namespace LY.MicroService.LocalizationManagement
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -98,10 +100,7 @@ namespace LY.MicroService.LocalizationManagement
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
var env = context.GetEnvironment(); var env = context.GetEnvironment();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链
@ -130,5 +129,4 @@ namespace LY.MicroService.LocalizationManagement
// 路由 // 路由
app.UseConfiguredEndpoints(); app.UseConfiguredEndpoints();
} }
}
} }

3
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.Development.json

@ -30,7 +30,8 @@
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpFeatureManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None" "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpTextTemplating": "Server=127.0.0.1;Database=Platform-V70;User Id=root;Password=123456;SslMode=None"
}, },
"CAP": { "CAP": {
"EventBus": { "EventBus": {

3
aspnet-core/services/LY.MicroService.LocalizationManagement.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"AllowedHosts": "*", "AllowedHosts": "*",
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",

3
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.PlatformManagement.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.PlatformManagement.HttpApi.Host.dll"]

1
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/LY.MicroService.PlatformManagement.HttpApi.Host.csproj

@ -52,6 +52,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />

14
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs

@ -8,8 +8,10 @@ using LINGYUN.Platform.Localization;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -283,12 +285,22 @@ public partial class PlatformManagementHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

11
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authorization.OrganizationUnits; using LINGYUN.Abp.Authorization.OrganizationUnits;
@ -88,6 +89,7 @@ namespace LY.MicroService.PlatformManagement;
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class PlatformManagementHttpApiHostModule : AbpModule public partial class PlatformManagementHttpApiHostModule : AbpModule
@ -107,7 +109,6 @@ public partial class PlatformManagementHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureDbContext(); ConfigureDbContext();
ConfigureBlobStoring(); ConfigureBlobStoring();
ConfigureLocalization(); ConfigureLocalization();
@ -122,6 +123,7 @@ public partial class PlatformManagementHttpApiHostModule : AbpModule
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -150,10 +152,7 @@ public partial class PlatformManagementHttpApiHostModule : AbpModule
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
var env = context.GetEnvironment(); var env = context.GetEnvironment();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链

3
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json

@ -54,7 +54,8 @@
"AbpSaas": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpSaas": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpSettingManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None", "AbpPermissionManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None" "AbpLocalizationManagement": "Server=127.0.0.1;Database=Platform-v70;User Id=root;Password=123456;SslMode=None",
"AbpTextTemplating": "Server=127.0.0.1;Database=Platform-V70;User Id=root;Password=123456;SslMode=None"
}, },
"Features": { "Features": {
"Validation": { "Validation": {

3
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"AllowedHosts": "*", "AllowedHosts": "*",
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",

3
aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/Dockerfile

@ -8,4 +8,7 @@ EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.RealtimeMessage.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.RealtimeMessage.HttpApi.Host.dll"]

1
aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj

@ -56,6 +56,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> <ProjectReference Include="..\..\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Features.LimitValidation.Redis.Client\LINGYUN.Abp.Features.LimitValidation.Redis.Client.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Features.LimitValidation.Redis.Client\LINGYUN.Abp.Features.LimitValidation.Redis.Client.csproj" />

14
aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.Configure.cs

@ -12,8 +12,10 @@ using LY.MicroService.RealtimeMessage.BackgroundJobs;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -323,12 +325,22 @@ public partial class RealtimeMessageHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

11
aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authorization.OrganizationUnits; using LINGYUN.Abp.Authorization.OrganizationUnits;
@ -105,6 +106,7 @@ namespace LY.MicroService.RealtimeMessage;
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
public partial class RealtimeMessageHttpApiHostModule : AbpModule public partial class RealtimeMessageHttpApiHostModule : AbpModule
@ -128,7 +130,6 @@ public partial class RealtimeMessageHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
ConfigureNotifications(); ConfigureNotifications();
@ -144,6 +145,7 @@ public partial class RealtimeMessageHttpApiHostModule : AbpModule
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureBackgroundTasks(configuration); ConfigureBackgroundTasks(configuration);
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureMvc(context.Services, configuration);
ConfigureCors(context.Services, configuration); ConfigureCors(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLocking(context.Services, configuration); ConfigureDistributedLocking(context.Services, configuration);
@ -154,10 +156,7 @@ public partial class RealtimeMessageHttpApiHostModule : AbpModule
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 本地化 // 本地化
app.UseMapRequestLocalization(); app.UseMapRequestLocalization();
// http调用链 // http调用链

3
aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"ForwardedHeaders": "XForwardedFor,XForwardedProto"
},
"AllowedHosts": "*", "AllowedHosts": "*",
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",

25
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile

@ -6,11 +6,32 @@ COPY . /app
## 有些定时作业可能需要建立独立的数据库连接, 可能跨不同的数据库, 配置一下MSSQL ## 有些定时作业可能需要建立独立的数据库连接, 可能跨不同的数据库, 配置一下MSSQL
## 解决连接SqlServer TLS版本过高问题 ## 解决连接SqlServer TLS版本过高问题
RUN sed -i 's/TLSv1.2/TLSv1.0/g' /etc/ssl/openssl.cnf RUN sed -i 's/\[openssl_init\]/# \[openssl_init\]/g' /etc/ssl/openssl.cnf
RUN sed -i 's/TLSv1.2/TLSv1.0/g' /usr/lib/ssl/openssl.cnf RUN sed -i '$a\[openssl_init]' /etc/ssl/openssl.cnf
RUN sed -i '$a\providers = provider_sect' /etc/ssl/openssl.cnf
RUN sed -i '$a\ssl_conf = ssl_sect' /etc/ssl/openssl.cnf
RUN sed -i '$a\[provider_sect]' /etc/ssl/openssl.cnf
RUN sed -i '$a\default = default_sect' /etc/ssl/openssl.cnf
RUN sed -i '$a\legacy = legacy_sect' /etc/ssl/openssl.cnf
RUN sed -i '$a\[default_sect]' /etc/ssl/openssl.cnf
RUN sed -i '$a\activate = 1' /etc/ssl/openssl.cnf
RUN sed -i '$a\[legacy_sect]' /etc/ssl/openssl.cnf
RUN sed -i '$a\activate = 1' /etc/ssl/openssl.cnf
RUN sed -i '$a\[ssl_sect]' /etc/ssl/openssl.cnf
RUN sed -i '$a\system_default = system_default_sect' /etc/ssl/openssl.cnf
RUN sed -i '$a\[system_default_sect]' /etc/ssl/openssl.cnf
RUN sed -i '$a\CipherString = DEFAULT:@SECLEVEL=0' /etc/ssl/openssl.cnf
EXPOSE 80/tcp EXPOSE 80/tcp
VOLUME [ "./app/Logs" ] VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ] VOLUME [ "./app/Modules" ]
RUN apt update
RUN apt install wget -y
ENTRYPOINT ["dotnet", "LY.MicroService.TaskManagement.HttpApi.Host.dll"] ENTRYPOINT ["dotnet", "LY.MicroService.TaskManagement.HttpApi.Host.dll"]

1
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj

@ -49,6 +49,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" /> <ProjectReference Include="..\..\framework\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" /> <ProjectReference Include="..\..\framework\common\LINGYUN.Abp.ExceptionHandling.Emailing\LINGYUN.Abp.ExceptionHandling.Emailing.csproj" />

14
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.Configure.cs

@ -9,7 +9,9 @@ using LINGYUN.Abp.TaskManagement.Localization;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -286,12 +288,22 @@ public partial class TaskManagementHttpApiHostModule
}); });
} }
private void ConfigureMvc() private void ConfigureMvc(IServiceCollection services, IConfiguration configuration)
{ {
Configure<AbpAspNetCoreMvcOptions>(options => Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.ExposeIntegrationServices = true; options.ExposeIntegrationServices = true;
}); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add((builder) =>
{
builder.Endpoints.MapHealthChecks(configuration["App:HealthChecks"] ?? "/healthz");
});
});
services.AddHealthChecks();
} }
private void ConfigureVirtualFileSystem() private void ConfigureVirtualFileSystem()

11
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AspNetCore.Mvc.Localization; using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch; using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.Authorization.OrganizationUnits; using LINGYUN.Abp.Authorization.OrganizationUnits;
@ -78,6 +79,7 @@ namespace LY.MicroService.TaskManagement;
typeof(AbpLocalizationCultureMapModule), typeof(AbpLocalizationCultureMapModule),
typeof(AbpHttpClientWrapperModule), typeof(AbpHttpClientWrapperModule),
typeof(AbpAspNetCoreMvcWrapperModule), typeof(AbpAspNetCoreMvcWrapperModule),
typeof(AbpAspNetCoreHttpOverridesModule),
typeof(AbpCAPEventBusModule), typeof(AbpCAPEventBusModule),
typeof(AbpAutofacModule) typeof(AbpAutofacModule)
)] )]
@ -99,7 +101,6 @@ public partial class TaskManagementHttpApiHostModule : AbpModule
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureMvc();
ConfigureDbContext(); ConfigureDbContext();
ConfigureLocalization(); ConfigureLocalization();
ConfigureBackgroundTasks(); ConfigureBackgroundTasks();
@ -113,6 +114,7 @@ public partial class TaskManagementHttpApiHostModule : AbpModule
ConfigureMultiTenancy(configuration); ConfigureMultiTenancy(configuration);
ConfigureSwagger(context.Services); ConfigureSwagger(context.Services);
ConfigureJsonSerializer(configuration); ConfigureJsonSerializer(configuration);
ConfigureMvc(context.Services, configuration);
ConfigureOpenTelemetry(context.Services, configuration); ConfigureOpenTelemetry(context.Services, configuration);
ConfigureDistributedLock(context.Services, configuration); ConfigureDistributedLock(context.Services, configuration);
ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment()); ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment());
@ -123,10 +125,7 @@ public partial class TaskManagementHttpApiHostModule : AbpModule
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();
var env = context.GetEnvironment(); var env = context.GetEnvironment();
app.UseForwardedHeaders(new ForwardedHeadersOptions app.UseForwardedHeaders();
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAbpRequestLocalization(); app.UseAbpRequestLocalization();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseCorrelationId(); app.UseCorrelationId();

3
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/appsettings.json

@ -2,6 +2,9 @@
"Clock": { "Clock": {
"Kind": "Local" "Kind": "Local"
}, },
"Forwarded": {
"Headers": "XForwardedFor,XForwardedProto"
},
"StringEncryption": { "StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra", "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84", "InitVectorBytes": "s83ng0abvd02js84",

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save