feat: add employee management, backend health check, and UI improvements
This commit is contained in:
@@ -9,7 +9,10 @@
|
||||
<div class="hero-copy">
|
||||
<div class="hero-tag">{{ selectedEmployee.employeeNo }}</div>
|
||||
<h2>{{ selectedEmployee.name }}</h2>
|
||||
<p>{{ selectedEmployee.department }} / {{ selectedEmployee.position }} / {{ selectedEmployee.grade }}</p>
|
||||
<p>
|
||||
{{ selectedEmployee.department }} / {{ selectedEmployee.position }} /
|
||||
{{ selectedEmployee.grade }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -218,12 +221,13 @@
|
||||
<nav class="status-tabs" aria-label="员工状态筛选">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab"
|
||||
:key="tab.label"
|
||||
type="button"
|
||||
:class="{ active: activeTab === tab }"
|
||||
@click="activeTab = tab"
|
||||
:class="{ active: activeTab === tab.label }"
|
||||
@click="activeTab = tab.label"
|
||||
>
|
||||
{{ tab }}
|
||||
<span>{{ tab.label }}</span>
|
||||
<small>{{ tab.count }}</small>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -231,25 +235,204 @@
|
||||
<div class="filter-set">
|
||||
<div class="list-search">
|
||||
<i class="mdi mdi-magnify"></i>
|
||||
<input type="search" placeholder="搜索员工姓名、工号、部门或岗位..." />
|
||||
<input
|
||||
v-model="searchKeyword"
|
||||
type="search"
|
||||
placeholder="搜索姓名、工号、部门、岗位"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button v-for="filter in filters" :key="filter" type="button" class="filter-btn">
|
||||
<span>{{ filter }}</span>
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div class="picker-filter" :class="{ open: activeFilterPopover === 'department' }">
|
||||
<button
|
||||
class="picker-trigger"
|
||||
type="button"
|
||||
:aria-expanded="activeFilterPopover === 'department'"
|
||||
aria-haspopup="dialog"
|
||||
@click="toggleFilterPopover('department')"
|
||||
>
|
||||
<span class="picker-label">{{ selectedDepartment || '组织部门' }}</span>
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div
|
||||
v-if="activeFilterPopover === 'department'"
|
||||
class="picker-popover"
|
||||
role="dialog"
|
||||
aria-label="选择组织部门"
|
||||
>
|
||||
<header>
|
||||
<strong>选择组织部门</strong>
|
||||
<button type="button" aria-label="关闭组织部门选择" @click="closeFilterPopover">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</header>
|
||||
<div class="picker-option-list">
|
||||
<button
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: !selectedDepartment }"
|
||||
@click="selectFilter('department', '')"
|
||||
>
|
||||
全部部门
|
||||
</button>
|
||||
<button
|
||||
v-for="department in departmentOptions"
|
||||
:key="department"
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: selectedDepartment === department }"
|
||||
@click="selectFilter('department', department)"
|
||||
>
|
||||
{{ department }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="picker-filter" :class="{ open: activeFilterPopover === 'grade' }">
|
||||
<button
|
||||
class="picker-trigger"
|
||||
type="button"
|
||||
:aria-expanded="activeFilterPopover === 'grade'"
|
||||
aria-haspopup="dialog"
|
||||
@click="toggleFilterPopover('grade')"
|
||||
>
|
||||
<span class="picker-label">{{ selectedGrade || '职级' }}</span>
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div
|
||||
v-if="activeFilterPopover === 'grade'"
|
||||
class="picker-popover"
|
||||
role="dialog"
|
||||
aria-label="选择职级"
|
||||
>
|
||||
<header>
|
||||
<strong>选择职级</strong>
|
||||
<button type="button" aria-label="关闭职级选择" @click="closeFilterPopover">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</header>
|
||||
<div class="picker-option-list">
|
||||
<button
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: !selectedGrade }"
|
||||
@click="selectFilter('grade', '')"
|
||||
>
|
||||
全部职级
|
||||
</button>
|
||||
<button
|
||||
v-for="grade in gradeOptions"
|
||||
:key="grade"
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: selectedGrade === grade }"
|
||||
@click="selectFilter('grade', grade)"
|
||||
>
|
||||
{{ grade }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="picker-filter" :class="{ open: activeFilterPopover === 'role' }">
|
||||
<button
|
||||
class="picker-trigger"
|
||||
type="button"
|
||||
:aria-expanded="activeFilterPopover === 'role'"
|
||||
aria-haspopup="dialog"
|
||||
@click="toggleFilterPopover('role')"
|
||||
>
|
||||
<span class="picker-label">{{ selectedRole || '系统角色' }}</span>
|
||||
<i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div
|
||||
v-if="activeFilterPopover === 'role'"
|
||||
class="picker-popover"
|
||||
role="dialog"
|
||||
aria-label="选择系统角色"
|
||||
>
|
||||
<header>
|
||||
<strong>选择系统角色</strong>
|
||||
<button type="button" aria-label="关闭系统角色选择" @click="closeFilterPopover">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</header>
|
||||
<div class="picker-option-list">
|
||||
<button
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: !selectedRole }"
|
||||
@click="selectFilter('role', '')"
|
||||
>
|
||||
全部角色
|
||||
</button>
|
||||
<button
|
||||
v-for="role in roleFilterOptions"
|
||||
:key="role"
|
||||
type="button"
|
||||
class="picker-option"
|
||||
:class="{ active: selectedRole === role }"
|
||||
@click="selectFilter('role', role)"
|
||||
>
|
||||
{{ role }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="create-btn" type="button">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
<span>新增员工</span>
|
||||
</button>
|
||||
<div class="toolbar-actions">
|
||||
<button v-if="hasActiveFilters" class="ghost-filter-btn" type="button" @click="resetFilters">
|
||||
<i class="mdi mdi-filter-remove-outline"></i>
|
||||
<span>清空筛选</span>
|
||||
</button>
|
||||
|
||||
<button class="create-btn" type="button">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
<span>新增员工</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="hint"><i class="mdi mdi-information-outline"></i> 点击任意员工行可进入基础信息与角色权限编辑界面</p>
|
||||
<p class="hint">
|
||||
<i class="mdi mdi-information-outline"></i>
|
||||
点击任意员工行可进入基础信息与角色权限编辑界面
|
||||
</p>
|
||||
|
||||
<div v-if="activeFilterTokens.length" class="active-filter-strip">
|
||||
<span v-for="token in activeFilterTokens" :key="token" class="active-filter-chip">
|
||||
{{ token }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div v-if="loading" class="table-state">
|
||||
<i class="mdi mdi-loading mdi-spin"></i>
|
||||
<p>正在加载员工数据...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="errorMessage" class="table-state error">
|
||||
<i class="mdi mdi-alert-circle-outline"></i>
|
||||
<p>{{ errorMessage }}</p>
|
||||
<button type="button" class="state-action" @click="loadEmployees">重新加载</button>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!visibleEmployees.length" class="table-state empty">
|
||||
<i class="mdi mdi-account-search-outline"></i>
|
||||
<p>没有匹配的员工数据</p>
|
||||
</div>
|
||||
|
||||
<table v-else>
|
||||
<colgroup>
|
||||
<col class="col-employee">
|
||||
<col class="col-employee-no">
|
||||
<col class="col-department">
|
||||
<col class="col-position">
|
||||
<col class="col-grade">
|
||||
<col class="col-role">
|
||||
<col class="col-status">
|
||||
<col class="col-updated">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>员工</th>
|
||||
@@ -257,8 +440,6 @@
|
||||
<th>部门</th>
|
||||
<th>岗位</th>
|
||||
<th>职级</th>
|
||||
<th>直属上级</th>
|
||||
<th>财务归口</th>
|
||||
<th>系统角色</th>
|
||||
<th>状态</th>
|
||||
<th>最近更新</th>
|
||||
@@ -284,20 +465,81 @@
|
||||
<td>{{ employee.department }}</td>
|
||||
<td>{{ employee.position }}</td>
|
||||
<td><span class="level-pill">{{ employee.grade }}</span></td>
|
||||
<td>{{ employee.manager }}</td>
|
||||
<td>{{ employee.financeOwner }}</td>
|
||||
<td>
|
||||
<div class="role-stack">
|
||||
<span v-for="role in employee.roles.slice(0, 2)" :key="role" class="role-pill">{{ role }}</span>
|
||||
<span v-if="employee.roles.length > 2" class="more-pill">+{{ employee.roles.length - 2 }}</span>
|
||||
<span
|
||||
v-for="role in employee.roles.slice(0, 2)"
|
||||
:key="role"
|
||||
class="role-pill"
|
||||
>
|
||||
{{ role }}
|
||||
</span>
|
||||
<span v-if="employee.roles.length > 2" class="more-pill">
|
||||
+{{ employee.roles.length - 2 }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="status-pill" :class="employee.statusTone">{{ employee.status }}</span></td>
|
||||
<td>
|
||||
<span class="status-pill" :class="employee.statusTone">{{ employee.status }}</span>
|
||||
</td>
|
||||
<td>{{ employee.updatedAt }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer v-if="!loading && !errorMessage && totalCount" class="list-foot">
|
||||
<span class="page-summary">共 {{ totalCount }} 条,目前第 {{ currentPage }} 页</span>
|
||||
<div class="pager" aria-label="分页">
|
||||
<button
|
||||
class="page-nav"
|
||||
type="button"
|
||||
:disabled="currentPage === 1"
|
||||
aria-label="上一页"
|
||||
@click="currentPage--"
|
||||
>
|
||||
<i class="mdi mdi-chevron-left"></i>
|
||||
</button>
|
||||
<button
|
||||
v-for="page in totalPages"
|
||||
:key="page"
|
||||
class="page-number"
|
||||
:class="{ active: currentPage === page }"
|
||||
type="button"
|
||||
:aria-current="currentPage === page ? 'page' : undefined"
|
||||
@click="currentPage = page"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button
|
||||
class="page-nav"
|
||||
type="button"
|
||||
:disabled="currentPage === totalPages"
|
||||
aria-label="下一页"
|
||||
@click="currentPage++"
|
||||
>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="page-size-wrap">
|
||||
<button class="page-size" type="button" @click="togglePageSizeOpen">
|
||||
{{ pageSize }} 条/页 <i class="mdi mdi-chevron-down"></i>
|
||||
</button>
|
||||
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
|
||||
<button
|
||||
v-for="size in pageSizes"
|
||||
:key="size"
|
||||
type="button"
|
||||
role="option"
|
||||
:aria-selected="pageSize === size"
|
||||
:class="{ active: pageSize === size }"
|
||||
@click="changePageSize(size)"
|
||||
>
|
||||
{{ size }} 条/页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</Transition>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user