diff --git a/apps/vue/src/api/task-management/backgroundJobInfo.ts b/apps/vue/src/api/task-management/backgroundJobInfo.ts index fbf58bd43..41f46f7f4 100644 --- a/apps/vue/src/api/task-management/backgroundJobInfo.ts +++ b/apps/vue/src/api/task-management/backgroundJobInfo.ts @@ -8,6 +8,7 @@ import { } from './model/backgroundJobInfoModel'; import { format } from '/@/utils/strings'; import { ListResultDto, PagedResultDto } from '../model/baseModel'; +import { DefineParamter, DynamicQueryable } from '/@/components/Table/src/types/advancedSearch'; enum Api { GetById = '/api/task-management/background-jobs/{id}', @@ -27,6 +28,8 @@ enum Api { BulkStop = '/api/task-management/background-jobs/bulk-stop', BulkDelete = '/api/task-management/background-jobs/bulk-delete', GetDefinitions = '/api/task-management/background-jobs/definitions', + GetAvailableFields = '/api/task-management/background-jobs/available-fields', + AdvancedSearch = '/api/task-management/background-jobs/search', } export const getById = (id: string) => { @@ -42,6 +45,19 @@ export const getList = (input: BackgroundJobInfoGetListInput) => { }); }; +export const getAvailableFields = () => { + return defAbpHttp.get>({ + url: Api.GetAvailableFields, + }); +} + +export const advancedSearch = (input: DynamicQueryable) => { + return defAbpHttp.post>({ + url: Api.AdvancedSearch, + data: input, + }); +} + export const getDefinitions = () => { return defAbpHttp.get>({ url: Api.GetDefinitions, diff --git a/apps/vue/src/components/Table/src/BasicTable.vue b/apps/vue/src/components/Table/src/BasicTable.vue index 80a33c439..f5b8c8ff3 100644 --- a/apps/vue/src/components/Table/src/BasicTable.vue +++ b/apps/vue/src/components/Table/src/BasicTable.vue @@ -13,6 +13,16 @@ + -->
+ + + diff --git a/apps/vue/src/components/Table/src/hooks/useDataSource.ts b/apps/vue/src/components/Table/src/hooks/useDataSource.ts index 6c3c5d86a..3a82c07ed 100644 --- a/apps/vue/src/components/Table/src/hooks/useDataSource.ts +++ b/apps/vue/src/components/Table/src/hooks/useDataSource.ts @@ -239,16 +239,22 @@ export function useDataSource( return findRow(dataSourceRef.value); } - async function fetch(opt?: FetchParams) { + async function fetch(opt?: FetchParams, api?: (...arg: any) => Promise, request?: any) { + const { api: apiFunc, useSearchForm } = unref(propsRef); + api = api || apiFunc; + if (!api || !isFunction(api)) return; + request = request || useSearchForm ? getFieldsValue() : {}; + _fetch(opt, api, request); + } + + async function _fetch(opt?: FetchParams, api?: (...arg: any) => Promise, request?: any) { const { - api, searchInfo, defSort, fetchSetting, beforeFetch, beforeResponse, afterFetch, - useSearchForm, pagination, } = unref(propsRef); if (!api || !isFunction(api)) return; @@ -274,7 +280,7 @@ export function useDataSource( let params: Recordable = merge( pageParams, - useSearchForm ? getFieldsValue() : {}, + request, searchInfo, opt?.searchInfo ?? {}, defSort, @@ -308,7 +314,7 @@ export function useDataSource( setPagination({ current: currentTotalPage, }); - return await fetch(opt); + return await _fetch(opt, api, request); } } diff --git a/apps/vue/src/components/Table/src/hooks/useTableForm.ts b/apps/vue/src/components/Table/src/hooks/useTableForm.ts index bc09c7f6d..f89e28e89 100644 --- a/apps/vue/src/components/Table/src/hooks/useTableForm.ts +++ b/apps/vue/src/components/Table/src/hooks/useTableForm.ts @@ -1,5 +1,6 @@ import type { ComputedRef, Slots } from 'vue'; import type { BasicTableProps, FetchParams } from '../types/table'; +import type { DynamicQueryable } from '../types/advancedSearch'; import { unref, computed } from 'vue'; import type { FormProps } from '/@/components/Form'; import { isFunction } from '/@/utils/is'; @@ -7,7 +8,7 @@ import { isFunction } from '/@/utils/is'; export function useTableForm( propsRef: ComputedRef, slots: Slots, - fetch: (opt?: FetchParams | undefined) => Promise, + fetch: (opt?: FetchParams | undefined, api?: (...arg: any) => Promise, request?: any) => Promise, getLoading: ComputedRef, ) { const getFormProps = computed((): Partial => { @@ -28,6 +29,12 @@ export function useTableForm( .filter((item) => !!item) as string[]; }); + const getAdvancedSearchProps = computed(() => { + const { advancedSearchConfig } = unref(propsRef); + + return advancedSearchConfig; + }); + function replaceFormSlotKey(key: string) { if (!key) return ''; return key?.replace?.(/form\-/, '') ?? ''; @@ -41,10 +48,19 @@ export function useTableForm( fetch({ searchInfo: info, page: 1 }); } + function handleAdvanceSearchChange(queryable: DynamicQueryable) { + const { advancedSearchConfig } = unref(propsRef); + if (!advancedSearchConfig) return; + const { fetchApi } = advancedSearchConfig; + fetch({ searchInfo: { queryable: queryable }, page: 1 }, fetchApi, {}); + } + return { getFormProps, + getAdvancedSearchProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange, + handleAdvanceSearchChange, }; } diff --git a/apps/vue/src/components/Table/src/types/advancedSearch.ts b/apps/vue/src/components/Table/src/types/advancedSearch.ts new file mode 100644 index 000000000..c87ddce21 --- /dev/null +++ b/apps/vue/src/components/Table/src/types/advancedSearch.ts @@ -0,0 +1,82 @@ +/** 高级查询条件属性 */ +export interface AdvanceSearchProps { + /** 使用高级查询 */ + useAdvancedSearch?: boolean; + /** 字段列表api */ + defineFieldApi?: () => Promise; + /** + * 字段列表api返回结果字段 + * @remarks 从服务器返回字段列表在数据结构中的字段名,默认: items + */ + listField?: string; + /** 高级查询api */ + fetchApi?: (...arg: any) => Promise, +} + +/** 自定义字段 */ +export interface DefineParamter { + /** 字段名称 */ + name: string; + /** 字段描述 */ + description?: string; + /** 数据类型(后端) */ + type: string; + /** 数据类型(js) */ + javaScriptType: string; +} + +/** 连接条件 */ +export enum DynamicLogic { + /** 且 */ + And = 0, + /** 或 */ + Or = 1 +} + +/** 运算条件 */ +export enum DynamicComparison { + /** 等于 */ + Equal = 0, + /** 不等于 */ + NotEqual = 1, + /** 小于 */ + LessThan = 2, + /** 小于等于 */ + LessThanOrEqual = 3, + /** 大于 */ + GreaterThan = 4, + /** 大于等于 */ + GreaterThanOrEqual = 5, + /** 左包含 */ + StartsWith = 6, + /** 左不包含 */ + NotStartsWith = 7, + /** 右包含 */ + EndsWith = 8, + /** 右不包含 */ + NotEndsWith = 9, + /** 包含 */ + Contains = 10, + /** 不包含 */ + NotContains = 11 +} + +/** 动态查询字段 */ +export interface DynamicParamter { + /** 字段名称 */ + field: string; + /** 连接条件 */ + logic: DynamicLogic; + /** 运算条件 */ + comparison: DynamicComparison; + /** 比较值 */ + value: any; + /** 数据类型(js), 仅作为前端输入控件切换 */ + javaScriptType?: string; +} + +/** 动态查询条件 */ +export interface DynamicQueryable { + /** 参数列表 */ + paramters: DynamicParamter[]; +} diff --git a/apps/vue/src/components/Table/src/types/table.ts b/apps/vue/src/components/Table/src/types/table.ts index 6e261fb4f..dcc949230 100644 --- a/apps/vue/src/components/Table/src/types/table.ts +++ b/apps/vue/src/components/Table/src/types/table.ts @@ -1,8 +1,8 @@ import type { VNodeChild } from 'vue'; import type { PaginationProps } from './pagination'; import type { FormProps } from '/@/components/Form'; -import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface'; -import type { ColumnProps } from 'ant-design-vue/lib/table'; +import type { ColumnProps, TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface'; +import type { AdvanceSearchProps } from './advancedSearch'; import { ComponentType } from './componentType'; import { VueNode } from '/@/utils/propTypes'; @@ -185,6 +185,8 @@ export interface BasicTableProps { useSearchForm?: boolean; // 表单配置 formConfig?: Partial; + // 高级查询配置 + advancedSearchConfig?: Partial; // 列配置 columns: BasicColumn[]; // 是否显示序号列 diff --git a/apps/vue/src/locales/lang/en/component.ts b/apps/vue/src/locales/lang/en/component.ts index 379f0d081..0308706e6 100644 --- a/apps/vue/src/locales/lang/en/component.ts +++ b/apps/vue/src/locales/lang/en/component.ts @@ -68,6 +68,30 @@ export default { settingFullScreen: 'Full Screen', index: 'Index', total: 'total of {total}', + advancedSearch: { + title: 'Advanced Search', + conditions: 'Condition', + addCondition: 'Add Condition', + delCondition: 'Del Condition', + field: 'Field', + logic: 'Logic', + and: 'And', + or: 'Or', + comparison: 'Comparison', + value: 'Value', + equal: 'Equal', + notEqual: 'Not Equal', + lessThan: 'Less Than', + lessThanOrEqual: 'less Than Or Equal', + greaterThan: 'Greater Than', + greaterThanOrEqual: 'Greater Than Or Equal', + startsWith: 'Starts With', + notStartsWith: 'Not Starts With', + endsWith: 'Ends With', + notEndsWith: 'Not Ends With', + contains: 'Contains', + notContains: 'Not Contains', + } }, time: { before: ' ago', diff --git a/apps/vue/src/locales/lang/en/table.ts b/apps/vue/src/locales/lang/en/table.ts index 9b0e00bf3..ac8672f9b 100644 --- a/apps/vue/src/locales/lang/en/table.ts +++ b/apps/vue/src/locales/lang/en/table.ts @@ -1,3 +1,4 @@ export default { action: 'Actions', + sureToDelete: 'Sure to delete?', }; diff --git a/apps/vue/src/locales/lang/zh-CN/component.ts b/apps/vue/src/locales/lang/zh-CN/component.ts index 07bc8f1ea..363b658cf 100644 --- a/apps/vue/src/locales/lang/zh-CN/component.ts +++ b/apps/vue/src/locales/lang/zh-CN/component.ts @@ -68,10 +68,32 @@ export default { settingFixedLeft: '固定到左侧', settingFixedRight: '固定到右侧', settingFullScreen: '全屏', - index: '序号', - total: '共 {total} 条数据', + advancedSearch: { + title: '高级查询', + conditions: '查询条件', + addCondition: '增加条件', + delCondition: '删除条件', + field: '字段', + logic: '连接条件', + and: '且', + or: '或', + comparison: '运算符', + value: '比较值', + equal: '等于', + notEqual: '不等于', + lessThan: '小于', + lessThanOrEqual: '小于等于', + greaterThan: '大于', + greaterThanOrEqual: '大于等于', + startsWith: '左包含', + notStartsWith: '左不包含', + endsWith: '右包含', + notEndsWith: '右不包含', + contains: '包含', + notContains: '不包含', + } }, time: { before: '前', diff --git a/apps/vue/src/locales/lang/zh-CN/table.ts b/apps/vue/src/locales/lang/zh-CN/table.ts index 0f863333a..219934071 100644 --- a/apps/vue/src/locales/lang/zh-CN/table.ts +++ b/apps/vue/src/locales/lang/zh-CN/table.ts @@ -1,3 +1,4 @@ export default { action: '操作方法', + sureToDelete: '你确定要删除吗?', }; diff --git a/apps/vue/src/views/task-management/background-jobs/components/JobTable.vue b/apps/vue/src/views/task-management/background-jobs/components/JobTable.vue index 6f581ddd0..e99ba1355 100644 --- a/apps/vue/src/views/task-management/background-jobs/components/JobTable.vue +++ b/apps/vue/src/views/task-management/background-jobs/components/JobTable.vue @@ -112,6 +112,8 @@ deleteById, bulkStop, bulkStart, + getAvailableFields, + advancedSearch, } from '/@/api/task-management/backgroundJobInfo'; import { JobStatus } from '/@/api/task-management/model/backgroundJobInfoModel'; import { getDataColumns } from '../datas/TableData'; @@ -145,6 +147,11 @@ immediate: true, clickToRowSelect: false, formConfig: getSearchFormSchemas(), + advancedSearchConfig: { + useAdvancedSearch: true, + defineFieldApi: getAvailableFields, + fetchApi: advancedSearch, + }, rowSelection: { type: 'checkbox', onChange: handleSelectChange, diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs index 93d637c3d..0f63b83c8 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs @@ -5,4 +5,5 @@ public class DynamicParamterDto public string Name { get; set; } public string Description { get; set; } public string Type { get; set; } + public string JavaScriptType { get; set; } } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs index 47101f6a4..263e52643 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; @@ -36,7 +37,8 @@ public abstract class DynamicQueryableAppService : Applicat { Name = propertyInfo.Name, Type = propertyInfo.PropertyType.FullName, - Description = localizedProp.Value ?? propertyInfo.Name + Description = localizedProp.Value ?? propertyInfo.Name, + JavaScriptType = ConvertToJavaScriptType(propertyInfo.PropertyType) }); } @@ -71,4 +73,48 @@ public abstract class DynamicQueryableAppService : Applicat { return ObjectMapper.Map, List>(entities); } + + protected virtual string ConvertToJavaScriptType(Type propertyType) + { + if (propertyType.IsNullableType()) + { + propertyType = propertyType.GetGenericArguments().FirstOrDefault(); + } + var typeCode = Type.GetTypeCode(propertyType); + switch (typeCode) + { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Byte: + case TypeCode.Double: + case TypeCode.SByte: + case TypeCode.Decimal: + return "number"; + case TypeCode.Boolean: + return "boolean"; + case TypeCode.Char: + case TypeCode.String: + return "string"; + case TypeCode.DateTime: + return "Date"; + case TypeCode.Object: + if (propertyType.IsArray) + { + return "array"; + } + else + { + return "object"; + } + default: + case TypeCode.Empty: + case TypeCode.DBNull: + return "object"; + } + } } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Reflection/NullableTypeExtensions.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Reflection/NullableTypeExtensions.cs new file mode 100644 index 000000000..d1d92ffea --- /dev/null +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Reflection/NullableTypeExtensions.cs @@ -0,0 +1,9 @@ +namespace System.Reflection; + +public static class NullableTypeExtensions +{ + public static bool IsNullableType(this Type theType) + { + return (theType.IsGenericType && theType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))); + } +}