feat(公共组件): 增加评论人ids

This commit is contained in:
xinxin.wu 2024-01-29 19:13:51 +08:00 committed by Craftsman
parent a3e8453536
commit b3822a3607
13 changed files with 63 additions and 15 deletions

View File

@ -795,7 +795,6 @@
/*** 徽标 ****/ /*** 徽标 ****/
.arco-badge-number { .arco-badge-number {
width: 30px !important;
height: 16px !important; height: 16px !important;
line-height: 16px; line-height: 16px;
text-align: center; text-align: center;

View File

@ -74,6 +74,8 @@ export default defineComponent({
currentItem.parentId = item.parentId || ''; currentItem.parentId = item.parentId || '';
}; };
const noticeUserIds = ref<string[]>([]);
const renderInput = (item: CommentItem) => { const renderInput = (item: CommentItem) => {
return ( return (
<CommentInput <CommentInput

View File

@ -10,7 +10,7 @@
<div class="w-full items-center"> <div class="w-full items-center">
<a-input v-if="!isActive" class="w-full" @click="isActive = true"></a-input> <a-input v-if="!isActive" class="w-full" @click="isActive = true"></a-input>
<div v-else class="flex flex-col justify-between"> <div v-else class="flex flex-col justify-between">
<MsRichText v-model:raw="currentContent" class="w-full" /> <MsRichText v-model:raw="currentContent" v-model:commentIds="commentIds" class="w-full" />
<div class="mt-4 flex flex-row justify-end gap-[12px]"> <div class="mt-4 flex flex-row justify-end gap-[12px]">
<a-button @click="cancelClick">{{ t('common.cancel') }}</a-button> <a-button @click="cancelClick">{{ t('common.cancel') }}</a-button>
<a-button type="primary" :disabled="!currentContent" @click="publish">{{ t('common.publish') }}</a-button> <a-button type="primary" :disabled="!currentContent" @click="publish">{{ t('common.publish') }}</a-button>
@ -22,6 +22,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useVModel } from '@vueuse/core';
import MsAvatar from '@/components/pure/ms-avatar/index.vue'; import MsAvatar from '@/components/pure/ms-avatar/index.vue';
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue'; import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
@ -38,6 +39,7 @@
isShowAvatar: boolean; // isShowAvatar: boolean; //
isUseBottom: boolean; // isUseBottom: boolean; //
defaultValue?: string; // defaultValue?: string; //
noticeUserIds?: string[]; // id
}>(); }>();
const currentContent = ref(props.defaultValue || ''); const currentContent = ref(props.defaultValue || '');
@ -48,6 +50,7 @@
}>(); }>();
const isActive = ref(false); const isActive = ref(false);
const commentIds = useVModel(props, 'noticeUserIds', emit);
const publish = () => { const publish = () => {
emit('publish', currentContent.value); emit('publish', currentContent.value);

View File

@ -128,6 +128,7 @@
popupContainer: 'body', popupContainer: 'body',
disabledWidthDrag: false, disabledWidthDrag: false,
okPermission: () => [], // okPermission: () => [], //
closable: true,
}); });
const emit = defineEmits(['update:visible', 'confirm', 'cancel', 'continue']); const emit = defineEmits(['update:visible', 'confirm', 'cancel', 'continue']);

View File

