|
|
@@ -2,7 +2,7 @@
|
|
|
<el-drawer
|
|
|
title="按员工选择"
|
|
|
v-model="visibleProxy"
|
|
|
- :direction="'rtl'"
|
|
|
+ direction="rtl"
|
|
|
size="500px"
|
|
|
@close="handleClose"
|
|
|
>
|
|
|
@@ -10,216 +10,137 @@
|
|
|
<div class="employee-selector__search">
|
|
|
<el-form :model="searchForm" inline>
|
|
|
<el-form-item label="姓名">
|
|
|
- <el-input v-model="searchForm.name" placeholder="输入员工姓名" style="width: 120px" />
|
|
|
+ <el-input v-model="searchForm.name" placeholder="输入员工姓名" style="width:120px" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="手机号">
|
|
|
- <el-input v-model="searchForm.phone" placeholder="输入员工手机号" style="width: 120px" />
|
|
|
+ <el-input v-model="searchForm.phone" placeholder="输入手机号" style="width:120px" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<div class="employee-selector__actions">
|
|
|
- <el-button type="default" @click="handleReset">重置</el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="employee-selector__tabs">
|
|
|
- <el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
|
|
+ <el-tabs v-model="activeTab">
|
|
|
<el-tab-pane label="全部" name="all" />
|
|
|
- <el-tab-pane label="已选 ({{ internalSelectedIds.length }}人)" name="selected" />
|
|
|
+ <el-tab-pane label="已选 ({{ selectedIds.length }}人)" name="selected" />
|
|
|
</el-tabs>
|
|
|
</div>
|
|
|
|
|
|
- <div class="employee-selector__table">
|
|
|
- <el-table
|
|
|
- ref="tableRef"
|
|
|
- :data="displayEmployees"
|
|
|
- row-key="id"
|
|
|
- border
|
|
|
- :max-height="300"
|
|
|
- @selection-change="handleSelectionChange"
|
|
|
- >
|
|
|
- <el-table-column type="selection" width="50" :reserve-selection="true" />
|
|
|
- <el-table-column label="员工姓名" prop="name" />
|
|
|
- <el-table-column label="手机号" prop="phone" width="140" />
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
+ <el-table
|
|
|
+ ref="tableRef"
|
|
|
+ :data="displayEmployees"
|
|
|
+ row-key="id"
|
|
|
+ border
|
|
|
+ :max-height="300"
|
|
|
+ @row-click="handleRowClick"
|
|
|
+ >
|
|
|
+ <el-table-column width="50">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-checkbox
|
|
|
+ :model-value="selectedIds.includes(scope.row.id)"
|
|
|
+ @click.stop="toggleRow(scope.row.id)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="员工姓名" prop="name" />
|
|
|
+ <el-table-column label="手机号" prop="phone" width="140" />
|
|
|
+ </el-table>
|
|
|
|
|
|
<div class="employee-selector__pagination">
|
|
|
<el-pagination
|
|
|
v-model:current-page="pagination.page_no"
|
|
|
v-model:page-size="pagination.page_size"
|
|
|
:total="pagination.total"
|
|
|
- :page-sizes="[10, 20, 50]"
|
|
|
- layout="prev, pager, next, jumper, ->, total, sizes"
|
|
|
+ :page-sizes="[10,20,50]"
|
|
|
+ layout="prev,pager,next,jumper,->,total,sizes"
|
|
|
@current-change="fetchEmployees"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div class="employee-selector__footer">
|
|
|
<el-button @click="handleClose">取消</el-button>
|
|
|
- <el-button type="primary" @click="handleConfirm">保存</el-button>
|
|
|
+ <el-button type="primary" @click="handleConfirm">保存 ({{ selectedIds.length }})</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-drawer>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed, watch, nextTick } from "vue";
|
|
|
+import { ref, computed, watch } from "vue";
|
|
|
import EmployeeAPI from "@/api/module_payment/employee";
|
|
|
|
|
|
-interface Employee {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- phone: string;
|
|
|
-}
|
|
|
-
|
|
|
-interface Props {
|
|
|
- visible: boolean;
|
|
|
- selectedIds: string[];
|
|
|
- enterpriseId: string;
|
|
|
-}
|
|
|
+interface Row { id: string; name: string; phone: string }
|
|
|
+interface Props { visible: boolean; selectedIds: string[]; enterpriseId: string }
|
|
|
|
|
|
const props = defineProps<Props>();
|
|
|
+const emit = defineEmits<{ (e: "update:visible", value: boolean): void; (e: "confirm", ids: string[]): void }>();
|
|
|
|
|
|
-const emit = defineEmits<{
|
|
|
- (e: "update:visible", value: boolean): void;
|
|
|
- (e: "confirm", ids: string[]): void;
|
|
|
-}>();
|
|
|
-
|
|
|
-const visibleProxy = computed({
|
|
|
- get: () => props.visible,
|
|
|
- set: (v: boolean) => emit("update:visible", v),
|
|
|
-});
|
|
|
+const visibleProxy = computed({ get: () => props.visible, set: (v) => emit("update:visible", v) });
|
|
|
|
|
|
const searchForm = ref({ name: "", phone: "" });
|
|
|
const activeTab = ref("all");
|
|
|
const pagination = ref({ page_no: 1, page_size: 10, total: 0 });
|
|
|
-
|
|
|
-const allEmployees = ref<Employee[]>([]);
|
|
|
+const allEmployees = ref<Row[]>([]);
|
|
|
const tableRef = ref();
|
|
|
|
|
|
-// 追踪已选 ID 集合(跨页持久)
|
|
|
-const internalSelectedIds = ref<string[]>([]);
|
|
|
+/** 独立维护选中 ID(跨页持久) */
|
|
|
+const selectedIds = ref<string[]>([]);
|
|
|
|
|
|
-watch(
|
|
|
- () => props.visible,
|
|
|
- (v) => {
|
|
|
- if (v) {
|
|
|
- internalSelectedIds.value = [...props.selectedIds];
|
|
|
- fetchEmployees();
|
|
|
- }
|
|
|
+watch(() => props.visible, (v) => {
|
|
|
+ if (v) {
|
|
|
+ activeTab.value = "all";
|
|
|
+ selectedIds.value = [...props.selectedIds];
|
|
|
+ fetchEmployees();
|
|
|
}
|
|
|
-);
|
|
|
+});
|
|
|
|
|
|
async function fetchEmployees() {
|
|
|
+ const params: Record<string, unknown> = {
|
|
|
+ page_no: pagination.value.page_no, page_size: pagination.value.page_size,
|
|
|
+ enterprise_id: props.enterpriseId,
|
|
|
+ };
|
|
|
+ if (searchForm.value.name) params.employee_name = searchForm.value.name;
|
|
|
+ if (searchForm.value.phone) params.employee_mobile = searchForm.value.phone;
|
|
|
try {
|
|
|
- const params: Record<string, unknown> = {
|
|
|
- page_no: pagination.value.page_no,
|
|
|
- page_size: pagination.value.page_size,
|
|
|
- enterprise_id: props.enterpriseId,
|
|
|
- };
|
|
|
- if (searchForm.value.name) params.employee_name = searchForm.value.name;
|
|
|
- if (searchForm.value.phone) params.employee_mobile = searchForm.value.phone;
|
|
|
-
|
|
|
const res = await EmployeeAPI.listEmployee(params);
|
|
|
const data = res?.data?.data || res?.data;
|
|
|
const list = data?.items || data?.list || [];
|
|
|
- allEmployees.value = (list).map((item: any) => ({
|
|
|
+ allEmployees.value = list.map((item: any) => ({
|
|
|
id: item.employee_id || item.id,
|
|
|
name: item.employee_name || "-",
|
|
|
phone: item.employee_mobile || "-",
|
|
|
}));
|
|
|
pagination.value.total = data?.total || 0;
|
|
|
-
|
|
|
- // 重新勾选已选条目
|
|
|
- nextTick(() => {
|
|
|
- if (tableRef.value) {
|
|
|
- allEmployees.value.forEach((row) => {
|
|
|
- if (internalSelectedIds.value.includes(row.id)) {
|
|
|
- tableRef.value.toggleRowSelection(row, true);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (e) {
|
|
|
- console.error("获取员工列表失败", e);
|
|
|
- }
|
|
|
+ } catch (e) { console.error(e); }
|
|
|
}
|
|
|
|
|
|
-function handleSelectionChange(rows: Employee[]) {
|
|
|
- // 只更新当前页的选中状态
|
|
|
- const currentIds = rows.map((r) => r.id);
|
|
|
- const otherIds = internalSelectedIds.value.filter(
|
|
|
- (id) => !allEmployees.value.some((e) => e.id === id)
|
|
|
- );
|
|
|
- internalSelectedIds.value = [...new Set([...otherIds, ...currentIds])];
|
|
|
+function toggleRow(id: string) {
|
|
|
+ const idx = selectedIds.value.indexOf(id);
|
|
|
+ if (idx >= 0) selectedIds.value.splice(idx, 1);
|
|
|
+ else selectedIds.value.push(id);
|
|
|
}
|
|
|
|
|
|
+function handleRowClick(row: Row) { toggleRow(row.id); }
|
|
|
+
|
|
|
const displayEmployees = computed(() => {
|
|
|
- if (activeTab.value === "selected") {
|
|
|
- return allEmployees.value.filter((e) => internalSelectedIds.value.includes(e.id));
|
|
|
- }
|
|
|
+ if (activeTab.value === "selected") return allEmployees.value.filter((e) => selectedIds.value.includes(e.id));
|
|
|
return allEmployees.value;
|
|
|
});
|
|
|
|
|
|
-function handleClose() {
|
|
|
- emit("update:visible", false);
|
|
|
-}
|
|
|
-
|
|
|
-function handleReset() {
|
|
|
- searchForm.value = { name: "", phone: "" };
|
|
|
- pagination.value.page_no = 1;
|
|
|
- fetchEmployees();
|
|
|
-}
|
|
|
-
|
|
|
-function handleSearch() {
|
|
|
- pagination.value.page_no = 1;
|
|
|
- fetchEmployees();
|
|
|
-}
|
|
|
-
|
|
|
-function handleTabChange() {
|
|
|
- // 不重置
|
|
|
-}
|
|
|
-
|
|
|
-function handleConfirm() {
|
|
|
- emit("confirm", internalSelectedIds.value);
|
|
|
- emit("update:visible", false);
|
|
|
-}
|
|
|
+function handleClose() { emit("update:visible", false); }
|
|
|
+function handleReset() { searchForm.value = { name: "", phone: "" }; pagination.value.page_no = 1; fetchEmployees(); }
|
|
|
+function handleSearch() { pagination.value.page_no = 1; fetchEmployees(); }
|
|
|
+function handleConfirm() { emit("confirm", [...selectedIds.value]); emit("update:visible", false); }
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.employee-selector {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16px;
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-.employee-selector__search {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: flex-start;
|
|
|
-}
|
|
|
-.employee-selector__actions {
|
|
|
- display: flex;
|
|
|
- gap: 8px;
|
|
|
-}
|
|
|
-.employee-selector__tabs {
|
|
|
- background: #f5f7fa;
|
|
|
- padding: 8px;
|
|
|
-}
|
|
|
-.employee-selector__table {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-.employee-selector__pagination {
|
|
|
- padding: 8px 0;
|
|
|
- border-top: 1px solid #ebeef5;
|
|
|
-}
|
|
|
-.employee-selector__footer {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 8px;
|
|
|
- padding-top: 16px;
|
|
|
- border-top: 1px solid #ebeef5;
|
|
|
-}
|
|
|
+.employee-selector { display: flex; flex-direction: column; gap: 16px; height: 100%; }
|
|
|
+.employee-selector__search { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
|
+.employee-selector__actions { display: flex; gap: 8px; }
|
|
|
+.employee-selector__tabs { background: #f5f7fa; padding: 8px; }
|
|
|
+.employee-selector__pagination { padding: 8px 0; border-top: 1px solid #ebeef5; }
|
|
|
+.employee-selector__footer { display: flex; justify-content: flex-end; gap: 8px; padding-top: 16px; border-top: 1px solid #ebeef5; }
|
|
|
</style>
|