Browse Source

生产退仓

master
han\hanst 3 months ago
parent
commit
c009f0d7c2
  1. 55
      src/api/production/productionWithdrawal.js
  2. 4
      src/router/index.js
  3. 4
      src/views/modules/production-inbound/production.vue
  4. 264
      src/views/modules/production-withdrawal/order-list.vue
  5. 804
      src/views/modules/production-withdrawal/scan-hu.vue

55
src/api/production/productionWithdrawal.js

@ -0,0 +1,55 @@
/**
* 生产退库API接口
*
* <p><b>功能说明</b></p>
* <ul>
* <li>从IFS获取工单接收历史</li>
* <li>扫描HU验证合法性</li>
* <li>提交生产退库</li>
* </ul>
*
* @author System
* @since 2025-01-21
*/
import { createAPI } from "@/utils/httpRequest.js";
/**
* 从IFS获取工单接收历史记录
* @param {Object} data - 查询参数
* @param {String} data.ifsSiteID - IFS工厂ID
* @param {String} data.ifsOrderNo - IFS工单号
* @param {String} data.ifsReleaseNo - IFS下达号
* @param {String} data.ifsSequenceNo - IFS序列号
*/
export const getShopOrderReceiveHist = data => createAPI(`production/withdrawal/getShopOrderReceiveHist`, 'POST', data)
/**
* 扫描HU验证是否可退库
* @param {Object} data - 扫描参数
* @param {String} data.unitId - HU编码
* @param {String} data.site - 工厂编码
*/
export const scanHuForWithdrawal = data => createAPI(`production/withdrawal/scanHuForWithdrawal`, 'POST', data)
/**
* 提交生产退库
* @param {Object} data - 退库参数
* @param {String} data.site - 工厂编码
* @param {String} data.orderNo - 工单号
* @param {String} data.releaseNo - 下达号
* @param {String} data.sequenceNo - 序列号
* @param {String} data.partNo - 料号
* @param {String} data.locationNo - 库位编码
* @param {String} data.batchNo - 批次号
* @param {Number} data.transQty - 退库数量
* @param {Number} data.qtyReceived - 已接收数量
* @param {Number} data.accountingId - 会计ID
* @param {Number} data.transactionId - 事务ID
* @param {String} data.lineItemNo - 行号
* @param {String} data.warehouseId - 仓库编码
* @param {Array} data.unitIds - HU单元列表
* @param {String} data.withdrawalReason - 退库原因
*/
export const submitWithdrawal = data => createAPI(`production/withdrawal/submitWithdrawal`, 'POST', data)

4
src/router/index.js

