| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- <template>
- <div class="ele-body" v-loading="loading">
- <el-card shadow="never">
- <!-- 搜索区 -->
- <el-form label-width="80px" class="ele-form-search" @keyup.enter.native="applyFilters" @submit.native.prevent>
- <el-row :gutter="15">
- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
- <el-form-item label="领用单号">
- <el-input v-model.trim="draftFilters.keyword" clearable placeholder="请输入单号关键字" />
- </el-form-item>
- </el-col>
- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
- <el-form-item label="领用员工">
- <el-input v-model.trim="draftFilters.employeeName" clearable placeholder="请输入员工姓名" />
- </el-form-item>
- </el-col>
- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
- <el-form-item label="状态">
- <el-select v-model="draftFilters.receiptStatus" clearable placeholder="请选择" style="width: 100%">
- <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
- <div class="ele-form-actions">
- <el-button type="primary" icon="el-icon-search" class="ele-btn-icon" @click="applyFilters">
- 查询
- </el-button>
- <el-button icon="el-icon-refresh" class="ele-btn-icon" @click="resetFilters">
- 重置
- </el-button>
- </div>
- </el-col>
- </el-row>
- </el-form>
- <!-- 列表表格 -->
- <ele-pro-table ref="table" row-key="id" :columns="columns" :datasource="fetchPage" :need-page="true"
- :page-size="20" :cache-key="$tableCacheKey('hr-item-receipt')">
- <template v-slot:toolbar>
- <el-button v-if="$hasPermission('main:hrItemReceipt:save')" type="primary" icon="el-icon-plus"
- class="ele-btn-icon" :loading="creating" @click="openCreate">
- 新建
- </el-button>
- </template>
- <template v-slot:status="{ row }">
- <el-tag v-if="row.receiptStatus" size="mini" :type="statusTagType(row.receiptStatus)">
- {{ statusLabel(row.receiptStatus) }}
- </el-tag>
- <span v-else>-</span>
- </template>
- <!-- 点领用单号开详情,替代原操作列「查看」入口 -->
- <template v-slot:documentCode="{ row }">
- <el-link v-if="row.documentCode" type="primary" :underline="false" @click="openDetail(row)">
- {{ row.documentCode }}
- </el-link>
- <span v-else>-</span>
- </template>
- <template v-slot:itemSummary="{ row }">
- <template v-if="Array.isArray(row.details) && row.details.length">
- {{row.details.slice(0, 2).map(d => d.itemName).join('、')}}
- <span v-if="row.details.length > 2" class="item-summary-more">
- 等{{ row.details.length }}项
- </span>
- </template>
- <span v-else>-</span>
- </template>
- <template v-slot:action="{ row }">
- <el-link v-if="canEdit(row) && $hasPermission('main:hrItemReceipt:update')" type="primary"
- :underline="false" icon="el-icon-edit" @click="openEdit(row)">
- 编辑
- </el-link>
- <el-link v-if="canSubmit(row) && $hasPermission('main:hrItemReceipt:submit')" type="primary"
- :underline="false" icon="el-icon-s-promotion" @click="submitApproval(row)">
- 提交
- </el-link>
- <el-link v-if="canViewApproval(row)" type="primary" :underline="false" icon="el-icon-share"
- @click="openApprovalRecord(row)">
- 审批记录
- </el-link>
- <el-link v-if="canDelete(row) && $hasPermission('main:hrItemReceipt:delete')" type="danger"
- :underline="false" icon="el-icon-delete" :disabled="submitting" @click="confirmDelete(row)">
- 删除
- </el-link>
- </template>
- <template slot="empty">
- <el-empty description="暂无物品领用记录" />
- </template>
- </ele-pro-table>
- </el-card>
- <!-- 编辑弹框(新建/编辑 共用) -->
- <ele-modal :title="editTitle" :visible.sync="editVisible" width="1080px" custom-class="receipt-edit-modal"
- :close-on-click-modal="false" append-to-body top="5vh" :maxable="true" @closed="onEditClosed">
- <div v-if="editVisible" class="receipt-edit-body">
- <el-form label-width="88px" class="receipt-edit-form">
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="领用员工" required>
- <el-select v-if="!editing.id" v-model="editing.employeeId" filterable remote
- :remote-method="searchEmployees" :loading="employeeLoading" placeholder="搜索姓名或工号" style="width: 100%"
- :rules="[{ required: true, message: '请选择领用员工', trigger: 'change' }]">
- <el-option v-for="item in employeeOptions" :key="String(item.id)"
- :label="`${item.name || '-'}(${item.employeeNo || item.jobNumber || item.id})`"
- :value="String(item.id)" />
- </el-select>
- <el-input v-else :value="employeeDisplay" disabled />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="单据号">
- <el-input :value="editing.documentCode || '保存后生成'" disabled />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="创建时间">
- <el-input :value="editing.createTime || '-'" disabled />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- <!-- 复用 ItemReceiptPanel(独立嵌入模式) -->
- <ItemReceiptPanel ref="itemReceiptPanel" :employee-id="employeeIdForPanel" :init-receipt-id="editing.id || ''"
- :embedded="true" @saved="onSavedFromPanel" @request-approval="openReceiptApproval" />
- </div>
- <span slot="footer">
- <el-button @click="editVisible = false">关闭</el-button>
- <el-button type="primary" icon="el-icon-folder-checked" :loading="panelSaving" @click="saveFromPanel">
- 保存草稿
- </el-button>
- </span>
- </ele-modal>
- <!-- 详情弹框 -->
- <ele-modal :title="'领用单详情 · ' + (detailRow?.documentCode || detailRow?.id || '')" :visible.sync="detailVisible"
- width="980px" custom-class="receipt-detail-modal" :close-on-click-modal="false" append-to-body top="5vh">
- <div v-if="detailRow" class="receipt-detail-body">
- <div class="detail-header">
- <div class="detail-header-left">
- <div class="detail-title">{{ detailRow.employeeName || '-' }}</div>
- <div class="detail-subtitle">
- {{ detailRow.jobNumber || '-' }} ·
- {{ detailRow.departmentName || '-' }} ·
- {{ detailRow.legalEntityName || '-' }}
- <span v-if="detailRow.createTime" class="detail-time">
- · {{ detailRow.createTime }}
- </span>
- </div>
- </div>
- <el-tag size="medium" :type="statusTagType(detailRow.receiptStatus)">
- {{ statusLabel(detailRow.receiptStatus) }}
- </el-tag>
- </div>
- <el-table :data="detailRow.details || []" border size="small" empty-text="暂无明细" class="detail-table">
- <el-table-column type="index" label="#" width="48" align="center" />
- <el-table-column prop="itemName" label="物品名称" min-width="140" show-overflow-tooltip />
- <el-table-column prop="itemType" label="物品类型" width="110" />
- <el-table-column prop="itemModel" label="型号" min-width="120" show-overflow-tooltip />
- <el-table-column prop="specifications" label="规格" min-width="140" show-overflow-tooltip />
- <el-table-column prop="itemUnit" label="单位" width="70" align="center" />
- <el-table-column prop="quantity" label="数量" width="80" align="center" />
- <el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
- </el-table>
- </div>
- <span slot="footer">
- <el-button @click="detailVisible = false">关闭</el-button>
- <el-button v-if="detailRow && canEdit(detailRow) && $hasPermission('main:hrItemReceipt:update')"
- type="primary" icon="el-icon-edit-outline" @click="editFromDetail">
- 编辑
- </el-button>
- </span>
- </ele-modal>
- <!-- 提交审批弹框(公共组件) -->
- <processSubmitDialog :processSubmitDialogFlag.sync="processSubmitDialogFlag" v-if="processSubmitDialogFlag"
- ref="processSubmitDialogRef" @reload="reloadTable" />
- <!-- 审批流程详情弹框 -->
- <BpmProcessDetailDialog ref="approvalRecordDialog" />
- </div>
- </template>
- <script>
- import {
- getItemReceiptPage,
- getItemReceipt,
- deleteItemReceipt,
- receiptStatusLabel,
- RECEIPT_APPROVAL_BUSINESS_KEY,
- } from "@/api/hr/onboardingItemReceipt";
- import { searchUsersByKeyword, getUserPage, adaptEmployee } from "@/api/organization";
- import { showRequestError } from "@/utils/message";
- import { getCurrentUser } from "@/utils/token-util";
- import submitGuard from "@/mixins/submit-guard";
- import ItemReceiptPanel from "@/views/hrManagement/onboardingHandling/ItemReceiptPanel.vue";
- import processSubmitDialog from "@/BIZComponents/processSubmitDialog/processSubmitDialog.vue";
- import BpmProcessDetailDialog from "@/views/bpm/processInstance/detailDialog.vue";
- export default {
- name: "ItemReceiptManagement",
- components: { ItemReceiptPanel, processSubmitDialog, BpmProcessDetailDialog },
- mixins: [submitGuard],
- data() {
- return {
- loading: false,
- creating: false,
- processSubmitDialogFlag: false,
- draftFilters: { keyword: "", employeeName: "", receiptStatus: "" },
- filters: { keyword: "", employeeName: "", receiptStatus: "" },
- editVisible: false,
- detailVisible: false,
- detailRow: null,
- editing: this.emptyEditRow(),
- employeeOptions: [],
- employeeLoading: false,
- statusOptions: [
- { value: "DRAFT", label: "草稿" },
- { value: "APPROVING", label: "审批中" },
- { value: "COMPLETED", label: "已完成" },
- ],
- };
- },
- computed: {
- styleResponsive() {
- return this.$store.state.theme?.styleResponsive !== false;
- },
- columns() {
- return [
- { type: "index", label: "序号", width: 60, align: "center" },
- {
- prop: "documentCode",
- label: "领用单号",
- minWidth: 160,
- align: "left",
- showOverflowTooltip: true,
- slot: "documentCode",
- },
- {
- prop: "employeeName",
- label: "领用员工",
- minWidth: 110,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "jobNumber",
- label: "工号",
- width: 110,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "legalEntityName",
- label: "所属机构",
- minWidth: 180,
- align: "left",
- showOverflowTooltip: true,
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "departmentName",
- label: "所属部门",
- minWidth: 140,
- align: "left",
- showOverflowTooltip: true,
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "itemSummary",
- label: "物品概览",
- minWidth: 200,
- align: "left",
- slot: "itemSummary",
- },
- {
- prop: "receiptStatus",
- label: "状态",
- width: 100,
- align: "left",
- slot: "status",
- },
- {
- prop: "createTime",
- label: "创建时间",
- width: 100,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "createUserName",
- label: "创建人",
- width: 90,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "updateTime",
- label: "修改时间",
- width: 100,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- prop: "updateUserName",
- label: "修改人",
- width: 90,
- align: "left",
- formatter: (r, _c, v) => v || "-",
- },
- {
- columnKey: "action",
- label: "操作",
- width: 320,
- fixed: "right",
- slot: "action",
- },
- ];
- },
- editTitle() {
- return this.editing.id ? "编辑物品领用单" : "新建物品领用单";
- },
- employeeIdForPanel() {
- return this.editing.employeeId || "";
- },
- employeeDisplay() {
- return [
- this.editing.employeeName,
- this.editing.jobNumber,
- ].filter(Boolean).join(" · ") || String(this.editing.employeeId || "");
- },
- panelSaving() {
- return this.$refs.itemReceiptPanel?.saving || false;
- },
- },
- methods: {
- emptyEditRow() {
- return {
- id: "",
- employeeId: "",
- employeeName: "",
- jobNumber: "",
- departmentName: "",
- legalEntityName: "",
- documentCode: "",
- createTime: "",
- };
- },
- statusLabel(value) {
- return receiptStatusLabel(value) || value || "-";
- },
- statusTagType(value) {
- const key = String(value || "").toUpperCase();
- if (key === "COMPLETED") return "success";
- if (key === "APPROVING") return "warning";
- return "info";
- },
- canEdit(row) {
- const status = String(row?.receiptStatus || "").toUpperCase();
- return status === "DRAFT";
- },
- canDelete(row) {
- return this.canEdit(row);
- },
- canSubmit(row) {
- return this.canEdit(row);
- },
- canViewApproval(row) {
- const status = String(row?.receiptStatus || "").toUpperCase();
- return status === "APPROVING" || status === "COMPLETED";
- },
- // ========== 搜索 ==========
- applyFilters() {
- this.filters = { ...this.draftFilters };
- this.reloadTable();
- },
- resetFilters() {
- this.draftFilters = { keyword: "", employeeName: "", receiptStatus: "" };
- this.filters = { keyword: "", employeeName: "", receiptStatus: "" };
- this.reloadTable();
- },
- reloadTable() {
- this.$nextTick(() => this.$refs.table?.reload({ page: 1 }));
- },
- // ========== 列表数据源 ==========
- async fetchPage({ page, limit }) {
- const pageNum = Number(page) || 1;
- const size = Number(limit) || 10;
- this.loading = true;
- try {
- const data = await getItemReceiptPage({
- pageNum,
- size,
- offset: (pageNum - 1) * size,
- receiptStatus: this.filters.receiptStatus || undefined,
- sortName: "createTime",
- orderBy: "descending",
- });
- const list = (data.list || []).map(this.decorateRow);
- return { list, count: Number(data.count ?? list.length) };
- } catch (error) {
- showRequestError(error, "物品领用列表加载失败");
- return { list: [], count: 0 };
- } finally {
- this.loading = false;
- }
- },
- decorateRow(row) {
- // 分页返回的 list 可能不带 details(为了性能),让详情弹框再 GET 拉
- return row;
- },
- // ========== 员工搜索 ==========
- async searchEmployees(keyword) {
- this.employeeLoading = true;
- try {
- const data = keyword
- ? await searchUsersByKeyword(keyword, { pageNum: 1, size: 30 })
- : await getUserPage({ pageNum: 1, size: 30 });
- const list = Array.isArray(data?.list) ? data.list : [];
- this.employeeOptions = list.map((item) => {
- const emp = adaptEmployee(item);
- return { ...emp, id: String(emp.id) };
- });
- } catch (error) {
- this.employeeOptions = [];
- } finally {
- this.employeeLoading = false;
- }
- },
- // ========== 新建 ==========
- openCreate() {
- this.editing = this.emptyEditRow();
- this.editVisible = true;
- this.searchEmployees("");
- },
- // ========== 编辑 ==========
- openEdit(row) {
- if (!this.canEdit(row)) {
- this.$message.warning("仅草稿可编辑");
- return;
- }
- this.openEditFromId(row.id);
- },
- async openEditFromId(id) {
- try {
- const detail = await getItemReceipt(id);
- this.editing = {
- id: detail.id ?? "",
- employeeId: detail.employeeId ?? "",
- employeeName: detail.employeeName || "",
- jobNumber: detail.jobNumber || "",
- departmentName: detail.departmentName || "",
- legalEntityName: detail.legalEntityName || "",
- documentCode: detail.documentCode || "",
- createTime: detail.createTime || "",
- };
- // 确保员工下拉有这个选项(编辑时不允许换员工,但展示用)
- if (this.editing.employeeId) {
- const exists = this.employeeOptions.some(
- (it) => String(it.id) === String(this.editing.employeeId),
- );
- if (!exists) {
- this.employeeOptions.unshift({
- id: String(this.editing.employeeId),
- name: this.editing.employeeName,
- employeeNo: this.editing.jobNumber,
- });
- }
- }
- this.editVisible = true;
- } catch (error) {
- showRequestError(error, "领用单详情加载失败");
- }
- },
- // ========== 详情 ==========
- async openDetail(row) {
- this.detailVisible = true;
- this.detailRow = row;
- if (!row.details || !row.details.length) {
- try {
- this.detailRow = await getItemReceipt(row.id);
- } catch (error) {
- showRequestError(error, "领用单详情加载失败");
- }
- }
- },
- editFromDetail() {
- if (!this.detailRow) return;
- this.detailVisible = false;
- this.openEditFromId(this.detailRow.id);
- },
- // ========== 删除 ==========
- confirmDelete(row) {
- if (!this.canDelete(row)) {
- this.$message.warning("仅草稿可删除");
- return undefined;
- }
- return this.runSubmit(
- async () => {
- await this.$confirm(
- `确定删除领用单「${row.documentCode || row.id}」吗?删除后不可恢复。`,
- "删除确认",
- { type: "warning" },
- );
- await deleteItemReceipt(row.id);
- this.reloadTable();
- },
- { success: "删除成功", errorMessage: "删除失败" },
- );
- },
- // ========== ItemReceiptPanel 事件 ==========
- onSavedFromPanel(saved) {
- if (saved?.id) {
- this.editing.id = saved.id;
- this.editing.documentCode = saved.documentCode || this.editing.documentCode;
- this.editing.createTime = saved.createTime || this.editing.createTime;
- }
- this.reloadTable();
- },
- async saveFromPanel() {
- const panel = this.$refs.itemReceiptPanel;
- if (!panel) return;
- const saved = await panel.persist({ silent: false });
- if (saved) {
- this.editing.id = saved.id;
- // 保存成功即自动关闭弹窗(列表已由 onSavedFromPanel 刷新)
- this.editVisible = false;
- }
- },
- onEditClosed() {
- this.editing = this.emptyEditRow();
- },
- // ========== 提交审批 ==========
- async submitApproval(row) {
- let target = row;
- // 如果当前在编辑弹框里,先保存再提交
- if (this.editVisible && this.editing.id === row.id) {
- const panel = this.$refs.itemReceiptPanel;
- if (!panel) return;
- const saved = await panel.persist({ silent: true });
- if (!saved || !saved.id) return;
- target = { ...row, ...saved };
- this.editing.id = saved.id;
- }
- this.openReceiptApproval(target);
- },
- openReceiptApproval(receipt) {
- const id = receipt?.id;
- if (!id) {
- this.$message.warning("领用单保存失败,无法提交审批");
- return;
- }
- this.processSubmitDialogFlag = true;
- this.$nextTick(() => {
- const currentUser = getCurrentUser() || {};
- this.$refs.processSubmitDialogRef?.init({
- businessId: id,
- businessKey: RECEIPT_APPROVAL_BUSINESS_KEY,
- formCreateUserId:
- receipt.createUserId || currentUser.id || currentUser.userId || "",
- variables: {
- businessCode: String(id),
- businessName: "入职物品领用",
- businessType: "入职物品领用审批",
- },
- });
- });
- },
- // ========== 查看审批流程 ==========
- openApprovalRecord(row) {
- this.$refs.approvalRecordDialog?.open(row);
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .receipt-edit-body {
- .receipt-edit-form {
- margin-bottom: 0;
- }
- }
- .item-summary-more {
- color: #8492a6;
- font-size: 14px;
- }
- .receipt-detail-body {
- min-height: 200px;
- }
- .detail-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 16px;
- background: #f7f9fc;
- border-radius: 4px;
- }
- .detail-title {
- font-size: 16px;
- font-weight: 600;
- color: #303133;
- }
- .detail-subtitle {
- font-size: 14px;
- color: #909399;
- margin-top: 4px;
- }
- .detail-time {
- font-size: 14px;
- color: #b0b3b8;
- }
- .detail-table {
- margin-top: 8px;
- }
- </style>
|