refactor(projects): 1、优化新增 产品和新增项目;2、调整角色提示信息

This commit is contained in:
2026-05-18 22:25:04 +08:00
parent 2367e03146
commit acd41555f9
22 changed files with 3588 additions and 643 deletions

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import type { TableInstance } from 'element-plus';
import { filterProductMembers, formatProductMemberDate, getProductTeamTableHeight } from '../shared';
defineOptions({ name: 'SettingTeamPanel' });
@@ -15,6 +16,7 @@ interface Emits {
(e: 'create'): void;
(e: 'edit', member: Api.Product.ProductMember): void;
(e: 'remove', member: Api.Product.ProductMember): void;
(e: 'batch-remove', members: Api.Product.ProductMember[]): void;
}
const props = withDefaults(defineProps<Props>(), {
@@ -26,19 +28,41 @@ const emit = defineEmits<Emits>();
const searchKeyword = ref('');
const selectedRoleId = ref('');
const teamTableHeight = getProductTeamTableHeight(5);
const tableRef = ref<TableInstance | null>(null);
const selectedRows = ref<Api.Product.ProductMember[]>([]);
const selectedCount = computed(() => selectedRows.value.length);
function isRowSelectable(row: Api.Product.ProductMember) {
return row.status === 0 && !row.managerFlag;
}
function handleSelectionChange(rows: Api.Product.ProductMember[]) {
selectedRows.value = rows;
}
function handleBatchRemove() {
if (!selectedRows.value.length) return;
emit('batch-remove', [...selectedRows.value]);
}
function clearSelection() {
tableRef.value?.clearSelection();
selectedRows.value = [];
}
defineExpose({ clearSelection });
const roleFilterOptions = computed(() => {
const roleMap = new Map<string, string>();
const seen = new Set<string>();
const result: Api.SystemManage.RoleSimple[] = [];
props.roleOptions.forEach(role => {
if (!roleMap.has(role.id)) {
roleMap.set(role.id, role.name);
}
if (role.visible === 0) return;
if (seen.has(role.id)) return;
seen.add(role.id);
result.push(role);
});
return [...roleMap.entries()].map(([value, label]) => ({
value,
label
}));
return result;
});
const filteredMembers = computed(() =>
filterProductMembers(props.members, {
@@ -49,7 +73,7 @@ const filteredMembers = computed(() =>
const hasFilter = computed(() => Boolean(searchKeyword.value.trim() || selectedRoleId.value));
watch(roleFilterOptions, options => {
if (selectedRoleId.value && !options.some(item => item.value === selectedRoleId.value)) {
if (selectedRoleId.value && !options.some(item => item.id === selectedRoleId.value)) {
selectedRoleId.value = '';
}
});
@@ -72,12 +96,14 @@ function getMemberStatusTagType(status: Api.Product.ProductMemberStatus) {
</div>
<div class="setting-team-panel__toolbar">
<ElSelect v-model="selectedRoleId" clearable placeholder="筛选角色" class="setting-team-panel__role-filter">
<ElOption
v-for="option in roleFilterOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<ElOption v-for="role in roleFilterOptions" :key="role.id" :label="role.name" :value="role.id">
<div class="setting-team-panel__role-option">
<span class="setting-team-panel__role-option-name">{{ role.name }}</span>
<ElTooltip v-if="role.remark" :content="role.remark" placement="right" :show-after="120">
<icon-ep:info-filled class="setting-team-panel__role-option-info" @click.stop />
</ElTooltip>
</div>
</ElOption>
</ElSelect>
<ElInput v-model="searchKeyword" clearable placeholder="搜索成员姓名" class="setting-team-panel__search" />
<ElButton
@@ -89,35 +115,42 @@ function getMemberStatusTagType(status: Api.Product.ProductMemberStatus) {
>
新增成员
</ElButton>
<ElButton
v-if="!props.readonly"
v-auth="{ code: 'project:product:update', source: 'object' }"
type="danger"
plain
:disabled="selectedCount === 0"
@click="handleBatchRemove"
>
批量移出{{ selectedCount > 0 ? `${selectedCount}` : '' }}
</ElButton>
</div>
</div>
</template>
<ElTable
ref="tableRef"
v-loading="props.loading"
:data="filteredMembers"
:height="teamTableHeight"
:empty-text="hasFilter ? '未找到匹配成员' : '暂无成员'"
border
row-key="id"
@selection-change="handleSelectionChange"
>
<ElTableColumn
v-if="!props.readonly"
type="selection"
width="48"
align="center"
:selectable="(row: Api.Product.ProductMember) => isRowSelectable(row)"
/>
<ElTableColumn type="index" label="序号" width="64" align="center" />
<ElTableColumn prop="userNickname" label="成员姓名" min-width="140" />
<ElTableColumn label="当前角色" min-width="180">
<template #default="{ row }">
<div class="setting-team-panel__role-cell">
<span class="setting-team-panel__role-main">{{ row.roleName || '--' }}</span>
<ElTag
v-for="extra in row.additionalRoleNames"
:key="extra"
size="small"
type="info"
effect="plain"
class="setting-team-panel__role-extra"
>
{{ extra }}
</ElTag>
</div>
{{ row.roleName || '--' }}
</template>
</ElTableColumn>
<ElTableColumn label="成员状态" width="110" align="center">
@@ -196,15 +229,31 @@ function getMemberStatusTagType(status: Api.Product.ProductMemberStatus) {
gap: 12px;
}
.setting-team-panel__role-cell {
display: inline-flex;
.setting-team-panel__role-option {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
justify-content: space-between;
gap: 8px;
width: 100%;
}
.setting-team-panel__role-extra {
font-weight: 400;
.setting-team-panel__role-option-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.setting-team-panel__role-option-info {
flex-shrink: 0;
font-size: 14px;
color: var(--el-text-color-placeholder);
cursor: help;
}
.setting-team-panel__role-option-info:hover {
color: var(--el-color-primary);
}
@media (width <= 768px) {