@ -67,6 +67,10 @@ const globalRoutes = [
{path: "/productionInboundProduction",name: "productionInboundProduction", component: resolve => require(["@/views/modules/production-inbound/production.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{path: "/productionInboundRegister",name: "productionInboundRegister", component: resolve => require(["@/views/modules/production-inbound/inboundRegister.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
// 生产退库
{path: "/productionWithdrawalOrderList", name: "productionWithdrawalOrderList", component: resolve => require(["@/views/modules/production-withdrawal/order-list.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{path: "/productionWithdrawalScan", name: "productionWithdrawalScan", component: resolve => require(["@/views/modules/production-withdrawal/scan-hu.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: false } },
// 委外发料
{ path: "/outsource",name: "outsource", component: resolve => require(["@/views/modules/outsourcing-issue/index.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{ path:"/outsourcingDirectIssue",name:"outsourcingDirectIssue",component: resolve => require(["@/views/modules/outsourcing-issue/outsourcingDirectIssue.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},

4
src/views/modules/production-inbound/production.vue

@ -42,8 +42,8 @@ export default {
icon: "revoke",
label: "生产退库",
iconClass: "qualified",
to: "productionReturn",
disabled: true,
to: "/productionWithdrawalOrderList",
disabled: false,
},
],
};

264
src/views/modules/production-withdrawal/order-list.vue

@ -0,0 +1,264 @@
<template>
<div>
<div class="pda-container" v-loading.fullscreen.lock="fullscreenLoading"
element-loading-background="rgba(255, 255, 255, 0.3)"
element-loading-spinner="el-icon-loading"
:element-loading-text="loadingText">
<!-- 头部栏 -->
<div class="status-bar">
<div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div>
<div class="goBack">生产退库</div>
<div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
</div>
<!-- 主要内容区 -->
<div style="overflow-y: auto">
<!-- 扫描工单号 -->
<div class="scan-box" style="margin: 2px;">
<el-input clearable v-model="scanCode" placeholder="扫描或输入工单号"
inputmode="none"
autocomplete="off"
autocorrect="off"
spellcheck="false"
@keyup.enter.native="searchShopOrder" ref="scanCodeRef" />
</div>
<!-- 工单接收历史列表 -->
<div class="item-list" v-if="orderList.length > 0" style="margin: 2px;">
<el-form label-position="top" style="margin: 3px;">
<el-row :gutter="5"
v-for="(item, index) in orderList"
:key="index"
:class="index < orderList.length - 1 ? 'bottom-line-row' : ''">
<el-col :span="8">
<el-form-item label="料号"><span>{{ item.partNo }}</span></el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行号/下达号"><span>{{ item.sourceRef2 }}/{{ item.sourceRef3 }}</span></el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="">
<el-button type="text" class="recvButton" @click="selectShopOrder(item)"
style="margin-top: 10px;margin-left: 20px" size="small">退库</el-button>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="库位"><span>{{ item.locationNo }}</span></el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="接收数量"><span style="color: #67C23A;">{{ item.quantity }}</span></el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="批次号"><span>{{ item.lotBatchNo }}</span></el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="已退数量"><span>{{ item.qtyReversed }}</span></el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script>
import { getShopOrderReceiveHist } from '@/api/production/productionWithdrawal'
export default {
name: 'ProductionWithdrawalOrderList',
data() {
return {
//
scanCode: '',
//
orderList: [],
//
fullscreenLoading: false,
loadingText: '加载中...'
}
},
mounted() {
this.$nextTick(() => {
if (this.$refs.scanCodeRef) {
this.$refs.scanCodeRef.focus();
}
});
},
methods: {
/**
* 搜索工单
*/
searchShopOrder() {
if (!this.scanCode.trim()) {
return this.$message.error('请输入工单号')
}
this.loadingText = '查询中...';
this.fullscreenLoading = true;
let orderNo = this.scanCode.trim();
let releaseNo = "*";
let sequenceNo = "*";
// "-" orderNo-releaseNo-sequenceNo
const parts = orderNo.split('-');
if (parts.length >= 2) {
orderNo = parts[0];
releaseNo = parts[1];
if (parts.length >= 3) {
sequenceNo = parts[2];
}
}
const queryData = {
ifsSiteID: localStorage.getItem('site'),
ifsOrderNo: orderNo,
ifsReleaseNo: releaseNo,
ifsSequenceNo: sequenceNo
};
getShopOrderReceiveHist(queryData).then(({ data }) => {
this.fullscreenLoading = false;
if (data && data.code === 0) {
this.orderList = data.data || []
if (this.orderList.length === 0) {
this.$message.success('暂无接收历史记录')
}
} else {
this.$message.error(data.msg || '查询失败')
this.orderList = []
}
//
this.$nextTick(() => {
if (this.$refs.scanCodeRef) {
this.$refs.scanCodeRef.blur();
}
});
}).catch(error => {
this.fullscreenLoading = false;
this.$message.error('查询失败')
console.error(error);
this.orderList = []
//
this.$nextTick(() => {
if (this.$refs.scanCodeRef) {
this.$refs.scanCodeRef.blur();
}
});
})
this.scanCode = '';
},
/**
* 选择工单记录跳转到扫描HU页面
*/
selectShopOrder(item) {
//
this.$router.push({
path: '/productionWithdrawalScan',
query: {
orderNo: item.sourceRef1,
releaseNo: item.sourceRef2,
sequenceNo: item.sourceRef3,
lineItemNo: item.sourceRef4,
partNo: item.partNo,
locationNo: item.locationNo,
batchNo: item.lotBatchNo,
serialNo: item.serialNo,
wdr: item.waivDevRejNo,
engChgLevel: item.engChgLevel,
quantity: item.quantity,
qtyReversed: item.qtyReversed,
accountingId: item.accountingId,
transactionId: item.transactionId
}
})
},
/**
* 返回上级页面
*/
handleBack() {
this.$router.back()
}
}
}
</script>
<style scoped>
/* 复用inboundRegister.vue的样式 */
.pda-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.status-bar {
background: #17B3A3;
color: white;
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
min-height: 40px;
}
.goBack {
cursor: pointer;
font-size: 16px;
font-weight: 500;
}
.scan-box input {
width: 100%;
padding: 12px;
font-size: 16px;
}
.item-list {
flex: 1;
overflow-y: auto;
margin: 10px 0;
border: 1px solid rgba(200, 200, 200, 0.8);
background: white;
}
.item-list span {
color: #000;
font-size: 15px;
}
.bottom-line-row {
border-bottom: 1px solid #f0f0f0;
margin-bottom: 8px;
padding-bottom: 8px;
}
.recvButton {
font-size: 16px;
border-radius: 3px;
color: #17b3a3;
}
.item-list .el-row {
cursor: pointer;
transition: background 0.3s;
padding: 10px;
}
.item-list .el-row:hover {
background: #f5f7fa;
}
</style>

804
src/views/modules/production-withdrawal/scan-hu.vue

@ -0,0 +1,804 @@
<template>
<div>
<div class="pda-container">
<div class="status-bar">
<div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div>
<div class="goBack">扫描HU退库</div>
<div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
</div>
<!-- 全页面Loading -->
<div v-if="loading || scanLoading" class="page-loading-overlay">
<div class="page-loading-content">
<i class="el-icon-loading page-loading-icon"></i>
<div class="page-loading-text">
{{ scanLoading ? '验证中...' : loadingText }}
</div>
</div>
</div>
<div style="overflow-y: auto" :class="{ 'content-disabled': loading || scanLoading }">
<!-- 工单信息显示 -->
<div class="material-info-card">
<div class="input-form">
<div class="form-row">
<div class="form-item">
<label class="form-label">料号</label>
<span>{{ orderInfo.partNo }}</span>
</div>
<div class="form-item">
<label class="form-label">接收数量</label>
<span style="color: #67C23A;">{{ orderInfo.quantity }}</span>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="form-label">库位</label>
<span>{{ orderInfo.locationNo }}</span>
</div>
<div class="form-item">
<label class="form-label">批次号</label>
<span>{{ orderInfo.batchNo }}</span>
</div>
</div>
</div>
</div>
<!-- 扫描区域 -->
<div class="search-container">
<el-input clearable class="compact-input"
v-model="scanCode"
placeholder="请扫描HandlingUnit条码"
prefix-icon="el-icon-search"
inputmode="none"
autocomplete="off"
autocorrect="off"
spellcheck="false"
@keyup.enter.native="handleScan"
ref="scanInput"
/>
<div class="mode-switch">
<el-switch
class="custom-switch"
v-model="isRemoveMode"
active-color="#ff4949"
inactive-color="#13ce66">
</el-switch>
<span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span>
<span v-else class="switch-text2">{{ '添加' }}</span>
</div>
</div>
<!-- 扫描信息确认标题 -->
<div class="section-title">
<div class="title-left">
<i class="el-icon-box"></i>
<span>扫描信息确认 (总数量: {{ totalScannedQty }})</span>
</div>
</div>
<!-- 扫描的HandlingUnit明细列表 -->
<div class="scanned-items" v-if="scannedHus.length > 0" style="margin: 2px;">
<div class="label-list">
<el-form label-position="top" style="margin: 3px;">
<el-row :gutter="5"
v-for="(hu, index) in scannedHus"
:key="hu.unitId"
:class="index < scannedHus.length - 1 ? 'bottom-line-row' : ''"
style="border: 1px solid #e0e0e0; border-radius: 4px; margin-bottom: 8px; padding: 8px;">
<el-col :span="16">
<el-form-item label="HandlingUnit">
<span>{{ hu.unitId }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="物料编码">
<span>{{ hu.partNo }}</span>
</el-form-item>
</el-col>
<el-col :span="24" v-if="hu.partDesc" class="description-col">
<el-form-item label="物料描述">
<span class="part-description">{{ hu.partDesc }}</span>
</el-form-item>
</el-col>
<el-col :span="10" v-if="hu.batchNo">
<el-form-item label="批次号">
<span>{{ hu.batchNo }}</span>
</el-form-item>
</el-col>
<el-col :span="10" v-if="hu.locationId">
<el-form-item label="当前库位">
<span>{{ hu.locationId }}</span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="数量">
<span>{{ hu.qty }} </span>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
<!-- 空状态 -->
<div v-if="scannedHus.length === 0" class="empty-labels">
<div style="text-align: center; padding: 20px;">
<p style="color: #999; margin: 0;">暂无扫描HandlingUnit</p>
</div>
</div>
<!-- 底部操作按钮 -->
<div class="bottom-actions" v-if="scannedHus.length > 0">
<button class="action-btn secondary" @click="handleBack">
回退
</button>
<button class="action-btn primary" @click="confirmWithdrawal">
确认退库
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { scanHuForWithdrawal, submitWithdrawal } from '@/api/production/productionWithdrawal'
export default {
name: 'ProductionWithdrawalScanHu',
data() {
return {
//
orderInfo: {
orderNo: '',
releaseNo: '',
sequenceNo: '',
lineItemNo: '',
partNo: '',
locationNo: '',
batchNo: '',
serialNo: '',
wdr: '',
engChgLevel: '',
quantity: 0,
qtyReversed: 0,
accountingId: '',
transactionId: ''
},
//
scanCode: '',
// HU
scannedHus: [],
// 退
withdrawalReason: '',
//
isRemoveMode: false,
//
loading: false,
scanLoading: false,
loadingText: '处理中...',
site: localStorage.getItem('site')
}
},
computed: {
/**
* 计算扫描的总数量
*/
totalScannedQty() {
return this.scannedHus.reduce((sum, hu) => sum + (parseFloat(hu.qty) || 0), 0)
}
},
mounted() {
//
this.orderInfo = {
orderNo: this.$route.query.orderNo || '',
releaseNo: this.$route.query.releaseNo || '',
sequenceNo: this.$route.query.sequenceNo || '',
lineItemNo: this.$route.query.lineItemNo || '',
partNo: this.$route.query.partNo || '',
locationNo: this.$route.query.locationNo || '',
batchNo: this.$route.query.batchNo || '',
serialNo: this.$route.query.serialNo || '',
wdr: this.$route.query.wdr || '',
engChgLevel: this.$route.query.engChgLevel || '',
quantity: parseFloat(this.$route.query.quantity) || 0,
qtyReversed: parseFloat(this.$route.query.qtyReversed) || 0,
accountingId: this.$route.query.accountingId || '',
transactionId: this.$route.query.transactionId || ''
}
//
this.$nextTick(() => {
if (this.$refs.scanInput) {
this.$refs.scanInput.focus()
}
})
},
methods: {
/**
* 处理扫描
*/
handleScan() {
if (!this.scanCode.trim()) {
return
}
if (this.isRemoveMode) {
this.removeHuByCode(this.scanCode.trim())
} else {
this.validateAndAddHu(this.scanCode.trim())
}
this.scanCode = ''
},
/**
* 验证HU并添加到列表
*/
validateAndAddHu(unitId) {
//
const exists = this.scannedHus.find(hu => hu.unitId === unitId)
if (exists) {
this.$message.warning('该HU已扫描,请勿重复扫描')
return
}
// loading
this.scanLoading = true
const params = {
site: this.site,
unitId: unitId,
orderNo: this.orderInfo.orderNo,
releaseNo: this.orderInfo.releaseNo,
sequenceNo: this.orderInfo.sequenceNo,
partNo: this.orderInfo.partNo,
batchNo: this.orderInfo.batchNo
}
scanHuForWithdrawal(params).then(({ data }) => {
if (data && data.code === 0 && data.data) {
const huInfo = data.data
// HU退
if (huInfo.canWithdraw!=='Y') {
this.$message.error('该HU不符合退库条件')
return
}
//
this.scannedHus.push({
unitId: huInfo.unitId,
partNo: huInfo.partNo,
partDesc: huInfo.partDesc,
qty: huInfo.qty,
batchNo: huInfo.batchNo,
locationId: huInfo.locationId,
warehouseId: huInfo.warehouseId
})
this.$message.success('扫描成功')
} else {
this.$message.error(data.msg || 'HU校验失败')
}
}).catch(error => {
this.$message.error('HU校验失败')
console.error(error)
}).finally(() => {
// loading
this.scanLoading = false
})
},
/**
* 通过条码移除HU
*/
removeHuByCode(unitId) {
const index = this.scannedHus.findIndex(hu => hu.unitId === unitId)
if (index !== -1) {
this.scannedHus.splice(index, 1)
this.$message.success('移除成功')
} else {
this.$message.warning('未找到该HU')
}
},
/**
* 确认退库
*/
confirmWithdrawal() {
if (this.scannedHus.length === 0) {
this.$message.warning('请先扫描HandlingUnit')
return
}
// 退
const totalQty = this.totalScannedQty
// 退退
const availableQty = this.orderInfo.quantity - this.orderInfo.qtyReversed
if (totalQty > availableQty) {
this.$message.error(`退库数量不能超过可退数量 ${availableQty}`)
return
}
this.$confirm('确认要进行生产退库操作吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.submitWithdrawalData()
}).catch(() => {
//
})
},
/**
* 提交退库数据
*/
submitWithdrawalData() {
const unitIds = this.scannedHus.map(hu => hu.unitId)
const params = {
site: this.site,
orderNo: this.orderInfo.orderNo,
releaseNo: this.orderInfo.releaseNo,
sequenceNo: this.orderInfo.sequenceNo,
partNo: this.orderInfo.partNo,
locationNo: this.orderInfo.locationNo,
batchNo: this.orderInfo.batchNo,
serialNo: this.orderInfo.serialNo,
engChgLevel: this.orderInfo.engChgLevel,
wdr: this.orderInfo.wdr,
transQty: this.totalScannedQty,
qtyReceived: this.orderInfo.quantity,
accountingId: this.orderInfo.accountingId,
transactionId: this.orderInfo.transactionId,
lineItemNo: this.orderInfo.lineItemNo,
warehouseId: this.scannedHus[0].warehouseId,
unitIds: unitIds,
withdrawalReason: this.withdrawalReason || ''
}
// loading
this.loadingText = '退库中...'
this.loading = true
// API
submitWithdrawal(params).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: '生产退库成功!处理单元: ' + unitIds.length + '个',
type: 'success',
duration: 3000
})
//
this.$router.back()
} else {
this.$message.error(data.msg || '生产退库失败')
}
}).catch(error => {
console.error('生产退库失败:', error)
this.$message.error('生产退库失败,请重试')
}).finally(() => {
// loading
this.loading = false
})
},
/**
* 返回上级页面
*/
handleBack() {
if (this.scannedHus.length > 0) {
this.$confirm('当前有未保存的扫描记录,确认返回?', '操作确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$router.back()
})
} else {
this.$router.back()
}
}
}
}
</script>
<style scoped>
/* 复用qualifiedStorage.vue的样式 */
.pda-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.status-bar {
background: #17B3A3;
color: white;
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
min-height: 40px;
}
.goBack {
cursor: pointer;
font-size: 16px;
font-weight: 500;
}
/* 物料描述样式 - 防止重叠 */
.description-col {
margin-bottom: 15px !important;
}
.part-description {
word-wrap: break-word;
word-break: break-all;
line-height: 1.8;
display: block;
max-width: 100%;
margin-bottom: 10px;
padding-bottom: 5px;
}
/* 确保物料描述的form-item有足够间距 */
.description-col .el-form-item {
margin-bottom: 15px !important;
}
/* 搜索容器 */
.search-container {
padding: 12px 16px;
background: white;
display: flex;
align-items: center;
gap: 12px;
}
.search-container .el-input {
width: 240px;
margin-right: 12px;
}
/* 紧凑型输入框样式 */
.compact-input >>> .el-input__inner {
height: 36px;
padding: 0 12px 0 35px;
font-size: 14px;
}
.compact-input >>> .el-input__prefix {
left: 10px;
}
.compact-input >>> .el-input__suffix {
right: 30px;
}
/* 模式切换开关 */
.mode-switch {
position: relative;
display: inline-block;
}
.custom-switch {
transform: scale(1.3);
}
/* 中间文字 */
.switch-text {
position: absolute;
left: 25%;
transform: translateX(-50%);
top: 50%;
transform: translateY(-50%) translateX(-50%);
font-size: 12px;
font-weight: 500;
color: #606266;
white-space: nowrap;
pointer-events: none;
z-index: 1;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
.switch-text2 {
position: absolute;
left: 75%;
transform: translateX(-50%);
top: 50%;
transform: translateY(-50%) translateX(-50%);
font-size: 12px;
font-weight: 500;
color: #606266;
white-space: nowrap;
pointer-events: none;
z-index: 1;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
/* 调整 switch 尺寸以便容纳文字 */
.custom-switch >>> .el-switch__core {
width: 60px;
height: 22px;
}
/* 物料信息卡片 */
.material-info-card {
background: white;
margin: 4px 4px;
padding: 6px 20px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #f0f0f0;
}
/* 表单样式 */
.form-row {
display: flex;
gap: 12px;
margin-bottom: 12px;
}
.form-row:last-child {
margin-bottom: 0;
}
.form-item {
flex: 1;
display: flex;
flex-direction: column;
}
.form-label {
font-size: 11px;
color: #666;
font-weight: 500;
margin-bottom: 4px;
display: block;
}
.form-item .el-input {
width: 100%;
}
/* 区域标题 */
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 8px;
background: white;
margin: 0 4px;
margin-top: 4px;
border-radius: 8px 8px 0 0;
border-bottom: 2px solid #17B3A3;
}
.title-left {
display: flex;
align-items: center;
}
.title-left i {
color: #17B3A3;
font-size: 16px;
margin-right: 8px;
}
.title-left span {
color: #17B3A3;
font-size: 14px;
font-weight: 500;
}
/* 标签列表 */
.label-list {
background: white;
margin: 4px 4px;
border-radius: 0 0 8px 8px;
overflow: hidden;
}
.label-list .el-form-item {
margin-bottom: 1px;
}
.label-list .el-form-item__label {
padding-bottom: 1px;
margin-bottom: 0;
line-height: 1.1;
font-size: 11px;
}
.label-list .el-form-item__content {
line-height: 1.2;
font-size: 12px;
}
.bottom-line-row {
border-bottom: 1px solid #f0f0f0;
margin-bottom: 8px;
padding-bottom: 8px;
}
.empty-labels {
padding: 20px;
text-align: center;
color: #999;
background: white;
margin: 0 4px;
border-radius: 8px;
}
/* 底部操作按钮 */
.bottom-actions {
display: flex;
padding: 16px;
gap: 20px;
background: white;
margin-top: auto;
}
.action-btn {
flex: 1;
padding: 12px;
border: 1px solid #17B3A3;
background: #17B3A3;
color: white;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
}
.action-btn.secondary {
background: white;
color: #17B3A3;
}
.action-btn:hover {
background: #0d8f7f;
border-color: #0d8f7f;
}
.action-btn.secondary:hover {
background: #17B3A3;
color: white;
}
.action-btn:active {
transform: scale(0.98);
}
.action-btn:disabled {
background: #c0c4cc !important;
border-color: #c0c4cc !important;
color: white !important;
cursor: not-allowed !important;
transform: none !important;
box-shadow: none !important;
}
.action-btn:disabled:hover {
background: #c0c4cc !important;
transform: none !important;
box-shadow: none !important;
}
.action-btn.secondary:disabled {
background: #f5f7fa !important;
color: #c0c4cc !important;
border-color: #e4e7ed !important;
}
/* 全页面Loading样式 */
.page-loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.page-loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
min-width: 150px;
}
.page-loading-icon {
font-size: 32px;
color: #17B3A3;
animation: rotating 1s linear infinite;
margin-bottom: 16px;
}
.page-loading-text {
color: #17B3A3;
font-size: 16px;
font-weight: 500;
text-align: center;
}
.content-disabled {
pointer-events: none;
opacity: 0.6;
}
@keyframes rotating {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 响应式设计 */
@media (max-width: 360px) {
.status-bar {
padding: 8px 12px;
}
.search-container {
padding: 8px 12px;
margin: 1px 4px;
}
.material-info-card {
margin: 4px 4px;
padding: 6px 16px;
}
.form-row {
gap: 8px;
margin-bottom: 8px;
}
.form-label {
font-size: 10px;
margin-bottom: 2px;
}
}
</style>
Loading…
Cancel
Save