chore: sync local latest state and repository cleanup

This commit is contained in:
Your Name
2026-03-23 13:02:36 +08:00
parent f1ff3d629f
commit 2ef0f17961
493 changed files with 46912 additions and 7977 deletions

View File

@@ -16,19 +16,22 @@
<option value="super_admin">超级管理员</option>
<option value="system_admin">系统管理员</option>
<option value="operation_manager">运营经理</option>
<option value="operation_member">运营</option>
<option value="operation_specialist">运营</option>
<option value="marketing_manager">市场经理</option>
<option value="marketing_member">市场</option>
<option value="marketing_specialist">市场</option>
<option value="finance_manager">财务经理</option>
<option value="finance_member">财务</option>
<option value="finance_specialist">财务</option>
<option value="risk_manager">风控经理</option>
<option value="risk_member">风控</option>
<option value="customer_service">客服</option>
<option value="risk_specialist">风控</option>
<option value="cs_agent">客服专员</option>
<option value="cs_manager">客服主管</option>
<option value="auditor">审计员</option>
<option value="viewer">只读</option>
</select>
</div>
<button class="mos-btn mos-btn-accent w-full" @click="sendInvite">发送邀请演示</button>
<PermissionButton permission="user.index.create.ALL" variant="primary" class="w-full" :disabled="loading" @click="sendInvite">
{{ loading ? '处理中...' : '发送邀请' }}
</PermissionButton>
</div>
<ListSection :page="page" :total-pages="totalPages" @prev="page--" @next="page++">
@@ -54,20 +57,22 @@
</div>
<div class="flex items-center gap-2 text-xs text-mosquito-ink/70">
<span class="rounded-full bg-mosquito-accent/10 px-2 py-1 text-[10px] font-semibold text-mosquito-brand">{{ invite.status }}</span>
<button
<PermissionButton
v-if="invite.status !== '已接受'"
class="mos-btn mos-btn-secondary !py-1 !px-2 !text-xs"
permission="user.index.update.ALL"
variant="secondary"
@click="resendInvite(invite.id)"
>
重发
</button>
<button
<span class="!py-1 !px-2 !text-xs">重发</span>
</PermissionButton>
<PermissionButton
v-if="invite.status === '待接受'"
class="mos-btn mos-btn-secondary !py-1 !px-2 !text-xs"
permission="user.index.update.ALL"
variant="secondary"
@click="expireInvite(invite.id)"
>
设为过期
</button>
<span class="!py-1 !px-2 !text-xs">设为过期</span>
</PermissionButton>
</div>
</div>
</div>
@@ -85,6 +90,7 @@ import { useAuditStore } from '../stores/audit'
import { useUserStore } from '../stores/users'
import { useDataService } from '../services'
import ListSection from '../components/ListSection.vue'
import PermissionButton from '../components/PermissionButton.vue'
import { RoleLabels, type AdminRole } from '../auth/roles'
const auditStore = useAuditStore()
@@ -94,15 +100,19 @@ const query = ref('')
const statusFilter = ref('')
const page = ref(0)
const pageSize = 6
const loading = ref(false)
const form = ref({
email: '',
role: 'operation_manager'
})
onMounted(async () => {
const invites = await service.getInvites()
userStore.init([], invites, [])
})
// 状态映射:后端英文 -> 前端中文
const statusMap: Record<string, string> = {
'PENDING': '待接受',
'ACCEPTED': '已接受',
'REJECTED': '已拒绝',
'EXPIRED': '已过期'
}
const roleLabel = (role: string) => {
return RoleLabels[role as AdminRole] || role
@@ -110,23 +120,85 @@ const roleLabel = (role: string) => {
const formatDate = (value: string) => new Date(value).toLocaleString('zh-CN')
const sendInvite = () => {
userStore.addInvite(form.value.email || '未填写邮箱', form.value.role as AdminRole)
auditStore.addLog('发送用户邀请', form.value.email || '未填写邮箱')
form.value.email = ''
form.value.role = 'operation_manager'
// 从后端数据转换为前端显示格式
const convertBackendInvite = (invite: any) => ({
id: invite.id,
email: invite.email,
role: invite.role,
status: statusMap[invite.status] || invite.status,
statusRaw: invite.status,
invitedAt: invite.invitedAt,
expiredAt: invite.expiredAt
})
// 加载邀请列表
const loadInvites = async () => {
loading.value = true
try {
const invites = await service.getInvites()
const converted = Array.isArray(invites) ? invites.map(convertBackendInvite) : []
userStore.init([], converted, [])
} catch (error) {
console.error('加载邀请列表失败:', error)
} finally {
loading.value = false
}
}
const resendInvite = (id: string) => {
userStore.resendInvite(id)
const invite = userStore.invites.find((item) => item.id === id)
auditStore.addLog('重发邀请', invite?.email ?? id)
onMounted(async () => {
await loadInvites()
})
const sendInvite = async () => {
if (!form.value.email) {
alert('请输入邮箱地址')
return
}
loading.value = true
try {
await service.createInvite(form.value.email, form.value.role)
auditStore.addLog('发送用户邀请', form.value.email)
form.value.email = ''
form.value.role = 'operation_manager'
await loadInvites()
alert('邀请发送成功')
} catch (error: any) {
alert(error.message || '发送邀请失败')
} finally {
loading.value = false
}
}
const expireInvite = (id: string) => {
userStore.expireInvite(id)
const invite = userStore.invites.find((item) => item.id === id)
auditStore.addLog('设置邀请过期', invite?.email ?? id)
const resendInvite = async (id: number | string) => {
const numericId = typeof id === 'string' ? parseInt(id, 10) || 0 : id
loading.value = true
try {
await service.resendInvite(numericId)
const invite = userStore.invites.find((item) => String(item.id) === String(id))
auditStore.addLog('重发邀请', invite?.email ?? String(id))
await loadInvites()
alert('邀请重发成功')
} catch (error: any) {
alert(error.message || '重发邀请失败')
} finally {
loading.value = false
}
}
const expireInvite = async (id: number | string) => {
const numericId = typeof id === 'string' ? parseInt(id, 10) || 0 : id
loading.value = true
try {
await service.expireInvite(numericId)
const invite = userStore.invites.find((item) => String(item.id) === String(id))
auditStore.addLog('设置邀请过期', invite?.email ?? String(id))
await loadInvites()
alert('邀请已设置为过期')
} catch (error: any) {
alert(error.message || '设置邀请过期失败')
} finally {
loading.value = false
}
}
const filteredInvites = computed(() => {