From 11b40328f7aa7599f89189d0ebcbbdf8773f9e1b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 21 四月 2026 10:41:41 +0800
Subject: [PATCH] 新疆马铃薯 1.巡检记录添加巡检状态和巡检结果展示字段
---
src/views/equipmentManagement/ledger/index.vue | 780 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 697 insertions(+), 83 deletions(-)
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index 4327e98..8e3fae5 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -1,104 +1,718 @@
<template>
- <div class="app-container">
- <el-form :model="filters" :inline="true">
- <el-form-item label="鎼滅储">
+ <div class="app-container ledger-view">
+ <div class="left-panel">
+ <div class="tree-toolbar">
<el-input
- v-model="filters.searchText"
- style="width: 240px"
- placeholder="璇疯緭鍏�"
+ v-model="treeKeyword"
+ style="width: calc(100% - 102px)"
+ placeholder="璇疯緭鍏ュ尯鍩熷悕绉�"
clearable
- :prefix-icon="Search"
- @change="getTableData"
+ prefix-icon="Search"
+ @input="filterTree"
+ @clear="filterTree"
/>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="getTableData">鎼滅储</el-button>
- <el-button @click="resetFilters">閲嶇疆</el-button>
- </el-form-item>
- </el-form>
- <PIMTable
- :column="columns"
- :tableData="dataList"
- :page="{
- current: pagination.currentPage,
- size: pagination.pageSize,
- total: pagination.total,
- }"
+ <el-button type="primary" @click="openAreaDialog('addRoot')">鏂板鍖哄煙</el-button>
+ </div>
+ <div class="tree-actions">
+ <el-button link type="primary" @click="resetTreeSelection">鍏ㄩ儴鍖哄煙</el-button>
+ </div>
+ <el-tree
+ ref="treeRef"
+ v-loading="treeLoading"
+ :data="treeData"
+ :props="treeProps"
+ node-key="id"
+ highlight-current
+ default-expand-all
+ :expand-on-click-node="false"
+ :filter-node-method="filterTreeNode"
+ class="ledger-tree"
+ @node-click="handleTreeNodeClick"
+ >
+ <template #default="{ node, data }">
+ <div class="tree-node">
+ <span class="tree-node-content">
+ <el-icon class="tree-node-icon">
+ <component
+ :is="
+ data.children && data.children.length > 0
+ ? node.expanded
+ ? 'FolderOpened'
+ : 'Folder'
+ : 'Tickets'
+ "
+ />
+ </el-icon>
+ <span class="tree-node-label">{{ data.areaName }}</span>
+ </span>
+ <div class="tree-node-actions">
+ <el-button link type="primary" @click.stop="openAreaDialog('edit', data)">缂栬緫</el-button>
+ <el-button link type="primary" @click.stop="openAreaDialog('addChild', data)">鏂板</el-button>
+ <el-button
+ v-if="!hasChildren(data)"
+ link
+ type="danger"
+ @click.stop="handleDeleteArea(data)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ </template>
+ </el-tree>
+ </div>
+
+ <div class="right-panel">
+ <el-form :model="filters" :inline="true">
+ <el-form-item label="璁惧鍚嶇О">
+ <el-input
+ v-model="filters.deviceName"
+ style="width: 200px"
+ placeholder="璇疯緭鍏ヨ澶囧悕绉�"
+ clearable
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item label="瑙勬牸鍨嬪彿">
+ <el-input
+ v-model="filters.deviceModel"
+ style="width: 200px"
+ placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+ clearable
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item label="渚涘簲鍟�">
+ <el-input
+ v-model="filters.supplierName"
+ style="width: 200px"
+ placeholder="璇疯緭鍏ヤ緵搴斿晢"
+ clearable
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item label="褰曞叆鏃ユ湡">
+ <el-date-picker
+ v-model="filters.entryDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="changeDaterange"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+ <el-button @click="handleResetFilters">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <div class="table_list">
+ <div class="actions">
+ <div class="actions-tip">
+ <span v-if="selectedAreaName">褰撳墠鍖哄煙锛歿{ selectedAreaName }}</span>
+ </div>
+ <div>
+ <el-button type="primary" icon="Plus" @click="add">鏂板</el-button>
+ <el-button type="info" icon="Upload" @click="handleImport">瀵煎叆</el-button>
+ <el-button icon="download" @click="handleOut">瀵煎嚭</el-button>
+ <el-button
+ type="danger"
+ icon="Delete"
+ :disabled="multipleList.length <= 0"
+ @click="deleteRow(multipleList.map((item) => item.id))"
+ >
+ 鎵归噺鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <PIMTable
+ rowKey="id"
+ isSelection
+ :column="columns"
+ :tableData="dataList"
+ :page="{
+ current: pagination.currentPage,
+ size: pagination.pageSize,
+ total: pagination.total,
+ }"
+ @selection-change="handleSelectionChange"
+ @pagination="changePage"
+ />
+ </div>
+ </div>
+
+ <Modal ref="modalRef" @success="getTableData" />
+
+ <el-dialog
+ v-model="areaDialogVisible"
+ :title="areaDialogTitle"
+ width="480px"
+ @close="closeAreaDialog"
>
- <template #operation>
- <el-button type="primary" text>缂栬緫</el-button>
- <el-button type="danger" text>鍒犻櫎</el-button>
+ <el-form ref="areaFormRef" :model="areaForm" :rules="areaRules" label-width="88px">
+ <el-form-item label="鍖哄煙鍚嶇О" prop="areaName">
+ <el-input v-model="areaForm.areaName" placeholder="璇疯緭鍏ュ尯鍩熷悕绉�" />
+ </el-form-item>
+ <el-form-item label="鎺掑簭" prop="sort">
+ <el-input-number v-model="areaForm.sort" :min="0" :step="1" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input
+ v-model="areaForm.remark"
+ type="textarea"
+ :rows="4"
+ maxlength="200"
+ show-word-limit
+ placeholder="璇疯緭鍏ュ娉�"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitAreaForm">纭畾</el-button>
+ <el-button @click="closeAreaDialog">鍙栨秷</el-button>
+ </div>
</template>
- </PIMTable>
+ </el-dialog>
+
+ <el-dialog v-model="qrDialogVisible" title="浜岀淮鐮�" width="300px" draggable>
+ <div class="qr-dialog">
+ <img :src="qrCodeUrl" alt="浜岀淮鐮�" class="qr-image" />
+ <div class="qr-footer">
+ <el-button type="primary" @click="downloadQRCode">涓嬭浇浜岀淮鐮佸浘鐗�</el-button>
+ </div>
+ </div>
+ </el-dialog>
+
+ <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+ <el-upload
+ ref="uploadRef"
+ :limit="1"
+ accept=".xlsx, .xls"
+ :headers="upload.headers"
+ :action="upload.url"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :auto-upload="false"
+ drag
+ >
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <template #tip>
+ <div class="el-upload__tip text-center">
+ <span>浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢銆�</span>
+ <el-link
+ type="primary"
+ :underline="false"
+ style="font-size: 12px; vertical-align: baseline; margin-left: 5px"
+ @click="importTemplate"
+ >
+ 涓嬭浇妯℃澘
+ </el-link>
+ </div>
+ </template>
+ </el-upload>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitFileForm">纭畾</el-button>
+ <el-button @click="upload.open = false">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup>
import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { Search } from "@element-plus/icons-vue";
+import { getLedgerPage, delLedger } from "@/api/equipmentManagement/ledger";
+import {
+ getDeviceAreaTree,
+ getDeviceAreaDetail,
+ addDeviceArea,
+ updateDeviceArea,
+ deleteDeviceArea,
+} from "@/api/equipmentManagement/deviceArea";
+import { onMounted, getCurrentInstance, ref, reactive } from "vue";
+import Modal from "./Modal.vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import { UploadFilled } from "@element-plus/icons-vue";
+import { getToken } from "@/utils/auth";
+import dayjs from "dayjs";
+import QRCode from "qrcode";
defineOptions({
name: "璁惧鍙拌处",
});
-const { filters, columns, dataList, pagination, getTableData, resetFilters } =
- usePaginationApi(
- () => {},
+const multipleList = ref([]);
+const { proxy } = getCurrentInstance();
+const modalRef = ref();
+const treeRef = ref();
+const areaFormRef = ref();
+const treeKeyword = ref("");
+const treeLoading = ref(false);
+const treeData = ref([]);
+const selectedAreaName = ref("");
+const areaDialogVisible = ref(false);
+const areaDialogTitle = ref("鏂板鍖哄煙");
+const areaDialogMode = ref("addRoot");
+const qrDialogVisible = ref(false);
+const qrCodeUrl = ref("");
+const qrRowData = ref(null);
+const uploadRef = ref(null);
+
+const treeProps = {
+ children: "children",
+ label: "areaName",
+};
+
+const upload = reactive({
+ open: false,
+ title: "",
+ isUploading: false,
+ headers: { Authorization: "Bearer " + getToken() },
+ url: import.meta.env.VITE_APP_BASE_API + "/device/ledger/import",
+});
+
+const areaForm = reactive({
+ id: undefined,
+ areaName: "",
+ parentId: undefined,
+ sort: 0,
+ remark: "",
+});
+
+const areaRules = {
+ areaName: [{ required: true, message: "璇疯緭鍏ュ尯鍩熷悕绉�", trigger: "blur" }],
+};
+
+const {
+ filters,
+ columns,
+ dataList,
+ pagination,
+ getTableData,
+ onCurrentChange,
+} = usePaginationApi(
+ getLedgerPage,
+ {
+ deviceName: undefined,
+ deviceModel: undefined,
+ supplierName: undefined,
+ entryDate: undefined,
+ entryDateStart: undefined,
+ entryDateEnd: undefined,
+ areaId: undefined,
+ areaName: undefined,
+ },
+ [
{
- searchText: undefined,
+ label: "鎵�鍦ㄥ尯鍩�",
+ prop: "areaName",
},
- [
- {
- label: "璁惧鍚嶇О",
- align: "center",
+ {
+ label: "璁惧鍚嶇О",
+ prop: "deviceName",
+ },
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "deviceModel",
+ },
+ {
+ label: "璁惧鍝佺墝",
+ prop: "deviceBrand",
+ },
+ {
+ label: "璁惧绫诲瀷",
+ prop: "type",
+ },
+ {
+ label: "渚涘簲鍟�",
+ prop: "supplierName",
+ },
+ {
+ label: "瀛樻斁浣嶇疆",
+ prop: "storageLocation",
+ },
+ {
+ label: "鏁伴噺",
+ prop: "number",
+ },
+ {
+ label: "褰曞叆浜�",
+ prop: "createUser",
+ },
+ {
+ label: "褰曞叆鏃ユ湡",
+ prop: "createTime",
+ formatData: (v) => {
+ if (!v) return "";
+ return v.includes(" ") ? v.split(" ")[0] : v;
},
- {
- label: "瑙勬牸鍨嬪彿",
- align: "center",
- },
- {
- label: "渚涘簲鍟�",
- align: "center",
- },
- {
- label: "鍗曚綅",
- align: "center",
- },
- {
- label: "鏁伴噺",
- align: "center",
- },
- {
- label: "鍚◣鍗曚环",
- align: "center",
- },
- {
- label: "鍚◣鎬讳环",
- align: "center",
- },
- {
- label: "绋庣巼",
- align: "center",
- },
- {
- label: "涓嶅惈绋庢�讳环",
- align: "center",
- },
- {
- label: "褰曞叆浜�",
- align: "center",
- },
- {
- label: "褰曞叆鏃ユ湡",
- align: "center",
- },
- {
- label: "鎿嶄綔",
- dataType: "slot",
- slot: "operation",
- align: "center",
- width: "150px",
- },
- ]
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 150,
+ operation: [
+ {
+ name: "缂栬緫",
+ clickFun: (row) => {
+ edit(row.id);
+ },
+ },
+ {
+ name: "鐢熸垚浜岀淮鐮�",
+ clickFun: (row) => {
+ showQRCode(row);
+ },
+ },
+ ],
+ },
+ ]
+);
+
+const loadTreeData = async () => {
+ treeLoading.value = true;
+ try {
+ const res = await getDeviceAreaTree();
+ treeData.value = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
+ } finally {
+ treeLoading.value = false;
+ }
+};
+
+const resetAreaForm = () => {
+ areaForm.id = undefined;
+ areaForm.areaName = "";
+ areaForm.parentId = undefined;
+ areaForm.sort = 0;
+ areaForm.remark = "";
+};
+
+const filterTree = () => {
+ treeRef.value?.filter(treeKeyword.value);
+};
+
+const filterTreeNode = (value, data) => {
+ if (!value) {
+ return true;
+ }
+ return String(data.areaName || "").includes(value);
+};
+
+const handleTreeNodeClick = (data) => {
+ filters.areaId = data.id;
+ filters.areaName = data.areaName;
+ selectedAreaName.value = data.areaName || "";
+ getTableData();
+};
+
+const openAreaDialog = async (mode, row) => {
+ areaDialogMode.value = mode;
+ areaDialogTitle.value =
+ mode === "edit" ? "缂栬緫鍖哄煙" : mode === "addChild" ? "鏂板瀛愬尯鍩�" : "鏂板鍖哄煙";
+ resetAreaForm();
+ areaDialogVisible.value = true;
+ if (mode === "addChild") {
+ areaForm.parentId = row.id;
+ areaForm.sort = 0;
+ return;
+ }
+ if (mode === "edit" && row?.id) {
+ const res = await getDeviceAreaDetail(row.id);
+ const detail = res?.data || {};
+ areaForm.id = detail.id;
+ areaForm.areaName = detail.areaName || "";
+ areaForm.parentId = detail.parentId;
+ areaForm.sort = detail.sort ?? 0;
+ areaForm.remark = detail.remark || "";
+ }
+};
+
+const closeAreaDialog = () => {
+ areaDialogVisible.value = false;
+ areaFormRef.value?.resetFields();
+ resetAreaForm();
+};
+
+const submitAreaForm = () => {
+ areaFormRef.value?.validate(async (valid) => {
+ if (!valid) {
+ return;
+ }
+ const submitData = {
+ id: areaForm.id,
+ areaName: areaForm.areaName,
+ parentId: areaForm.parentId,
+ sort: areaForm.sort,
+ remark: areaForm.remark,
+ };
+ const request = areaDialogMode.value === "edit" ? updateDeviceArea : addDeviceArea;
+ const { code } = await request(submitData);
+ if (code === 200) {
+ ElMessage.success(areaDialogMode.value === "edit" ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+ closeAreaDialog();
+ await loadTreeData();
+ }
+ });
+};
+
+const handleDeleteArea = (row) => {
+ if (hasChildren(row)) {
+ ElMessage.warning("褰撳墠鍖哄煙瀛樺湪涓嬬骇鍖哄煙锛屼笉鑳藉垹闄�");
+ return;
+ }
+ ElMessageBox.confirm("姝ゆ搷浣滃皢鍒犻櫎璇ヨ澶囧尯鍩燂紝鏄惁缁х画锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(async () => {
+ const { code } = await deleteDeviceArea([row.id]);
+ if (code === 200) {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ if (filters.areaId === row.id) {
+ resetTreeSelection();
+ }
+ await loadTreeData();
+ }
+ });
+};
+
+const hasChildren = (row) => Array.isArray(row?.children) && row.children.length > 0;
+
+const resetTreeSelection = () => {
+ treeRef.value?.setCurrentKey(null);
+ selectedAreaName.value = "";
+ filters.areaId = undefined;
+ filters.areaName = undefined;
+ getTableData();
+};
+
+const handleSelectionChange = (selectionList) => {
+ multipleList.value = selectionList;
+};
+
+const add = () => {
+ modalRef.value.openModal();
+};
+
+const edit = (id) => {
+ modalRef.value.loadForm(id);
+};
+
+const changePage = ({ page, limit }) => {
+ pagination.currentPage = page;
+ pagination.pageSize = limit;
+ onCurrentChange(page);
+};
+
+const deleteRow = (id) => {
+ ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹紝鏄惁缁х画锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(async () => {
+ const { code } = await delLedger(id);
+ if (code == 200) {
+ ElMessage({
+ type: "success",
+ message: "鍒犻櫎鎴愬姛",
+ });
+ getTableData();
+ }
+ });
+};
+
+const changeDaterange = (value) => {
+ if (value) {
+ filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+ filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+ } else {
+ filters.entryDateStart = undefined;
+ filters.entryDateEnd = undefined;
+ }
+ getTableData();
+};
+
+const handleResetFilters = () => {
+ filters.deviceName = undefined;
+ filters.deviceModel = undefined;
+ filters.supplierName = undefined;
+ filters.entryDate = undefined;
+ filters.entryDateStart = undefined;
+ filters.entryDateEnd = undefined;
+ getTableData();
+};
+
+const handleOut = () => {
+ ElMessageBox.confirm("褰撳墠鏌ヨ缁撴灉灏嗚瀵煎嚭锛屾槸鍚︾‘璁ゅ鍑猴紵", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/device/ledger/export", {}, "璁惧鍙拌处妗f.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+
+const showQRCode = async (row) => {
+ const qrContent = proxy.javaApi + "/device-info?deviceId=" + row.id;
+ qrCodeUrl.value = await QRCode.toDataURL(qrContent);
+ qrRowData.value = row;
+ qrDialogVisible.value = true;
+};
+
+const downloadQRCode = () => {
+ const a = document.createElement("a");
+ a.href = qrCodeUrl.value;
+ a.download = `${qrRowData.value.deviceName || "浜岀淮鐮�"}.png`;
+ a.click();
+};
+
+const handleImport = () => {
+ upload.title = "璁惧鍙拌处瀵煎叆";
+ upload.open = true;
+};
+
+const importTemplate = () => {
+ proxy.download("/device/ledger/downloadTemplate", {}, `璁惧鍙拌处瀵煎叆妯℃澘_${new Date().getTime()}.xlsx`);
+};
+
+const handleFileUploadProgress = () => {
+ upload.isUploading = true;
+};
+
+const handleFileSuccess = (response, file) => {
+ upload.open = false;
+ upload.isUploading = false;
+ uploadRef.value?.handleRemove(file);
+ proxy.$alert(
+ "<div style='overflow:auto;overflow-x:hidden;max-height:70vh;padding:10px 20px 0;'>" +
+ response.msg +
+ "</div>",
+ "瀵煎叆缁撴灉",
+ { dangerouslyUseHTMLString: true }
);
+ getTableData();
+};
+
+const submitFileForm = () => {
+ uploadRef.value?.submit();
+};
+
+onMounted(async () => {
+ await loadTreeData();
+ getTableData();
+});
</script>
+
+<style lang="scss" scoped>
+.ledger-view {
+ display: flex;
+ gap: 20px;
+}
+
+.left-panel {
+ width: 320px;
+ min-width: 320px;
+ padding: 16px;
+ background: #fff;
+ border-radius: 4px;
+}
+
+.right-panel {
+ flex: 1;
+ min-width: 0;
+ padding: 16px;
+ background: #fff;
+ border-radius: 4px;
+}
+
+.tree-toolbar {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.tree-actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 8px;
+}
+
+.ledger-tree {
+ height: calc(100vh - 230px);
+ overflow-y: auto;
+}
+
+.tree-node {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+}
+
+.tree-node-content {
+ display: flex;
+ align-items: center;
+ min-width: 0;
+}
+
+.tree-node-icon {
+ color: #e6a23c;
+ margin-right: 8px;
+ font-size: 18px;
+}
+
+.tree-node-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.tree-node-actions {
+ flex-shrink: 0;
+}
+
+.table_list {
+ margin-top: 0;
+}
+
+.actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ gap: 12px;
+}
+
+.actions-tip {
+ color: #606266;
+ font-size: 14px;
+}
+
+.qr-dialog {
+ text-align: center;
+}
+
+.qr-image {
+ width: 200px;
+ height: 200px;
+}
+
+.qr-footer {
+ margin: 10px 0;
+}
+</style>
--
Gitblit v1.9.3