feat:菜单可配置

This commit is contained in:
houjunxiang
2025-11-27 16:12:25 +08:00
parent f3c15b3692
commit 6c86dc0760
46 changed files with 79 additions and 162 deletions

View File

@@ -0,0 +1,104 @@
<template>
<up-popup :show="visible" mode="right" closeable @close="handleClose" @open="handleOpen">
<uni-section titleFontSize="20px" type="line" title="设备使用记录"> </uni-section>
<scroll-view scroll-y="true" class="content">
<up-row>
<up-col span="6"
>开始使用人
<text class="value">{{ detailInfo.userName }}</text>
</up-col>
<up-col span="6"
>开始时间
<text class="value">{{ detailInfo.useTimeStart }}</text>
</up-col>
</up-row>
<up-row>
<up-col span="6"
>结束使用人
<text class="value">{{ detailInfo.userNameEnd }}</text>
</up-col>
<up-col span="6"
>结束时间
<text class="value">{{ detailInfo.useTimeEnd }}</text>
</up-col>
</up-row>
<up-row>
<up-col span="4"
>使用前状态
<text class="value">{{ detailInfo.stateBefore }}</text>
</up-col>
<up-col span="4"
>使用后状态
<text class="value">{{ detailInfo.stateAfter }}</text>
</up-col>
<up-col span="4"
>温度()
<text class="value">{{ detailInfo.temperature }}</text>
</up-col>
</up-row>
<up-row>
<up-col span="4"
>湿度(%RH)
<text class="value">{{ detailInfo.humidity }}</text>
</up-col>
<up-col span="6"
>样品类别/个数/任务
<text class="value">{{ detailInfo.useRemark }}</text>
</up-col>
</up-row>
</scroll-view>
</up-popup>
</template>
<script setup>
import { ref, reactive, onMounted, watch, computed } from 'vue'
const props = defineProps({
show: {
type: Boolean,
default: false
},
checkInfo: {
type: Object
}
})
const visible = ref(props.show)
// 监听外部传入的show属性变化
watch(
() => props.show,
newVal => {
visible.value = newVal
}
)
let detailInfo = ref({})
const emit = defineEmits(['close', 'open'])
function handleClose() {
emit('close')
}
function handleOpen() {
detailInfo.value = props.checkInfo
}
</script>
<style lang="scss" scoped>
.content {
font-size: 18px;
height: 80vh;
width: 80vw;
padding: 10px 20px;
.u-row {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.value {
color: #666;
font-size: 16px;
}
}
:deep(.uicon-close) {
font-size: 22px !important;
}
</style>

View File

@@ -0,0 +1,330 @@
<template>
<view>
<up-sticky>
<navbar-back title="使用">
<up-button
v-if="detailInfo.id"
type="primary"
:plain="true"
icon="list"
size="small"
text="设备使用记录"
@click="handleUseRecord"
></up-button>
</navbar-back>
</up-sticky>
<view class="container">
<n-scanTemp v-if="!detailInfo.id" title="请扫描设备条码进行设备使用" @deviceId="id => handleTestAction(id)" />
<view v-else class="content">
<view>
<uni-section titleFontSize="20px" type="line" title="设备信息"> </uni-section>
<up-row class="flex-wrap p10" style="background-color: #f5f7fa">
<up-col class="mb10" :span="gridCol" v-for="(item, index) in detailSchema">
<view style="color: #666"
><span style="color: #333">{{ item.label }}</span>{{ detailInfo[item.value] }}</view
>
</up-col>
</up-row>
</view>
<view>
<up-row justify="space-around">
<up-col span="3.5">
<view class="p5">使用前状态</view>
<uni-data-select :disabled="useIng" v-model="formData.stateBefore" :localdata="ditData"></uni-data-select>
</up-col>
<up-col span="3.5">
<view class="p5">使用中状态</view>
<uni-data-select :disabled="!useIng" v-model="formData.stateRun" :localdata="ditData"></uni-data-select>
</up-col>
<up-col span="3.5">
<view class="p5">使用后状态</view>
<uni-data-select :disabled="!useIng" v-model="formData.stateAfter" :localdata="ditData"></uni-data-select>
</up-col>
</up-row>
<up-row justify="space-around">
<up-col span="3.5">
<view class="p5">温度</view>
<up-input v-model="formData.temperature"></up-input>
</up-col>
<up-col span="3.5">
<view class="p5">湿度%HR</view>
<up-input v-model="formData.humidity"></up-input>
</up-col>
<up-col span="3.5">
<view class="p5">样品类别/个数/任务</view>
<up-input v-model="formData.useRemark"></up-input>
</up-col>
</up-row>
<up-row justify="space-around">
<up-col span="11.5">
<view class="p5">{{ useIng ? '结束使用人' : '开始使用人' }}</view>
<up-input v-if="useIng" v-model="formData.userNameEnd"></up-input>
<up-input v-else v-model="formData.userName"></up-input>
</up-col>
</up-row>
<up-row justify="space-around">
<up-col span="11.5">
<view class="p5">备注</view>
<up-textarea v-model="formData.remark" placeholder="请输入内容" autoHeight></up-textarea>
</up-col>
</up-row>
</view>
<up-button
style="width: 90%"
class="mt40"
loadingText="提交中..."
type="primary"
:text="useIng ? '结束使用' : '开始使用'"
@click="handleValidate"
></up-button>
</view>
</view>
<up-modal
:show="modalShow"
title="提示"
content="确定提交吗?"
ref="uModal"
:asyncClose="true"
showCancelButton
@confirm="submitConfirm"
@cancel="modalShow = false"
></up-modal>
<up-loading-page :loading="pageLoading"></up-loading-page>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, watch, toRefs } from 'vue'
import { onShow, onLoad } from '@dcloudio/uni-app'
import { getDeviceBusInfoById } from '@/nx/api/deviceInfo'
import { getUseRecordById, addUseRecord, editUseRecord } from './useRecord.api'
import dailyCheckApi from '@/nx/api/dailyCheck'
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
import { useGridCol } from '@/nx/hooks/useGridCol'
import nx from '@/nx'
const { gridCol } = useGridCol([700], [6, 4])
const { lockOrientation } = useScreenOrientation()
const ditData = ref([
{ value: '正常', text: '正常' },
{ value: '异常', text: '异常' }
])
const detailSchema = [
{ label: '设备名称', value: 'deviceName' },
{ label: '设备型号', value: 'modelNo' },
{ label: '设备编码', value: 'deviceCode' },
{ label: '使用班组', value: 'deptName' }
]
const pageLoading = ref(false)
const userId = nx.$store('user').userInfo['id']
const userName = nx.$store('user').userInfo['realname']
let detailInfo = ref({})
const { scanQRInfo } = toRefs(nx.$store('biz'))
watch(scanQRInfo, newVal => {
if (newVal && nx.$router.getCurrentPage().route == 'pages/device/deviceBusUseRecord/index') {
try {
const codeObj = JSON.parse(newVal)
if (!pageLoading.value) {
getLastDailyCheckOfToday(codeObj.id)
}
scanQRInfo.value = ''
} catch (error) {
scanQRInfo.value = ''
uni.showToast({
title: '请扫描设备码',
icon: 'none'
})
}
}
})
onShow(() => {
scanQRInfo.value = ''
})
// 检查该设备在使用前是否已经点检过
function getLastDailyCheckOfToday(id) {
pageLoading.value = true
try {
dailyCheckApi.getLastDailyCheckOfToday({ deviceId: id }).then(async res => {
if (!res || res.submitFlag == '0') {
setTimeout(() => {
uni.showToast({
title: '设备使用前请先进行设备点检',
icon: 'none'
})
}, 100)
pageLoading.value = false
nx.$router.go('/pages/deviceBusDailyCheck/index', { deviceId: id })
} else {
getDeviceInfo(id)
await getUseIngRecord(id)
pageLoading.value = false
}
})
} catch (error) {
pageLoading.value = false
}
}
function handleTestAction(id) {
getLastDailyCheckOfToday(id)
}
// 获取设备详情
async function getDeviceInfo(id) {
const res = await getDeviceBusInfoById(id).finally(() => {})
detailInfo.value = res
lockOrientation('landscape')
}
const useIng = ref(false)
// 获取使用记录判断是否在使用中
async function getUseIngRecord(id) {
const res = await getUseRecordById(id)
if (res) {
useIng.value = true
formData.value = res
formData.value.userIdEnd = userId
formData.value.userNameEnd = userName
// checkUseStatusIsMySelf(res)
} else {
useIng.value = false
}
}
// 定义一个变量,用于标记当前是否有 modal 正在显示
let isModalShowing = false
// 如果设备在使用中,检查设备使用记录是否是所登录用户的
function checkUseStatusIsMySelf(useInfo) {
if (isModalShowing) {
return
}
if (useInfo.userId != userId) {
isModalShowing = true
uni.showModal({
title: '提示',
content: '该设备正在使用中,是否结束使用',
success: function (res) {
if (res.confirm) {
submitConfirm()
isModalShowing = false
} else if (res.cancel) {
console.log('用户点击取消')
isModalShowing = false
}
}
})
}
}
let formData = ref({
userName,
userId,
stateBefore: '正常'
})
const modalShow = ref(false)
function handleValidate() {
if (!useIng.value && !formData.value.stateBefore) {
return uni.showToast({
title: '请选择使用前状态',
icon: 'none'
})
}
if (useIng.value) {
if (!formData.value.stateRun) {
return uni.showToast({
title: '请选择使用中状态',
icon: 'none'
})
}
if (!formData.value.stateAfter) {
return uni.showToast({
title: '请选择使用后状态',
icon: 'none'
})
}
}
if (!formData.value.userName) {
return uni.showToast({
title: '请输入使用人',
icon: 'none'
})
}
if (!formData.value.useRemark) {
return uni.showToast({
title: '请输入样品类别/个数/任务',
icon: 'none'
})
}
modalShow.value = true
}
const submitLoading = ref(false)
async function submitConfirm() {
if (submitLoading.value) return
submitLoading.value = true
let submitParams = {
...formData.value,
deviceId: detailInfo.value.id,
temperature: formData.value.temperature || '/',
humidity: formData.value.humidity || '/'
}
// 如果在使用中加入使用结束参数
let currentTime = nx.$dayjs().format('YYYY-MM-DD HH:mm:ss')
let submitApi = useIng.value ? editUseRecord : addUseRecord
if (useIng.value) {
submitParams.useTimeEnd = currentTime
} else {
submitParams.useTimeStart = currentTime
}
await submitApi(submitParams)
.then(() => {
submitLoading.value = false
modalShow.value = false
reset()
})
.catch(() => {
modalShow.value = false
submitLoading.value = false
})
}
function reset() {
formData.value = {
userName: nx.$store('user').userInfo['realname']
}
detailInfo.value = {}
}
function handleUseRecord() {
nx.$store('biz').deviceInfo = detailInfo.value
nx.$router.go('/pages/device/deviceBusUseRecord/list')
}
</script>
<style lang="scss" scoped>
.u-sticky {
top: 0 !important;
}
.container {
.content {
background-color: #fff;
height: 100%;
padding: 16px;
padding-top: 10px;
font-size: 16px;
.title {
font-size: 22px;
font-weight: bold;
}
.check-header {
.u-sticky {
top: 70px !important;
}
}
.fill-content {
border-radius: 3px;
box-shadow: 2px 2px 8px 2px rgba(0, 0, 0, 0.2);
background-color: #fdf6ec;
padding: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<view>
<up-sticky v-if="!isComponent">
<navbar-back title="设备使用记录查看"> </navbar-back>
</up-sticky>
<uni-card spacing="0">
<uni-section v-if="!isComponent" titleFontSize="20px" type="line" :title="deviceText"> </uni-section>
<view class="p10" style="width: 50%">
<!-- <uni-datetime-picker v-model="startEndTime" type="daterange" @change="datetimeChange" /> -->
<up-search
v-model="keyword"
shape="square"
placeholder="请输入使用人"
actionText="重置"
:clearabled="false"
:showAction="true"
@change="handleInputSearch"
@custom="reset"
></up-search>
</view>
<view :style="{ height: isComponent ? '60vh' : '62vh' }">
<zb-table
ref="zbTableRef"
isShowLoadMore
stripe
:fit="false"
:columns="column"
:cellStyle="setCellStyle"
:cellHeaderStyle="setCellHeaderStyle"
:data="listData"
@detail="handleDetail"
@pullUpLoading="pullUpLoadingAction"
></zb-table>
</view>
</uni-card>
<use-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
</view>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, watch, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
import UseDetailPopup from './detail.vue'
import { getList } from './useRecord.api'
import { throttle } from '@/uview-plus'
import { useListData } from '@/nx/hooks/usePageListData'
import nx from '@/nx'
let props = defineProps({
isComponent: {
type: Boolean,
default: false
}
})
let startEndTime = ref([])
const column = reactive([
{
label: '开始使用人',
name: 'userName',
width: 100
},
{
label: '开始时间',
name: 'useTimeStart',
width: 170
},
{
label: '结束使用人',
name: 'userNameEnd',
width: 100
},
{
label: '结束时间',
name: 'useTimeEnd',
width: 170
},
{
label: '使用前状态',
name: 'stateBefore'
},
{
label: '使用后状态',
name: 'stateAfter'
},
{
label: '温度(℃)',
name: 'temperature'
},
{
label: '湿度(%RH)',
name: 'humidity'
},
{
name: 'operation',
type: 'operation',
label: '操作',
renders: [{ name: '详情', func: 'detail' }]
}
])
let zbTableRef = ref(null)
const deviceId = ref('')
const deviceText = ref('')
onMounted(() => {
const { id, deviceName } = nx.$store('biz').deviceInfo
deviceId.value = id
deviceText.value = deviceName
getInitData()
})
const searchParams = computed(() => ({
deviceId: deviceId.value,
userName: keyword.value
}))
const { listData, loadingData, getInitData, scrollToLower, loadStatus } = useListData({
searchParams,
api: getList
})
function pullUpLoadingAction(done) {
if (loadingData.value) return
if (loadStatus.value === 'nomore') {
zbTableRef.value.pullUpCompleteLoading('ok')
} else {
scrollToLower()
}
}
let keyword = ref('')
function handleInputSearch() {
if (!keyword.value) return
throttle(getInitData, 500)
}
function reset() {
keyword.value = ''
getInitData()
}
function datetimeChange(e) {
startEndTime.value = [nx.$dayjs(e[0]).format('YYYY-MM-DD 00:00:00'), nx.$dayjs(e[1]).format('YYYY-MM-DD 23:59:59')]
getInitData()
}
const detailShow = ref(false)
const checkInfo = ref({})
function handleDetail(row) {
checkInfo.value = row
detailShow.value = true
}
</script>
<style lang="scss" scoped>
.u-sticky {
top: 0 !important;
background-color: #fff !important;
}
:deep(.zb-table uni-button[type='primary']) {
background-color: $uni-color-primary !important;
}
:deep(.u-search__action--active) {
padding: 5px;
border-radius: 3px;
background: #0055a2;
color: #fff;
}
</style>

View File

@@ -0,0 +1,36 @@
import request from '@/nx/request'
export function addUseRecord(data) {
return request({
url: '/lims/bus/device/use-record/create',
method: 'POST',
data,
custom: {
showSuccess: true
}
})
}
export function editUseRecord(data) {
return request({
url: '/lims/bus/device/use-record/edit',
method: 'POST',
data,
custom: {
showSuccess: true
}
})
}
export function getList(params) {
return request({
url: '/lims/bus/device/use-record/list',
method: 'GET',
params
})
}
export function getUseRecordById(id) {
return request({
url: '/lims/bus/device/use-record/queryLastUsingData',
method: 'GET',
params: { deviceId: id }
})
}