@ -1,5 +1,5 @@
<template> <template>
<FormCreate v-model:api="formApi" :rule="formRules" :option="option"></FormCreate> <FormCreate v-model:api="formApi" :rule="formRules" :option="props.option"></FormCreate>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -94,6 +94,7 @@
uploadImage?: (file: File) => Promise<any>; uploadImage?: (file: File) => Promise<any>;
maxHeight?: string; maxHeight?: string;
filedIds?: string[]; filedIds?: string[];
commentIds?: string[];
}>(), }>(),
{ {
raw: '', raw: '',
@ -107,9 +108,11 @@
(event: 'update:raw', value: string): void; (event: 'update:raw', value: string): void;
(event: 'update:filedIds', value: string[]): void; (event: 'update:filedIds', value: string[]): void;
(event: 'update', value: string): void; (event: 'update', value: string): void;
(event: 'update:commentIds', value: string): void;
}>(); }>();
const imagesNodes = useVModel(props, 'filedIds', emit); const imagesNodesIds = useVModel(props, 'filedIds', emit);
const commentNodeIds = useVModel(props, 'commentIds', emit);
async function asyncWorker(arg: Task): Promise<void> { async function asyncWorker(arg: Task): Promise<void> {
if (!props.uploadImage) { if (!props.uploadImage) {
@ -142,6 +145,7 @@
const attachmentSelectorModal = ref(false); const attachmentSelectorModal = ref(false);
const selectedimagesNode = ref<string>(); const selectedimagesNode = ref<string>();
const selectedCommentNode = ref<string>();
onMounted(() => { onMounted(() => {
const debounceOnUpdate = useDebounceFn(() => { const debounceOnUpdate = useDebounceFn(() => {
@ -194,7 +198,7 @@
images.push(node.attrs.fileId); images.push(node.attrs.fileId);
} }
}); });
imagesNodes.value = images; imagesNodesIds.value = images;
if (!selectedimagesNode.value) { if (!selectedimagesNode.value) {
// eslint-disable-next-line prefer-destructuring // eslint-disable-next-line prefer-destructuring
selectedimagesNode.value = images[0]; selectedimagesNode.value = images[0];
@ -206,6 +210,32 @@
]; ];
}, },
}), }),
Extension.create({
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey(Mention.name),
props: {
decorations: (state) => {
const commentUsers: string[] = [];
const { doc } = state;
doc.descendants((node) => {
if (node.type.name === 'mention') {
commentUsers.push(node.attrs.id);
}
});
commentNodeIds.value = commentUsers;
if (!selectedCommentNode.value) {
// eslint-disable-next-line prefer-destructuring
selectedCommentNode.value = commentUsers[0];
}
return DecorationSet.empty;
},
},
}),
];
},
}),
ExtensionTaskList, ExtensionTaskList,
ExtensionLink.configure({ ExtensionLink.configure({
autolink: false, autolink: false,

View File

@ -37,6 +37,7 @@ export default {
'menu.caseManagement.featureCaseRecycle': 'Recycle', 'menu.caseManagement.featureCaseRecycle': 'Recycle',
'menu.caseManagement.featureCaseList': 'Case list', 'menu.caseManagement.featureCaseList': 'Case list',
'menu.caseManagement.featureCaseDetail': 'Create Case', 'menu.caseManagement.featureCaseDetail': 'Create Case',
'menu.caseManagement.featureCaseEdit': 'Edit Case',
'menu.caseManagement.featureCaseCreateSuccess': 'Create Success', 'menu.caseManagement.featureCaseCreateSuccess': 'Create Success',
'menu.caseManagement.caseManagementReview': 'Feature Case Review', 'menu.caseManagement.caseManagementReview': 'Feature Case Review',
'menu.caseManagement.caseManagementReviewCreate': 'Create Review', 'menu.caseManagement.caseManagementReviewCreate': 'Create Review',

View File

@ -41,6 +41,7 @@ export default {
'menu.caseManagement.featureCaseRecycle': '回收站', 'menu.caseManagement.featureCaseRecycle': '回收站',
'menu.caseManagement.featureCaseList': '用例列表', 'menu.caseManagement.featureCaseList': '用例列表',
'menu.caseManagement.featureCaseDetail': '创建用例', 'menu.caseManagement.featureCaseDetail': '创建用例',
'menu.caseManagement.featureCaseEdit': '编辑用例',
'menu.caseManagement.featureCaseCreateSuccess': '创建用例成功', 'menu.caseManagement.featureCaseCreateSuccess': '创建用例成功',
'menu.caseManagement.caseManagementReview': '用例评审', 'menu.caseManagement.caseManagementReview': '用例评审',
'menu.caseManagement.caseManagementReviewCreate': '创建评审', 'menu.caseManagement.caseManagementReviewCreate': '创建评审',

View File

@ -224,6 +224,7 @@ export interface TabItemType {
key: string; key: string;
title: string; title: string;
enable: boolean; enable: boolean;
total: number;
} }
// 需求 // 需求

View File

@ -42,8 +42,9 @@ const CaseManagement: AppRouteRecordRaw = {
}, },
{ {
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL, name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
editTag: 'id',
locale: 'menu.caseManagement.featureCaseDetail', locale: 'menu.caseManagement.featureCaseDetail',
editTag: 'id',
editLocale: 'menu.caseManagement.featureCaseEdit',
}, },
], ],
}, },

View File

