| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <!-- æç´¢åºå --> |
| | | <el-row :gutter="20" |
| | | class="search-row"> |
| | | <el-col :span="6"> |
| | | <el-input v-model="searchForm.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | clearable |
| | | @keyup.enter="handleSearch"> |
| | | <template #prefix> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </template> |
| | | </el-input> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.identifierType" |
| | | placeholder="è¯·éæ©æ è¯ç±»å" |
| | | clearable> |
| | | <el-option label="äºç»´ç " |
| | | value="äºç»´ç "></el-option> |
| | | <el-option label="é²ä¼ªç " |
| | | value="é²ä¼ªç "></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | clearable> |
| | | <el-option label="å·²çæ" |
| | | value="å·²çæ"></el-option> |
| | | <el-option label="å·²åé
" |
| | | value="å·²åé
"></el-option> |
| | | <el-option label="已使ç¨" |
| | | value="已使ç¨"></el-option> |
| | | <el-option label="å·²ä½åº" |
| | | value="å·²ä½åº"></el-option> |
| | | </el-select> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-button type="primary" |
| | | @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | <el-button style="float: right;" |
| | | type="primary" |
| | | @click="handleAdd"> |
| | | æ°å¢æ è¯ |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- äº§åæ è¯å表 --> |
| | | <el-table :data="filteredList" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column prop="id" |
| | | label="ID" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="productName" |
| | | label="产ååç§°" |
| | | width="150" /> |
| | | <el-table-column prop="productCode" |
| | | label="产åç¼ç " |
| | | width="120" /> |
| | | <el-table-column prop="batchNo" |
| | | label="æ¹æ¬¡å·" |
| | | width="120" /> |
| | | <el-table-column prop="identifierType" |
| | | label="æ è¯ç±»å" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getIdentifierTypeType(scope.row.identifierType)"> |
| | | {{ scope.row.identifierType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="identifierCode" |
| | | label="æ è¯ç " /> |
| | | <el-table-column prop="status" |
| | | label="ç¶æ" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ scope.row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="generateTime" |
| | | label="çææ¶é´" |
| | | width="160" /> |
| | | <el-table-column label="æä½" |
| | | fixed="right" |
| | | align="center" |
| | | width="280"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleView(scope.row)">æ¥ç</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">ç¼è¾</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="generateQRCode(scope.row)">çæäºç»´ç </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleExport(scope.row)">导åº</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleReassign(scope.row)" |
| | | v-if="scope.row.status === 'å·²åé
'">éæ°åé
</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <!-- å页 --> |
| | | <pagination :total="pagination.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.currentPage" |
| | | :limit="pagination.pageSize" |
| | | @pagination="handleCurrentChange" /> |
| | | </el-card> |
| | | <!-- æ°å¢/ç¼è¾å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="700px"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产åç¼ç " |
| | | prop="productCode"> |
| | | <el-input v-model="form.productCode" |
| | | placeholder="请è¾å
¥äº§åç¼ç "></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¹æ¬¡å·" |
| | | prop="batchNo"> |
| | | <el-input v-model="form.batchNo" |
| | | placeholder="请è¾å
¥æ¹æ¬¡å·"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ è¯ç±»å" |
| | | prop="identifierType"> |
| | | <el-select v-model="form.identifierType" |
| | | placeholder="è¯·éæ©æ è¯ç±»å" |
| | | style="width: 100%"> |
| | | <el-option label="äºç»´ç " |
| | | value="äºç»´ç "></el-option> |
| | | <el-option label="é²ä¼ªç " |
| | | value="é²ä¼ªç "></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="çææ°é" |
| | | prop="quantity"> |
| | | <el-input-number v-model="form.quantity" |
| | | :min="1" |
| | | :max="10000" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | style="width: 100%"> |
| | | <el-option label="å·²çæ" |
| | | value="å·²çæ"></el-option> |
| | | <el-option label="å·²åé
" |
| | | value="å·²åé
"></el-option> |
| | | <el-option label="已使ç¨" |
| | | value="已使ç¨"></el-option> |
| | | <el-option label="å·²ä½åº" |
| | | value="å·²ä½åº"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input type="textarea" |
| | | v-model="form.remark" |
| | | placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" |
| | | rows="3"></el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="handleSubmit">ç¡® å®</el-button> |
| | | <el-button @click="dialogVisible = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- æ è¯çæå¯¹è¯æ¡ --> |
| | | <el-dialog v-model="generateDialogVisible" |
| | | title="æ è¯çæ" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产ååç§°"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="产åç¼ç "> |
| | | <span>{{ currentProduct.productCode }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹æ¬¡å·"> |
| | | <span>{{ currentProduct.batchNo }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ è¯ç±»å"> |
| | | <span>{{ currentProduct.identifierType }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="çææ°é" |
| | | prop="generateQuantity"> |
| | | <el-input-number v-model="generateQuantity" |
| | | :min="1" |
| | | :max="10000" |
| | | style="width: 100%"></el-input-number> |
| | | </el-form-item> |
| | | <el-form-item label="ç¼ç è§å" |
| | | prop="codeRule"> |
| | | <el-select v-model="codeRule" |
| | | placeholder="è¯·éæ©ç¼ç è§å" |
| | | style="width: 100%"> |
| | | <el-option label="产åç¼ç +æ¹æ¬¡å·+åºå·" |
| | | value="产åç¼ç +æ¹æ¬¡å·+åºå·"></el-option> |
| | | <el-option label="æ¶é´æ³+éæºæ°" |
| | | value="æ¶é´æ³+éæºæ°"></el-option> |
| | | <el-option label="èªå®ä¹è§å" |
| | | value="èªå®ä¹è§å"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="èªå®ä¹åç¼" |
| | | prop="customPrefix" |
| | | v-if="codeRule === 'èªå®ä¹è§å'"> |
| | | <el-input v-model="customPrefix" |
| | | placeholder="请è¾å
¥èªå®ä¹åç¼"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="generateIdentifiers">ç æ</el-button> |
| | | <el-button @click="generateDialogVisible = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- éæ°åé
å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="reassignDialogVisible" |
| | | title="éæ°åé
æ è¯" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产ååç§°"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ è¯ç "> |
| | | <span>{{ currentProduct.identifierCode }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ°æ¹æ¬¡å·" |
| | | prop="newBatchNo"> |
| | | <el-input v-model="newBatchNo" |
| | | placeholder="请è¾å
¥æ°æ¹æ¬¡å·"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="åé
åå " |
| | | prop="reassignReason"> |
| | | <el-input type="textarea" |
| | | v-model="reassignReason" |
| | | rows="3" |
| | | placeholder="请è¾å
¥éæ°åé
åå "></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="saveReassign">ç¡® å®</el-button> |
| | | <el-button @click="reassignDialogVisible = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- äºç»´ç é¢è§å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="qrCodeDialogVisible" |
| | | title="äºç»´ç é¢è§" |
| | | width="500px" |
| | | center> |
| | | <div class="qr-preview-container"> |
| | | <div v-if="qrCodeUrl" |
| | | class="qr-image-container"> |
| | | <img :src="qrCodeUrl" |
| | | alt="äºç»´ç " |
| | | class="qr-image" /> |
| | | <div class="qr-info"> |
| | | <p><strong>产ååç§°ï¼</strong>{{ currentQRProduct.productName }}</p> |
| | | <p><strong>产åç¼ç ï¼</strong>{{ currentQRProduct.productCode }}</p> |
| | | <p><strong>æ¹æ¬¡å·ï¼</strong>{{ currentQRProduct.batchNo }}</p> |
| | | <p><strong>æ è¯ç ï¼</strong>{{ currentQRProduct.identifierCode }}</p> |
| | | <p><strong>æ è¯ç±»åï¼</strong>{{ currentQRProduct.identifierType }}</p> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | class="qr-loading"> |
| | | <el-icon class="is-loading"> |
| | | <Loading /> |
| | | </el-icon> |
| | | <p>æ£å¨çæäºç»´ç ...</p> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="qrCodeDialogVisible = false">å
³é</el-button> |
| | | <el-button v-if="qrCodeUrl" |
| | | type="primary" |
| | | @click="copyQRContent" |
| | | icon="CopyDocument"> |
| | | å¤å¶å
容 |
| | | </el-button> |
| | | <el-button v-if="qrCodeUrl" |
| | | type="success" |
| | | @click="downloadQRCode" |
| | | icon="Download"> |
| | | ä¸è½½äºç»´ç |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search, Loading, Download } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import QRCode from "qrcode"; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const loading = ref(false); |
| | | const searchForm = reactive({ |
| | | productName: "", |
| | | identifierType: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const identifierList = ref([ |
| | | { |
| | | id: 1, |
| | | productName: "å·¥ä¸ä¼ æå¨Aå", |
| | | productCode: "SENSOR001", |
| | | batchNo: "B202312001", |
| | | identifierType: "äºç»´ç ", |
| | | identifierCode: "QR_SENSOR001_B202312001_001", |
| | | status: "å·²åé
", |
| | | generateTime: "2023-12-01 10:00:00", |
| | | remark: "éè¦äº§åæ è¯", |
| | | }, |
| | | { |
| | | id: 2, |
| | | productName: "æ§å¶é¢æ¿Bå", |
| | | productCode: "PANEL002", |
| | | batchNo: "B202312002", |
| | | identifierType: "é²ä¼ªç ", |
| | | identifierCode: "SEC_PANEL002_B202312002_001", |
| | | status: "å·²çæ", |
| | | generateTime: "2023-12-02 14:30:00", |
| | | remark: "常è§äº§åæ è¯", |
| | | }, |
| | | { |
| | | id: 3, |
| | | productName: "æ°æ®ééå¨Cå", |
| | | productCode: "COLLECTOR003", |
| | | batchNo: "B202312003", |
| | | identifierType: "é²ä¼ªç ", |
| | | identifierCode: "SEC_COLLECTOR003_B202312003_001", |
| | | status: "已使ç¨", |
| | | generateTime: "2023-12-03 09:15:00", |
| | | remark: "æµè¯äº§åæ è¯", |
| | | }, |
| | | ]); |
| | | |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | }); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("æ°å¢æ è¯"); |
| | | const form = reactive({ |
| | | productName: "", |
| | | productCode: "", |
| | | batchNo: "", |
| | | identifierType: "", |
| | | quantity: 1, |
| | | status: "å·²çæ", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | productName: [{ required: true, message: "请è¾å
¥äº§ååç§°", trigger: "blur" }], |
| | | productCode: [{ required: true, message: "请è¾å
¥äº§åç¼ç ", trigger: "blur" }], |
| | | batchNo: [{ required: true, message: "请è¾å
¥æ¹æ¬¡å·", trigger: "blur" }], |
| | | identifierType: [ |
| | | { required: true, message: "è¯·éæ©æ è¯ç±»å", trigger: "change" }, |
| | | ], |
| | | quantity: [{ required: true, message: "请è¾å
¥çææ°é", trigger: "blur" }], |
| | | status: [{ required: true, message: "è¯·éæ©ç¶æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const isEdit = ref(false); |
| | | const editId = ref(null); |
| | | const generateDialogVisible = ref(false); |
| | | const reassignDialogVisible = ref(false); |
| | | const currentProduct = ref({}); |
| | | const generateQuantity = ref(1); |
| | | const codeRule = ref(""); |
| | | const customPrefix = ref(""); |
| | | const newBatchNo = ref(""); |
| | | const reassignReason = ref(""); |
| | | const formRef = ref(); |
| | | |
| | | // äºç»´ç ç¸å
³åé |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | const currentQRProduct = ref({}); |
| | | |
| | | // 计ç®å±æ§ |
| | | const filteredList = computed(() => { |
| | | let list = identifierList.value; |
| | | if (searchForm.productName) { |
| | | list = list.filter(item => |
| | | item.productName.includes(searchForm.productName) |
| | | ); |
| | | } |
| | | if (searchForm.identifierType) { |
| | | list = list.filter( |
| | | item => item.identifierType === searchForm.identifierType |
| | | ); |
| | | } |
| | | if (searchForm.status) { |
| | | list = list.filter(item => item.status === searchForm.status); |
| | | } |
| | | return list; |
| | | }); |
| | | |
| | | // æ¹æ³ |
| | | const getIdentifierTypeType = type => { |
| | | const typeMap = { |
| | | äºç»´ç : "success", |
| | | é²ä¼ªç : "warning", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | å·²çæ: "info", |
| | | å·²åé
: "primary", |
| | | 已使ç¨: "success", |
| | | å·²ä½åº: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // æç´¢é»è¾å·²å¨computedä¸å¤ç |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.productName = ""; |
| | | searchForm.identifierType = ""; |
| | | searchForm.status = ""; |
| | | }; |
| | | |
| | | const handleAdd = () => { |
| | | dialogTitle.value = "æ°å¢æ è¯"; |
| | | isEdit.value = false; |
| | | form.productName = ""; |
| | | form.productCode = ""; |
| | | form.batchNo = ""; |
| | | form.identifierType = ""; |
| | | form.quantity = 1; |
| | | form.status = "å·²çæ"; |
| | | form.remark = ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleView = row => { |
| | | // æ¥çæ è¯è¯¦æ
|
| | | ElMessage.info("æ¥çæ è¯è¯¦æ
åè½å¾
å®ç°"); |
| | | }; |
| | | |
| | | const handleEdit = row => { |
| | | dialogTitle.value = "ç¼è¾æ è¯"; |
| | | isEdit.value = true; |
| | | editId.value = row.id; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleExport = row => { |
| | | // å¯¼åºæ è¯ |
| | | ElMessage.success(`å·²å¯¼åºæ è¯: ${row.identifierCode}`); |
| | | }; |
| | | |
| | | const handleReassign = row => { |
| | | currentProduct.value = row; |
| | | newBatchNo.value = ""; |
| | | reassignReason.value = ""; |
| | | reassignDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥æ è¯åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = identifierList.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | identifierList.value.splice(index, 1); |
| | | pagination.total--; |
| | | ElMessage.success("å 餿å"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // çæäºç»´ç |
| | | const generateQRCode = async row => { |
| | | try { |
| | | // æ£æ¥å¿
è¦å段 |
| | | if (!row.productName || !row.productCode || !row.batchNo) { |
| | | ElMessage.warning("产åä¿¡æ¯ä¸å®æ´ï¼æ æ³çæäºç»´ç "); |
| | | return; |
| | | } |
| | | |
| | | currentQRProduct.value = row; |
| | | qrCodeUrl.value = ""; |
| | | qrCodeDialogVisible.value = true; |
| | | |
| | | // æå»ºäºç»´ç å
容 |
| | | let qrContent = ""; |
| | | if (row.identifierType === "äºç»´ç ") { |
| | | qrContent = `${row.productName}|${row.productCode}|${row.batchNo}|${row.identifierCode}`; |
| | | } else if (row.identifierType === "é²ä¼ªç ") { |
| | | // é²ä¼ªç æ ¼å¼ï¼SEC_产åç¼ç _æ¹æ¬¡å·_æ¶é´æ³_éæºæ° |
| | | const timestamp = Date.now(); |
| | | const random = Math.random().toString(36).substr(2, 8); |
| | | qrContent = `SEC_${row.productCode}_${row.batchNo}_${timestamp}_${random}`; |
| | | } |
| | | |
| | | // çæäºç»´ç |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent, { |
| | | width: 256, |
| | | margin: 2, |
| | | color: { |
| | | dark: "#000000", |
| | | light: "#FFFFFF", |
| | | }, |
| | | errorCorrectionLevel: row.identifierType === "é²ä¼ªç " ? "H" : "M", |
| | | }); |
| | | |
| | | ElMessage.success("äºç»´ç çææåï¼"); |
| | | } catch (error) { |
| | | console.error("çæäºç»´ç 失败:", error); |
| | | ElMessage.error("çæäºç»´ç 失败ï¼" + error.message); |
| | | qrCodeDialogVisible.value = false; |
| | | } |
| | | }; |
| | | |
| | | // ä¸è½½äºç»´ç |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | ElMessage.warning("请å
çæäºç»´ç "); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement("a"); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `${currentQRProduct.value.productName}_${ |
| | | currentQRProduct.value.identifierType |
| | | }_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | ElMessage.success("ä¸è½½æåï¼"); |
| | | }; |
| | | |
| | | // å¤å¶äºç»´ç å
容 |
| | | const copyQRContent = async () => { |
| | | if (!currentQRProduct.value) { |
| | | ElMessage.warning("没æå¯å¤å¶çå
容"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | let content = ""; |
| | | if (currentQRProduct.value.identifierType === "äºç»´ç ") { |
| | | content = `${currentQRProduct.value.productName}|${currentQRProduct.value.productCode}|${currentQRProduct.value.batchNo}|${currentQRProduct.value.identifierCode}`; |
| | | } else if (currentQRProduct.value.identifierType === "é²ä¼ªç ") { |
| | | const timestamp = Date.now(); |
| | | const random = Math.random().toString(36).substr(2, 8); |
| | | content = `SEC_${currentQRProduct.value.productCode}_${currentQRProduct.value.batchNo}_${timestamp}_${random}`; |
| | | } |
| | | |
| | | await navigator.clipboard.writeText(content); |
| | | ElMessage.success("å
容已å¤å¶å°åªè´´æ¿"); |
| | | } catch (error) { |
| | | // éçº§æ¹æ¡ |
| | | const textArea = document.createElement("textarea"); |
| | | textArea.value = content; |
| | | document.body.appendChild(textArea); |
| | | textArea.select(); |
| | | document.execCommand("copy"); |
| | | document.body.removeChild(textArea); |
| | | ElMessage.success("å
容已å¤å¶å°åªè´´æ¿"); |
| | | } |
| | | }; |
| | | |
| | | const generateIdentifiers = () => { |
| | | if (!codeRule.value) { |
| | | ElMessage.warning("è¯·éæ©ç¼ç è§å"); |
| | | return; |
| | | } |
| | | |
| | | // çææ è¯çé»è¾ |
| | | const newIdentifiers = []; |
| | | for (let i = 1; i <= generateQuantity.value; i++) { |
| | | let identifierCode = ""; |
| | | if (codeRule.value === "产åç¼ç +æ¹æ¬¡å·+åºå·") { |
| | | identifierCode = `${currentProduct.value.productCode}_${ |
| | | currentProduct.value.batchNo |
| | | }_${String(i).padStart(3, "0")}`; |
| | | } else if (codeRule.value === "æ¶é´æ³+éæºæ°") { |
| | | identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`; |
| | | } else if (codeRule.value === "èªå®ä¹è§å") { |
| | | identifierCode = `${customPrefix.value || "CUSTOM"}_${Date.now()}_${i}`; |
| | | } |
| | | |
| | | newIdentifiers.push({ |
| | | id: Math.max(...identifierList.value.map(item => item.id)) + i, |
| | | productName: currentProduct.value.productName, |
| | | productCode: currentProduct.value.productCode, |
| | | batchNo: currentProduct.value.batchNo, |
| | | identifierType: currentProduct.value.identifierType, |
| | | identifierCode: identifierCode, |
| | | status: "å·²çæ", |
| | | generateTime: new Date().toLocaleString(), |
| | | remark: "æ¹éçæ", |
| | | }); |
| | | } |
| | | |
| | | identifierList.value.push(...newIdentifiers); |
| | | pagination.total += newIdentifiers.length; |
| | | ElMessage.success(`æåçæ ${newIdentifiers.length} 个æ è¯`); |
| | | generateDialogVisible.value = false; |
| | | }; |
| | | |
| | | const saveReassign = () => { |
| | | if (!newBatchNo.value) { |
| | | ElMessage.warning("请è¾å
¥æ°æ¹æ¬¡å·"); |
| | | return; |
| | | } |
| | | |
| | | const index = identifierList.value.findIndex( |
| | | item => item.id === currentProduct.value.id |
| | | ); |
| | | if (index > -1) { |
| | | identifierList.value[index].batchNo = newBatchNo.value; |
| | | identifierList.value[index].status = "å·²åé
"; |
| | | ElMessage.success("æ è¯éæ°åé
æå"); |
| | | reassignDialogVisible.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | // ç¼è¾ |
| | | const index = identifierList.value.findIndex( |
| | | item => item.id === editId.value |
| | | ); |
| | | if (index > -1) { |
| | | identifierList.value[index] = { ...form, id: editId.value }; |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } |
| | | } else { |
| | | // æ°å¢ |
| | | const newId = |
| | | Math.max(...identifierList.value.map(item => item.id)) + 1; |
| | | |
| | | // æ ¹æ®æ è¯ç±»åçæä¸åçæ è¯ç |
| | | let identifierCode = ""; |
| | | if (form.identifierType === "äºç»´ç ") { |
| | | identifierCode = `QR_${form.productCode}_${form.batchNo}_001`; |
| | | } else if (form.identifierType === "é²ä¼ªç ") { |
| | | identifierCode = `SEC_${form.productCode}_${form.batchNo}_001`; |
| | | } |
| | | |
| | | identifierList.value.push({ |
| | | ...form, |
| | | id: newId, |
| | | identifierCode: identifierCode, |
| | | generateTime: new Date().toLocaleString(), |
| | | }); |
| | | pagination.total++; |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = val => { |
| | | pagination.currentPage = val.page; |
| | | pagination.pageSize = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .search-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .quick-actions-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .quick-actions-row .el-alert { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .quick-actions-row .el-alert p { |
| | | margin: 5px 0; |
| | | font-size: 14px; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | /* äºç»´ç é¢è§æ ·å¼ */ |
| | | .qr-preview-container { |
| | | text-align: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .qr-image-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .qr-image { |
| | | max-width: 100%; |
| | | height: auto; |
| | | border: 2px solid #e0e0e0; |
| | | border-radius: 8px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .qr-info { |
| | | text-align: left; |
| | | background: #f8f9fa; |
| | | padding: 15px; |
| | | border-radius: 8px; |
| | | min-width: 300px; |
| | | } |
| | | |
| | | .qr-info p { |
| | | margin: 8px 0; |
| | | color: #666; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .qr-loading { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 15px; |
| | | padding: 40px 0; |
| | | } |
| | | |
| | | .qr-loading .el-icon { |
| | | font-size: 32px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .qr-loading p { |
| | | color: #666; |
| | | margin: 0; |
| | | } |
| | | </style> |