← All Blocks Team 团队

Team Settings 团队成员管理

邀请栏(邮箱 + 角色 Select + 邀请按钮)+ DataGrid 成员列表(Avatar + Tag + 状态 + Dropdown 操作)。

team-settings source
TeamSettings.vue vue
<script setup lang="ts">
import { ref } from 'vue';
import {
  CfButton,
  CfDataGrid,
  CfAvatar,
  CfTag,
  CfInput,
  CfSelect,
  CfDropdown,
  CfIconButton,
} from '@chufix-design/vue';

const inviteEmail = ref('');
const inviteRole = ref('member');
const roleOptions = [
  { label: 'Owner', value: 'owner' },
  { label: 'Admin', value: 'admin' },
  { label: 'Member', value: 'member' },
  { label: 'Viewer', value: 'viewer' },
];

const cols = [
  { key: 'user', title: '成员', dataIndex: 'user' },
  { key: 'role', title: '角色', dataIndex: 'role', width: 110 },
  { key: 'status', title: '状态', dataIndex: 'status', width: 110 },
  { key: 'joined', title: '加入', dataIndex: 'joined', width: 120 },
  { key: 'actions', title: '', dataIndex: 'actions', width: 60 },
];
const rows = [
  { id: '1', name: 'Jane Liu', email: '[email protected]', avatar: 'JL', role: 'owner', status: 'active', joined: '2024-03-12' },
  { id: '2', name: 'Bob Lin', email: '[email protected]', avatar: 'BL', role: 'admin', status: 'active', joined: '2024-04-08' },
  { id: '3', name: 'Alice Chen', email: '[email protected]', avatar: 'AC', role: 'member', status: 'active', joined: '2024-09-22' },
  { id: '4', name: 'Tim Wong', email: '[email protected]', avatar: 'TW', role: 'member', status: 'pending', joined: '—' },
  { id: '5', name: 'Sue Zhang', email: '[email protected]', avatar: 'SZ', role: 'viewer', status: 'active', joined: '2025-02-14' },
];

const memberActions = [
  { label: '修改角色', value: 'role' },
  { label: '重发邀请', value: 'resend' },
  { separator: true },
  { label: '移除成员', value: 'remove', danger: true },
];

function invite() {
  if (!inviteEmail.value) return;
  alert(`邀请 ${inviteEmail.value} 作为 ${inviteRole.value}`);
  inviteEmail.value = '';
}
</script>

<template>
  <div class="team">
    <header class="team__head">
      <h2>团队成员</h2>
      <p>{{ rows.length }} 个成员 · {{ rows.filter(r => r.status === 'pending').length }} 个待加入</p>
    </header>

    <section class="team__invite">
      <CfInput v-model="inviteEmail" placeholder="输入邮箱邀请新成员…" />
      <CfSelect v-model="inviteRole" :options="roleOptions" />
      <CfButton variant="primary" :disabled="!inviteEmail" @click="invite">邀请</CfButton>
    </section>

    <section class="team__list">
      <CfDataGrid :columns="cols" :rows="rows">
        <template #cell-user="{ row }">
          <div class="team__user">
            <CfAvatar size="sm">{{ row.avatar }}</CfAvatar>
            <div class="team__user-info">
              <div class="team__user-name">{{ row.name }}</div>
              <div class="team__user-mail">{{ row.email }}</div>
            </div>
          </div>
        </template>
        <template #cell-role="{ row }">
          <CfTag :tone="row.role === 'owner' ? 'accent' : row.role === 'admin' ? 'info' : 'default'" size="sm">
            {{ row.role }}
          </CfTag>
        </template>
        <template #cell-status="{ row }">
          <CfTag :tone="row.status === 'active' ? 'success' : 'warning'" size="sm">
            {{ row.status === 'active' ? '已加入' : '待加入' }}
          </CfTag>
        </template>
        <template #cell-actions="{ row }">
          <CfDropdown :items="memberActions" @select="(v) => alert(`${v} on ${row.email}`)">
            <CfIconButton aria-label="更多">⋯</CfIconButton>
          </CfDropdown>
        </template>
      </CfDataGrid>
    </section>
  </div>
