物品领用生成二维码

This commit is contained in:
浅念
2026-03-13 16:50:21 +08:00
parent ce0e9cd377
commit 786082305d
2 changed files with 663 additions and 22 deletions
+47
View File
@@ -72,4 +72,51 @@ export class invitemService {
params
})
}
// 生成二维码
static getQrCode(params: { id: number; qr_claim_limit?: number }) {
return request.post<Api.Common.AddResponse>({
url: '/admin/inv/item/qr-code',
params
})
}
// 删除二维码
static deleteQrCode(params: { id: number }) {
return request.post<Api.Common.AddResponse>({
url: '/admin/inv/item/qr-code/delete',
params
})
}
// 管理员带领
static proxyClaim(params: any) {
return request.post<Api.Common.AddResponse>({
url: '/admin/requisition/proxy-claim',
params
})
}
// 获取库存变化记录
static getStockLogList(params: {
item_id: number
current?: number
size?: number
change_type?: number
}) {
return request.get<Api.Common.CommonListData>({
url: '/admin/stock-log/list',
params
})
}
// 获取扫码记录
static getQrClaimsList(params: {
item_id: number
current?: number
size?: number
claim_typeid?: number
start_time?: string
end_time?: string
}) {
return request.get<Api.Common.CommonListData>({
url: '/admin/requisition/qr-claims',
params
})
}
}
+616 -22
View File
@@ -33,13 +33,26 @@
<ElTableColumn label="名称" prop="item_name" />
<ElTableColumn label="物品管理员" prop="manage_user_name" />
<ElTableColumn label="备注" prop="remark" />
<!-- <ElTableColumn label="是否耗材" prop="is_consumable" width="180" >
<ElTableColumn label="库存数量" prop="stock_qty" />
<ElTableColumn label="扫码领取" prop="allow_qr_claim">
<template #default="scope">
<ElTag :type="scope.row.allow_qr_claim ? 'primary' : 'info'">
{{ scope.row.allow_qr_claim ? '是' : '否' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="领取限制数量" prop="qr_code_num">
<template #default="scope">
{{ scope.row.qr_code_num === 0 ? '不限制' : scope.row.qr_code_num }}
</template>
</ElTableColumn>
<ElTableColumn label="是否耗材" prop="is_consumable" width="180">
<template #default="scope">
<ElTag :type="scope.row.is_consumable ? 'primary' : 'info'">
{{ scope.row.is_consumable ? '是' : '否' }}
</ElTag>
</template>
</ElTableColumn> -->
</ElTableColumn>
<ElTableColumn label="状态" prop="status">
<template #default="scope">
<ElTag :type="scope.row.status ? 'primary' : 'info'">
@@ -48,25 +61,47 @@
</template>
</ElTableColumn>
<ElTableColumn label="创建时间" prop="created_at" />
<ElTableColumn fixed="right" label="操作">
<ElTableColumn fixed="right" label="操作" width="160" align="center">
<template #default="scope">
<ElRow>
<el-button
link
@click="showDialog('edit', scope.row)"
v-if="hasAuth('item_list@edit')"
>
编辑
</el-button>
<el-button
type="danger"
link
@click="deleteRole(scope.row)"
v-if="hasAuth('item_list@del')"
>
删除
</el-button>
</ElRow>
<ElButton link type="primary" @click="showDialog('edit', scope.row)" v-if="hasAuth('item_list@edit')">
编辑
</ElButton>
<ElDropdown trigger="click">
<ElButton link type="primary">
更多
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</ElButton>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem @click="generateQrCode(scope.row)">
<el-icon><Document /></el-icon>
{{ scope.row.qr_code_pull ? '查看二维码' : '生成二维码' }}
</ElDropdownItem>
<ElDropdownItem @click="showProxyClaimDialog(scope.row)">
<el-icon><User /></el-icon>
管理员代领
</ElDropdownItem>
<ElDropdownItem @click="showStockLogDialog(scope.row)">
<el-icon><List /></el-icon>
库存记录
</ElDropdownItem>
<ElDropdownItem @click="showQrClaimsDialog(scope.row)">
<el-icon><Tickets /></el-icon>
领取记录
</ElDropdownItem>
<ElDropdownItem v-if="scope.row.qr_code_pull" @click="deleteQrCode(scope.row)">
<el-icon><Delete /></el-icon>
删除二维码
</ElDropdownItem>
<ElDropdownItem divided @click="deleteRole(scope.row)" v-if="hasAuth('item_list@del')">
<el-icon style="color: #f56c6c"><Delete /></el-icon>
<span style="color: #f56c6c">删除物品</span>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</template>
</ElTableColumn>
</template>
@@ -119,15 +154,40 @@
<ElFormItem label="计量单位" prop="unit">
<ElInput v-model="form.unit" placeholder="请输入计量单位" />
</ElFormItem> -->
<!-- <ElFormItem label="是否耗材" prop="is_consumable">
<ElFormItem label="是否耗材" prop="is_consumable">
<el-radio-group v-model="form.is_consumable">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</ElFormItem> -->
</ElFormItem>
<ElFormItem label="备注" prop="remark">
<ElInput v-model="form.remark" placeholder="请输入备注" />
</ElFormItem>
<ElFormItem label="库存数量" prop="stock_qty">
<ElInputNumber
v-model="form.stock_qty"
:min="0"
placeholder="请输入库存数量"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="扫码领取" prop="allow_qr_claim">
<el-radio-group v-model="form.allow_qr_claim">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</ElFormItem>
<ElFormItem v-if="form.allow_qr_claim === 1" label="单人领取限制" prop="qr_code_num">
<ElInputNumber
v-model="form.qr_code_num"
:min="0"
placeholder="0为不限制"
style="width: 100%"
/>
<span style="margin-left: 8px; color: var(--el-text-color-secondary); font-size: 12px">
单人扫码领取数量限制,0为不限制
</span>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElSwitch v-model="form.status" :active-value="1" :inactive-value="0" />
</ElFormItem>
@@ -139,11 +199,212 @@
</div>
</template>
</ElDialog>
<!-- 二维码弹窗 -->
<ElDialog v-model="qrCodeDialogVisible" title="物品二维码" width="400px" align-center>
<div class="qr-code-container">
<div v-if="qrCodeData" class="qr-code-wrapper">
<img :src="qrCodeData" alt="二维码" class="qr-code-image" />
<p class="qr-code-desc">扫描二维码领取物品</p>
</div>
<div v-else-if="qrCodeLoading" class="qr-code-loading">
<el-icon class="is-loading" :size="40">
<Loading />
</el-icon>
<p>生成中...</p>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="closeQrCodeDialog">{{ qrCodeData ? '关闭' : '取消' }}</ElButton>
<ElButton v-if="qrCodeData" type="primary" @click="downloadQrCode">下载二维码</ElButton>
</div>
</template>
</ElDialog>
<!-- 管理员代领弹窗 -->
<ElDialog v-model="proxyClaimDialogVisible" title="管理员代领" width="500px" align-center>
<ElForm ref="proxyClaimFormRef" :model="proxyClaimForm" label-width="100px">
<ElFormItem label="选择用户" prop="target_user_ids">
<ElSelect
v-model="proxyClaimForm.target_user_ids"
placeholder="请选择用户"
filterable
multiple
style="width: 100%"
>
<ElOption v-for="item in userList" :key="item.id" :label="item.name" :value="item.id" />
</ElSelect>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="proxyClaimDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleProxyClaimSubmit">确定</ElButton>
</div>
</template>
</ElDialog>
<!-- 库存记录弹窗 -->
<ElDialog v-model="stockLogDialogVisible" title="库存变化记录" width="80%" align-center>
<!-- 搜索表单 -->
<ElForm :model="stockLogSearchForm" inline style="margin-bottom: 16px">
<ElFormItem label="变动类型">
<ElSelect
v-model="stockLogSearchForm.change_type"
placeholder="请选择变动类型"
clearable
style="width: 150px"
>
<ElOption label="入库" :value="1" />
<ElOption label="出库(领用)" :value="2" />
<ElOption label="出库(损耗)" :value="3" />
<ElOption label="入库(归还)" :value="4" />
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="searchStockLog">查询</ElButton>
<ElButton @click="resetStockLogSearch">重置</ElButton>
</ElFormItem>
</ElForm>
<ElTable :data="stockLogList" :loading="stockLogLoading" max-height="400">
<ElTableColumn label="物品名称" prop="item_name" />
<ElTableColumn label="变化类型" prop="change_type_text" />
<ElTableColumn label="变化数量" prop="change_qty">
<template #default="scope">
<span :style="{ color: scope.row.change_qty > 0 ? '#67C23A' : '#F56C6C' }">
{{ scope.row.change_qty > 0 ? '+' : '' }}{{ scope.row.change_qty }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="变化前" prop="before_qty"/>
<ElTableColumn label="变化后" prop="after_qty" />
<ElTableColumn label="业务类型" prop="biz_type_text" />
<ElTableColumn label="操作人" prop="operator_name" />
<ElTableColumn label="备注" prop="remark" min-width="120" />
<ElTableColumn label="创建时间" prop="created_at"/>
</ElTable>
<!-- 分页 -->
<div style="margin-top: 16px; display: flex; justify-content: flex-end">
<ElPagination
v-model:current-page="stockLogPagination.currentPage"
v-model:page-size="stockLogPagination.pageSize"
:total="stockLogPagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleStockLogSizeChange"
@current-change="handleStockLogCurrentChange"
/>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="stockLogDialogVisible = false">关闭</ElButton>
</div>
</template>
</ElDialog>
<!-- 领取记录弹窗 -->
<ElDialog v-model="qrClaimsDialogVisible" title="领取记录" width="80%" align-center>
<!-- 搜索表单 -->
<ElForm :model="qrClaimsSearchForm" inline style="margin-bottom: 16px">
<ElFormItem label="领取方式">
<ElSelect
v-model="qrClaimsSearchForm.claim_type"
placeholder="请选择领取方式"
clearable
style="width: 150px"
>
<ElOption label="扫码领取" :value="2" />
<ElOption label="管理员代领" :value="3" />
</ElSelect>
</ElFormItem>
<ElFormItem label="开始时间">
<ElDatePicker
v-model="qrClaimsSearchForm.start_time"
type="datetime"
placeholder="请选择开始时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</ElFormItem>
<ElFormItem label="结束时间">
<ElDatePicker
v-model="qrClaimsSearchForm.end_time"
type="datetime"
placeholder="请选择结束时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="searchQrClaims">查询</ElButton>
<ElButton @click="resetQrClaimsSearch">重置</ElButton>
</ElFormItem>
</ElForm>
<ElTable :data="qrClaimsList" :loading="qrClaimsLoading" max-height="400">
<ElTableColumn label="物品">
<template #default="scope">
<span v-if="scope.row.items && scope.row.items.length > 0">
<template v-for="(item, index) in scope.row.items" :key="index">
{{ item.item_name_snap }}<span v-if="Number(index) < scope.row.items.length - 1">, </span>
</template>
</span>
<span v-else>-</span>
</template>
</ElTableColumn>
<ElTableColumn label="数量">
<template #default="scope">
<span v-if="scope.row.items && scope.row.items.length > 0">
<template v-for="(item, index) in scope.row.items" :key="index">
{{ item.approved_qty }}<span v-if="Number(index) < scope.row.items.length - 1">, </span>
</template>
</span>
<span v-else>-</span>
</template>
</ElTableColumn>
<ElTableColumn label="领取人" prop="applicant_name" />
<ElTableColumn label="领取类型" prop="claim_type_text" />
<ElTableColumn label="领取时间" prop="created_at" />
<ElTableColumn label="代领人" prop="proxy_claim_user_name">
<template #default="scope">
{{ scope.row.proxy_claim_user_name || '-' }}
</template>
</ElTableColumn>
<ElTableColumn label="领取方式" prop="borrow_reason" />
<ElTableColumn label="备注" prop="remark" />
</ElTable>
<!-- 分页 -->
<div style="margin-top: 16px; display: flex; justify-content: flex-end">
<ElPagination
v-model:current-page="qrClaimsPagination.currentPage"
v-model:page-size="qrClaimsPagination.pageSize"
:total="qrClaimsPagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleQrClaimsSizeChange"
@current-change="handleQrClaimsCurrentChange"
/>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="qrClaimsDialogVisible = false">关闭</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import { Loading, ArrowDown, Document, User, List, Tickets, Delete } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import { invitemService } from '@/api/admin/Itemrequisition'
import { manageService } from '@/api/adminApi'
@@ -154,6 +415,37 @@
const keyword = ref<string>('')
const loading = ref(false)
const dialogVisible = ref(false)
const qrCodeDialogVisible = ref(false)
const qrCodeData = ref('')
const qrCodeLoading = ref(false)
const proxyClaimDialogVisible = ref(false)
const stockLogDialogVisible = ref(false)
const stockLogList = ref<any[]>([])
const stockLogLoading = ref(false)
const currentStockLogItemId = ref<number | null>(null)
const stockLogSearchForm = reactive({
change_type: undefined as number | undefined
})
const stockLogPagination = reactive({
currentPage: 1,
pageSize: 10,
total: 0
})
const qrClaimsDialogVisible = ref(false)
const qrClaimsList = ref<any[]>([])
const qrClaimsLoading = ref(false)
const currentQrClaimsItemId = ref<number | null>(null)
const qrClaimsSearchForm = reactive({
claim_type: undefined as number | undefined,
start_time: '',
end_time: ''
})
const qrClaimsPagination = reactive({
currentPage: 1,
pageSize: 10,
total: 0
})
const proxyClaimFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
const rules = reactive<FormRules>({
company_id: [{ required: true, message: '请选择公司', trigger: 'change' }],
@@ -166,6 +458,10 @@
manage_user_id: '',
remark: '',
item_name: '',
stock_qty: 0,
allow_qr_claim: 0,
is_consumable: 0,
qr_code_num: 0,
status: 1,
id: ''
})
@@ -186,6 +482,22 @@
const response = await manageService.presetusers()
manageUserList.value = response || []
}
// 管理员代领相关
const proxyClaimForm = reactive({
item_id: '',
target_user_ids: [] as number[]
})
const userList = ref<any[]>([])
// 获取所有用户列表
const getAllUserList = async () => {
try {
const response = await manageService.presetusers()
userList.value = response || []
} catch (error) {
console.error('获取用户列表失败:', error)
}
}
// 表格数据
const tableData = ref<any[]>([])
@@ -196,6 +508,7 @@
getWorkList()
getcompanyList()
getCompanyUserList()
getAllUserList()
})
watch([keyword], () => {
pagination.currentPage = 1
@@ -247,6 +560,10 @@
item_name: row.item_name,
manage_user_id: row.manage_user_id,
remark: row.remark,
stock_qty: row.stock_qty ?? 0,
is_consumable: row.is_consumable ?? 0,
allow_qr_claim: row.allow_qr_claim ?? 0,
qr_code_num: row.qr_code_num ?? 0,
status: row.status,
id: row.id
})
@@ -257,6 +574,10 @@
item_name: '',
manage_user_id: '',
remark: '',
stock_qty: 0,
is_consumable: 0,
allow_qr_claim: 0,
qr_code_num: 0,
status: 1,
id: ''
})
@@ -276,6 +597,21 @@
})
}
// 删除二维码
const deleteQrCode = (row: any) => {
ElMessageBox.confirm('确定删除该物品的二维码吗?', '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
const { message } = await invitemService.deleteQrCode({
id: row.id
})
ElMessage.success(message)
getWorkList()
})
}
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
@@ -286,6 +622,10 @@
item_name: form.item_name,
manage_user_id: form.manage_user_id,
remark: form.remark,
stock_qty: form.stock_qty,
is_consumable: form.is_consumable,
allow_qr_claim: form.allow_qr_claim,
qr_code_num: form.qr_code_num || 0,
status: form.status
})
ElMessage.success(message)
@@ -296,6 +636,10 @@
item_name: form.item_name,
manage_user_id: form.manage_user_id,
remark: form.remark,
stock_qty: form.stock_qty,
is_consumable: form.is_consumable,
allow_qr_claim: form.allow_qr_claim,
qr_code_num: form.qr_code_num || 0,
status: form.status
})
ElMessage.success(message)
@@ -305,6 +649,224 @@
}
})
}
// 生成/查看二维码
const generateQrCode = async (row: any) => {
qrCodeDialogVisible.value = true
// 如果已有二维码URL,直接使用
if (row.qr_code_pull) {
qrCodeData.value = row.qr_code_pull
qrCodeLoading.value = false
return
}
// 没有二维码URL,调用接口生成
qrCodeLoading.value = true
qrCodeData.value = ''
try {
const response = await invitemService.getQrCode({ id: row.id })
qrCodeData.value = (response as any)?.data?.qr_code_url || ''
ElMessage.success('二维码生成成功')
// 刷新列表以更新 qr_code_pull
getWorkList()
} catch (error) {
console.error('生成二维码失败:', error)
ElMessage.error('生成二维码失败')
} finally {
qrCodeLoading.value = false
}
}
// 关闭二维码弹窗
const closeQrCodeDialog = () => {
qrCodeDialogVisible.value = false
qrCodeData.value = ''
qrCodeLoading.value = false
}
// 下载二维码
const downloadQrCode = () => {
if (!qrCodeData.value) {
ElMessage.warning('暂无二维码可下载')
return
}
const link = document.createElement('a')
link.href = qrCodeData.value
link.download = `item-qr-code-${Date.now()}.png`
link.click()
ElMessage.success('二维码下载成功')
}
// 显示管理员代领弹窗
const showProxyClaimDialog = (row: any) => {
proxyClaimDialogVisible.value = true
proxyClaimForm.item_id = row.id
proxyClaimForm.target_user_ids = []
}
// 提交管理员代领
const handleProxyClaimSubmit = async () => {
if (!proxyClaimForm.target_user_ids || proxyClaimForm.target_user_ids.length === 0) {
ElMessage.warning('请选择用户')
return
}
try {
const { message } = await invitemService.proxyClaim({
item_id: proxyClaimForm.item_id,
target_user_id: proxyClaimForm.target_user_ids.join(',')
})
ElMessage.success(message)
proxyClaimDialogVisible.value = false
getWorkList()
} catch (error) {
console.error('管理员代领失败:', error)
ElMessage.error('操作失败')
}
}
// 显示库存记录弹窗
const showStockLogDialog = async (row: any) => {
currentStockLogItemId.value = row.id
stockLogDialogVisible.value = true
// 重置搜索条件和分页
stockLogSearchForm.change_type = undefined
stockLogPagination.currentPage = 1
stockLogPagination.pageSize = 10
await getStockLogList()
}
// 获取库存记录列表
const getStockLogList = async () => {
if (!currentStockLogItemId.value) return
stockLogLoading.value = true
stockLogList.value = []
try {
const params: any = {
item_id: currentStockLogItemId.value,
current: stockLogPagination.currentPage,
size: stockLogPagination.pageSize
}
if (stockLogSearchForm.change_type) {
params.change_type = stockLogSearchForm.change_type
}
const response = await invitemService.getStockLogList(params)
const records = (response as any)?.data || (response as any)?.records || []
stockLogList.value = Array.isArray(records) ? records : []
stockLogPagination.total = (response as any)?.total || 0
} catch (error) {
console.error('获取库存记录失败:', error)
ElMessage.error('获取库存记录失败')
} finally {
stockLogLoading.value = false
}
}
// 搜索库存记录
const searchStockLog = () => {
stockLogPagination.currentPage = 1
getStockLogList()
}
// 重置搜索条件
const resetStockLogSearch = () => {
stockLogSearchForm.change_type = undefined
stockLogPagination.currentPage = 1
getStockLogList()
}
// 分页大小变化
const handleStockLogSizeChange = (newPageSize: number) => {
stockLogPagination.pageSize = newPageSize
getStockLogList()
}
// 当前页变化
const handleStockLogCurrentChange = (newCurrentPage: number) => {
stockLogPagination.currentPage = newCurrentPage
getStockLogList()
}
// 显示扫码记录弹窗
const showQrClaimsDialog = async (row: any) => {
currentQrClaimsItemId.value = row.id
qrClaimsDialogVisible.value = true
// 重置搜索条件和分页
qrClaimsSearchForm.claim_type = undefined
qrClaimsSearchForm.start_time = ''
qrClaimsSearchForm.end_time = ''
qrClaimsPagination.currentPage = 1
qrClaimsPagination.pageSize = 10
await getQrClaimsList()
}
// 获取扫码记录列表
const getQrClaimsList = async () => {
if (!currentQrClaimsItemId.value) return
qrClaimsLoading.value = true
qrClaimsList.value = []
try {
const params: any = {
item_id: currentQrClaimsItemId.value,
current: qrClaimsPagination.currentPage,
size: qrClaimsPagination.pageSize
}
if (qrClaimsSearchForm.claim_type) {
params.claim_type = qrClaimsSearchForm.claim_type
}
if (qrClaimsSearchForm.start_time) {
params.start_time = qrClaimsSearchForm.start_time
}
if (qrClaimsSearchForm.end_time) {
params.end_time = qrClaimsSearchForm.end_time
}
const response = await invitemService.getQrClaimsList(params)
const records = (response as any)?.data || (response as any)?.records || []
qrClaimsList.value = Array.isArray(records) ? records : []
qrClaimsPagination.total = (response as any)?.total || 0
} catch (error) {
console.error('获取扫码记录失败:', error)
ElMessage.error('获取扫码记录失败')
} finally {
qrClaimsLoading.value = false
}
}
// 搜索扫码记录
const searchQrClaims = () => {
qrClaimsPagination.currentPage = 1
getQrClaimsList()
}
// 重置搜索条件
const resetQrClaimsSearch = () => {
qrClaimsSearchForm.claim_type = undefined
qrClaimsSearchForm.start_time = ''
qrClaimsSearchForm.end_time = ''
qrClaimsPagination.currentPage = 1
getQrClaimsList()
}
// 分页大小变化
const handleQrClaimsSizeChange = (newPageSize: number) => {
qrClaimsPagination.pageSize = newPageSize
getQrClaimsList()
}
// 当前页变化
const handleQrClaimsCurrentChange = (newCurrentPage: number) => {
qrClaimsPagination.currentPage = newCurrentPage
getQrClaimsList()
}
</script>
<style lang="scss" scoped>
@@ -332,4 +894,36 @@
display: flex;
gap: 12px;
}
.qr-code-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
}
.qr-code-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.qr-code-image {
width: 280px;
height: 280px;
object-fit: contain;
}
.qr-code-desc {
margin: 0;
font-size: 14px;
color: var(--el-text-color-secondary);
}
.qr-code-loading {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
color: var(--el-text-color-secondary);
p {
margin: 0;
}
}
</style>