diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index 0b79f15..2ac5397 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -77,14 +77,13 @@ {{ selectedDevices.length }} - @@ -222,7 +221,6 @@ :loading="seriesLoading" clearable > - ([]) - const shopTreeData = ref([]) + const shopCascadeOptions = ref([]) + const shopCascadeProps = { + lazy: true, + checkStrictly: true, + lazyLoad: async (node: any, resolve: any) => { + const { level, value } = node + const parentId = level === 0 ? undefined : value + + try { + const res = await ShopService.getShopsCascade({ parent_id: parentId }) + if (res.code === 0) { + const nodes = (res.data || []).map((item: any) => ({ + value: item.id, + label: item.shop_name, + leaf: !item.has_children + })) + resolve(nodes) + } else { + resolve([]) + } + } catch (error) { + console.error('加载店铺级联数据失败:', error) + resolve([]) + } + }, + value: 'value', + label: 'label', + children: 'children' + } const allocateResult = ref(null) const recallResult = ref(null) @@ -1146,7 +1164,6 @@ onMounted(() => { getTableData() - loadShopTree() }) // 当页面被 keep-alive 激活时自动刷新数据 @@ -1154,49 +1171,6 @@ getTableData() }) - // 加载店铺树形数据 - const loadShopTree = async () => { - try { - const res = await ShopService.getShops({ - page: 1, - page_size: 9999, // 获取所有数据用于构建树形结构 - status: 1 // 只获取启用的店铺 - }) - if (res.code === 0) { - shopTreeData.value = buildShopTree(res.data.items || []) - } - } catch (error) { - console.error('获取店铺列表失败:', error) - } - } - - // 构建店铺树形结构 - const buildShopTree = (shops: any[]) => { - const map = new Map() - const tree: any[] = [] - - // 先将所有项放入 map - shops.forEach((shop) => { - map.set(shop.id, { ...shop, children: [] }) - }) - - // 构建树形结构 - shops.forEach((shop) => { - const node = map.get(shop.id)! - if (shop.parent_id && map.has(shop.parent_id)) { - // 有父节点,添加到父节点的 children 中 - const parent = map.get(shop.parent_id)! - if (!parent.children) parent.children = [] - parent.children.push(node) - } else { - // 没有父节点或父节点不存在,作为根节点 - tree.push(node) - } - }) - - return tree - } - // 获取设备列表 const getTableData = async () => { loading.value = true @@ -1278,7 +1252,43 @@ } }) .catch(() => { - // 用户取消删除 + // 用户取消 + }) + } + + // 清除设备套餐系列绑定 + const handleClearDeviceSeries = async (deviceNo: string) => { + const device = deviceList.value.find((d) => d.virtual_no === deviceNo) + if (!device) { + ElMessage.error('未找到该设备') + return + } + + ElMessageBox.confirm(`确定要清除设备 ${deviceNo} 的套餐系列关联吗?`, '确认清除', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }) + .then(async () => { + try { + const res = await DeviceService.batchSetDeviceSeriesBinding({ + device_ids: [device.id], + series_id: 0 + }) + if (res.code === 0) { + if (res.data.fail_count === 0) { + ElMessage.success('清除关联成功') + } else { + ElMessage.warning(`部分失败:${res.data.failed_items?.[0]?.reason || '未知原因'}`) + } + await getTableData() + } + } catch (error) { + console.error('清除关联失败:', error) + } + }) + .catch(() => { + // 用户取消 }) } @@ -1534,6 +1544,9 @@ case 'delete': await handleDeleteDeviceByNo(deviceNo) break + case 'clear-series': + await handleClearDeviceSeries(deviceNo) + break } } @@ -1794,6 +1807,14 @@ }) } + if (hasAuth('device:clear_series')) { + moreActions.push({ + label: '清除关联', + handler: () => handleDeviceOperation('clear-series', row.virtual_no, row), + type: 'primary' + }) + } + // if (hasAuth('device:manual_deactivate')) { // moreActions.push({ // label: '手动停用', diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue index 6c7a682..e679d42 100644 --- a/src/views/asset-management/iot-card-management/index.vue +++ b/src/views/asset-management/iot-card-management/index.vue @@ -94,24 +94,15 @@ label-width="120px" > - - - + /> @@ -342,7 +333,6 @@ :loading="seriesLoading" clearable > - >() // 店铺相关 - const targetShopLoading = ref(false) - const targetShopList = ref([]) + const shopCascadeOptions = ref([]) + const shopCascadeProps = { + lazy: true, + checkStrictly: true, + lazyLoad: async (node: any, resolve: any) => { + const { level, value } = node + const parentId = level === 0 ? undefined : value + + try { + const res = await ShopService.getShopsCascade({ parent_id: parentId }) + if (res.code === 0) { + const nodes = (res.data || []).map((item: any) => ({ + value: item.id, + label: item.shop_name, + leaf: !item.has_children + })) + resolve(nodes) + } else { + resolve([]) + } + } catch (error) { + console.error('加载店铺级联数据失败:', error) + resolve([]) + } + }, + value: 'value', + label: 'label', + children: 'children' + } // 搜索表单初始值 const initialSearchState: { @@ -1213,33 +1230,6 @@ selectedCards.value = selection } - // 加载目标店铺列表 - const loadTargetShops = async (shopName?: string) => { - targetShopLoading.value = true - try { - const params: any = { - page: 1, - page_size: 20 - } - if (shopName) { - params.shop_name = shopName - } - const res = await ShopService.getShops(params) - if (res.code === 0) { - targetShopList.value = res.data.items || [] - } - } catch (error) { - console.error('获取目标店铺列表失败:', error) - } finally { - targetShopLoading.value = false - } - } - - // 搜索目标店铺 - const searchTargetShops = async (query: string) => { - await loadTargetShops(query || undefined) - } - // 显示批量分配对话框 const showAllocateDialog = () => { if (selectedCards.value.length === 0) { @@ -1258,8 +1248,6 @@ batch_no: '', remark: '' }) - // 加载默认店铺列表 - loadTargetShops() if (allocateFormRef.value) { allocateFormRef.value.resetFields() } @@ -1536,6 +1524,13 @@ }) } + if (hasAuth('iot_card:clear_series')) { + items.push({ + key: 'clearSeries', + label: '清除关联' + }) + } + return items }) @@ -1565,9 +1560,52 @@ case 'changePackage': changePackage() break + case 'clearSeries': + handleClearCardSeries() + break } } + // 清除卡套餐系列绑定 + const handleClearCardSeries = async () => { + if (selectedCards.value.length === 0) { + ElMessage.warning('请先选择要清除关联的卡') + return + } + + ElMessageBox.confirm( + `确定要清除所选 ${selectedCards.value.length} 张卡的套餐系列关联吗?`, + '确认清除', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + } + ) + .then(async () => { + try { + const res = await CardService.batchSetCardSeriesBinding({ + iccids: selectedCards.value.map((card) => card.iccid), + series_id: 0 + }) + if (res.code === 0) { + if (res.data.fail_count === 0) { + ElMessage.success('清除关联成功') + } else { + ElMessage.warning(`部分失败:${res.data.failed_items?.[0]?.reason || '未知原因'}`) + } + selectedCards.value = [] + await getTableData() + } + } catch (error) { + console.error('清除关联失败:', error) + } + }) + .catch(() => { + // 用户取消 + }) + } + // 网卡分销 - 正在开发中 const cardDistribution = () => { ElMessage.info('功能正在开发中') diff --git a/src/views/package-management/package-list/index.vue b/src/views/package-management/package-list/index.vue index a040b48..63f3267 100644 --- a/src/views/package-management/package-list/index.vue +++ b/src/views/package-management/package-list/index.vue @@ -51,7 +51,7 @@ :close-on-click-modal="false" @closed="handleDialogClosed" > - +