mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-12-04 21:19:52 +08:00
feat(版本管理): 版本管理&脑图调整
This commit is contained in:
parent
684e3cf965
commit
62d7a62ce1
@ -0,0 +1,60 @@
|
||||
import MSR from '@/api/http';
|
||||
import {
|
||||
AddVersion,
|
||||
DeleteVersion,
|
||||
EnableVersion,
|
||||
GetVersionOptions,
|
||||
GetVersionStatus,
|
||||
ToggleVersionStatus,
|
||||
UpdateVersion,
|
||||
UseLatestVersion,
|
||||
VersionPage,
|
||||
} from '@/api/requrls/project-management/projectVersion';
|
||||
|
||||
import type { CommonList, TableQueryParams } from '@/models/common';
|
||||
import type { ProjectCommon, ProjectItem, ProjectVersionOption } from '@/models/projectManagement/projectVersion';
|
||||
|
||||
// 更新版本
|
||||
export function updateVersion(data: ProjectItem) {
|
||||
return MSR.post({ url: UpdateVersion, data });
|
||||
}
|
||||
|
||||
// 获取版本列表
|
||||
export function getVersionList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<ProjectItem>>({ url: VersionPage, data });
|
||||
}
|
||||
|
||||
// 添加版本
|
||||
export function addVersion(data: ProjectCommon) {
|
||||
return MSR.post({ url: AddVersion, data });
|
||||
}
|
||||
|
||||
// 切换版本状态
|
||||
export function toggleVersionStatus(id: string) {
|
||||
return MSR.get({ url: ToggleVersionStatus, params: id });
|
||||
}
|
||||
|
||||
// 切换最新版本
|
||||
export function useLatestVersion(id: string) {
|
||||
return MSR.get({ url: UseLatestVersion, params: id });
|
||||
}
|
||||
|
||||
// 开启/关闭项目版本
|
||||
export function toggleVersion(id: string) {
|
||||
return MSR.get({ url: EnableVersion, params: id });
|
||||
}
|
||||
|
||||
// 获取项目版本列表选项
|
||||
export function getVersionOptions(id: string) {
|
||||
return MSR.get<ProjectVersionOption[]>({ url: GetVersionOptions, params: id });
|
||||
}
|
||||
|
||||
// 获取版本状态
|
||||
export function getVersionStatus(id: string) {
|
||||
return MSR.get<boolean>({ url: GetVersionStatus, params: id });
|
||||
}
|
||||
|
||||
// 删除版本
|
||||
export function deleteVersion(id: string) {
|
||||
return MSR.get({ url: DeleteVersion, params: id });
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
export const UpdateVersion = '/project/version/update'; // 更新版本
|
||||
export const VersionPage = '/project/version/list'; // 版本列表
|
||||
export const AddVersion = '/project/version/add'; // 新增版本
|
||||
export const ToggleVersionStatus = '/project/version/switch/status'; // 启用/禁用版本
|
||||
export const UseLatestVersion = '/project/version/switch/latest'; // 使用最新版本
|
||||
export const EnableVersion = '/project/version/switch/enable'; // 启用版本
|
||||
export const GetVersionOptions = '/project/version/option'; // 获取版本下拉列表
|
||||
export const GetVersionStatus = '/project/version/enable'; // 获取版本状态
|
||||
export const DeleteVersion = '/project/version/delete'; // 获取版本状态
|
BIN
frontend/src/assets/images/project_assign.png
Normal file
BIN
frontend/src/assets/images/project_assign.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
frontend/src/assets/images/project_compare.png
Normal file
BIN
frontend/src/assets/images/project_compare.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
frontend/src/assets/images/project_filter.png
Normal file
BIN
frontend/src/assets/images/project_filter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
@ -171,6 +171,12 @@
|
||||
.btn-text-sec-active();
|
||||
.btn-text-sec-disabled();
|
||||
}
|
||||
.arco-btn-text--danger {
|
||||
color: rgb(var(--danger-6)) !important;
|
||||
.btn-text-danger-hover();
|
||||
.btn-text-danger-active();
|
||||
.btn-text-danger-disabled();
|
||||
}
|
||||
.arco-btn-outline {
|
||||
border: 1px solid rgb(var(--primary-5)) !important;
|
||||
color: rgb(var(--primary-5)) !important;
|
||||
|
@ -162,6 +162,21 @@
|
||||
color: rgb(var(--primary-3)) !important;
|
||||
}
|
||||
}
|
||||
.btn-text-danger-hover() {
|
||||
&:not(:disabled):hover {
|
||||
background-color: rgb(var(--danger-1)) !important;
|
||||
}
|
||||
}
|
||||
.btn-text-danger-active() {
|
||||
&:not(:disabled):active {
|
||||
background-color: var(--danger-2) !important;
|
||||
}
|
||||
}
|
||||
.btn-text-danger-disabled() {
|
||||
&:disabled {
|
||||
color: rgb(var(--danger-2)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/** 滚动条 **/
|
||||
.ms-scroll-bar() {
|
||||
|
@ -22,6 +22,7 @@ export default {
|
||||
expand_six: 'Expand six level',
|
||||
},
|
||||
insert: {
|
||||
insert: 'Insert',
|
||||
down: 'Subordinate',
|
||||
up: 'Superior',
|
||||
same: 'Same',
|
||||
|
@ -14,14 +14,9 @@ export default {
|
||||
expand: {
|
||||
expand: '展开',
|
||||
folding: '收起',
|
||||
expand_one: '展开到一级节点',
|
||||
expand_tow: '展开到二级节点',
|
||||
expand_three: '展开到三级节点',
|
||||
expand_four: '展开到四级节点',
|
||||
expand_five: '展开到五级节点',
|
||||
expand_six: '展开到六级节点',
|
||||
},
|
||||
insert: {
|
||||
insert: '插入',
|
||||
down: '插入下级主题',
|
||||
up: '插入上级主题',
|
||||
same: '插入同级主题',
|
||||
|
@ -1,56 +1,31 @@
|
||||
<template>
|
||||
<header>
|
||||
<a-tabs v-model="activeName" class="mind_tab-content">
|
||||
<a-tab-pane key="editMenu" :title="t('minder.main.header.minder')">
|
||||
<div class="mind-tab-panel">
|
||||
<edit-menu
|
||||
:minder="minder"
|
||||
:move-enable="props.moveEnable"
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
:progress-enable="props.progressEnable"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
:tags="props.tags"
|
||||
:distinct-tags="props.distinctTags"
|
||||
:del-confirm="props.delConfirm"
|
||||
/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="viewMenu" :title="t('minder.main.header.style')">
|
||||
<div class="mind-tab-panel">
|
||||
<view-menu
|
||||
v-if="props.viewMenuEnable"
|
||||
:minder="minder"
|
||||
:default-mold="props.defaultMold"
|
||||
:arrange-enable="props.arrangeEnable"
|
||||
:mold-enable="props.moldEnable"
|
||||
:font-enable="props.fontEnable"
|
||||
:style-enable="props.styleEnable"
|
||||
@mold-change="handleMoldChange"
|
||||
/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div class="mind-tab-panel">
|
||||
<edit-menu
|
||||
:minder="minder"
|
||||
:move-enable="props.moveEnable"
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
:progress-enable="props.progressEnable"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
:tags="props.tags"
|
||||
:distinct-tags="props.distinctTags"
|
||||
:del-confirm="props.delConfirm"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="headerVue" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import editMenu from '../menu/edit/editMenu.vue';
|
||||
import viewMenu from '../menu/view/viewMenu.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { delProps, editMenuProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../props';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
...editMenuProps,
|
||||
...moleProps,
|
||||
@ -60,15 +35,6 @@
|
||||
...viewMenuProps,
|
||||
minder: null,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'moldChange', data: number): void;
|
||||
}>();
|
||||
const activeName = ref('editMenu');
|
||||
|
||||
function handleMoldChange(data: number) {
|
||||
emit('moldChange', data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@ -80,42 +46,3 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
header {
|
||||
@apply bg-white;
|
||||
|
||||
font-size: 12px;
|
||||
& > ul {
|
||||
@apply m-0 flex items-center p-0;
|
||||
|
||||
height: 30px;
|
||||
background-color: #e1e1e1;
|
||||
li {
|
||||
@apply inline-flex h-full list-none;
|
||||
|
||||
width: 80px;
|
||||
line-height: 30px;
|
||||
a {
|
||||
@apply text-center no-underline;
|
||||
|
||||
font-size: 14px;
|
||||
color: #337ab7;
|
||||
}
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: #23527c;
|
||||
}
|
||||
}
|
||||
li.selected {
|
||||
@apply bg-white;
|
||||
a {
|
||||
@apply text-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.arco-tabs-content {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div class="edit-del-group">
|
||||
<div class="edit menu-btn" :disabled="textDisabled" @click="edit">
|
||||
<i class="tab-icons" />
|
||||
<span>
|
||||
{{ t('minder.commons.edit') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="del menu-btn" :disabled="removeNodeDisabled" @click="del">
|
||||
<i class="tab-icons" />
|
||||
<span>
|
||||
{{ t('minder.commons.delete') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a-button
|
||||
class="arco-btn-outline--secondary mb-[4px]"
|
||||
:disabled="removeNodeDisabled"
|
||||
type="outline"
|
||||
size="small"
|
||||
@click="del"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-minus />
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t('minder.commons.delete') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,14 +21,13 @@
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { delProps } from '../../props';
|
||||
import { isDeleteDisableNode, isDisableNode } from '../../script/tool/utils';
|
||||
import { isDeleteDisableNode } from '../../script/tool/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps(delProps);
|
||||
|
||||
let minder = reactive<any>({});
|
||||
const textDisabled = ref(true);
|
||||
const removeNodeDisabled = ref(true);
|
||||
|
||||
function checkDisabled() {
|
||||
@ -40,7 +39,6 @@
|
||||
}
|
||||
const node = minder.getSelectedNode();
|
||||
removeNodeDisabled.value = !node || !!isDeleteDisableNode(minder) || node.parent === null;
|
||||
textDisabled.value = !node || !!isDisableNode(minder);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -52,26 +50,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
function editNode() {
|
||||
if (!minder.queryCommandValue) return;
|
||||
const editor = window.minderEditor;
|
||||
const receiverElement = editor.receiver.element;
|
||||
const { fsm } = editor;
|
||||
const { receiver } = editor;
|
||||
|
||||
receiverElement.innerText = minder.queryCommandValue('text');
|
||||
fsm.jump('input', 'input-request');
|
||||
receiver.selectAll();
|
||||
}
|
||||
|
||||
function edit() {
|
||||
if (textDisabled.value || !minder.queryCommandState) {
|
||||
return;
|
||||
}
|
||||
if (minder.queryCommandState('text') !== -1) {
|
||||
editNode();
|
||||
}
|
||||
}
|
||||
function del() {
|
||||
if (removeNodeDisabled.value || !minder.queryCommandState || !minder.execCommand) {
|
||||
return;
|
||||
|
@ -1,51 +1,97 @@
|
||||
<template>
|
||||
<div class="menu-container">
|
||||
<expand />
|
||||
<selection />
|
||||
<insert-box />
|
||||
<move-box :move-enable="props.moveEnable" />
|
||||
<edit-del :del-confirm="props.delConfirm" />
|
||||
<sequence-box
|
||||
v-if="props.sequenceEnable"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
/>
|
||||
<progress-box v-if="props.progressEnable" />
|
||||
<tag-box
|
||||
v-if="props.tagEnable"
|
||||
:tags="props.tags"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:distinct-tags="props.distinctTags"
|
||||
/>
|
||||
<div class="menu-group">
|
||||
<div class="menu-item">
|
||||
<a-button class="arco-btn-outline--secondary mb-[4px]" type="outline" size="small" @click="expand">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t('minder.menu.expand.expand') }}
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a-button class="arco-btn-outline--secondary mb-[4px]" type="outline" size="small" @click="folding">
|
||||
<template #icon>
|
||||
<icon-minus />
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t('minder.menu.expand.folding') }}
|
||||
</div>
|
||||
<move-box :move-enable="props.moveEnable" />
|
||||
<insert-box />
|
||||
<edit-del :del-confirm="props.delConfirm" />
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<tag-box
|
||||
v-if="props.tagEnable"
|
||||
:tags="props.tags"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:distinct-tags="props.distinctTags"
|
||||
/>
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<sequence-box
|
||||
v-if="props.sequenceEnable"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="editMenu" setup>
|
||||
import editDel from './editDel.vue';
|
||||
import expand from './expand.vue';
|
||||
import insertBox from './insertBox.vue';
|
||||
import moveBox from './moveBox.vue';
|
||||
import progressBox from './progressBox.vue';
|
||||
import selection from './selection.vue';
|
||||
import sequenceBox from './sequenceBox.vue';
|
||||
import TagBox from './tagBox.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { delProps, editMenuProps, priorityProps, tagProps } from '../../props';
|
||||
|
||||
const props = defineProps({ ...editMenuProps, ...priorityProps, ...tagProps, ...delProps });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.menu-container {
|
||||
height: 60px;
|
||||
i {
|
||||
@apply inline-block;
|
||||
const { t } = useI18n();
|
||||
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
const hasSelectedNode = ref(false);
|
||||
let minder = reactive<any>({});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
minder = window.minder;
|
||||
if (minder.on) {
|
||||
minder.on('selectionchange', () => {
|
||||
hasSelectedNode.value = Object.keys(minder).length > 0 && minder.getSelectedNode();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 展开
|
||||
*/
|
||||
function expand() {
|
||||
const state = window.minder?.queryCommandState('Expand');
|
||||
if (state === 0) {
|
||||
// 有选中的节点
|
||||
window.minder?.execCommand('Expand');
|
||||
} else {
|
||||
window.minder?.execCommand('ExpandToLevel', 999);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
/**
|
||||
* 收起
|
||||
*/
|
||||
function folding() {
|
||||
if (hasSelectedNode.value) {
|
||||
window.minder?.execCommand('Collapse');
|
||||
} else {
|
||||
window.minder?.execCommand('ExpandToLevel', 1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,20 +1,7 @@
|
||||
<template>
|
||||
<div class="expand-group">
|
||||
<a-button class="tab-icons expand" type="text" @click="expandAll" />
|
||||
<a-dropdown :popup-max-height="false" @select="handleCommand">
|
||||
<span class="dropdown-link">
|
||||
{{ t('minder.menu.expand.expand') }}
|
||||
<icon-caret-down />
|
||||
</span>
|
||||
<template #content>
|
||||
<a-doption value="1">{{ t('minder.menu.expand.expand_one') }}</a-doption>
|
||||
<a-doption value="2">{{ t('minder.menu.expand.expand_tow') }}</a-doption>
|
||||
<a-doption value="3">{{ t('minder.menu.expand.expand_three') }}</a-doption>
|
||||
<a-doption value="4">{{ t('minder.menu.expand.expand_four') }}</a-doption>
|
||||
<a-doption value="5">{{ t('minder.menu.expand.expand_five') }}</a-doption>
|
||||
<a-doption value="6">{{ t('minder.menu.expand.expand_six') }}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-button class="tab-icons expand" type="text" @click="expand" />
|
||||
{{ t('minder.menu.expand.expand') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -23,12 +10,8 @@
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function handleCommand(command: string | number | Record<string, any> | undefined) {
|
||||
window.minder?.execCommand('ExpandToLevel', command);
|
||||
}
|
||||
|
||||
function expandAll() {
|
||||
window.minder?.execCommand('ExpandToLevel', 9999);
|
||||
function expand() {
|
||||
window.minder?.execCommand('Expand', 9999);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,25 +1,23 @@
|
||||
<template>
|
||||
<div class="insert-group">
|
||||
<div class="insert-child-box menu-btn" :disabled="appendChildNodeDisabled" @click="execCommand('AppendChildNode')">
|
||||
<i class="tab-icons" />
|
||||
<span>{{ t('minder.menu.insert.down') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="insert-parent-box menu-btn"
|
||||
:disabled="appendParentNodeDisabled"
|
||||
@click="execCommand('AppendParentNode')"
|
||||
>
|
||||
<i class="tab-icons" />
|
||||
<span>{{ t('minder.menu.insert.up') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="insert-sibling-box menu-btn"
|
||||
:disabled="appendSiblingNodeDisabled"
|
||||
@click="execCommand('AppendSiblingNode')"
|
||||
>
|
||||
<i class="tab-icons" />
|
||||
<span>{{ t('minder.menu.insert.same') }}</span>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a-dropdown @select="handleCommand">
|
||||
<a-button
|
||||
class="arco-btn-outline--secondary mb-[4px]"
|
||||
:disabled="appendChildNodeDisabled && appendParentNodeDisabled && appendSiblingNodeDisabled"
|
||||
type="outline"
|
||||
size="small"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption :disabled="appendChildNodeDisabled" value="down">{{ t('minder.menu.insert.down') }}</a-doption>
|
||||
<a-doption :disabled="appendParentNodeDisabled" value="up">{{ t('minder.menu.insert.up') }}</a-doption>
|
||||
<a-doption :disabled="appendSiblingNodeDisabled" value="same">{{ t('minder.menu.insert.same') }}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
{{ t('minder.menu.insert.insert') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -64,4 +62,20 @@
|
||||
minder.value.execCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCommand(val: string | number | Record<string, any> | undefined) {
|
||||
switch (val) {
|
||||
case 'down':
|
||||
execCommand('AppendChildNode');
|
||||
break;
|
||||
case 'up':
|
||||
execCommand('AppendParentNode');
|
||||
break;
|
||||
case 'same':
|
||||
execCommand('AppendSiblingNode');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,13 +1,31 @@
|
||||
<template>
|
||||
<div class="move-group">
|
||||
<div class="move-up menu-btn" :disabled="arrangeUpDisabled" @click="execCommand('ArrangeUp')">
|
||||
<i class="tab-icons" />
|
||||
<span>{{ t('minder.menu.move.up') }}</span>
|
||||
</div>
|
||||
<div class="move-down menu-btn" :disabled="arrangeDownDisabled" @click="execCommand('ArrangeDown')">
|
||||
<i class="tab-icons" />
|
||||
<span>{{ t('minder.menu.move.down') }}</span>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a-button
|
||||
class="arco-btn-outline--secondary mb-[4px]"
|
||||
:disabled="arrangeUpDisabled"
|
||||
type="outline"
|
||||
size="small"
|
||||
@click="execCommand('ArrangeUp')"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t('minder.menu.move.up') }}
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a-button
|
||||
class="arco-btn-outline--secondary mb-[4px]"
|
||||
:disabled="arrangeDownDisabled"
|
||||
type="outline"
|
||||
size="small"
|
||||
@click="execCommand('ArrangeDown')"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t('minder.menu.move.down') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div :disabled="commandDisabled">
|
||||
<a-button class="delete-btn !mx-[4px] !my-0 !h-[23px] !w-[23px] !p-[2px]" shape="circle" @click="execCommand()">
|
||||
<div class="flex items-center">
|
||||
<a-button
|
||||
:disabled="commandDisabled"
|
||||
class="delete-btn !mx-[4px] !my-0 !h-[23px] !w-[23px] !p-[2px]"
|
||||
shape="circle"
|
||||
@click="execCommand()"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
@ -9,6 +14,7 @@
|
||||
<a-button
|
||||
v-if="pIndex != 0"
|
||||
:key="item"
|
||||
:disabled="commandDisabled"
|
||||
class="priority-btn mr-[4px] h-[22px] w-[22px] rounded-[8px] pr-[5px] text-[12px]"
|
||||
:class="'priority-btn_' + pIndex"
|
||||
size="small"
|
||||
@ -98,7 +104,7 @@
|
||||
border-bottom: 3px solid #840023;
|
||||
background-color: #ff1200;
|
||||
}
|
||||
.priority-btn_1:hover {
|
||||
.priority-btn_1:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #840023;
|
||||
color: white;
|
||||
background-color: #ff1200;
|
||||
@ -107,7 +113,7 @@
|
||||
border-bottom: 3px solid #01467f;
|
||||
background-color: #0074ff;
|
||||
}
|
||||
.priority-btn_2:hover {
|
||||
.priority-btn_2:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #01467f;
|
||||
color: white;
|
||||
background-color: #0074ff;
|
||||
@ -116,7 +122,7 @@
|
||||
border-bottom: 3px solid #006300;
|
||||
background-color: #00af00;
|
||||
}
|
||||
.priority-btn_3:hover {
|
||||
.priority-btn_3:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #006300;
|
||||
color: white;
|
||||
background-color: #00af00;
|
||||
@ -125,7 +131,7 @@
|
||||
border-bottom: 3px solid #b25000;
|
||||
background-color: #ff962e;
|
||||
}
|
||||
.priority-btn_4:hover {
|
||||
.priority-btn_4:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #b25000;
|
||||
color: white;
|
||||
background-color: #ff962e;
|
||||
@ -134,7 +140,7 @@
|
||||
border-bottom: 3px solid #4720c4;
|
||||
background-color: #a464ff;
|
||||
}
|
||||
.priority-btn_5:hover {
|
||||
.priority-btn_5:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #4720c4;
|
||||
color: white;
|
||||
background-color: #a464ff;
|
||||
@ -143,7 +149,7 @@
|
||||
border-bottom: 3px solid #515151;
|
||||
background-color: #a3a3a3;
|
||||
}
|
||||
.priority-btn_6:hover {
|
||||
.priority-btn_6:hover:not(:disabled) {
|
||||
border-bottom: 3px solid #515151;
|
||||
color: white;
|
||||
background-color: #a3a3a3;
|
||||
|
@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div :disabled="commandDisabled">
|
||||
<div class="flex items-center">
|
||||
<a-tag
|
||||
v-for="item in props.tags"
|
||||
:key="item"
|
||||
size="small"
|
||||
:color="getResourceColor(item)"
|
||||
:class="commandDisabled ? 'disabledTag' : ''"
|
||||
@click="editResource(item)"
|
||||
>{{ item }}</a-tag
|
||||
>
|
||||
{{ item }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -92,10 +93,10 @@
|
||||
.arco-tag:first-child {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.add-btn {
|
||||
padding: 0 !important;
|
||||
width: 36px;
|
||||
height: 24px;
|
||||
border-style: dashed !important;
|
||||
.disabledTag {
|
||||
@apply !cursor-not-allowed;
|
||||
|
||||
color: var(--color-text-4);
|
||||
background-color: var(--color-secondary-disabled);
|
||||
}
|
||||
</style>
|
||||
|
@ -3,277 +3,19 @@
|
||||
@apply h-full w-full;
|
||||
.menu-container {
|
||||
@apply flex;
|
||||
& > div {
|
||||
@apply inline-flex flex-wrap items-center overflow-hidden;
|
||||
|
||||
border-right: 1px dashed #eeeeee;
|
||||
}
|
||||
& > div:last-of-type {
|
||||
@apply border-r-0;
|
||||
}
|
||||
}
|
||||
.menu-btn {
|
||||
@apply flex cursor-pointer items-center justify-center;
|
||||
}
|
||||
.menu-btn:not([disabled='true']):hover {
|
||||
background-color: rgb(var(--primary-9));
|
||||
}
|
||||
.menu-btn[disabled='true'] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.tab-icons {
|
||||
@apply inline-flex;
|
||||
height: 60px;
|
||||
.menu-group {
|
||||
@apply flex;
|
||||
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.do-group {
|
||||
@apply h-full;
|
||||
|
||||
padding: 0 5px;
|
||||
width: 40px;
|
||||
p {
|
||||
@apply m-0 flex items-center justify-center;
|
||||
|
||||
height: 50%;
|
||||
}
|
||||
.undo i {
|
||||
background-position: 0 -1240px;
|
||||
}
|
||||
.redo i {
|
||||
background-position: 0 -1220px;
|
||||
}
|
||||
}
|
||||
.insert-group {
|
||||
width: 110px;
|
||||
& > div {
|
||||
margin: 0 5px;
|
||||
}
|
||||
.insert-sibling-box {
|
||||
i {
|
||||
background-position: 0 -20px;
|
||||
padding: 4px 8px;
|
||||
gap: 4px;
|
||||
&:not(:last-child) {
|
||||
border-right: 1px dashed #eeeeee;
|
||||
}
|
||||
}
|
||||
.insert-parent-box {
|
||||
i {
|
||||
background-position: 0 -40px;
|
||||
.menu-item {
|
||||
@apply flex flex-col items-center justify-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.edit-del-group,
|
||||
.move-group {
|
||||
@apply flex items-center justify-center;
|
||||
|
||||
width: 70px;
|
||||
}
|
||||
.move-group {
|
||||
.move-up {
|
||||
i {
|
||||
background-position: 0 -280px;
|
||||
}
|
||||
}
|
||||
.move-down {
|
||||
i {
|
||||
background-position: 0 -300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.edit-del-group {
|
||||
.edit {
|
||||
i {
|
||||
background-position: 0 -60px;
|
||||
}
|
||||
}
|
||||
.del {
|
||||
i {
|
||||
background-position: 0 -80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.attachment-group {
|
||||
@apply flex items-center justify-center;
|
||||
|
||||
width: 185px;
|
||||
button {
|
||||
@apply flex items-center justify-center border-none bg-transparent bg-no-repeat p-0 outline-none;
|
||||
|
||||
width: 45px;
|
||||
height: 20px;
|
||||
background-position: right;
|
||||
span {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
button:hover {
|
||||
background-color: rgb(var(--primary-9));
|
||||
}
|
||||
& > div {
|
||||
@apply flex h-full flex-wrap items-center justify-center;
|
||||
|
||||
width: 60px;
|
||||
}
|
||||
.insert {
|
||||
@apply bg-no-repeat;
|
||||
|
||||
height: 25px;
|
||||
}
|
||||
.link {
|
||||
.insert {
|
||||
background-position: 50% -100px;
|
||||
}
|
||||
}
|
||||
.img {
|
||||
.insert {
|
||||
background-position: 50% -125px;
|
||||
}
|
||||
}
|
||||
.remark {
|
||||
.insert {
|
||||
background-position: 50% -1150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progress-group,
|
||||
.sequence-group {
|
||||
@apply flex items-center justify-center;
|
||||
|
||||
width: 135px;
|
||||
ul {
|
||||
@apply m-0 list-none p-0;
|
||||
|
||||
width: 120px;
|
||||
li {
|
||||
@apply inline-block;
|
||||
|
||||
margin: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sequence-group {
|
||||
.loop(@i) when (@i >= 0) {
|
||||
.sequence-@{i} {
|
||||
background-position: 0 -20px * (-1 + @i);
|
||||
}
|
||||
.loop(@i - 1);
|
||||
}
|
||||
.loop(9);
|
||||
}
|
||||
.progress-group {
|
||||
.loop(@i) when (@i >= 0) {
|
||||
.progress-@{i} {
|
||||
background-position: 0 -20px * (-1 + @i);
|
||||
}
|
||||
.loop(@i - 1);
|
||||
}
|
||||
.loop(9);
|
||||
}
|
||||
.arrange-group {
|
||||
width: 65px;
|
||||
.arrange {
|
||||
@apply flex flex-wrap items-center justify-center;
|
||||
}
|
||||
.tab-icons {
|
||||
@apply m-0 inline-block bg-no-repeat;
|
||||
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-position: 0 -150px;
|
||||
}
|
||||
}
|
||||
.style-group {
|
||||
width: 150px;
|
||||
.clear-style-btn {
|
||||
@apply flex flex-wrap items-center justify-center;
|
||||
|
||||
width: 65px;
|
||||
.tab-icons {
|
||||
@apply m-0 inline-block bg-no-repeat;
|
||||
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-position: 0 -175px;
|
||||
}
|
||||
}
|
||||
.copy-paste-panel {
|
||||
width: 70px;
|
||||
.tab-icons {
|
||||
@apply inline-block;
|
||||
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.copy-style {
|
||||
.tab-icons {
|
||||
background-position: 0 -200px;
|
||||
}
|
||||
}
|
||||
.paste-style {
|
||||
.tab-icons {
|
||||
background-position: 0 -220px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-group {
|
||||
width: 250px;
|
||||
* {
|
||||
font-size: 12px;
|
||||
}
|
||||
.font-family-select {
|
||||
width: 150px;
|
||||
}
|
||||
.font-size-select {
|
||||
margin-left: 5px;
|
||||
width: 80px;
|
||||
}
|
||||
.font-bold,
|
||||
.font-italic {
|
||||
@apply my-0 inline-block;
|
||||
|
||||
margin: 0 3px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.font-bold {
|
||||
background-position: 0 -242px;
|
||||
}
|
||||
.font-italic {
|
||||
background-position: 0 -262px;
|
||||
}
|
||||
}
|
||||
.expand-group,
|
||||
.selection-group {
|
||||
@apply flex items-center justify-center;
|
||||
|
||||
margin: 0 5px;
|
||||
width: 60px;
|
||||
button {
|
||||
@apply border-none outline-none;
|
||||
}
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.expand-group {
|
||||
.expand {
|
||||
width: 40px;
|
||||
height: 25px;
|
||||
background-position: center -995px;
|
||||
}
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.selection-group {
|
||||
.selection {
|
||||
width: 40px;
|
||||
height: 25px;
|
||||
background-position: 7px -1175px;
|
||||
}
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
:class="`ms-button ms-button-${props.type} ms-button--${props.status} ${
|
||||
props.disabled || props.loading ? 'ms-button--disabled' : ''
|
||||
props.disabled || props.loading ? `ms-button--${props.status}--disabled` : ''
|
||||
}`"
|
||||
@click="clickHandler"
|
||||
>
|
||||
@ -45,9 +45,13 @@
|
||||
border-radius: var(--border-radius-mini);
|
||||
line-height: 22px;
|
||||
}
|
||||
.ms-button--disabled {
|
||||
.ms-button--primary--disabled {
|
||||
color: var(--color-text-4) !important;
|
||||
}
|
||||
.ms-button--danger--disabled {
|
||||
color: rgb(var(--danger-2)) !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ms-button-text {
|
||||
@apply p-0;
|
||||
|
||||
|
@ -440,7 +440,7 @@
|
||||
return typeof props.rowClass === 'function' ? props.rowClass(record, rowIndex) : props.rowClass;
|
||||
}
|
||||
|
||||
if (!record.enable && !props.noDisable) {
|
||||
if (record.enable === false && !props.noDisable) {
|
||||
return 'ms-table-row-disabled';
|
||||
}
|
||||
}
|
||||
|
23
frontend/src/models/projectManagement/projectVersion.ts
Normal file
23
frontend/src/models/projectManagement/projectVersion.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// 项目版本公共信息
|
||||
export interface ProjectCommon {
|
||||
name: string;
|
||||
description?: string;
|
||||
status?: boolean;
|
||||
publishTime?: number;
|
||||
latest?: boolean;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
// 项目版本项
|
||||
export interface ProjectItem extends ProjectCommon {
|
||||
createTime: number;
|
||||
createUser: string;
|
||||
id: string;
|
||||
}
|
||||
// 项目版本选项列表
|
||||
export interface ProjectVersionOption {
|
||||
name: string;
|
||||
id: string;
|
||||
latest: boolean;
|
||||
enable: boolean;
|
||||
}
|
@ -33,11 +33,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<FilterPanel v-show="isExpandFilter"></FilterPanel>
|
||||
<MinderEditor :tags="['模块', '用例', '前置条件', '备注', '步骤', '预期结果']" tag-enable sequence-enable />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MinderEditor from '@/components/pure/minder-editor/minderEditor.vue';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue';
|
||||
|
||||
|
@ -1,169 +1,578 @@
|
||||
<template>
|
||||
<div>项目版本 waiting for development </div>
|
||||
<MsFormCreate v-model:form-rule="formRules" :form-create-key="FormCreateKeyEnum.ORGANIZE_TEMPLATE" />
|
||||
<br />
|
||||
<br />
|
||||
<a-spin v-if="!projectVersionStatus" :loading="loading" class="h-full w-full">
|
||||
<div class="flex h-full flex-col items-center p-[24px]">
|
||||
<div class="mt-[200px] text-[16px] font-medium text-[var(--color-text-1)]">
|
||||
{{ t('project.projectVersion.version') }}
|
||||
</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="tip-card">
|
||||
<img src="@/assets/images/project_assign.png" width="78" height="60" class="tip-icon" />
|
||||
<div>
|
||||
<div class="tip-title">{{ t('project.projectVersion.assign') }}</div>
|
||||
<div class="tip-sub-text">{{ t('project.projectVersion.assignTip') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<img src="@/assets/images/project_filter.png" width="78" height="60" class="tip-icon" />
|
||||
|
||||
<div>
|
||||
<div class="tip-title">{{ t('project.projectVersion.filter') }}</div>
|
||||
<div class="tip-sub-text">{{ t('project.projectVersion.filterTip') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<img src="@/assets/images/project_compare.png" width="78" height="60" class="tip-icon" />
|
||||
|
||||
<div>
|
||||
<div class="tip-title">{{ t('project.projectVersion.compare') }}</div>
|
||||
<div class="tip-sub-text">{{ t('project.projectVersion.compareTip') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[40px] flex justify-center">
|
||||
<a-button type="outline" @click="() => openProjectVersion(true)">
|
||||
{{ t('project.projectVersion.openVersion') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
<div v-if="projectVersionStatus">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<a-switch
|
||||
v-model:model-value="projectVersionStatus"
|
||||
size="small"
|
||||
:before-change="(val) => openProjectVersion(val)"
|
||||
>
|
||||
</a-switch>
|
||||
<span class="ml-[4px] font-medium">{{ t('project.projectVersion.version') }}</span>
|
||||
</div>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('project.projectVersion.searchPlaceholder')"
|
||||
class="w-[230px]"
|
||||
allow-clear
|
||||
@search="searchVersion"
|
||||
@press-enter="searchVersion"
|
||||
/>
|
||||
</div>
|
||||
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
|
||||
<template #statusTitle>
|
||||
<div class="flex items-center">
|
||||
{{ t('project.projectVersion.status') }}
|
||||
<a-popover :title="t('project.projectVersion.statusTip')" position="right">
|
||||
<icon-info-circle class="ml-[4px] hover:text-[rgb(var(--primary-5))]" size="16" />
|
||||
<template #content>
|
||||
<div class="mt-[12px] w-[256px] rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]">
|
||||
<div class="statusTipContent">
|
||||
<div class="px-[8px] py-[4px] text-[14px] font-medium text-[var(--color-text-1)]">
|
||||
{{ t('project.projectVersion.versionInfo') }}
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center rounded-[var(--border-radius-small)] bg-[rgb(var(--primary-1))] px-[8px] py-[4px] text-[14px] text-[rgb(var(--primary-6))]"
|
||||
>
|
||||
<div class="flex">
|
||||
v4.0
|
||||
<div class="ml-[4px] bg-[rgb(var(--primary-9))] px-[4px] text-[rgb(var(--primary-6))]">
|
||||
{{ t('project.projectVersion.versionLatest') }}
|
||||
</div>
|
||||
</div>
|
||||
<icon-check class="ml-auto text-[rgb(var(--primary-6))]" />
|
||||
</div>
|
||||
<div class="flex items-center px-[8px] py-[4px] text-[14px] text-[var(--color-text-1)]"> v3.0 </div>
|
||||
<div class="flex items-center px-[8px] py-[4px] text-[14px] text-[var(--color-text-1)]"> v2.0 </div>
|
||||
<div class="flex items-center px-[8px] py-[4px] text-[14px] text-[var(--color-text-1)]"> v1.0 </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
</template>
|
||||
<template #quickCreate>
|
||||
<a-form
|
||||
v-if="showQuickCreateForm"
|
||||
ref="quickCreateFormRef"
|
||||
:model="quickCreateForm"
|
||||
layout="inline"
|
||||
size="small"
|
||||
class="flex items-center"
|
||||
>
|
||||
<a-form-item
|
||||
field="name"
|
||||
:rules="[{ required: true, message: t('project.projectVersion.versionNameRequired') }]"
|
||||
no-style
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="quickCreateForm.name"
|
||||
:placeholder="t('project.projectVersion.versionNamePlaceholder')"
|
||||
class="w-[262px]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="publishTime" no-style>
|
||||
<a-date-picker
|
||||
v-model:model-value="quickCreateForm.publishTime"
|
||||
:placeholder="t('project.projectVersion.publishTimePlaceholder')"
|
||||
show-time
|
||||
class="ml-[8px] w-[262px]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item no-style>
|
||||
<a-button type="outline" size="mini" class="ml-[12px] mr-[8px] px-[8px]" @click="quickCreateConfirm">
|
||||
{{ t('common.confirm') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary px-[8px]"
|
||||
size="mini"
|
||||
@click="quickCreateCancel"
|
||||
>
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<MsButton v-else @click="showQuickCreateForm = true">
|
||||
<MsIcon type="icon-icon_add_outlined" size="14" class="mr-[8px]" />
|
||||
{{ t('project.projectVersion.quickCreate') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-switch
|
||||
v-model:model-value="record.status"
|
||||
size="small"
|
||||
:before-change="(val) => handleStatusChange(val, record)"
|
||||
></a-switch>
|
||||
</template>
|
||||
<template #latest="{ record }">
|
||||
<a-switch
|
||||
v-model:model-value="record.latest"
|
||||
:disabled="record.latest"
|
||||
:before-change="() => handleUseLatestVersionChange(record)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-tooltip :content="t('project.projectVersion.latestVersionDeleteTip')" :disabled="!record.latest">
|
||||
<MsButton
|
||||
type="text"
|
||||
:loading="delLoading"
|
||||
:disabled="record.latest"
|
||||
status="danger"
|
||||
size="small"
|
||||
@click="delVersion(record)"
|
||||
>
|
||||
{{ t('common.delete') }}
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<a-modal
|
||||
v-model:visible="latestModalVisible"
|
||||
title="Modal Form"
|
||||
:on-before-ok="handleBeforeLatestModalOk"
|
||||
class="p-[4px]"
|
||||
title-align="start"
|
||||
body-class="px-0 py-[8px]"
|
||||
:ok-text="t('common.confirmClose')"
|
||||
:ok-button-props="{ status: 'danger' }"
|
||||
@cancel="handleLatestModalCancel"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center justify-start">
|
||||
<icon-exclamation-circle-fill size="20" class="mr-[8px] text-[rgb(var(--danger-6))]" />
|
||||
<div class="text-[var(--color-text-1)]">
|
||||
{{ t('project.projectVersion.confirmCloseTitle', { name: activeRecord.name }) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div>{{ t('project.projectVersion.confirmCloseTipContent1') }}</div>
|
||||
<div>{{ t('project.projectVersion.confirmCloseTipContent2') }}</div>
|
||||
<a-select
|
||||
v-model:model-value="replaceVersion"
|
||||
class="mt-[10px]"
|
||||
:placeholder="t('project.projectVersion.replaceVersionPlaceholder')"
|
||||
:options="
|
||||
versionOptions
|
||||
.filter((e) => e.id !== activeRecord.id)
|
||||
.map((e) => ({ ...e, disabled: !e.enable, value: e.id, label: e.name }))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
|
||||
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||
import {
|
||||
addVersion,
|
||||
deleteVersion,
|
||||
getVersionList,
|
||||
getVersionOptions,
|
||||
getVersionStatus,
|
||||
toggleVersion,
|
||||
toggleVersionStatus,
|
||||
updateVersion,
|
||||
useLatestVersion,
|
||||
} from '@/api/modules/project-management/projectVersion';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const formRules = ref<FormItem[]>([
|
||||
{
|
||||
type: 'SELECT',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
value: '',
|
||||
subDesc: '请输入姓名',
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: '1001',
|
||||
text: '单选',
|
||||
},
|
||||
{
|
||||
value: '1002',
|
||||
text: '多选',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'MULTIPLE_SELECT',
|
||||
name: 'gender',
|
||||
label: '性别',
|
||||
value: [],
|
||||
subDesc: '请选择性别',
|
||||
optionMethod: 'getGenderOptions',
|
||||
inputSearch: true,
|
||||
required: true,
|
||||
couplingConfig: {
|
||||
type: 'initOptions',
|
||||
cascade: 'name',
|
||||
matchRule: 'includes',
|
||||
},
|
||||
import { ProjectItem, ProjectVersionOption } from '@/models/projectManagement/projectVersion';
|
||||
import { ColumnEditTypeEnum } from '@/enums/tableEnum';
|
||||
|
||||
options: [
|
||||
{
|
||||
value: '1',
|
||||
text: '单选',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
text: '多选',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'TEXTAREA',
|
||||
name: 'member',
|
||||
label: '成员',
|
||||
value: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'MULTIPLE_MEMBER',
|
||||
name: 'multiple_member',
|
||||
label: '多选成员',
|
||||
value: [],
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: '1',
|
||||
text: '单选',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
text: '多选',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'INT',
|
||||
name: 'birthday1',
|
||||
label: '出生日期',
|
||||
value: 0,
|
||||
subDesc: '请选择出生日期',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'DATE',
|
||||
name: 'birthday',
|
||||
label: '出生日期',
|
||||
value: '',
|
||||
subDesc: '请选择出生日期',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'MULTIPLE_MEMBER',
|
||||
name: 'radio',
|
||||
label: '单选',
|
||||
value: '',
|
||||
subDesc: '请选择出生日期',
|
||||
inputSearch: true,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: '1',
|
||||
text: '单选',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
text: '多选',
|
||||
},
|
||||
],
|
||||
couplingConfig: {
|
||||
type: 'initOptions',
|
||||
cascade: 'member',
|
||||
matchRule: 'includes',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'SELECT',
|
||||
name: 'selectName',
|
||||
label: '单选',
|
||||
value: '',
|
||||
subDesc: '请选择出生日期',
|
||||
inputSearch: true,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: '1',
|
||||
text: '单选',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
text: '多选',
|
||||
},
|
||||
],
|
||||
couplingConfig: {
|
||||
type: 'initOptions',
|
||||
cascade: 'member',
|
||||
matchRule: 'includes',
|
||||
},
|
||||
},
|
||||
]);
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
const { openModal } = useModal();
|
||||
|
||||
const options = ref({
|
||||
resetBtn: false,
|
||||
submitBtn: false,
|
||||
on: false,
|
||||
form: {
|
||||
layout: 'vertical',
|
||||
labelAlign: 'left',
|
||||
},
|
||||
row: {
|
||||
gutter: 0,
|
||||
},
|
||||
wrap: {
|
||||
'asterisk-position': 'end',
|
||||
'validate-trigger': ['change'],
|
||||
},
|
||||
const loading = ref(false);
|
||||
const projectVersionStatus = ref(false);
|
||||
|
||||
async function getProjectVersionStatus() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getVersionStatus(appStore.currentProjectId);
|
||||
projectVersionStatus.value = res;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换项目版本开关
|
||||
* @param val 开关
|
||||
*/
|
||||
async function openProjectVersion(val?: string | number | boolean) {
|
||||
try {
|
||||
loading.value = true;
|
||||
await toggleVersion(appStore.currentProjectId);
|
||||
Message.success(!val ? t('common.closeSuccess') : t('project.projectVersion.openVersionSuccess'));
|
||||
return true;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
return false;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
await getProjectVersionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
getProjectVersionStatus();
|
||||
});
|
||||
|
||||
/**
|
||||
* 更新版本名称
|
||||
* @param record 当前行数据
|
||||
*/
|
||||
async function updateVersionName(record: ProjectItem) {
|
||||
try {
|
||||
await updateVersion({
|
||||
...record,
|
||||
publishTime: dayjs(record.publishTime).valueOf(),
|
||||
createTime: dayjs(record.createTime).valueOf(),
|
||||
});
|
||||
Message.success(t('common.updateSuccess'));
|
||||
return Promise.resolve(true);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
const keyword = ref('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.projectVersion.versionName',
|
||||
dataIndex: 'name',
|
||||
showTooltip: true,
|
||||
editType: ColumnEditTypeEnum.INPUT,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'project.projectVersion.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
titleSlotName: 'statusTitle',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: 'project.projectVersion.latest',
|
||||
dataIndex: 'latest',
|
||||
slotName: 'latest',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: 'project.projectVersion.publishTime',
|
||||
dataIndex: 'publishTime',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: 'project.projectVersion.createTime',
|
||||
dataIndex: 'createTime',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: 'project.projectVersion.creator',
|
||||
dataIndex: 'createUser',
|
||||
},
|
||||
{
|
||||
title: 'common.operation',
|
||||
fixed: 'right',
|
||||
slotName: 'action',
|
||||
dataIndex: 'operation',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
const { propsRes, propsEvent, loadList, setKeyword, setLoadListParams } = useTable(
|
||||
getVersionList,
|
||||
{
|
||||
columns,
|
||||
size: 'default',
|
||||
},
|
||||
(item) => {
|
||||
return {
|
||||
...item,
|
||||
publishTime: item.publishTime ? dayjs(item.publishTime).format('YYYY-MM-DD HH:mm:ss') : '',
|
||||
};
|
||||
},
|
||||
updateVersionName
|
||||
);
|
||||
|
||||
function searchVersion() {
|
||||
setKeyword(keyword.value);
|
||||
loadList();
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
setLoadListParams({
|
||||
projectId: appStore.currentProjectId,
|
||||
});
|
||||
searchVersion();
|
||||
});
|
||||
|
||||
const showQuickCreateForm = ref(false);
|
||||
const quickCreateFormRef = ref<FormInstance>();
|
||||
const quickCreateForm = ref({
|
||||
name: '',
|
||||
publishTime: '',
|
||||
});
|
||||
const quickCreateLoading = ref(false);
|
||||
|
||||
function quickCreateCancel() {
|
||||
showQuickCreateForm.value = false;
|
||||
quickCreateForm.value = {
|
||||
name: '',
|
||||
publishTime: '',
|
||||
};
|
||||
quickCreateFormRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速创建版本
|
||||
*/
|
||||
function quickCreateConfirm() {
|
||||
quickCreateFormRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
quickCreateLoading.value = true;
|
||||
await addVersion({
|
||||
...quickCreateForm.value,
|
||||
projectId: appStore.currentProjectId,
|
||||
publishTime: quickCreateForm.value.publishTime
|
||||
? dayjs(quickCreateForm.value.publishTime).valueOf()
|
||||
: undefined,
|
||||
latest: false,
|
||||
status: false,
|
||||
});
|
||||
Message.success(t('common.createSuccess'));
|
||||
quickCreateCancel();
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
quickCreateLoading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const latestModalVisible = ref(false);
|
||||
const activeRecord = ref<ProjectItem>({
|
||||
id: '',
|
||||
name: '',
|
||||
latest: false,
|
||||
status: false,
|
||||
publishTime: 0,
|
||||
createTime: 0,
|
||||
createUser: '',
|
||||
projectId: '',
|
||||
});
|
||||
const replaceVersion = ref('');
|
||||
const versionOptions = ref<ProjectVersionOption[]>([]);
|
||||
|
||||
function handleLatestModalCancel() {
|
||||
latestModalVisible.value = false;
|
||||
replaceVersion.value = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截切换最新版确认
|
||||
* @param done 关闭弹窗
|
||||
*/
|
||||
async function handleBeforeLatestModalOk(done: (closed: boolean) => void) {
|
||||
try {
|
||||
if (replaceVersion.value !== '') {
|
||||
await useLatestVersion(replaceVersion.value);
|
||||
}
|
||||
await toggleVersionStatus(activeRecord.value.id);
|
||||
Message.success(t('project.projectVersion.close', { name: activeRecord.value.name }));
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
done(false);
|
||||
} finally {
|
||||
done(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截切换最新版
|
||||
* @param record 当前行数据
|
||||
*/
|
||||
async function handleUseLatestVersionChange(record: ProjectItem) {
|
||||
try {
|
||||
await useLatestVersion(record.id);
|
||||
Message.success(t('project.projectVersion.switchLatestSuccess', { name: record.name }));
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截切换状态
|
||||
* @param val 开关
|
||||
* @param type 状态类型
|
||||
* @param record 当前行数据
|
||||
*/
|
||||
async function handleStatusChange(val: string | number | boolean, record: ProjectItem) {
|
||||
try {
|
||||
if (!val && record.latest) {
|
||||
// 若关闭的是最新版,则提示需要替换最新版
|
||||
const res = await getVersionOptions(appStore.currentProjectId);
|
||||
versionOptions.value = res;
|
||||
latestModalVisible.value = true;
|
||||
activeRecord.value = record;
|
||||
return false;
|
||||
}
|
||||
if (!val) {
|
||||
// 关闭二次确认
|
||||
openModal({
|
||||
type: 'error',
|
||||
title: t('project.projectVersion.confirmCloseTitle', { name: record.name }),
|
||||
content: t('project.projectVersion.confirmCloseTip'),
|
||||
okText: t('common.confirmClose'),
|
||||
cancelText: t('common.cancel'),
|
||||
okButtonProps: {
|
||||
status: 'danger',
|
||||
},
|
||||
onBeforeOk: async () => {
|
||||
try {
|
||||
await toggleVersionStatus(record.id);
|
||||
Message.success(t('project.projectVersion.close', { name: record.name }));
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
hideCancel: false,
|
||||
});
|
||||
} else {
|
||||
await toggleVersionStatus(record.id);
|
||||
Message.success(t('project.projectVersion.open', { name: record.name }));
|
||||
loadList();
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const delLoading = ref(false);
|
||||
async function delVersion(record: ProjectItem) {
|
||||
try {
|
||||
delLoading.value = true;
|
||||
await deleteVersion(record.id);
|
||||
Message.success(t('common.deleteSuccess'));
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
delLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style lang="less" scoped>
|
||||
.tip-card {
|
||||
@apply flex;
|
||||
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
border-radius: var(--border-radius-medium);
|
||||
background-color: var(--color-text-n9);
|
||||
.tip-icon {
|
||||
box-shadow: 0 4px 10px -1px rgb(100 100 102 / 15%);
|
||||
}
|
||||
.tip-title {
|
||||
@apply font-medium;
|
||||
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.tip-sub-text {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
}
|
||||
.statusTipContent {
|
||||
@apply flex flex-col;
|
||||
|
||||
padding: 6px;
|
||||
border: 0.5px solid rgb(212 212 216 / 100%);
|
||||
border-radius: var(--border-radius-medium);
|
||||
background: white;
|
||||
box-shadow: 0 5px 5px -3px rgb(0 0 0 / 10%), 0 8px 10px 1px rgb(0 0 0 / 6%), 0 3px 14px 2px rgb(0 0 0 / 5%);
|
||||
gap: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,37 @@
|
||||
export default {
|
||||
'project.projectVersion.version': 'Project version',
|
||||
'project.projectVersion.tip':
|
||||
'Project version management is mainly divided into file management and use case management to ensure the traceability of project files during the project process.',
|
||||
'project.projectVersion.assign': 'Version designation',
|
||||
'project.projectVersion.assignTip':
|
||||
'You can specify that the content of a certain change in the resource is the content of the specified version number.',
|
||||
'project.projectVersion.compare': 'Version comparison',
|
||||
'project.projectVersion.compareTip':
|
||||
'The resource details page provides version switching and version comparison functions.',
|
||||
'project.projectVersion.filter': 'Version filter',
|
||||
'project.projectVersion.filterTip': 'Added version display column and version filtering function to resource list',
|
||||
'project.projectVersion.openVersion': 'Enable project version',
|
||||
'project.projectVersion.openVersionSuccess': 'Activated successfully',
|
||||
'project.projectVersion.versionName': 'Version name',
|
||||
'project.projectVersion.status': 'Status',
|
||||
'project.projectVersion.latest': 'Latest version',
|
||||
'project.projectVersion.publishTime': 'Release time',
|
||||
'project.projectVersion.createTime': 'Creation time',
|
||||
'project.projectVersion.creator': 'Creator',
|
||||
'project.projectVersion.searchPlaceholder': 'Search by ID/name',
|
||||
'project.projectVersion.quickCreate': 'Quickly create versions',
|
||||
'project.projectVersion.versionNamePlaceholder': 'Please enter version name',
|
||||
'project.projectVersion.versionNameRequired': 'Version name cannot be empty',
|
||||
'project.projectVersion.publishTimePlaceholder': 'Please select a release date',
|
||||
'project.projectVersion.open': '{name} is turned on',
|
||||
'project.projectVersion.close': '{name} is closed',
|
||||
'project.projectVersion.switchLatestSuccess': 'The latest version has been switched to {name}',
|
||||
'project.projectVersion.confirmCloseTitle': 'Are you sure you want to close {name}?',
|
||||
'project.projectVersion.confirmCloseTip':
|
||||
'After closing, the resource list will not display the data of this version, please operate with caution!',
|
||||
'project.projectVersion.confirmCloseTipContent1':
|
||||
'It is currently the latest version. After closing, the default display data of the list will be empty;',
|
||||
'project.projectVersion.confirmCloseTipContent2': 'You can switch other versions to the latest version',
|
||||
'project.projectVersion.replaceVersionPlaceholder': 'Please select an alternative version',
|
||||
'project.projectVersion.latestVersionDeleteTip': 'The latest version cannot be deleted',
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
export default {
|
||||
'project.projectVersion.version': '项目版本',
|
||||
'project.projectVersion.tip': '项目版本管理主要分为文件管理和用例管理,保证项目文件在项目过程中可追溯性',
|
||||
'project.projectVersion.assign': '版本指定',
|
||||
'project.projectVersion.assignTip': '可指定资源的某一次变更内容为指定版本号的内容',
|
||||
'project.projectVersion.compare': '版本对比',
|
||||
'project.projectVersion.compareTip': '资源详情页提供版本切换和版本对比的功能',
|
||||
'project.projectVersion.filter': '版本筛选',
|
||||
'project.projectVersion.filterTip': '资源列表处增加版本展示列和版本过滤功能',
|
||||
'project.projectVersion.openVersion': '启用项目版本',
|
||||
'project.projectVersion.openVersionSuccess': '启用成功',
|
||||
'project.projectVersion.versionName': '版本名称',
|
||||
'project.projectVersion.status': '状态',
|
||||
'project.projectVersion.latest': '最新版',
|
||||
'project.projectVersion.publishTime': '发布时间',
|
||||
'project.projectVersion.createTime': '创建时间',
|
||||
'project.projectVersion.creator': '创建人',
|
||||
'project.projectVersion.searchPlaceholder': '通过ID/名称搜索',
|
||||
'project.projectVersion.quickCreate': '快速创建版本',
|
||||
'project.projectVersion.versionNamePlaceholder': '请输入版本名称',
|
||||
'project.projectVersion.versionNameRequired': '版本名称不能为空',
|
||||
'project.projectVersion.publishTimePlaceholder': '请选择发布日期',
|
||||
'project.projectVersion.open': '{name} 已开启',
|
||||
'project.projectVersion.close': '{name} 已关闭',
|
||||
'project.projectVersion.switchLatestSuccess': '最新版已切换为 {name}',
|
||||
'project.projectVersion.confirmCloseTitle': '确认关闭 {name} 吗?',
|
||||
'project.projectVersion.confirmCloseTip': '关闭后,资源列表将不展示该版本的数据,请谨慎操作!',
|
||||
'project.projectVersion.confirmCloseTipContent1': '当前为最新版本,关闭后,会导致列表默认展示数据为空;',
|
||||
'project.projectVersion.confirmCloseTipContent2': '可切换其他版本为最新版本',
|
||||
'project.projectVersion.replaceVersionPlaceholder': '请选择替换版本',
|
||||
'project.projectVersion.latestVersionDeleteTip': '最新版本不可删除',
|
||||
'project.projectVersion.statusTip': '关闭后在版本信息下拉框则不显示',
|
||||
'project.projectVersion.versionInfo': '版本信息示例',
|
||||
'project.projectVersion.versionLatest': '最新版本',
|
||||
};
|
Loading…
Reference in New Issue
Block a user