feat(组件): 脑图支持新功能&部分组件调整

This commit is contained in:
baiqi 2023-11-10 10:23:44 +08:00 committed by Craftsman
parent 17215a686e
commit bff4a60c58
29 changed files with 588 additions and 194 deletions

View File

@ -90,5 +90,16 @@ export default {
priority: 'Priority', priority: 'Priority',
tag: 'Tag', tag: 'Tag',
}, },
hotboxMenu: {
expand: 'Expand/Collapse',
insetParent: 'Insert one level up',
insetSon: 'Insert next level',
insetBrother: 'Insert sibling',
copy: 'Copy',
cut: 'Cut',
paste: 'Paste',
delete: 'Delete',
enterNode: 'Enter the current node',
},
}, },
}; };

View File

@ -84,5 +84,16 @@ export default {
priority: '优先级', priority: '优先级',
tag: '标签', tag: '标签',
}, },
hotboxMenu: {
expand: '展开/收起',
insetParent: '插入上一级',
insetSon: '插入下一级',
insetBrother: '插入同级',
copy: '复制',
cut: '剪切',
paste: '粘贴',
delete: '删除',
enterNode: '进入当前节点',
},
}, },
}; };

View File

@ -4,6 +4,7 @@
<edit-menu <edit-menu
:minder="minder" :minder="minder"
:move-enable="props.moveEnable" :move-enable="props.moveEnable"
:move-confirm="props.moveConfirm"
:sequence-enable="props.sequenceEnable" :sequence-enable="props.sequenceEnable"
:tag-enable="props.tagEnable" :tag-enable="props.tagEnable"
:progress-enable="props.progressEnable" :progress-enable="props.progressEnable"

View File

@ -1,18 +1,96 @@
<template> <template>
<div ref="mec" class="minder-container" :style="{ height: `${props.height}px` }"> <div ref="mec" class="minder-container" :style="{ height: `${props.height}px` }">
<a-button type="primary" :disabled="props.disabled" class="save-btn bottom-[30px] right-[30px]" @click="save">{{ <a-button type="primary" :disabled="props.disabled" class="save-btn bottom-[30px] right-[30px]" @click="save">
t('minder.main.main.save') {{ t('minder.main.main.save') }}
}}</a-button> </a-button>
<navigator /> <navigator />
<a-dropdown
v-model:popup-visible="menuVisible"
class="minder-dropdown"
position="bl"
:popup-translate="menuPopupOffset"
@select="handleMinderMenuSelect"
>
<span></span>
<template #content>
<a-doption value="expand">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.expand') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">( / )</div>
</div>
</a-doption>
<a-doption value="insetParent">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.insetParent') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Shift + Tab)</div>
</div>
</a-doption>
<a-doption value="insetSon">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.insetSon') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Tab)</div>
</div>
</a-doption>
<a-doption value="insetBrother">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.insetBrother') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Enter)</div>
</div>
</a-doption>
<a-doption value="copy">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.copy') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Ctrl + C)</div>
</div>
</a-doption>
<a-doption value="cut">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.cut') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Ctrl + X)</div>
</div>
</a-doption>
<a-doption value="paste">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.paste') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Ctrl + V)</div>
</div>
</a-doption>
<a-doption value="delete">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.delete') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Backspace)</div>
</div>
</a-doption>
<a-doption value="enterNode">
<div class="flex items-center">
<div>{{ t('minder.hotboxMenu.enterNode') }}</div>
<div class="ml-[4px] text-[var(--color-text-4)]">(Ctrl+ Enter)</div>
</div>
</a-doption>
</template>
</a-dropdown>
<div
v-if="innerImportJson.treePath?.length > 1"
class="absolute left-[50%] top-[24px] z-50 translate-x-[-50%] bg-white p-[8px]"
>
<a-breadcrumb>
<a-breadcrumb-item v-for="crumb of innerImportJson.treePath" :key="crumb.name" @click="switchNode(crumb)">
{{ crumb.text }}
</a-breadcrumb-item>
</a-breadcrumb>
</div>
</div> </div>
</template> </template>
<script lang="ts" name="minderContainer" setup> <script lang="ts" name="minderContainer" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { cloneDeep } from 'lodash-es';
import Navigator from './navigator.vue'; import Navigator from './navigator.vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useMinderStore from '@/store/modules/components/minder-editor';
import { findNodePathByKey } from '@/utils';
import { editMenuProps, mainEditorProps, priorityProps, tagProps } from '../props'; import { editMenuProps, mainEditorProps, priorityProps, tagProps } from '../props';
import Editor from '../script/editor'; import Editor from '../script/editor';
@ -25,9 +103,12 @@
const emit = defineEmits({ const emit = defineEmits({
afterMount: () => ({}), afterMount: () => ({}),
save: (json) => json, save: (json) => json,
enterNode: (data) => data,
}); });
const minderStore = useMinderStore();
const mec: Ref<HTMLDivElement | null> = ref(null); const mec: Ref<HTMLDivElement | null> = ref(null);
const innerImportJson = ref<any>({});
function save() { function save() {
emit('save', window.minder.exportJson()); emit('save', window.minder.exportJson());
@ -129,6 +210,80 @@
onMounted(async () => { onMounted(async () => {
init(); init();
}); });
watch(
() => props.importJson,
(val) => {
innerImportJson.value = val;
window.minder.importJson(val);
}
);
const menuVisible = ref(false);
const menuPopupOffset = ref([0, 0]);
function switchNode(node: any) {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.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]);
}
watch(
() => minderStore.event.timestamp,
() => {
if (minderStore.event.name === 'hotbox') {
const nodeDomWidth = minderStore.event.nodeDom?.getBoundingClientRect().width || 0;
menuPopupOffset.value = [
minderStore.event.nodePosition.x + nodeDomWidth / 2,
minderStore.event.nodePosition.y - nodeDomWidth / 4,
];
menuVisible.value = true;
}
if (minderStore.event.name === 'enterNode') {
switchNode(minderStore.event.nodeData);
}
}
);
function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) {
const selectedNode = window.minder.getSelectedNode();
switch (val) {
case 'expand':
if (selectedNode.data.expandState === 'collapse') {
window.minder.execCommand('Expand');
} else {
window.minder.execCommand('Collapse');
}
break;
case 'insetParent':
window.minder.execCommand('AppendParentNode');
break;
case 'insetSon':
window.minder.execCommand('AppendChildNode');
break;
case 'insetBrother':
window.minder.execCommand('AppendSiblingNode');
break;
case 'copy':
window.minder.execCommand('Copy');
break;
case 'cut':
window.minder.execCommand('Cut');
break;
case 'paste':
window.minder.execCommand('Paste');
break;
case 'delete':
window.minder.execCommand('RemoveNode');
break;
case 'enterNode':
switchNode(selectedNode.data);
break;
default:
break;
}
}
</script> </script>
<style lang="less"> <style lang="less">
@ -139,4 +294,9 @@
.minder-container { .minder-container {
@apply relative; @apply relative;
} }
.minder-dropdown {
.arco-dropdown-list-wrapper {
max-height: none;
}
}
</style> </style>

