fix: bug 修复

This commit is contained in:
baiqi 2024-02-21 12:40:54 +08:00 committed by 刘瑞斌
parent 2ab459c6ad
commit 6a3b557e36
28 changed files with 388 additions and 235 deletions

View File

@ -70,6 +70,8 @@
}
}
.arco-modal-body {
@apply overflow-x-hidden;
color: var(--color-text-2);
}
.arco-modal-footer {

View File

@ -393,24 +393,23 @@
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
<div class="inline-flex w-[calc(100%-34px)] items-center justify-between !bg-transparent">
{t(element?.meta?.locale || '')}
{orgTrigger(element, menuSwitchOrgVisible, () => (
<div
class={collapsed.value ? 'hidden' : '!bg-transparent'} //
onMouseenter={() => {
if (xPack.value) {
// xpack
mouseEnterTimer = setTimeout(() => {
menuSwitchOrgVisible.value = true;
}, 500);
}
}}
onMouseleave={() => {
clearTimeout(mouseEnterTimer);
}}
>
<MsIcon type="icon-icon_switch_outlined1" class="text-[var(--color-text-4)]" />
</div>
))}
{xPack.value
? orgTrigger(element, menuSwitchOrgVisible, () => (
<div
class={collapsed.value ? 'hidden' : '!bg-transparent'} //
onMouseenter={() => {
mouseEnterTimer = setTimeout(() => {
menuSwitchOrgVisible.value = true;
}, 500);
}}
onMouseleave={() => {
clearTimeout(mouseEnterTimer);
}}
>
<MsIcon type="icon-icon_switch_outlined1" class="text-[var(--color-text-4)]" />
</div>
))
: ''}
</div>
</a-menu-item>
) : (

View File

@ -1,77 +1,82 @@
<template>
<div class="mb-[16px] flex items-center justify-between">
<div class="font-medium text-[var(--color-text-1)]">{{ t('ms.personal.baseInfo') }}</div>
<a-button v-if="!isEdit" type="outline" size="mini" class="p-[2px_8px]" @click="isEdit = true">
{{ t('common.update') }}
</a-button>
</div>
<div class="mb-[16px] flex items-center">
<MsAvatar :avatar="userStore.avatar || 'default'" :size="58" class="mb-[4px]" />
<a-button
type="outline"
class="arco-btn-outline--secondary ml-[8px] p-[2px_8px]"
size="mini"
@click="avatarModalVisible = true"
>
{{ t('ms.personal.changeAvatar') }}
</a-button>
</div>
<a-form v-if="isEdit" ref="baseInfoFormRef" :model="baseInfoForm" layout="vertical">
<a-form-item
field="name"
:label="t('ms.personal.name')"
:rules="[{ required: true, message: t('ms.personal.nameRequired') }]"
asterisk-position="end"
>
<a-input
v-model:modelValue="baseInfoForm.name"
:placeholder="t('ms.personal.namePlaceholder')"
:max-length="255"
/>
</a-form-item>
<a-form-item
field="email"
:label="t('ms.personal.email')"
:rules="[{ required: true, message: t('ms.personal.emailRequired') }, { validator: checkUerEmail }]"
asterisk-position="end"
>
<a-input v-model:modelValue="baseInfoForm.email" :placeholder="t('ms.personal.emailPlaceholder')" />
<MsFormItemSub :text="t('ms.personal.emailTip')" :show-fill-icon="false" />
</a-form-item>
<a-form-item
field="phone"
:label="t('ms.personal.phone')"
:rules="[{ required: true, message: t('ms.personal.phoneRequired') }, { validator: checkUerPhone }]"
asterisk-position="end"
>
<a-input
v-model:modelValue="baseInfoForm.phone"
:placeholder="t('ms.personal.phonePlaceholder')"
:max-length="11"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" class="mr-[14px]" :loading="updateLoading" @click="editBaseInfo">
<div class="flex h-full flex-col overflow-hidden">
<div class="mb-[16px] flex items-center justify-between">
<div class="font-medium text-[var(--color-text-1)]">{{ t('ms.personal.baseInfo') }}</div>
<a-button v-if="!isEdit" type="outline" size="mini" class="p-[2px_8px]" @click="isEdit = true">
{{ t('common.update') }}
</a-button>
<a-button type="secondary" :disabled="updateLoading" @click="cancelEdit">{{ t('common.cancel') }}</a-button>
</a-form-item>
</a-form>
<MsDescription v-else :descriptions="descriptions">
<template #tag>
<div v-for="org of orgList" :key="org.orgId" class="mb-[16px]">
<MsTag class="h-[26px]"> {{ org.orgName }} </MsTag>
<br />
<MsTag
v-for="project of org.projectList"
:key="project.projectId"
class="!mr-[8px] mt-[8px] !bg-[rgb(var(--primary-1))] !text-[rgb(var(--primary-5))]"
>
{{ project.projectName }}
</MsTag>
</div>
</template>
</MsDescription>
</div>
<div class="mb-[16px] flex items-center">
<MsAvatar :avatar="userStore.avatar || 'default'" :size="58" class="mb-[4px]" />
<a-button
type="outline"
class="arco-btn-outline--secondary ml-[8px] p-[2px_8px]"
size="mini"
@click="avatarModalVisible = true"
>
{{ t('ms.personal.changeAvatar') }}
</a-button>
</div>
<a-form v-if="isEdit" ref="baseInfoFormRef" :model="baseInfoForm" layout="vertical">
<a-form-item
field="name"
:label="t('ms.personal.name')"
:rules="[{ required: true, message: t('ms.personal.nameRequired') }]"
asterisk-position="end"
>
<a-input
v-model:modelValue="baseInfoForm.name"
:placeholder="t('ms.personal.namePlaceholder')"
:max-length="255"
/>
</a-form-item>
<a-form-item
field="email"
:label="t('ms.personal.email')"
:rules="[{ required: true, message: t('ms.personal.emailRequired') }, { validator: checkUerEmail }]"
asterisk-position="end"
>
<a-input v-model:modelValue="baseInfoForm.email" :placeholder="t('ms.personal.emailPlaceholder')" />
<MsFormItemSub :text="t('ms.personal.emailTip')" :show-fill-icon="false" />
</a-form-item>
<a-form-item
field="phone"
:label="t('ms.personal.phone')"
:rules="[{ required: true, message: t('ms.personal.phoneRequired') }, { validator: checkUerPhone }]"
asterisk-position="end"
>
<a-input
v-model:modelValue="baseInfoForm.phone"
:placeholder="t('ms.personal.phonePlaceholder')"
:max-length="11"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" class="mr-[14px]" :loading="updateLoading" @click="editBaseInfo">
{{ t('common.update') }}
</a-button>
<a-button type="secondary" :disabled="updateLoading" @click="cancelEdit">{{ t('common.cancel') }}</a-button>
</a-form-item>
</a-form>
<div v-else class="h-[calc(100%-118px)]">
<MsDescription :descriptions="descriptions">
<template #tag>
<div v-for="org of orgList" :key="org.orgId" class="mb-[16px]">
<MsTag class="h-[26px]" max-width="100%"> {{ org.orgName }} </MsTag>
<br />
<MsTag
v-for="project of org.projectList"
:key="project.projectId"
class="!mr-[8px] mt-[8px] !bg-[rgb(var(--primary-1))] !text-[rgb(var(--primary-5))]"
max-width="100%"
>
{{ project.projectName }}
</MsTag>
</div>
</template>
</MsDescription>
</div>
</div>
<a-modal
v-model:visible="avatarModalVisible"
:title="t('ms.personal.changeAvatar')"

View File

@ -14,7 +14,7 @@
<div v-for="config of dynamicForm" :key="config.key" class="platform-card">
<div class="mb-[16px] flex items-center">
<a-image :src="`/plugin/image/${config.key}?imagePath=static/${config.key}.jpg`" width="24"></a-image>
<div class="ml-[8px] mr-[4px] font-medium text-[var(--color-text-1)]">{{ config.name }}</div>
<div class="ml-[8px] mr-[4px] font-medium text-[var(--color-text-1)]">{{ config.pluginName }}</div>
<a-tooltip v-if="config.tooltip" :content="config.tooltip" position="right">
<icon-exclamation-circle
class="mr-[8px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"

View File

@ -10,10 +10,10 @@
}"
:target-input-search-props="props.targetInputSearchProps"
>
<template #source-title="{ countSelected, checked, indeterminate, onSelectAllChange }">
<template #source-title="{ countTotal, checked, indeterminate, onSelectAllChange }">
<div class="flex items-center gap-[8px]">
<a-checkbox :model-value="checked" :indeterminate="indeterminate" @change="onSelectAllChange" />
{{ t('ms.transfer.optional', { count: countSelected }) }}
{{ t('ms.transfer.optional', { count: countTotal }) }}
</div>
</template>
<template #target-title="{ countTotal, checked, indeterminate, onSelectAllChange, onClear }">
@ -22,7 +22,7 @@
<a-checkbox :model-value="checked" :indeterminate="indeterminate" @change="onSelectAllChange" />
{{ t('ms.transfer.selected', { count: countTotal }) }}
</div>
<MsButton type="text" @click="onClear">{{ t('ms.transfer.clear') }}</MsButton>
<MsButton type="text" :disabled="countTotal === 0" @click="onClear">{{ t('ms.transfer.clear') }}</MsButton>
</div>
</template>
<template #source="{ selectedKeys, onSelect }">

