| | |
| | | <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-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"> |
| | | <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"> |
| | | <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"> |
| | | <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> |
| | | <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" |
| | | /> |
| | | <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-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 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 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 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-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 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-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 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> |
| | | <el-button type="primary" @click="handleSubmit">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 标识生成对话框 --> |
| | | <el-dialog v-model="generateDialogVisible" title="标识生成" width="500px"> |
| | | <el-dialog v-model="generateDialogVisible" |
| | | title="标识生成" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品名称"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | |
| | | <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 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-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 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> |
| | | <el-button type="primary" @click="generateIdentifiers">生 成</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 重新分配对话框 --> |
| | | <el-dialog v-model="reassignDialogVisible" title="重新分配标识" width="500px"> |
| | | <el-dialog v-model="reassignDialogVisible" |
| | | title="重新分配标识" |
| | | width="500px"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品名称"> |
| | | <span>{{ currentProduct.productName }}</span> |
| | |
| | | <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 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 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> |
| | | <el-button type="primary" @click="saveReassign">确 定</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 二维码预览对话框 --> |
| | | <el-dialog v-model="qrCodeDialogVisible" title="二维码预览" width="500px" center> |
| | | <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 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.identifierType }}</p> |
| | | </div> |
| | | </div> |
| | | <div v-else class="qr-loading"> |
| | | <el-icon class="is-loading"><Loading /></el-icon> |
| | | <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 v-if="qrCodeUrl" |
| | | type="primary" |
| | | @click="copyQRContent" |
| | | icon="CopyDocument"> |
| | | 复制内容 |
| | | </el-button> |
| | | <el-button |
| | | v-if="qrCodeUrl" |
| | | type="success" |
| | | @click="downloadQRCode" |
| | | icon="Download" |
| | | > |
| | | <el-button v-if="qrCodeUrl" |
| | | type="success" |
| | | @click="downloadQRCode" |
| | | icon="Download"> |
| | | 下载二维码 |
| | | </el-button> |
| | | </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' |
| | | 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 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 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 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 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 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 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 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 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 getIdentifierTypeType = type => { |
| | | const typeMap = { |
| | | 二维码: "success", |
| | | 防伪码: "warning", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '已生成': 'info', |
| | | '已分配': 'primary', |
| | | '已使用': 'success', |
| | | '已作废': 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | 已生成: "info", |
| | | 已分配: "primary", |
| | | 已使用: "success", |
| | | 已作废: "danger", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | } |
| | | const handleSearch = () => { |
| | | // 搜索逻辑已在computed中处理 |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.productName = '' |
| | | searchForm.identifierType = '' |
| | | searchForm.status = '' |
| | | } |
| | | 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 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 handleView = row => { |
| | | // 查看标识详情 |
| | | ElMessage.info("查看标识详情功能待实现"); |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = '编辑标识' |
| | | isEdit.value = true |
| | | editId.value = row.id |
| | | Object.assign(form, row) |
| | | dialogVisible.value = true |
| | | } |
| | | 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 handleExport = row => { |
| | | // 导出标识 |
| | | ElMessage.success(`已导出标识: ${row.identifierCode}`); |
| | | }; |
| | | |
| | | const handleReassign = (row) => { |
| | | currentProduct.value = row |
| | | newBatchNo.value = '' |
| | | reassignReason.value = '' |
| | | reassignDialogVisible.value = true |
| | | } |
| | | 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) |
| | | 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.splice(index, 1) |
| | | pagination.total-- |
| | | ElMessage.success('删除成功') |
| | | identifierList.value[index].batchNo = newBatchNo.value; |
| | | identifierList.value[index].status = "已分配"; |
| | | ElMessage.success("标识重新分配成功"); |
| | | reassignDialogVisible.value = false; |
| | | } |
| | | }) |
| | | } |
| | | }; |
| | | |
| | | // 生成二维码 |
| | | 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 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; |
| | | |
| | | // 下载二维码 |
| | | 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('下载成功!') |
| | | } |
| | | // 根据标识类型生成不同的标识码 |
| | | let identifierCode = ""; |
| | | if (form.identifierType === "二维码") { |
| | | identifierCode = `QR_${form.productCode}_${form.batchNo}_001`; |
| | | } else if (form.identifierType === "防伪码") { |
| | | identifierCode = `SEC_${form.productCode}_${form.batchNo}_001`; |
| | | } |
| | | |
| | | // 复制二维码内容 |
| | | 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('编辑成功') |
| | | identifierList.value.push({ |
| | | ...form, |
| | | id: newId, |
| | | identifierCode: identifierCode, |
| | | generateTime: new Date().toLocaleString(), |
| | | }); |
| | | pagination.total++; |
| | | 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 |
| | | } |
| | | }) |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | pagination.currentPage = val.page |
| | | pagination.pageSize = val.limit |
| | | } |
| | | const handleCurrentChange = val => { |
| | | pagination.currentPage = val.page; |
| | | pagination.pageSize = val.limit; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .search-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | .search-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .quick-actions-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | .quick-actions-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .quick-actions-row .el-alert { |
| | | margin-bottom: 0; |
| | | } |
| | | .quick-actions-row .el-alert { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .quick-actions-row .el-alert p { |
| | | margin: 5px 0; |
| | | font-size: 14px; |
| | | line-height: 1.5; |
| | | } |
| | | .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-preview-container { |
| | | text-align: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .qr-image-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 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-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 { |
| | | 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-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 { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 15px; |
| | | padding: 40px 0; |
| | | } |
| | | |
| | | .qr-loading .el-icon { |
| | | font-size: 32px; |
| | | color: #409EFF; |
| | | } |
| | | .qr-loading .el-icon { |
| | | font-size: 32px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .qr-loading p { |
| | | color: #666; |
| | | margin: 0; |
| | | } |
| | | .qr-loading p { |
| | | color: #666; |
| | | margin: 0; |
| | | } |
| | | </style> |