View File

@ -17,7 +17,7 @@
</a-button> </a-button>
{{ t('minder.menu.expand.folding') }} {{ t('minder.menu.expand.folding') }}
</div> </div>
<move-box :move-enable="props.moveEnable" /> <move-box :move-enable="props.moveEnable" :move-confirm="props.moveConfirm" />
<insert-box /> <insert-box />
<edit-del :del-confirm="props.delConfirm" /> <edit-del :del-confirm="props.delConfirm" />
</div> </div>
@ -39,10 +39,14 @@
:priority-start-with-zero="props.priorityStartWithZero" :priority-start-with-zero="props.priorityStartWithZero"
/> />
</div> </div>
<div class="menu-group">
<mold v-if="props.moldEnable" :default-mold="props.defaultMold" @mold-change="handleMoldChange" />
</div>
</div> </div>
</template> </template>
<script lang="ts" name="editMenu" setup> <script lang="ts" name="editMenu" setup>
import mold from '../view/mold.vue';
import editDel from './editDel.vue'; import editDel from './editDel.vue';
import insertBox from './insertBox.vue'; import insertBox from './insertBox.vue';
import moveBox from './moveBox.vue'; import moveBox from './moveBox.vue';
@ -51,9 +55,19 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { delProps, editMenuProps, priorityProps, tagProps } from '../../props'; import { delProps, editMenuProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../../props';
const props = defineProps({ ...editMenuProps, ...priorityProps, ...tagProps, ...delProps }); const props = defineProps({
...editMenuProps,
...priorityProps,
...tagProps,
...delProps,
...viewMenuProps,
...moleProps,
});
const emit = defineEmits<{
(e: 'moldChange', data: number): void;
}>();
const { t } = useI18n(); const { t } = useI18n();
@ -94,4 +108,8 @@
window.minder?.execCommand('ExpandToLevel', 1); window.minder?.execCommand('ExpandToLevel', 1);
} }
} }
function handleMoldChange(data: number) {
emit('moldChange', data);
}
</script> </script>

View File

@ -40,6 +40,7 @@
const props = defineProps<{ const props = defineProps<{
moveEnable: boolean; moveEnable: boolean;
moveConfirm: any;
}>(); }>();
let minder = reactive<any>({}); let minder = reactive<any>({});

View File