</template>

<style scoped>
.team {
  display: flex;
  flex-direction: column;
  gap: 16px;
  font-family: var(--font-sans);
}
.team__head h2 {
  margin: 0;
  font-size: var(--t-22);
  font-weight: var(--w-medium);
  color: var(--fg-1);
}
.team__head p {
  margin: 4px 0 0;
  color: var(--fg-3);
  font-size: var(--t-12);
}
.team__invite {
  display: grid;
  grid-template-columns: 1fr 160px auto;
  gap: 8px;
  padding: 12px;
  background: var(--bg-2);
  border-radius: var(--r-6);
}
.team__user {
  display: flex;
  align-items: center;
  gap: 8px;
}
.team__user-name {
  color: var(--fg-1);
  font-weight: var(--w-medium);
  font-size: var(--t-13);
}
.team__user-mail {
  color: var(--fg-3);
  font-family: var(--font-mono);
  font-size: var(--t-11);
}
</style>
TeamSettings.tsx tsx
import { useState } from 'react';
import {
  CfButton,
  CfDataGrid,
  CfAvatar,
  CfTag,
  CfInput,
  CfSelect,
  CfDropdown,
  CfIconButton,
} from '@chufix-design/react';

const roleOptions = [
  { label: 'Owner', value: 'owner' },
  { label: 'Admin', value: 'admin' },
  { label: 'Member', value: 'member' },
  { label: 'Viewer', value: 'viewer' },
];

const cols = [
  { key: 'user', title: '成员', dataIndex: 'user' },
  { key: 'role', title: '角色', dataIndex: 'role', width: 110 },
  { key: 'status', title: '状态', dataIndex: 'status', width: 110 },
  { key: 'joined', title: '加入', dataIndex: 'joined', width: 120 },
  { key: 'actions', title: '', dataIndex: 'actions', width: 60 },
];
const rows = [
  { id: '1', name: 'Jane Liu', email: '[email protected]', avatar: 'JL', role: 'owner', status: 'active', joined: '2024-03-12' },
  { id: '2', name: 'Bob Lin', email: '[email protected]', avatar: 'BL', role: 'admin', status: 'active', joined: '2024-04-08' },
  { id: '3', name: 'Alice Chen', email: '[email protected]', avatar: 'AC', role: 'member', status: 'active', joined: '2024-09-22' },
  { id: '4', name: 'Tim Wong', email: '[email protected]', avatar: 'TW', role: 'member', status: 'pending', joined: '—' },
  { id: '5', name: 'Sue Zhang', email: '[email protected]', avatar: 'SZ', role: 'viewer', status: 'active', joined: '2025-02-14' },
];

const memberActions = [
  { label: '修改角色', value: 'role' },
  { label: '重发邀请', value: 'resend' },
  { separator: true },
  { label: '移除成员', value: 'remove', danger: true },
];

export function TeamSettings() {
  const [inviteEmail, setInviteEmail] = useState('');
  const [inviteRole, setInviteRole] = useState('member');

  const invite = () => {
    if (!inviteEmail) return;
    alert(`邀请 ${inviteEmail} 作为 ${inviteRole}`);
    setInviteEmail('');
  };

  return (
    <div className="team">
      <header className="team__head">
        <h2>团队成员</h2>
        <p>{rows.length} 个成员 · {rows.filter((r) => r.status === 'pending').length} 个待加入</p>
      </header>

      <section className="team__invite">
        <CfInput value={inviteEmail} onChange={(e) => setInviteEmail(e.target.value)} placeholder="输入邮箱邀请新成员…" />
        <CfSelect value={inviteRole} onChange={setInviteRole as any} options={roleOptions} />
        <CfButton variant="primary" disabled={!inviteEmail} onClick={invite}>邀请</CfButton>
      </section>

      <section className="team__list">
        <CfDataGrid columns={cols} rows={rows} />
      </section>
    </div>
  );
}