@ -146,6 +146,7 @@
:content="commentContent" :content="commentContent"
is-show-avatar is-show-avatar
is-use-bottom is-use-bottom
:notice-user-ids="noticeUserIds"
@publish="publishHandler" @publish="publishHandler"
/> />
</template> </template>
@ -205,6 +206,7 @@
const appStore = useAppStore(); const appStore = useAppStore();
const commentContent = ref(''); const commentContent = ref('');
const commentRef = ref(); const commentRef = ref();
const noticeUserIds = ref<string[]>([]); // ids
const currentProjectId = computed(() => appStore.currentProjectId); const currentProjectId = computed(() => appStore.currentProjectId);

View File

@ -12,6 +12,7 @@
:pagination="props.pagination" :pagination="props.pagination"
:table-data="props.tableData" :table-data="props.tableData"
:page-change="props.pageChange" :page-change="props.pageChange"
:mask-closable="true"
@loaded="loadedCase" @loaded="loadedCase"
> >
<template #titleLeft> <template #titleLeft>
@ -184,6 +185,7 @@
</div> </div>
<inputComment <inputComment
v-model:content="content" v-model:content="content"
v-model:notice-user-ids="noticeUserIds"
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']" v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
:is-active="isActive" :is-active="isActive"
is-show-avatar is-show-avatar
@ -483,8 +485,9 @@
watch( watch(
() => props.visible, () => props.visible,
(val) => { (val) => {
showDrawerVisible.value = val;
if (val) { if (val) {
showDrawerVisible.value = val;
activeTab.value = 'detail';
settingDrawerRef.value.getTabModule(); settingDrawerRef.value.getTabModule();
} }
} }
@ -508,21 +511,17 @@
const content = ref(''); const content = ref('');
const commentRef = ref(); const commentRef = ref();
const isActive = ref<boolean>(false); const isActive = ref<boolean>(false);
const noticeUserIds = ref<string[]>([]);
async function publishHandler(currentContent: string) { async function publishHandler(currentContent: string) {
const regex = /data-id="([^"]*)"/g;
const matchesNotifier = currentContent.match(regex);
let notifiers = '';
if (matchesNotifier?.length) {
notifiers = matchesNotifier.map((match) => match.replace('data-id="', '').replace('"', '')).join(';');
}
try { try {
const params: CommentParams = { const params: CommentParams = {
caseId: detailInfo.value.id, caseId: detailInfo.value.id,
notifier: notifiers, notifier: noticeUserIds.value.join(';'),
replyUser: '', replyUser: '',
parentId: '', parentId: '',
content: currentContent, content: currentContent,
event: notifiers ? 'AT' : 'COMMENT', // (: COMMENT; @: AT; /@: REPLAY;) event: noticeUserIds.value.join(';') ? 'AT' : 'COMMENT', // (: COMMENT; @: AT; /@: REPLAY;)
}; };
await createCommentList(params); await createCommentList(params);
commentRef.value.getAllCommentList(); commentRef.value.getAllCommentList();

View File

@ -82,11 +82,13 @@
key: 'requirement', key: 'requirement',
title: 'caseManagement.featureCase.requirement', title: 'caseManagement.featureCase.requirement',
enable: true, enable: true,
total: 0,
}, },
{ {
key: 'bug', key: 'bug',
title: 'caseManagement.featureCase.bug', title: 'caseManagement.featureCase.bug',
enable: true, enable: true,
total: 0,
}, },
], ],
// testPlan: [ // testPlan: [
@ -108,31 +110,37 @@
key: 'changeHistory', key: 'changeHistory',
title: 'caseManagement.featureCase.changeHistory', title: 'caseManagement.featureCase.changeHistory',
enable: true, enable: true,
total: 0,
}, },
]; ];
} }
return []; return [];
} }
const tabDefaultSettingList = ref<TabItemType[]>([ const tabDefaultSettingList = ref<TabItemType[]>([
{ {
key: 'case', key: 'case',
title: 'caseManagement.featureCase.case', title: 'caseManagement.featureCase.case',
enable: true, enable: true,
total: 0,
}, },
{ {
key: 'dependency', key: 'dependency',
title: 'caseManagement.featureCase.dependency', title: 'caseManagement.featureCase.dependency',
enable: true, enable: true,
total: 0,
}, },
{ {
key: 'caseReview', key: 'caseReview',
title: 'caseManagement.featureCase.caseReview', title: 'caseManagement.featureCase.caseReview',
enable: true, enable: true,
total: 0,
}, },
{ {
key: 'comments', key: 'comments',
title: 'caseManagement.featureCase.comments', title: 'caseManagement.featureCase.comments',
enable: true, enable: true,
total: 0,
}, },
// TOTO Xpack // TOTO Xpack
// ...getTabList(), // ...getTabList(),