登录界面优化

This commit is contained in:
2026-06-18 15:10:50 +08:00
parent d7461853cf
commit 8041f5734b
16 changed files with 1844 additions and 151 deletions
+24
View File
@@ -137,3 +137,27 @@ export const reqTestPackageNoList = (unitWorkId, isFinish, testPackageNo)=>get(`
* @param {String} ptp_Id * @param {String} ptp_Id
*/ */
export const reqTestPackageDetail = (ptp_Id)=>get(`TestPackage/GetTestPackageDetail?ptp_Id=${ptp_Id}`) export const reqTestPackageDetail = (ptp_Id)=>get(`TestPackage/GetTestPackageDetail?ptp_Id=${ptp_Id}`)
// =========================== 扫码 ========================================
/**
* 获取材料详情 (入库单管理)
* @param {String} materialCode
*/
export const reqMaterialInfoByMaterialCode = (materialCode)=>get(`BaseInfo/GetMaterialInfoByMaterialCode?materialCode=${materialCode}`)
/**
* 获取焊前基础信息
* @param {String} WeldJointId
*/
export const reqPreWeldJointByWeldJointId = (WeldJointId)=>get(`PreWeldInspection/getPreWeldJointByWeldJointId?WeldJointId=${WeldJointId}`)
/**
* 保存焊前基础信息(材料编码及炉批号校验、材料数量校验)
* @param {Object} data
*/
export const reqSaveCuttingCheck = (data)=> post(`PreWeldInspection/SaveCuttingCheck`, data)
/**
* 获取坡口类型列表
* TODO: 确认接口地址
*/
export const reqGetGrooveTypeList = ()=>get(`BaseInfo/GetGrooveTypeList`)
+144 -32
View File
@@ -2,45 +2,108 @@
// 登录 // 登录
.login { .login {
display: flex;
flex-direction: column;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: $u-bg-white; background: #f0f2f5;
overflow: hidden;
.sys-name { // 顶部区域:背景图 + Logo + 标题
font-size: 48rpx; .login-header {
font-weight: 700; flex-shrink: 0;
color: #333333;
position: absolute; &__bg {
top: 300rpx; display: block;
width: 100%; width: 100%;
text-align: center; height: auto;
}
}
// 表单卡片
.login-card {
flex: 1;
position: relative;
z-index: 1;
margin: -40rpx 32rpx 0;
background: #ffffff;
border-radius: 24rpx 24rpx 0 0;
padding: 48rpx 40rpx 0;
box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.08);
&__brand {
display: flex;
align-items: center;
justify-content: center;
gap: 24rpx;
margin-bottom: 40rpx;
}
.login-logo {
display: block;
width: 100rpx;
height: 100rpx;
flex-shrink: 0;
}
.sys-name {
font-size: 36rpx;
font-weight: 700;
color: #1a1a2e;
letter-spacing: 2rpx;
}
} }
.login-form { .login-form {
position: absolute; :deep(.u-form-item) {
bottom: 240rpx; margin-bottom: 24rpx;
right: 60rpx; }
left: 60rpx;
:deep(.u-form-item__body) {
padding: 18rpx 28rpx;
background: #f5f6f8;
border-radius: 14rpx;
border: 1rpx solid transparent;
transition: border-color 0.25s;
}
:deep(.u-input__input) {
font-size: 30rpx;
color: #1a1a2e;
&::placeholder {
color: #b0bec5;
}
}
:deep(.u-button--primary) {
height: 92rpx;
background: linear-gradient(135deg, #2979ff, #5a9aff);
border-radius: 14rpx;
font-size: 32rpx;
font-weight: 600;
letter-spacing: 2rpx;
box-shadow: 0 6rpx 24rpx rgba(41, 121, 255, 0.35);
border: none;
&::after {
border: none;
}
&:active {
opacity: 0.9;
transform: scale(0.98);
}
}
} }
} }
// 首页 // 首页
.home { .home {
&-navbar{ display: flex;
padding-top: 40px; flex-direction: column;
height: 85px; height: 100vh;
background-color: var(--u-type-primary);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
&-title {
font-size: 32rpx;
color: #ffffff;
position: relative;
}
}
.u-border-bottom:after { .u-border-bottom:after {
border: none; border: none;
} }
@@ -61,11 +124,12 @@
height: 85px; height: 85px;
background-color: $u-type-primary; background-color: $u-type-primary;
padding: 40px 20rpx 0; padding: 40px 20rpx 0;
flex-shrink: 0;
&-title { &-title {
flex: 1; flex: 1;
text-align: center; text-align: center;
font-size: 28rpx; font-size: 14px;
color: #ffffff; color: #ffffff;
} }
@@ -84,6 +148,7 @@
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
padding: 0 20rpx; padding: 0 20rpx;
flex-shrink: 0;
} }
&-icon { &-icon {
@@ -102,6 +167,13 @@
margin-left: 60rpx; margin-left: 60rpx;
} }
&-scroll {
flex: 1;
overflow-y: auto;
height: 0;
padding-bottom: 10px;
}
&-hj { &-hj {
margin: 20rpx; margin: 20rpx;
background: #fff; background: #fff;
@@ -299,14 +371,15 @@
} }
.edit-grid { .edit-grid {
width: 100%; flex: 1;
overflow-y: scroll; min-height: 0;
overflow: hidden;
display: grid; display: grid;
grid-template-columns: repeat(4, calc(25% - 20rpx)); grid-template-columns: repeat(4, calc(25% - 20rpx));
gap: 20rpx; gap: 20rpx;
padding: 24rpx 0; padding: 24rpx 0 160rpx;
min-height: 0;
box-sizing: border-box; box-sizing: border-box;
align-content: flex-start;
} }
.edit-item { .edit-item {
@@ -327,6 +400,10 @@
border: 2rpx solid $u-type-primary; border: 2rpx solid $u-type-primary;
} }
&.hidden {
display: none;
}
.icon-box { .icon-box {
width: 96rpx; width: 96rpx;
height: 96rpx; height: 96rpx;
@@ -361,6 +438,41 @@
} }
} }
// 搜索框
.edit-search {
display: flex;
align-items: center;
gap: 12rpx;
margin: 16rpx 0 8rpx;
padding: 18rpx 24rpx;
background: #f5f6f8;
border-radius: 12rpx;
&__input {
flex: 1;
font-size: 26rpx;
color: #333;
background: transparent;
border: none;
outline: none;
&::placeholder {
color: #b0bec5;
}
}
}
// 空状态
.edit-empty {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: center;
padding: 60rpx 0;
font-size: 26rpx;
color: #999;
}
.edit-btns { .edit-btns {
display: flex; display: flex;
gap: 24rpx; gap: 24rpx;
+11 -3
View File
@@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "nbd-icon"; /* Project id 5193086 */ font-family: "nbd-icon"; /* Project id 5193086 */
src: url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff2?t=1781056075220') format('woff2'), src: url('//at.alicdn.com/t/c/font_5193086_0r8nbjxfoygi.woff2?t=1781694668587') format('woff2'),
url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.woff?t=1781056075220') format('woff'), url('//at.alicdn.com/t/c/font_5193086_0r8nbjxfoygi.woff?t=1781694668587') format('woff'),
url('//at.alicdn.com/t/c/font_5193086_dyc00qt3bz.ttf?t=1781056075220') format('truetype'); url('//at.alicdn.com/t/c/font_5193086_0r8nbjxfoygi.ttf?t=1781694668587') format('truetype');
} }
.nbd-icon { .nbd-icon {
@@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.nbd-icon-zudui:before {
content: "\e619";
}
.nbd-icon-xialiaochoujianjilu:before {
content: "\e609";
}
.nbd-icon-a-facheguanli:before { .nbd-icon-a-facheguanli:before {
content: "\e683"; content: "\e683";
} }
+23
View File
@@ -123,6 +123,29 @@
} }
} }
] ]
},
{
"root": "scanpages",
"pages": [
{
"path": "hj/materials_detail",
"style": {
"navigationBarTitleText": "材料详情"
}
},
{
"path": "hj/material_sampling",
"style": {
"navigationBarTitleText": "下料抽检"
}
},
{
"path": "hj/make_right",
"style": {
"navigationBarTitleText": "组队抽查"
}
}
]
} }
], ],
"tabBar": { "tabBar": {
+152 -88
View File
@@ -19,95 +19,99 @@
<text>待办</text> <text>待办</text>
</view> </view>
</view> </view>
<view class="home-hj"> <scroll-view class="home-scroll" scroll-y>
<view class="hj-content"> <view class="home-hj">
<view class="hj-row"> <view class="hj-content">
<view class="hj-cell"> <view class="hj-row">
<text class="hj-label">工厂焊接一次合格率</text> <view class="hj-cell">
<text class="hj-value">{{ hjIndexData?.GCFirstPassRate|| '--'}}</text> <text class="hj-label">工厂焊接一次合格率</text>
<text class="hj-value">{{ hjIndexData?.GCFirstPassRate|| '--'}}</text>
</view>
<view class="hj-cell">
<text class="hj-label">现场焊接一次合格率</text>
<text class="hj-value">{{ hjIndexData?.XCFirstPassRate || '--' }}</text>
</view>
</view> </view>
<view class="hj-cell"> <view class="hj-row">
<text class="hj-label">现场焊接一次合格率</text> <view class="hj-cell">
<text class="hj-value">{{ hjIndexData?.XCFirstPassRate || '--' }}</text> <text class="hj-label">工厂预制进度</text>
<text class="hj-value hj-sub">{{ hjIndexData?.GCProgress|| '--' }}</text>
</view>
<view class="hj-cell">
<text class="hj-label">现场焊接进度</text>
<text class="hj-value hj-sub">{{ hjIndexData?.XCProgress || '--'}}</text>
</view>
</view>
<view class="hj-row">
<view class="hj-cell">
<text class="hj-label">工厂焊工功效</text>
<text class="hj-value">{{ hjIndexData?.GCWelderEfficacy || '--'}}</text>
</view>
<view class="hj-cell">
<text class="hj-label">现场焊接功效</text>
<text class="hj-value">{{ hjIndexData?.XCWelderEfficacy || '--'}}</text>
</view>
</view> </view>
</view> </view>
<view class="hj-row"> <view class="hj-footer">
<view class="hj-cell"> <text class="hj-footer-label">项目管道实时预制率 (包含工厂和现场的预制口)</text>
<text class="hj-label">工厂预制进度</text> <text class="hj-footer-value">{{ hjIndexData?.PipePrefabricationRate || '--'}}</text>
<text class="hj-value hj-sub">{{ hjIndexData?.GCProgress|| '--' }}</text>
</view>
<view class="hj-cell">
<text class="hj-label">现场焊接进度</text>
<text class="hj-value hj-sub">{{ hjIndexData?.XCProgress || '--'}}</text>
</view>
</view>
<view class="hj-row">
<view class="hj-cell">
<text class="hj-label">工厂焊工功效</text>
<text class="hj-value">{{ hjIndexData?.GCWelderEfficacy || '--'}}</text>
</view>
<view class="hj-cell">
<text class="hj-label">现场焊接功效</text>
<text class="hj-value">{{ hjIndexData?.XCWelderEfficacy || '--'}}</text>
</view>
</view> </view>
</view> </view>
<view class="hj-footer">
<text class="hj-footer-label">项目管道实时预制率 (包含工厂和现场的预制口)</text>
<text class="hj-footer-value">{{ hjIndexData?.PipePrefabricationRate || '--'}}</text>
</view>
</view>
<!-- 快捷应用 --> <!-- 快捷应用 -->
<view class="section-card home-quick"> <view class="section-card home-quick">
<view class="section-title"> <view class="section-title">
<view class="title-left"> <view class="title-left">
<text class="title-line"></text> <text class="title-line"></text>
<text>快捷应用</text> <text>快捷应用</text>
</view>
<u-icon class="edit-btn" name="edit-pen" :size="32" color="#999" @click="openEditQuick" />
</view>
<view class="section-grid">
<view class="grid-item" v-for="(item, idx) in quickApps" :key="idx" @click="handleSkip(item)">
<view class="icon-box">
<u-icon :name="item.icon" :size="56" :custom-prefix='item.prefix'
:color="pastelColors[idx % 6]" />
</view> </view>
<text class="item-label">{{ item.name }}</text> <u-icon class="edit-btn" name="edit-pen" :size="32" color="#999" @click="openEditQuick" />
</view> </view>
<view v-if="quickApps.length === 0" class="empty-tip">请选择应用</view> <view class="section-grid">
</view> <view class="grid-item" v-for="(item, idx) in quickApps" :key="idx" @click="handleSkip(item)">
</view> <view class="icon-box">
<u-icon :name="item.icon" :size="56" :custom-prefix='item.prefix'
:color="pastelColors[idx % 6]" />
</view>
<!-- 业务查询 --> <text class="item-label">{{ item.name }}</text>
<view class="section-card home-query" v-if="queryApps && queryApps.length > 0">
<view class="section-title">
<view class="title-left">
<text class="title-line"></text>
<text>业务查询</text>
</view>
</view>
<view class="section-grid">
<view class="grid-item" v-for="(item, idx) in queryApps" :key="idx" @click="handleSkip(item)">
<view class="icon-box">
<u-icon :name="item.icon" :custom-prefix='item.prefix' :size="56"
:color="pastelColors[idx % 6]" />
</view> </view>
<text class="item-label">{{ item.name }}</text> <view v-if="quickApps.length === 0" class="empty-tip">请选择应用</view>
</view> </view>
</view> </view>
</view>
<!-- 业务查询 -->
<view class="section-card home-query" v-if="queryApps && queryApps.length > 0">
<view class="section-title">
<view class="title-left">
<text class="title-line"></text>
<text>业务查询</text>
</view>
</view>
<view class="section-grid">
<view class="grid-item" v-for="(item, idx) in queryApps" :key="idx" @click="handleSkip(item)">
<view class="icon-box">
<u-icon :name="item.icon" :custom-prefix='item.prefix' :size="56"
:color="pastelColors[idx % 6]" />
</view>
<text class="item-label">{{ item.name }}</text>
</view>
</view>
</view>
</scroll-view>
<!-- 编辑快捷应用弹窗 --> <!-- 编辑快捷应用弹窗 -->
<u-popup v-model="showEditQuick" mode="bottom" height="70%"> <u-popup v-model="showEditQuick" mode="bottom" height="75%">
<view class="edit-quick"> <view class="edit-quick">
<view class="edit-title"> <view class="edit-title">
<text>选择快捷应用</text> <text>选择快捷应用</text>
</view> </view>
<view class="edit-grid"> <view class="edit-search">
<view v-for="(item, idx) in editCache" :key="idx" class="edit-item" <u-icon name="search" :size="28" color="#999" />
<input class="edit-search__input" v-model="keyword" placeholder="搜索应用名称" confirm-type="search" />
</view>
<scroll-view class="edit-grid" scroll-y>
<view v-for="(item, idx) in filteredEditCache" :key="idx" class="edit-item"
:class="{ active: item.selected }" @click="toggleSelect(item)"> :class="{ active: item.selected }" @click="toggleSelect(item)">
<view class="icon-box"> <view class="icon-box">
<u-icon :name="item.icon" :size="48" :custom-prefix='item.prefix' <u-icon :name="item.icon" :size="48" :custom-prefix='item.prefix'
@@ -118,7 +122,10 @@
<u-icon name="checkmark" :size="24" color="#fff" /> <u-icon name="checkmark" :size="24" color="#fff" />
</view> </view>
</view> </view>
</view> <view v-if="filteredEditCache.length === 0" class="edit-empty">
<text>没有匹配的应用</text>
</view>
</scroll-view>
<view class="edit-btns"> <view class="edit-btns">
<u-button size="medium" class="btn-cancel" @click="showEditQuick = false">取消</u-button> <u-button size="medium" class="btn-cancel" @click="showEditQuick = false">取消</u-button>
<u-button size="medium" class="btn-confirm" type="primary" @click="handleConfirmEdit">确定</u-button> <u-button size="medium" class="btn-confirm" type="primary" @click="handleConfirmEdit">确定</u-button>
@@ -151,8 +158,11 @@
} from 'pinia'; } from 'pinia';
import { import {
scanToSkipUrl parseScanResult
} from '@/utils/scanUtils.js' } from '@/utils/scanUtils.js'
import {
getUrlParam
} from '@/utils/request.js'
const userStore = useUserStore() const userStore = useUserStore()
const menuStore = useMenuStore() const menuStore = useMenuStore()
@@ -180,14 +190,17 @@
const projectNameKeyword = ref('') const projectNameKeyword = ref('')
const showProject = ref(false) const showProject = ref(false)
const showEditQuick = ref(false) const showEditQuick = ref(false)
const keyword = ref('')
const currentProjectName = computed(() => currentProject.value?.ProjectName || '') const currentProjectName = computed(() => currentProject.value?.ProjectName || '')
const handleSkipByPath = (path)=>{ // 搜索过滤后的编辑列表
uni.navigateTo({ const filteredEditCache = computed(() => {
url: path const kw = keyword.value.trim().toLowerCase()
}) if (!kw) return editCache.value
} return editCache.value.filter(item => item.name.toLowerCase().includes(kw))
})
const handleSkip = (item) => { const handleSkip = (item) => {
if (!currentProject.value.ProjectId) { if (!currentProject.value.ProjectId) {
handleShowProjectSelect() handleShowProjectSelect()
@@ -197,6 +210,10 @@
}) })
return return
} }
if (item.isScan) {
handleMenuScan(item.path)
return
}
uni.navigateTo({ uni.navigateTo({
url: item.path url: item.path
}) })
@@ -205,6 +222,7 @@
// 打开编辑弹窗 // 打开编辑弹窗
const openEditQuick = () => { const openEditQuick = () => {
menuStore.openEdit() menuStore.openEdit()
keyword.value = ''
showEditQuick.value = true showEditQuick.value = true
} }
@@ -243,23 +261,69 @@
projectNameKeyword.value = keyword projectNameKeyword.value = keyword
} }
/**
* 菜单扫一扫
*/
const handleMenuScan = (path) => {
wx.scanCode({
scanType: ['datamatrix', 'barCode', 'qrCode', 'wxCode', 'pdf417'],
success(res) {
// 获取路径给的ID
let id = getUrlParam(res.result, 'id')
uni.navigateTo({
url: path + (id ? `?id=${id}` : '')
})
},
fail(err) {
console.log(err)
uni.showToast({
title: '扫码失败,请重试',
icon: 'none'
})
}
})
}
/** /**
* 扫一扫 * 扫一扫
*/ */
const handleScan = () => { const handleScan = () => {
uni.scanCode({ // 调起条码扫描
wx.scanCode({
scanType: ['datamatrix', 'barCode', 'qrCode', 'wxCode', 'pdf417'],
success(res) { success(res) {
console.log('扫码结果:', res) console.log('扫码结果==>', res)
const result = res.result || ''
const url = scanToSkipUrl(result) if (res.scanType != 'QR_CODE') {
if (url) { uni.navigateTo({
uni.navigateTo({ url }) url: '/scanpages/hj/materials_detail?id=' + res.result
} else { })
uni.showToast({ title: '无法识别的二维码/条形码', icon: 'none' }) }
if (res.scanType == 'QR_CODE') {
const result = res.result || ''
// 优先按 URL 规则解析(焊接接头等)
const parsed = parseScanResult(result)
if (parsed) {
uni.navigateTo({
url: `${parsed.path}?${parsed.query}`
})
return
}else{
uni.showToast({
title: '无对应业务',
icon: 'none'
})
}
} }
}, },
fail(err) { fail(err) {
console.error('扫码失败:', err) console.log(err)
uni.showToast({
title: '扫码失败,请重试',
icon: 'none'
})
} }
}) })
} }
+28 -19
View File
@@ -1,26 +1,35 @@
<template> <template>
<view class="wrap login"> <view class="wrap login">
<view class="sys-name"> <!-- 顶部背景图 -->
施工管理系统 <view class="login-header">
<image class="login-header__bg" src="/static/images/bg.jpg" mode="widthFix" />
</view> </view>
<view class="login-form"> <!-- 登录表单卡片 -->
<u-form :model="form" ref="uFormRef"> <view class="login-card">
<u-form-item prop="account" > <view class="login-card__brand">
<template #leftIcon> <image class="login-logo" src="/static/images/logo.png" mode="aspectFit" />
<u-icon name="account" size="32" /> <view class="sys-name">管道安装管理系统</view>
</template> </view>
<u-input v-model="loginForm.account" placeholder="请输入账号" /></u-form-item> <view class="login-form">
<u-form-item prop="password"> <u-form :model="form" ref="uFormRef">
<template #leftIcon> <u-form-item prop="account">
<u-icon name="lock" size="32" /> <template #leftIcon>
</template> <u-icon name="account" size="32" color="#2979ff" />
<u-input v-model="loginForm.password" type="password" :password-icon="true" placeholder="请输入密码" /> </template>
</u-form-item> <u-input v-model="loginForm.account" placeholder="请输入账号" />
<view class="u-margin-top-60"> </u-form-item>
<u-button type="primary" :loading="loading" @click="fetchLogin">登录</u-button> <u-form-item prop="password">
</view> <template #leftIcon>
</u-form> <u-icon name="lock" size="32" color="#2979ff" />
</template>
<u-input v-model="loginForm.password" type="password" :password-icon="true" placeholder="请输入密码" />
</u-form-item>
<view class="u-margin-top-60">
<u-button type="primary" :loading="loading" @click="fetchLogin">登录</u-button>
</view>
</u-form>
</view>
</view> </view>
</view> </view>
</template> </template>
+463
View File
@@ -0,0 +1,463 @@
<template>
<view class="detail-page">
<!-- 焊口主标识 -->
<view class="joint-hero">
<text class="joint-hero__code">{{ form.WeldJointCode || '焊口编码' }}</text>
<view class="joint-hero__meta">
<view class="joint-hero__pill">
<text class="pill-label">管线编码</text>
<text class="pill-value">{{ form.PipelineCode || '--' }}</text>
</view>
<view class="joint-hero__pill">
<text class="pill-label">项目编号</text>
<text class="pill-value">{{ form.ProjectName || form.ProjectId || '--' }}</text>
</view>
</view>
</view>
<!-- 坡口类型 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">坡口类型</text>
</view>
<view class="card">
<view class="card__row" :class="{ 'card__row--warn': !form.GrooveTypeName }" @click="showGroovePicker = true">
<text class="card__label">坡口类型名称</text>
<view class="card__value-wrap card__value-wrap--clickable">
<text class="card__value" :class="{ 'card__value--placeholder': !form.GrooveTypeName }">
{{ form.GrooveTypeName || '请选择坡口类型' }}
</text>
<u-icon name="arrow-down" :size="24" color="#bbb" />
</view>
</view>
<view class="card__row">
<text class="card__label">坡口类型编码</text>
<text class="card__value">{{ form.GrooveTypeCode || '--' }}</text>
</view>
<view class="card__row">
<text class="card__label">坡口加工类型</text>
<text class="card__value">{{ form.GrooveProcessType || '--' }}</text>
</view>
</view>
</view>
<!-- 坡口参数 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">坡口参数</text>
</view>
<view class="card">
<view class="card__row">
<text class="card__label">坡口角度</text>
<view class="card__value-wrap">
<text class="card__value">{{ form.GrooveAngle ?? '--' }}</text>
<text class="card__unit">°</text>
</view>
</view>
<view class="card__row">
<text class="card__label">组对间隙</text>
<view class="card__value-wrap">
<text class="card__value">{{ form.FitupGap ?? '--' }}</text>
<text class="card__unit">mm</text>
</view>
</view>
<view class="card__row">
<text class="card__label">错边量</text>
<view class="card__value-wrap">
<text class="card__value">{{ form.Misalignment ?? '--' }}</text>
<text class="card__unit">mm</text>
</view>
</view>
</view>
</view>
<!-- 坡口类型选择器 -->
<u-popup v-model="showGroovePicker" mode="bottom" border-radius="16">
<view class="picker-panel">
<view class="picker-panel__header">
<text class="picker-panel__cancel" @click="showGroovePicker = false">取消</text>
<text class="picker-panel__title">选择坡口类型</text>
<text class="picker-panel__confirm" @click="showGroovePicker = false">确定</text>
</view>
<scroll-view class="picker-panel__list" scroll-y>
<view class="picker-option" v-for="(item, idx) in grooveTypeOptions" :key="idx"
:class="{ 'picker-option--active': pickerSelected === idx }"
@click="selectGrooveType(idx)">
<view class="picker-option__main">
<text class="picker-option__label">{{ item.BaseInfoName }}</text>
<text class="picker-option__code">{{ item.BaseInfoCode }}</text>
</view>
<u-icon v-if="pickerSelected === idx" name="checkmark" :size="28" color="#2979ff" />
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/store'
import { reqPreWeldJointByWeldJointId, reqGetGrooveTypeList } from '@/api/hj'
const userStore = useUserStore()
const { currentProject } = storeToRefs(userStore)
const showGroovePicker = ref(false)
const pickerSelected = ref(-1)
const grooveTypeOptions = ref([])
const form = ref({
WeldJointId: '',
WeldJointCode: '',
PipelineId: '',
PipelineCode: '',
ProjectId: '',
GrooveTypeId: '',
GrooveTypeCode: '',
GrooveTypeName: '',
GrooveProcessType: '',
GrooveAngle: '',
FitupGap: '',
Misalignment: ''
})
const fetchGrooveTypes = async () => {
try {
const res = await reqGetGrooveTypeList()
if (res.code === 1 && res.data) {
grooveTypeOptions.value = res.data
}
} catch (err) {
console.error('获取坡口类型列表失败:', err)
}
}
const selectGrooveType = (idx) => {
pickerSelected.value = idx
const item = grooveTypeOptions.value[idx]
form.value.GrooveTypeId = item.BaseInfoId
form.value.GrooveTypeCode = item.BaseInfoCode
form.value.GrooveTypeName = item.BaseInfoName
showGroovePicker.value = false
}
onMounted(() => {
fetchGrooveTypes()
})
onLoad((opt) => {
if (opt?.id) {
loadData(opt.id)
}
if (currentProject.value?.ProjectId) {
form.value.ProjectId = currentProject.value.ProjectId
}
})
const loadData = async (id) => {
try {
uni.showLoading({ title: '加载中...', mask: true })
const res = await reqPreWeldJointByWeldJointId(id)
if (res.code === 1 && res.data) {
form.value = res.data
}
} catch (err) {
console.error('加载焊口数据失败:', err)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
</script>
<style lang="scss" scoped>
$primary: #2979ff;
$primary-light: #e8f0fe;
$primary-mid: #d0ddff;
$primary-dark: #1a56cc;
$bg: #f0f2f5;
$text: #0f1724;
$text-secondary: #6b7c93;
$border: #e8ecf1;
$card-bg: #ffffff;
$radius: 16rpx;
$shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
$shadow-md: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
.detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 180rpx;
}
// ===== 焊口主标识 =====
.joint-hero {
position: relative;
margin: 24rpx;
padding: 36rpx 32rpx 28rpx;
background: $card-bg;
border-radius: $radius;
box-shadow: $shadow-md;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: linear-gradient(90deg, $primary, #6ba3ff, $primary);
background-size: 200% 100%;
animation: shimmer 3s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { background-position: 0% 0; }
50% { background-position: 100% 0; }
}
&__code {
display: block;
font-size: 52rpx;
font-weight: 800;
color: $text;
line-height: 1.2;
word-break: break-all;
font-family: monospace;
letter-spacing: 2rpx;
margin-bottom: 28rpx;
position: relative;
padding-left: 24rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 6rpx;
bottom: 6rpx;
width: 4rpx;
background: $primary;
border-radius: 2rpx;
}
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
&__pill {
display: inline-flex;
width: 100%;
align-items: center;
gap: 8rpx;
background: $primary-light;
padding: 10rpx 18rpx;
border-radius: 10rpx;
border: 1rpx solid $primary-mid;
.pill-label {
font-size: 22rpx;
color: $text-secondary;
width: 60rpx;
}
.pill-value {
font-size: 24rpx;
font-weight: 600;
color: $primary-dark;
font-family: monospace;
}
}
}
// ===== 区块标题 =====
.section {
margin: 0 24rpx 20rpx;
&__header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 14rpx;
padding: 0 6rpx;
}
&__bar {
width: 6rpx;
height: 26rpx;
background: linear-gradient(180deg, $primary, #6ba3ff);
border-radius: 3rpx;
flex-shrink: 0;
}
&__title {
font-size: 28rpx;
font-weight: 600;
color: $text;
letter-spacing: 0.5rpx;
}
}
// ===== 信息卡片 =====
.card {
background: $card-bg;
border-radius: $radius;
overflow: hidden;
box-shadow: $shadow-md;
&__row {
display: flex;
align-items: center;
padding: 22rpx 28rpx;
border-bottom: 1rpx solid $border;
&:last-child {
border-bottom: none;
}
&:active {
background: #fafbfc;
}
&--warn .card__label::before {
background: #ef4444;
}
}
&__label {
width: 180rpx;
font-size: 26rpx;
color: $text-secondary;
flex-shrink: 0;
position: relative;
padding-left: 16rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4rpx;
height: 16rpx;
background: $primary-mid;
border-radius: 2rpx;
}
}
&__value {
flex: 1;
font-size: 28rpx;
color: $text;
font-weight: 500;
text-align: right;
&--placeholder {
color: #b0bec5;
font-weight: 400;
}
}
&__value-wrap {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8rpx;
&--clickable {
min-height: 48rpx;
}
}
&__unit {
font-size: 24rpx;
color: $text-secondary;
flex-shrink: 0;
}
}
// ===== 坡口类型选择器弹窗 =====
.picker-panel {
padding-bottom: env(safe-area-inset-bottom);
&__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
border-bottom: 1rpx solid $border;
}
&__cancel,
&__confirm {
font-size: 26rpx;
color: $text-secondary;
padding: 8rpx;
&:active {
opacity: 0.6;
}
}
&__confirm {
color: $primary;
font-weight: 600;
}
&__title {
font-size: 28rpx;
font-weight: 600;
color: $text;
}
&__list {
max-height: 560rpx;
}
}
.picker-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #f5f5f5;
transition: background 0.2s;
&:active {
background: #f8f9fa;
}
&--active {
background: $primary-light;
}
&__main {
flex: 1;
min-width: 0;
}
&__label {
font-size: 28rpx;
font-weight: 500;
color: $text;
display: block;
}
&__code {
font-size: 22rpx;
color: $text-secondary;
margin-top: 4rpx;
display: block;
font-family: monospace;
}
}
</style>
+686
View File
@@ -0,0 +1,686 @@
<template>
<view class="detail-page">
<!-- 焊口主标识 -->
<view class="joint-hero">
<text class="joint-hero__code">{{ info.WeldJointCode || '--' }}</text>
<view class="joint-hero__meta">
<view class="joint-hero__pill">
<text class="pill-label">管线编码</text>
<text class="pill-value">{{ info.PipelineCode || '--' }}</text>
</view>
<view class="joint-hero__pill">
<text class="pill-label">项目名称</text>
<text class="pill-value">{{ info.ProjectName || info.ProjectId || '--' }}</text>
</view>
</view>
</view>
<!-- 坡口参数 - 测量仪表 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">坡口参数</text>
</view>
<view class="spec-grid">
<view class="spec-item">
<view class="spec-item__header">
<text class="spec-item__label">坡口角度</text>
<text class="spec-item__value">{{ info.GrooveAngle || '--' }}<text class="spec-item__unit">°</text></text>
</view>
<view class="spec-item__bar-track">
<view class="spec-item__bar-fill" :style="{ width: gaugeWidth(info.GrooveAngle, 0, 90) }"></view>
</view>
</view>
<view class="spec-item">
<view class="spec-item__header">
<text class="spec-item__label">组对间隙</text>
<text class="spec-item__value">{{ info.FitupGap || '--' }}<text class="spec-item__unit">mm</text></text>
</view>
<view class="spec-item__bar-track">
<view class="spec-item__bar-fill fill--amber" :style="{ width: gaugeWidth(info.FitupGap, 0, 10) }"></view>
</view>
</view>
<view class="spec-item">
<view class="spec-item__header">
<text class="spec-item__label">错边量</text>
<text class="spec-item__value">{{ info.Misalignment || '--' }}<text class="spec-item__unit">mm</text></text>
</view>
<view class="spec-item__bar-track">
<view class="spec-item__bar-fill fill--teal" :style="{ width: gaugeWidth(info.Misalignment, 0, 5) }"></view>
</view>
</view>
</view>
</view>
<!-- 坡口类型 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">坡口信息</text>
</view>
<view class="card">
<view class="card__row">
<text class="card__label">坡口类型名称</text>
<view class="card__value-wrap">
<text class="card__value">{{ info.GrooveTypeName || '--' }}</text>
</view>
</view>
<view class="card__row">
<text class="card__label">坡口类型编号</text>
<view class="card__value-wrap">
<text class="card__value">{{ info.GrooveTypeCode || '--' }}</text>
</view>
</view>
<view class="card__row">
<text class="card__label">坡口加工类型</text>
<text class="card__value">{{ info.GrooveProcessType || '--' }}</text>
</view>
</view>
</view>
<!-- 材料校验 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">材料校验</text>
</view>
<view class="check-card">
<view class="check-item">
<view class="check-item__left">
<text class="check-item__label">材料编码及炉批号校验</text>
<text class="check-item__desc">核对实物编码与批次信息</text>
</view>
<view class="check-item__action" @click="toggleCheck('CodeBatch')">
<view class="check-btn" :class="{
'check-btn--pass': check.CodeBatch === true,
'check-btn--fail': check.CodeBatch === false
}">
<template v-if="check.CodeBatch === true">
<text class="check-btn__icon"></text>
<text class="check-btn__text">通过</text>
</template>
<template v-else>
<text class="check-btn__icon"></text>
<text class="check-btn__text">不通过</text>
</template>
</view>
</view>
</view>
<view class="check-item">
<view class="check-item__left">
<text class="check-item__label">材料数量校验</text>
<text class="check-item__desc">核对实物数量与单据一致</text>
</view>
<view class="check-item__action" @click="toggleCheck('Quantity')">
<view class="check-btn" :class="{
'check-btn--pass': check.Quantity === true,
'check-btn--fail': check.Quantity === false
}">
<template v-if="check.Quantity === true">
<text class="check-btn__icon"></text>
<text class="check-btn__text">通过</text>
</template>
<template v-else>
<text class="check-btn__icon"></text>
<text class="check-btn__text">不通过</text>
</template>
</view>
</view>
</view>
</view>
</view>
<!-- 备注 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">备注</text>
</view>
<view class="remark-card">
<textarea
class="remark-input"
v-model="remark"
placeholder="请输入备注信息(选填)"
:maxlength="200"
auto-height
/>
<text class="remark-count">{{ remark.length }}/200</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-bar">
<view class="submit-btn" @click="handleSubmit">
<text class="submit-btn__text">提交校验结果</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/store'
import { reqPreWeldJointByWeldJointId, reqSaveCuttingCheck } from '@/api/hj'
const userStore = useUserStore()
const { userInfo, currentProject } = storeToRefs(userStore)
const info = ref({})
const remark = ref('')
// 校验状态:true=通过,false=不通过
const check = ref({
CodeBatch: true,
Quantity: true
})
const toggleCheck = (key) => {
const val = check.value[key]
check.value[key] = val === true ? false : true
}
const handleSubmit = () => {
uni.showModal({
title: '确认提交',
content: '确定提交校验结果吗?',
success: async (res) => {
if (res.confirm) {
const now = new Date()
const pad = (n) => String(n).padStart(2, '0')
const checkTime = `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
const params = {
ProjectId: currentProject.value?.ProjectId,
WeldJointId: info.value?.WeldJointId,
IsMaterialCodeBatchNoAccurate: check.value.CodeBatch,
IsMaterialQuantityAccurate: check.value.Quantity,
CheckPerson: userInfo.value?.PersonId,
CheckTime: checkTime,
Remark: remark.value
}
console.log('提交参数:', params)
await reqSaveCuttingCheck(params)
uni.showToast({ title: '提交成功', icon: 'none' })
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}
})
}
onLoad((opt) => {
if (opt.id) {
getInfo(opt.id)
}
})
const getInfo = async (id) => {
try {
uni.showLoading({ title: '加载中...', mask: true })
const res = await reqPreWeldJointByWeldJointId(id)
if (res.code === 1) { info.value = res.data }
} catch (error) {
console.error('获取焊口详情失败:', error)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
// 模拟进度条宽度(0~100%
const gaugeWidth = (val, min, max) => {
if (!val && val !== 0) return '0%'
const num = parseFloat(val)
if (isNaN(num)) return '0%'
const pct = Math.min(Math.max((num - min) / (max - min) * 100, 0), 100)
return pct + '%'
}
</script>
<style lang="scss" scoped>
$primary: #2979ff;
$primary-light: #e8f0fe;
$primary-mid: #d0ddff;
$primary-dark: #1a56cc;
$bg: #f0f2f5;
$text: #0f1724;
$text-secondary: #6b7c93;
$border: #e8ecf1;
$card-bg: #ffffff;
$radius: 16rpx;
$shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
$shadow-md: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
.detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 180rpx;
}
// ===== 焊口主标识 =====
.joint-hero {
position: relative;
margin: 24rpx;
padding: 36rpx 32rpx 28rpx;
background: $card-bg;
border-radius: $radius;
box-shadow: $shadow-md;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: linear-gradient(90deg, $primary, #6ba3ff, $primary);
background-size: 200% 100%;
animation: shimmer 3s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { background-position: 0% 0; }
50% { background-position: 100% 0; }
}
&__code {
display: block;
font-size: 52rpx;
font-weight: 800;
color: $text;
line-height: 1.2;
word-break: break-all;
font-family: monospace;
letter-spacing: 2rpx;
margin-bottom: 28rpx;
position: relative;
padding-left: 24rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 6rpx;
bottom: 6rpx;
width: 4rpx;
background: $primary;
border-radius: 2rpx;
}
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
&__pill {
display: inline-flex;
width: 100%;
align-items: center;
gap: 8rpx;
background: $primary-light;
padding: 10rpx 18rpx;
border-radius: 10rpx;
border: 1rpx solid $primary-mid;
.pill-label {
font-size: 22rpx;
color: $text-secondary;
width: 60rpx;
}
.pill-value {
font-size: 24rpx;
font-weight: 600;
color: $primary-dark;
font-family: monospace;
}
}
}
// ===== 区块标题 =====
.section {
margin: 0 24rpx 20rpx;
&__header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 14rpx;
padding: 0 6rpx;
}
&__bar {
width: 6rpx;
height: 26rpx;
background: linear-gradient(180deg, $primary, #6ba3ff);
border-radius: 3rpx;
flex-shrink: 0;
}
&__title {
font-size: 28rpx;
font-weight: 600;
color: $text;
letter-spacing: 0.5rpx;
}
}
// ===== 坡口参数仪表 =====
.spec-grid {
background: $card-bg;
border-radius: $radius;
padding: 8rpx 0;
box-shadow: $shadow-md;
.spec-item {
padding: 24rpx 28rpx;
border-bottom: 1rpx solid $border;
&:last-child {
border-bottom: none;
}
&__header {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 14rpx;
}
&__label {
font-size: 24rpx;
color: $text-secondary;
font-weight: 500;
}
&__value {
font-size: 40rpx;
font-weight: 700;
color: $text;
line-height: 1;
font-variant-numeric: tabular-nums;
}
&__unit {
font-size: 24rpx;
font-weight: 400;
color: $text-secondary;
margin-left: 6rpx;
}
&__bar-track {
height: 8rpx;
background: #f0f2f5;
border-radius: 4rpx;
overflow: hidden;
position: relative;
}
&__bar-fill {
height: 100%;
border-radius: 4rpx;
background: linear-gradient(90deg, $primary, #6ba3ff);
transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 12rpx;
border-radius: 4rpx;
background: rgba(255, 255, 255, 0.35);
}
&.fill--amber {
background: linear-gradient(90deg, #f59e0b, #fbbf24);
}
&.fill--teal {
background: linear-gradient(90deg, #14b8a6, #2dd4bf);
}
}
}
}
// ===== 信息卡片 =====
.card {
background: $card-bg;
border-radius: $radius;
overflow: hidden;
box-shadow: $shadow-md;
&__row {
display: flex;
align-items: center;
padding: 22rpx 28rpx;
border-bottom: 1rpx solid $border;
&:last-child {
border-bottom: none;
}
&:active {
background: #fafbfc;
}
}
&__label {
width: 180rpx;
font-size: 26rpx;
color: $text-secondary;
flex-shrink: 0;
position: relative;
padding-left: 16rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4rpx;
height: 16rpx;
background: $primary-mid;
border-radius: 2rpx;
}
}
&__value {
flex: 1;
font-size: 28rpx;
color: $text;
font-weight: 500;
}
&__value-wrap {
flex: 1;
display: flex;
align-items: center;
gap: 12rpx;
}
}
// ===== 校验卡片 =====
.check-card {
background: $card-bg;
border-radius: $radius;
overflow: hidden;
box-shadow: $shadow-md;
}
.check-item {
display: flex;
align-items: center;
padding: 22rpx 28rpx;
border-bottom: 1rpx solid $border;
&:last-child {
border-bottom: none;
}
&__left {
flex: 1;
min-width: 0;
}
&__label {
font-size: 26rpx;
font-weight: 500;
color: $text;
display: block;
margin-bottom: 4rpx;
}
&__desc {
font-size: 22rpx;
color: $text-secondary;
display: block;
}
&__action {
flex-shrink: 0;
margin-left: 20rpx;
}
}
// ===== 校验按钮(二态切换) =====
.check-btn {
display: inline-flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 24rpx;
border-radius: 12rpx;
transition: all 0.25s ease;
min-width: 120rpx;
justify-content: center;
&--pass {
background: #ebf9f0;
border: 1rpx solid #b7ebc6;
.check-btn__icon {
font-size: 26rpx;
color: #22c55e;
font-weight: 700;
}
.check-btn__text {
font-size: 24rpx;
color: #16a34a;
font-weight: 600;
}
}
&--fail {
background: #fef2f2;
border: 1rpx solid #fecaca;
.check-btn__icon {
font-size: 26rpx;
color: #ef4444;
font-weight: 700;
}
.check-btn__text {
font-size: 24rpx;
color: #dc2626;
font-weight: 600;
}
}
&:active {
transform: scale(0.96);
}
}
// ===== 提交栏(悬浮底部) =====
.submit-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 24rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: linear-gradient(180deg, rgba(240, 242, 245, 0) 0%, $bg 20%);
}
.submit-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
background: linear-gradient(135deg, $primary, #5a9aff);
border-radius: 14rpx;
box-shadow: 0 6rpx 24rpx rgba($primary, 0.3);
transition: all 0.25s ease;
&:active {
opacity: 0.9;
transform: scale(0.98);
}
&--disabled {
background: #c5cbd5;
box-shadow: none;
&:active {
opacity: 1;
transform: none;
}
.submit-btn__text {
color: #94a3b8;
}
}
&__text {
font-size: 30rpx;
font-weight: 600;
color: #ffffff;
letter-spacing: 2rpx;
}
}
// ===== 备注输入框 =====
.remark-card {
background: $card-bg;
border-radius: $radius;
padding: 24rpx 28rpx;
box-shadow: $shadow-md;
position: relative;
.remark-input {
width: 100%;
min-height: 120rpx;
font-size: 26rpx;
color: $text;
line-height: 1.6;
padding: 0;
margin: 0;
background: transparent;
&::placeholder {
color: #b0bec5;
}
}
.remark-count {
position: absolute;
bottom: 16rpx;
right: 24rpx;
font-size: 22rpx;
color: $text-secondary;
}
}
</style>
+270
View File
@@ -0,0 +1,270 @@
<template>
<view class="detail-page">
<!-- 代码标识 MaterialCode 拆解展示 -->
<view class="code-hero">
<view class="code-hero__tag">扫描结果</view>
<view class="code-hero__body">
<text class="code-hero__text">{{ info.MaterialCode || '--' }}</text>
</view>
</view>
<!-- 身份信息 -->
<view class="section">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">材料信息</text>
</view>
<view class="card">
<view class="card__row">
<text class="card__label">编码</text>
<text class="card__value card__value--mono">{{ info.Code || '--' }}</text>
</view>
<view class="card__row">
<text class="card__label">炉号</text>
<text class="card__value card__value--mono">{{ info.HeatNo || '--' }}</text>
</view>
<view class="card__row">
<text class="card__label">批号</text>
<text class="card__value card__value--mono">{{ info.BatchNo || '--' }}</text>
</view>
<view class="card__row">
<text class="card__label">名称</text>
<text class="card__value">{{ info.MaterialName || '--' }}</text>
</view>
<view class="card__row">
<text class="card__label">规格</text>
<view class="card__value-wrap">
<text class="card__value">{{ info.MaterialSpec || '--' }}</text>
</view>
</view>
<view class="card__row">
<text class="card__label">单位</text>
<view class="card__value-wrap">
<view class="badge badge--unit">{{ info.MaterialUnit || '--' }}</view>
</view>
</view>
</view>
</view>
<!-- 描述 -->
<view class="section" v-if="info.MaterialDef">
<view class="section__header">
<view class="section__bar"></view>
<text class="section__title">描述</text>
</view>
<view class="card">
<text class="desc-text">{{ info.MaterialDef }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { reqMaterialInfoByMaterialCode } from '@/api/hj.js'
const info = ref({})
onLoad((opt) => {
if (opt.id) {
try {
reqMaterialInfoByMaterialCode(encodeURIComponent(opt.id)).then(res=>{
if(res.code == 1){
info.value = res.data
}
})
} catch (e) {
console.error('解析材料数据失败:', e)
}
}
})
</script>
<style lang="scss" scoped>
$primary: #2979ff;
$primary-light: #e8f0fe;
$primary-dark: #1a56cc;
$bg: #f5f7fa;
$text: #1a2332;
$text-secondary: #6b7c93;
$border: #e2e8f0;
$card-bg: #ffffff;
$radius: 16rpx;
.detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 48rpx;
}
// ===== 代码标识区 =====
.code-hero {
margin: 24rpx;
background: $card-bg;
border-radius: $radius;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
padding-bottom: 24rpx;
&__tag {
display: inline-flex;
align-items: center;
margin: 28rpx 0 0 28rpx;
padding: 4rpx 16rpx;
font-size: 22rpx;
color: $primary;
background: $primary-light;
border-radius: 6rpx;
font-weight: 500;
}
&__body {
padding: 20rpx 28rpx 0;
}
&__text {
display: block;
font-size: 34rpx;
font-weight: 700;
color: $text;
line-height: 1.4;
word-break: break-all;
font-family: monospace;
letter-spacing: 0.5rpx;
}
}
// ===== 区块标题 =====
.section {
margin: 0 24rpx 20rpx;
&__header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 16rpx;
padding: 0 4rpx;
}
&__bar {
width: 6rpx;
height: 28rpx;
background: $primary;
border-radius: 3rpx;
flex-shrink: 0;
}
&__title {
font-size: 28rpx;
font-weight: 600;
color: $text;
}
}
// ===== 卡片 =====
.card {
background: $card-bg;
border-radius: $radius;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
&__row {
display: flex;
align-items: center;
padding: 24rpx 28rpx;
border-bottom: 1rpx solid $border;
&:last-child {
border-bottom: none;
}
}
&__label {
width: 120rpx;
font-size: 26rpx;
color: $text-secondary;
flex-shrink: 0;
}
&__value {
flex: 1;
font-size: 28rpx;
color: $text;
font-weight: 500;
&--mono {
font-family: monospace;
color: $primary-dark;
}
}
&__value-wrap {
flex: 1;
display: flex;
align-items: center;
gap: 12rpx;
}
}
// ===== 单位徽章 =====
.badge--unit {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
font-size: 22rpx;
font-weight: 600;
color: $primary;
background: $primary-light;
border-radius: 6rpx;
letter-spacing: 2rpx;
}
// ===== 描述文本 =====
.desc-text {
display: block;
padding: 28rpx;
font-size: 28rpx;
color: $text;
line-height: 1.6;
}
// ===== 批次追溯网格 =====
.trace-grid {
display: flex;
gap: 16rpx;
.trace-cell {
flex: 1;
background: $card-bg;
border-radius: $radius;
padding: 28rpx 20rpx 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
border-top: 4rpx solid $primary;
&__label {
font-size: 22rpx;
color: $text-secondary;
font-weight: 500;
}
&__value {
font-size: 32rpx;
font-weight: 700;
color: $text;
font-family: monospace;
text-align: center;
word-break: break-all;
}
&__sub {
font-size: 20rpx;
color: #b0bccf;
letter-spacing: 1rpx;
}
}
}
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

+2 -2
View File
@@ -17,7 +17,7 @@ export const useMenuStore = defineStore('menu', () => {
const buildAppsList = () => { const buildAppsList = () => {
return defaultApps.map(app => ({ return defaultApps.map(app => ({
...app, ...app,
selected: quickAppIds.value.includes(app.menuId) selected: quickAppIds.value.includes(app.path)
})) }))
} }
@@ -62,7 +62,7 @@ export const useMenuStore = defineStore('menu', () => {
// 只持久化快捷应用的 menuId 列表 // 只持久化快捷应用的 menuId 列表
quickAppIds.value = appsList.value quickAppIds.value = appsList.value
.filter(app => app.selected) .filter(app => app.selected)
.map(app => app.menuId) .map(app => app.path)
} }
/** /**
+2 -2
View File
@@ -22,8 +22,8 @@ export const useUserStore = defineStore('user', () => {
const currentProject = ref(null) const currentProject = ref(null)
// 监听 currentProject 变化,获取菜单权限 // 监听 currentProject 变化,获取菜单权限
watch(() => currentProject.value?.ProjectId, (newId) => { watch(() => currentProject.value?.ProjectId, (newId,oldId) => {
if (newId) { if (newId!==oldId) {
// 项目变化 请求菜单权限 // 项目变化 请求菜单权限
const menuStore = useMenuStore() const menuStore = useMenuStore()
const userId = userInfo.value?.PersonId const userId = userInfo.value?.PersonId
+18
View File
@@ -49,5 +49,23 @@ export const defaultApps = [{
path: "/pipe/precast/list", path: "/pipe/precast/list",
prefix: 'nbd-icon', prefix: 'nbd-icon',
selected: false selected: false
},
{
name: '下料抽检',
icon: 'xialiaochoujianjilu',
menuId: "",
path: "/scanpages/hj/material_sampling",
isScan: true,
prefix: 'nbd-icon',
selected: false
},
{
name: '组队抽查',
icon: 'zudui',
menuId: "",
path: "/scanpages/hj/make_right",
isScan: true,
prefix: 'nbd-icon',
selected: false
} }
] ]
+21 -5
View File
@@ -1,7 +1,23 @@
import {getUrlParam} from './request' import {getUrlParam} from './request'
export const scanToSkipUrl = (str)=>{
// 焊接日报跳转 /**
if(str.includes('weldjoint')){ * 解析扫码结果,返回跳转页面路径
return `/pipe/weld/daily_paper?id=${getUrlParam(str,"id")}` * @param {string} str 扫码结果字符串
} * @returns {{ path: string, query: string } | null} 返回 { path, query } 或 null(材料编码场景)
*/
export const parseScanResult = (str) => {
if (!str) return null
// 焊接接头二维码:https://sggl.sedin.com.cn/StaticPage/HJGL/index.html?id=xxx&weldjoint=1
// if (str.includes('weldjoint')) {
// const id = getUrlParam(str, 'id')
// if (id) {
// return { path: '/scanpages/hj/material_sampling', query: `id=${id}` }
// }
// }
// 后续可在此扩展其他扫码类型...
// 非 URL 格式,视为材料编码
return null
} }