|
|
@@ -21,7 +21,7 @@
|
|
|
</div>
|
|
|
<p class="empty-text">您还没有加入任何企业</p>
|
|
|
|
|
|
- <el-button type="primary" @click="showCreateDialog = true">
|
|
|
+ <el-button type="primary" @click="openCreateDialog">
|
|
|
创建企业
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -48,45 +48,37 @@
|
|
|
|
|
|
<!-- 员工列表表格 -->
|
|
|
<div class="table-section">
|
|
|
- <el-table
|
|
|
- ref="employeeTable"
|
|
|
- :data="employeeList"
|
|
|
- style="width: 100%"
|
|
|
- :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: '500' }"
|
|
|
- empty-text="暂无员工数据"
|
|
|
- @selection-change="handleSelectionChange"
|
|
|
- >
|
|
|
+ <el-table ref="employeeTable" :data="employeeList" style="width: 100%"
|
|
|
+ :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: '500' }" empty-text="暂无员工数据"
|
|
|
+ @selection-change="handleSelectionChange">
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
<el-table-column prop="id" label="编号" width="100" align="center" />
|
|
|
<el-table-column prop="name" label="姓名" min-width="150" align="center" />
|
|
|
<el-table-column prop="email" label="邮箱" min-width="200" align="center" />
|
|
|
- <el-table-column prop="status" label="状态" width="120" align="center">
|
|
|
+ <el-table-column prop="role_text" label="角色" width="140" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="getStatusType(row.status)" size="small">
|
|
|
- {{ getStatusText(row.status) }}
|
|
|
+ <el-tag :type="row.role === 1 ? 'success' : 'info'" size="small">
|
|
|
+ {{ row.role_text }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="createdAt" label="注册时间" width="180" align="center">
|
|
|
+ <el-table-column prop="status" label="状态" width="120" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- {{ formatDate(row.createdAt || row.created_time || row.createdTime) }}
|
|
|
+ <el-tag :type="getStatusType(row.status)" size="small">
|
|
|
+ {{ row.status_text }}
|
|
|
+ </el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column prop="join_time_formatted" label="加入时间" width="160" align="center" />
|
|
|
+
|
|
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- @click="handleOpenProductSeatDialog([row.id])"
|
|
|
- >
|
|
|
+ <el-button type="primary" size="small" :disabled="!row.can_operate"
|
|
|
+ @click="handleOpenProductSeatDialog([row.id])">
|
|
|
产品分配
|
|
|
</el-button>
|
|
|
- <el-button
|
|
|
- type="danger"
|
|
|
- size="small"
|
|
|
- :disabled="row.id === currentUserId"
|
|
|
- @click="handleDeleteMember(row)"
|
|
|
- >
|
|
|
+ <el-button type="danger" size="small" :disabled="!row.can_operate || row.id === currentUserId"
|
|
|
+ @click="handleDeleteMember(row)">
|
|
|
删除
|
|
|
</el-button>
|
|
|
</template>
|
|
|
@@ -100,19 +92,11 @@
|
|
|
<span>已选择 <strong>{{ selectedEmployeeIds.length }}</strong> 位员工</span>
|
|
|
</div>
|
|
|
<div class="batch-buttons">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- :icon="Goods"
|
|
|
- @click="handleOpenProductSeatDialog(selectedEmployeeIds)"
|
|
|
- >
|
|
|
+ <el-button type="primary" size="small" :icon="Goods" :disabled="!canOperateSelected"
|
|
|
+ @click="handleOpenProductSeatDialog(selectedEmployeeIds)">
|
|
|
批量产品分配
|
|
|
</el-button>
|
|
|
- <el-button
|
|
|
- size="small"
|
|
|
- :icon="Close"
|
|
|
- @click="handleClearSelection"
|
|
|
- >
|
|
|
+ <el-button size="small" :icon="Close" @click="handleClearSelection">
|
|
|
取消选择
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -120,43 +104,25 @@
|
|
|
|
|
|
<!-- 分页控件 -->
|
|
|
<div v-if="employeeList.length > 0 || total > 0" class="pagination-section">
|
|
|
- <el-pagination
|
|
|
- :current-page="currentPage"
|
|
|
- :page-size="pageSize"
|
|
|
- :page-sizes="pageSizes"
|
|
|
- :total="total"
|
|
|
- layout="total, sizes, prev, pager, next, jumper"
|
|
|
- background
|
|
|
- @size-change="handlePageSizeChange"
|
|
|
- @current-change="handleCurrentPageChange"
|
|
|
- />
|
|
|
+ <el-pagination :current-page="currentPage" :page-size="pageSize" :page-sizes="pageSizes" :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper" background @size-change="handlePageSizeChange"
|
|
|
+ @current-change="handleCurrentPageChange" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 创建企业弹框 -->
|
|
|
- <CreateEnterpriseDialog
|
|
|
- v-model:visible="showCreateDialog"
|
|
|
- @success="handleCreateSuccess"
|
|
|
- @close="handleDialogClose"
|
|
|
- />
|
|
|
+ <CreateEnterpriseDialog v-model:visible="showCreateDialog" @success="handleCreateSuccess"
|
|
|
+ @close="handleDialogClose" />
|
|
|
|
|
|
<!-- 添加企业成员弹框 -->
|
|
|
- <AddMemberDialog
|
|
|
- v-model:visible="showAddMemberDialog"
|
|
|
- :company-id="currentCompanyId"
|
|
|
- @success="handleAddMemberSuccess"
|
|
|
- @close="handleAddMemberDialogClose"
|
|
|
- />
|
|
|
+ <AddMemberDialog v-model:visible="showAddMemberDialog" :company-id="currentCompanyId" @success="loadEnterpriseList"
|
|
|
+ @close="showAddMemberDialog = false" />
|
|
|
|
|
|
<!-- 产品分配席位弹框 -->
|
|
|
- <ProductSeatDialog
|
|
|
- v-model:visible="showProductSeatDialog"
|
|
|
- :member-list="employeeList"
|
|
|
- :selected-member-ids="selectedMemberIdsForDialog"
|
|
|
- @success="handleProductSeatSuccess"
|
|
|
- @close="handleProductSeatDialogClose"
|
|
|
- />
|
|
|
+ <ProductSeatDialog v-model:visible="showProductSeatDialog" :member-list="employeeList"
|
|
|
+ :selected-member-ids="selectedMemberIdsForDialog" @success="handleProductSeatSuccess"
|
|
|
+ @close="handleProductSeatDialogClose" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -169,17 +135,23 @@ import { useUserInfo } from '/@/stores/userInfo'
|
|
|
import CreateEnterpriseDialog from '../CreateEnterpriseDialog.vue'
|
|
|
import AddMemberDialog from '../AddMemberDialog.vue'
|
|
|
import ProductSeatDialog from '../ProductSeatDialog.vue'
|
|
|
+import { checkCompany, myCompanies, members, removeMember } from '/@/api/frontend/me'
|
|
|
|
|
|
defineOptions({ name: 'MeEnterprise' })
|
|
|
|
|
|
type Employee = {
|
|
|
id: number
|
|
|
- name: string
|
|
|
- email: string
|
|
|
- status: number
|
|
|
- createdAt: string
|
|
|
- created_time?: string
|
|
|
- createdTime?: string
|
|
|
+ name: string // 企业名称
|
|
|
+ email: string // 电子邮箱
|
|
|
+ role: number // 角色:1创建人/管理员,2普通成员
|
|
|
+ status: number // 状态:1正常,2禁用
|
|
|
+ join_time: string // 加入时间
|
|
|
+ create_time: string // 创建时间
|
|
|
+ role_text: string // 角色文本
|
|
|
+ status_text: string // 状态文本
|
|
|
+ join_time_formatted: string // 格式化的加入时间
|
|
|
+ create_time_formatted: string // 格式化的创建时间
|
|
|
+ can_operate: boolean // 是否可以操作(只有创建人可以操作)
|
|
|
}
|
|
|
|
|
|
type ProductSeatResult = {
|
|
|
@@ -192,25 +164,19 @@ const { id: userId } = storeToRefs(userStore)
|
|
|
|
|
|
const loading = ref(false)
|
|
|
const employeeList = ref<Employee[]>([
|
|
|
- { id: 1, name: '张三', email: 'zhangsan@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 2, name: '李四', email: 'lisi@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 3, name: '王五', email: 'wangwu@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 4, name: '赵六', email: 'zhaoliu@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 5, name: '孙七', email: 'sunqi@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 6, name: '周八', email: 'zhouba@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 7, name: '吴九', email: 'wujiu@example.com', status: 1, createdAt: '2025-01-01 12:00:00' },
|
|
|
- { id: 8, name: '郑十', email: 'zhengshi@example.com', status: 1, createdAt: '2025-01-01 12:00:00' }
|
|
|
+
|
|
|
])
|
|
|
const showCreateDialog = ref(false)
|
|
|
const showAddMemberDialog = ref(false)
|
|
|
const showProductSeatDialog = ref(false)
|
|
|
const selectedEmployeeIds = ref<number[]>([])
|
|
|
const selectedMemberIdsForDialog = ref<number[]>([])
|
|
|
+
|
|
|
const employeeTable = ref()
|
|
|
const currentPage = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
const pageSizes = [10, 20, 30, 50]
|
|
|
-const total = ref(1)
|
|
|
+const total = ref(0)
|
|
|
|
|
|
// 企业数据占位,后续可通过接口填充
|
|
|
const companyId = ref<number | string | null>(null)
|
|
|
@@ -223,7 +189,7 @@ const hasEnterprise = computed(() => {
|
|
|
return true
|
|
|
})
|
|
|
|
|
|
-const currentCompanyId = computed(() => companyId.value)
|
|
|
+const currentCompanyId: any = computed(() => companyId.value)
|
|
|
|
|
|
const currentEnterpriseName = computed(() => {
|
|
|
if (!hasEnterprise.value) return '我的企业'
|
|
|
@@ -233,46 +199,55 @@ const currentEnterpriseName = computed(() => {
|
|
|
|
|
|
const currentUserId = computed(() => userId.value || null)
|
|
|
|
|
|
-const formatDate = (dateStr?: string) => {
|
|
|
- if (!dateStr) return '-'
|
|
|
- const date = new Date(dateStr)
|
|
|
- if (Number.isNaN(date.getTime())) return dateStr
|
|
|
-
|
|
|
- const year = date.getFullYear()
|
|
|
- const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
- const day = String(date.getDate()).padStart(2, '0')
|
|
|
- const hours = String(date.getHours()).padStart(2, '0')
|
|
|
- const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
+// 检查选中的员工是否都可以操作
|
|
|
+const canOperateSelected = computed(() => {
|
|
|
+ if (selectedEmployeeIds.value.length === 0) return false
|
|
|
+ const selectedEmployees = employeeList.value.filter(emp => selectedEmployeeIds.value.includes(emp.id))
|
|
|
+ return selectedEmployees.every(emp => emp.can_operate)
|
|
|
+})
|
|
|
|
|
|
- return `${year}-${month}-${day} ${hours}:${minutes}`
|
|
|
-}
|
|
|
+// 请求锁,防止重复请求
|
|
|
+const isLoadingList = ref(false)
|
|
|
|
|
|
-type TagType = 'success' | 'warning' | 'info' | 'danger' | 'primary'
|
|
|
-
|
|
|
-const getStatusType = (status: number): TagType => {
|
|
|
- const statusMap: Record<number, TagType> = {
|
|
|
- 1: 'success',
|
|
|
- 2: 'danger'
|
|
|
+// 获取企业列表及员工
|
|
|
+const loadEnterpriseList = async () => {
|
|
|
+ if (!hasEnterprise.value) {
|
|
|
+ loading.value = false
|
|
|
+ employeeList.value = []
|
|
|
+ total.value = 0
|
|
|
+ return
|
|
|
}
|
|
|
- return statusMap[status] || 'info'
|
|
|
-}
|
|
|
|
|
|
-const getStatusText = (status: number) => {
|
|
|
- const statusMap: Record<number, string> = {
|
|
|
- 1: '启用',
|
|
|
- 2: '禁用'
|
|
|
+ // 如果正在加载,直接返回,避免重复请求
|
|
|
+ if (isLoadingList.value) {
|
|
|
+ return
|
|
|
}
|
|
|
- return statusMap[status] || '未知'
|
|
|
-}
|
|
|
|
|
|
-const loadEnterpriseList = async () => {
|
|
|
- // TODO: 接入实际接口
|
|
|
- loading.value = false
|
|
|
-}
|
|
|
+ isLoadingList.value = true
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ // 获取企业成员列表
|
|
|
+ const membersRes = await members({
|
|
|
+ company_id: companyId.value,
|
|
|
+ page: currentPage.value,
|
|
|
+ limit: pageSize.value
|
|
|
+ })
|
|
|
|
|
|
-const initEnterpriseData = async () => {
|
|
|
- // TODO: 拉取企业数据并填充 companyId/companyName
|
|
|
- loadEnterpriseList()
|
|
|
+ employeeList.value = membersRes.data?.list || []
|
|
|
+ total.value = membersRes.data?.total || 0
|
|
|
+ } catch (error: any) {
|
|
|
+ // 如果是取消的请求(axios 自动取消重复请求),不显示错误
|
|
|
+ // axios 取消的请求通常会有 message 属性包含 'cancel' 或 'Cancel'
|
|
|
+ if (error?.message && (error.message.includes('cancel') || error.message.includes('Cancel'))) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.error('获取成员列表失败')
|
|
|
+ employeeList.value = []
|
|
|
+ total.value = 0
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ isLoadingList.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const handlePageSizeChange = (val: number) => {
|
|
|
@@ -286,54 +261,99 @@ const handleCurrentPageChange = (val: number) => {
|
|
|
loadEnterpriseList()
|
|
|
}
|
|
|
|
|
|
-const handleCreateSuccess = async () => {
|
|
|
- // TODO: 创建成功后刷新企业信息
|
|
|
- currentPage.value = 1
|
|
|
- loadEnterpriseList()
|
|
|
- ElMessage.success('企业创建成功')
|
|
|
+const handleCreateSuccess = async (data?: any) => {
|
|
|
+ // 创建成功后刷新企业信息
|
|
|
+ if (data?.id) {
|
|
|
+ currentPage.value = 1
|
|
|
+ companyId.value = data.id
|
|
|
+ companyName.value = data.name || ''
|
|
|
+ // companyId 变化会触发 watch,自动加载列表,不需要手动调用
|
|
|
+ } else {
|
|
|
+ // 如果没有返回数据,重新检查企业信息
|
|
|
+ await handleCheckCompany()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const handleDialogClose = () => {
|
|
|
showCreateDialog.value = false
|
|
|
}
|
|
|
|
|
|
+// 创建企业
|
|
|
+const openCreateDialog = () => {
|
|
|
+ console.log('openCreateDialog called, showCreateDialog before:', showCreateDialog.value)
|
|
|
+ showCreateDialog.value = true
|
|
|
+ console.log('showCreateDialog after:', showCreateDialog.value)
|
|
|
+}
|
|
|
+
|
|
|
const handleDeleteMember = async (member: Employee) => {
|
|
|
if (!member?.id) {
|
|
|
ElMessage.error('成员信息不完整')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ if (!member.can_operate) {
|
|
|
+ ElMessage.warning('您没有权限操作此成员')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
await ElMessageBox.confirm(`确定要删除成员 "${member.name || member.email}" 吗?`, '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
})
|
|
|
- // TODO: 调用删除成员 API
|
|
|
+
|
|
|
+ const res = await removeMember({
|
|
|
+ member_id: member.id,
|
|
|
+ company_id: companyId.value
|
|
|
+ })
|
|
|
+
|
|
|
ElMessage.success('删除成员成功')
|
|
|
loadEnterpriseList()
|
|
|
- } catch {
|
|
|
- // 用户取消
|
|
|
+ } catch (error: any) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('删除成员失败:', error)
|
|
|
+ ElMessage.error('删除成员失败')
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const handleAddMemberSuccess = () => {
|
|
|
- loadEnterpriseList()
|
|
|
-}
|
|
|
-
|
|
|
-const handleAddMemberDialogClose = () => {
|
|
|
- showAddMemberDialog.value = false
|
|
|
+// 检查是否有企业
|
|
|
+const handleCheckCompany = async () => {
|
|
|
+ try {
|
|
|
+ const res = await checkCompany()
|
|
|
+ if (res.code === 1 && res.data?.has_company) {
|
|
|
+ // 如果已有企业,获取企业信息
|
|
|
+ const companiesRes = await myCompanies({})
|
|
|
+ const company = companiesRes.data
|
|
|
+ currentPage.value = 1
|
|
|
+ companyId.value = company.id
|
|
|
+ companyName.value = company.name || ''
|
|
|
+ // companyId 变化会触发 watch,自动加载列表,不需要手动调用
|
|
|
+ } else {
|
|
|
+ // 没有企业,清空数据
|
|
|
+ companyId.value = null
|
|
|
+ companyName.value = ''
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('检查企业失败:', error)
|
|
|
+ ElMessage.error('检查企业状态失败')
|
|
|
+ companyId.value = null
|
|
|
+ companyName.value = ''
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const handleSelectionChange = (selection: Employee[]) => {
|
|
|
selectedEmployeeIds.value = selection.map(item => item.id)
|
|
|
}
|
|
|
|
|
|
+// 分配产品席位
|
|
|
const handleClearSelection = () => {
|
|
|
employeeTable.value?.clearSelection()
|
|
|
selectedEmployeeIds.value = []
|
|
|
}
|
|
|
|
|
|
+// 选择分配员工
|
|
|
const handleOpenProductSeatDialog = (memberIds: number[]) => {
|
|
|
if (!memberIds || memberIds.length === 0) {
|
|
|
ElMessage.warning('请至少选择一个员工')
|
|
|
@@ -359,18 +379,33 @@ const handleProductSeatDialogClose = () => {
|
|
|
selectedMemberIdsForDialog.value = []
|
|
|
}
|
|
|
|
|
|
+type TagType = 'success' | 'warning' | 'info' | 'danger' | 'primary'
|
|
|
+
|
|
|
+const getStatusType = (status: number): TagType => {
|
|
|
+ const statusMap: Record<number, TagType> = {
|
|
|
+ 1: 'success',
|
|
|
+ 2: 'danger'
|
|
|
+ }
|
|
|
+ return statusMap[status] || 'info'
|
|
|
+}
|
|
|
+
|
|
|
watch(
|
|
|
companyId,
|
|
|
(newVal, oldVal) => {
|
|
|
- if (newVal && !oldVal) {
|
|
|
+ // 只在 companyId 从无到有或发生变化时加载
|
|
|
+ // 避免初始化时 immediate 触发(因为初始值为 null)
|
|
|
+ if (newVal && newVal !== oldVal) {
|
|
|
loadEnterpriseList()
|
|
|
+ } else if (!newVal && oldVal) {
|
|
|
+ // 如果企业被清空,清空列表
|
|
|
+ employeeList.value = []
|
|
|
+ total.value = 0
|
|
|
}
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
+ }
|
|
|
)
|
|
|
|
|
|
onMounted(() => {
|
|
|
- initEnterpriseData()
|
|
|
+ handleCheckCompany()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
@@ -589,6 +624,7 @@ onMounted(() => {
|
|
|
0% {
|
|
|
transform: rotate(0deg);
|
|
|
}
|
|
|
+
|
|
|
100% {
|
|
|
transform: rotate(360deg);
|
|
|
}
|