修复
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m35s

This commit is contained in:
sexygoat
2026-03-31 15:13:19 +08:00
parent 104d62b17d
commit 3372be99b8
9 changed files with 383 additions and 218 deletions

View File

@@ -24,6 +24,7 @@
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
@@ -73,45 +74,55 @@
<ElDialog
v-model="allocateDialogVisible"
:title="$t('enterpriseDevices.dialog.allocateTitle')"
width="700px"
width="75%"
@close="handleAllocateDialogClose"
>
<ElForm
ref="allocateFormRef"
:model="allocateForm"
:rules="allocateRules"
label-width="120px"
<!-- 搜索过滤条件 -->
<ArtSearchBar
v-model:filter="deviceSearchForm"
:items="deviceSearchFormItems"
label-width="85"
:show-expand="false"
@reset="handleDeviceSearchReset"
@search="handleDeviceSearch"
></ArtSearchBar>
<!-- 设备列表 -->
<div class="device-selection-info">
已选择 {{ selectedAvailableDevices.length }} 台设备
</div>
<ArtTable
ref="availableDevicesTableRef"
row-key="id"
:loading="availableDevicesLoading"
:data="availableDevicesList"
:currentPage="devicePagination.page"
:pageSize="devicePagination.pageSize"
:total="devicePagination.total"
:marginTop="10"
@size-change="handleDevicePageSizeChange"
@current-change="handleDevicePageChange"
@selection-change="handleAvailableDevicesSelectionChange"
>
<ElFormItem :label="$t('enterpriseDevices.form.deviceNos')" prop="virtual_nos">
<ElInput
v-model="deviceNosText"
type="textarea"
:rows="6"
:placeholder="$t('enterpriseDevices.form.deviceNosPlaceholder')"
@input="handleDeviceNosChange"
<template #default>
<ElTableColumn type="selection" width="55" :selectable="checkDeviceSelectable" />
<ElTableColumn
v-for="col in availableDeviceColumns"
:key="col.prop || col.type"
v-bind="col"
/>
<div style="margin-top: 4px; font-size: 12px; color: var(--el-color-info)">
{{
$t('enterpriseDevices.form.selectedCount', {
count: allocateForm.virtual_nos?.length || 0
})
}}
</div>
</ElFormItem>
<ElFormItem :label="$t('enterpriseDevices.form.remark')">
<ElInput
v-model="allocateForm.remark"
type="textarea"
:rows="3"
:placeholder="$t('enterpriseDevices.form.remarkPlaceholder')"
/>
</ElFormItem>
</ElForm>
</template>
</ArtTable>
<template #footer>
<div class="dialog-footer">
<ElButton @click="allocateDialogVisible = false">{{ $t('common.cancel') }}</ElButton>
<ElButton type="primary" @click="handleAllocate" :loading="allocateLoading">
<ElButton
type="primary"
@click="handleAllocate"
:loading="allocateLoading"
:disabled="selectedAvailableDevices.length === 0"
>
{{ $t('common.confirm') }}
</ElButton>
</div>
@@ -225,7 +236,7 @@
import { h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { EnterpriseService } from '@/api/modules'
import { EnterpriseService, DeviceService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { SearchFormItem } from '@/types'
@@ -239,6 +250,7 @@
FailedDeviceItem
} from '@/types/api/enterpriseDevice'
import type { EnterpriseItem } from '@/types/api'
import type { Device, DeviceStatus } from '@/types/api/device'
defineOptions({ name: 'EnterpriseDevices' })
@@ -252,12 +264,10 @@
const recallLoading = ref(false)
const resultDialogVisible = ref(false)
const tableRef = ref()
const allocateFormRef = ref<FormInstance>()
const recallFormRef = ref<FormInstance>()
const selectedDevices = ref<EnterpriseDeviceItem[]>([])
const enterpriseId = ref<number>(0)
const enterpriseInfo = ref<EnterpriseItem | null>(null)
const deviceNosText = ref('')
const operationResult = ref<AllocateDevicesResponse | RecallDevicesResponse>({
success_count: 0,
fail_count: 0,
@@ -265,6 +275,28 @@
authorized_devices: null
})
// 可用设备列表相关
const availableDevicesTableRef = ref()
const availableDevicesLoading = ref(false)
const availableDevicesList = ref<Device[]>([])
const selectedAvailableDevices = ref<Device[]>([])
const allocatedDeviceVirtualNos = ref<Set<string>>(new Set())
// 设备搜索表单初始值
const initialDeviceSearchState = {
status: undefined,
virtual_no: '',
device_name: '',
shop_id: undefined
}
const deviceSearchForm = reactive({ ...initialDeviceSearchState })
const devicePagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 搜索表单初始值
const initialSearchState = {
virtual_no: ''
@@ -273,31 +305,6 @@
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 授权表单
const allocateForm = reactive({
virtual_nos: [] as string[],
remark: ''
})
// 授权表单验证规则
const allocateRules = reactive<FormRules>({
virtual_nos: [
{
required: true,
validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error(t('enterpriseDevices.validation.deviceNosRequired')))
} else if (value.length > 100) {
callback(new Error(t('enterpriseDevices.validation.deviceNosMaxLength')))
} else {
callback()
}
},
trigger: 'change'
}
]
})
// 撤销表单
const recallForm = reactive({})
@@ -324,6 +331,43 @@
}
]
// 设备列表搜索表单配置
const deviceSearchFormItems: SearchFormItem[] = [
{
label: '设备状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '在库', value: 1 },
{ label: '已分销', value: 2 },
{ label: '已激活', value: 3 },
{ label: '已停用', value: 4 }
]
},
{
label: '设备号',
prop: 'virtual_no',
type: 'input',
config: {
clearable: true,
placeholder: '请输入设备号'
}
},
{
label: '设备名称',
prop: 'device_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入设备名称'
}
}
]
// 列配置
const columnOptions = [
{ label: t('enterpriseDevices.table.deviceId'), prop: 'device_id' },
@@ -336,6 +380,38 @@
const deviceList = ref<EnterpriseDeviceItem[]>([])
// 获取设备状态标签类型
const getDeviceStatusTag = (status: DeviceStatus) => {
switch (status) {
case 1:
return 'info'
case 2:
return 'warning'
case 3:
return 'success'
case 4:
return 'danger'
default:
return 'info'
}
}
// 获取设备状态文本
const getDeviceStatusText = (status: DeviceStatus) => {
switch (status) {
case 1:
return '在库'
case 2:
return '已分销'
case 3:
return '已激活'
case 4:
return '已停用'
default:
return '未知'
}
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
@@ -372,6 +448,45 @@
}
])
// 可用设备列表列配置
const availableDeviceColumns = computed(() => [
{
prop: 'virtual_no',
label: '设备号',
minWidth: 150
},
{
prop: 'device_name',
label: '设备名称',
minWidth: 150
},
{
prop: 'device_model',
label: '设备型号',
width: 120
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: Device) => {
return h(ElTag, { type: getDeviceStatusTag(row.status) }, () =>
getDeviceStatusText(row.status)
)
}
},
{
prop: 'shop_name',
label: '所属店铺',
width: 150
},
{
prop: 'bound_card_count',
label: '绑定卡数',
width: 100
}
])
onMounted(() => {
const id = route.query.id
if (id) {
@@ -469,86 +584,130 @@
router.back()
}
// 显示授权对话框
const showAllocateDialog = () => {
allocateDialogVisible.value = true
deviceNosText.value = ''
allocateForm.virtual_nos = []
allocateForm.remark = ''
if (allocateFormRef.value) {
allocateFormRef.value.resetFields()
// 获取可用设备列表
const getAvailableDevicesList = async () => {
availableDevicesLoading.value = true
try {
const params: any = {
page: devicePagination.page,
page_size: devicePagination.pageSize,
...deviceSearchForm
}
// 清理空值
Object.keys(params).forEach((key) => {
if (params[key] === '' || params[key] === undefined) {
delete params[key]
}
})
const res = await DeviceService.getDevices(params)
if (res.code === 0) {
availableDevicesList.value = res.data.items || []
devicePagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
ElMessage.error('获取设备列表失败')
} finally {
availableDevicesLoading.value = false
}
}
// 处理设备号输入变化
const handleDeviceNosChange = () => {
// 解析输入的设备号,支持逗号、空格、换行分隔
const deviceNos = deviceNosText.value
.split(/[,\s\n]+/)
.map((no) => no.trim())
.filter((no) => no.length > 0)
allocateForm.virtual_nos = deviceNos
// 处理设备列表选择变化
const handleAvailableDevicesSelectionChange = (selection: Device[]) => {
selectedAvailableDevices.value = selection
}
// 设备列表搜索
const handleDeviceSearch = () => {
devicePagination.page = 1
getAvailableDevicesList()
}
// 设备列表重置
const handleDeviceSearchReset = () => {
Object.assign(deviceSearchForm, { ...initialDeviceSearchState })
devicePagination.page = 1
getAvailableDevicesList()
}
// 设备列表分页大小变化
const handleDevicePageSizeChange = (newPageSize: number) => {
devicePagination.pageSize = newPageSize
getAvailableDevicesList()
}
// 设备列表页码变化
const handleDevicePageChange = (newPage: number) => {
devicePagination.page = newPage
getAvailableDevicesList()
}
// 检查设备是否可选(已授权的设备不可选)
const checkDeviceSelectable = (row: Device) => {
return !allocatedDeviceVirtualNos.value.has(row.virtual_no)
}
// 显示授权对话框
const showAllocateDialog = () => {
allocateDialogVisible.value = true
selectedAvailableDevices.value = []
// 收集已授权的设备的 virtual_no
allocatedDeviceVirtualNos.value = new Set(deviceList.value.map((device) => device.virtual_no))
// 重置搜索条件
Object.assign(deviceSearchForm, { ...initialDeviceSearchState })
devicePagination.page = 1
devicePagination.pageSize = 20
// 加载可用设备列表
getAvailableDevicesList()
}
// 执行授权
const handleAllocate = async () => {
if (!allocateFormRef.value) return
if (selectedAvailableDevices.value.length === 0) {
ElMessage.warning('请选择要授权的设备')
return
}
await allocateFormRef.value.validate(async (valid) => {
if (valid) {
if (allocateForm.virtual_nos.length === 0) {
ElMessage.warning(t('enterpriseDevices.messages.deviceNosEmpty'))
return
}
allocateLoading.value = true
try {
const device_nos = selectedAvailableDevices.value.map((device) => device.virtual_no)
const res = await EnterpriseService.allocateDevices(enterpriseId.value, {
device_nos
})
if (allocateForm.virtual_nos.length > 100) {
ElMessage.warning(t('enterpriseDevices.messages.deviceNosMaxLimit'))
return
}
if (res.code === 0) {
operationResult.value = res.data
allocateDialogVisible.value = false
resultDialogVisible.value = true
allocateLoading.value = true
try {
const res = await EnterpriseService.allocateDevices(enterpriseId.value, {
virtual_nos: allocateForm.virtual_nos,
remark: allocateForm.remark || undefined
})
if (res.code === 0) {
operationResult.value = res.data
allocateDialogVisible.value = false
resultDialogVisible.value = true
// 显示成功消息
if (res.data.success_count > 0 && res.data.fail_count === 0) {
ElMessage.success(t('enterpriseDevices.messages.allocateSuccess'))
} else if (res.data.success_count > 0 && res.data.fail_count > 0) {
ElMessage.warning(
t('enterpriseDevices.messages.allocatePartialSuccess', {
success: res.data.success_count,
fail: res.data.fail_count
})
)
} else {
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
}
getTableData()
}
} catch (error) {
console.error(error)
// 显示成功消息
if (res.data.success_count > 0 && res.data.fail_count === 0) {
ElMessage.success(t('enterpriseDevices.messages.allocateSuccess'))
} else if (res.data.success_count > 0 && res.data.fail_count > 0) {
ElMessage.warning(
t('enterpriseDevices.messages.allocatePartialSuccess', {
success: res.data.success_count,
fail: res.data.fail_count
})
)
} else {
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
} finally {
allocateLoading.value = false
}
getTableData()
}
})
} catch (error) {
console.error(error)
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
} finally {
allocateLoading.value = false
}
}
// 关闭授权对话框
const handleAllocateDialogClose = () => {
if (allocateFormRef.value) {
allocateFormRef.value.resetFields()
}
selectedAvailableDevices.value = []
}
// 显示撤销授权对话框
@@ -582,7 +741,7 @@
recallLoading.value = true
try {
const res = await EnterpriseService.recallDevices(enterpriseId.value, {
virtual_nos: selectedDevices.value.map((device) => device.virtual_no)
device_nos: selectedDevices.value.map((device) => device.virtual_no)
})
if (res.code === 0) {
@@ -636,5 +795,12 @@
align-items: center;
justify-content: space-between;
}
.device-selection-info {
margin-top: 10px;
margin-bottom: 12px;
color: var(--el-color-info);
font-size: 14px;
}
}
</style>