|
|
@@ -143,9 +143,24 @@
|
|
|
</q-td>
|
|
|
</template>
|
|
|
<template v-else-if="props.row.id !== editid">
|
|
|
- <q-td key="current_location" :props="props">{{
|
|
|
- props.row.current_location
|
|
|
- }}</q-td>
|
|
|
+ <q-td key="current_location" :props="props">
|
|
|
+ <div class="row items-center no-wrap">
|
|
|
+ <div class="col">{{ props.row.current_location }}</div>
|
|
|
+ <q-btn
|
|
|
+ v-if="props.row.current_location && props.row.current_location !== 'N/A' && String(props.row.current_location).startsWith('W')"
|
|
|
+ flat
|
|
|
+ dense
|
|
|
+ round
|
|
|
+ size="sm"
|
|
|
+ icon="link"
|
|
|
+ color="primary"
|
|
|
+ @click="openLocationBindDialog(props.row)"
|
|
|
+ class="q-ml-xs"
|
|
|
+ >
|
|
|
+ <q-tooltip>库位绑定</q-tooltip>
|
|
|
+ </q-btn>
|
|
|
+ </div>
|
|
|
+ </q-td>
|
|
|
</template>
|
|
|
|
|
|
<template v-if="props.row.id === editid">
|
|
|
@@ -329,6 +344,219 @@
|
|
|
</q-card-section>
|
|
|
</q-card>
|
|
|
</q-dialog>
|
|
|
+
|
|
|
+ <!-- 库位绑定对话框 -->
|
|
|
+ <q-dialog v-model="locationBindDialog" persistent>
|
|
|
+ <q-card style="min-width: 600px">
|
|
|
+ <q-card-section class="row items-center q-pb-none">
|
|
|
+ <div class="text-h6">库位绑定管理</div>
|
|
|
+ <q-space />
|
|
|
+ <q-btn icon="close" flat round dense v-close-popup />
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-card-section v-if="locationBindData">
|
|
|
+ <q-inner-loading :showing="locationBindData.loading">
|
|
|
+ <q-spinner color="primary" size="50px" />
|
|
|
+ </q-inner-loading>
|
|
|
+
|
|
|
+ <div v-if="!locationBindData.loading">
|
|
|
+ <div class="row q-mb-md">
|
|
|
+ <div class="col-6">
|
|
|
+ <div class="text-caption text-grey">库位编码</div>
|
|
|
+ <div class="text-body1">{{ locationBindData.location_code || '-' }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="col-3">
|
|
|
+ <div class="text-caption text-grey">层/行/列</div>
|
|
|
+ <div class="text-body1">
|
|
|
+ {{ locationBindData.layer || '-' }}/{{ locationBindData.row || '-' }}/{{ locationBindData.col || '-' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="col-3">
|
|
|
+ <div class="text-caption text-grey">库位状态</div>
|
|
|
+ <div class="text-body1">{{ locationBindData.status || '-' }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-separator class="q-my-md" />
|
|
|
+
|
|
|
+ <div class="q-mb-md">
|
|
|
+ <div class="text-subtitle2 q-mb-sm">已绑定的托盘</div>
|
|
|
+ <div v-if="locationBindData.bound_containers && locationBindData.bound_containers.length > 0">
|
|
|
+ <q-list bordered>
|
|
|
+ <q-item
|
|
|
+ v-for="(container, index) in locationBindData.bound_containers"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label>
|
|
|
+ 托盘码: {{ container.container_code }}
|
|
|
+ <q-badge
|
|
|
+ v-if="isSameContainerCode(container.container_code, locationBindData.current_container_code)"
|
|
|
+ color="green"
|
|
|
+ class="q-ml-sm"
|
|
|
+ >
|
|
|
+ 当前托盘
|
|
|
+ </q-badge>
|
|
|
+ <q-badge
|
|
|
+ v-else-if="locationBindData.current_container_code && !isSameContainerCode(container.container_code, locationBindData.current_container_code)"
|
|
|
+ color="orange"
|
|
|
+ class="q-ml-sm"
|
|
|
+ >
|
|
|
+ 不一致
|
|
|
+ </q-badge>
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption>绑定时间: {{ formatDateTime(container.put_time) }}</q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+ <q-item-section side>
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ dense
|
|
|
+ round
|
|
|
+ icon="link_off"
|
|
|
+ color="negative"
|
|
|
+ @click="handleUnbindContainer(container.container_code)"
|
|
|
+ :loading="container.unbinding"
|
|
|
+ :disable="bindingContainer"
|
|
|
+ >
|
|
|
+ <q-tooltip>解除绑定</q-tooltip>
|
|
|
+ </q-btn>
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
+ </div>
|
|
|
+ <div v-else class="text-grey text-caption">该库位未绑定托盘</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="q-mb-md">
|
|
|
+ <div class="text-subtitle2 q-mb-sm">当前托盘信息(从托盘列表)</div>
|
|
|
+ <div class="text-body2">
|
|
|
+ 托盘码: <strong>{{ locationBindData.current_container_code || '-' }}</strong>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-separator class="q-my-md" />
|
|
|
+
|
|
|
+ <div v-if="locationBindData.needs_bind" class="q-mb-md">
|
|
|
+ <q-banner
|
|
|
+ rounded
|
|
|
+ dense
|
|
|
+ class="bg-orange-1 text-orange-9"
|
|
|
+ >
|
|
|
+ <template v-slot:avatar>
|
|
|
+ <q-icon name="warning" color="orange" />
|
|
|
+ </template>
|
|
|
+ <div class="text-weight-medium q-mb-xs">绑定关系不一致</div>
|
|
|
+ <div class="text-body2">
|
|
|
+ {{ locationBindData.bind_reason || '库位绑定关系与托盘列表不一致,请确认是否需要重新绑定。' }}
|
|
|
+ </div>
|
|
|
+ </q-banner>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else class="q-mb-md">
|
|
|
+ <q-banner
|
|
|
+ rounded
|
|
|
+ dense
|
|
|
+ class="bg-green-1 text-green-9"
|
|
|
+ >
|
|
|
+ <template v-slot:avatar>
|
|
|
+ <q-icon name="check_circle" color="green" />
|
|
|
+ </template>
|
|
|
+ <div class="text-body2">绑定关系一致</div>
|
|
|
+ </q-banner>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 绑定操作按钮区域 -->
|
|
|
+ <div class="q-mt-md">
|
|
|
+ <div class="row q-gutter-sm">
|
|
|
+ <!-- 始终显示绑定按钮(即使没有绑定关系) -->
|
|
|
+ <div
|
|
|
+ v-if="locationBindData.current_container_code"
|
|
|
+ class="col"
|
|
|
+ >
|
|
|
+ <q-btn
|
|
|
+ color="primary"
|
|
|
+ :label="locationBindData.bound_containers && locationBindData.bound_containers.length > 0 && locationBindData.bound_containers.some(c => isSameContainerCode(c.container_code, locationBindData.current_container_code)) ? '重新绑定当前托盘' : '绑定当前托盘'"
|
|
|
+ icon="link"
|
|
|
+ @click="handleBindContainer"
|
|
|
+ :loading="bindingContainer"
|
|
|
+ class="full-width"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <!-- 如果有已绑定的托盘,显示解除所有绑定按钮 -->
|
|
|
+ <div
|
|
|
+ v-if="locationBindData.bound_containers && locationBindData.bound_containers.length > 0"
|
|
|
+ class="col"
|
|
|
+ >
|
|
|
+ <q-btn
|
|
|
+ color="negative"
|
|
|
+ label="解除所有绑定"
|
|
|
+ icon="link_off"
|
|
|
+ @click="handleUnbindAllContainers"
|
|
|
+ :loading="unbindingAll"
|
|
|
+ class="full-width"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-card-actions align="right">
|
|
|
+ <q-btn flat label="关闭" v-close-popup />
|
|
|
+ </q-card-actions>
|
|
|
+ </q-card>
|
|
|
+ </q-dialog>
|
|
|
+
|
|
|
+ <!-- 新增托盘对话框 -->
|
|
|
+ <q-dialog v-model="addContainerDialog" persistent>
|
|
|
+ <q-card style="min-width: 400px">
|
|
|
+ <q-card-section class="row items-center q-pb-none">
|
|
|
+ <div class="text-h6">新增托盘</div>
|
|
|
+ <q-space />
|
|
|
+ <q-btn icon="close" flat round dense v-close-popup />
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-card-section>
|
|
|
+ <q-input
|
|
|
+ v-model="newContainerCode"
|
|
|
+ label="托盘编码(5位)"
|
|
|
+ outlined
|
|
|
+ :rules="[
|
|
|
+ val => (val && val.length === 5) || '托盘编码必须为5位',
|
|
|
+ val => /^[A-Za-z0-9]+$/.test(val) || '托盘编码只能包含字母和数字'
|
|
|
+ ]"
|
|
|
+ maxlength="5"
|
|
|
+ @input="checkContainerCodeExists"
|
|
|
+ :loading="checkingContainerCode"
|
|
|
+ >
|
|
|
+ <template v-slot:hint>
|
|
|
+ <div v-if="containerCodeCheckResult === 'exists'" class="text-negative">
|
|
|
+ 该托盘编码已存在
|
|
|
+ </div>
|
|
|
+ <div v-else-if="containerCodeCheckResult === 'available'" class="text-positive">
|
|
|
+ 该托盘编码可以使用
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </q-input>
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-card-actions align="right">
|
|
|
+ <q-btn flat label="取消" v-close-popup @click="resetAddDialog" />
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ label="确定"
|
|
|
+ color="primary"
|
|
|
+ @click="confirmAddContainer"
|
|
|
+ :disable="!newContainerCode || newContainerCode.length !== 5 || containerCodeCheckResult === 'exists' || checkingContainerCode"
|
|
|
+ :loading="addingContainer"
|
|
|
+ />
|
|
|
+ </q-card-actions>
|
|
|
+ </q-card>
|
|
|
+ </q-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -461,12 +689,21 @@ export default {
|
|
|
activeSearchField: '',
|
|
|
activeSearchLabel: '',
|
|
|
userComponentPermissions: [], // 用户权限
|
|
|
- login_mode: LocalStorage.getItem('login_mode') // 登录模式
|
|
|
+ login_mode: LocalStorage.getItem('login_mode'), // 登录模式
|
|
|
+ locationBindDialog: false,
|
|
|
+ locationBindData: null,
|
|
|
+ bindingContainer: false,
|
|
|
+ // 新增托盘对话框相关
|
|
|
+ addContainerDialog: false,
|
|
|
+ newContainerCode: '',
|
|
|
+ containerCodeCheckResult: '', // 'exists' | 'available' | ''
|
|
|
+ checkingContainerCode: false,
|
|
|
+ addingContainer: false
|
|
|
}
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
- // 检查用户是否有指定页面的组件访问权限
|
|
|
+ // 检查用户是否有指定页面的组件访问权限
|
|
|
loadUserPermissions () {
|
|
|
postauth('staff/role-comPermissions/' + this.login_mode + '/', {
|
|
|
page: '/container/containerlist'
|
|
|
@@ -695,22 +932,100 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
add () {
|
|
|
- postauth('/container/list/', {})
|
|
|
- .then((res) => {
|
|
|
- this.$q.notify({
|
|
|
- message: '添加成功',
|
|
|
- icon: 'done',
|
|
|
- color: 'positive'
|
|
|
- })
|
|
|
- this.reFresh()
|
|
|
+ this.resetAddDialog()
|
|
|
+ this.addContainerDialog = true
|
|
|
+ },
|
|
|
+ // 重置新增对话框
|
|
|
+ resetAddDialog () {
|
|
|
+ this.newContainerCode = ''
|
|
|
+ this.containerCodeCheckResult = ''
|
|
|
+ this.checkingContainerCode = false
|
|
|
+ },
|
|
|
+ // 校验托盘码是否存在
|
|
|
+ async checkContainerCodeExists () {
|
|
|
+ // 清空之前的校验结果
|
|
|
+ this.containerCodeCheckResult = ''
|
|
|
+
|
|
|
+ // 如果托盘码不是5位,不进行校验
|
|
|
+ if (!this.newContainerCode || this.newContainerCode.length !== 5) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.checkingContainerCode = true
|
|
|
+ try {
|
|
|
+ // 查询是否已存在该托盘码
|
|
|
+ const url = `${this.pathname}?container_code=${encodeURIComponent(this.newContainerCode)}`
|
|
|
+ const response = await getauth(url)
|
|
|
+
|
|
|
+ if (response && response.results && response.results.length > 0) {
|
|
|
+ this.containerCodeCheckResult = 'exists'
|
|
|
+ } else {
|
|
|
+ this.containerCodeCheckResult = 'available'
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('校验托盘码失败:', error)
|
|
|
+ // 校验失败时不清除结果,让用户知道需要重试
|
|
|
+ } finally {
|
|
|
+ this.checkingContainerCode = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 确认新增托盘
|
|
|
+ async confirmAddContainer () {
|
|
|
+ // 最终校验
|
|
|
+ if (!this.newContainerCode || this.newContainerCode.length !== 5) {
|
|
|
+ this.$q.notify({
|
|
|
+ message: '托盘编码必须为5位',
|
|
|
+ icon: 'close',
|
|
|
+ color: 'negative'
|
|
|
})
|
|
|
- .catch((err) => {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果校验结果不明确,再次校验
|
|
|
+ if (this.containerCodeCheckResult !== 'available') {
|
|
|
+ await this.checkContainerCodeExists()
|
|
|
+ if (this.containerCodeCheckResult === 'exists') {
|
|
|
this.$q.notify({
|
|
|
- message: err.detail,
|
|
|
+ message: '该托盘编码已存在,请使用其他编码',
|
|
|
icon: 'close',
|
|
|
color: 'negative'
|
|
|
})
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.containerCodeCheckResult !== 'available') {
|
|
|
+ this.$q.notify({
|
|
|
+ message: '请稍候,正在校验托盘编码...',
|
|
|
+ icon: 'info',
|
|
|
+ color: 'info'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.addingContainer = true
|
|
|
+ try {
|
|
|
+ await postauth('/container/list/', {
|
|
|
+ container_code: this.newContainerCode
|
|
|
+ })
|
|
|
+
|
|
|
+ this.$q.notify({
|
|
|
+ message: '添加成功',
|
|
|
+ icon: 'done',
|
|
|
+ color: 'positive'
|
|
|
+ })
|
|
|
+ this.addContainerDialog = false
|
|
|
+ this.resetAddDialog()
|
|
|
+ this.reFresh()
|
|
|
+ } catch (err) {
|
|
|
+ const errorMessage = err.detail || err.message || '添加失败'
|
|
|
+ this.$q.notify({
|
|
|
+ message: errorMessage,
|
|
|
+ icon: 'close',
|
|
|
+ color: 'negative'
|
|
|
})
|
|
|
+ } finally {
|
|
|
+ this.addingContainer = false
|
|
|
+ }
|
|
|
},
|
|
|
check_container () {
|
|
|
var _this = this
|
|
|
@@ -921,11 +1236,348 @@ export default {
|
|
|
color: 'negative'
|
|
|
})
|
|
|
})
|
|
|
+ },
|
|
|
+ // 打开库位绑定对话框
|
|
|
+ async openLocationBindDialog (row) {
|
|
|
+ if (!row || !row.current_location || row.current_location === 'N/A') {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '该托盘没有当前位置信息'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const locationCode = row.current_location
|
|
|
+ this.locationBindDialog = true
|
|
|
+ this.locationBindData = {
|
|
|
+ location_code: locationCode,
|
|
|
+ current_container_code: row.container_code,
|
|
|
+ loading: true
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 查询库位信息
|
|
|
+ // await this.queryLocationInfo(locationCode)
|
|
|
+ // 查询库位绑定的托盘
|
|
|
+ await this.queryLocationContainers(locationCode)
|
|
|
+ // 检查是否需要绑定
|
|
|
+ this.checkBindingStatus()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询库位信息失败:', error)
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '查询库位信息失败: ' + (error?.message || '未知错误')
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ this.locationBindData.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 查询库位信息
|
|
|
+ async queryLocationInfo (locationCode) {
|
|
|
+ try {
|
|
|
+ // 从库位列表查询库位信息
|
|
|
+ const url = `bin/?location_code=${encodeURIComponent(locationCode)}`
|
|
|
+ const response = await getauth(url)
|
|
|
+
|
|
|
+ if (response && response.results && response.results.length > 0) {
|
|
|
+ const location = response.results[0]
|
|
|
+
|
|
|
+ this.locationBindData = {
|
|
|
+ ...this.locationBindData,
|
|
|
+ location_code: location.location_code,
|
|
|
+ row: location.row,
|
|
|
+ col: location.col,
|
|
|
+ layer: location.layer,
|
|
|
+ status: location.status,
|
|
|
+ location_type: location.location_type
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果查询不到,使用基本信息
|
|
|
+ this.locationBindData = {
|
|
|
+ ...this.locationBindData,
|
|
|
+ location_code: locationCode
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询库位信息失败:', error)
|
|
|
+ // 即使查询失败,也保留基本信息
|
|
|
+ this.locationBindData = {
|
|
|
+ ...this.locationBindData,
|
|
|
+ location_code: locationCode
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 查询库位绑定的托盘(使用新的 link API)
|
|
|
+ async queryLocationContainers (locationCode) {
|
|
|
+ try {
|
|
|
+ // 使用新的 link API 查询库位关联的托盘
|
|
|
+ const url = `bin/link/?location_code=${encodeURIComponent(locationCode)}&is_active=true`
|
|
|
+ const response = await getauth(url)
|
|
|
+
|
|
|
+ const boundContainers = []
|
|
|
+ if (response && response.results && Array.isArray(response.results)) {
|
|
|
+ response.results.forEach(link => {
|
|
|
+ if (link.is_active && link.container_code) {
|
|
|
+ boundContainers.push({
|
|
|
+ container_code: link.container_code,
|
|
|
+ put_time: link.put_time || link.create_time
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ this.locationBindData.bound_containers = boundContainers
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询库位关联托盘失败:', error)
|
|
|
+ this.locationBindData.bound_containers = []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 检查绑定状态
|
|
|
+ checkBindingStatus () {
|
|
|
+ const boundContainers = this.locationBindData.bound_containers || []
|
|
|
+ const currentContainerCode = this.locationBindData.current_container_code
|
|
|
+ const locationStatus = this.locationBindData.status
|
|
|
+
|
|
|
+ // 检查是否需要绑定
|
|
|
+ let needsBind = false
|
|
|
+ let bindReason = ''
|
|
|
+
|
|
|
+ // 如果没有绑定任何托盘,且当前托盘存在,需要绑定
|
|
|
+ if (boundContainers.length === 0 && currentContainerCode) {
|
|
|
+ needsBind = true
|
|
|
+ if (locationStatus === 'occupied' || locationStatus === 'reserved') {
|
|
|
+ bindReason = '库位状态为占用,但未绑定托盘'
|
|
|
+ } else {
|
|
|
+ bindReason = '库位未绑定托盘,需要绑定当前托盘'
|
|
|
+ }
|
|
|
+ } else if (locationStatus === 'occupied' || locationStatus === 'reserved') {
|
|
|
+ // 库位状态为占用,应该绑定托盘
|
|
|
+ if (boundContainers.length === 0) {
|
|
|
+ needsBind = true
|
|
|
+ bindReason = '库位状态为占用,但未绑定托盘'
|
|
|
+ } else if (currentContainerCode && !boundContainers.some(c => this.isSameContainerCode(c.container_code, currentContainerCode))) {
|
|
|
+ needsBind = true
|
|
|
+ bindReason = '库位绑定的托盘与当前托盘不一致'
|
|
|
+ }
|
|
|
+ } else if (locationStatus === 'available' && boundContainers.length > 0) {
|
|
|
+ // 库位状态为可用,但绑定了托盘
|
|
|
+ if (currentContainerCode && boundContainers.some(c => this.isSameContainerCode(c.container_code, currentContainerCode))) {
|
|
|
+ needsBind = true
|
|
|
+ bindReason = '库位状态为可用,但绑定了托盘'
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.locationBindData.needs_bind = needsBind
|
|
|
+ this.locationBindData.bind_reason = bindReason
|
|
|
+ },
|
|
|
+ // 绑定托盘
|
|
|
+ async handleBindContainer () {
|
|
|
+ if (!this.locationBindData || !this.locationBindData.location_code) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const locationCode = this.locationBindData.location_code
|
|
|
+ const containerCode = this.locationBindData.current_container_code
|
|
|
+
|
|
|
+ if (!containerCode) {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '无法获取托盘编码'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.bindingContainer = true
|
|
|
+ try {
|
|
|
+ const url = '/location_statistics/bind-container/'
|
|
|
+ const payload = {
|
|
|
+ location_code: locationCode,
|
|
|
+ container_code: containerCode
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await postauth(url, payload)
|
|
|
+
|
|
|
+ if (response && (response.code === '200' || response.success)) {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'positive',
|
|
|
+ message: '绑定成功',
|
|
|
+ icon: 'check'
|
|
|
+ })
|
|
|
+ // 重新查询绑定关系和库位信息(确保状态已更新,参考任务结束后的逻辑)
|
|
|
+ await Promise.all([
|
|
|
+ this.queryLocationContainers(locationCode),
|
|
|
+ this.queryLocationInfo(locationCode)
|
|
|
+ ])
|
|
|
+ this.checkBindingStatus()
|
|
|
+ // 刷新列表
|
|
|
+ this.getSearchList()
|
|
|
+ } else {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: response?.msg || response?.message || '绑定失败'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('绑定失败:', error)
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '绑定失败: ' + (error?.message || error?.detail || '未知错误')
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ this.bindingContainer = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 解除单个托盘绑定
|
|
|
+ async handleUnbindContainer (containerCode) {
|
|
|
+ if (!this.locationBindData || !this.locationBindData.location_code) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const locationCode = this.locationBindData.location_code
|
|
|
+
|
|
|
+ // 确认对话框
|
|
|
+ this.$q
|
|
|
+ .dialog({
|
|
|
+ title: '确认解除绑定',
|
|
|
+ message: `确定要解除库位 ${locationCode} 与托盘 ${containerCode} 的绑定关系吗?`,
|
|
|
+ cancel: true,
|
|
|
+ persistent: true
|
|
|
+ })
|
|
|
+ .onOk(async () => {
|
|
|
+ // 找到对应的容器对象,设置 loading 状态
|
|
|
+ const container = this.locationBindData.bound_containers.find(
|
|
|
+ c => this.isSameContainerCode(c.container_code, containerCode)
|
|
|
+ )
|
|
|
+ if (container) {
|
|
|
+ this.$set(container, 'unbinding', true)
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const url = '/location_statistics/unbind-container/'
|
|
|
+ const payload = {
|
|
|
+ location_code: locationCode,
|
|
|
+ container_code: containerCode
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await postauth(url, payload)
|
|
|
+
|
|
|
+ if (response && (response.code === '200' || response.success)) {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'positive',
|
|
|
+ message: '解除绑定成功',
|
|
|
+ icon: 'check'
|
|
|
+ })
|
|
|
+ // 重新查询绑定关系和库位信息(确保状态已更新)
|
|
|
+ await Promise.all([
|
|
|
+ this.queryLocationContainers(locationCode),
|
|
|
+ this.queryLocationInfo(locationCode)
|
|
|
+ ])
|
|
|
+ this.checkBindingStatus()
|
|
|
+ // 刷新列表
|
|
|
+ this.getSearchList()
|
|
|
+ } else {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: response?.msg || response?.message || '解除绑定失败'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解除绑定失败:', error)
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '解除绑定失败: ' + (error?.message || error?.detail || '未知错误')
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ if (container) {
|
|
|
+ this.$set(container, 'unbinding', false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 解除所有绑定
|
|
|
+ async handleUnbindAllContainers () {
|
|
|
+ if (!this.locationBindData || !this.locationBindData.location_code) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const locationCode = this.locationBindData.location_code
|
|
|
+ const boundContainers = this.locationBindData.bound_containers || []
|
|
|
+
|
|
|
+ if (boundContainers.length === 0) {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'info',
|
|
|
+ message: '该库位没有绑定的托盘'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确认对话框
|
|
|
+ this.$q
|
|
|
+ .dialog({
|
|
|
+ title: '确认解除所有绑定',
|
|
|
+ message: `确定要解除库位 ${locationCode} 的所有托盘绑定关系吗?共 ${boundContainers.length} 个托盘。`,
|
|
|
+ cancel: true,
|
|
|
+ persistent: true
|
|
|
+ })
|
|
|
+ .onOk(async () => {
|
|
|
+ this.unbindingAll = true
|
|
|
+
|
|
|
+ try {
|
|
|
+ const url = '/location_statistics/unbind-container/'
|
|
|
+ const payload = {
|
|
|
+ location_code: locationCode
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await postauth(url, payload)
|
|
|
+
|
|
|
+ if (response && (response.code === '200' || response.success)) {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'positive',
|
|
|
+ message: '解除所有绑定成功',
|
|
|
+ icon: 'check'
|
|
|
+ })
|
|
|
+ // 重新查询绑定关系和库位信息(确保状态已更新)
|
|
|
+ await Promise.all([
|
|
|
+ this.queryLocationContainers(locationCode),
|
|
|
+ this.queryLocationInfo(locationCode)
|
|
|
+ ])
|
|
|
+ this.checkBindingStatus()
|
|
|
+ // 刷新列表
|
|
|
+ this.getSearchList()
|
|
|
+ } else {
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: response?.msg || response?.message || '解除绑定失败'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解除绑定失败:', error)
|
|
|
+ this.$q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: '解除绑定失败: ' + (error?.message || error?.detail || '未知错误')
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ this.unbindingAll = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 格式化时间
|
|
|
+ formatDateTime (dateStr) {
|
|
|
+ if (!dateStr) return '-'
|
|
|
+ if (typeof dateStr === 'string' && dateStr.includes('T')) {
|
|
|
+ return dateStr.replace('T', ' ').substring(0, 19)
|
|
|
+ }
|
|
|
+ return dateStr
|
|
|
+ },
|
|
|
+ // 比较容器码(处理类型不一致问题)
|
|
|
+ isSameContainerCode (code1, code2) {
|
|
|
+ if (!code1 || !code2) return false
|
|
|
+ return String(code1).trim() === String(code2).trim()
|
|
|
}
|
|
|
},
|
|
|
created () {
|
|
|
var _this = this
|
|
|
- _this.loadUserPermissions();
|
|
|
+ _this.loadUserPermissions()
|
|
|
if (LocalStorage.has('openid')) {
|
|
|
_this.openid = LocalStorage.getItem('openid')
|
|
|
} else {
|