init: CATL电池线PAD Web首次入库

This commit is contained in:
XingCheng3
2026-06-08 17:11:45 +08:00
commit 414f4aadab
43 changed files with 9669 additions and 0 deletions

248
src/views/404.vue Normal file
View File

@@ -0,0 +1,248 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img
class="pic-404__parent"
src="@/assets/404_images/404.png"
alt="404"
/>
<img
class="pic-404__child left"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child mid"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child right"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">
版权所有
<a class="link-type" href="https://wallstreetcn.com" target="_blank"
>华尔街见闻</a
>
</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">
请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告
</div>
<a href="/" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Page404",
computed: {
message() {
return "网管说这个页面你不能进......";
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404-container {
transform: translate(-50%, -50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -0,0 +1,124 @@
<template>
<div class="card">
<van-steps :active="callActive" active-color="#23d96e">
<van-step>选择物料</van-step>
<van-step>AGV送料中</van-step>
<van-step>物料验证</van-step>
</van-steps>
<el-tabs type="border-card" v-model="callActive" style="margin-top: 5vh;">
<el-tab-pane :disabled="callActive != 0" :name="0" label="选择物料">
<van-dropdown-menu>
<van-field v-model="materials" is-link readonly name="picker" label="叫料图号" placeholder="选择物料图号"
@click="isShowMaterialsPicker = true" />
<van-popup :show="isShowMaterialsPicker" destroy-on-close position="bottom">
<van-picker :columns="materialsList" :model-value="carValue" @confirm="materialsOnConfirm"
@cancel="isShowMaterialsPicker = false" />
</van-popup>
<van-field v-model="carPos" is-link readonly name="picker" label="叫料库位" placeholder="选择库位"
@click="isShowCarPosPicker = true" />
<van-popup :show="isShowCarPosPicker" destroy-on-close position="bottom">
<van-picker :columns="carPosList" :model-value="materialsValue" @confirm="carPosOnConfirm"
@cancel="isShowCarPosPicker = false" />
</van-popup>
</van-dropdown-menu>
<el-button icon="ShoppingCartFull" type="primary" @click="CallMaterials" style="margin-top: 5vh;">请求叫料</el-button>
</el-tab-pane>
<el-tab-pane :disabled="callActive != 1" :name="1" label="AGV送料中">
<van-cell-group>
<van-cell title="叫料时间" value="2025-03-25 10:00:00" />
<van-cell title="叫料图号" :value="materials" />
<van-cell title="叫料库位" :value="carPos" />
</van-cell-group>
<el-button icon="FolderChecked" type="primary" @click="handHandle" style="margin-top: 2vh;">已到达手动确认</el-button>
</el-tab-pane>
<el-tab-pane :disabled="callActive != 2" :name="2" label="物料验证">
<van-cell-group>
<van-cell title="计划订单" value="TestOrder1" />
<van-cell title="工件编号" value="DES1235123" />
<van-cell title="物料型号" value="DES" />
<van-cell title="叫料时间" value="2025-03-25 10:00:00" />
<van-cell title="叫料图号" :value="materials" />
<van-cell title="叫料库位" :value="carPos" />
<van-field value="物料码值" label="码值" right-icon="scan" :right-icon-size="19" />
</van-cell-group>
<el-button icon="FullScreen" type="primary" @click="verifyCode" style="margin-top: 5vh;">校验物料</el-button>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ElMessageBox } from 'element-plus'
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
// import { showSuccessToast, showFailToast } from 'vant';
const ExecDatabaseByParam = inject('ExecDatabaseByParam')
const callActive = ref(0)
// 缓存库下拉
const carPos = ref()
const carValue = ref()
const isShowCarPosPicker = ref(false)
const carPosList = reactive([
{ text: '缓存库位1', value: '缓存库位1' },
{ text: '缓存库位2', value: '缓存库位2' },
{ text: '缓存库位3', value: '缓存库位3' },
])
// 选择物料下拉
const materials = ref()
const materialsValue = ref()
const isShowMaterialsPicker = ref(false)
const materialsList = reactive([
{ text: 'MaterialsType1', value: 'MaterialsType1' },
{ text: 'MaterialsType2', value: 'MaterialsType2' },
{ text: 'MaterialsType3', value: 'MaterialsType3' },
])
onMounted(() => {
})
const nextTo = () => {
if (callActive.value == 2) {
callActive.value = 0
} else {
callActive.value++
}
}
const carPosOnConfirm = (selectedValues, eslectedOptions) => {
carPos.value = selectedValues?.text;
isShowCarPosPicker.value = false;
};
const materialsOnConfirm = (selectedValues, eslectedOptions) => {
materials.value = selectedValues?.text;
isShowMaterialsPicker.value = false;
setTimeout(() => {
if (carPos.value == undefined) {
carPos.value = carPosList[2].text
}
}, 1000);
};
const CallMaterials = () => {
ElMessage.success('物料呼叫成功!等待送达')
nextTo()
}
const handHandle = () => {
ElMessage.success('手动确认到达,请扫码校验物料')
nextTo()
}
const verifyCode = () => {
ElMessage.success('物料验证成功!送料结束')
carPos.value = ''
materials.value = ''
nextTo()
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,167 @@
<template>
<div class="card">
<div class="dataList">
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:04:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:05:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235123</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo1</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:18:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:22:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235124</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo2</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:18:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:22:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235124</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo2</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:18:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:22:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235124</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo2</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:18:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:22:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235124</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo2</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">叫料时间:</div>
<div class="value">2025-03-22 13:18:21</div>
</div>
<div class="tItem">
<div class="name">到料时间:</div>
<div class="value">2025-03-22 13:22:12</div>
</div>
<div class="tItem">
<div class="name">绑定工件:</div>
<div class="value">DES1235124</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器总成</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo2</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
onMounted(() => {
})
// 离开网页 删除定时器
onUnmounted(() => {
})
</script>
<style lang="scss" scoped>
.dataList {
.item {
border: 2px solid #ccc;
padding: 10px 10px;
// 圆角 阴影
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
margin: 10px 0;
.tItem {
display: flex;
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<div class="main">
<div class="card">
<van-cell-group>
<van-field v-model="carBigPos" is-link readonly label="选择库位" placeholder="请选库位号"
@click="isShowCarBigPosPicker = true" />
<van-popup :show="isShowCarBigPosPicker" round position="bottom">
<van-cascader v-model="carBigPosValue" title="请选库位号" :options="carBigPosList"
@close="isShowCarBigPosPicker = false" @finish="carBigPosFinish" />
</van-popup>
</van-cell-group>
</div>
<div class="card" v-if="carStatus == 1">
<van-cell-group inset>
库位绑定信息
<van-cell title="物料名称" value="断路器总成" />
<van-cell title="物料图号" value="DESA" />
<van-cell title="物料编号" value="DESA1235123" />
<van-cell title="订单号" value="Order001" />
<van-cell title="入库时间" value="2025-03-22" />
</van-cell-group>
<van-button icon="replay" type="primary" size="large">库位解绑</van-button>
</div>
<div class="card" v-if="carStatus == 2">
<van-cell-group inset>
空库位绑定
<!-- <van-field label="物料名称" v-model="bindMaterialsData.materialName" right-icon="browsing-history-o"
:right-icon-size="19" /> -->
<van-field v-model="bindMaterialsData.materialName" is-link readonly name="picker" label="物料名称" placeholder="选择物料"
@click="isShowMaterialsPicker = true" />
<van-popup :show="isShowMaterialsPicker" destroy-on-close position="bottom">
<van-picker :columns="materialsList" :model-value="bindMaterialsData.materialCode" @confirm="bindMaterialsOnConfirm"
@cancel="isShowMaterialsPicker = false" />
</van-popup>
<van-cell title="物料图号" :value="bindMaterialsData.materialCode" right-icon="more-o" :right-icon-size="19" />
<!-- <van-field label="物料号" v-model="bindMaterialsData.materialCode" right-icon="more-o" :right-icon-size="19" /> -->
<van-field label="物料编号" v-model="bindMaterialsData.materialNo" right-icon="scan" :right-icon-size="19" />
<van-field label="订单号" v-model="bindMaterialsData.orderNo" right-icon="newspaper-o" :right-icon-size="19" />
<van-button icon="back-top" type="primary" size="large">绑定库位信息</van-button>
</van-cell-group>
</div>
</div>
</template>
<script setup>
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
const ExecDatabaseByParam = inject('ExecDatabaseByParam')
const carStatus = ref(0)
// 选择库位 下拉
const carBigPos = ref()
const carBigPosValue = ref()
const isShowCarBigPosPicker = ref(false)
const carBigPosList = reactive([
{
text: '缓存库1',
value: '缓存库1',
children: [{ text: '1-1', value: '1-1' }, { text: '1-2', value: '1-2' }, { text: '1-3', value: '1-3' }],
},
{
text: '缓存库2',
value: '缓存库2',
children: [{ text: '2-1', value: '2-1' }, { text: '2-2', value: '2-2' }, { text: '3-3', value: '3-3' }],
},
])
// 选择物料种类 下拉
const isShowMaterialsPicker = ref(false)
const materialsList = reactive([
{ text: '测试物料1', value: 'MaterialsType1' },
{ text: '测试物料2', value: 'MaterialsType2' },
{ text: '测试物料3', value: 'MaterialsType3' },
])
const bindMaterialsData = reactive({
materialName: '',
materialNo: '',
materialCode: '',
orderNo: '',
inTime: '',
})
onMounted(() => {
})
// 全部选项选择完毕后,会触发 finish 事件
const carBigPosFinish = (selectedOptions) => {
console.log(selectedOptions, selectedOptions.selectedOptions);
if(selectedOptions.selectedOptions[0].text == '缓存库1') {
carStatus.value = 1
} else {
carStatus.value = 2
}
carBigPos.value = selectedOptions.selectedOptions.map((option) => option.text).join(' / ');
isShowCarBigPosPicker.value = false;
console.log(carStatus)
};
const bindMaterialsOnConfirm = (selectedValues, eslectedOptions) => {
bindMaterialsData.materialName = selectedValues?.text;
bindMaterialsData.materialCode = selectedValues?.value;
isShowMaterialsPicker.value = false;
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="card">
<div class="dataList">
<div class="item">
<div class="tItem">
<div class="name">操作时间:</div>
<div class="value">2025-03-22 13:04:21</div>
</div>
<div class="tItem">
<div class="name">操作类型:</div>
<van-tag class="value" type="success">库位绑定</van-tag>
</div>
<div class="tItem">
<div class="name">操作库位:</div>
<div class="value">缓存库2 / 1-1</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器壳体</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo1</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">操作时间:</div>
<div class="value">2025-03-22 13:04:21</div>
</div>
<div class="tItem">
<div class="name">操作类型:</div>
<van-tag class="value" type="warning">库位解绑</van-tag>
</div>
<div class="tItem">
<div class="name">操作库位:</div>
<div class="value">缓存库2 / 1-1</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器壳体</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo1</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">操作时间:</div>
<div class="value">2025-03-22 13:04:21</div>
</div>
<div class="tItem">
<div class="name">操作类型:</div>
<van-tag class="value" type="warning">库位解绑</van-tag>
</div>
<div class="tItem">
<div class="name">操作库位:</div>
<div class="value">缓存库2 / 1-1</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器壳体</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo1</div>
</div>
</div>
<div class="item">
<div class="tItem">
<div class="name">操作时间:</div>
<div class="value">2025-03-22 13:04:21</div>
</div>
<div class="tItem">
<div class="name">操作类型:</div>
<van-tag class="value" type="success">库位绑定</van-tag>
</div>
<div class="tItem">
<div class="name">操作库位:</div>
<div class="value">缓存库2 / 1-1</div>
</div>
<div class="tItem">
<div class="name">物料名称:</div>
<div class="value">断路器壳体</div>
</div>
<div class="tItem">
<div class="name">物料编号:</div>
<div class="value">MaterialsNo1</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
onMounted(() => {
})
// 离开网页 删除定时器
onUnmounted(() => {
})
</script>
<style lang="scss" scoped>
.dataList {
.item {
border: 2px solid #ccc;
padding: 10px 10px;
// 圆角 阴影
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
margin: 10px 0;
.tItem {
display: flex;
}
}
}
</style>

206
src/views/Layout/index.vue Normal file
View File

@@ -0,0 +1,206 @@
<template>
<div class="main">
<router-view />
</div>
</template>
<script setup>
import { inject, onMounted, ref } from 'vue'
import { useAppStore } from '@/store/app.js'
import { storeToRefs } from 'pinia'
import { addDynamicRoutes } from '@/router/index.js'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import Cookies from 'js-cookie'
const router = useRouter()
const store = useAppStore()
const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')
// storeToRefs 响应式解构赋值 pinia
const { user, isLoading, routerData } = storeToRefs(store)
const menuValue = ref('')
const date = ref('')
const time = ref('')
const nowWeek = ref('')
const week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const now = new Date()
onMounted(() => {
setTimeout(() => {
if (!store.user.AID) {
if (Cookies.get('AID')) {
user.value.token = Cookies.get('token')
user.value.AID = Cookies.get('AID')
user.value.name = Cookies.get('name')
user.value.role = Cookies.get('role')
user.value.userName = Cookies.get('userName')
user.value.opName = Cookies.get('opName')
user.value.opNameText = Cookies.get('opNameText')
} else {
console.log('store.user.AID为空返回主页')
ElMessage.warning('用户信息过期,请重新登陆!')
router.push({ path: '/login' })
}
} else {
// 获取当前实时日期时间 与星期几
// setInterval(() => {
// date.value = now.toLocaleDateString()
// time.value = now.toLocaleTimeString()
// nowWeek.value = week[now.getDay()]
// }, 1000)
}
}, 200)
})
// 获取权限列表
const getRouter = async () => {
// 获取权限列表
var param = []
param[0] = ['userId', store.user.AID]
var Data = CreateData('11', 'PDA_基础表_权限模块_查询', param)
ExecDatabase(Data).then(response => {
if (response.data.length > 0) {
for (let item of response.data) {
item.selected = false
}
response.data[0].selected = true
routerData.value.userRouterList = response.data
initRouter()
} else {
ElMessage.warning('该角色未分配角色模块,请先进行分配!')
}
})
}
const initRouter = async () => {
// 动态添加路由
await addDynamicRoutes(routerData.value.userRouterList).then(() => {
menuValue.value = routerData.value.userRouterList[0].path
routerData.nowRouterPath = routerData.value.userRouterList[0].path
routerData.nowRouterTitle = routerData.value.userRouterList[0].权限模块
router.push({ path: '/' + routerData.value.userRouterList[0].path })
store.isLoading = false
})
}
const routerTo = (item) => {
const targetRoute = routerData.value.userRouterList.find(r => r.path === item.path)
if (targetRoute) {
routerData.nowRouterPath = targetRoute.path
routerData.nowRouterTitle = targetRoute.权限模块
routerData.selected = true
router.push({ path: '/' + targetRoute.path })
}
}
const logout = () => {
ElMessage.warning('退出登录成功!')
store.$reset() // 重置pinia数据
router.push('/login')
// var param = []
// param[0] = ['userId', user.AID]
// param[1] = ['opName', user.opName]
// var Data = CreateData('11', '基础表_用户角色_退出登录', param)
// ExecDatabase(Data).then(response => {
// if (response.data.length > 0) {
// ElMessage.warning('退出登录成功!')
// store.$reset() // 重置pinia数据
// router.push('/login')
// } else {
// ElMessage.warning('退出登录失败,数据未清空!')
// }
// })
}
</script>
<style lang="scss" scoped>
.topBar {
width: 100vw;
overflow: hidden;
.logo {
width: 20vw;
height: 10vh;
background-image: url("/public/TopLogo.png");
background-size: 100% 100%;
}
.dateTime {
font-size: .8vw;
float: right;
width: 10vw;
color: #fff;
text-align: center;
div {
height: 20px;
line-height: 20px;
}
}
}
// 主内容窗体大小
.main {
height: 92vh;
width: 100vw;
overflow: auto;
background-color: #f0f3f6;
}
// .t-menu__operations {
// .t-button {
// margin-left: 8px;
// }
// }
.t-demo-menu--dark {
.t-button {
color: #fff;
&:hover {
background-color: #4b4b4b;
border-color: transparent;
--ripple-color: #383838;
}
}
}
// logo 两旁留空
::v-deep() .t-head-menu .t-menu__logo:not(:empty) {
margin-right: 0;
}
::v-deep() .t-menu__logo>* {
margin-left: 0 !important;
}
// 顶部栏高度
::v-deep() .t-head-menu__inner{
height: 8vh;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 4px;
/* 设置纵轴y轴轴滚动条 */
height: 4px;
/* 设置横轴x轴轴滚动条 */
}
/* 滚动条滑块(里面小方块) */
::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #7ba2cc;
}
/* 滚动条轨道 */
::-webkit-scrollbar-track {
border-radius: 0;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #fff;
}
</style>

238
src/views/Login/index.vue Normal file
View File

@@ -0,0 +1,238 @@
<template>
<div class="main">
<div class="login">
<div class="title">AMES 工位登录</div>
<div class="login-form">
<div class="form-item">
<el-input v-model="userform.userName" :prefix-icon="User" clearable placeholder="请输入用户名" size="large"
class="login-input" />
</div>
<div class="form-item">
<el-input v-model="userform.password" :prefix-icon="Lock" clearable placeholder="请输入密码" show-password
size="large" class="login-input" @keyup.enter="login" />
</div>
<div class="form-item">
<el-select v-model="userform.opName" placeholder="请选择台位号" size="large" class="login-select">
<el-option v-for="item in opNameList" :key="item.工位号" :label="item.工位号" :value="item.工位号" />
</el-select>
</div>
<div class="form-item">
<el-button type="danger" size="large" class="login-button" @click="login">
</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { inject, onMounted, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from "@/store/app.js";
import { storeToRefs } from 'pinia';
import { encrypt, cryptoDecrypt, generateSalt } from "../../utils/AES-Pkcs7";
import Cookies from 'js-cookie'
const router = useRouter()
const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')
const store = useAppStore();
// storeToRefs 响应式解构赋值 pinia
const { user, isLoading } = storeToRefs(store);
const opNameList = reactive([])
const userform = reactive({
userName: '',
password: '',
opName: '',
opNameText: ''
})
onMounted(() => {
getOpName()
})
const getOpName = () => {
opNameList.length = 0
// 获取当前操作员名称
var param = []
var Data = CreateData('11', 'MES_计划BOM_工位与名称_PDA用_查询', param)
ExecDatabase(Data).then(response => {
if (response.data.length > 0) {
opNameList.push(...response.data)
// 获取上次登录的工位号
const lastOpName = Cookies.get('lastOpName')
if (lastOpName && opNameList.some(op => op.工位号 === lastOpName)) {
userform.opName = lastOpName
userform.opNameText = opNameList.find(op => op.工位号 === lastOpName).工位名称
} else {
userform.opName = opNameList[0].工位号
userform.opNameText = opNameList[0].工位名称
}
}
console.log(response)
})
}
const login = () => {
console.log(user)
var param = []
param.push(['用户名', userform.userName])
param.push(['密码', userform.password])
param.push(['功能类型', 0])
var Data = CreateData('11', '菜单模块系统_用户名密码_查询数据_改造新增', param)
ExecDatabase(Data).then(response => {
const data = response.data
if (data.length === 0 || data.result === '0' || data[0].result === '0') {
ElMessage.warning('账号或密码错误,请重试!')
} else {
const param2 = []
param2[0] = ['工位号', userform.opName]
param2[1] = ['操作者工号', userform.userName]
param2[2] = ['班次名称', 'A'] // this.loginForm.class]
const Data = CreateData('12', '测量数据工位_监控系统_操作者工号_增加', param2)
ExecDatabase(Data).then(() => { })
// 存储当前工位号到 cookie
Cookies.set('lastOpName', userform.opName, { expires: 365 }) // 保存一年
Cookies.set('AID', data[0].人员编号, { expires: 1 })
Cookies.set('name', data[0].姓名, { expires: 1 })
Cookies.set('role', data[0].角色编号, { expires: 1 })
Cookies.set('userName', data[0].考勤编号, { expires: 1 })
Cookies.set('opName', userform.opName, { expires: 1 })
Cookies.set('opNameText', userform.opNameText, { expires: 1 })
user.value.AID = response.data[0].人员编号
user.value.name = response.data[0].姓名
user.value.role = response.data[0].角色编号
user.value.userName = response.data[0].考勤编号
user.value.opName = userform.opName
user.value.opNameText = userform.opNameText
ElMessage.success('登录成功')
store.isLoading = true
router.push('/')
}
})
}
</script>
<style lang="scss" scoped>
.main {
position: relative;
height: 100vh;
width: 100vw;
background-image: url("/public/login.png");
background-size: cover;
background-position: center;
display: flex;
align-items: center;
justify-content: center;
.login {
width: 85vw;
padding: 40px 20px;
background-color: rgba(255, 255, 255, 0.8);
border: 2px solid #730215;
border-radius: 16px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
.title {
color: #e43852;
font-size: 42px;
text-align: center;
font-weight: bold;
margin-bottom: 40px;
}
.login-form {
width: 90%;
margin: 0 auto;
.form-item {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
margin-top: 40px;
}
.login-input {
height: 50px;
font-size: 16px;
:deep(.el-input__wrapper) {
background-color: rgba(255, 255, 255, 0.9);
border-radius: 8px;
padding: 0 15px;
backdrop-filter: blur(5px);
}
:deep(.el-input__inner) {
height: 50px;
}
}
.login-select {
width: 100%;
height: 50px;
:deep(.el-input__wrapper) {
background-color: rgba(255, 255, 255, 0.9);
border-radius: 8px;
padding: 0 15px;
backdrop-filter: blur(5px);
}
:deep(.el-input__inner) {
height: 50px;
font-size: 16px;
}
}
.login-button {
width: 100%;
height: 50px;
font-size: 18px;
font-weight: bold;
border-radius: 8px;
background-color: #e43852;
border: none;
&:hover {
background-color: darken(#e43852, 10%);
}
&:active {
background-color: darken(#e43852, 20%);
}
}
}
}
}
}
// 适配不同屏幕尺寸
@media screen and (min-width: 768px) {
.main {
.login {
width: 70vw;
max-width: 600px;
}
}
}
@media screen and (max-width: 767px) {
.main {
.login {
width: 90vw;
.title {
font-size: 36px;
}
}
}
}
</style>

View File

@@ -0,0 +1,642 @@
<template>
<div class="container">
<!-- 数据栏 -->
<div class="data-section">
<div class="data-grid">
<div class="data-item">
<span class="label">工位名称</span>
<span class="value">{{ user.opNameText }}</span>
</div>
<div class="data-item">
<span class="label">工位编号</span>
<span class="value">{{ user.opName }}</span>
</div>
<div class="data-item">
<span class="label">操作者</span>
<span class="value">{{ user.name }}</span>
</div>
<div class="data-item">
<span class="label">产品编号</span>
<span class="value long-text">{{ opSatus.productNumber }}</span>
</div>
<div class="data-item">
<span class="label">产品型号</span>
<span class="value">{{ opSatus.productModel }}</span>
</div>
<div class="data-item">
<span class="label">订单号</span>
<span class="value">{{ opSatus.orderNumber }}</span>
</div>
</div>
<div class="data-item chassis-item">
<span class="label">底盘编号</span>
<span class="value">{{ opSatus.chassisNumber }}</span>
</div>
</div>
<!-- 功能按钮 -->
<div class="button-section">
<el-button type="primary" class="action-button" @click="showScanDialog">
产品进站
</el-button>
<el-button type="warning" class="action-button" @click="handleProblemReport">
检查项确认
</el-button>
<el-button type="success" class="action-button" @click="handleProcessComplete">
装配完成
</el-button>
<el-button type="danger" class="action-button" @click="handleLogout">
退出登录
</el-button>
</div>
<!-- 扫码弹窗 -->
<el-dialog v-model="scanDialogVisible" title="扫码" width="80%" :close-on-click-modal="false"
:close-on-press-escape="false" :show-close="false">
<div class="scan-dialog-content">
<el-input v-model="scanCode" placeholder="请扫描或输入随车码" size="large" @keyup.enter="submitScanCode" />
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeScanDialog">取消</el-button>
<el-button type="primary" @click="submitScanCode">
提交码值
</el-button>
</span>
</template>
</el-dialog>
<!-- 装配完成弹窗 -->
<el-dialog v-model="workFinishDialogVisible" title="装配完成" width="80%" :close-on-click-modal="false"
:close-on-press-escape="false" :show-close="false">
<div class="work-finish-dialog-content">
<el-form :model="workFinishForm" :rules="workFinishFormRules" ref="workFinishFormRef">
<el-form-item label="订单号:" prop="orderCode">
<el-input v-model="workFinishForm.orderCode" readonly />
</el-form-item>
<el-form-item label="产品编号:" prop="productNumber">
<el-input v-model="workFinishForm.productNumber" readonly />
</el-form-item>
<el-form-item label="产品型号:" prop="productModel">
<el-input v-model="workFinishForm.productModel" readonly />
</el-form-item>
<el-form-item label="底盘编号:" prop="chassisNumber">
<el-input v-model="workFinishForm.chassisNumber" readonly />
</el-form-item>
<el-form-item label="结果:" prop="isOk">
<el-select v-model="workFinishForm.isOk" placeholder="请选择结果">
<el-option label="通过" value="1" />
<el-option label="未通过" value="0" />
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="workFinishDialogVisible = false">取消</el-button>
<el-button type="primary" @click="finishWorkSubmit">
提交
</el-button>
</span>
</template>
</el-dialog>
<!-- 检查项弹窗 -->
<el-dialog v-model="checkItemsDialogVisible" title="检查项确认" :top="'10vh'" width="100%"
:close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
<div v-loading="checkItemsLoading" class="check-items-dialog-content">
<el-table :data="checkItemsList" border style="width: 100%" max-height="400px">
<el-table-column prop="排序" label="序号" width="60" align="center" />
<el-table-column prop="检查内容" label="检查内容" min-width="120">
<template #default="scope">
<div class="multiline-cell">{{ scope.row.检查内容 }}</div>
</template>
</el-table-column>
<el-table-column prop="检查要求" label="检查要求" min-width="180">
<template #default="scope">
<div class="multiline-cell">{{ scope.row.检查要求 }}</div>
</template>
</el-table-column>
<el-table-column label="状态" width="120" align="center">
<template #default="scope">
<el-tag v-if="scope.row.检查结果 === '0'" type="warning">未确认</el-tag>
<el-tag v-else-if="scope.row.检查结果 === '1'" type="success">OK</el-tag>
<el-tag v-else-if="scope.row.检查结果 === '2'" type="danger">NOK</el-tag>
<span v-else>{{ scope.row.检查结果 }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="scope">
<div v-if="scope.row.检查结果 === '0'">
<el-button type="success" size="mini" @click="handleCheckResult(scope.row, '1')">OK</el-button>
<el-button type="danger" size="mini" @click="handleCheckResult(scope.row, '2')">NOK</el-button>
</div>
<div v-else>
<el-button type="info" size="mini" disabled>已确认</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="检查备注" label="备注" min-width="120">
<template #default="scope">
<span v-if="scope.row.检查结果 !== '0'">{{ scope.row.检查备注 || '-' }}</span>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="checkItemsDialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
<!-- NOK备注弹窗 -->
<el-dialog v-model="nokRemarkDialogVisible" title="NOK备注" width="500px" :append-to-body="true">
<el-form :model="nokRemarkForm" ref="nokRemarkFormRef" :rules="nokRemarkRules">
<el-form-item label="NOK原因" prop="remark" label-width="80px">
<el-input v-model="nokRemarkForm.remark" type="textarea" :rows="3" placeholder="请输入NOK原因">
</el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="nokRemarkDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmNokRemark">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
import { useAppStore } from '@/store/app.js'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
const router = useRouter()
const store = useAppStore()
const { user, isLoading, routerData } = storeToRefs(store)
const ExecDatabaseByParam = inject('ExecDatabaseByParam')
const isEnableUI = ref(false)
const carSectionList = reactive([])
const opSatus = reactive({
workstationName: '',
workstationCode: '',
operator: '',
productNumber: '',
productModel: '',
orderNumber: '',
chassisNumber: '',
})
const nowStatus = reactive({
isPeople: false,
isCarArrive: false,
isRgvShipping: false,
})
var timer1 = null
// 扫码相关
const scanDialogVisible = ref(false)
const scanCode = ref('')
// 装配完成相关
const workFinishDialogVisible = ref(false)
const workFinishForm = reactive({
orderCode: '',
productNumber: '',
productModel: '',
chassisNumber: '',
isOk: '1'
})
const workFinishFormRef = ref(null)
const workFinishFormRules = {
orderCode: [
{ required: true, message: '请输入订单号', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在2到50个字符之间', trigger: 'blur' }
],
productNumber: [
{ required: true, message: '请输入产品编号', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在2到50个字符之间', trigger: 'blur' }
],
productModel: [
{ required: true, message: '请输入产品型号', trigger: 'blur' },
{ min: 0, max: 50, message: '长度在2到50个字符之间', trigger: 'blur' }
],
chassisNumber: [
{ required: true, message: '请输入底盘编号', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在2到50个字符之间', trigger: 'blur' }
],
isOk: [
{ required: true, message: '请选择结果', trigger: 'change' }
]
}
// 检查项相关
const checkItemsDialogVisible = ref(false)
const checkItemsLoading = ref(false)
const checkItemsList = ref([])
const nokRemarkDialogVisible = ref(false)
const nokRemarkForm = reactive({
id: null,
remark: '',
resultType: ''
})
const nokRemarkRules = {
remark: [
{ required: true, message: '请输入NOK原因', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在2到50个字符之间', trigger: 'blur' }
]
}
onMounted(() => {
setTimeout(() => {
getOpMoby()
}, 1000)
store.isLoading = false
})
// 离开网页 删除定时器
onUnmounted(() => {
clearInterval(timer1)
})
// 显示扫码弹窗
const showScanDialog = async () => {
await getOpMoby()
if (opSatus.productNumber != '') {
ElMessage.warning('当前工件未离站 不可重新进站')
return
}
scanCode.value = ''
scanDialogVisible.value = true
}
// 关闭扫码弹窗
const closeScanDialog = () => {
scanDialogVisible.value = false
scanCode.value = ''
}
// 提交扫码数据
const submitScanCode = async () => {
if (!scanCode.value) {
ElMessage.warning('请输入随车码')
return
}
try {
const param = []
param[0] = ['工位号', user.value.opName]
param[1] = ['产品编码', scanCode.value]
param[2] = ['操作者', user.value.name]
let response = await ExecDatabaseByParam('11', 'CATL_测量数据车体MOBY_车体进站', param)
if (response.data.length > 0) {
const isOk = response.data[0].是否合格
const msg = response.data[0].msg
if (isOk === 1) {
ElMessage.success(msg || '产品进站成功')
closeScanDialog()
getOpMoby()
} else {
ElMessage.error(msg || '产品进站失败')
}
} else {
ElMessage.error('产品进站失败!')
}
} catch (error) {
console.error('提交失败:', error)
ElMessage.error('系统错误,请重试')
}
}
// 获取工位数据
const getOpMoby = async () => {
let param = []
console.log(store.user.opName)
param[0] = ['工位号', store.user.opName]
let response = await ExecDatabaseByParam('11', 'CATL_测量数据工位MOBY_详细信息_查询', param)
if (response.data.length > 0) {
opSatus.workstationName = response.data[0].工位名称
opSatus.workstationCode = response.data[0].工位编号
opSatus.operator = response.data[0].操作者
opSatus.productNumber = response.data[0].Last_发动机号
opSatus.productModel = response.data[0].Last_机型号
opSatus.orderNumber = response.data[0].Last_工单号
opSatus.chassisNumber = response.data[0].车体二维码
} else {
opSatus.workstationName = ''
opSatus.workstationCode = ''
opSatus.productNumber = ''
opSatus.productModel = ''
opSatus.orderNumber = ''
opSatus.chassisNumber = ''
}
}
// 装配完成前检查
const handleProcessComplete = async () => {
if (!opSatus.productNumber) {
ElMessage.error('工位未在工作!')
return
}
try {
let param = []
param[0] = ['产品编码', opSatus.productNumber]
let response = await ExecDatabaseByParam('11', 'CATL_测量数据_出站前检查', param)
if (response.data.length > 0) {
const isOk = response.data[0].是否合格
const msg = response.data[0].msg
if (isOk === 1) {
showFinishDialog()
} else {
ElMessage.error(msg || '出站检查未通过')
}
} else {
ElMessage.error('产品出站失败!')
}
} catch (error) {
console.error('出站检查失败:', error)
ElMessage.error('系统错误,请重试')
}
}
// 显示装配完成弹窗
const showFinishDialog = () => {
workFinishForm.orderCode = opSatus.orderNumber
workFinishForm.productNumber = opSatus.productNumber
workFinishForm.productModel = opSatus.productModel
workFinishForm.chassisNumber = opSatus.chassisNumber
workFinishForm.isOk = '1'
workFinishDialogVisible.value = true
}
// 装配完成提交
const finishWorkSubmit = async () => {
try {
let param = []
param[0] = ['发动机流水号', workFinishForm.productNumber]
param[1] = ['工位编码', user.value.opName]
param[2] = ['托盘号', 1]
param[3] = ['过点属性', 1]
param[4] = ['采集时间', '']
param[5] = ['操作者', user.value.name]
param[6] = ['合格标志', workFinishForm.isOk]
param[7] = ['过站状态', 1]
param[8] = ['岔道上下件', 1]
param[9] = ['重量', '0']
param[10] = ['操作标记', 1]
param[11] = ['车体二维码', workFinishForm.chassisNumber]
let response = await ExecDatabaseByParam('12', 'Event_MOM_过点数据', param)
if (response.data.length > 0 && response.data[0].result == "1") {
ElMessage.success('装配完成')
workFinishDialogVisible.value = false
getOpMoby() // 刷新数据
} else {
ElMessage.error(response.data[0]?.msg || '装配完成失败')
}
} catch (error) {
console.error('装配完成失败:', error)
ElMessage.error('系统错误,请重试')
}
}
// 检查项相关
const handleProblemReport = async () => {
if (!opSatus.productNumber) {
ElMessage.error('工位未在工作!')
return
}
checkItemsDialogVisible.value = true
loadCheckItems()
}
// 加载检查项列表
const loadCheckItems = async () => {
checkItemsLoading.value = true
try {
let param = []
param[0] = ['工位号', user.value.opName]
param[1] = ['工件编号', opSatus.productNumber]
let response = await ExecDatabaseByParam('11', 'CATL_工位检查项_记录表_工位查询', param)
checkItemsList.value = response.data || []
} catch (error) {
console.error('加载检查项失败:', error)
ElMessage.error('加载检查项失败')
} finally {
checkItemsLoading.value = false
}
}
// 处理检查结果
const handleCheckResult = (row, resultType) => {
if (resultType === '2') {
// NOK需要填写备注
nokRemarkForm.id = row.ID
nokRemarkForm.resultType = resultType
nokRemarkForm.remark = ''
nokRemarkDialogVisible.value = true
} else {
// OK直接提交
submitCheckResult(row.ID, resultType, '')
}
}
// 确认NOK备注
const confirmNokRemark = () => {
if (!nokRemarkForm.remark) {
ElMessage.warning('请输入NOK原因')
return
}
submitCheckResult(nokRemarkForm.id, nokRemarkForm.resultType, nokRemarkForm.remark)
nokRemarkDialogVisible.value = false
}
// 提交检查结果
const submitCheckResult = async (id, resultType, remark) => {
try {
let param = []
param[0] = ['ID', id]
param[1] = ['操作者', user.value.name]
param[2] = ['检查结果', resultType]
param[3] = ['检查备注', remark]
let response = await ExecDatabaseByParam('12', 'CATL_工位检查项_记录表_工位检查确认', param)
if (response.data?.[0]?.result === '1') {
ElMessage.success(resultType === '1' ? '检查项确认OK' : '检查项确认NOK')
loadCheckItems() // 刷新列表
} else {
ElMessage.error('提交失败')
}
} catch (error) {
console.error('提交检查结果失败:', error)
ElMessage.error('提交失败')
}
}
// 退出登录
const handleLogout = () => {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
ElMessage.warning('退出登录成功!')
store.$reset() // 重置pinia数据
router.push('/login')
}).catch(() => { })
}
</script>
<style lang="scss" scoped>
.container {
height: 80vh;
display: flex;
flex-direction: column;
padding: 20px;
background-color: #f5f7fa;
}
.data-section {
flex: 1;
background-color: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.data-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-bottom: 15px;
}
.data-item {
display: flex;
align-items: center;
font-size: 1.2rem;
padding: 10px;
border-bottom: 1px solid #ebeef5;
.label {
width: 120px;
color: #606266;
font-weight: bold;
}
.value {
flex: 1;
color: #303133;
word-break: break-all;
}
.long-text {
font-size: 1rem;
}
}
.chassis-item {
grid-column: 1 / -1;
border-top: 2px solid #dcdfe6;
margin-top: 10px;
padding-top: 15px;
}
.button-section {
display: flex;
flex-direction: column;
gap: 15px;
padding: 20px;
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.action-button {
height: 60px;
font-size: 1.2rem;
border-radius: 8px;
}
.el-button+.el-button {
margin-left: 0px;
}
// 响应式调整
@media screen and (max-width: 1024px) {
.data-item {
font-size: 1rem;
.label {
width: 100px;
}
}
.action-button {
height: 50px;
font-size: 1rem;
}
}
.scan-dialog-content {
padding: 20px;
.el-input {
width: 100%;
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
// 调整按钮样式
.button-section {
.el-button--danger {
background-color: #f56c6c;
border-color: #f56c6c;
&:hover {
background-color: #f78989;
border-color: #f78989;
}
}
}
.work-finish-dialog-content {
padding: 20px;
}
.check-items-dialog-content {
padding: 20px;
}
.nok-remark-dialog-content {
padding: 20px;
}
.multiline-cell {
white-space: pre-wrap;
word-break: break-all;
line-height: 1.5;
}
</style>