@ -1,27 +1,34 @@
<template> <template>
<div class="mold-group" :disabled="disabled"> <a-dropdown class="toggle" :disabled="disabled" @select="handleCommand">
<a-dropdown class="toggle" @select="handleCommand"> <span class="dropdown-toggle mold-icons menu-btn cursor-pointer" :class="'mold-' + (moldIndex + 1)" />
<div> <template #content>
<span class="dropdown-toggle mold-icons menu-btn" :class="'mold-' + (moldIndex + 1)" /> <a-doption class="dropdown-item" :value="1">
<span class="dropdown-link"> <div class="mold-icons mold-1"></div>
<icon-caret-down /> </a-doption>
</span> <a-doption class="dropdown-item" :value="2">
</div> <div class="mold-icons mold-2"></div>
<template #content> </a-doption>
<a-doption class="dropdown-item mold-icons mold-1" :value="1" /> <a-doption class="dropdown-item" :value="3">
<a-doption class="dropdown-item mold-icons mold-2" :value="2" /> <div class="mold-icons mold-3"></div>
<a-doption class="dropdown-item mold-icons mold-3" :value="3" /> </a-doption>
<a-doption class="dropdown-item mold-icons mold-4" :value="4" /> <a-doption class="dropdown-item" :value="4">
<a-doption class="dropdown-item mold-icons mold-5" :value="5" /> <div class="mold-icons mold-4"></div>
<a-doption class="dropdown-item mold-icons mold-6" :value="6" /> </a-doption>
</template> <a-doption class="dropdown-item" :value="5">
</a-dropdown> <div class="mold-icons mold-5"></div>
</div> </a-doption>
<a-doption class="dropdown-item" :value="6">
<div class="mold-icons mold-6"></div>
</a-doption>
</template>
</a-dropdown>
</template> </template>
<script lang="ts" name="Mold" setup> <script lang="ts" name="Mold" setup>
import { computed, nextTick, onMounted, ref } from 'vue'; import { computed, nextTick, onMounted, ref } from 'vue';
import useMinderStore from '@/store/modules/components/minder-editor';
import { moleProps } from '../../props'; import { moleProps } from '../../props';
const props = defineProps(moleProps); const props = defineProps(moleProps);
@ -30,6 +37,7 @@
(e: 'moldChange', data: number): void; (e: 'moldChange', data: number): void;
}>(); }>();
const minderStore = useMinderStore();
const moldIndex = ref(0); const moldIndex = ref(0);
const disabled = computed(() => { const disabled = computed(() => {
@ -45,9 +53,10 @@
const templateList = computed(() => window.kityminder.Minder.getTemplateList()); const templateList = computed(() => window.kityminder.Minder.getTemplateList());
function handleCommand(value: string | number | Record<string, any> | undefined) { function handleCommand(value: string | number | Record<string, any> | undefined) {
moldIndex.value = value as number; moldIndex.value = (value as number) - 1;
window.minder.execCommand('template', Object.keys(templateList.value)[value as number]); window.minder.execCommand('template', Object.keys(templateList.value)[(value as number) - 1]);
emit('moldChange', value as number); minderStore.setMold((value as number) - 1);
emit('moldChange', (value as number) - 1);
} }
onMounted(() => { onMounted(() => {
@ -55,40 +64,19 @@
}); });
</script> </script>
<style lang="less">
.toggle {
.arco-dropdown-list {
@apply grid grid-cols-2;
padding: 5px;
gap: 5px;
}
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
:deep(.arco-dropdown-list) {
@apply grid grid-cols-2;
}
.dropdown-toggle .mold-icons, .dropdown-toggle .mold-icons,
.mold-icons { .mold-icons {
background-image: url('@/assets/images/minder/mold.png'); background-image: url('@/assets/images/minder/mold.png');
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.mold-group { .dropdown-item {
@apply relative flex items-center justify-center; @apply flex items-center justify-center;
width: 80px; height: 50px !important;
.dropdown-toggle {
@apply flex;
margin-top: 5px;
width: 50px;
height: 50px;
}
}
.dropdown-link {
@apply absolute cursor-pointer;
right: 3px;
bottom: 2px;
} }
.mold-loop(@i) when (@i > 0) { .mold-loop(@i) when (@i > 0) {
.mold-@{i} { .mold-@{i} {
@ -96,7 +84,7 @@
margin-top: 5px; margin-top: 5px;
width: 50px; width: 50px;
height: 50px; height: 45px;
background-position: (1 - @i) * 50px 0; background-position: (1 - @i) * 50px 0;
} }
.mold-loop(@i - 1); .mold-loop(@i - 1);

View File

@ -9,6 +9,7 @@
:priority-start-with-zero="props.priorityStartWithZero" :priority-start-with-zero="props.priorityStartWithZero"
:tags="props.tags" :tags="props.tags"
:move-enable="props.moveEnable" :move-enable="props.moveEnable"
:move-confirm="props.moveConfirm"
:tag-edit-check="props.tagEditCheck" :tag-edit-check="props.tagEditCheck"
:tag-disable-check="props.tagDisableCheck" :tag-disable-check="props.tagDisableCheck"
:priority-disable-check="props.priorityDisableCheck" :priority-disable-check="props.priorityDisableCheck"
@ -26,6 +27,7 @@
:sequence-enable="props.sequenceEnable" :sequence-enable="props.sequenceEnable"
:tag-enable="props.tagEnable" :tag-enable="props.tagEnable"
:move-enable="props.moveEnable" :move-enable="props.moveEnable"
:move-confirm="props.moveConfirm"
:progress-enable="props.progressEnable" :progress-enable="props.progressEnable"
:import-json="props.importJson" :import-json="props.importJson"
:height="props.height" :height="props.height"
@ -38,6 +40,7 @@
:priority-start-with-zero="props.priorityStartWithZero" :priority-start-with-zero="props.priorityStartWithZero"
@after-mount="emit('afterMount')" @after-mount="emit('afterMount')"
@save="save" @save="save"
@enter-node="handleEnterNode"
/> />
</div> </div>
</template> </template>
@ -54,6 +57,8 @@
(e: 'moldChange', data: number): void; (e: 'moldChange', data: number): void;
(e: 'save', data: Record<string, any>): void; (e: 'save', data: Record<string, any>): void;
(e: 'afterMount'): void; (e: 'afterMount'): void;
(e: 'enterNode', data: any): void;
(e: 'nodeClick', data: any): void;
}>(); }>();
const props = defineProps({ const props = defineProps({
@ -73,7 +78,28 @@
function handleMoldChange(data: number) { function handleMoldChange(data: number) {
emit('moldChange', data); emit('moldChange', data);
} }
function save(data: Record<string, any>) { function save(data: Record<string, any>) {
emit('save', data); emit('save', data);
} }
function handleEnterNode(data: any) {
emit('enterNode', data);
}
onMounted(() => {
nextTick(() => {
if (window.minder.on) {
window.minder.on('mousedown', (e: any) => {
if (e.originEvent.button === 0) {
//
const selectedNode = window.minder.getSelectedNode();
if (Object.keys(window.minder).length > 0 && selectedNode) {
emit('nodeClick', selectedNode.data);
}
}
});
}
});
});
</script> </script>

View File

@ -7,25 +7,9 @@ export const mainEditorProps = {
type: Object, type: Object,
default() { default() {
return { return {
root: { root: {},
data: {
text: 'test111',
},
children: [
{
data: {
text: '地图',
},
},
{
data: {
text: '百科',
expandState: 'collapse',
},
},
],
},
template: 'default', template: 'default',
treePath: [] as any[],
}; };
}, },
}, },
@ -95,6 +79,10 @@ export const editMenuProps = {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
moveConfirm: {
type: Function,
default: null,
},
}; };
export const moleProps = { export const moleProps = {

View File

@ -16,20 +16,20 @@ const MimeType = () => {
'\uFFFF': 'application/km', '\uFFFF': 'application/km',
}; };
function getSpitor(): string { function getSplitor(): string {
return SPLITOR; return SPLITOR;
} }
function isPureText(text: string): boolean { function isPureText(text: string): boolean {
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
return !~text.indexOf(getSpitor()); return !~text.indexOf(getSplitor());
} }
function getPureText(text: string): string { function getPureText(text: string): string {
if (isPureText(text)) { if (isPureText(text)) {
return text; return text;
} }
return text.split(getSpitor())[1]; return text.split(getSplitor())[1];
} }
function getMimeType(sign?: string): MimeTypes | string | null { function getMimeType(sign?: string): MimeTypes | string | null {
@ -43,7 +43,7 @@ const MimeType = () => {
if (isPureText(text)) { if (isPureText(text)) {
return null; return null;
} }
return getMimeType(text.split(getSpitor())[0]); return getMimeType(text.split(getSplitor())[0]);
} }
function process(mimetype: string | false, text: string): string { function process(mimetype: string | false, text: string): string {
@ -84,8 +84,12 @@ const MimeType = () => {
return { return {
registerMimeTypeProtocol, registerMimeTypeProtocol,
getMimeTypeProtocol, getMimeTypeProtocol,
getSpitor, getSplitor,
getMimeType, getMimeType,
whichMimeType,
process,
getPureText,
isPureText,
}; };
}; };

View File

@ -13,10 +13,6 @@ interface IData {
}; };
} }
interface ICliboardEvent extends ClipboardEvent {
clipboardData: DataTransfer;
}
export default function ClipboardRuntime(this: any) { export default function ClipboardRuntime(this: any) {
const { minder } = this; const { minder } = this;
const { receiver } = this; const { receiver } = this;
@ -38,11 +34,10 @@ export default function ClipboardRuntime(this: any) {
return kmencode(Data.getRegisterProtocol('json').encode(_nodes)); return kmencode(Data.getRegisterProtocol('json').encode(_nodes));
} }
const beforeCopy = (e: ICliboardEvent) => { const beforeCopy = (e: ClipboardEvent) => {
if (document.activeElement === receiver.element) { if (document.activeElement === receiver.element) {
const clipBoardEvent = e; const clipBoardEvent = e;
const state = this.fsm.state(); const state = this.fsm.state();
switch (state) { switch (state) {
case 'input': { case 'input': {
break; break;
@ -80,7 +75,7 @@ export default function ClipboardRuntime(this: any) {
} }
} }
const str = encode(nodes); const str = encode(nodes);
clipBoardEvent.clipboardData.setData('text/plain', str); clipBoardEvent.clipboardData?.setData('text/plain', str);
} }
e.preventDefault(); e.preventDefault();
break; break;
@ -193,7 +188,7 @@ export default function ClipboardRuntime(this: any) {
* @Editor: Naixor * @Editor: Naixor
* @Date: 2015.9.24 * @Date: 2015.9.24
*/ */
document.addEventListener('copy', () => beforeCopy); document.addEventListener('copy', (e) => beforeCopy(e));
document.addEventListener('cut', () => beforeCut); document.addEventListener('cut', (e) => beforeCut(e));
document.addEventListener('paste', () => beforePaste); document.addEventListener('paste', (e) => beforePaste(e));
} }

View File

@ -51,7 +51,7 @@ class FSM {
} }
const oldState = this.currentState; const oldState = this.currentState;
const notify = [oldState, newState].concat([].slice.call(args, 1)); const notify = [oldState, newState].concat([reason, args[0]]);
let i; let i;
let handler; let handler;
@ -67,7 +67,6 @@ class FSM {
this.currentState = newState; this.currentState = newState;
this.debug.log('[{0}] {1} -> {2}', reason, oldState, newState); this.debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
// 跳转后 // 跳转后
for (i = 0; i < this.handlers.length; i++) { for (i = 0; i < this.handlers.length; i++) {
handler = this.handlers[i]; handler = this.handlers[i];
@ -99,8 +98,9 @@ class FSM {
* * to - * * to -
* * reason - * * reason -
*/ */
public when(condition: string, handler: Handler | string): void { public when(...args: any[]): void {
if (arguments.length === 1) { let [condition, handler] = args;
if (args.length === 1) {
handler = condition; handler = condition;
condition = '* -> *'; condition = '* -> *';
} }
@ -138,8 +138,8 @@ class FSM {
} }
} }
function FSMRumtime(this: any) { function FSMRuntime(this: any) {
this.fsm = new FSM('normal'); this.fsm = new FSM('normal');
} }
export default FSMRumtime; export default FSMRuntime;

View File

@ -1,3 +1,5 @@
import useMinderStore from '@/store/modules/components/minder-editor';
interface Position { interface Position {
x: number; x: number;
y: number; y: number;
@ -10,10 +12,11 @@ function HotboxRuntime(this: any) {
const { container } = this; const { container } = this;
const { HotBox } = window; const { HotBox } = window;
const hotbox = new HotBox(container); const hotbox = new HotBox(container);
const minderStore = useMinderStore();
hotbox.setParentFSM(fsm); hotbox.setParentFSM(fsm);
fsm.when('normal -> hotbox', () => { function handleHotBoxShow() {
const node = minder.getSelectedNode(); const node = minder.getSelectedNode();
let position: Position | undefined; let position: Position | undefined;
if (node) { if (node) {
@ -22,18 +25,46 @@ function HotboxRuntime(this: any) {
x: box.cx, x: box.cx,
y: box.cy, y: box.cy,
}; };
minderStore.dispatchEvent('hotbox', position, node.rc.node);
} }
hotbox.active('main', position); }
fsm.when('normal -> hotbox', () => {
handleHotBoxShow();
}); });
fsm.when('hotbox -> hotbox', () => {
handleHotBoxShow();
});
function handleShortcut(e: any) {
// 检查是否按下Ctrl键Windows和Linux或Command键Mac
const isCtrlKey = e.ctrlKey || e.metaKey;
// 检查是否按下Enter键
const isEnterKey = e.key === 'Enter';
// 检查是否同时按下Ctrl或Command和Enter键
if (isCtrlKey && isEnterKey) {
// 处理Ctrl+Enter组合键事件
e.preventDefault(); // 阻止默认行为
// 执行进入模块方法
const node = minder.getSelectedNode();
let position: Position | undefined;
if (node) {
const box = node.getRenderBox();
position = {
x: box.cx,
y: box.cy,
};
minderStore.dispatchEvent('enterNode', position, node.rc.node, node.data);
return;
}
}
minder.dispatchKeyEvent(e);
}
fsm.when('normal -> normal', (exit: any, enter: any, reason: any, e: any) => { fsm.when('normal -> normal', (exit: any, enter: any, reason: any, e: any) => {
if (reason === 'shortcut-handle') { if (reason === 'shortcut-handle') {
const handleResult = hotbox.dispatch(e); handleShortcut(e);
if (handleResult) {
e.preventDefault();
} else {
minder.dispatchKeyEvent(e);
}
} }
}); });

View File

@ -65,7 +65,6 @@ function isIntendToInput(e: KeyboardEvent): boolean {
function JumpingRuntime(this: IJumpingRuntime): void { function JumpingRuntime(this: IJumpingRuntime): void {
const { fsm, minder, receiver, container, hotbox } = this; const { fsm, minder, receiver, container, hotbox } = this;
const receiverElement = receiver.element; const receiverElement = receiver.element;
// normal -> * // normal -> *
receiver.listen('normal', (e: KeyboardEvent) => { receiver.listen('normal', (e: KeyboardEvent) => {
// 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable // 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable
@ -141,6 +140,7 @@ function JumpingRuntime(this: IJumpingRuntime): void {
/// /////////////////////////////////////////// /// ///////////////////////////////////////////
let downX: number; let downX: number;
let downY: number; let downY: number;
const MOUSE_LB = 1; // 左键
const MOUSE_RB = 2; // 右键 const MOUSE_RB = 2; // 右键
container.addEventListener( container.addEventListener(
@ -149,10 +149,10 @@ function JumpingRuntime(this: IJumpingRuntime): void {
if (e.button === MOUSE_RB) { if (e.button === MOUSE_RB) {
e.preventDefault(); e.preventDefault();
} }
if (fsm.state() === 'hotbox') {
hotbox.active(HotBox.STATE_IDLE); if (fsm.state() === 'hotbox' && e.button === MOUSE_LB) {
fsm.jump('normal', 'blur'); fsm.jump('normal', 'blur');
} else if (fsm.state() === 'normal' && e.button === MOUSE_RB) { } else if (e.button === MOUSE_RB) {
downX = e.clientX; downX = e.clientX;
downY = e.clientY; downY = e.clientY;
} }
@ -164,7 +164,6 @@ function JumpingRuntime(this: IJumpingRuntime): void {
'mousewheel', 'mousewheel',
() => { () => {
if (fsm.state() === 'hotbox') { if (fsm.state() === 'hotbox') {
hotbox.active(HotBox.STATE_IDLE);
fsm.jump('normal', 'mousemove-blur'); fsm.jump('normal', 'mousemove-blur');
} }
}, },
@ -178,14 +177,14 @@ function JumpingRuntime(this: IJumpingRuntime): void {
container.addEventListener( container.addEventListener(
'mouseup', 'mouseup',
(e) => { (e) => {
if (fsm.state() !== 'normal') { if (fsm.state() !== 'normal' && e.button === MOUSE_LB) {
return; return;
} }
if (e.button !== MOUSE_RB || e.clientX !== downX || e.clientY !== downY) { if (e.button !== MOUSE_RB || e.clientX !== downX || e.clientY !== downY) {
return; return;
} }
if (!minder.getSelectedNode()) { if (!minder.getSelectedNode()) {
return; return false;
} }
fsm.jump('hotbox', 'content-menu'); fsm.jump('hotbox', 'content-menu');
}, },

View File

@ -50,6 +50,13 @@ export default function NodeRuntime(this: { minder: any; hotbox: any; editText:
return; return;
} }
markDeleteNode(minder); markDeleteNode(minder);
} else if (command.indexOf('ArrangeUp') > -1 || command.indexOf('ArrangeDown') > -1) {
if (
!window.minderProps.moveEnable ||
(window.minderProps.moveConfirm && !window.minderProps.moveConfirm())
) {
return;
}
} }
minder.execCommand(command); minder.execCommand(command);
} }

View File

@ -11,33 +11,7 @@ function ReceiverRuntime(this: any) {
// 侦听器,接收到的事件会派发给所有侦听器 // 侦听器,接收到的事件会派发给所有侦听器
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const listeners: ((event: KeyboardEvent) => boolean)[] = []; const listeners: (((event: KeyboardEvent) => boolean) | string)[] = [];
const dispatchKeyEvent = (e: KeyboardEvent) => {
(e as any).is = (keyExpression: string) => {
const subs = keyExpression.split('|');
for (let i = 0; i < subs.length; i++) {
if (key.is(this, subs[i])) return true;
}
return false;
};
let listener;
for (let i = 0; i < listeners.length; i++) {
listener = listeners[i];
// 忽略不在侦听状态的侦听器
if ((listener as any).notifyState !== '*' && (listener as any).notifyState !== this.fsm.state()) {
// eslint-disable-next-line no-continue
continue;
}
if (listener.call(null, e)) {
return;
}
}
};
element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent;
this.container.appendChild(element);
interface Receiver { interface Receiver {
element: HTMLDivElement; element: HTMLDivElement;
@ -48,7 +22,7 @@ function ReceiverRuntime(this: any) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
onblur(handler: (event: FocusEvent) => void): void; onblur(handler: (event: FocusEvent) => void): void;
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
listen?(state: string, listener: (event: KeyboardEvent) => boolean): void; listen?(state: string, listener: ((event: KeyboardEvent) => boolean) | string): void;
} }
// receiver 对象 // receiver 对象
@ -76,11 +50,47 @@ function ReceiverRuntime(this: any) {
element.blur(); element.blur();
element.focus(); element.focus();
}, },
// eslint-disable-next-line no-unused-vars
onblur(handler: (event: FocusEvent) => void) { onblur(handler: (event: FocusEvent) => void) {
element.onblur = handler; element.onblur = handler;
}, },
}; };
// 侦听指定状态下的事件,如果不传 state侦听所有状态
receiver.listen = (...args) => {
let [state, listener] = args;
if (args.length <= 1) {
listener = state;
state = '*';
}
(listener as any).notifyState = state;
listeners.push(listener as any);
};
const dispatchKeyEvent = (e: KeyboardEvent) => {
(e as any).is = (keyExpression: string) => {
const subs = keyExpression.split('|');
for (let i = 0; i < subs.length; i++) {
if (key.is(this, subs[i])) return true;
}
return false;
};
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
// 忽略不在侦听状态的侦听器
if ((listener as any).notifyState !== '*' && (listener as any).notifyState !== this.fsm.state()) {
// eslint-disable-next-line no-continue
continue;
}
if (typeof listener === 'function' && listener.call(null, e)) {
return;
}
}
};
element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent;
this.container.appendChild(element);
receiver.selectAll(); receiver.selectAll();
this.minder.on('beforemousedown', receiver.selectAll); this.minder.on('beforemousedown', receiver.selectAll);
this.minder.on('receiverfocus', receiver.selectAll); this.minder.on('receiverfocus', receiver.selectAll);
@ -91,22 +101,6 @@ function ReceiverRuntime(this: any) {
window.editor.hotbox.$container.removeChild(window.editor.hotbox.$element); window.editor.hotbox.$container.removeChild(window.editor.hotbox.$element);
}); });
// 侦听指定状态下的事件,如果不传 state侦听所有状态
// eslint-disable-next-line no-unused-vars
receiver.listen = (state: string, listener: ((event: KeyboardEvent) => boolean) | string) => {
if (arguments.length === 1) {
// eslint-disable-next-line no-param-reassign
listener = state;
// eslint-disable-next-line no-param-reassign
state = '*';
}
// eslint-disable-next-line no-param-reassign
if (typeof listener === 'function') {
(listener as any).notifyState = state;
}
listeners.push(listener as any);
};
this.receiver = receiver as any; this.receiver = receiver as any;
} }

View File

@ -92,7 +92,7 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useTableStore } from '@/store'; import { useTableStore } from '@/store';
import { TableOpenDetailMode } from '@/store/modules/ms-table/types'; import { TableOpenDetailMode } from '@/store/modules/components/ms-table/types';
import { MsTableColumn } from './type'; import { MsTableColumn } from './type';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
@ -219,3 +219,4 @@
font-size: 12px; font-size: 12px;
} }
</style> </style>
@/store/modules/components/ms-table/types

View File

@ -4,7 +4,7 @@ import localforage from 'localforage';
import { MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type'; import { MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import { PageSizeMap, SelectorColumnMap, TableOpenDetailMode } from '@/store/modules/ms-table/types'; import { PageSizeMap, SelectorColumnMap, TableOpenDetailMode } from '@/store/modules/components/ms-table/types';
import { isArraysEqualWithOrder } from '@/utils/equal'; import { isArraysEqualWithOrder } from '@/utils/equal';
import { SpecialColumnEnum } from '@/enums/tableEnum'; import { SpecialColumnEnum } from '@/enums/tableEnum';

View File

@ -1,7 +1,7 @@
export default { export default {
'common.pleaseSelectMember': 'Please select member', 'common.pleaseSelectMember': 'Please select a member',
'common.add': 'Add', 'common.add': 'Add',
'common.saveAndContinue': 'Save & Continue', 'common.saveAndContinue': 'Save & continue',
'common.edit': 'Edit', 'common.edit': 'Edit',
'common.delete': 'Delete', 'common.delete': 'Delete',
'common.save': 'Save', 'common.save': 'Save',
@ -17,6 +17,23 @@ export default {
'common.close': 'Close', 'common.close': 'Close',
'common.create': 'Create', 'common.create': 'Create',
'common.update': 'Update', 'common.update': 'Update',
'common.all': 'All',
'common.operation': 'Operation',
'common.remove': 'Remove',
'common.revoked': 'Revoked',
'common.createSuccess': 'Create success',
'common.createFailed': 'Create failed',
'common.updateSuccess': 'Update success',
'common.updateFailed': 'Update failed',
'common.deleteConfirm': 'Delete confirm',
'common.deleteSuccess': 'Delete success',
'common.deleteFailed': 'Delete failed',
'common.addSuccess': 'Added successfully',
'common.addFailed': 'Add failed',
'common.editSuccess': 'Edit success',
'common.editFailed': 'Edit failed',
'common.saveSuccess': 'Save success',
'common.saveFailed': 'Save failed',
'common.confirmEnable': 'Confirm enable', 'common.confirmEnable': 'Confirm enable',
'common.confirmDisable': 'Confirm disable', 'common.confirmDisable': 'Confirm disable',
'common.confirmClose': 'Confirm close', 'common.confirmClose': 'Confirm close',
@ -25,21 +42,6 @@ export default {
'common.enableFailed': 'Enable failed', 'common.enableFailed': 'Enable failed',
'common.closeSuccess': 'Close success', 'common.closeSuccess': 'Close success',
'common.closeFailed': 'Close failed', 'common.closeFailed': 'Close failed',
'common.updateSuccess': 'Update success',
'common.updateFailed': 'Update failed',
'common.all': 'All',
'common.operation': 'Operation',
'common.remove': 'Remove',
'common.revoked': 'Revoked',
'common.deleteConfirm': 'Confirm delete?',
'common.deleteSuccess': 'Delete success',
'common.deleteFailed': 'Delete failed',
'common.addSuccess': 'Add success',
'common.addFailed': 'Add failed',
'common.editSuccess': 'Edit success',
'common.editFailed': 'Edit failed',
'common.saveSuccess': 'Save success',
'common.saveFailed': 'Save failed',
'common.operationSuccess': 'Operation success', 'common.operationSuccess': 'Operation success',
'common.operationFailed': 'Operation failed', 'common.operationFailed': 'Operation failed',
'common.removeSuccess': 'Remove success', 'common.removeSuccess': 'Remove success',
@ -48,18 +50,20 @@ export default {
'common.revokeDelete': 'Revoke delete', 'common.revokeDelete': 'Revoke delete',
'common.revokeDeleteSuccess': 'Revoke delete success', 'common.revokeDeleteSuccess': 'Revoke delete success',
'common.unSaveLeaveTitle': 'Leave this page?', 'common.unSaveLeaveTitle': 'Leave this page?',
'common.unSaveLeaveContent': 'The system may not save your changes', 'common.unSaveLeaveContent': 'Your changes may not be saved',
'common.leave': 'Leave', 'common.leave': 'Leave',
'common.rename': 'Rename', 'common.rename': 'Rename',
'common.noData': 'No data', 'common.noData': 'No data',
'common.internal': 'Internal', 'common.internal': 'Internal',
'common.custom': 'Custom', 'common.custom': 'Custom',
'common.preview': 'Preview', 'common.preview': 'Preview',
'common.fullScreen': 'Full Screen', 'common.fullScreen': 'Full screen',
'common.offFullScreen': 'Exit', 'common.offFullScreen': 'Off full screen',
'common.allSelect': 'Select All', 'common.allSelect': 'All select',
'common.setting': 'Setting', 'common.setting': 'Setting',
'common.resetDefault': 'Restore default', 'common.resetDefault': 'Reset default',
'common.pleaseSelect': 'Please Select', 'common.tagPlaceholder': 'Add tag and press Enter to end',
'common.quickAddMember': 'Quick Add Member', 'common.batchModify': 'Batch Edit',
'common.pleaseSelect': 'please choose',
'common.quickAddMember': 'Quickly add members',
}; };

View File

@ -35,10 +35,10 @@ function setI18nLanguage(locale: LocaleType) {
async function changeLocale(locale: LocaleType) { async function changeLocale(locale: LocaleType) {
const globalI18n = i18n.global; const globalI18n = i18n.global;
const currentLocale = unref(globalI18n.locale); const currentLocale = unref(globalI18n.locale);
Message.loading(currentLocale === 'zh-CN' ? '语言切换中...' : 'Language switching...');
if (currentLocale === locale) { if (currentLocale === locale) {
return locale; return locale;
} }
Message.loading(currentLocale === 'zh-CN' ? '语言切换中...' : 'Language switching...');
if (loadLocalePool.includes(locale)) { if (loadLocalePool.includes(locale)) {
setI18nLanguage(locale); setI18nLanguage(locale);

View File

@ -4,11 +4,12 @@ import useTableStore from '@/hooks/useTableStore';
import useAppStore from './modules/app'; import useAppStore from './modules/app';
import useVisitStore from './modules/app/visit'; import useVisitStore from './modules/app/visit';
import useMinderStore from './modules/components/minder-editor';
import useUserStore from './modules/user'; import useUserStore from './modules/user';
import { debouncePlugin } from './plugins'; import { debouncePlugin } from './plugins';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia().use(debouncePlugin).use(piniaPluginPersistedstate); const pinia = createPinia().use(debouncePlugin).use(piniaPluginPersistedstate);
export { useAppStore, useTableStore, useUserStore, useVisitStore }; export { useAppStore, useMinderStore, useTableStore, useUserStore, useVisitStore };
export default pinia; export default pinia;

View File

@ -0,0 +1,36 @@
import { defineStore } from 'pinia';
import { MinderNodePosition, MinderState } from './types';
// 脑图组件的 store
const useMinderStore = defineStore('minder', {
state: (): MinderState => ({
event: {
name: '',
timestamp: 0,
nodePosition: {
x: 0,
y: 0,
},
nodeDom: undefined,
nodeData: undefined,
},
mold: 0,
}),
actions: {
dispatchEvent(name: string, position: MinderNodePosition, nodeDom?: HTMLElement, nodeData?: Record<string, any>) {
this.event = {
name,
timestamp: Date.now(),
nodePosition: position,
nodeDom,
nodeData,
};
},
setMold(val: number) {
this.mold = val;
},
},
});
export default useMinderStore;

View File

@ -0,0 +1,17 @@
export interface MinderNodePosition {
x: number;
y: number;
}
export interface MinderEvent {
name: string;
timestamp: number;
nodePosition: MinderNodePosition;
nodeDom?: HTMLElement;
nodeData?: Record<string, any>;
}
export interface MinderState {
event: MinderEvent;
mold: number;
}

View File

@ -234,6 +234,32 @@ export function findNodeByKey<T>(trees: TreeNode<T>[], targetKey: string, custom
return null; // 如果在整个树形数组中都没有找到匹配的节点,则返回 null return null; // 如果在整个树形数组中都没有找到匹配的节点,则返回 null
} }
/**
* key
*/
export function findNodePathByKey<T>(
tree: TreeNode<T>[],
targetKey: string,
dataKey?: string,
customKey = 'key'
): TreeNode<T> | null {
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (dataKey ? node[dataKey]?.[customKey] === targetKey : node[customKey] === targetKey) {
return { ...node, treePath: [dataKey ? node[dataKey] : node] }; // 如果当前节点的 key 与目标 key 匹配,则返回当前节点
}
if (Array.isArray(node.children) && node.children.length > 0) {
const result = findNodePathByKey(node.children, targetKey, dataKey, customKey); // 递归在子节点中查找
if (result) {
result.treePath.unshift(dataKey ? node[dataKey] : node);
return result; // 如果在子节点中找到了匹配的节点,则返回该节点
}
}
}
return null;
}
/** /**
* *
* @param targetMap * @param targetMap

View File

@ -33,13 +33,23 @@
</div> </div>
</div> </div>
<FilterPanel v-show="isExpandFilter"></FilterPanel> <FilterPanel v-show="isExpandFilter"></FilterPanel>
<MinderEditor :tags="['模块', '用例', '前置条件', '备注', '步骤', '预期结果']" tag-enable sequence-enable /> <MinderEditor
:import-json="importJson"
:tags="['模块', '用例', '前置条件', '备注', '步骤', '预期结果']"
tag-enable
sequence-enable
@node-click="handleNodeClick"
/>
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
{{ nodeData.text }}
</MsDrawer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import MinderEditor from '@/components/pure/minder-editor/minderEditor.vue'; import MinderEditor from '@/components/pure/minder-editor/minderEditor.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue'; import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue'; import FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue';
@ -65,6 +75,62 @@
const isExpandFilterHandler = () => { const isExpandFilterHandler = () => {
isExpandFilter.value = !isExpandFilter.value; isExpandFilter.value = !isExpandFilter.value;
}; };
const visible = ref<boolean>(false);
const nodeData = ref<any>({});
const importJson = ref<any>({});
function handleNodeClick(data: any) {
if (data.resource && data.resource.includes('用例')) {
visible.value = true;
nodeData.value = data;
}
}
onBeforeMount(() => {
importJson.value = {
root: {
data: {
text: '测试用例',
id: 'xxxx',
},
children: [
{
data: {
id: 'sdasdas',
text: '模块 1',
resource: ['模块'],
},
},
{
data: {
id: 'dasdasda',
text: '模块 2',
expandState: 'collapse',
},
children: [
{
data: {
id: 'frihofiuho3f',
text: '用例 1',
resource: ['用例'],
},
},
{
data: {
id: 'df09348f034f',
text: ' 用例 2',
resource: ['用例'],
},
},
],
},
],
},
template: 'default',
};
});
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -1,11 +1,11 @@
<template> <template>
<a-spin v-if="!projectVersionStatus" :loading="loading" class="h-full w-full"> <a-spin v-if="!projectVersionStatus" :loading="loading" class="h-full w-full min-w-[930px]">
<div class="flex h-full flex-col items-center p-[24px]"> <div class="flex h-full flex-col items-center p-[24px]">
<div class="mt-[200px] text-[16px] font-medium text-[var(--color-text-1)]"> <div class="mt-[200px] text-[16px] font-medium text-[var(--color-text-1)]">
{{ t('project.projectVersion.version') }} {{ t('project.projectVersion.version') }}
</div> </div>
<div class="mt-[16px] text-[var(--color-text-4)]">{{ t('project.projectVersion.tip') }}</div> <div class="mt-[16px] text-[var(--color-text-4)]">{{ t('project.projectVersion.tip') }}</div>
<div class="mt-[24px] flex justify-between gap-[16px]"> <div class="mt-[24px] grid grid-cols-3 gap-[16px]">
<div class="tip-card"> <div class="tip-card">
<img src="@/assets/images/project_assign.png" width="78" height="60" class="tip-icon" /> <img src="@/assets/images/project_assign.png" width="78" height="60" class="tip-icon" />
<div> <div>
@ -61,8 +61,11 @@
<template #statusTitle> <template #statusTitle>
<div class="flex items-center"> <div class="flex items-center">
{{ t('project.projectVersion.status') }} {{ t('project.projectVersion.status') }}
<a-popover :title="t('project.projectVersion.statusTip')" position="right"> <a-popover position="rt">
<icon-info-circle class="ml-[4px] hover:text-[rgb(var(--primary-5))]" size="16" /> <icon-info-circle class="ml-[4px] hover:text-[rgb(var(--primary-5))]" size="16" />
<template #title>
<div class="w-[256px]"> {{ t('project.projectVersion.statusTip') }} </div>
</template>
<template #content> <template #content>
<div class="mt-[12px] w-[256px] rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]"> <div class="mt-[12px] w-[256px] rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]">
<div class="statusTipContent"> <div class="statusTipContent">
@ -172,7 +175,7 @@
:on-before-ok="handleBeforeLatestModalOk" :on-before-ok="handleBeforeLatestModalOk"
class="p-[4px]" class="p-[4px]"
title-align="start" title-align="start"
body-class="px-0 py-[8px]" body-class="p-0"
:ok-text="t('common.confirmClose')" :ok-text="t('common.confirmClose')"
:ok-button-props="{ status: 'danger' }" :ok-button-props="{ status: 'danger' }"
@cancel="handleLatestModalCancel" @cancel="handleLatestModalCancel"
@ -515,11 +518,11 @@
}, },
hideCancel: false, hideCancel: false,
}); });
} else { return false;
await toggleVersionStatus(record.id);
Message.success(t('project.projectVersion.open', { name: record.name }));
loadList();
} }
await toggleVersionStatus(record.id);
Message.success(t('project.projectVersion.open', { name: record.name }));
loadList();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -14,7 +14,7 @@ export default {
'project.projectVersion.openVersionSuccess': 'Activated successfully', 'project.projectVersion.openVersionSuccess': 'Activated successfully',
'project.projectVersion.versionName': 'Version name', 'project.projectVersion.versionName': 'Version name',
'project.projectVersion.status': 'Status', 'project.projectVersion.status': 'Status',
'project.projectVersion.latest': 'Latest version', 'project.projectVersion.latest': 'Latest',
'project.projectVersion.publishTime': 'Release time', 'project.projectVersion.publishTime': 'Release time',
'project.projectVersion.createTime': 'Creation time', 'project.projectVersion.createTime': 'Creation time',
'project.projectVersion.creator': 'Creator', 'project.projectVersion.creator': 'Creator',
@ -34,4 +34,7 @@ export default {
'project.projectVersion.confirmCloseTipContent2': 'You can switch other versions to the latest version', 'project.projectVersion.confirmCloseTipContent2': 'You can switch other versions to the latest version',
'project.projectVersion.replaceVersionPlaceholder': 'Please select an alternative version', 'project.projectVersion.replaceVersionPlaceholder': 'Please select an alternative version',
'project.projectVersion.latestVersionDeleteTip': 'The latest version cannot be deleted', 'project.projectVersion.latestVersionDeleteTip': 'The latest version cannot be deleted',
'project.projectVersion.statusTip': 'After closing, the version information drop-down box will not be displayed.',
'project.projectVersion.versionInfo': 'Version information example',
'project.projectVersion.versionLatest': 'Latest',
}; };

View File

@ -275,7 +275,10 @@
const type = ref(''); // const type = ref(''); //
const _module = ref(''); // const _module = ref(''); //
const content = ref(''); // const content = ref(''); //
const time = ref<(Date | string | number)[]>([dayjs().subtract(1, 'M').valueOf(), dayjs().valueOf()]); // const time = ref<(Date | string | number)[]>([
dayjs().subtract(1, 'M').hour(0).minute(0).second(1).valueOf(),
dayjs().valueOf(),
]); //
const selectedTime = ref<Date | string | number | undefined>(''); // const selectedTime = ref<Date | string | number | undefined>(''); //
/** /**