handy hai 2 meses
pai
achega
364c0f0c9a
Modificáronse 2 ficheiros con 85 adicións e 10 borrados
  1. 76 10
      src/App.vue
  2. 9 0
      src/components/WarehouseMap.vue

+ 76 - 10
src/App.vue

@@ -42,12 +42,23 @@
         </label>
         <label class="filter-item filter-input-item">
           <span class="selector-label">库位组</span>
-          <input
-            v-model.trim="locGroupKeyword"
-            class="filter-input"
-            type="text"
-            placeholder="输入库位组"
-          />
+          <span class="filter-input-wrap">
+            <input
+              v-model="locGroupKeywordInput"
+              class="filter-input"
+              type="text"
+              placeholder="输入库位组"
+              @keydown.enter="applyLocGroupFilter"
+            />
+            <button class="filter-confirm-btn" type="button" @click="applyLocGroupFilter">
+              <svg viewBox="0 0 16 16" aria-hidden="true" class="filter-confirm-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="toggle-item">
           <span class="selector-label">库位组边框</span>
@@ -82,8 +93,9 @@
           :current-level="currentLevel"
           :selected-category="selectedCategory"
           :selected-location-attribute="selectedLocationAttribute"
-          :loc-group-keyword="locGroupKeyword"
+          :loc-group-keyword="appliedLocGroupKeyword"
           :show-group-border="showGroupBorder"
+          @select-loc-group="handleSelectLocGroup"
         />
       </div>
     </main>
@@ -99,14 +111,25 @@ import LoginModal from './components/LoginModal.vue'
 import { config } from './config'
 import { isAuthenticated, removeToken } from './utils/auth'
 
-const currentLevel = ref(1)
+const LEVEL_STORAGE_KEY = 'warehouse-map.current-level'
+const getInitialLevel = () => {
+  const savedLevel = window.localStorage.getItem(LEVEL_STORAGE_KEY)
+  const parsedLevel = savedLevel ? Number(savedLevel) : NaN
+  if (Number.isInteger(parsedLevel) && parsedLevel >= config.minLevel && parsedLevel <= config.maxLevel) {
+    return parsedLevel
+  }
+  return config.minLevel
+}
+
+const currentLevel = ref(getInitialLevel())
 const locations = ref<LocationResourceDataVO[]>([])
 const loading = ref(false)
 const error = ref('')
 const showLoginModal = ref(false)
 const selectedCategory = ref('')
 const selectedLocationAttribute = ref<LocationAttributeCode | ''>('')
-const locGroupKeyword = ref('')
+const locGroupKeywordInput = ref('')
+const appliedLocGroupKeyword = ref('')
 const showGroupBorder = ref(true)
 const now = ref(Date.now())
 const nextRefreshAt = ref(Date.now() + config.refreshInterval)
@@ -140,6 +163,10 @@ const getLocationAttributeLabel = (attribute: LocationAttributeCode) => {
   return LOCATION_ATTRIBUTE_LABEL_MAP[attribute] || attribute
 }
 
+const applyLocGroupFilter = () => {
+  appliedLocGroupKeyword.value = locGroupKeywordInput.value.trim()
+}
+
 const refreshCountdownText = computed(() => {
   const remainMs = Math.max(nextRefreshAt.value - now.value, 0)
   const totalSeconds = Math.floor(remainMs / 1000)
@@ -182,6 +209,7 @@ const scheduleNextRefresh = () => {
 }
 
 const handleLevelChange = () => {
+  window.localStorage.setItem(LEVEL_STORAGE_KEY, String(currentLevel.value))
   loadLocationData()
   scheduleNextRefresh()
 }
@@ -197,6 +225,11 @@ const handleLoginSuccess = () => {
   scheduleNextRefresh()
 }
 
+const handleSelectLocGroup = (locGroup1: string) => {
+  locGroupKeywordInput.value = locGroup1
+  appliedLocGroupKeyword.value = locGroup1
+}
+
 const handleLoginCancel = () => {
   // 不允许取消登录,必须登录才能使用
 }
@@ -373,9 +406,16 @@ onBeforeUnmount(() => {
   line-height: 1;
 }
 
+.filter-input-wrap {
+  position: relative;
+  display: inline-flex;
+  width: 110px;
+}
+
 .filter-input {
-  min-width: 110px;
+  width: 100%;
   padding: 6px 8px;
+  padding-right: 28px;
   background: rgba(255, 255, 255, 0.04);
   border: 1px solid rgba(111, 140, 167, 0.4);
   color: #eef4f8;
@@ -395,6 +435,32 @@ onBeforeUnmount(() => {
   border-color: #7a96af;
 }
 
+.filter-confirm-btn {
+  position: absolute;
+  top: 1px;
+  right: 1px;
+  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;
+  border-radius: 0 3px 3px 0;
+}
+
+.filter-confirm-btn:hover {
+  background: rgba(111, 140, 167, 0.14);
+}
+
+.filter-confirm-icon {
+  width: 12px;
+  height: 12px;
+}
+
 .toggle-input {
   position: absolute;
   opacity: 0;

+ 9 - 0
src/components/WarehouseMap.vue

@@ -9,6 +9,7 @@
           :class="['grid-cell', getCellClass(cell)]"
           :style="getCellStyle(cell)"
           :title="getCellTitle(cell)"
+          @click="handleCellClick(cell)"
         >
           <div v-if="cell && isCellMatched(cell)" class="cell-content">
             <div class="category-badge" :style="getCategoryStyle(cell)">
@@ -52,6 +53,9 @@ interface GridCell extends LocationResourceDataVO {
 }
 
 const props = defineProps<Props>()
+const emit = defineEmits<{
+  (event: 'select-loc-group', locGroup1: string): void
+}>()
 const mapWrapperRef = ref<HTMLElement | null>(null)
 const wrapperSize = ref({
   width: 0,
@@ -322,6 +326,11 @@ const getCellStyle = (cell: GridCell | null): CSSProperties => {
   }
 }
 
+const handleCellClick = (cell: GridCell | null) => {
+  if (!cell || !isCellMatched(cell)) return
+  emit('select-loc-group', cell.locGroup1)
+}
+
 const updateWrapperSize = () => {
   if (!mapWrapperRef.value) return
   wrapperSize.value = {