| | |
| | | }); |
| | | }; |
| | | |
| | | // è·åç©è设å¤å®æ¶æ°éæ°æ® |
| | | export const getIotRealtimeData = (id) => { |
| | | return request({ |
| | | url: `/stockInventory/iotRealtime/${id}`, |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // ç»å®ç©è设å¤å°åºå |
| | | export const bindIotDevice = (id, warehouse) => { |
| | | return request({ |
| | | url: `/stockInventory/bindIotDevice/${id}`, |
| | | method: "put", |
| | | data: { warehouse }, |
| | | }); |
| | | }; |
| | | |
| | |
| | | > |
| | | <el-option |
| | | v-for="person in employees" |
| | | :key="person.id" |
| | | :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`" |
| | | :value="person.id" |
| | | :key="person.userId" |
| | | :label="`${person.nickName || person.userName}${person.dept?.deptName ? ` (${person.dept.deptName})` : ''}`" |
| | | :value="person.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | import {ElMessage} from 'element-plus' |
| | | import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue' |
| | | import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | |
| | | // å½åç³è¯·ç±»å |
| | | const currentType = ref('department') // approval: å®¡æ¹æµç¨, department: é¨é¨çº§, notification: éç¥åå¸ |
| | |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data |
| | | }) |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || '')) |
| | | userListNoPageByTenantId().then(res => { |
| | | employees.value = res.data || [] |
| | | }) |
| | | }) |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="isShow" |
| | | title="ç»å®ç©è设å¤" |
| | | width="600" |
| | | @close="closeModal"> |
| | | <el-form label-width="100px" |
| | | :model="formState" |
| | | ref="formRef"> |
| | | <el-form-item label="产ååç§°"> |
| | | <el-input v-model="props.record.productName" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-input v-model="props.record.model" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹å·"> |
| | | <el-input v-model="props.record.batchNo" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="ç©è设å¤" |
| | | prop="deviceIds" |
| | | :rules="[ |
| | | { |
| | | required: false, |
| | | message: 'è¯·éæ©ç©è设å¤', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.deviceIds" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç©è设å¤" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in deviceOptions" |
| | | :key="item.id" |
| | | :label="`${item.deviceName} (${item.deviceModel})`" |
| | | :value="item.id"> |
| | | <span style="float: left">{{ item.deviceName }}</span> |
| | | <span style="float: right; color: #8492a6; font-size: 13px">{{ item.deviceModel }}</span> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确认</el-button> |
| | | <el-button @click="closeModal">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, getCurrentInstance } from "vue"; |
| | | import { getLedgerPage } from "@/api/equipmentManagement/ledger.js"; |
| | | import { bindIotDevice } from "@/api/inventoryManagement/stockInventory.js"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | record: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible", "completed"]); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const formRef = ref(null); |
| | | const submitLoading = ref(false); |
| | | const deviceOptions = ref([]); |
| | | |
| | | const formState = ref({ |
| | | deviceIds: [], |
| | | }); |
| | | |
| | | // è·åç©è设å¤å表 |
| | | const getDeviceOptions = async () => { |
| | | try { |
| | | const res = await getLedgerPage({ |
| | | isIotDevice: 1, |
| | | page: 1, |
| | | size: 999, |
| | | }); |
| | | if (res.data && res.data.records) { |
| | | deviceOptions.value = res.data.records; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç©è设å¤å表失败:", error); |
| | | proxy.$modal.msgError("è·åç©è设å¤å表失败"); |
| | | } |
| | | }; |
| | | |
| | | // åå§åå·²ç»å®è®¾å¤ |
| | | const initSelectedDevices = () => { |
| | | if (props.record.warehouse) { |
| | | // warehouse åæ®µåå¨çæ¯éå·åéç设å¤ID |
| | | const deviceIds = props.record.warehouse.split(",").map(id => Number(id.trim())).filter(id => !isNaN(id)); |
| | | formState.value.deviceIds = deviceIds; |
| | | } |
| | | }; |
| | | |
| | | const closeModal = () => { |
| | | formState.value.deviceIds = []; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | submitLoading.value = true; |
| | | // å°è®¾å¤IDæ°ç»è½¬æ¢ä¸ºéå·åéçå符串 |
| | | const warehouse = formState.value.deviceIds.join(","); |
| | | bindIotDevice(props.record.id, warehouse) |
| | | .then(res => { |
| | | submitLoading.value = false; |
| | | proxy.$modal.msgSuccess("ç»å®æå"); |
| | | closeModal(); |
| | | emit("completed"); |
| | | }) |
| | | .catch(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getDeviceOptions(); |
| | | initSelectedDevices(); |
| | | }); |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="isShow" |
| | | title="ç©è设å¤å®æ¶æ°é" |
| | | width="800" |
| | | @close="closeModal"> |
| | | <div v-if="loading" v-loading="loading" element-loading-text="å è½½ä¸..." style="min-height: 200px;"></div> |
| | | <div v-else-if="!hasDevices" class="empty-state"> |
| | | <el-empty description="ææ ç»å®çç©è设å¤"> |
| | | <el-button type="primary" @click="closeModal">å»ç»å®</el-button> |
| | | </el-empty> |
| | | </div> |
| | | <div v-else> |
| | | <div class="device-header"> |
| | | <div class="refresh-switch"> |
| | | <el-switch v-model="autoRefresh" active-text="èªå¨å·æ°(30s)" /> |
| | | </div> |
| | | <el-button type="primary" size="small" @click="fetchData" :loading="loading"> |
| | | <el-icon><Refresh /></el-icon>å·æ° |
| | | </el-button> |
| | | </div> |
| | | <div class="devices-container"> |
| | | <el-card v-for="device in deviceData.devices" :key="device.deviceId" class="device-card"> |
| | | <template #header> |
| | | <div class="device-header-info"> |
| | | <div class="device-title"> |
| | | <span class="device-name">{{ device.deviceName }}</span> |
| | | <el-tag size="small" type="info">{{ device.deviceModel }}</el-tag> |
| | | </div> |
| | | <div class="device-status"> |
| | | <span class="status-dot" :class="getStatusClass(device.status)"></span> |
| | | <span :class="getStatusTextClass(device.status)">{{ device.status || 'æªç¥' }}</span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div v-if="device.status === 'å¨çº¿'" class="device-data"> |
| | | <el-row :gutter="10"> |
| | | <el-col :span="8" v-if="device.temperature"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Sunny /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">温度</div> |
| | | <div class="data-value">{{ device.temperature }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.humidity"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Drizzling /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">湿度</div> |
| | | <div class="data-value">{{ device.humidity }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.co2"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><WindPower /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">CO2</div> |
| | | <div class="data-value">{{ device.co2 }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.light"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Sunrise /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">å
ç
§</div> |
| | | <div class="data-value">{{ device.light }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.battery"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Lightning /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">çµé</div> |
| | | <div class="data-value">{{ device.battery }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <div v-else class="device-offline"> |
| | | <el-alert :title="device.statusMessage || '设å¤ç¦»çº¿'" type="warning" :closable="false" show-icon /> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeModal">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted, watch } from "vue"; |
| | | import { getIotRealtimeData } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { Refresh, Sunny, Drizzling, WindPower, Sunrise, Lightning } from "@element-plus/icons-vue"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | record: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible"]); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const loading = ref(false); |
| | | const deviceData = ref({ |
| | | inventoryId: null, |
| | | iotDeviceIds: "", |
| | | devices: [], |
| | | }); |
| | | const autoRefresh = ref(false); |
| | | let refreshTimer = null; |
| | | |
| | | const hasDevices = computed(() => { |
| | | return deviceData.value.devices && deviceData.value.devices.length > 0; |
| | | }); |
| | | |
| | | const getStatusClass = (status) => { |
| | | switch (status) { |
| | | case "å¨çº¿": |
| | | return "status-online"; |
| | | case "offline": |
| | | return "status-offline"; |
| | | case "error": |
| | | return "status-error"; |
| | | default: |
| | | return "status-offline"; |
| | | } |
| | | }; |
| | | |
| | | const getStatusTextClass = (status) => { |
| | | switch (status) { |
| | | case "å¨çº¿": |
| | | return "text-online"; |
| | | case "offline": |
| | | return "text-offline"; |
| | | case "error": |
| | | return "text-error"; |
| | | default: |
| | | return "text-offline"; |
| | | } |
| | | }; |
| | | |
| | | const fetchData = async () => { |
| | | if (!props.record.id) return; |
| | | loading.value = true; |
| | | try { |
| | | const res = await getIotRealtimeData(props.record.id); |
| | | if (res.code === 200 && res.data) { |
| | | deviceData.value = res.data; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç©èè®¾å¤æ°æ®å¤±è´¥:", error); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const closeModal = () => { |
| | | autoRefresh.value = false; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | // èªå¨å·æ° |
| | | watch(autoRefresh, (val) => { |
| | | if (val) { |
| | | refreshTimer = setInterval(() => { |
| | | fetchData(); |
| | | }, 30000); |
| | | } else { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | refreshTimer = null; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | fetchData(); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .empty-state { |
| | | padding: 40px 0; |
| | | } |
| | | |
| | | .device-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .refresh-switch { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .devices-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .device-card { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .device-header-info { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .device-name { |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .device-status { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | } |
| | | |
| | | .status-dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .status-online { |
| | | background-color: #67c23a; |
| | | } |
| | | |
| | | .status-offline { |
| | | background-color: #909399; |
| | | } |
| | | |
| | | .status-error { |
| | | background-color: #f56c6c; |
| | | } |
| | | |
| | | .text-online { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .text-offline { |
| | | color: #909399; |
| | | } |
| | | |
| | | .text-error { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .device-data { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .data-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .data-icon { |
| | | font-size: 24px; |
| | | color: #409eff; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .data-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .data-label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .device-offline { |
| | | padding: 20px 0; |
| | | } |
| | | </style> |
| | |
| | | value="unqualified" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ä»åº" |
| | | prop="warehouse" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©ä»åº', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.warehouse" |
| | | placeholder="è¯·éæ©ä»åº"> |
| | | <el-option v-for="item in warehouseOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åºåæ°é" |
| | | prop="qualitity"> |
| | | <el-input-number v-model="formState.qualitity" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, watch, getCurrentInstance, onMounted } from "vue"; |
| | | import { ref, computed, watch, getCurrentInstance } from "vue"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { createStockUnInventory } from "@/api/inventoryManagement/stockUninventory.js"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | |
| | | productName: "", |
| | | productModelName: "", |
| | | unit: "", |
| | | warehouse: undefined, |
| | | type: undefined, |
| | | qualitity: 0, |
| | | batchNo: null, |
| | |
| | | createTime: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | // ä»åºé项 |
| | | const warehouseOptions = ref([]); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | |
| | | }); |
| | | |
| | | const showProductSelectDialog = ref(false); |
| | | |
| | | // è·åä»åºåå
¸æ°æ® |
| | | const getWarehouseOptions = async () => { |
| | | const res = await getDicts("warehouse"); |
| | | if (res.code === 200) { |
| | | warehouseOptions.value = res.data.map(item => ({ |
| | | label: item.dictLabel, |
| | | value: item.dictValue, |
| | | })); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getWarehouseOptions(); |
| | | }); |
| | | |
| | | // æ¹å·ä¸ºç©ºæ¶è½¬ä¸º null |
| | | watch( |
| | |
| | | productName: "", |
| | | productModelName: "", |
| | | unit: "", |
| | | warehouse: undefined, |
| | | type: undefined, |
| | | qualitity: 0, |
| | | batchNo: null, |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | min-width="80" |
| | | min-width="190" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="showDetailModal(scope.row)">详æ
</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="showBindDeviceModal(scope.row)">ç»å®è®¾å¤</el-button> |
| | | <el-button link |
| | | type="info" |
| | | @click="showIotDataModal(scope.row)">æ¥çæ°é</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | :operation-type="operationType" |
| | | :type="record.stockType" |
| | | @completed="handleQuery" /> |
| | | <!-- ç»å®ç©èè®¾å¤ --> |
| | | <bind-device-dialog v-if="isShowBindDeviceModal" |
| | | v-model:visible="isShowBindDeviceModal" |
| | | :record="record" |
| | | @completed="handleQuery" /> |
| | | <!-- æ¥çç©èè®¾å¤æ°é --> |
| | | <iot-data-dialog v-if="isShowIotDataModal" |
| | | v-model:visible="isShowIotDataModal" |
| | | :record="record" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | const BatchNoQtyDetail = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue") |
| | | ); |
| | | const BindDeviceDialog = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/BindDeviceDialog.vue") |
| | | ); |
| | | const IotDataDialog = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/IotDataDialog.vue") |
| | | ); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | |
| | | const operationType = ref("frozen"); |
| | | // æ¯å¦æ¾ç¤ºå¯¼å
¥å¼¹æ¡ |
| | | const isShowImportModal = ref(false); |
| | | // æ¯å¦æ¾ç¤ºç»å®è®¾å¤å¼¹æ¡ |
| | | const isShowBindDeviceModal = ref(false); |
| | | // æ¯å¦æ¾ç¤ºç©èæ°éå¼¹æ¡ |
| | | const isShowIotDataModal = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productName: "", |
| | |
| | | operationType.value = "thaw"; |
| | | }; |
| | | |
| | | // ç¹å»ç»å®è®¾å¤ |
| | | const showBindDeviceModal = row => { |
| | | record.value = row; |
| | | isShowBindDeviceModal.value = true; |
| | | }; |
| | | |
| | | // ç¹å»æ¥çæ°é |
| | | const showIotDataModal = row => { |
| | | record.value = row; |
| | | isShowIotDataModal.value = true; |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | // è¿æ»¤æåæ°æ® |
| | |
| | | <el-input-number v-model="formState.qualitity" :step="1" :min="1" :max="maxQuality" style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="åºåºæ¹æ¬¡" prop="outboundBatches"> |
| | | <el-input |
| | | v-model="formState.outboundBatches" |
| | | placeholder="ç空èªå¨çæ" |
| | | maxlength="100" |
| | | show-word-limit |
| | | /> |
| | | <div class="form-tip">ä¸å¡«åèªå¨çæï¼æ ¼å¼ï¼CKå¹´ææ¥-åºå·</div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="formState.remark" type="textarea" /> |
| | | </el-form-item> |
| | |
| | | model: "", |
| | | unit: "", |
| | | qualitity: 0, |
| | | outboundBatches: "", |
| | | remark: '', |
| | | }); |
| | | |
| | |
| | | productModelId: undefined, |
| | | productName: "", |
| | | productModelName: "", |
| | | outboundBatches: "", |
| | | description: '', |
| | | }; |
| | | isShow.value = false; |
| | |
| | | handleSubmit, |
| | | isShow, |
| | | }); |
| | | </script> |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .form-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |