<template>
|
<div class="staff-page">
|
<section class="dept-panel">
|
<div class="panel-title">部门</div>
|
<el-input
|
v-model="deptKeyword"
|
class="dept-search"
|
placeholder="搜索部门名称"
|
clearable
|
>
|
<template #prefix>
|
<el-icon><Search /></el-icon>
|
</template>
|
</el-input>
|
<div class="dept-tree-wrap">
|
<div
|
class="dept-all"
|
:class="{ active: filterDeptId == null }"
|
@click="clearDeptFilter"
|
>
|
全部部门
|
</div>
|
<el-tree
|
ref="treeRef"
|
:data="filteredDeptTree"
|
node-key="id"
|
:props="{ label: 'label', children: 'children' }"
|
highlight-current
|
default-expand-all
|
:expand-on-click-node="false"
|
class="dept-tree"
|
@node-click="onDeptNodeClick"
|
/>
|
</div>
|
</section>
|
|
<section class="content-panel">
|
<div class="search-card">
|
<div class="panel-title">员工列表</div>
|
<el-form class="search-grid" @submit.prevent="doSearch">
|
<el-form-item label="姓名">
|
<el-input v-model="filters.realName" placeholder="请输入姓名" clearable />
|
</el-form-item>
|
<el-form-item label="人员编号">
|
<el-input v-model="filters.staffNo" placeholder="请输入人员编号" clearable />
|
</el-form-item>
|
<el-form-item label="联系方式">
|
<el-input v-model="filters.mobile" placeholder="请输入联系方式" clearable />
|
</el-form-item>
|
<el-form-item label="人员分组">
|
<el-select
|
v-model="filters.staffGroupId"
|
placeholder="请选择"
|
clearable
|
filterable
|
>
|
<el-option
|
v-for="g in groupOptions"
|
:key="g.id"
|
:label="g.groupName"
|
:value="g.id"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="职位">
|
<el-select v-model="filters.position" placeholder="请选择" clearable>
|
<el-option label="部门负责人" value="LEADER" />
|
<el-option label="员工" value="STAFF" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="状态">
|
<el-select v-model="filters.status" placeholder="请选择" clearable>
|
<el-option label="启用" :value="1" />
|
<el-option label="禁用" :value="0" />
|
</el-select>
|
</el-form-item>
|
<div class="search-actions">
|
<el-button @click="resetSearch">
|
<el-icon><Refresh /></el-icon>
|
重置
|
</el-button>
|
<el-button type="primary" native-type="submit">
|
<el-icon><Search /></el-icon>
|
搜索
|
</el-button>
|
</div>
|
</el-form>
|
</div>
|
|
<div class="table-card">
|
<div class="table-toolbar">
|
<div class="toolbar-left">
|
<el-button v-permission="'sys:staff:import'" @click="downloadTemplate">下载导入模板</el-button>
|
<el-upload
|
v-permission="'sys:staff:import'"
|
:show-file-list="false"
|
accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
:http-request="doImport"
|
>
|
<el-button>批量导入</el-button>
|
</el-upload>
|
</div>
|
<el-button v-permission="'sys:staff:create'" type="primary" @click="openCreate">添加</el-button>
|
</div>
|
|
<el-table :data="rows" v-loading="loading" stripe border class="staff-table">
|
<el-table-column label="姓名" min-width="130">
|
<template #default="{ row }">
|
<div class="person-cell">
|
<el-avatar v-if="row.avatarUrl" :size="30" :src="row.avatarUrl" />
|
<el-avatar v-else :size="30">{{ (row.realName || "?").slice(0, 1) }}</el-avatar>
|
<span>{{ row.realName || "—" }}</span>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column prop="staffNo" label="员工编号" width="120" show-overflow-tooltip />
|
<el-table-column prop="mobileMasked" label="联系方式" width="130" />
|
<el-table-column prop="deptName" label="所属部门" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="positionLabel" label="所属职位" width="120" />
|
<el-table-column prop="groupDisplay" label="所属分组" min-width="170" show-overflow-tooltip />
|
<el-table-column label="状态" width="92">
|
<template #default="{ row }">
|
<el-switch
|
v-permission="'sys:staff:status'"
|
:model-value="row.status === 1"
|
inline-prompt
|
active-text="启用"
|
inactive-text="禁用"
|
@change="patchStatus(row, row.status === 1 ? 0 : 1)"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="180" fixed="right">
|
<template #default="{ row }">
|
<div class="table-actions table-ops">
|
<el-button v-permission="'sys:staff:update'" type="primary" link @click="openEdit(row)">编辑</el-button>
|
<el-button
|
v-permission="'sys:staff:status'"
|
v-if="row.status === 1"
|
type="primary"
|
link
|
@click="patchStatus(row, 0)"
|
>
|
禁用
|
</el-button>
|
<el-button v-else v-permission="'sys:staff:status'" type="primary" link @click="patchStatus(row, 1)">
|
启用
|
</el-button>
|
<el-button v-permission="'sys:staff:delete'" type="primary" link @click="confirmDelete(row)">删除</el-button>
|
</div>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<div class="pager-wrap">
|
<div class="pager-total">共 {{ total }} 条</div>
|
<el-pagination
|
background
|
layout="sizes, prev, pager, next"
|
:total="total"
|
:page-size="size"
|
:page-sizes="[10, 16, 20, 50, 100]"
|
v-model:current-page="page"
|
@current-change="load"
|
@size-change="onSizeChange"
|
/>
|
</div>
|
</div>
|
|
<el-dialog v-model="dlg" :title="dlgTitle" width="520px" destroy-on-close @closed="resetForm">
|
<el-form :model="form" label-width="100px">
|
<el-form-item label="员工名称" required>
|
<el-input v-model="form.nickName" maxlength="128" placeholder="请输入" />
|
</el-form-item>
|
<el-form-item label="编号">
|
<el-input v-model="form.staffNo" maxlength="64" placeholder="请输入人员编号(选填)" />
|
</el-form-item>
|
<el-form-item label="员工头像">
|
<div class="avatar-upload-row">
|
<el-avatar v-if="form.avatarUrl" :size="64" :src="form.avatarUrl" />
|
<el-avatar v-else :size="64">{{ (form.nickName || "?").slice(0, 1) }}</el-avatar>
|
<div class="avatar-upload-actions">
|
<el-upload
|
:show-file-list="false"
|
accept=".jpg,.jpeg,.png,.webp,.gif"
|
:http-request="uploadAvatar"
|
>
|
<el-button v-permission="editingId == null ? 'sys:staff:create' : 'sys:staff:update'" :loading="avatarUploading">上传头像</el-button>
|
</el-upload>
|
<el-button v-if="form.avatarUrl" link type="danger" @click="form.avatarUrl = ''">清除</el-button>
|
<div class="avatar-upload-tip">支持 jpg、png、webp、gif,大小不超过 5MB</div>
|
</div>
|
</div>
|
</el-form-item>
|
<el-form-item label="联系电话" required>
|
<el-input v-model="form.mobile" maxlength="32" placeholder="请输入11位手机号" />
|
</el-form-item>
|
<el-form-item label="所属部门" required>
|
<el-tree-select
|
v-model="form.deptId"
|
:data="deptOptions"
|
:props="{ value: 'id', label: 'label', children: 'children' }"
|
check-strictly
|
:render-after-expand="false"
|
placeholder="请选择部门"
|
class="fullw"
|
/>
|
</el-form-item>
|
<el-form-item label="职位" required>
|
<el-radio-group v-model="form.deptLeader">
|
<el-radio :label="false">员工</el-radio>
|
<el-radio :label="true">部门负责人</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
<el-form-item v-if="detailGroupNames.length" label="所属分组">
|
<div class="group-tags">
|
<el-tag v-for="g in detailGroupNames" :key="g" size="small" class="g-tag">{{ g }}</el-tag>
|
</div>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="dlg = false">取消</el-button>
|
<el-button
|
v-permission="editingId == null ? 'sys:staff:create' : 'sys:staff:update'"
|
type="primary"
|
:loading="saving"
|
@click="submitForm"
|
>
|
保存
|
</el-button>
|
</template>
|
</el-dialog>
|
</section>
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { computed, onMounted, reactive, ref } from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import { Refresh, Search } from "@element-plus/icons-vue";
|
import type { UploadRequestOptions } from "element-plus";
|
import http from "../api/http";
|
|
type DeptTree = { id: number; label: string; children?: DeptTree[] };
|
type StaffRow = {
|
id: number;
|
uuid: string;
|
realName?: string;
|
avatarUrl?: string;
|
staffNo?: string;
|
mobileMasked: string;
|
deptName: string;
|
positionLabel: string;
|
groupDisplay: string;
|
status: number;
|
};
|
|
const treeRef = ref();
|
const loading = ref(false);
|
const saving = ref(false);
|
const avatarUploading = ref(false);
|
const rows = ref<StaffRow[]>([]);
|
const page = ref(1);
|
const size = ref(16);
|
const total = ref(0);
|
const deptTree = ref<DeptTree[]>([]);
|
const deptOptions = ref<DeptTree[]>([]);
|
const filterDeptId = ref<number | undefined>(undefined);
|
const deptKeyword = ref("");
|
const groupOptions = ref<{ id: number; groupName: string }[]>([]);
|
const detailGroupNames = ref<string[]>([]);
|
|
const filters = reactive({
|
realName: "",
|
staffNo: "",
|
mobile: "",
|
position: "" as string,
|
status: undefined as number | undefined,
|
staffGroupId: undefined as number | undefined,
|
});
|
|
const dlg = ref(false);
|
const dlgTitle = ref("添加员工");
|
const editingId = ref<number | null>(null);
|
const form = reactive({
|
uuid: "",
|
nickName: "",
|
staffNo: "",
|
avatarUrl: "",
|
mobile: "",
|
deptId: undefined as number | undefined,
|
deptLeader: false,
|
});
|
|
function mapDeptToTree(nodes: Record<string, unknown>[]): DeptTree[] {
|
return nodes.map((n) => ({
|
id: n.id as number,
|
label: (n.status === 0 ? `${n.deptName}(禁用)` : String(n.deptName)) as string,
|
children: n.children ? mapDeptToTree(n.children as Record<string, unknown>[]) : undefined,
|
}));
|
}
|
|
async function loadDeptTree() {
|
const { data } = await http.get("/sys/dept-manage/tree");
|
const raw = (data.result || []) as Record<string, unknown>[];
|
deptTree.value = mapDeptToTree(raw);
|
}
|
|
async function loadStaffGroupOptions() {
|
const { data } = await http.get("/sys/staff-groups", { params: { page: 1, size: 500 } });
|
groupOptions.value = (data.result || []) as { id: number; groupName: string }[];
|
}
|
|
async function loadDeptOptions() {
|
const { data } = await http.get("/sys/depts");
|
const flat = (data.result || []) as { id: number; deptName: string; parentId?: number }[];
|
const byId = new Map<number, DeptTree>();
|
const roots: DeptTree[] = [];
|
for (const d of flat) {
|
byId.set(d.id, { id: d.id, label: d.deptName, children: [] });
|
}
|
for (const d of flat) {
|
const node = byId.get(d.id)!;
|
const pid = d.parentId ?? 0;
|
if (pid === 0) {
|
roots.push(node);
|
} else {
|
const p = byId.get(pid);
|
if (p) {
|
if (!p.children) p.children = [];
|
p.children.push(node);
|
} else {
|
roots.push(node);
|
}
|
}
|
}
|
const stripEmpty = (nodes: DeptTree[]): DeptTree[] =>
|
nodes.map((n) => ({
|
...n,
|
children: n.children?.length ? stripEmpty(n.children) : undefined,
|
}));
|
deptOptions.value = stripEmpty(roots);
|
}
|
|
function filterTree(nodes: DeptTree[], keyword: string): DeptTree[] {
|
if (!keyword) {
|
return nodes;
|
}
|
const lower = keyword.toLowerCase();
|
return nodes
|
.map((node) => {
|
const children = node.children ? filterTree(node.children, keyword) : undefined;
|
const matched = node.label.toLowerCase().includes(lower);
|
if (matched || (children && children.length)) {
|
return { ...node, children };
|
}
|
return null;
|
})
|
.filter((node): node is DeptTree => node !== null);
|
}
|
|
const filteredDeptTree = computed(() => filterTree(deptTree.value, deptKeyword.value.trim()));
|
|
function onDeptNodeClick(data: DeptTree) {
|
filterDeptId.value = data.id;
|
page.value = 1;
|
load();
|
}
|
|
function clearDeptFilter() {
|
filterDeptId.value = undefined;
|
treeRef.value?.setCurrentKey(undefined);
|
page.value = 1;
|
load();
|
}
|
|
function buildParams() {
|
return {
|
page: page.value,
|
size: size.value,
|
deptId: filterDeptId.value,
|
realName: filters.realName?.trim() || undefined,
|
staffNo: filters.staffNo?.trim() || undefined,
|
mobile: filters.mobile?.trim() || undefined,
|
position: filters.position || undefined,
|
status: filters.status,
|
staffGroupId: filters.staffGroupId,
|
};
|
}
|
|
async function load() {
|
loading.value = true;
|
try {
|
const { data } = await http.get("/sys/staff", { params: buildParams() });
|
rows.value = (data.result || []) as StaffRow[];
|
total.value = Number(data.total) || 0;
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "加载失败");
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
function doSearch() {
|
page.value = 1;
|
load();
|
}
|
|
function resetSearch() {
|
filters.realName = "";
|
filters.staffNo = "";
|
filters.mobile = "";
|
filters.position = "";
|
filters.status = undefined;
|
filters.staffGroupId = undefined;
|
page.value = 1;
|
load();
|
}
|
|
function onSizeChange(v: number) {
|
size.value = v;
|
page.value = 1;
|
load();
|
}
|
|
function openCreate() {
|
editingId.value = null;
|
detailGroupNames.value = [];
|
dlgTitle.value = "添加员工";
|
form.uuid = "";
|
form.nickName = "";
|
form.staffNo = "";
|
form.avatarUrl = "";
|
form.mobile = "";
|
form.deptId = filterDeptId.value;
|
form.deptLeader = false;
|
dlg.value = true;
|
}
|
|
async function openEdit(row: StaffRow) {
|
editingId.value = row.id;
|
dlgTitle.value = "编辑员工";
|
try {
|
const { data } = await http.get(`/sys/staff/${row.id}`, { params: { uuid: row.uuid } });
|
const r = data.result;
|
form.uuid = r.uuid;
|
form.nickName = r.nickName || "";
|
form.staffNo = r.staffNo || "";
|
form.avatarUrl = r.avatarUrl || "";
|
form.mobile = r.mobile || "";
|
form.deptId = r.deptId;
|
form.deptLeader = !!r.deptLeader;
|
detailGroupNames.value = Array.isArray(r.groupNames) ? r.groupNames : [];
|
dlg.value = true;
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "加载失败");
|
}
|
}
|
|
function resetForm() {
|
editingId.value = null;
|
}
|
|
async function submitForm() {
|
if (!form.nickName.trim()) {
|
ElMessage.warning("请填写员工名称");
|
return;
|
}
|
if (!form.mobile.trim()) {
|
ElMessage.warning("请填写联系电话");
|
return;
|
}
|
if (form.deptId == null) {
|
ElMessage.warning("请选择所属部门");
|
return;
|
}
|
saving.value = true;
|
try {
|
const body = {
|
nickName: form.nickName.trim(),
|
staffNo: form.staffNo.trim() || undefined,
|
avatarUrl: form.avatarUrl.trim() || undefined,
|
mobile: form.mobile.trim(),
|
deptId: form.deptId,
|
deptLeader: form.deptLeader,
|
};
|
if (editingId.value == null) {
|
await http.post("/sys/staff", body);
|
ElMessage.success("已添加");
|
} else {
|
await http.put(`/sys/staff/${editingId.value}`, {
|
uuid: form.uuid,
|
...body,
|
});
|
ElMessage.success("已保存");
|
}
|
dlg.value = false;
|
load();
|
loadDeptTree();
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "保存失败");
|
} finally {
|
saving.value = false;
|
}
|
}
|
|
async function patchStatus(row: StaffRow, status: number) {
|
try {
|
if (status === 0) {
|
await ElMessageBox.confirm(
|
"是否确认禁用? 禁用后该账号不可再登录系统",
|
"确认禁用",
|
{ type: "warning" },
|
);
|
}
|
await http.patch(`/sys/staff/${row.id}/status`, { uuid: row.uuid, status });
|
ElMessage.success(status === 1 ? "已启用" : "已禁用");
|
row.status = status;
|
} catch (e: unknown) {
|
if (e === "cancel" || e === "close") {
|
return;
|
}
|
ElMessage.error(e instanceof Error ? e.message : "操作失败");
|
}
|
}
|
|
function confirmDelete(row: StaffRow) {
|
ElMessageBox.confirm("确认删除该员工?", "确认删除", { type: "warning" })
|
.then(async () => {
|
try {
|
await http.delete(`/sys/staff/${row.id}`, { params: { uuid: row.uuid } });
|
ElMessage.success("已删除");
|
load();
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "删除失败");
|
}
|
})
|
.catch(() => {});
|
}
|
|
async function downloadTemplate() {
|
try {
|
const res = await http.get("/sys/staff/import-template", { responseType: "blob" });
|
const blob = new Blob([res.data], {
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
});
|
const url = URL.createObjectURL(blob);
|
const a = document.createElement("a");
|
a.href = url;
|
a.download = "staff-import-template.xlsx";
|
a.click();
|
URL.revokeObjectURL(url);
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "下载失败");
|
}
|
}
|
|
async function doImport(opt: UploadRequestOptions) {
|
const fd = new FormData();
|
fd.append("file", opt.file as File);
|
try {
|
const { data } = await http.post("/sys/staff/import", fd, {
|
headers: { "Content-Type": "multipart/form-data" },
|
});
|
const r = data.result as { success: number; fail: number; errors: string[] };
|
ElMessage.success(`成功 ${r.success} 条,失败 ${r.fail} 条`);
|
if (r.errors?.length) {
|
ElMessageBox.alert(r.errors.slice(0, 20).join("\n"), "导入明细", {
|
type: r.fail ? "warning" : "success",
|
});
|
}
|
load();
|
loadDeptTree();
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "导入失败");
|
}
|
}
|
|
async function uploadAvatar(opt: UploadRequestOptions) {
|
const fd = new FormData();
|
fd.append("file", opt.file as File);
|
avatarUploading.value = true;
|
try {
|
const { data } = await http.post("/common/upload/image", fd, {
|
headers: { "Content-Type": "multipart/form-data" },
|
});
|
form.avatarUrl = String(data.result?.url || "");
|
ElMessage.success("头像上传成功");
|
} catch (e: unknown) {
|
ElMessage.error(e instanceof Error ? e.message : "头像上传失败");
|
} finally {
|
avatarUploading.value = false;
|
}
|
}
|
|
onMounted(async () => {
|
try {
|
await loadDeptTree();
|
await loadDeptOptions();
|
await loadStaffGroupOptions();
|
} catch {
|
ElMessage.error("加载部门失败");
|
}
|
load();
|
});
|
</script>
|
|
<style scoped>
|
.staff-page {
|
display: grid;
|
grid-template-columns: 250px minmax(0, 1fr);
|
gap: 12px;
|
min-height: calc(100vh - 170px);
|
}
|
|
.dept-panel,
|
.search-card,
|
.table-card {
|
background: #ffffff;
|
border: 1px solid #e5ecfb;
|
border-radius: 10px;
|
box-shadow: 0 8px 24px rgba(77, 111, 197, 0.06);
|
}
|
|
.dept-panel {
|
padding: 14px 12px;
|
}
|
|
.content-panel {
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
min-width: 0;
|
}
|
|
.panel-title {
|
color: #2e446e;
|
font-size: 18px;
|
font-weight: 700;
|
margin-bottom: 14px;
|
}
|
|
.dept-search {
|
margin-bottom: 14px;
|
}
|
|
.dept-tree-wrap {
|
min-height: 520px;
|
}
|
|
.dept-all {
|
height: 38px;
|
line-height: 38px;
|
padding: 0 12px;
|
border-radius: 10px;
|
color: #5d6f92;
|
cursor: pointer;
|
transition: all 0.18s ease;
|
}
|
|
.dept-all.active,
|
.dept-all:hover {
|
background: #edf3ff;
|
color: #3b6fe2;
|
}
|
|
.dept-tree :deep(.el-tree-node__content) {
|
height: 36px;
|
border-radius: 10px;
|
margin: 2px 0;
|
}
|
|
.dept-tree :deep(.el-tree-node.is-current > .el-tree-node__content) {
|
background: #edf3ff;
|
color: #3b6fe2;
|
}
|
|
.search-card {
|
padding: 16px 18px 10px;
|
}
|
|
.search-grid {
|
display: grid;
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
gap: 8px 18px;
|
align-items: start;
|
}
|
|
.search-grid :deep(.el-form-item) {
|
margin-bottom: 12px;
|
}
|
|
.search-grid :deep(.el-form-item__label) {
|
color: #42567f;
|
font-weight: 600;
|
}
|
|
.search-grid :deep(.el-input),
|
.search-grid :deep(.el-select) {
|
width: 100%;
|
}
|
|
.search-actions {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
grid-column: 3 / 5;
|
padding-top: 2px;
|
}
|
|
.table-card {
|
padding: 14px 16px 12px;
|
}
|
|
.table-toolbar {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
gap: 12px;
|
margin-bottom: 12px;
|
}
|
|
.toolbar-left {
|
display: flex;
|
gap: 10px;
|
flex-wrap: wrap;
|
}
|
|
.person-cell {
|
display: inline-flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
.table-actions {
|
display: flex;
|
align-items: center;
|
gap: 4px;
|
flex-wrap: wrap;
|
}
|
|
.staff-table :deep(.el-table__row td) {
|
background: #ffffff;
|
}
|
|
.pager-wrap {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-top: 14px;
|
}
|
|
.pager-total {
|
color: #7484a6;
|
font-size: 13px;
|
}
|
|
.fullw {
|
width: 100%;
|
}
|
|
.group-tags {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 6px;
|
}
|
|
.g-tag {
|
margin: 0;
|
}
|
|
.avatar-upload-row {
|
display: flex;
|
align-items: center;
|
gap: 14px;
|
}
|
|
.avatar-upload-actions {
|
display: flex;
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 8px;
|
}
|
|
.avatar-upload-tip {
|
color: #8a97b3;
|
font-size: 12px;
|
line-height: 1.4;
|
}
|
|
@media (max-width: 1360px) {
|
.search-grid {
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
}
|
|
.search-actions {
|
grid-column: 2 / 4;
|
}
|
}
|
|
@media (max-width: 1100px) {
|
.staff-page {
|
grid-template-columns: 1fr;
|
}
|
|
.search-grid {
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
}
|
|
.search-actions {
|
grid-column: 1 / 3;
|
}
|
|
.pager-wrap {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 10px;
|
}
|
}
|
</style>
|