View File

@ -22,19 +22,23 @@
<slot name="item-value" :item="item">
<template v-if="item.isTag">
<slot name="tag" :item="item">
<MsTag
<a-tooltip
v-for="tag of Array.isArray(item.value) ? item.value : [item.value]"
:key="`${tag}`"
:theme="item.tagTheme || 'outline'"
:type="item.tagType || 'primary'"
:max-width="item.tagMaxWidth"
color="var(--color-text-n8)"
:class="`mb-[8px] mr-[8px] font-normal !text-[var(--color-text-1)] ${item.tagClass || ''}`"
:closable="item.closable"
@close="emit('tagClose', tag, item)"
:content="(tag as string)"
>
{{ tag }}
</MsTag>
<MsTag
:theme="item.tagTheme || 'outline'"
:type="item.tagType || 'primary'"
:max-width="item.tagMaxWidth"
color="var(--color-text-n8)"
:class="`mb-[8px] mr-[8px] font-normal !text-[var(--color-text-1)] ${item.tagClass || ''}`"
:closable="item.closable"
@close="emit('tagClose', tag, item)"
>
{{ tag }}
</MsTag>
</a-tooltip>
<span v-if="!item.showTagAdd" v-show="Array.isArray(item.value) && item.value.length === 0">-</span>
<div v-else>
<template v-if="showTagInput">
@ -42,7 +46,7 @@
ref="inputRef"
v-model.trim="addTagInput"
size="mini"
:max-length="255"
:max-length="64"
:error="!!tagInputError"
@keyup.enter="handleAddTag(item)"
@blur="handleAddTag(item)"
@ -227,7 +231,8 @@
<style lang="less" scoped>
.ms-description {
@apply flex flex-wrap;
@apply flex max-h-full flex-wrap overflow-auto;
.ms-scroll-bar();
.ms-description-item {
@apply flex;

View File

@ -2,7 +2,7 @@
<a-drawer
v-bind="props"
v-model:visible="visible"
:width="drawerWidth"
:width="fullScreen?.isFullScreen ? '100%' : drawerWidth"
:footer="props.footer"
:mask="props.mask"
:popup-container="props.popupContainer"
@ -16,23 +16,45 @@
@close="handleClose"
>
<template #title>
<slot name="title">
<div class="flex w-full items-center justify-between">
<div class="flex items-center">
<a-tooltip :content="props.title">
<span> {{ props.title }}</span>
</a-tooltip>
<div class="flex items-center justify-between gap-[4px]">
<slot name="title">
<div class="flex flex-1 items-center justify-between">
<div class="flex items-center">
<a-tooltip :content="props.title">
<span> {{ props.title }}</span>
</a-tooltip>
<slot name="headerLeft"></slot>
<a-tag v-if="titleTag" :color="props.titleTagColor" class="ml-[8px] mr-auto">
{{ props.titleTag }}
</a-tag>
<slot name="headerLeft"></slot>
<a-tag v-if="titleTag" :color="props.titleTagColor" class="ml-[8px] mr-auto">
{{ props.titleTag }}
</a-tag>
</div>
<slot name="tbutton"></slot>
</div>
<slot name="tbutton"></slot>
</slot>
<div>
<MsButton
v-if="props.showFullScreen"
type="icon"
status="secondary"
class="ms-drawer-fullscreen-btn"
@click="fullScreen?.toggleFullScreen"
>
<MsIcon
:type="fullScreen?.isFullScreen ? 'icon-icon_off_screen' : 'icon-icon_full_screen_one'"
class="ms-drawer-fullscreen-btn-icon"
size="14"
/>
{{ fullScreen?.isFullScreen ? t('common.offFullScreen') : t('common.fullScreen') }}
</MsButton>
</div>
</slot>
</div>
</template>
<div v-if="!props.disabledWidthDrag && typeof drawerWidth === 'number'" class="handle" @mousedown="startResize">
<div
v-if="!props.disabledWidthDrag && typeof drawerWidth === 'number' && !fullScreen?.isFullScreen"
class="handle"
@mousedown="startResize"
>
<icon-drag-dot-vertical class="absolute left-[-3px] top-[50%] w-[14px]" size="14" />
</div>
<a-scrollbar class="h-full overflow-y-auto">
@ -91,9 +113,13 @@
<script setup lang="ts">
import { defineAsyncComponent, ref, watch } from 'vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import type { Description } from '@/components/pure/ms-description/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import useFullScreen from '@/hooks/useFullScreen';
import { useI18n } from '@/hooks/useI18n';
import { getMaxZIndexLayer } from '@/utils/dom';
//
const MsDescription = defineAsyncComponent(() => import('@/components/pure/ms-description/index.vue'));
@ -121,6 +147,7 @@
closable?: boolean; //
noTitle?: boolean; //
drawerStyle?: Record<string, string>; //
showFullScreen?: boolean; //
}
const props = withDefaults(defineProps<DrawerProps>(), {
@ -131,6 +158,7 @@
showContinue: false,
popupContainer: 'body',
disabledWidthDrag: false,
showFullScreen: false,
okPermission: () => [], //
});
const emit = defineEmits(['update:visible', 'confirm', 'cancel', 'continue']);
@ -202,6 +230,19 @@
window.addEventListener('mouseup', handleMouseUp);
}
};
const fullScreen = ref();
watch(
() => visible.value,
(val) => {
if (val) {
nextTick(() => {
const topDrawer = getMaxZIndexLayer('.ms-drawer');
fullScreen.value = useFullScreen(topDrawer?.querySelector('.arco-drawer'));
});
}
}
);
</script>
<style lang="less" scoped>
@ -222,6 +263,20 @@
@apply w-full;
line-height: 24px;
.ms-drawer-fullscreen-btn {
border-radius: var(--border-radius-small);
color: var(--color-text-1);
.ms-drawer-fullscreen-btn-icon {
margin-right: 8px;
color: var(--color-text-1);
}
&:hover {
color: rgb(var(--primary-5));
.ms-drawer-fullscreen-btn-icon {
color: rgb(var(--primary-5));
}
}
}
}
.arco-drawer-close-btn {
@apply flex items-center;

View File

@ -145,7 +145,7 @@
<a-tooltip
v-else
placement="top"
content-class="max-w-[600px]"
content-class="max-w-[400px]"
:content="String(record[item.dataIndex as string])"
>
<div class="one-line-text">
@ -717,5 +717,10 @@
&:hover {
@apply bg-white;
}
.arco-table-sorter-icon:not(.arco-table-sorter-icon-active) {
.arco-icon-caret-up {
color: var(--color-neutral-5);
}
}
}
</style>

