|
|
@@ -631,6 +631,196 @@
|
|
|
</template>
|
|
|
</el-drawer>
|
|
|
|
|
|
+ <!-- 扫描设备抽屉 -->
|
|
|
+ <el-drawer
|
|
|
+ v-model="scanDrawerVisible"
|
|
|
+ :title="t('扫描')"
|
|
|
+ direction="rtl"
|
|
|
+ size="50%"
|
|
|
+ destroy-on-close
|
|
|
+ class="scan-drawer"
|
|
|
+ >
|
|
|
+ <div class="scan-drawer-content">
|
|
|
+ <div class="scan-toolbar">
|
|
|
+ <div class="scan-toolbar-left">
|
|
|
+ <el-button v-if="scanMatched" @click="handleRematch">
|
|
|
+ <Icon icon="mdi:refresh" width="16" height="16" style="margin-right: 4px" />
|
|
|
+ {{ t('再次匹配') }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="scan-toolbar-right">
|
|
|
+ <el-button @click="credentialDrawerVisible = true">
|
|
|
+ <Icon icon="mdi:key-variant" width="16" height="16" style="margin-right: 4px" />
|
|
|
+ {{ t('账号配置') }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-table v-loading="scanLoading" :data="discoveredDevices" stripe>
|
|
|
+ <template #empty>
|
|
|
+ <el-empty :description="t('暂无发现设备')" />
|
|
|
+ </template>
|
|
|
+ <el-table-column type="index" :label="t('序号')" width="60" align="center" />
|
|
|
+ <el-table-column prop="ip" label="IP" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="port" :label="t('端口')" width="80" align="center" />
|
|
|
+ <el-table-column prop="deviceName" :label="t('设备名称')" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column v-if="scanMatched" :label="t('匹配状态')" width="100" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <Icon
|
|
|
+ v-if="row.matchStatus === 'MATCHED'"
|
|
|
+ icon="mdi:check-circle"
|
|
|
+ width="20"
|
|
|
+ height="20"
|
|
|
+ style="color: #67c23a"
|
|
|
+ />
|
|
|
+ <Icon
|
|
|
+ v-else-if="row.matchStatus === 'UNMATCHED'"
|
|
|
+ icon="mdi:close-circle"
|
|
|
+ width="20"
|
|
|
+ height="20"
|
|
|
+ style="color: #f56c6c"
|
|
|
+ />
|
|
|
+ <Icon
|
|
|
+ v-else-if="row.matchStatus === 'MATCHING'"
|
|
|
+ icon="mdi:progress-clock"
|
|
|
+ width="20"
|
|
|
+ height="20"
|
|
|
+ style="color: #e6a23c"
|
|
|
+ />
|
|
|
+ <span v-else style="color: #909399">{{ t('待匹配') }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column v-if="scanMatched" :label="t('操作')" width="80" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button
|
|
|
+ v-if="row.matchStatus === 'MATCHED' && !row.bound"
|
|
|
+ type="primary"
|
|
|
+ link
|
|
|
+ @click="handleBindDevice(row)"
|
|
|
+ >
|
|
|
+ {{ t('添加') }}
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="drawer-footer">
|
|
|
+ <el-button @click="scanDrawerVisible = false">{{ t('取消') }}</el-button>
|
|
|
+ <el-button v-if="!scanMatched" type="primary" :loading="matchLoading" @click="handleTriggerMatch">
|
|
|
+ {{ t('匹配') }}
|
|
|
+ </el-button>
|
|
|
+ <el-button v-else type="primary" @click="scanDrawerVisible = false">
|
|
|
+ {{ t('完成') }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <!-- 账号配置抽屉(第二层) -->
|
|
|
+ <el-drawer
|
|
|
+ v-model="credentialDrawerVisible"
|
|
|
+ :title="t('账号配置')"
|
|
|
+ direction="rtl"
|
|
|
+ size="50%"
|
|
|
+ destroy-on-close
|
|
|
+ :append-to-body="true"
|
|
|
+ class="credential-drawer"
|
|
|
+ >
|
|
|
+ <div class="credential-content">
|
|
|
+ <div class="credential-toolbar">
|
|
|
+ <el-form :model="credentialSearchForm" inline>
|
|
|
+ <el-form-item>
|
|
|
+ <el-input
|
|
|
+ v-model.trim="credentialSearchForm.username"
|
|
|
+ :placeholder="t('用户名')"
|
|
|
+ clearable
|
|
|
+ style="width: 150px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-input
|
|
|
+ v-model.trim="credentialSearchForm.password"
|
|
|
+ :placeholder="t('密码')"
|
|
|
+ clearable
|
|
|
+ style="width: 150px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="loadCredentials">
|
|
|
+ <Icon icon="mdi:magnify" width="16" height="16" style="margin-right: 4px" />
|
|
|
+ {{ t('查询') }}
|
|
|
+ </el-button>
|
|
|
+ <el-button type="info" @click="handleCredentialReset">
|
|
|
+ <Icon icon="mdi:refresh" width="16" height="16" style="margin-right: 4px" />
|
|
|
+ {{ t('重置') }}
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" @click="handleAddCredential">
|
|
|
+ <Icon icon="mdi:plus" width="16" height="16" style="margin-right: 4px" />
|
|
|
+ {{ t('新增') }}
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <el-table v-loading="credentialLoading" :data="filteredCredentials" stripe>
|
|
|
+ <template #empty>
|
|
|
+ <el-empty :description="t('暂无发现设备')" />
|
|
|
+ </template>
|
|
|
+ <el-table-column prop="username" :label="t('用户名')" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="password" :label="t('密码')" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column :label="t('设备控制')" width="100" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button type="primary" link @click="handleEditCredential(row)">
|
|
|
+ <Icon icon="mdi:note-edit-outline" width="20" height="20" />
|
|
|
+ </el-button>
|
|
|
+ <el-button type="danger" link @click="handleDeleteCredential(row)">
|
|
|
+ <Icon icon="mdi:delete" width="20" height="20" />
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <!-- 凭证编辑对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="credentialDialogVisible"
|
|
|
+ :title="isEditCredential ? t('编辑凭证') : t('新增凭证')"
|
|
|
+ width="500px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :append-to-body="true"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <el-form ref="credentialFormRef" :model="credentialForm" :rules="credentialRules" label-width="100px">
|
|
|
+ <el-form-item :label="t('凭证名称')" prop="name">
|
|
|
+ <el-input v-model="credentialForm.name" :placeholder="t('请输入凭证名称')" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('用户名')" prop="username">
|
|
|
+ <el-input v-model="credentialForm.username" :placeholder="t('请输入用户名')" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('密码')" prop="password">
|
|
|
+ <el-input v-model="credentialForm.password" :placeholder="t('请输入密码')" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('厂商')">
|
|
|
+ <el-input v-model="credentialForm.vendor" :placeholder="t('请选择')" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('优先级')">
|
|
|
+ <el-input-number v-model="credentialForm.priority" :min="0" :max="999" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('启用')">
|
|
|
+ <el-switch v-model="credentialForm.enabled" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('备注')">
|
|
|
+ <el-input v-model="credentialForm.remark" type="textarea" :rows="3" :placeholder="t('请输入备注')" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="credentialDialogVisible = false">{{ t('取消') }}</el-button>
|
|
|
+ <el-button type="primary" :loading="credentialSubmitting" @click="handleSubmitCredential">
|
|
|
+ {{ isEditCredential ? t('更新') : t('添加') }}
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
<!-- 分页 -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
@@ -659,6 +849,16 @@ import { useRouter } from 'vue-router'
|
|
|
import { listLssNodes, deleteLssNode, setLssNodeEnabled, updateLssNode } from '@/api/lss'
|
|
|
import { adminListCameras, adminAddCamera, adminUpdateCamera, adminDeleteCamera, adminGetCamera } from '@/api/camera'
|
|
|
import { listCameraVendors } from '@/api/camera-vendor'
|
|
|
+import {
|
|
|
+ scanDevices,
|
|
|
+ getDiscoveredDevices,
|
|
|
+ triggerMatch,
|
|
|
+ bindDevice,
|
|
|
+ getCredentials,
|
|
|
+ addCredential,
|
|
|
+ updateCredential,
|
|
|
+ deleteCredential
|
|
|
+} from '@/api/camera-scan'
|
|
|
import CodeEditor from '@/components/CodeEditor.vue'
|
|
|
import type {
|
|
|
LssNodeDTO,
|
|
|
@@ -670,7 +870,9 @@ import type {
|
|
|
CameraUpdateRequest,
|
|
|
CameraVendorDTO,
|
|
|
IAbly,
|
|
|
- CameraHeartbeatStatus
|
|
|
+ CameraHeartbeatStatus,
|
|
|
+ DiscoveredCameraDTO,
|
|
|
+ CameraCredentialDTO
|
|
|
} from '@/types'
|
|
|
|
|
|
const { t } = useI18n({ useScope: 'global' })
|
|
|
@@ -1041,10 +1243,261 @@ function handleViewDetail(row: LssNodeDTO) {
|
|
|
detailDrawerVisible.value = true
|
|
|
}
|
|
|
|
|
|
-function handleScanDevices(row: LssNodeDTO) {
|
|
|
- console.log(row)
|
|
|
+// ==================== 扫描设备相关 ====================
|
|
|
+const scanDrawerVisible = ref(false)
|
|
|
+const scanLoading = ref(false)
|
|
|
+const matchLoading = ref(false)
|
|
|
+const scanMatched = ref(false)
|
|
|
+const discoveredDevices = ref<DiscoveredCameraDTO[]>([])
|
|
|
+const scanLssId = ref('')
|
|
|
+
|
|
|
+async function handleScanDevices(row: LssNodeDTO) {
|
|
|
+ scanLssId.value = row.lssId
|
|
|
+ scanMatched.value = false
|
|
|
+
|
|
|
+ // 先尝试获取已有的发现设备
|
|
|
+ try {
|
|
|
+ const res = await getDiscoveredDevices(row.lssId)
|
|
|
+ if (res.success && res.data && res.data.length > 0) {
|
|
|
+ // 已有扫描结果,直接打开抽屉
|
|
|
+ discoveredDevices.value = res.data
|
|
|
+ scanDrawerVisible.value = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取发现设备失败', error)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有扫描结果,弹确认框询问是否开启扫描
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(t('当前 LSS 节点尚未扫描设备,是否开启扫描?'), t('扫描设备'), {
|
|
|
+ confirmButtonText: t('开始扫描'),
|
|
|
+ cancelButtonText: t('取消'),
|
|
|
+ type: 'info'
|
|
|
+ })
|
|
|
+ } catch {
|
|
|
+ // 用户取消
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户确认,触发扫描
|
|
|
+ scanDrawerVisible.value = true
|
|
|
+ scanLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await scanDevices(row.lssId)
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('扫描完成'))
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('扫描失败'))
|
|
|
+ }
|
|
|
+ // 刷新列表
|
|
|
+ await loadDiscoveredDevices()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('扫描失败', error)
|
|
|
+ ElMessage.error(t('扫描失败'))
|
|
|
+ } finally {
|
|
|
+ scanLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function loadDiscoveredDevices() {
|
|
|
+ if (!scanLssId.value) return
|
|
|
+ scanLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await getDiscoveredDevices(scanLssId.value)
|
|
|
+ if (res.success) {
|
|
|
+ discoveredDevices.value = res.data || []
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('获取发现设备失败'))
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取发现设备失败', error)
|
|
|
+ } finally {
|
|
|
+ scanLoading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+async function handleTriggerMatch() {
|
|
|
+ if (!scanLssId.value) return
|
|
|
+ matchLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await triggerMatch(scanLssId.value)
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('匹配完成'))
|
|
|
+ scanMatched.value = true
|
|
|
+ await loadDiscoveredDevices()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('匹配失败'))
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('匹配失败', error)
|
|
|
+ ElMessage.error(t('匹配失败'))
|
|
|
+ } finally {
|
|
|
+ matchLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function handleRematch() {
|
|
|
+ scanMatched.value = false
|
|
|
+ await handleTriggerMatch()
|
|
|
+}
|
|
|
+
|
|
|
+async function handleBindDevice(row: DiscoveredCameraDTO) {
|
|
|
+ // 占位:后续接入真实 bind 接口
|
|
|
+ console.log('bind device', row)
|
|
|
+ ElMessage.info('绑定功能开发中')
|
|
|
+}
|
|
|
+
|
|
|
+// ==================== 凭证管理相关 ====================
|
|
|
+const credentialDrawerVisible = ref(false)
|
|
|
+const credentialLoading = ref(false)
|
|
|
+const credentials = ref<CameraCredentialDTO[]>([])
|
|
|
+const credentialSearchForm = reactive({ username: '', password: '' })
|
|
|
+
|
|
|
+const filteredCredentials = computed(() => {
|
|
|
+ let list = credentials.value
|
|
|
+ if (credentialSearchForm.username) {
|
|
|
+ list = list.filter((c) => c.username.includes(credentialSearchForm.username))
|
|
|
+ }
|
|
|
+ if (credentialSearchForm.password) {
|
|
|
+ list = list.filter((c) => c.password.includes(credentialSearchForm.password))
|
|
|
+ }
|
|
|
+ return list
|
|
|
+})
|
|
|
+
|
|
|
+async function loadCredentials() {
|
|
|
+ credentialLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await getCredentials()
|
|
|
+ if (res.success) {
|
|
|
+ credentials.value = res.data || []
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('获取凭证列表失败'))
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取凭证列表失败', error)
|
|
|
+ } finally {
|
|
|
+ credentialLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleCredentialReset() {
|
|
|
+ credentialSearchForm.username = ''
|
|
|
+ credentialSearchForm.password = ''
|
|
|
+}
|
|
|
+
|
|
|
+// 凭证编辑
|
|
|
+const credentialDialogVisible = ref(false)
|
|
|
+const isEditCredential = ref(false)
|
|
|
+const credentialSubmitting = ref(false)
|
|
|
+const currentCredential = ref<CameraCredentialDTO | null>(null)
|
|
|
+const credentialFormRef = ref<FormInstance>()
|
|
|
+const credentialForm = reactive({
|
|
|
+ name: '',
|
|
|
+ username: '',
|
|
|
+ password: '',
|
|
|
+ vendor: '',
|
|
|
+ priority: 0,
|
|
|
+ enabled: true,
|
|
|
+ remark: ''
|
|
|
+})
|
|
|
+
|
|
|
+const credentialRules = computed<FormRules>(() => ({
|
|
|
+ name: [{ required: true, message: t('请输入凭证名称'), trigger: 'blur' }],
|
|
|
+ username: [{ required: true, message: t('请输入用户名'), trigger: 'blur' }],
|
|
|
+ password: [{ required: true, message: t('请输入密码'), trigger: 'blur' }]
|
|
|
+}))
|
|
|
+
|
|
|
+function resetCredentialForm() {
|
|
|
+ credentialForm.name = ''
|
|
|
+ credentialForm.username = ''
|
|
|
+ credentialForm.password = ''
|
|
|
+ credentialForm.vendor = ''
|
|
|
+ credentialForm.priority = 0
|
|
|
+ credentialForm.enabled = true
|
|
|
+ credentialForm.remark = ''
|
|
|
+ credentialFormRef.value?.clearValidate()
|
|
|
+}
|
|
|
+
|
|
|
+function handleAddCredential() {
|
|
|
+ isEditCredential.value = false
|
|
|
+ currentCredential.value = null
|
|
|
+ resetCredentialForm()
|
|
|
+ credentialDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+function handleEditCredential(row: CameraCredentialDTO) {
|
|
|
+ isEditCredential.value = true
|
|
|
+ currentCredential.value = row
|
|
|
+ credentialForm.name = row.name
|
|
|
+ credentialForm.username = row.username
|
|
|
+ credentialForm.password = row.password
|
|
|
+ credentialForm.vendor = row.vendor || ''
|
|
|
+ credentialForm.priority = row.priority || 0
|
|
|
+ credentialForm.enabled = row.enabled
|
|
|
+ credentialForm.remark = row.remark || ''
|
|
|
+ credentialDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+async function handleSubmitCredential() {
|
|
|
+ if (!credentialFormRef.value) return
|
|
|
+ await credentialFormRef.value.validate(async (valid) => {
|
|
|
+ if (!valid) return
|
|
|
+ credentialSubmitting.value = true
|
|
|
+ try {
|
|
|
+ if (isEditCredential.value && currentCredential.value) {
|
|
|
+ const res = await updateCredential({
|
|
|
+ id: currentCredential.value.id,
|
|
|
+ ...credentialForm
|
|
|
+ })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('更新成功'))
|
|
|
+ credentialDialogVisible.value = false
|
|
|
+ loadCredentials()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('更新失败'))
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const res = await addCredential({ ...credentialForm })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('新增成功'))
|
|
|
+ credentialDialogVisible.value = false
|
|
|
+ loadCredentials()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('新增失败'))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存凭证失败', error)
|
|
|
+ ElMessage.error(t('操作失败'))
|
|
|
+ } finally {
|
|
|
+ credentialSubmitting.value = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+async function handleDeleteCredential(row: CameraCredentialDTO) {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(t('确定要删除该凭证吗?'), t('提示'), { type: 'warning' })
|
|
|
+ const res = await deleteCredential(row.id)
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('删除凭证成功'))
|
|
|
+ loadCredentials()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('删除失败'))
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('删除凭证失败', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 打开凭证抽屉时加载数据
|
|
|
+watch(credentialDrawerVisible, (val) => {
|
|
|
+ if (val) loadCredentials()
|
|
|
+})
|
|
|
+
|
|
|
function handleEdit(row: LssNodeDTO, tab: 'detail' | 'camera' | 'pusher') {
|
|
|
currentLss.value = row
|
|
|
lssEditForm.lssName = row.lssName || ''
|
|
|
@@ -1733,6 +2186,50 @@ onMounted(() => {
|
|
|
gap: 12px;
|
|
|
}
|
|
|
|
|
|
+// 扫描抽屉样式
|
|
|
+.scan-drawer {
|
|
|
+ :deep(.el-drawer__body) {
|
|
|
+ padding: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.scan-drawer-content {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.scan-toolbar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+// 凭证抽屉样式
|
|
|
+.credential-drawer {
|
|
|
+ :deep(.el-drawer__body) {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.credential-content {
|
|
|
+ padding: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.credential-toolbar {
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ :deep(.el-form) {
|
|
|
+ .el-form-item {
|
|
|
+ margin-bottom: 0;
|
|
|
+ margin-right: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 表格样式
|
|
|
:deep(.el-table) {
|
|
|
--el-table-row-hover-bg-color: #f0f0ff;
|