|
|
@@ -17,7 +17,6 @@
|
|
|
</span>
|
|
|
</h1>
|
|
|
<div class="controls">
|
|
|
- <span class="warehouse-label">仓库: {{ config.warehouse }}</span>
|
|
|
<label class="filter-item">
|
|
|
<span class="selector-label">资源类型</span>
|
|
|
<select v-model="selectedCategory" class="level-select">
|
|
|
@@ -41,7 +40,7 @@
|
|
|
</select>
|
|
|
</label>
|
|
|
<label class="filter-item">
|
|
|
- <span class="selector-label">是否有容器</span>
|
|
|
+ <span class="selector-label">容器</span>
|
|
|
<select v-model="selectedHasContainer" class="level-select">
|
|
|
<option value="">全部</option>
|
|
|
<option value="Y">有容器</option>
|
|
|
@@ -58,8 +57,54 @@
|
|
|
placeholder="输入库位组"
|
|
|
@keydown.enter="applyLocGroupFilter"
|
|
|
/>
|
|
|
+ <button
|
|
|
+ v-if="locGroupKeywordInput"
|
|
|
+ class="filter-clear-btn"
|
|
|
+ type="button"
|
|
|
+ @click="clearLocGroupFilter"
|
|
|
+ >
|
|
|
+ <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-action-icon">
|
|
|
+ <path
|
|
|
+ d="M4.22 4.22a.75.75 0 0 1 1.06 0L8 6.94l2.72-2.72a.75.75 0 1 1 1.06 1.06L9.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L8 9.06l-2.72 2.72a.75.75 0 1 1-1.06-1.06L6.94 8 4.22 5.28a.75.75 0 0 1 0-1.06z"
|
|
|
+ fill="currentColor"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
<button class="filter-confirm-btn" type="button" @click="applyLocGroupFilter">
|
|
|
- <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-confirm-icon">
|
|
|
+ <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-action-icon">
|
|
|
+ <path
|
|
|
+ d="M6.5 2.5a4 4 0 1 0 2.47 7.15l2.69 2.68 1.06-1.06-2.68-2.69A4 4 0 0 0 6.5 2.5zm0 1.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5z"
|
|
|
+ fill="currentColor"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </span>
|
|
|
+ </label>
|
|
|
+ <label class="filter-item filter-input-item">
|
|
|
+ <span class="selector-label">库位号</span>
|
|
|
+ <span class="filter-input-wrap">
|
|
|
+ <input
|
|
|
+ v-model="locationIdKeywordInput"
|
|
|
+ class="filter-input"
|
|
|
+ type="text"
|
|
|
+ placeholder="输入库位号"
|
|
|
+ @keydown.enter="applyLocationIdFilter"
|
|
|
+ />
|
|
|
+ <button
|
|
|
+ v-if="locationIdKeywordInput"
|
|
|
+ class="filter-clear-btn"
|
|
|
+ type="button"
|
|
|
+ @click="clearLocationIdFilter"
|
|
|
+ >
|
|
|
+ <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-action-icon">
|
|
|
+ <path
|
|
|
+ d="M4.22 4.22a.75.75 0 0 1 1.06 0L8 6.94l2.72-2.72a.75.75 0 1 1 1.06 1.06L9.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L8 9.06l-2.72 2.72a.75.75 0 1 1-1.06-1.06L6.94 8 4.22 5.28a.75.75 0 0 1 0-1.06z"
|
|
|
+ fill="currentColor"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ <button class="filter-confirm-btn" type="button" @click="applyLocationIdFilter">
|
|
|
+ <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-action-icon">
|
|
|
<path
|
|
|
d="M6.5 2.5a4 4 0 1 0 2.47 7.15l2.69 2.68 1.06-1.06-2.68-2.69A4 4 0 0 0 6.5 2.5zm0 1.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5z"
|
|
|
fill="currentColor"
|
|
|
@@ -69,23 +114,64 @@
|
|
|
</span>
|
|
|
</label>
|
|
|
<label class="toggle-item">
|
|
|
- <span class="selector-label">库位组边框</span>
|
|
|
+ <span class="selector-label">边框</span>
|
|
|
<input v-model="showGroupBorder" class="toggle-input" type="checkbox" />
|
|
|
<span class="toggle-track">
|
|
|
<span class="toggle-thumb"></span>
|
|
|
</span>
|
|
|
</label>
|
|
|
- <label class="level-selector">
|
|
|
- <span class="selector-label">楼层</span>
|
|
|
- <select v-model.number="currentLevel" class="level-select" @change="handleLevelChange">
|
|
|
- <option v-for="level in levelRange" :key="level" :value="level">
|
|
|
- {{ level }}层
|
|
|
- </option>
|
|
|
- </select>
|
|
|
+ <label class="toggle-item">
|
|
|
+ <span class="selector-label">卡片</span>
|
|
|
+ <input v-model="showTooltip" class="toggle-input" type="checkbox" />
|
|
|
+ <span class="toggle-track">
|
|
|
+ <span class="toggle-thumb"></span>
|
|
|
+ </span>
|
|
|
</label>
|
|
|
- <button class="refresh-btn" :disabled="loading" @click="handleManualRefresh">
|
|
|
- {{ loading ? '刷新中...' : `刷新 ${refreshCountdownText}` }}
|
|
|
- </button>
|
|
|
+ <select
|
|
|
+ v-model.number="currentLevel"
|
|
|
+ class="level-select level-select-floor"
|
|
|
+ @change="handleLevelChange"
|
|
|
+ >
|
|
|
+ <option v-for="level in levelRange" :key="level" :value="level">
|
|
|
+ {{ level }}层
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ <div ref="refreshControlRef" class="refresh-control">
|
|
|
+ <button
|
|
|
+ class="refresh-btn"
|
|
|
+ :disabled="loading"
|
|
|
+ @click="handleManualRefresh"
|
|
|
+ @contextmenu.prevent="handleRefreshContextMenu"
|
|
|
+ >
|
|
|
+ {{ refreshCountdownText }}
|
|
|
+ </button>
|
|
|
+ <div
|
|
|
+ v-if="showRefreshPopover"
|
|
|
+ class="refresh-popover"
|
|
|
+ @click.stop
|
|
|
+ >
|
|
|
+ <input
|
|
|
+ ref="refreshIntervalInputRef"
|
|
|
+ v-model="refreshIntervalInput"
|
|
|
+ class="refresh-popover-input"
|
|
|
+ type="text"
|
|
|
+ inputmode="numeric"
|
|
|
+ @keydown.enter="applyRefreshInterval"
|
|
|
+ />
|
|
|
+ <button
|
|
|
+ class="refresh-popover-confirm"
|
|
|
+ type="button"
|
|
|
+ @click="applyRefreshInterval"
|
|
|
+ >
|
|
|
+ <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-action-icon">
|
|
|
+ <path
|
|
|
+ d="M6.46 11.03 3.43 8a.75.75 0 1 1 1.06-1.06l1.97 1.96 5.05-5.04A.75.75 0 0 1 12.57 4.9l-5.58 5.59a.75.75 0 0 1-1.06 0z"
|
|
|
+ fill="currentColor"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<button class="logout-btn" @click="handleLogout">退出</button>
|
|
|
</div>
|
|
|
</header>
|
|
|
@@ -103,8 +189,11 @@
|
|
|
:selected-location-attribute="selectedLocationAttribute"
|
|
|
:selected-has-container="selectedHasContainer"
|
|
|
:loc-group-keyword="appliedLocGroupKeyword"
|
|
|
+ :location-id-keyword="appliedLocationIdKeyword"
|
|
|
:show-group-border="showGroupBorder"
|
|
|
+ :show-tooltip="showTooltip"
|
|
|
@select-loc-group="handleSelectLocGroup"
|
|
|
+ @select-location-id="handleSelectLocationId"
|
|
|
/>
|
|
|
</div>
|
|
|
</main>
|
|
|
@@ -112,7 +201,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted, computed, onBeforeUnmount } from 'vue'
|
|
|
+import { ref, onMounted, computed, onBeforeUnmount, nextTick } from 'vue'
|
|
|
import { fetchLocationData } from './api/location'
|
|
|
import type { LocationAttributeCode, LocationResourceDataVO } from './types'
|
|
|
import WarehouseMap from './components/WarehouseMap.vue'
|
|
|
@@ -121,6 +210,7 @@ import { config } from './config'
|
|
|
import { isAuthenticated, removeToken } from './utils/auth'
|
|
|
|
|
|
const LEVEL_STORAGE_KEY = 'warehouse-map.current-level'
|
|
|
+const REFRESH_INTERVAL_STORAGE_KEY = 'warehouse-map.refresh-interval-ms'
|
|
|
const getInitialLevel = () => {
|
|
|
const savedLevel = window.localStorage.getItem(LEVEL_STORAGE_KEY)
|
|
|
const parsedLevel = savedLevel ? Number(savedLevel) : NaN
|
|
|
@@ -130,6 +220,15 @@ const getInitialLevel = () => {
|
|
|
return config.minLevel
|
|
|
}
|
|
|
|
|
|
+const getInitialRefreshInterval = () => {
|
|
|
+ const savedInterval = window.localStorage.getItem(REFRESH_INTERVAL_STORAGE_KEY)
|
|
|
+ const parsedInterval = savedInterval ? Number(savedInterval) : NaN
|
|
|
+ if (Number.isInteger(parsedInterval) && parsedInterval > 0) {
|
|
|
+ return parsedInterval
|
|
|
+ }
|
|
|
+ return config.refreshInterval
|
|
|
+}
|
|
|
+
|
|
|
const currentLevel = ref(getInitialLevel())
|
|
|
const locations = ref<LocationResourceDataVO[]>([])
|
|
|
const loading = ref(false)
|
|
|
@@ -140,9 +239,17 @@ const selectedLocationAttribute = ref<LocationAttributeCode | ''>('')
|
|
|
const selectedHasContainer = ref<'Y' | 'N' | ''>('')
|
|
|
const locGroupKeywordInput = ref('')
|
|
|
const appliedLocGroupKeyword = ref('')
|
|
|
+const locationIdKeywordInput = ref('')
|
|
|
+const appliedLocationIdKeyword = ref('')
|
|
|
const showGroupBorder = ref(false)
|
|
|
+const showTooltip = ref(true)
|
|
|
+const refreshIntervalMs = ref(getInitialRefreshInterval())
|
|
|
+const showRefreshPopover = ref(false)
|
|
|
+const refreshIntervalInput = ref(String(Math.max(Math.floor(refreshIntervalMs.value / 1000), 1)))
|
|
|
const now = ref(Date.now())
|
|
|
-const nextRefreshAt = ref(Date.now() + config.refreshInterval)
|
|
|
+const nextRefreshAt = ref(Date.now() + refreshIntervalMs.value)
|
|
|
+const refreshControlRef = ref<HTMLElement | null>(null)
|
|
|
+const refreshIntervalInputRef = ref<HTMLInputElement | null>(null)
|
|
|
let refreshTimer: number | null = null
|
|
|
let countdownTimer: number | null = null
|
|
|
|
|
|
@@ -173,10 +280,45 @@ const getLocationAttributeLabel = (attribute: LocationAttributeCode) => {
|
|
|
return LOCATION_ATTRIBUTE_LABEL_MAP[attribute] || attribute
|
|
|
}
|
|
|
|
|
|
+const copyText = async (text: string) => {
|
|
|
+ if (!text) return
|
|
|
+
|
|
|
+ try {
|
|
|
+ await navigator.clipboard.writeText(text)
|
|
|
+ return
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('Clipboard API copy failed, fallback to execCommand.', error)
|
|
|
+ }
|
|
|
+
|
|
|
+ const textarea = document.createElement('textarea')
|
|
|
+ textarea.value = text
|
|
|
+ textarea.setAttribute('readonly', 'true')
|
|
|
+ textarea.style.position = 'fixed'
|
|
|
+ textarea.style.top = '-9999px'
|
|
|
+ document.body.appendChild(textarea)
|
|
|
+ textarea.select()
|
|
|
+ document.execCommand('copy')
|
|
|
+ document.body.removeChild(textarea)
|
|
|
+}
|
|
|
+
|
|
|
const applyLocGroupFilter = () => {
|
|
|
appliedLocGroupKeyword.value = locGroupKeywordInput.value.trim()
|
|
|
}
|
|
|
|
|
|
+const applyLocationIdFilter = () => {
|
|
|
+ appliedLocationIdKeyword.value = locationIdKeywordInput.value.trim()
|
|
|
+}
|
|
|
+
|
|
|
+const clearLocGroupFilter = () => {
|
|
|
+ locGroupKeywordInput.value = ''
|
|
|
+ appliedLocGroupKeyword.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+const clearLocationIdFilter = () => {
|
|
|
+ locationIdKeywordInput.value = ''
|
|
|
+ appliedLocationIdKeyword.value = ''
|
|
|
+}
|
|
|
+
|
|
|
const refreshCountdownText = computed(() => {
|
|
|
const remainMs = Math.max(nextRefreshAt.value - now.value, 0)
|
|
|
const totalSeconds = Math.floor(remainMs / 1000)
|
|
|
@@ -211,11 +353,11 @@ const scheduleNextRefresh = () => {
|
|
|
if (refreshTimer !== null) {
|
|
|
window.clearTimeout(refreshTimer)
|
|
|
}
|
|
|
- nextRefreshAt.value = Date.now() + config.refreshInterval
|
|
|
+ nextRefreshAt.value = Date.now() + refreshIntervalMs.value
|
|
|
refreshTimer = window.setTimeout(async () => {
|
|
|
await loadLocationData()
|
|
|
scheduleNextRefresh()
|
|
|
- }, config.refreshInterval)
|
|
|
+ }, refreshIntervalMs.value)
|
|
|
}
|
|
|
|
|
|
const handleLevelChange = () => {
|
|
|
@@ -229,13 +371,48 @@ const handleManualRefresh = () => {
|
|
|
scheduleNextRefresh()
|
|
|
}
|
|
|
|
|
|
+const applyRefreshInterval = () => {
|
|
|
+ const nextSeconds = Number(refreshIntervalInput.value.trim())
|
|
|
+ if (!Number.isInteger(nextSeconds) || nextSeconds <= 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ refreshIntervalMs.value = nextSeconds * 1000
|
|
|
+ refreshIntervalInput.value = String(nextSeconds)
|
|
|
+ window.localStorage.setItem(REFRESH_INTERVAL_STORAGE_KEY, String(refreshIntervalMs.value))
|
|
|
+ showRefreshPopover.value = false
|
|
|
+ scheduleNextRefresh()
|
|
|
+}
|
|
|
+
|
|
|
+const handleRefreshContextMenu = async () => {
|
|
|
+ refreshIntervalInput.value = String(Math.max(Math.floor(refreshIntervalMs.value / 1000), 1))
|
|
|
+ showRefreshPopover.value = true
|
|
|
+ await nextTick()
|
|
|
+ refreshIntervalInputRef.value?.focus()
|
|
|
+ refreshIntervalInputRef.value?.select()
|
|
|
+}
|
|
|
+
|
|
|
+const handleDocumentClick = (event: MouseEvent) => {
|
|
|
+ if (!showRefreshPopover.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const target = event.target as Node | null
|
|
|
+ if (target && refreshControlRef.value?.contains(target)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ showRefreshPopover.value = false
|
|
|
+}
|
|
|
+
|
|
|
const handleLoginSuccess = () => {
|
|
|
showLoginModal.value = false
|
|
|
loadLocationData()
|
|
|
scheduleNextRefresh()
|
|
|
}
|
|
|
|
|
|
-const handleSelectLocGroup = (locGroup1: string) => {
|
|
|
+const handleSelectLocGroup = async (locGroup1: string) => {
|
|
|
+ await copyText(locGroup1)
|
|
|
if (appliedLocGroupKeyword.value === locGroup1) {
|
|
|
locGroupKeywordInput.value = ''
|
|
|
appliedLocGroupKeyword.value = ''
|
|
|
@@ -244,6 +421,22 @@ const handleSelectLocGroup = (locGroup1: string) => {
|
|
|
|
|
|
locGroupKeywordInput.value = locGroup1
|
|
|
appliedLocGroupKeyword.value = locGroup1
|
|
|
+ locationIdKeywordInput.value = ''
|
|
|
+ appliedLocationIdKeyword.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+const handleSelectLocationId = async (locationId: string) => {
|
|
|
+ await copyText(locationId)
|
|
|
+ if (appliedLocationIdKeyword.value === locationId) {
|
|
|
+ locationIdKeywordInput.value = ''
|
|
|
+ appliedLocationIdKeyword.value = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ locationIdKeywordInput.value = locationId
|
|
|
+ appliedLocationIdKeyword.value = locationId
|
|
|
+ locGroupKeywordInput.value = ''
|
|
|
+ appliedLocGroupKeyword.value = ''
|
|
|
}
|
|
|
|
|
|
const handleLoginCancel = () => {
|
|
|
@@ -264,6 +457,7 @@ onMounted(() => {
|
|
|
countdownTimer = window.setInterval(() => {
|
|
|
now.value = Date.now()
|
|
|
}, 1000)
|
|
|
+ document.addEventListener('mousedown', handleDocumentClick)
|
|
|
|
|
|
if (!isAuthenticated()) {
|
|
|
showLoginModal.value = true
|
|
|
@@ -280,6 +474,7 @@ onBeforeUnmount(() => {
|
|
|
if (countdownTimer !== null) {
|
|
|
window.clearInterval(countdownTimer)
|
|
|
}
|
|
|
+ document.removeEventListener('mousedown', handleDocumentClick)
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
@@ -356,17 +551,7 @@ onBeforeUnmount(() => {
|
|
|
.controls {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.warehouse-label {
|
|
|
- font-size: 12px;
|
|
|
- color: #dce8f3;
|
|
|
- padding: 5px 10px;
|
|
|
- background: rgba(111, 140, 167, 0.14);
|
|
|
- border: 1px solid rgba(111, 140, 167, 0.6);
|
|
|
- border-radius: 4px;
|
|
|
- line-height: 1;
|
|
|
+ gap: 6px;
|
|
|
}
|
|
|
|
|
|
.level-selector {
|
|
|
@@ -379,18 +564,18 @@ onBeforeUnmount(() => {
|
|
|
.filter-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 6px;
|
|
|
+ gap: 4px;
|
|
|
color: #d4e2f2;
|
|
|
}
|
|
|
|
|
|
.filter-input-item {
|
|
|
- min-width: 180px;
|
|
|
+ min-width: auto;
|
|
|
}
|
|
|
|
|
|
.toggle-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 6px;
|
|
|
+ gap: 4px;
|
|
|
color: #d4e2f2;
|
|
|
}
|
|
|
|
|
|
@@ -400,7 +585,7 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
|
|
|
|
.level-select {
|
|
|
- min-width: 82px;
|
|
|
+ min-width: 76px;
|
|
|
padding: 6px 24px 6px 8px;
|
|
|
background: rgba(255, 255, 255, 0.04);
|
|
|
border: 1px solid rgba(111, 140, 167, 0.4);
|
|
|
@@ -422,16 +607,20 @@ onBeforeUnmount(() => {
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
+.level-select-floor {
|
|
|
+ min-width: 64px;
|
|
|
+}
|
|
|
+
|
|
|
.filter-input-wrap {
|
|
|
position: relative;
|
|
|
display: inline-flex;
|
|
|
- width: 110px;
|
|
|
+ width: 136px;
|
|
|
}
|
|
|
|
|
|
.filter-input {
|
|
|
width: 100%;
|
|
|
padding: 6px 8px;
|
|
|
- padding-right: 28px;
|
|
|
+ padding-right: 50px;
|
|
|
background: rgba(255, 255, 255, 0.04);
|
|
|
border: 1px solid rgba(111, 140, 167, 0.4);
|
|
|
color: #eef4f8;
|
|
|
@@ -468,11 +657,28 @@ onBeforeUnmount(() => {
|
|
|
border-radius: 0 3px 3px 0;
|
|
|
}
|
|
|
|
|
|
+.filter-clear-btn {
|
|
|
+ position: absolute;
|
|
|
+ top: 1px;
|
|
|
+ right: 25px;
|
|
|
+ width: 24px;
|
|
|
+ height: calc(100% - 2px);
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border: none;
|
|
|
+ border-left: 1px solid rgba(111, 140, 167, 0.3);
|
|
|
+ background: transparent;
|
|
|
+ color: #dce8f3;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-clear-btn:hover,
|
|
|
.filter-confirm-btn:hover {
|
|
|
background: rgba(111, 140, 167, 0.14);
|
|
|
}
|
|
|
|
|
|
-.filter-confirm-icon {
|
|
|
+.filter-action-icon {
|
|
|
width: 12px;
|
|
|
height: 12px;
|
|
|
}
|
|
|
@@ -521,7 +727,8 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
|
|
|
|
.refresh-btn {
|
|
|
- padding: 6px 10px;
|
|
|
+ min-width: 58px;
|
|
|
+ padding: 6px 8px;
|
|
|
background: rgba(111, 140, 167, 0.12);
|
|
|
border: 1px solid rgba(111, 140, 167, 0.5);
|
|
|
color: #e6edf3;
|
|
|
@@ -532,6 +739,60 @@ onBeforeUnmount(() => {
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
+.refresh-control {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-popover {
|
|
|
+ position: absolute;
|
|
|
+ top: calc(100% + 6px);
|
|
|
+ right: 0;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 4px;
|
|
|
+ background: rgba(20, 29, 37, 0.96);
|
|
|
+ border: 1px solid rgba(111, 140, 167, 0.4);
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0 8px 18px rgba(0, 0, 0, 0.24);
|
|
|
+ z-index: 20;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-popover-input {
|
|
|
+ width: 56px;
|
|
|
+ padding: 6px 8px;
|
|
|
+ background: rgba(255, 255, 255, 0.04);
|
|
|
+ border: 1px solid rgba(111, 140, 167, 0.4);
|
|
|
+ color: #eef4f8;
|
|
|
+ font-size: 12px;
|
|
|
+ border-radius: 4px;
|
|
|
+ outline: none;
|
|
|
+ line-height: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-popover-input:focus {
|
|
|
+ background: rgba(111, 140, 167, 0.12);
|
|
|
+ border-color: #7a96af;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-popover-confirm {
|
|
|
+ width: 24px;
|
|
|
+ height: 28px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border: 1px solid rgba(111, 140, 167, 0.3);
|
|
|
+ background: rgba(255, 255, 255, 0.04);
|
|
|
+ color: #dce8f3;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-popover-confirm:hover {
|
|
|
+ background: rgba(111, 140, 167, 0.14);
|
|
|
+ border-color: #7a96af;
|
|
|
+}
|
|
|
+
|
|
|
.refresh-btn:hover:not(:disabled) {
|
|
|
background: rgba(111, 140, 167, 0.2);
|
|
|
border-color: #7a96af;
|