From d19c001a8bd390e1d729b989a6335b86e06247df Mon Sep 17 00:00:00 2001 From: RubyLiu Date: Fri, 29 Dec 2023 16:54:52 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=A7=A3=E5=86=B3=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E6=89=93=E5=8C=85ts=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/ms-batch-form/index.vue | 4 +- .../business/ms-link-file/linkFileTable.vue | 2 +- .../components/business/ms-select/index.tsx | 2 + .../minder-editor/script/runtime/clipboard.ts | 1 + .../minder-editor/script/runtime/input.ts | 3 +- .../pure/ms-advance-filter/FilterForm.vue | 17 +- .../pure/ms-advance-filter/index.vue | 1 + .../components/pure/ms-advance-filter/type.ts | 34 +++- .../pure/ms-form-create/ms-form-create.vue | 4 +- .../components/pure/ms-form-create/types.ts | 2 - .../pure/ms-pagination/pagination.tsx | 1 + frontend/src/components/pure/ms-table/type.ts | 5 + .../src/components/pure/ms-table/useTable.ts | 22 ++- frontend/src/models/bug-management.ts | 8 + .../components/batchEditModal.vue | 167 ++++++++++++++++-- frontend/src/views/bug-management/edit.vue | 1 - frontend/src/views/bug-management/index.vue | 99 ++++++++--- .../src/views/bug-management/locale/zh-CN.ts | 20 +++ .../fileManagement/components/rightBox.vue | 2 +- .../components/messageList.vue | 2 +- .../system/log/components/logCards.vue | 4 +- frontend/tsconfig.json | 3 +- 22 files changed, 348 insertions(+), 56 deletions(-) diff --git a/frontend/src/components/business/ms-batch-form/index.vue b/frontend/src/components/business/ms-batch-form/index.vue index ab6e2c7a04..8358b28847 100644 --- a/frontend/src/components/business/ms-batch-form/index.vue +++ b/frontend/src/components/business/ms-batch-form/index.vue @@ -203,9 +203,9 @@ watchEffect(() => { props.models.forEach((e) => { // 默认填充表单项 - let value = null; + let value: string | number | boolean | string[] | number[] | undefined; if (e.type === 'inputNumber') { - value = null; + value = undefined; } else if (e.type === 'tagInput') { value = []; } else { diff --git a/frontend/src/components/business/ms-link-file/linkFileTable.vue b/frontend/src/components/business/ms-link-file/linkFileTable.vue index 149af1c282..e038152be2 100644 --- a/frontend/src/components/business/ms-link-file/linkFileTable.vue +++ b/frontend/src/components/business/ms-link-file/linkFileTable.vue @@ -202,7 +202,7 @@ async function initFileTypes() { try { fileTypeLoading.value = true; - let res = null; + let res: string[] = []; if (fileType.value === 'storage') { res = await getRepositoryFileTypes(appStore.currentProjectId); } else { diff --git a/frontend/src/components/business/ms-select/index.tsx b/frontend/src/components/business/ms-select/index.tsx index 6ee4cf0640..efffe44fca 100644 --- a/frontend/src/components/business/ms-select/index.tsx +++ b/frontend/src/components/business/ms-select/index.tsx @@ -47,7 +47,9 @@ export interface RadioProps { export interface MsSearchSelectSlots { prefix?: string; + // @ts-ignore header?: (() => JSX.Element) | Slot; + // @ts-ignore default?: () => JSX.Element[]; footer?: Slot; empty?: Slot; diff --git a/frontend/src/components/pure/minder-editor/script/runtime/clipboard.ts b/frontend/src/components/pure/minder-editor/script/runtime/clipboard.ts index 59aea22ffe..aa36ed2d43 100644 --- a/frontend/src/components/pure/minder-editor/script/runtime/clipboard.ts +++ b/frontend/src/components/pure/minder-editor/script/runtime/clipboard.ts @@ -29,6 +29,7 @@ export default function ClipboardRuntime(this: any) { function encode(nodes: Array): string { const _nodes = []; for (let i = 0, l = nodes.length; i < l; i++) { + // @ts-ignore _nodes.push(minder.exportNode(nodes[i])); } return kmencode(Data.getRegisterProtocol('json').encode(_nodes)); diff --git a/frontend/src/components/pure/minder-editor/script/runtime/input.ts b/frontend/src/components/pure/minder-editor/script/runtime/input.ts index b430e2e3fd..4588700dac 100644 --- a/frontend/src/components/pure/minder-editor/script/runtime/input.ts +++ b/frontend/src/components/pure/minder-editor/script/runtime/input.ts @@ -21,6 +21,7 @@ if (!('innerText' in document.createElement('a')) && 'getSelection' in window) { let i; if (selection) { for (i = 0; i < selection.rangeCount; i++) { + // @ts-ignore ranges[i] = selection.getRangeAt(i); } @@ -52,7 +53,7 @@ function InputRuntime(this: any) { this.isGecko = window.kity.Browser.gecko; const updatePosition = (): void => { - let timer = null; + let timer: null | NodeJS.Timeout = null; const focusNode = this.minder.getSelectedNode(); if (!focusNode) return; diff --git a/frontend/src/components/pure/ms-advance-filter/FilterForm.vue b/frontend/src/components/pure/ms-advance-filter/FilterForm.vue index 48fa6a9e4f..a98fc32e06 100644 --- a/frontend/src/components/pure/ms-advance-filter/FilterForm.vue +++ b/frontend/src/components/pure/ms-advance-filter/FilterForm.vue @@ -7,8 +7,8 @@
{{ t('advanceFilter.accordBelow') }}
- {{ t('advanceFilter.all') }} - {{ t('advanceFilter.any') }} + {{ t('advanceFilter.all') }} + {{ t('advanceFilter.any') }}
{{ t('advanceFilter.condition') }}
@@ -212,12 +212,13 @@ import { useI18n } from '@/hooks/useI18n'; import { SelectValue } from '@/models/projectManagement/menuManagement'; + import { OptionsItem } from '@/models/setting/log'; import { OPERATOR_MAP } from './index'; - import { AccordBelowType, BackEndEnum, FilterFormItem, FilterResult, FilterType } from './type'; + import { AccordBelowType, BackEndEnum, CombineItem, FilterFormItem, FilterResult, FilterType } from './type'; const { t } = useI18n(); - const accordBelow = ref('all'); + const accordBelow = ref('AND'); const formRef = ref(null); const formModel = reactive<{ list: FilterFormItem[] }>({ list: [], @@ -245,7 +246,7 @@ }; const getOperationOption = (type: FilterType, dataIndex: string) => { - let result = []; + let result: { label: string; value: string }[] = []; switch (type) { case FilterType.NUMBER: result = OPERATOR_MAP.number; @@ -322,15 +323,17 @@ const handleFilter = () => { formRef.value?.validate((errors) => { if (!errors) { - const tmpObj: FilterResult = {}; + const tmpObj: FilterResult = { accordBelow: 'AND', combine: {} }; + const combine: CombineItem = {}; formModel.list.forEach((item) => { - tmpObj[item.dataIndex as string] = { + combine[item.dataIndex as string] = { operator: item.operator, value: item.value, backendType: item.backendType, }; }); tmpObj.accordBelow = accordBelow.value; + tmpObj.combine = combine; emit('onSearch', tmpObj); } }); diff --git a/frontend/src/components/pure/ms-advance-filter/index.vue b/frontend/src/components/pure/ms-advance-filter/index.vue index c4554ac5c3..4c6e7d5c69 100644 --- a/frontend/src/components/pure/ms-advance-filter/index.vue +++ b/frontend/src/components/pure/ms-advance-filter/index.vue @@ -82,6 +82,7 @@ }; const handleFilter = (filter: FilterResult) => { + console.log('filter', filter); emit('advSearch', filter); }; diff --git a/frontend/src/components/pure/ms-advance-filter/type.ts b/frontend/src/components/pure/ms-advance-filter/type.ts index 5c0fb71b26..e0dfdcddb2 100644 --- a/frontend/src/components/pure/ms-advance-filter/type.ts +++ b/frontend/src/components/pure/ms-advance-filter/type.ts @@ -1,9 +1,30 @@ -import { MsCascaderProps } from '@/components/business/ms-cascader/index.vue'; import type { MsSearchSelectProps, RadioProps } from '@/components/business/ms-select'; import type { CascaderOption, TreeNodeData } from '@arco-design/web-vue'; +import type { VirtualListProps } from '@arco-design/web-vue/es/_components/virtual-list-v2/interface'; import type { TreeSelectProps } from '@arco-design/web-vue/es/tree-select/interface'; +export type CascaderModelValue = string | number | Record | (string | number | Record)[]; + +export interface MsCascaderProps { + modelValue: CascaderModelValue; + options: CascaderOption[]; + mode?: 'MS' | 'native'; // MS的多选、原生;这里的多选指的是出了一级以外的多选,一级是顶级分类选项只能单选。原生模式使用 arco-design 的 cascader 组件,只加了getOptionComputedStyle + prefix?: string; // 输入框前缀 + levelTop?: string[]; // 顶级选项,多选时则必传 + level?: string; // 顶级选项,该级别为单选选项 + multiple?: boolean; // 是否多选 + strictly?: boolean; // 是否严格模式 + virtualListProps?: VirtualListProps; // 传入开启虚拟滚动 + panelWidth?: number; // 下拉框宽度,默认为 150px + placeholder?: string; + loading?: boolean; + optionSize?: 'small' | 'default'; + pathMode?: boolean; // 是否开启路径模式 + valueKey?: string; + labelKey?: string; // 传入自定义的 labelKey +} + /* eslint-disable no-shadow */ export enum BackEndEnum { STRING = 'string', @@ -41,10 +62,17 @@ export interface FilterFormItem { checkProps?: Partial; } -export type AccordBelowType = 'all' | 'any'; +export type AccordBelowType = 'AND' | 'OR'; + +export interface CombineItem { + [key: string]: Pick; +} export interface FilterResult { - [key: string]: Pick | AccordBelowType; + // 匹配模式 所有/任一 + accordBelow: AccordBelowType; + // 高级搜索 + combine: CombineItem; } export interface FilterFormProps { diff --git a/frontend/src/components/pure/ms-form-create/ms-form-create.vue b/frontend/src/components/pure/ms-form-create/ms-form-create.vue index 5d9cab7f88..49d9fd509d 100644 --- a/frontend/src/components/pure/ms-form-create/ms-form-create.vue +++ b/frontend/src/components/pure/ms-form-create/ms-form-create.vue @@ -95,8 +95,8 @@ // 处理控制器项 function getControlFormItemsMaps() { - const parentControlsItems = []; - formItems.value.forEach((item: FormItem) => { + const parentControlsItems: FormItem[] = []; + formItems.value.forEach((item) => { if (!item.displayConditions) { parentControlsItems.push(item); } else { diff --git a/frontend/src/components/pure/ms-form-create/types.ts b/frontend/src/components/pure/ms-form-create/types.ts index 0bb889c804..4685fc1e4d 100644 --- a/frontend/src/components/pure/ms-form-create/types.ts +++ b/frontend/src/components/pure/ms-form-create/types.ts @@ -1,7 +1,5 @@ import { FieldRule } from '@arco-design/web-vue'; -import { FormRule, Rule } from '@form-create/arco-design'; - export type FormItemType = | 'INPUT' | 'TEXTAREA' diff --git a/frontend/src/components/pure/ms-pagination/pagination.tsx b/frontend/src/components/pure/ms-pagination/pagination.tsx index fa6ab8043e..df22e3bc24 100644 --- a/frontend/src/components/pure/ms-pagination/pagination.tsx +++ b/frontend/src/components/pure/ms-pagination/pagination.tsx @@ -270,6 +270,7 @@ export default defineComponent({ }; const pageList = computed(() => { + // @ts-ignore const pageListArr: Array = []; if (pages.value < props.baseSize + props.bufferSize * 2) { diff --git a/frontend/src/components/pure/ms-table/type.ts b/frontend/src/components/pure/ms-table/type.ts index 262ae8b20f..b5e1afb6ca 100644 --- a/frontend/src/components/pure/ms-table/type.ts +++ b/frontend/src/components/pure/ms-table/type.ts @@ -140,4 +140,9 @@ export interface BatchActionQueryParams { selectAll: boolean; // 是否跨页全选 params?: TableQueryParams; // 查询参数 currentSelectCount: number; // 当前选中的数量 + condition?: any; // 查询条件 +} + +export interface CombineParams { + [key: string]: any; } diff --git a/frontend/src/components/pure/ms-table/useTable.ts b/frontend/src/components/pure/ms-table/useTable.ts index e05b1c0951..1e8c832118 100644 --- a/frontend/src/components/pure/ms-table/useTable.ts +++ b/frontend/src/components/pure/ms-table/useTable.ts @@ -9,7 +9,15 @@ import { useAppStore, useTableStore } from '@/store'; import type { CommonList, TableQueryParams } from '@/models/common'; import { SelectAllEnum } from '@/enums/tableEnum'; -import type { MsTableColumn, MsTableDataItem, MsTableErrorStatus, MsTableProps, SetPaginationPrams } from './type'; +import { FilterResult } from '../ms-advance-filter/type'; +import type { + CombineParams, + MsTableColumn, + MsTableDataItem, + MsTableErrorStatus, + MsTableProps, + SetPaginationPrams, +} from './type'; import type { TableData } from '@arco-design/web-vue'; export interface Pagination { @@ -84,6 +92,8 @@ export default function useTableProps( // keyword const keyword = ref(''); + // 高级筛选 + const advanceFilter = reactive({ accordBelow: 'AND', combine: {} }); // 是否分页 if (propsRes.value.showPagination) { @@ -151,11 +161,16 @@ export default function useTableProps( const setLoadListParams = (params?: object) => { loadListParams.value = params || {}; }; - + // 设置keyword const setKeyword = (v: string) => { keyword.value = v; }; + // 设置 advanceFilter + const setAdvanceFilter = (v: CombineParams) => { + advanceFilter.accordBelow = v.accordBelow; + advanceFilter.combine = v.combine; + }; // 给表格设置选中项 - add rowKey to selectedKeys const setTableSelected = (key: string) => { const { selectedKeys } = propsRes.value; @@ -184,6 +199,8 @@ export default function useTableProps( sort: sortItem.value, filter: filterItem.value, keyword: keyword.value, + combine: advanceFilter.combine, + searchMode: advanceFilter.accordBelow, ...loadListParams.value, }); const tmpArr = data.list; @@ -421,6 +438,7 @@ export default function useTableProps( setPagination, setLoadListParams, setKeyword, + setAdvanceFilter, resetPagination, getSelectedCount, resetSelector, diff --git a/frontend/src/models/bug-management.ts b/frontend/src/models/bug-management.ts index 1647bf944e..1494ddeb05 100644 --- a/frontend/src/models/bug-management.ts +++ b/frontend/src/models/bug-management.ts @@ -34,6 +34,7 @@ export interface BugEditCustomField { value: string; platformOptionJson?: string; // 选项的 Json required: boolean; + isMutiple?: boolean; } export interface BugEditFormObject { [key: string]: any; @@ -44,4 +45,11 @@ export interface BugEditCustomFieldItem { type: string; value: string; } +export type BugBatchUpdateFiledType = 'single_select' | 'multiple_select' | 'tag' | 'input' | 'user_selector' | 'date'; +export interface BugBatchUpdateFiledForm { + attribute: string; + value: string[]; + append: boolean; + inputValue: string; +} export default {}; diff --git a/frontend/src/views/bug-management/components/batchEditModal.vue b/frontend/src/views/bug-management/components/batchEditModal.vue index f281c9e8ea..6faf09f1ae 100644 --- a/frontend/src/views/bug-management/components/batchEditModal.vue +++ b/frontend/src/views/bug-management/components/batchEditModal.vue @@ -8,7 +8,7 @@ > @@ -50,19 +124,45 @@ import { type FormInstance, Message, type ValidatedError } from '@arco-design/web-vue'; import { BatchActionQueryParams } from '@/components/pure/ms-table/type'; + import { MsUserSelector } from '@/components/business/ms-user-selector'; + import { UserRequestTypeEnum } from '@/components/business/ms-user-selector/utils'; + import { updateBatchBug } from '@/api/modules/bug-management'; import { useI18n } from '@/hooks/useI18n'; + import { useAppStore } from '@/store'; + + import type { BugBatchUpdateFiledType } from '@/models/bug-management'; + import { BugBatchUpdateFiledForm, BugEditCustomField } from '@/models/bug-management'; + import { SelectValue } from '@/models/projectManagement/menuManagement'; const { t } = useI18n(); const props = defineProps<{ visible: boolean; selectParam: BatchActionQueryParams; + customFields: BugEditCustomField[]; }>(); const emit = defineEmits<{ (e: 'submit'): void; (e: 'update:visible', value: boolean): void; }>(); + const appStore = useAppStore(); const selectCount = computed(() => props.selectParam.currentSelectCount); + const systemOptionList = computed(() => [ + { + label: t('bugManagement.batchUpdate.handleUser'), + value: 'handleUser', + }, + { + label: t('bugManagement.batchUpdate.tag'), + value: 'tag', + }, + ]); + const customOptionList = computed(() => { + return props.customFields.map((item) => ({ + label: item.fieldName, + value: item.fieldId, + })); + }); const currentVisible = computed({ get() { return props.visible; @@ -73,24 +173,71 @@ }); const loading = ref(false); - const form = reactive({ + const form = reactive({ + // 批量更新的属性 attribute: '', + // 批量更新的值 value: [], append: false, + inputValue: '', }); + const valueMode = ref('single_select'); + + const showAppend = ref(false); + const formRef = ref(); const handleCancel = () => { currentVisible.value = false; loading.value = false; }; + const customFiledOption = ref<{ text: string; value: string }[]>([]); + + const handleArrtibuteChange = (value: SelectValue) => { + form.value = []; + form.inputValue = ''; + if (value === 'tag') { + valueMode.value = 'tag'; + showAppend.value = true; + } else if (value === 'handleUser') { + valueMode.value = 'user_selector'; + showAppend.value = true; + } else { + // 自定义字段 + const customField = props.customFields.find((item) => item.fieldId === value); + if (customField) { + if (customField.type?.toLowerCase() === 'input') { + showAppend.value = false; + valueMode.value = 'input'; + form.inputValue = ''; + } else { + // select + customFiledOption.value = JSON.parse(customField.platformOptionJson as string) || []; + const isMutiple = customField.type?.toLocaleLowerCase() === 'select' && customField.isMutiple; + showAppend.value = isMutiple || false; + valueMode.value = isMutiple ? 'multiple_select' : 'single_select'; + } + } + } + }; const handleConfirm = () => { formRef.value?.validate(async (errors: undefined | Record) => { if (!errors) { try { loading.value = true; + const params = { + excludeIds: props.selectParam.excludeIds, + selectIds: props.selectParam.selectedIds, + selectAll: props.selectParam.selectAll, + // 查询条件 + condition: props.selectParam.condition, + projectId: appStore.currentProjectId, + [form.attribute]: form.value || form.inputValue, + append: form.append, + }; + await updateBatchBug(params); Message.success(t('common.deleteSuccess')); handleCancel(); emit('submit'); diff --git a/frontend/src/views/bug-management/edit.vue b/frontend/src/views/bug-management/edit.vue index f9a5eeede5..dc9cfbfc72 100644 --- a/frontend/src/views/bug-management/edit.vue +++ b/frontend/src/views/bug-management/edit.vue @@ -165,7 +165,6 @@ import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase'; import { TableQueryParams } from '@/models/common'; import { SelectValue } from '@/models/projectManagement/menuManagement'; - import { FormCreateKeyEnum } from '@/enums/formCreateEnum'; import { convertToFile } from '../case-management/caseManagementFeature/components/utils'; diff --git a/frontend/src/views/bug-management/index.vue b/frontend/src/views/bug-management/index.vue index 6ebb122eda..d3222a0929 100644 --- a/frontend/src/views/bug-management/index.vue +++ b/frontend/src/views/bug-management/index.vue @@ -1,6 +1,11 @@