MobileStorageRelease.vue 4.92 KB
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'vuestic-ui'
import optionsApi from '../../services/options'
import storageLocationsApi from '../../services/storageLocations'

const emit = defineEmits<{
  (e: 'back'): void
}>()

const { t, locale } = useI18n()
const { init: notify } = useToast()

type SelectOption = { text: string; value: string }

const selectedLocationId = ref<string | null>(null)
const locationOptions = ref<SelectOption[]>([])
const isReleasing = ref(false)
const isLoadingLocations = ref(false)
const locationSelectRef = ref<any>(null)

const focusLocationSelect = async () => {
  await nextTick()
  if (typeof locationSelectRef.value?.focus === 'function') {
    locationSelectRef.value.focus()
    return
  }
  const nativeInput = locationSelectRef.value?.$el?.querySelector?.('input') as HTMLInputElement | null
  nativeInput?.focus()
}

const toggleLanguage = () => {
  locale.value = locale.value === 'cn' ? 'gb' : 'cn'
}

const backToHome = () => {
  emit('back')
}

const loadLocations = async () => {
  isLoadingLocations.value = true
  try {
    const res: any = await optionsApi.getLocations()
    const list = Array.isArray(res) ? res : res?.data || res?.Data || []
    locationOptions.value = list.map((item: any) => {
      const value = String(item.value ?? item.id ?? '')
      const name = String(item.locationName ?? item.LocationName ?? item.name ?? item.label ?? item.text ?? '').trim()
      const code = String(item.locationCode ?? item.LocationCode ?? item.code ?? '').trim()
      return {
        value,
        text: name || code || value,
      }
    })
  } catch (err: any) {
    notify({ message: err?.message || t('mobile.loadLocationsFailed'), color: 'danger' })
  } finally {
    isLoadingLocations.value = false
  }
}

const releaseLocation = async () => {
  const id = String(selectedLocationId.value ?? '').trim()
  if (!id) {
    notify({ message: t('mobile.releaseSelectRequired'), color: 'warning' })
    return
  }

  isReleasing.value = true
  try {
    const res = await storageLocationsApi.release(id)
    const successFlag = res?.Success ?? res?.success
    if (successFlag === false) {
      notify({ message: res?.Message || res?.message || t('mobile.releaseFailed'), color: 'danger' })
      return
    }

    notify({ message: t('mobile.releaseSuccess'), color: 'success' })
    selectedLocationId.value = null
    focusLocationSelect()
  } catch (err: any) {
    notify({ message: err?.message || t('mobile.releaseFailed'), color: 'danger' })
  } finally {
    isReleasing.value = false
  }
}

onMounted(() => {
  loadLocations()
  focusLocationSelect()
})
</script>

<template>
  <div class="mobile-release">
    <div class="mobile-release-header">
      <VaButton preset="secondary" icon="arrow_back" class="mobile-back-btn" @click="backToHome" />
      <h2 class="mobile-release-title">{{ t('mobile.releaseLocation') }}</h2>
      <VaButton preset="secondary" icon="translate" class="mobile-lang-btn" @click="toggleLanguage">
        {{ locale === 'cn' ? 'EN' : '中文' }}
      </VaButton>
    </div>

    <div class="mobile-release-content">
      <div class="mobile-form-group">
        <VaSelect
          ref="locationSelectRef"
          v-model="selectedLocationId"
          :label="t('mobile.locationSelectLabel')"
          :placeholder="t('mobile.locationSelectPlaceholder')"
          :options="locationOptions"
          text-by="text"
          value-by="value"
          track-by="value"
          :loading="isLoadingLocations"
          searchable
          clearable
          class="mobile-form-select"
        />
      </div>
    </div>

    <div class="mobile-release-footer">
      <VaButton color="danger" size="large" class="mobile-release-btn" :loading="isReleasing" @click="releaseLocation">
        {{ t('mobile.release') }}
      </VaButton>
    </div>
  </div>
</template>

<style scoped lang="scss">
.mobile-release {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: var(--va-background-primary);
}

.mobile-release-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px;
  border-bottom: 1px solid var(--va-background-border);
  background: var(--va-background-primary);
}

.mobile-release-title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0;
  color: var(--va-text-primary);
}

.mobile-release-content {
  flex: 1;
  padding: 20px 16px;
}

.mobile-form-group {
  margin-bottom: 20px;
}

.mobile-form-select {
  width: 100%;
}

.mobile-release-footer {
  padding: 16px;
  border-top: 1px solid var(--va-background-border);
  background: var(--va-background-primary);
}

.mobile-release-btn {
  width: 100%;
  height: 48px;
  font-size: 1rem;
  font-weight: 600;
}

.mobile-back-btn {
  min-width: 40px;
  width: 40px;
  height: 40px;
  padding: 0;
}

.mobile-back-btn-placeholder {
  min-width: 40px;
  width: 40px;
}

.mobile-lang-btn {
  min-width: 64px;
}
</style>