View File

@ -10,6 +10,8 @@
'min-width': props.width && `${props.width}ch`,
'max-width': props.maxWidth || '144px',
}"
:closable="props.closable"
@close="emit('close')"
>
<slot name="icon"></slot>
<div class="one-line-text">
@ -35,6 +37,7 @@
width?: number; // tag,max-width
maxWidth?: string;
noMargin?: boolean; // tag
closable?: boolean; //
}>(),
{
type: 'default',
@ -43,6 +46,9 @@
noMargin: false,
}
);
const emit = defineEmits<{
(e: 'close'): void;
}>();
//
const tagMargin = computed(() => {

View File

@ -9,45 +9,44 @@
:retain-input-value="props.retainInputValue"
:unique-value="props.uniqueValue"
:max-tag-count="props.maxTagCount"
:readonly="props.readonly"
@press-enter="tagInputEnter"
@blur="tagInputBlur"
>
<template v-if="props.customPrefix" #prefix>
<template v-if="$slots.prefix" #prefix>
<slot name="prefix"></slot>
</template>
<template v-if="props.customTag" #tag="{ data }">
<template v-if="$slots.tag" #tag="{ data }">
<slot name="tag" :data="data"></slot>
</template>
<template v-if="props.customSuffix" #suffix>
<template v-if="$slots.suffix" #suffix>
<slot name="suffix"></slot>
</template>
</a-input-tag>
<span v-if="isError" class="ml-[1px] text-[12px] text-[rgb(var(--danger-6))]">{{
t('common.tagInputMaxLength', { number: props.maxLength })
}}</span>
<span v-if="isError" class="ml-[1px] text-[12px] text-[rgb(var(--danger-6))]">
{{ t('common.tagInputMaxLength', { number: props.maxLength }) }}
</span>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import { Message, TagData } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n';
const props = withDefaults(
defineProps<{
modelValue: string[]; // arco BUGa-form-item :validate-trigger="['blur', 'input']"
modelValue: (string | number | TagData)[]; // arco BUGa-form-item :validate-trigger="['blur', 'input']"
inputValue?: string;
placeholder?: string;
retainInputValue?: boolean;
uniqueValue?: boolean;
allowClear?: boolean;
tagsDuplicateText?: string;
customPrefix?: boolean;
customTag?: boolean;
customSuffix?: boolean;
maxTagCount?: number;
maxLength?: number;
readonly?: boolean;
}>(),
{
retainInputValue: true,
@ -67,7 +66,7 @@
const isError = computed(
() =>
(innerInputValue.value || '').length > props.maxLength ||
innerModelValue.value.some((item) => item.length > props.maxLength)
innerModelValue.value.some((item) => item.toString().length > props.maxLength)
);
watch(
() => props.modelValue,

View File

@ -61,11 +61,12 @@ export const reviewStatusMap: ReviewStatusMap = {
color: 'rgb(var(--success-2))',
class: '!text-[rgb(var(--success-6))]',
},
ARCHIVED: {
label: 'caseManagement.caseReview.archived',
color: 'var(--color-text-n8)',
class: '!text-[var(--color-text-4)]',
},
// TODO: 第一版不上归档
// ARCHIVED: {
// label: 'caseManagement.caseReview.archived',
// color: 'var(--color-text-n8)',
// class: '!text-[var(--color-text-4)]',
// },
};
// 评审详情
export const reviewDefaultDetail: ReviewItem = {

View File

@ -108,13 +108,6 @@ export const pathMap: PathMapItem[] = [
permission: [],
level: MENU_LEVEL[2],
},
{
key: 'CASE_MANAGEMENT_CASE_RECYCLE', // 功能测试-功能用例-回收站
locale: 'menu.caseManagement.featureCaseRecycle',
route: RouteEnum.CASE_MANAGEMENT_CASE_RECYCLE,
permission: [],
level: MENU_LEVEL[2],
},
],
},
{

View File

@ -1,18 +1,33 @@
import { mergeStyles } from '@/utils/dom';
/**
* hook
* @param domRef dom ref
*/
export default function useFullScreen(domRef: Ref<HTMLElement | null | undefined>) {
export default function useFullScreen(
domRef: Ref<HTMLElement | null | undefined> | HTMLElement | Element | null | undefined
) {
const isFullScreen = ref(false);
const originalStyle = ref('');
function enterFullScreen() {
domRef.value?.setAttribute('style', 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 100;');
isFullScreen.value = true;
const dom = unref(domRef);
if (dom) {
originalStyle.value = dom.getAttribute('style') || '';
mergeStyles(
dom,
'position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 100; width: 100%; height: 100%;'
);
isFullScreen.value = true;
}
}
function exitFullscreen() {
domRef.value?.setAttribute('style', '');
isFullScreen.value = false;
const dom = unref(domRef);
if (dom) {
dom.setAttribute('style', originalStyle.value);
isFullScreen.value = false;
}
}
function toggleFullScreen() {

View File

@ -1,7 +1,7 @@
import { BatchApiParams, TableQueryParams } from '@/models/common';
// 评审状态, PREPARED: 待开始, UNDERWAY: 进行中, COMPLETED: 已完成, ARCHIVED: 已归档
export type ReviewStatus = 'PREPARED' | 'UNDERWAY' | 'COMPLETED' | 'ARCHIVED';
// 评审状态, PREPARED: 待开始, UNDERWAY: 进行中, COMPLETED: 已完成, ARCHIVED: 已归档(暂时没有)
export type ReviewStatus = 'PREPARED' | 'UNDERWAY' | 'COMPLETED';
// 评审结果UN_REVIEWED未评审UNDER_REVIEWED评审中PASS通过UN_PASS未通过RE_REVIEWED重新提审
export type ReviewResult = 'UN_REVIEWED' | 'UNDER_REVIEWED' | 'PASS' | 'UN_PASS' | 'RE_REVIEWED';
// 评审模块

View File

@ -1,3 +1,7 @@
/**
* px
* @param values px
*/
export function addPixelValues(...values: string[]) {
const pixelValues = values.filter((v) => v.endsWith('px')).map((v) => parseInt(v, 10));
const totalValue = pixelValues.reduce((acc, val) => acc + val, 0);
@ -12,4 +16,3 @@ export function translatePxToNumber(str: string): number {
const result = Number(str.replace('px', ''));
return Number.isNaN(result) ? 0 : result;
}
export default {};

View File

@ -105,3 +105,91 @@ export function getStyle(element: HTMLElement | null, prop: string | null) {
}
return null;
}
/**
*
* @param selector
*/
export function getMaxZIndexLayer(selector: string): HTMLElement | null {
const layers = document.querySelectorAll<HTMLElement>(selector);
let maxZIndex = 0;
let maxZIndexDrawer: HTMLElement | null = null;
layers.forEach((layer) => {
const zIndex = parseInt(window.getComputedStyle(layer).zIndex, 10);
if (!Number.isNaN(zIndex) && zIndex > maxZIndex) {
maxZIndex = zIndex;
maxZIndexDrawer = layer;
}
});
return maxZIndexDrawer;
}
/**
*
* @param element
* @param stylesToAdd
*/
export function mergeStyles(element: HTMLElement | Element | null, stylesToAdd: string): void {
if (element) {
const originalStyles = element.getAttribute('style') || '';
const mergedStyles: Record<string, string> = {};
const originalStylePairs = originalStyles.split(';').filter((style) => style.trim() !== '');
// 解析原有的 style 属性
originalStylePairs.forEach((pair) => {
const [key, value] = pair.split(':').map((item) => item.trim());
mergedStyles[key] = value;
});
// 解析要添加的样式属性
const stylesToAddPairs = stylesToAdd.split(';').filter((style) => style.trim() !== '');
stylesToAddPairs.forEach((pair) => {
const [key, value] = pair.split(':').map((item) => item.trim());
mergedStyles[key] = value;
});
// 构造新的 style 属性字符串
const mergedStyleString = Object.entries(mergedStyles)
.map(([key, value]) => `${key}: ${value}`)
.join(';');
// 设置新的 style 属性值
element.setAttribute('style', mergedStyleString);
}
}
/**
*
* @param element
* @param stylesToRemove
*/
export function removeStyles(element: HTMLElement | Element | null, stylesToRemove: string): void {
if (element) {
const originalStyles = element.getAttribute('style') || '';
const updatedStyles: Record<string, string> = {};
const originalStylePairs = originalStyles.split(';').filter((style) => style.trim() !== '');
// 解析原有的 style 属性
originalStylePairs.forEach((pair) => {
const [key, value] = pair.split(':').map((item) => item.trim());
updatedStyles[key] = value;
});
// 移除指定的样式属性
const stylesToRemovePairs = stylesToRemove.split(';').filter((style) => style.trim() !== '');
stylesToRemovePairs.forEach((pair) => {
const [key] = pair.split(':').map((item) => item.trim());
delete updatedStyles[key];
});
// 构造新的 style 属性字符串
const updatedStyleString = Object.entries(updatedStyles)
.map(([key, value]) => `${key}: ${value}`)
.join(';');
// 设置新的 style 属性值
element.setAttribute('style', updatedStyleString);
}
}

View File

@ -11,6 +11,7 @@
:pagination="props.pagination"
:table-data="props.tableData"
:page-change="props.pageChange"
show-full-screen
@loaded="loadedBug"
>
<template #titleRight="{ loading }">
@ -66,15 +67,6 @@
</template>
</a-dropdown>
</MsButton>
<MsButton
type="icon"
status="secondary"
class="!rounded-[var(--border-radius-small)]"
@click="toggleFullScreen"
>
<MsIcon :type="isFullScreen ? 'icon-icon_off_screen' : 'icon-icon_full_screen_one'" class="mr-1" size="16" />
{{ t('caseManagement.featureCase.fullScreen') }}
</MsButton>
</div>
</template>
<template #default>
@ -183,7 +175,6 @@
getBugDetail,
getTemplateById,
} from '@/api/modules/bug-management/index';
import useFullScreen from '@/hooks/useFullScreen';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { useAppStore } from '@/store';
@ -197,7 +188,7 @@
const route = useRoute();
const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>();
const wrapperRef = ref();
const { isFullScreen, toggleFullScreen } = useFullScreen(wrapperRef);
const { t } = useI18n();
const { openDeleteModal } = useModal();

View File

@ -14,6 +14,7 @@
:page-change="props.pageChange"
:mask-closable="true"
:edit-name="true"
show-full-screen
@loaded="loadedCase"
>
<template #titleLeft>
@ -80,15 +81,6 @@
</template>
</a-dropdown>
</MsButton>
<MsButton
type="icon"
status="secondary"
class="!rounded-[var(--border-radius-small)]"
@click="toggleFullScreen"
>
<MsIcon :type="isFullScreen ? 'icon-icon_off_screen' : 'icon-icon_full_screen_one'" class="mr-2" size="16" />
{{ t('caseManagement.featureCase.fullScreen') }}
</MsButton>
</div>
</template>
<template #default>
@ -96,12 +88,12 @@
ref="wrapperRef"
class="wrapperRef bg-white"
:style="{
height: isFullScreen ? '100%' : 'calc(100% - 86px)',
height: 'calc(100% - 86px)',
}"
>
<MsSplitBox
ref="wrapperRef"
:class="isFullScreen ? 'h-[100%]' : 'h-[calc(100% - 78px)]'"
class="h-[calc(100% - 78px)]"
expand-direction="right"
:max="0.7"
:min="0.7"
@ -246,7 +238,6 @@
getCaseDetail,
getCaseModuleTree,
} from '@/api/modules/case-management/featureCase';
import useFullScreen from '@/hooks/useFullScreen';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { useAppStore } from '@/store';
@ -266,7 +257,6 @@
const router = useRouter();
const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>();
const wrapperRef = ref();
const { isFullScreen, toggleFullScreen } = useFullScreen(wrapperRef);
const featureCaseStore = useFeatureCaseStore();
const userStore = useUserStore();
const { t } = useI18n();

View File

@ -659,7 +659,7 @@
showDrawer.value = true;
}
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
function handleChange(_fileList: MsFileItem[]) {
fileList.value = _fileList.map((e) => {
return {
...e,

View File

@ -85,7 +85,7 @@ export const executionResultMap = {
/** *
*
* @description
* @param {stafileInfotus} file
* @param fileInfo file
*/
export function convertToFile(fileInfo: AssociatedList): MsFileItem {
@ -156,5 +156,3 @@ export function getTableFields(customFields: CustomAttributes[], itemDataIndex:
return currentColumnData.defaultValue || '-';
}
}
export default {};

View File

@ -3,42 +3,36 @@
<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('caseManagement.caseReview.deleteReviewTitle', { name: props.record.name }) }}
</div>
<a-tooltip :content="props.record.name">
<div class="one-line-text text-[var(--color-text-1)]">
{{ t('caseManagement.caseReview.deleteReviewTitle', { name: characterLimit(props.record.name) }) }}
</div>
</a-tooltip>
</div>
</template>
<div v-if="props.record.status === 'COMPLETED'" class="mb-[10px]">
<!-- <div v-if="props.record.status === 'COMPLETED'" class="mb-[10px]">
<div>{{ t('caseManagement.caseReview.deleteFinishedReviewContent1') }}</div>
<div>{{ t('caseManagement.caseReview.deleteFinishedReviewContent2') }}</div>
</div>
<div v-else class="mb-[10px]">
</div> -->
<div class="mb-[10px]">
{{
props.record.status === 'UNDERWAY'
? t('caseManagement.caseReview.deleteReviewingContent')
: t('caseManagement.caseReview.deleteReviewContent', {
status: t(reviewStatusMap[props.record.status as ReviewStatus].label),
})
t('caseManagement.caseReview.deleteReviewContent', {
status: t(reviewStatusMap[props.record.status as ReviewStatus].label),
})
}}
</div>
<a-input
<!-- <a-input
v-model:model-value="confirmReviewName"
:placeholder="t('caseManagement.caseReview.deleteReviewPlaceholder')"
:max-length="255"
/>
/> -->
<template #footer>
<div class="flex items-center justify-end">
<a-button type="secondary" @click="handleDialogCancel">{{ t('common.cancel') }}</a-button>
<a-button
type="primary"
status="danger"
:disabled="confirmReviewName !== props.record.name || loading"
class="ml-[12px]"
@click="handleDeleteConfirm"
>
<a-button type="primary" status="danger" :disabled="loading" class="ml-[12px]" @click="handleDeleteConfirm">
{{ t('common.confirmDelete') }}
</a-button>
<a-button
<!-- <a-button
v-if="props.record.status === 'COMPLETED'"
:loading="loading"
type="primary"
@ -46,7 +40,7 @@
@click="handleDeleteConfirm"
>
{{ t('caseManagement.caseReview.archive') }}
</a-button>
</a-button> -->
</div>
</template>
</a-modal>
@ -60,6 +54,7 @@
import { reviewStatusMap } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { characterLimit } from '@/utils';
import { ReviewStatus } from '@/models/caseManagement/caseReview';
@ -80,7 +75,7 @@
const { t } = useI18n();
const dialogVisible = useVModel(props, 'visible', emit);
const confirmReviewName = ref('');
// const confirmReviewName = ref('');
const loading = ref(false);
function handleDialogCancel() {

View File

@ -19,7 +19,7 @@
<MsIcon :type="isExpandAll ? 'icon-icon_folder_collapse1' : 'icon-icon_folder_expansion1'" />
</MsButton>
</a-tooltip>
<popConfirm mode="add" :all-names="rootModulesName" parent-id="NONE" @add-finish="initModules">
<popConfirm mode="add" :all-names="rootModulesName" parent-id="NONE" @add-finish="() => initModules()">
<MsButton type="icon" class="!mr-0 p-[2px]">
<MsIcon
type="icon-icon_create_planarity"

View File

@ -184,7 +184,7 @@
import deleteReviewModal from './deleteReviewModal.vue';
import ModuleTree from './moduleTree.vue';
import { getReviewList, getReviewUsers } from '@/api/modules/case-management/caseReview';
import { getReviewList, getReviewUsers, moveReview } from '@/api/modules/case-management/caseReview';
import { getProjectMemberCommentOptions } from '@/api/modules/project-management/projectMember';
import { reviewStatusMap } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n';
@ -244,7 +244,7 @@
filterConfigList.value = [
{
title: 'ID',
dataIndex: 'ID',
dataIndex: 'id',
type: FilterType.INPUT,
},
{
@ -276,10 +276,10 @@
label: t(reviewStatusMap.COMPLETED.label),
value: 'COMPLETED',
},
{
label: t(reviewStatusMap.ARCHIVED.label),
value: 'ARCHIVED',
},
// {
// label: t(reviewStatusMap.ARCHIVED.label),
// value: 'ARCHIVED',
// },
],
},
},
@ -628,26 +628,26 @@
}
const moveModalVisible = ref(false);
const selectedModuleKeys = ref<(string | number)[]>([]);
const selectedModuleKeys = ref<string[]>([]);
const batchMoveFileLoading = ref(false);
async function handleReviewMove() {
try {
batchMoveFileLoading.value = true;
// await batchMoveFile({
// selectIds: isBatchMove.value ? batchParams.value?.selectedIds || [] : [activeFile.value?.id || ''],
// selectAll: !!batchParams.value?.selectAll,
// excludeIds: batchParams.value?.excludeIds || [],
// condition: { keyword: keyword.value, combine: combine.value },
// projectId: appStore.currentProjectId,
// fileType: tableFileType.value,
// moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder],
// moveModuleId: selectedModuleKeys.value[0],
// });
await moveReview({
selectIds: batchParams.value?.selectedIds || [],
selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
condition: { keyword: keyword.value },
projectId: appStore.currentProjectId,
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder],
moveModuleId: selectedModuleKeys.value[0],
});
Message.success(t('caseManagement.caseReview.batchMoveSuccess'));
tableSelected.value = [];
loadList();
resetSelector();
emit('init', { ...tableQueryParams.value });
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -664,7 +664,7 @@
/**
* 处理文件夹树节点选中事件
*/
function folderNodeSelect(keys: (string | number)[]) {
function folderNodeSelect(keys: string[]) {
selectedModuleKeys.value = keys;
}

View File

@ -51,7 +51,7 @@
class="mt-[12px]"
:disabled="submitDisabled"
:loading="submitReviewLoading"
@click="submitReview"
@click="() => submitReview()"
>
{{ t('caseManagement.caseReview.submitReview') }}
</a-button>
@ -125,7 +125,7 @@
}
//
function submitReview() {
function submitReview(done?: (close: boolean) => void) {
dialogFormRef.value?.validate(async (errors) => {
if (!errors) {
try {
@ -148,6 +148,9 @@
fileList: [] as MsFileItem[],
commentIds: [] as string[],
};
if (typeof done === 'function') {
done(true);
}
emit('done');
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -323,9 +323,9 @@
if (reviewDetail.value.status === 'COMPLETED') {
return [...fullActions];
}
if (reviewDetail.value.status === 'ARCHIVED') {
return fullActions.filter((e) => e.eventTag === 'delete');
}
// if (reviewDetail.value.status === 'ARCHIVED') {
// return fullActions.filter((e) => e.eventTag === 'delete');
// }
return fullActions.filter((e) => e.eventTag !== 'archive');
});

View File

@ -1,9 +1,9 @@
<template>
<MsCard simple no-content-padding>
<div class="flex items-center justify-between border-b border-[var(--color-text-n8)] p-[24px_24px_16px_24px]">
<a-button v-permission="['CASE_REVIEW:READ+ADD']" type="primary" @click="goCreateReview">{{
t('caseManagement.caseReview.create')
}}</a-button>
<a-button v-permission="['CASE_REVIEW:READ+ADD']" type="primary" @click="goCreateReview">
{{ t('caseManagement.caseReview.create') }}
</a-button>
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type">
<a-radio value="all">{{ t('common.all') }}</a-radio>
<a-radio value="reviewByMe">{{ t('caseManagement.caseReview.waitMyReview') }}</a-radio>

View File

@ -6,7 +6,7 @@
class="ms-modal-form ms-modal-medium"
@close="cancelInvite"
>
<a-form ref="inviteFormRef" class="rounded-[4px]" :model="emailForm" layout="vertical">
<a-form ref="inviteFormRef" class="overflow-hidden rounded-[4px]" :model="emailForm" layout="vertical">
<a-form-item
field="emails"
:label="t('system.user.inviteEmail')"

View File

@ -208,7 +208,7 @@
{{ t('system.user.importResultContent', { successNum: importSuccessCount }) }}
<div class="mx-[4px] text-[rgb(var(--danger-6))]">{{ importFailCount }}</div>
{{ t('system.user.importResultContentEnd') }}
<a-popover content-class="w-[400px] p-0" position="right">
<a-popover v-if="Object.keys(importErrorMessages).length > 0" content-class="w-[400px] p-0" position="bottom">
<MsButton type="text">
{{ t('system.user.importErrorDetail') }}
</MsButton>
@ -231,7 +231,7 @@
</div>
</div>
</div>
<div class="import-error-message-footer">
<div v-if="Object.keys(importErrorMessages).length > 8" class="import-error-message-footer">
<MsButton type="text" @click="importErrorMessageDrawerVisible = true">
{{ t('system.user.seeMore') }}
</MsButton>
@ -1037,10 +1037,10 @@
loadList();
break;
case 'allFail':
importResultTitle.value = t('system.user.importAllFailTitle');
importResultTitle.value = t('system.user.importModalTitle');
break;
case 'fail':
importResultTitle.value = t('system.user.importFailTitle');
importResultTitle.value = t('system.user.importModalTitle');
loadList();
break;
default: