设备管理样式 #148

Merged
hnu202326010221 merged 1 commits from pangqr_branch into develop 4 months ago

@ -36,6 +36,11 @@ const selectedStatus = ref('')
const selectedSearchField = ref('device_name') //
const showCustomModal = ref(false)
const customModalMessage = ref('')
const showConfirmModal = ref(false)
const confirmModalMessage = ref('')
const confirmModalCallback = ref(null)
const showToast = ref(false)
const toastMessage = ref('')
const statusOptions = [
{value: '', label: '全部状态', icon: '📊'},
@ -211,12 +216,43 @@ const showErrorModal = (message) => {
showCustomModal.value = true
}
// Toast
const showSuccessToast = (message) => {
toastMessage.value = message
showToast.value = true
setTimeout(() => {
showToast.value = false
}, 2000)
}
//
const closeCustomModal = () => {
showCustomModal.value = false
customModalMessage.value = ''
}
//
const showConfirm = (message, callback) => {
confirmModalMessage.value = message
confirmModalCallback.value = callback
showConfirmModal.value = true
}
//
const handleConfirm = () => {
if (confirmModalCallback.value) {
confirmModalCallback.value()
}
closeConfirmModal()
}
//
const closeConfirmModal = () => {
showConfirmModal.value = false
confirmModalMessage.value = ''
confirmModalCallback.value = null
}
const handleReset = () => {
searchQuery.value = ''
selectedStatus.value = ''
@ -278,50 +314,64 @@ const visiblePages = computed(() => {
// --- ---
const handleBatchExecute = async () => {
if (selectedIds.value.length === 0) {
alert('请先在表格左侧勾选需要操作的设备!')
showErrorModal('请先在表格左侧勾选需要操作的设备!')
return
}
try {
if (selectedBatchAction.value === 'unbind') {
if (!confirm(`确定要批量解除这 ${selectedIds.value.length} 台设备的绑定吗?`)) return
await batchUnbindDevices(selectedIds.value)
alert(`批量解绑成功!`)
showConfirm(`确定要批量解除这 ${selectedIds.value.length} 台设备的绑定吗?`, async () => {
try {
await batchUnbindDevices(selectedIds.value)
showSuccessToast('批量解绑成功!')
selectedIds.value = []
fetchDashboardData()
} catch (error) {
console.error("批量解绑失败", error)
showErrorModal('批量操作失败,请确保后端接口已启动')
}
})
} else if (selectedBatchAction.value === 'delete') {
if (!confirm(`⚠️ 警告:确定要永久删除这 ${selectedIds.value.length} 台设备吗?数据不可恢复!`)) return
await batchDeleteDevices(selectedIds.value)
alert(`批量删除成功!`)
showConfirm(`⚠️ 警告:确定要永久删除这 ${selectedIds.value.length} 台设备吗?数据不可恢复!`, async () => {
try {
await batchDeleteDevices(selectedIds.value)
showSuccessToast('批量删除成功!')
selectedIds.value = []
fetchDashboardData()
} catch (error) {
console.error("批量删除失败", error)
showErrorModal('批量操作失败,请确保后端接口已启动')
}
})
}
selectedIds.value = []
fetchDashboardData()
} catch (error) {
console.error("批量操作失败", error)
alert('批量操作失败,请确保后端接口已启动')
showErrorModal('批量操作失败,请确保后端接口已启动')
}
}
// --- ---
const unbindDevice = async (id, name) => {
if (confirm(`确定要解除设备 ${name} 的绑定吗?`)) {
showConfirm(`确定要解除设备 ${name} 的绑定吗?`, async () => {
try {
await adminUnbindDevice(id)
alert('解绑成功');
showSuccessToast('解绑成功')
fetchDashboardData()
} catch (e) {
alert('操作异常')
showErrorModal('操作异常')
}
}
})
}
const deleteDevice = async (id, name) => {
if (confirm(`确定要删除设备 ${name} 吗?`)) {
showConfirm(`确定要删除设备 ${name} 吗?`, async () => {
try {
await adminDeleteDevice(id)
alert('删除成功');
showSuccessToast('删除成功')
fetchDashboardData()
} catch (e) {
alert('操作异常')
showErrorModal('操作异常')
}
}
})
}
// --- ---
@ -340,7 +390,7 @@ const currentDeviceData = ref({
const viewData = async (deviceId, deviceName) => {
console.log("正在查看设备ID:", deviceId);
if (!deviceId) {
alert("无法获取设备ID请检查数据");
showErrorModal("无法获取设备ID请检查数据");
return;
}
@ -353,7 +403,7 @@ const viewData = async (deviceId, deviceName) => {
const userId = deviceDetail?.device?.user_id;
if (!userId) {
alert("该设备未绑定用户,无法查看数据");
showErrorModal("该设备未绑定用户,无法查看数据");
return;
}
@ -373,7 +423,7 @@ const viewData = async (deviceId, deviceName) => {
await loadDeviceHealthData()
} catch (error) {
console.error("获取设备详情失败:", error)
alert("获取设备详情失败: " + error.message)
showErrorModal("获取设备详情失败: " + error.message)
}
}
@ -391,7 +441,7 @@ const loadDeviceHealthData = async () => {
});
if (!userId) {
alert("设备未绑定用户,无法获取数据")
showErrorModal("设备未绑定用户,无法获取数据")
return
}
@ -415,7 +465,7 @@ const loadDeviceHealthData = async () => {
currentDeviceData.value.healthData = res || []
} catch (err) {
console.error('加载设备数据失败:', err)
alert('加载数据失败: ' + err.message)
showErrorModal('加载数据失败: ' + err.message)
} finally {
currentDeviceData.value.loading = false
}
@ -428,7 +478,7 @@ const changeDateRange = async (range) => {
const applyCustomDateRange = async () => {
if (!currentDeviceData.value.customStartDate || !currentDeviceData.value.customEndDate) {
alert('请输入完整日期');
showErrorModal('请输入完整日期');
return
}
await loadDeviceHealthData()
@ -484,6 +534,11 @@ const formatTime = (time) => {
<template>
<LayoutFrame title="设备管理">
<!-- Toast提示 -->
<div v-if="showToast" class="toast-message text-body">
{{ toastMessage }}
</div>
<!-- 统计概览 (保持不变) -->
<div class="cards-grid">
<div class="stat-card">
@ -840,6 +895,22 @@ const formatTime = (time) => {
</div>
</div>
</div>
<!-- 确认操作弹窗 -->
<div v-if="showConfirmModal" class="dialog-overlay" @click="closeConfirmModal">
<div class="dialog" @click.stop>
<div class="dialog-header">
<h3>确认操作</h3>
</div>
<div class="dialog-body">
<p>{{ confirmModalMessage }}</p>
</div>
<div class="dialog-actions">
<button @click="closeConfirmModal" class="btn secondary">取消</button>
<button @click="handleConfirm" class="btn primary">确定</button>
</div>
</div>
</div>
</LayoutFrame>
</template>
@ -1875,6 +1946,102 @@ const formatTime = (time) => {
background: #3a8ee6;
}
.modal-cancel-btn-notification {
background: #f5f5f5;
color: #606266;
border: 1px solid #dcdfe6;
border-radius: 6px;
padding: 8px 24px;
cursor: pointer;
transition: all 0.3s;
min-width: 80px;
margin-right: 12px;
}
.modal-cancel-btn-notification:hover {
background: #e6e6e6;
border-color: #c0c4cc;
}
.modal-cancel-btn-notification:active {
background: #d9d9d9;
}
/* 确认弹窗样式(与糖友圈发布管理页面一致) */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(4px);
}
.dialog {
background: white;
border-radius: var(--radius-xl);
padding: var(--space-6);
min-width: 400px;
max-width: 500px;
box-shadow: var(--shadow-xl);
}
.dialog-header {
margin-bottom: var(--space-4);
}
.dialog-header h3 {
font-size: var(--text-lg);
font-weight: 600;
color: var(--color-gray-900);
}
.dialog-body {
margin-bottom: var(--space-6);
}
.dialog-body p {
color: var(--color-gray-700);
line-height: 1.6;
}
.dialog-actions {
display: flex;
gap: var(--space-3);
justify-content: flex-end;
}
/* Toast提示样式与糖友圈发布管理页面一致 */
.toast-message {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #67c23a;
color: white;
padding: 12px 24px;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 9999;
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateX(-50%) translateY(-20px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
@ -1908,5 +2075,10 @@ const formatTime = (time) => {
padding-left: 20px;
padding-right: 20px;
}
.dialog {
min-width: 300px;
margin: var(--space-4);
}
}
</style>
Loading…
Cancel
Save