fix(全局): 项目管理/测试计划/脑图部分 bug 修复

This commit is contained in:
baiqi 2024-05-27 17:49:23 +08:00 committed by 刘瑞斌
parent c1a3f918e4
commit 0ad5eb7e64
17 changed files with 122 additions and 44 deletions

View File

@ -1,5 +1,4 @@
import MSR from '@/api/http/index';
import * as bugURL from '@/api/requrls/bug-management';
import { CommonList, TableQueryParams } from '@/models/common';
@ -91,5 +90,5 @@ export function getMessageReadAll(resourceType?: string) {
}
export function getMessageUnReadCount(projectId: string) {
return MSR.get<number>({ url: '/notification/un-read', params: projectId });
return MSR.get<number>({ url: '/notification/un-read', params: projectId }, { ignoreCancelToken: true });
}

View File

@ -36,7 +36,7 @@
>
{{ t('common.save') }}
</a-button>
<a-button type="secondary" :disabled="saveLoading">{{ t('common.cancel') }}</a-button>
<a-button type="secondary" :disabled="saveLoading" @click="handleCancel">{{ t('common.cancel') }}</a-button>
</div>
</div>
</template>
@ -47,6 +47,7 @@
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import { MinderJsonNode } from '@/components/pure/ms-minder-editor/props';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import { getCaseDefaultFields, updateCaseRequest } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
@ -61,6 +62,9 @@
activeCase: Record<string, any>;
loading: boolean;
}>();
const emit = defineEmits<{
(e: 'cancel'): void;
}>();
const appStore = useAppStore();
const userStore = useUserStore();
@ -149,7 +153,7 @@
fileList: [],
});
const selectedNode: MinderJsonNode = window.minder.getSelectedNode();
if (selectedNode.data) {
if (selectedNode?.data) {
selectedNode.data.text = baseInfoForm.value.name;
}
Message.success(t('common.saveSuccess'));
@ -164,6 +168,9 @@
}
});
}
function handleCancel() {
emit('cancel');
}
watch(
() => props.activeCase.id,

View File

@ -17,7 +17,12 @@
@save="handleMinderSave"
>
<template #extractTabContent>
<baseInfo v-if="activeExtraKey === 'baseInfo'" :loading="baseInfoLoading" :active-case="activeCase" />
<baseInfo
v-if="activeExtraKey === 'baseInfo'"
:loading="baseInfoLoading"
:active-case="activeCase"
@cancel="handleBaseInfoCancel"
/>
<attachment
v-else-if="activeExtraKey === 'attachment'"
v-model:model-value="fileList"
@ -69,7 +74,7 @@
const topTags = [moduleTag, caseTag];
const descTags = [t('ms.minders.stepDesc'), t('ms.minders.textDesc')];
const importJson = ref<MinderJson>({
root: {},
root: {} as MinderJsonNode,
template: 'default',
treePath: [],
});
@ -552,6 +557,11 @@
fileList.value = [];
}
function handleBaseInfoCancel() {
extraVisible.value = false;
resetExtractInfo();
}
/**
* 处理脑图节点激活/点击
* @param node 被激活/点击的节点

View File

@ -42,6 +42,7 @@
import { useI18n } from '@/hooks/useI18n';
import useUserStore from '@/store/modules/user';
import { encrypted } from '@/utils';
import { clearToken } from '@/utils/auth';
import { validatePasswordLength, validateWordPassword } from '@/utils/validate';
const router = useRouter();
@ -111,6 +112,7 @@
}, 1000);
setTimeout(() => {
clearInterval(timer);
clearToken();
router.push({
name: 'login',
query: {

View File

@ -92,6 +92,8 @@
import useMinderStore from '@/store/modules/components/minder-editor';
import { findNodePathByKey } from '@/utils';
import { MinderEventName } from '@/enums/minderEnum';
import { editMenuProps, insertProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props';
import Editor from '../script/editor';
import { markChangeNode, markDeleteNode } from '../script/tool/utils';
@ -222,8 +224,12 @@
const menuVisible = ref(false);
const menuPopupOffset = ref([0, 0]);
function switchNode(node: any) {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.id, 'data', 'id'));
/**
* 切换脑图展示的节点层级
* @param node 切换的节点
*/
function switchNode(node: MinderJsonNode) {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.data?.id, 'data', 'id'));
innerImportJson.value.data.expandState = 'expand';
window.minder.importJson(innerImportJson.value);
window.minder.execCommand('template', Object.keys(window.kityminder.Minder.getTemplateList())[minderStore.mold]);
@ -232,7 +238,7 @@
watch(
() => minderStore.event.timestamp,
() => {
if (minderStore.event.name === 'hotbox') {
if (minderStore.event.name === MinderEventName.HOTBOX && minderStore.event.nodePosition) {
const nodeDomWidth = minderStore.event.nodeDom?.getBoundingClientRect().width || 0;
menuPopupOffset.value = [
minderStore.event.nodePosition.x + nodeDomWidth / 2,
@ -240,12 +246,16 @@
];
menuVisible.value = true;
}
if (minderStore.event.name === 'enterNode') {
switchNode(minderStore.event.nodeData);
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.node) {
switchNode(minderStore.event.node);
}
}
);
/**
* 执行插入
* @param command 插入命令
*/
function execInsertCommand(command: string) {
const node: MinderJsonNode = window.minder.getSelectedNode();
if (props.insertNode) {
@ -257,6 +267,10 @@
}
}
/**
* 处理快捷菜单选择
* @param val 选择的菜单项
*/
function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) {
const selectedNode = window.minder.getSelectedNode();
switch (val) {

View File

@ -19,10 +19,15 @@
import { nextTick, onMounted, reactive, ref } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import useMinderStore from '@/store/modules/components/minder-editor';
import { MinderNodePosition } from '@/store/modules/components/minder-editor/types';
import { MinderEventName } from '@/enums/minderEnum';
import { delProps } from '../../props';
import { isDeleteDisableNode } from '../../script/tool/utils';
const minderStore = useMinderStore();
const { t } = useI18n();
const props = defineProps(delProps);
@ -54,9 +59,19 @@
if (removeNodeDisabled.value || !minder.queryCommandState || !minder.execCommand) {
return;
}
if (props.delConfirm) {
props.delConfirm();
return;
const node = minder.getSelectedNode();
let position: MinderNodePosition | undefined;
if (node) {
if (props.delConfirm) {
props.delConfirm(node);
return;
}
const box = node.getRenderBox();
position = {
x: box.cx,
y: box.cy,
};
minderStore.dispatchEvent(MinderEventName.DELETE_NODE, position, node.rc.node, node.data);
}
minder.forceRemoveNode();
}

View File

@ -19,8 +19,8 @@ export interface MinderJsonNodeData {
[key: string]: any;
}
export interface MinderJsonNode {
data: MinderJsonNodeData;
parent?: MinderJsonNode;
data?: MinderJsonNodeData;
children?: MinderJsonNode[];
[key: string]: any; // minder 内置字段
}
@ -148,8 +148,9 @@ export const moleProps = {
};
export const delProps = {
// 节点删除确认
delConfirm: {
type: Function,
type: Function as PropType<(node: MinderJsonNode) => void>,
default: null,
},
};

View File

@ -1,9 +1,7 @@
import useMinderStore from '@/store/modules/components/minder-editor';
import type { MinderNodePosition } from '@/store/modules/components/minder-editor/types';
interface Position {
x: number;
y: number;
}
import { MinderEventName } from '@/enums/minderEnum';
function HotboxRuntime(this: any) {
const { fsm } = this;
@ -18,14 +16,14 @@ function HotboxRuntime(this: any) {
function handleHotBoxShow() {
const node = minder.getSelectedNode();
let position: Position | undefined;
let position: MinderNodePosition | undefined;
if (node) {
const box = node.getRenderBox();
position = {
x: box.cx,
y: box.cy,
};
minderStore.dispatchEvent('hotbox', position, node.rc.node);
minderStore.dispatchEvent(MinderEventName.HOTBOX, position, node.rc.node);
}
}
@ -48,14 +46,14 @@ function HotboxRuntime(this: any) {
e.preventDefault(); // 阻止默认行为
// 执行进入模块方法
const node = minder.getSelectedNode();
let position: Position | undefined;
let position: MinderNodePosition | undefined;
if (node) {
const box = node.getRenderBox();
position = {
x: box.cx,
y: box.cy,
};
minderStore.dispatchEvent('enterNode', position, node.rc.node, node.data);
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, position, node.rc.node, node.data);
return;
}
}

View File

@ -68,8 +68,8 @@
modelValue.value = result;
nextTick(() => {
initNumberAndType();
emit('change', modelValue.value);
});
emit('change', modelValue.value);
}
const option = [

View File

@ -0,0 +1,7 @@
export enum MinderEventName {
'DELETE_NODE' = 'DELETE_NODE', // 删除节点
'HOTBOX' = 'HOTBOX', // 热键菜单
'ENTER_NODE' = 'ENTER_NODE', // 进入节点
}
export default {};

View File

@ -1,30 +1,41 @@
import { defineStore } from 'pinia';
import type { MinderJsonNode } from '@/components/pure/ms-minder-editor/props';
import type { MinderEventName } from '@/enums/minderEnum';
import { MinderNodePosition, MinderState } from './types';
// 脑图组件的 store
const useMinderStore = defineStore('minder', {
state: (): MinderState => ({
event: {
name: '',
name: '' as MinderEventName,
timestamp: 0,
nodePosition: {
x: 0,
y: 0,
},
nodeDom: undefined,
nodeData: undefined,
node: undefined,
},
mold: 0,
}),
actions: {
dispatchEvent(name: string, position: MinderNodePosition, nodeDom?: HTMLElement, nodeData?: Record<string, any>) {
/**
*
* @param name
* @param position /
* @param nodeDom DOM
* @param node
*/
dispatchEvent(name: MinderEventName, position?: MinderNodePosition, nodeDom?: HTMLElement, node?: MinderJsonNode) {
this.event = {
name,
timestamp: Date.now(),
nodePosition: position,
nodeDom,
nodeData,
node,
};
},
setMold(val: number) {

View File

@ -1,14 +1,18 @@
import type { MinderJsonNode } from '@/components/pure/ms-minder-editor/props';
import type { MinderEventName } from '@/enums/minderEnum';
export interface MinderNodePosition {
x: number;
y: number;
}
export interface MinderEvent {
name: string;
name: MinderEventName;
timestamp: number;
nodePosition: MinderNodePosition;
nodePosition?: MinderNodePosition;
nodeDom?: HTMLElement;
nodeData?: Record<string, any>;
node?: MinderJsonNode;
}
export interface MinderState {

View File

@ -142,11 +142,13 @@ const useUserStore = defineStore('user', {
appStore.resetSystemPackageType();
},
// 登出
async logout() {
async logout(silence = false) {
try {
const { t } = useI18n();
const appStore = useAppStore();
appStore.showLoading(t('message.logouting'));
if (!silence) {
const appStore = useAppStore();
appStore.showLoading(t('message.logouting'));
}
await userLogout();
} finally {
this.logoutCallBack();

View File

@ -162,7 +162,7 @@
setLoading(true);
try {
try {
await userStore.logout(); //
await userStore.logout(true); //
} catch (error) {
// eslint-disable-next-line no-console
console.log('logout error', error);

View File

@ -1,5 +1,5 @@
export default {
'login.form.title': 'One-stop open source continuous testing platform',
'login.form.title': 'Modern, open-source test management and interface testing tools',
'login.form.userName.errMsg': 'Username cannot be empty',
'login.form.password.errMsg': 'Password cannot be empty',
'login.form.login.errMsg': 'Login error, refresh and try again',

View File

@ -1,5 +1,5 @@
export default {
'login.form.title': '一站式开源持续测试平台',
'login.form.title': '现代化、开源的测试管理和接口测试工具',
'login.form.userName.errMsg': '邮箱不能为空',
'login.form.password.errMsg': '密码不能为空',
'login.form.login.errMsg': '登录出错,请刷新重试',

View File

@ -61,7 +61,7 @@
value-format="timestamp"
:separator="t('common.to')"
:time-picker-props="{
defaultValue: ['00:00:00', '00:00:00'],
defaultValue: tempRange,
}"
:disabled-time="disabledTime"
@select="handleTimeSelect"
@ -196,14 +196,14 @@
return (nodeData as ModuleTreeNode).name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
}
const tempRange = ref<(Date | string | number | undefined)[]>([]);
const tempRange = ref<(Date | string | number)[]>(['00:00:00', '00:00:00']);
function makeLessNumbers(value: number) {
function makeLessNumbers(value: number, isSecond = false) {
const res = [];
for (let i = 0; i < value; i++) {
res.push(i);
}
return res;
return isSecond && res.length === 0 ? [0] : res; // 1
}
function disabledTime(current: Date, type: 'start' | 'end'): DisabledTimeProps {
@ -230,7 +230,7 @@
currentDate.isSame(startDate, 'h') &&
currentDate.isSame(startDate, 'm')
) {
return makeLessNumbers(startDate.get('s'));
return makeLessNumbers(startDate.get('s'), true);
}
return [];
},
@ -240,7 +240,15 @@
}
function handleTimeSelect(value: (Date | string | number | undefined)[]) {
tempRange.value = value;
if (value) {
// const start = dayjs(value[0]);
// const end = dayjs(value[1]);
// if (start.isSame(end, 'D') && end.hour() === 0 && end.minute() === 0 && end.second() === 0) {
// const newEnd = end.hour(23).minute(59).second(59);
// value[1] = newEnd.valueOf();
// }
tempRange.value = value as number[];
}
}
const switchList: SwitchListModel[] = [