1
This commit is contained in:
257
pages/analysis/auncel/auncel-select.vue
Normal file
257
pages/analysis/auncel/auncel-select.vue
Normal file
@@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="选择天平"></navbar-back>
|
||||
|
||||
<u-grid :col="3" @click="click">
|
||||
<u-grid-item v-for="(auncel, index) in auncelList" :index="index" :key="index">
|
||||
<view
|
||||
style="
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
background-image: url(../../static/images/auncel.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
"
|
||||
>
|
||||
<view style="height: 100px">
|
||||
<view style="padding-top: 20px; text-align: center; font-size: 20rpx"
|
||||
><text>{{ auncel.code }}</text></view
|
||||
>
|
||||
<view style="padding-top: 5px; text-align: center"
|
||||
><text>{{ auncel.controlRealName }}</text></view
|
||||
>
|
||||
</view>
|
||||
<view class="weight">
|
||||
<view
|
||||
:class="
|
||||
auncel.weightStable == 0
|
||||
? 'weight-data-yellow'
|
||||
: auncel.weightStable == 1
|
||||
? 'weight-data'
|
||||
: 'weight-data-warning'
|
||||
"
|
||||
>
|
||||
{{ auncel.weightData }}
|
||||
</view>
|
||||
<view class="weight-unit">{{ auncel.weightUnit }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-grid-item>
|
||||
</u-grid>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
auncelList: [],
|
||||
currentAuncel: '',
|
||||
currentTaskNo: '',
|
||||
currentTaskType: ''
|
||||
}
|
||||
},
|
||||
onLoad(param) {
|
||||
this.currentTaskNo = param.currentTaskNo
|
||||
this.currentTaskType = param.currentTaskType
|
||||
//获取数据
|
||||
this.getPageData()
|
||||
console.log('onLoad')
|
||||
},
|
||||
onShow() {
|
||||
console.log('onShow')
|
||||
// #ifdef H5
|
||||
//注册websocket
|
||||
let regData = {
|
||||
msgId: this.$helper.uuid(),
|
||||
cmd: 'register',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
userId: this.userInfo.user_id,
|
||||
tenantId: this.userInfo.tenant_id,
|
||||
userRealName: this.userInfo.real_name
|
||||
}
|
||||
}
|
||||
this.$measure.setRegData(JSON.stringify(regData))
|
||||
this.$measure.reOpen()
|
||||
// #endif
|
||||
//监听websocket返回来的数据
|
||||
//设备数据
|
||||
uni.$on('deviceData', res => {
|
||||
switch (res.deviceType) {
|
||||
case 'auncel':
|
||||
this.auncelList.forEach((item, index) => {
|
||||
if (item.id === res.deviceId) {
|
||||
item.weightData = res.weightData
|
||||
item.weightUnit = res.weightUnit
|
||||
item.weightStable = res.weightStable
|
||||
item.isConnected = 1
|
||||
item.controlRealName = res.controlRealName
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
//设备状态
|
||||
uni.$on('deviceStatus', res => {
|
||||
this.auncelList.forEach((item, index) => {
|
||||
if (item.id === res.deviceId) {
|
||||
item.isConnected = res.connected
|
||||
if (res.connected == 0) {
|
||||
item.weightData = '天平断开'
|
||||
item.weightUnit = ''
|
||||
item.weightStable = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
//连接断开
|
||||
uni.$on('connClose', res => {
|
||||
//重置
|
||||
this.auncelList.forEach(i => {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.weightStable = 0
|
||||
i.controlRealName = ''
|
||||
i.isConnected = 0
|
||||
})
|
||||
})
|
||||
},
|
||||
onHide() {
|
||||
console.log('onHide')
|
||||
//移除监听websocket返回来的数据
|
||||
uni.$off('deviceData')
|
||||
uni.$off('deviceStatus')
|
||||
uni.$off('connClose')
|
||||
},
|
||||
methods: {
|
||||
getPageData() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
//获取计量点数量
|
||||
this.$u.api
|
||||
.getDeviceLaboratoryListBy({
|
||||
deviceType: 'auncel',
|
||||
pageNo: 0,
|
||||
pageSize: -1,
|
||||
status: '1',
|
||||
isEnable: '1'
|
||||
})
|
||||
.then(res => {
|
||||
let dataList = res.result.records
|
||||
dataList.forEach(i => {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.isConnected = 0
|
||||
i.weightStable = 0
|
||||
i.temperature = 0
|
||||
i.humidity = 0
|
||||
i.controlRealName = ''
|
||||
})
|
||||
this.auncelList = dataList
|
||||
uni.hideLoading()
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
click(index) {
|
||||
// this.$refs.uToast.show({
|
||||
// title: `点击了第${index + 1}宫格`,
|
||||
// type: 'warning'
|
||||
// });
|
||||
console.log(this.auncelList[index])
|
||||
this.currentAuncel = this.auncelList[index]
|
||||
if (this.currentAuncel.isConnected != 1) {
|
||||
this.$helper.showToast({
|
||||
title: '天平设备尚未连接!'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.currentAuncel.controlRealName && this.currentAuncel.controlRealName != this.userInfo.real_name) {
|
||||
this.$helper.showToast({
|
||||
title: '当前天平正被“' + this.currentAuncel.controlRealName + '”使用,请选择其他天平!'
|
||||
})
|
||||
return
|
||||
}
|
||||
//发送控制请求
|
||||
let controlDevice = {
|
||||
msgId: this.currentAuncel.id,
|
||||
cmd: 'controlDevice',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
deviceId: this.currentAuncel.id,
|
||||
isControl: true,
|
||||
controlRealName: this.userInfo.real_name
|
||||
}
|
||||
}
|
||||
//发送控制数据
|
||||
this.$measure.send(JSON.stringify(controlDevice))
|
||||
uni.redirectTo({
|
||||
url:
|
||||
'/pages/auncel/auncel-weigh?currentTaskNo=' +
|
||||
this.currentTaskNo +
|
||||
'¤tTaskType=' +
|
||||
this.currentTaskType +
|
||||
'¤tAuncelId=' +
|
||||
this.currentAuncel.id +
|
||||
'¤tAuncelName=' +
|
||||
this.currentAuncel.name +
|
||||
'¤tAuncelCode=' +
|
||||
this.currentAuncel.code
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.weight {
|
||||
width: 180px;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.weight-data {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #4cd964;
|
||||
text-align: right;
|
||||
line-height: 40px;
|
||||
letter-spacing: 2px;
|
||||
font-size: 32px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-data-yellow {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ffff00;
|
||||
text-align: right;
|
||||
line-height: 40px;
|
||||
letter-spacing: 2px;
|
||||
font-size: 32px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-data-warning {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ff3333;
|
||||
text-align: right;
|
||||
line-height: 40px;
|
||||
font-size: 32px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-unit {
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
229
pages/analysis/auncel/auncel-status.vue
Normal file
229
pages/analysis/auncel/auncel-status.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="天平状态"></navbar-back>
|
||||
<up-grid border :col="3">
|
||||
<up-grid-item v-for="(auncel, index) in auncelList" :index="index" :key="index">
|
||||
<view
|
||||
class="auncel-item"
|
||||
:style="{
|
||||
backgroundImage: `url(${require('@/static/images/auncel.png')})`
|
||||
}"
|
||||
>
|
||||
<view class="auncel-header">
|
||||
<view class="auncel-code">{{ auncel.code }}</view>
|
||||
<view class="auncel-name">{{ auncel.controlRealName }}</view>
|
||||
</view>
|
||||
<view class="weight">
|
||||
<view
|
||||
:class="{
|
||||
'weight-data-yellow': auncel.weightStable === 0,
|
||||
'weight-data': auncel.weightStable === 1,
|
||||
'weight-data-warning': auncel.weightStable === 2
|
||||
}"
|
||||
>
|
||||
{{ auncel.weightData || '' }}
|
||||
</view>
|
||||
<view class="weight-unit">{{ auncel.weightUnit }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</up-grid-item>
|
||||
</up-grid>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { onUnload, onHide } from '@dcloudio/uni-app'
|
||||
import { getTenantId } from '@/defaultBaseUrl'
|
||||
import nx from '@/nx'
|
||||
|
||||
// refs
|
||||
const auncelList = ref([])
|
||||
|
||||
// computed
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
// getPageData()
|
||||
// #ifdef H5
|
||||
const regData = {
|
||||
msgId: nx.$helper.uuid(),
|
||||
cmd: 'register',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
userId: userInfo.value.id,
|
||||
tenantId: getTenantId(),
|
||||
userRealName: userInfo.value.realname
|
||||
}
|
||||
}
|
||||
nx.$measure.setRegData(JSON.stringify(regData))
|
||||
nx.$measure.reOpen()
|
||||
// #endif
|
||||
|
||||
// 监听 WebSocket 数据
|
||||
uni.$on('deviceData', handleDeviceData)
|
||||
uni.$on('deviceStatus', handleDeviceStatus)
|
||||
uni.$on('connClose', handleConnClose)
|
||||
})
|
||||
onHide(() => {
|
||||
// #ifdef APP-PLUS
|
||||
cleanup()
|
||||
// #endif
|
||||
})
|
||||
onUnmounted(() => {
|
||||
// #ifdef H5
|
||||
cleanup()
|
||||
// #endif
|
||||
})
|
||||
|
||||
function cleanup() {
|
||||
uni.$off('deviceData', handleDeviceData)
|
||||
uni.$off('deviceStatus', handleDeviceStatus)
|
||||
uni.$off('connClose', handleConnClose)
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
function getPageData() {
|
||||
nx.$api.laboratory
|
||||
.getDeviceLaboratoryListBy({
|
||||
deviceType: 'auncel',
|
||||
status: '1',
|
||||
isEnable: '1',
|
||||
pageNo: 0,
|
||||
pageSize: -1
|
||||
})
|
||||
.then(res => {
|
||||
const dataList = res.records || []
|
||||
dataList.forEach(i => {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.isConnected = 0
|
||||
i.weightStable = 0
|
||||
i.temperature = 0
|
||||
i.humidity = 0
|
||||
i.controlRealName = ''
|
||||
})
|
||||
auncelList.value = dataList
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('获取天平列表失败', err)
|
||||
})
|
||||
}
|
||||
|
||||
// WebSocket 事件处理器
|
||||
function handleDeviceData(res) {
|
||||
if (res.deviceType !== 'auncel') return
|
||||
auncelList.value = auncelList.value.map(item => {
|
||||
if (item.id === res.deviceId) {
|
||||
return {
|
||||
...item,
|
||||
weightData: res.weightData,
|
||||
weightUnit: res.weightUnit,
|
||||
weightStable: res.weightStable,
|
||||
isConnected: 1,
|
||||
controlRealName: res.controlRealName
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
function handleDeviceStatus(res) {
|
||||
auncelList.value = auncelList.value.map(item => {
|
||||
if (item.id === res.deviceId) {
|
||||
const updated = { ...item, isConnected: res.connected }
|
||||
if (res.connected === 0) {
|
||||
updated.weightData = '天平断开'
|
||||
updated.weightUnit = ''
|
||||
updated.weightStable = 0
|
||||
}
|
||||
return updated
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
function handleConnClose() {
|
||||
auncelList.value = auncelList.value.map(i => ({
|
||||
...i,
|
||||
weightData: '',
|
||||
weightUnit: '',
|
||||
weightStable: 0,
|
||||
controlRealName: '',
|
||||
isConnected: 0
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.auncel-item {
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.auncel-header {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auncel-code {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.auncel-name {
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.weight {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.weight-data,
|
||||
.weight-data-yellow,
|
||||
.weight-data-warning {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
text-align: right;
|
||||
line-height: 40px;
|
||||
letter-spacing: 2px;
|
||||
font-size: 32px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-data {
|
||||
color: #4cd964;
|
||||
}
|
||||
|
||||
.weight-data-yellow {
|
||||
color: #ffff00;
|
||||
}
|
||||
|
||||
.weight-data-warning {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.weight-unit {
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
839
pages/analysis/auncel/auncel-weigh.vue
Normal file
839
pages/analysis/auncel/auncel-weigh.vue
Normal file
@@ -0,0 +1,839 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-navbar
|
||||
title-color="#fff"
|
||||
title-width="360"
|
||||
back-icon-color="#ffffff"
|
||||
:back-text-style="{ color: '#fff' }"
|
||||
back-text="返回"
|
||||
title="样品称重"
|
||||
:border-bottom="false"
|
||||
:custom-back="customBack"
|
||||
:background="{ 'background-color': '#0055A2' }"
|
||||
></u-navbar>
|
||||
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="3">
|
||||
<view class="content-title-name">
|
||||
<text>待称重样品</text>
|
||||
</view>
|
||||
<u-gap height="10" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="6">
|
||||
<view class="content-title-name">
|
||||
<text>{{ currentAuncel.code === '' ? '电子天平' : currentAuncel.code }}</text>
|
||||
</view>
|
||||
<u-gap height="10" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<view class="content-title-name">
|
||||
<text>已称重样品</text>
|
||||
</view>
|
||||
<u-gap height="10" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<u-row gutter="16" class="content-main-height" align="top">
|
||||
<u-col span="3">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-left" :scroll-top="scrollTop">
|
||||
<view
|
||||
v-for="(sample, index) in notWeighSampleList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']"
|
||||
:data-current="index"
|
||||
@tap.stop="swichSample(index)"
|
||||
>
|
||||
<u-row gutter="20" style="width: 100%">
|
||||
<u-col span="3" style="padding: 0rpx">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="9">
|
||||
<view>
|
||||
<text>{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view style="padding-top: 20rpx">
|
||||
<text>{{ sample.dataSourceType | getDataSourceTypeShow }}{{ sample.goodsName }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="content-main-left-operation">
|
||||
<u-row>
|
||||
<u-col span="2"></u-col>
|
||||
<u-col span="8">
|
||||
<view>
|
||||
<view style="padding: 20rpx; text-align: right">
|
||||
<text style="font-size: 30rpx; height: 50rpx">顺序称重:</text>
|
||||
<u-switch
|
||||
v-model="orderWeighChecked"
|
||||
active-color="#19be6b"
|
||||
inactive-color="#eee"
|
||||
@change="orderWeighChange"
|
||||
></u-switch>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="2"></u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="6">
|
||||
<view class="content-main-height">
|
||||
<view class="auncel-container">
|
||||
<view class="auncel">
|
||||
<view class="auncel-title">
|
||||
<view style="padding-top: 40rpx; height: 100rpx; width: 100%; text-align: center">
|
||||
<text style="font-size: 44rpx; font-weight: 300"
|
||||
>【{{ currentSample.sort }}】 {{ currentSample.dataSourceType | getDataSourceTypeShow }}</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view style="padding-top: 30rpx; height: 100rpx; width: 100%; text-align: center">
|
||||
<text style="font-size: 44rpx; font-weight: 300">{{ currentSample.sampleCode }}</text>
|
||||
</view>
|
||||
<view
|
||||
style="
|
||||
padding-top: 20rpx;
|
||||
padding-left: 140rpx;
|
||||
padding-right: 140rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
"
|
||||
>
|
||||
<block v-if="currentSample.dataSourceType">
|
||||
<u-row v-if="currentSample.dataSourceType == 2 || currentSample.dataSourceType == 3">
|
||||
<u-col span="1"></u-col>
|
||||
<u-col span="3">
|
||||
<u-input
|
||||
style="color: #3b4144; background-color: #ffffff"
|
||||
v-model="currentCupNum"
|
||||
type="number"
|
||||
border="border"
|
||||
:disabled="checkCupNumReadonly"
|
||||
placeholder="杯号"
|
||||
/>
|
||||
</u-col>
|
||||
<u-col span="2">
|
||||
<text style="width: 100%; text-align: left">号杯</text>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<u-input
|
||||
style="color: #3b4144; background-color: #ffffff"
|
||||
v-model="currentSample.remarks"
|
||||
type="number"
|
||||
border="border"
|
||||
placeholder="样重比例"
|
||||
/>
|
||||
</u-col>
|
||||
<u-col span="2">
|
||||
<text style="width: 100%; text-align: left">%</text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row v-else>
|
||||
<u-col span="2"></u-col>
|
||||
<u-col span="7">
|
||||
<u-input
|
||||
style="color: #3b4144; background-color: #ffffff"
|
||||
v-model="currentCupNum"
|
||||
type="number"
|
||||
border="border"
|
||||
:disabled="checkCupNumReadonly"
|
||||
placeholder="杯号"
|
||||
/>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<text style="width: 100%; text-align: left">号杯</text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="auncel-weight">
|
||||
<view class="weight">
|
||||
<view
|
||||
:class="
|
||||
currentAuncel.weightStable == 0
|
||||
? 'weight-data-yellow'
|
||||
: currentAuncel.weightStable == 1
|
||||
? 'weight-data'
|
||||
: 'weight-data-warning'
|
||||
"
|
||||
@click="selectAuncel"
|
||||
>
|
||||
{{ currentAuncel.weightData }}
|
||||
</view>
|
||||
<view class="weight-unit">{{ currentAuncel.weightUnit }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<view style="padding: 0rpx 60rpx 10rpx 60rpx">
|
||||
<block v-if="currentAuncel.code !== ''">
|
||||
<u-button
|
||||
style="height: 140rpx; font-size: 80rpx"
|
||||
type="success"
|
||||
:disabled="confirmWeightDisabled"
|
||||
shape="circle"
|
||||
@click="confirm"
|
||||
>确认采集</u-button
|
||||
>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right" :scroll-top="scrollTop">
|
||||
<view
|
||||
v-for="(sample, index) in finishWeighSampleList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
style="height: 240rpx"
|
||||
>
|
||||
<u-row gutter="20" style="width: 100%">
|
||||
<u-col span="3" style="padding: 0rpx">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
<view style="padding-top: 20rpx">
|
||||
<text>{{ sample.cupNum }}号杯</text>
|
||||
</view>
|
||||
<view v-if="sample.remarks" style="padding-top: 20rpx">
|
||||
<text>{{ sample.remarks }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="9">
|
||||
<view>
|
||||
<text>{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view style="padding-top: 20rpx">
|
||||
<text>{{ sample.dataSourceType | getDataSourceTypeShow }}{{ sample.goodsName }}</text>
|
||||
</view>
|
||||
<view style="padding-top: 20rpx">
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleWeight }} g</text>
|
||||
<u-button style="margin-left: 20rpx" size="mini" type="error" @click="weighRevoke(sample.id)"
|
||||
>撤销</u-button
|
||||
>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="2"></u-col>
|
||||
<u-col span="8">
|
||||
<u-button
|
||||
:disabled="finishWeighSampleList.length == 0"
|
||||
class="btn-operation"
|
||||
type="success"
|
||||
@click="submit"
|
||||
>立即提交</u-button
|
||||
>
|
||||
</u-col>
|
||||
<u-col span="2"></u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
orderWeighChecked: true, //顺序称重
|
||||
weightDataIsToZero: false, //重量数据是否归零
|
||||
currentCupNum: '',
|
||||
currentSample: {}, //当前样品
|
||||
currentTaskNo: '', //当前任务样品
|
||||
currentTaskType: '', //当前任务类型
|
||||
currentAuncel: {
|
||||
id: '',
|
||||
name: '',
|
||||
code: '',
|
||||
weightStable: 0,
|
||||
weightData: '请选天平',
|
||||
weightUnit: ''
|
||||
},
|
||||
allWeighSampleCount: 0,
|
||||
notWeighSampleList: [],
|
||||
finishWeighSampleList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
confirmWeightDisabled() {
|
||||
if (
|
||||
this.currentSample.sampleCode &&
|
||||
this.currentAuncel.weightStable == 1 &&
|
||||
Number(this.currentAuncel.weightData) > 0
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
checkCupNumReadonly() {
|
||||
if (!this.currentAuncel || this.currentAuncel.id == '') return true
|
||||
return false
|
||||
}
|
||||
},
|
||||
onLoad(param) {
|
||||
//console.log(param);
|
||||
if (param.currentTaskNo) {
|
||||
this.currentTaskNo = param.currentTaskNo
|
||||
this.currentTaskType = param.currentTaskType
|
||||
}
|
||||
if (param.currentAuncelId) {
|
||||
this.currentAuncel.id = param.currentAuncelId
|
||||
this.currentAuncel.weightData = ''
|
||||
}
|
||||
if (param.currentAuncelName) {
|
||||
this.currentAuncel.name = param.currentAuncelName
|
||||
}
|
||||
if (param.currentAuncelCode) {
|
||||
this.currentAuncel.code = param.currentAuncelCode
|
||||
}
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
},
|
||||
onShow() {
|
||||
console.log('onShow')
|
||||
|
||||
//注册websocket
|
||||
let regData = {
|
||||
msgId: this.$helper.uuid(),
|
||||
cmd: 'register',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
userId: this.userInfo.user_id,
|
||||
tenantId: this.userInfo.tenant_id,
|
||||
userRealName: this.userInfo.real_name
|
||||
}
|
||||
}
|
||||
this.$measure.setRegData(JSON.stringify(regData))
|
||||
this.$measure.reOpen()
|
||||
|
||||
//监听websocket返回来的数据
|
||||
//设备数据
|
||||
uni.$on('deviceData', res => {
|
||||
switch (res.deviceType) {
|
||||
case 'auncel':
|
||||
//console.log(res);
|
||||
if (this.currentAuncel.id === res.deviceId) {
|
||||
this.currentAuncel.weightData = res.weightData
|
||||
this.currentAuncel.weightUnit = res.weightUnit
|
||||
this.currentAuncel.weightStable = res.weightStable
|
||||
this.currentAuncel.isConnected = 1
|
||||
if (Number(res.weightData) == 0) {
|
||||
this.weightDataIsToZero = true
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
//设备状态
|
||||
uni.$on('deviceStatus', res => {
|
||||
if (this.currentAuncel.id === res.deviceId) {
|
||||
console.log(res)
|
||||
if (res.connected == 0) {
|
||||
this.currentAuncel.weightStable = 0
|
||||
this.currentAuncel.weightData = '天平断开'
|
||||
this.currentAuncel.weightUnit = ''
|
||||
}
|
||||
}
|
||||
this.currentAuncel.isConnected = res.connected
|
||||
})
|
||||
//控制设备状态
|
||||
uni.$on('controlDevice', res => {
|
||||
if (this.currentAuncel.id === res.deviceId) {
|
||||
console.log(res)
|
||||
|
||||
this.currentAuncel.id = ''
|
||||
this.currentAuncel.name = ''
|
||||
this.currentAuncel.code = ''
|
||||
this.currentAuncel.weightStable = 0
|
||||
this.currentAuncel.weightData = '请选天平'
|
||||
this.currentAuncel.weightUnit = ''
|
||||
}
|
||||
})
|
||||
//连接断开
|
||||
uni.$on('connClose', res => {
|
||||
//重置
|
||||
this.currentAuncel.weightData = ''
|
||||
this.currentAuncel.weightUnit = ''
|
||||
this.currentAuncel.weightStable = 0
|
||||
this.currentAuncel.controlUserName = ''
|
||||
this.currentAuncel.isConnected = 0
|
||||
})
|
||||
},
|
||||
onHide() {
|
||||
console.log('onHide')
|
||||
//移除监听websocket返回来的数据
|
||||
uni.$off('deviceData')
|
||||
uni.$off('deviceStatus')
|
||||
uni.$off('controlDevice')
|
||||
uni.$off('connClose')
|
||||
},
|
||||
onUnload() {
|
||||
//取消控制
|
||||
//发送控制请求
|
||||
let controlDevice = {
|
||||
msgId: this.currentAuncel.id,
|
||||
cmd: 'controlDevice',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
deviceId: this.currentAuncel.id,
|
||||
isControl: false,
|
||||
controlRealName: this.userInfo.username
|
||||
}
|
||||
}
|
||||
//发送控制数据
|
||||
this.$measure.send(JSON.stringify(controlDevice))
|
||||
},
|
||||
methods: {
|
||||
//返回上一页
|
||||
customBack() {
|
||||
uni.redirectTo({
|
||||
url: '/pages/sample/sample-weigh'
|
||||
})
|
||||
},
|
||||
//切换样品
|
||||
async swichSample(index) {
|
||||
if (index == this.current) return
|
||||
if (this.orderWeighChecked) return //顺序称重不允许选中
|
||||
this.current = index
|
||||
// 如果为0,意味着尚未初始化
|
||||
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
await this.getElRect('menu-scroll-view', 'menuHeight')
|
||||
await this.getElRect('u-tab-item', 'menuItemHeight')
|
||||
}
|
||||
// 将菜单菜单活动item垂直居中
|
||||
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2
|
||||
this.currentSample = this.notWeighSampleList[index]
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query
|
||||
.select('.' + elClass)
|
||||
.fields({ size: true }, res => {
|
||||
// 如果节点尚未生成,res值为null,循环调用执行
|
||||
if (!res) {
|
||||
setTimeout(() => {
|
||||
this.getElRect(elClass)
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
this[dataVal] = res.height
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
confirm() {
|
||||
//保存数据
|
||||
if (!this.weightDataIsToZero) {
|
||||
this.$helper.showToast({
|
||||
title: '天平必须先回零!'
|
||||
})
|
||||
return
|
||||
}
|
||||
//获取当前重量
|
||||
let weight = this.currentAuncel.weightData
|
||||
//获取当前样品
|
||||
let sample = this.notWeighSampleList[this.current]
|
||||
//判断杯号
|
||||
if (this.currentCupNum == null || typeof this.currentCupNum == 'undefined') this.currentCupNum = ''
|
||||
if (this.currentCupNum === '') {
|
||||
this.$helper.showToast({
|
||||
title: '杯号不允许为空!'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.current < this.notWeighSampleList.length - 1) {
|
||||
this.currentSample = this.notWeighSampleList[this.current + 1]
|
||||
}
|
||||
//杯号
|
||||
sample.cupNum = this.currentCupNum
|
||||
//删除当前样品
|
||||
this.notWeighSampleList.splice(this.current, 1)
|
||||
//样品重量赋值
|
||||
sample.sampleWeight = weight
|
||||
//天平编号赋值
|
||||
sample.auncelNo = this.currentAuncel.code
|
||||
if (sample.remarks) {
|
||||
sample.remarks = sample.remarks + '%'
|
||||
}
|
||||
this.$u.api
|
||||
.auncelWeigh(sample)
|
||||
.then(res => {
|
||||
//this.finishWeighSampleList.push(sample);
|
||||
//确认采集重量后,归零设为false
|
||||
this.weightDataIsToZero = false
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
if (this.orderWeighChecked) {
|
||||
this.current = 0
|
||||
} else {
|
||||
this.current = -1
|
||||
this.currentSample = {}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
submit() {
|
||||
if (this.notWeighSampleList.length != 0) {
|
||||
this.$helper.showToast({
|
||||
title: '当前任务“' + this.currentTaskNo + '”尚未完成称重!'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定提交数据?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.cancel) {
|
||||
console.log('用户点击取消')
|
||||
return
|
||||
}
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '正在提交...'
|
||||
})
|
||||
this.$u.api
|
||||
.weighBatchSubmitByTaskNo(this.currentTaskNo)
|
||||
.then(res => {
|
||||
this.$helper.showToast({
|
||||
title: '提交成功!'
|
||||
})
|
||||
uni.redirectTo({
|
||||
url: '/pages/sample/sample-weigh'
|
||||
})
|
||||
uni.hideLoading()
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
this.$helper.showToast({
|
||||
title: '提交失败!'
|
||||
})
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
/**
|
||||
if(this.currentTaskType == 4) { //临时样
|
||||
let ids = [];
|
||||
this.finishWeighSampleList.forEach(item => {
|
||||
ids.push(item.id);
|
||||
});
|
||||
|
||||
this.$u.api
|
||||
.weighBatchSubmitByIds(ids.join(","))
|
||||
.then(res => {
|
||||
this.$helper.showToast({
|
||||
title: '提交成功!'
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false;
|
||||
console.log(err);
|
||||
});
|
||||
} else {//其他批量秤完提交
|
||||
//if(this.allWeighSampleCount == this.finishWeighSampleList.length) {
|
||||
console.log(this.notWeighSampleList)
|
||||
if(this.notWeighSampleList.length == 0) {
|
||||
this.$u.api
|
||||
.weighBatchSubmitByTaskNo(this.currentTaskNo)
|
||||
.then(res => {
|
||||
this.$helper.showToast({
|
||||
title: '提交成功!'
|
||||
});
|
||||
uni.redirectTo({
|
||||
url: '/pages/sample/sample-weigh'
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false;
|
||||
console.log(err);
|
||||
});
|
||||
} else {
|
||||
this.$helper.showToast({
|
||||
title: '当前任务“'+this.currentTaskNo+'”尚未完成称重!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
**/
|
||||
},
|
||||
weighRevoke(id) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定撤销当前数据?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (!res.confirm) {
|
||||
console.log('用户点击取消')
|
||||
return
|
||||
}
|
||||
this.$u.api
|
||||
.auncelWeighRevoke(id)
|
||||
.then(res => {
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
if (this.orderWeighChecked) {
|
||||
this.current = 0
|
||||
} else {
|
||||
this.current = -1
|
||||
this.currentSample = {}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
//自动生成杯号(仅顺序称重):第一杯:手填,后续杯 = 上一杯 + 1
|
||||
autoGenerateCupNum() {
|
||||
if (!this.orderWeighChecked) return
|
||||
//取上一杯
|
||||
this.currentCupNum = ''
|
||||
let preSample = null
|
||||
if (this.finishWeighSampleList && this.finishWeighSampleList.length > 0) preSample = this.finishWeighSampleList[0]
|
||||
if (preSample == null) {
|
||||
return
|
||||
}
|
||||
const cupNum = preSample.cupNum
|
||||
if (!isNaN(cupNum)) this.currentCupNum = Number(cupNum) + 1
|
||||
},
|
||||
getAssayTaskDetail(taskNo) {
|
||||
this.notWeighSampleList = []
|
||||
this.finishWeighSampleList = []
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
|
||||
.then(res => {
|
||||
let dataList = res.result
|
||||
this.allWeighSampleCount = dataList.length
|
||||
dataList.forEach((item, index) => {
|
||||
if (item.weightSubmitStatus == 0) {
|
||||
this.notWeighSampleList.push(item)
|
||||
} else if (item.weightSubmitStatus == 1) {
|
||||
this.finishWeighSampleList.unshift(item)
|
||||
//this.finishWeighSampleList.push(item);
|
||||
}
|
||||
})
|
||||
this.showLoading = false
|
||||
//console.log(this.currentSample);
|
||||
if (this.notWeighSampleList[0]) {
|
||||
if (this.orderWeighChecked) {
|
||||
this.current = 0
|
||||
this.currentSample = this.notWeighSampleList[0]
|
||||
} else {
|
||||
this.current = -1
|
||||
this.currentSample = {}
|
||||
}
|
||||
}
|
||||
this.autoGenerateCupNum() //计算生成当前杯号
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
selectAuncel() {
|
||||
if (this.currentAuncel.weightData === '请选天平') {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/auncel/auncel-select?currentTaskNo=' +
|
||||
this.currentTaskNo +
|
||||
'¤tTaskType=' +
|
||||
this.currentTaskType
|
||||
})
|
||||
}
|
||||
},
|
||||
orderWeighChange(e) {
|
||||
if (e && this.notWeighSampleList.length > 0) {
|
||||
//this.current = 0;
|
||||
//this.currentSample = this.notWeighSampleList[0];
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
return
|
||||
}
|
||||
this.current = -1
|
||||
this.currentSample = {}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
getDataSourceTypeShow(val) {
|
||||
if (val == 2) return '【筛上】'
|
||||
if (val == 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
font-size: 46rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 250rpx);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
height: calc(100vh - 410rpx);
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
height: 160rpx;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 4rpx;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 32rpx;
|
||||
left: 0;
|
||||
top: 39rpx;
|
||||
}
|
||||
|
||||
.content-main-left-operation {
|
||||
height: 160rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 410rpx);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 160rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
height: 100rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.auncel-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auncel {
|
||||
width: 800rpx;
|
||||
height: 800rpx;
|
||||
background-image: url(../../static/images/auncel.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.auncel-title {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.auncel-weight {
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.weight {
|
||||
width: 100%;
|
||||
min-width: 200px;
|
||||
height: 200rpx;
|
||||
padding: 0 30rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
//background-color: #2c405a;
|
||||
}
|
||||
|
||||
.weight-data {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #4cd964;
|
||||
text-align: right;
|
||||
line-height: 200rpx;
|
||||
letter-spacing: 4rpx;
|
||||
font-size: 150rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-data-yellow {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ffff00;
|
||||
text-align: right;
|
||||
line-height: 200rpx;
|
||||
letter-spacing: 4rpx;
|
||||
font-size: 150rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-data-warning {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ff3333;
|
||||
text-align: right;
|
||||
line-height: 200rpx;
|
||||
font-size: 150rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-unit {
|
||||
color: #ffffff;
|
||||
font-size: 130rpx;
|
||||
line-height: 200rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
</style>
|
||||
105
pages/analysis/index/index.vue
Normal file
105
pages/analysis/index/index.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="检斤检化验系统" :autoBack="false" leftIcon="" :leftText="`您好!${userInfo.nickname}`">
|
||||
<u-icon @click="popupShow = true" size="28" color="#FFF" name="account-fill" />
|
||||
</navbar-back>
|
||||
|
||||
<up-grid :col="gridCol" :border="false">
|
||||
<up-grid-item class="mb20 mt20" v-for="item in menuItemList" :key="item.url" @click="goTo(item.url)">
|
||||
<u-icon :name="item.otherConf.icon" color="#0055A2" size="80" />
|
||||
<view class="grid-text">{{ item.name }}</view>
|
||||
</up-grid-item>
|
||||
</up-grid>
|
||||
|
||||
<mePopup :show="popupShow" @update:show="val => (popupShow = val)" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
import mePopup from '@/pages/index/me-popup.vue'
|
||||
|
||||
// 响应式数据
|
||||
const popupShow = ref(false)
|
||||
const isAllowAgainPrint = ref(false)
|
||||
|
||||
const menuItemList = ref([
|
||||
{ url: '/pages/analysis/sample/sample-receive', otherConf: { icon: 'arrow-downward' }, name: '收样' },
|
||||
{ url: '/pages/analysis/sample/sample-work-list', otherConf: { icon: 'edit-pen-fill' }, name: '样品分析' },
|
||||
{ url: '/pages/analysis/sample/sample-report', otherConf: { icon: 'arrow-upward' }, name: '数据上报' },
|
||||
{ url: '/pages/analysis/sample/sample-report-search', otherConf: { icon: 'search' }, name: '待审数据' },
|
||||
{ url: '/pages/analysis/sample/sample-print', otherConf: { icon: 'file-text-fill' }, name: '单据补打' },
|
||||
{ url: '/pages/analysis/setting/setting', otherConf: { icon: 'setting-fill' }, name: '系统设置' }
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const goTo = url => {
|
||||
nx.$router.go(url)
|
||||
}
|
||||
|
||||
const checkAllowAgainPrint = () => {
|
||||
const param = {
|
||||
userId: userInfo.value.id,
|
||||
clientId: 'auncel'
|
||||
}
|
||||
nx.$api.user.getAppPermission(param).then(res => {
|
||||
if (!res.success) return
|
||||
const ret = res.result || []
|
||||
if (ret.length === 0) return
|
||||
for (let i = 0; i < ret.length; i++) {
|
||||
if (ret[i].roleValue && ret[i].roleValue === 2) {
|
||||
isAllowAgainPrint.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const openLaboratoryWs = () => {
|
||||
const regData = {
|
||||
msgId: nx.$helper.uuid(),
|
||||
cmd: 'register',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
userId: userInfo.value.id,
|
||||
tenantId: userInfo.value.loginTenantId,
|
||||
userRealName: userInfo.value.realname
|
||||
}
|
||||
}
|
||||
nx.$measure.setRegData(JSON.stringify(regData))
|
||||
nx.$measure.open()
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// checkAllowAgainPrint()
|
||||
// openLaboratoryWs()
|
||||
})
|
||||
// 动态设置 grid 列数
|
||||
const { gridCol } = useGridCol([400], [2, 3])
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title_content {
|
||||
text-align: center;
|
||||
padding-top: 75px;
|
||||
letter-spacing: 10px;
|
||||
width: 100%;
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.banner {
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.grid-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
583
pages/analysis/index/index2.vue
Normal file
583
pages/analysis/index/index2.vue
Normal file
@@ -0,0 +1,583 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<u-navbar
|
||||
title="化学分析天平"
|
||||
:is-back="false"
|
||||
:border-bottom="false"
|
||||
title-color="#fff"
|
||||
:background="{ 'background-image': 'linear-gradient(45deg, rgb(54, 138, 217), rgb(64, 160, 255))' }"
|
||||
></u-navbar>
|
||||
<view style="text-align: center"><u-loading mode="flower" :show="showLoading" size="48"></u-loading></view>
|
||||
<view class="container">
|
||||
<view class="auncel" v-for="(item, index) in deviceLaboratoryList" :key="item.id">
|
||||
<view class="title">
|
||||
<u-row style="width: 100%; padding-bottom: 18rpx" gutter="16">
|
||||
<u-col span="8">
|
||||
<text class="measurePointName"
|
||||
>{{ item.name }} {{ item.controlUserName ? '[' + item.controlUserName + ']远程控制' : '' }}</text
|
||||
>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<!--
|
||||
<view style="width: 100%; display: flex; flex-direction: row; justify-content: flex-end;">
|
||||
<text class="measurePointName" >{{item.isAutoWork ? '自动计量' : '集控接管'}}</text>
|
||||
<u-icon style="padding: 0 20rpx;" name="file-text" label-pos="bottom" color="#2979ff" size="40" @click="goto(item.measurePointId)"></u-icon>
|
||||
<u-icon style="padding: 0 20rpx;" name="setting" label-pos="bottom" label="设置" color="#2979ff" size="40" @click="goto(item.measurePointId)"></u-icon>
|
||||
</view>
|
||||
-->
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
<view :class="item.isConnected == 1 ? 'title-line-green' : 'title-line-gray'"></view>
|
||||
<view class="weight-container">
|
||||
<!-- <view class="infrared-container">
|
||||
<view :class="item.infraredLeft == 0 ? 'infrared-line-gray' : item.infraredLeft == 1 ? 'infrared-line-green':'infrared-line-red'"></view>
|
||||
</view>
|
||||
-->
|
||||
<block v-if="item.deviceType == 'auncel'">
|
||||
<view class="weight">
|
||||
<view
|
||||
:class="
|
||||
item.weightStable == 0
|
||||
? 'weight-data-yellow'
|
||||
: item.weightStable == 1
|
||||
? 'weight-data'
|
||||
: 'weight-data-warning'
|
||||
"
|
||||
>
|
||||
{{ item.weightData }}
|
||||
</view>
|
||||
<view class="weight-unit">{{ item.weightUnit }}</view>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="item.deviceType == 'temp'">
|
||||
<view class="temp">
|
||||
<view class="charts-box"
|
||||
><qiun-data-charts
|
||||
type="gauge"
|
||||
:opts="temperatureOpts"
|
||||
:chartData="temperatureChartData"
|
||||
background="none"
|
||||
/></view>
|
||||
<view class="charts-box"
|
||||
><qiun-data-charts type="gauge" :opts="humidityOpts" :chartData="humidityChartData" background="none"
|
||||
/></view>
|
||||
</view>
|
||||
</block>
|
||||
<!--
|
||||
<view class="infrared-container">
|
||||
<view :class="item.infraredRight == 0 ? 'infrared-line-gray' : item.infraredRight == 1 ? 'infrared-line-green':'infrared-line-red'"></view>
|
||||
</view>
|
||||
-->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showLoading: false,
|
||||
runLogMaxLength: 8,
|
||||
deviceLaboratoryList: [],
|
||||
temperatureOpts: {},
|
||||
temperatureChartData: {
|
||||
categories: [
|
||||
{
|
||||
value: 0.2,
|
||||
color: '#1890ff'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
color: '#2fc25b'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '温度',
|
||||
data: 0.66
|
||||
}
|
||||
]
|
||||
},
|
||||
humidityOpts: {},
|
||||
humidityChartData: {
|
||||
categories: [
|
||||
{
|
||||
value: 1,
|
||||
color: '#1890ff'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '湿度',
|
||||
data: 0.66
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
//获取数据
|
||||
this.getPageData()
|
||||
|
||||
//监听websocket返回来的数据
|
||||
//设备数据
|
||||
uni.$on('deviceData', res => {
|
||||
switch (res.deviceType) {
|
||||
case 'weighbridge':
|
||||
this.mearsuPointList.forEach((item, index) => {
|
||||
if (item.measurePointId === res.measurePointId) {
|
||||
//item.weightData = (index + Number(res.deviceValue)).toFixed(2) + " "+ res.deviceUnit;
|
||||
item.weightData = res.deviceValue
|
||||
item.weightUnit = res.deviceUnit
|
||||
item.weightStable = res.deviceStable
|
||||
item.measureIsConnected = 1
|
||||
item.isAutoWork = res.isAutoWork
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'auncel':
|
||||
this.deviceLaboratoryList.forEach((item, index) => {
|
||||
if (item.id === res.deviceId) {
|
||||
//item.weightData = (index + Number(res.deviceValue)).toFixed(2) + " "+ res.deviceUnit;
|
||||
item.weightData = res.weightData
|
||||
item.weightUnit = res.weightUnit
|
||||
item.weightStable = 1
|
||||
item.isConnected = 1
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// this.weight = res.weighbridge;
|
||||
// this.infraredLeft = res.infrared;
|
||||
// this.infraredRight = res.infrared;
|
||||
})
|
||||
|
||||
//设备状态
|
||||
uni.$on('deviceStatus', res => {
|
||||
this.mearsuPointList.forEach((item, index) => {
|
||||
if (item.measurePointId === res.measurePointId) {
|
||||
item.devices.forEach((d, i) => {
|
||||
if (d.id === res.deviceId) {
|
||||
d.isConnected = res.connected
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//日志数据
|
||||
uni.$on('logData', res => {
|
||||
this.mearsuPointList.forEach((item, index) => {
|
||||
if (item.measurePointId === res.measurePointId) {
|
||||
if (item.runLogs.length >= this.runLogMaxLength) {
|
||||
//删除并返回数组的第一个元素
|
||||
//item.runLogs.shift();
|
||||
//删除并返回数组的最后一个元素
|
||||
item.runLogs.pop()
|
||||
}
|
||||
//向数组的末尾添加
|
||||
//item.runLogs.push(res);
|
||||
//向数组的开头添
|
||||
item.runLogs.unshift(res)
|
||||
|
||||
//滚动
|
||||
// this.$nextTick(function() {
|
||||
// item.scrollTop = 20*item.runLogs.length;
|
||||
// });
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//监听控制
|
||||
uni.$on('controlMeasurePoint', res => {
|
||||
console.log(res)
|
||||
let data = res.data
|
||||
this.mearsuPointList.forEach(i => {
|
||||
if (i.measurePointId === data.measurePointId) {
|
||||
if (data.success && data.isControl) {
|
||||
i.controlUserName = data.controlUserName
|
||||
} else {
|
||||
i.controlUserName = ''
|
||||
}
|
||||
console.log(i)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//计量点状态
|
||||
uni.$on('measurePointStatus', res => {
|
||||
//断开
|
||||
this.mearsuPointList.forEach(i => {
|
||||
if (i.measurePointId === res.measurePointId) {
|
||||
if (res.measureIsConnected != 1) {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.weightStable = 0
|
||||
i.infraredLeft = 0
|
||||
i.infraredRight = 0
|
||||
i.measureIsConnected = 0
|
||||
if (i.runLogs.length >= this.runLogMaxLength) {
|
||||
i.runLogs.pop()
|
||||
}
|
||||
i.runLogs.unshift({
|
||||
id: this.$helper.uuid(),
|
||||
content: '计量点连接断开!',
|
||||
measurePointId: i.measurePointId,
|
||||
time: this.$helper.dateFormat(new Date(), 'yy-MM-dd hh:mm:ss')
|
||||
})
|
||||
i.devices.forEach(d => {
|
||||
d.isConnected = 0
|
||||
})
|
||||
} else {
|
||||
i.measureIsConnected = 1
|
||||
/* if(i.runLogs.length >= 8) {
|
||||
i.runLogs.shift();
|
||||
}
|
||||
i.runLogs.push({
|
||||
id: this.$helper.uuid(),
|
||||
content: "计量点连接成功!",
|
||||
measurePointId: i.measurePointId,
|
||||
time: this.$helper.dateFormat(new Date(), "yy-MM-dd hh:mm:ss")
|
||||
}); */
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//连接断开
|
||||
uni.$on('connClose', res => {
|
||||
//重置
|
||||
this.mearsuPointList.forEach(i => {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.weightStable = 0
|
||||
i.controlUserName = ''
|
||||
i.measureIsConnected = 0
|
||||
if (i.runLogs.length >= this.runLogMaxLength) {
|
||||
i.runLogs.pop()
|
||||
}
|
||||
let lastLog = i.runLogs[i.runLogs.length - 1]
|
||||
if (lastLog && lastLog.content !== '控制中心连接断开!') {
|
||||
i.runLogs.unshift({
|
||||
id: this.$helper.uuid(),
|
||||
content: '控制中心连接断开!',
|
||||
measurePointId: i.measurePointId,
|
||||
time: this.$helper.dateFormat(new Date(), 'yy-MM-dd hh:mm:ss')
|
||||
})
|
||||
}
|
||||
i.devices.forEach(d => {
|
||||
d.isConnected = 0
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onUnload() {
|
||||
//移除监听websocket返回来的数据
|
||||
uni.$off('deviceData')
|
||||
uni.$off('deviceStatus')
|
||||
uni.$off('logData')
|
||||
uni.$off('controlMeasurePoint')
|
||||
|
||||
uni.$off('measurePointStatus')
|
||||
uni.$off('connClose')
|
||||
},
|
||||
filters: {
|
||||
numFilter(value) {
|
||||
// 截取当前数据到小数点后两位
|
||||
let realVal = value.toFixed(2)
|
||||
return realVal
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
getPageData() {
|
||||
//显示loading
|
||||
this.showLoading = true
|
||||
//获取计量点数量
|
||||
this.$u.api
|
||||
.getDeviceLaboratoryListBy()
|
||||
.then(res => {
|
||||
let dataList = res.data
|
||||
dataList.forEach(i => {
|
||||
i.weightData = ''
|
||||
i.weightUnit = ''
|
||||
i.isConnected = 0
|
||||
i.weightStable = 0
|
||||
i.temperature = 0
|
||||
i.humidity = 0
|
||||
i.controlUserName = ''
|
||||
})
|
||||
this.deviceLaboratoryList = dataList
|
||||
this.showLoading = false
|
||||
|
||||
//打开websocket
|
||||
this.openLaboratoryWs()
|
||||
|
||||
//发送检查计量点控制情况
|
||||
let checkControl = {
|
||||
msgId: this.$helper.uuid(),
|
||||
cmd: 'checkControl',
|
||||
clientType: 'consoleClient'
|
||||
}
|
||||
if (this.$measure.isOpen) {
|
||||
this.$measure.send(JSON.stringify(checkControl))
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
openLaboratoryWs() {
|
||||
//注册websocket
|
||||
let regData = {
|
||||
msgId: this.$helper.uuid(),
|
||||
cmd: 'register',
|
||||
clientType: 'caaClient',
|
||||
data: {
|
||||
userId: this.userInfo.user_id,
|
||||
tenantId: this.userInfo.tenant_id,
|
||||
userRealName: this.userInfo.real_name
|
||||
}
|
||||
}
|
||||
this.$measure.setRegData(JSON.stringify(regData))
|
||||
this.$measure.open()
|
||||
},
|
||||
scroll(e) {
|
||||
// console.log(e);
|
||||
// this.mearsuPointList.forEach((i) => {
|
||||
// if(i.measurepointid === e.currentTarget.dataset.measurepointid) {
|
||||
// i.oldScrollTop = e.detail.scrollTop;
|
||||
// }
|
||||
// });
|
||||
},
|
||||
goto(measurePointId) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/measure/measure?measurePointId=' + measurePointId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
/* #ifdef H5 */
|
||||
.auncel {
|
||||
width: 31.33%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx;
|
||||
margin: 1%;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 1px 1px 10px #909399;
|
||||
}
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.auncel {
|
||||
width: 48%;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.auncel {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
.auncel {
|
||||
width: 98%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx;
|
||||
margin: 1%;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 1px 1px 10px #909399;
|
||||
}
|
||||
/* #endif */
|
||||
.title-line-gray {
|
||||
width: 100%;
|
||||
height: 6rpx;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgb(128, 134, 149);
|
||||
border-radius: 4rpx 4rpx 0 0;
|
||||
}
|
||||
.title-line-green {
|
||||
width: 100%;
|
||||
height: 6rpx;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgb(27, 201, 142);
|
||||
border-radius: 4rpx 4rpx 0 0;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.temp {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.charts-box {
|
||||
width: 50%;
|
||||
height: 200px;
|
||||
}
|
||||
.measurePointName {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.weight-container {
|
||||
padding: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.infrared-container {
|
||||
width: 30%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.infrared-line-green {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #18b566;
|
||||
}
|
||||
.infrared-line-red {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #ff3333;
|
||||
}
|
||||
.infrared-line-gray {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #909399;
|
||||
}
|
||||
.infrared-left {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #18b566;
|
||||
}
|
||||
.infrared-left-warning {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #ff3333;
|
||||
}
|
||||
.weight {
|
||||
width: 100%;
|
||||
min-width: 200px;
|
||||
height: 90rpx;
|
||||
padding: 0 8rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #2c405a;
|
||||
}
|
||||
.weight-data {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #4cd964;
|
||||
text-align: right;
|
||||
line-height: 90rpx;
|
||||
letter-spacing: 4rpx;
|
||||
font-size: 90rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-data-yellow {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ffff00;
|
||||
text-align: right;
|
||||
line-height: 90rpx;
|
||||
letter-spacing: 4rpx;
|
||||
font-size: 90rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-data-warning {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ff3333;
|
||||
text-align: right;
|
||||
line-height: 90rpx;
|
||||
font-size: 90rpx;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
.weight-unit {
|
||||
color: #ffffff;
|
||||
font-size: 60rpx;
|
||||
line-height: 90rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
.infrared-right {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #18b566;
|
||||
}
|
||||
.infrared-right-warning {
|
||||
width: 8rpx;
|
||||
height: 90rpx;
|
||||
background-color: #ff3333;
|
||||
}
|
||||
.device-container {
|
||||
padding: 20rpx;
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.device {
|
||||
width: 25%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.device-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: #ff3333;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.device-icon-green {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: #18b566;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.device-icon-gray {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: rgb(128, 134, 149);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.device-icon-red {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: #ff3333;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.run-log {
|
||||
width: 100%;
|
||||
padding: 10rpx 20rpx;
|
||||
height: 400rpx;
|
||||
background-color: #ecf5ff;
|
||||
border-radius: 24rpx;
|
||||
--box-shadow: 1px 1px 10px #909399;
|
||||
}
|
||||
.log-content {
|
||||
font-size: 28rpx;
|
||||
font-weight: 200;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
</style>
|
||||
143
pages/analysis/sample/pdf-preview.vue
Normal file
143
pages/analysis/sample/pdf-preview.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="任务单预览"></navbar-back>
|
||||
<view></view>
|
||||
<!-- #ifdef H5 -->
|
||||
<web-view :src="pdfUrlH5"></web-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onLoad, onHide, onUnload } from '@dcloudio/uni-app'
|
||||
import { getBaseUrl } from '@/defaultBaseUrl'
|
||||
import nx from '@/nx'
|
||||
|
||||
// 响应式数据
|
||||
const taskId = ref('')
|
||||
const localFilePath = ref('')
|
||||
const reportKey = ref('')
|
||||
const hideResultFlag = ref('')
|
||||
const viewerUrl = '/hybrid/html/web/viewer.html?file='
|
||||
const pdfUrlH5 = ref('')
|
||||
|
||||
let wv = null // 计划创建的 webview
|
||||
|
||||
// 方法:下载 PDF
|
||||
const downloadPdf = (pdfUrl, retryCount = 0) => {
|
||||
const MAX_RETRY = 3 // 最多重试 3 次
|
||||
return new Promise(resolve => {
|
||||
if (retryCount >= MAX_RETRY) {
|
||||
console.error('PDF 下载失败:超过最大重试次数')
|
||||
return resolve(null)
|
||||
}
|
||||
|
||||
uni.downloadFile({
|
||||
url: pdfUrl,
|
||||
success: res => {
|
||||
if (res.statusCode !== 200) {
|
||||
console.warn(`下载失败,状态码: ${res.statusCode},重试第 ${retryCount + 1} 次`)
|
||||
return resolve(downloadPdf(pdfUrl, retryCount + 1))
|
||||
}
|
||||
|
||||
const tempFilePath = res.tempFilePath
|
||||
uni.getFileInfo({
|
||||
filePath: tempFilePath,
|
||||
success: fileInfo => {
|
||||
if (fileInfo.size > 0) {
|
||||
resolve(tempFilePath)
|
||||
} else {
|
||||
console.warn('文件为空,重试中...')
|
||||
resolve(downloadPdf(pdfUrl, retryCount + 1))
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
console.warn('获取文件信息失败,重试中...')
|
||||
resolve(downloadPdf(pdfUrl, retryCount + 1))
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
console.warn('网络请求失败,重试中...')
|
||||
resolve(downloadPdf(pdfUrl, retryCount + 1))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 方法:Android 加载 PDF
|
||||
const loadPdfOnAndroid = async pdfUrl => {
|
||||
const tempFilePath = await downloadPdf(pdfUrl)
|
||||
if (!tempFilePath) return
|
||||
const localPath = plus.io.convertLocalFileSystemURL(tempFilePath)
|
||||
localFilePath.value = localPath
|
||||
const allUrl = viewerUrl + encodeURIComponent(localPath)
|
||||
// 创建并加载 webview
|
||||
wv = plus.webview.create('', 'custom-webview', {
|
||||
top: uni.getSystemInfoSync().statusBarHeight + 44,
|
||||
bottom: 0
|
||||
})
|
||||
wv.loadURL(allUrl)
|
||||
const currentWebview = getCurrentPages().pop().$getAppWebview()
|
||||
currentWebview.append(wv)
|
||||
}
|
||||
|
||||
// 获取 PDF 预览 URL
|
||||
const getPdf = async () => {
|
||||
const printBaseUrl = getBaseUrl()
|
||||
const baseUrl = getBaseUrl()
|
||||
let dataUrl = `${baseUrl}/qms/bus/qmsBusAssayTask/getAssayTaskDataWithDetailData?taskId=${taskId.value}`
|
||||
dataUrl += `&hideResultFlag=${hideResultFlag.value}`
|
||||
const token = nx.$store('user').token
|
||||
let url = `${printBaseUrl}/report/gridpp/report/previewDocs`
|
||||
url += `?token=${token}`
|
||||
url += `&type=PDF`
|
||||
url += `&reportKey=${reportKey.value}`
|
||||
url += `&dataUrl=${encodeURIComponent(dataUrl)}`
|
||||
|
||||
// #ifdef H5
|
||||
pdfUrlH5.value = viewerUrl + encodeURIComponent(url)
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
await loadPdfOnAndroid(url)
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 删除临时文件
|
||||
const deleteTmpFile = () => {
|
||||
if (!localFilePath.value) return
|
||||
const dir = localFilePath.value.substring(0, localFilePath.value.lastIndexOf('/'))
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
dir,
|
||||
entry => {
|
||||
entry.removeRecursively(
|
||||
() => console.log('删除成功'),
|
||||
e => console.log('删除失败:' + e.message)
|
||||
)
|
||||
},
|
||||
e => console.log('目录不存在:' + e.message)
|
||||
)
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onLoad(param => {
|
||||
if (param.taskId) {
|
||||
taskId.value = param.taskId
|
||||
reportKey.value = param.reportKey
|
||||
hideResultFlag.value = param.hideResultFlag
|
||||
}
|
||||
getPdf()
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
deleteTmpFile()
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
deleteTmpFile()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
237
pages/analysis/sample/sample-look.vue
Normal file
237
pages/analysis/sample/sample-look.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="任 务 单" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']"
|
||||
:data-current="index"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 42rpx">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 20rpx">
|
||||
<text>{{ task.taskName }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 20rpx">
|
||||
<u-icon color="" name="clock"></u-icon>
|
||||
<text style="margin-left: 10rpx">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-height">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view
|
||||
v-if="index == 0 || (index > 0 && sampleList[index].sort != sampleList[index - 1].sort)"
|
||||
style="padding: 10rpx; font-size: 36rpx"
|
||||
>
|
||||
<u-row>
|
||||
<u-col span="2" style="text-align: center">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleName }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 10rpx" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
taskList: [],
|
||||
sampleList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
//获取任务列表
|
||||
this.getAssayTask()
|
||||
},
|
||||
methods: {
|
||||
//返回首页
|
||||
customBack() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/analysis/index/index'
|
||||
})
|
||||
},
|
||||
//切换任务
|
||||
async swichTask(index) {
|
||||
if (index == this.current) return
|
||||
this.current = index
|
||||
// 如果为0,意味着尚未初始化
|
||||
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
await this.getElRect('menu-scroll-view', 'menuHeight')
|
||||
await this.getElRect('u-tab-item', 'menuItemHeight')
|
||||
}
|
||||
// 将菜单菜单活动item垂直居中
|
||||
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2
|
||||
|
||||
//获取任务详情
|
||||
this.getAssayTaskDetail(this.taskList[index].taskNo)
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query
|
||||
.select('.' + elClass)
|
||||
.fields({ size: true }, res => {
|
||||
// 如果节点尚未生成,res值为null,循环调用执行
|
||||
if (!res) {
|
||||
setTimeout(() => {
|
||||
this.getElRect(elClass)
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
this[dataVal] = res.height
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
getAssayTask() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
const param = {
|
||||
assayOper: this.userInfo.realname,
|
||||
operateType: 'task'
|
||||
}
|
||||
this.$u.api
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
this.taskList = res.result
|
||||
if (this.taskList && this.taskList.length > 0) {
|
||||
this.getAssayTaskDetail(this.taskList[0].taskNo)
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
getAssayTaskDetail(taskNo) {
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
|
||||
.then(res => {
|
||||
this.sampleList = res.result
|
||||
uni.hideLoading()
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
font-size: 46rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 250rpx);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 10rpx;
|
||||
height: 200rpx;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 4rpx;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 32rpx;
|
||||
left: 0;
|
||||
top: 39rpx;
|
||||
}
|
||||
</style>
|
||||
318
pages/analysis/sample/sample-print.vue
Normal file
318
pages/analysis/sample/sample-print.vue
Normal file
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="单据补打" @leftClick="customBack"></navbar-back>
|
||||
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<!-- 左侧任务列表 -->
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
ref="menuScrollView"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="{ 'u-tab-item-active': current === index }"
|
||||
@tap.stop="switchTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 18px">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 10px">
|
||||
<text>{{ task.taskName }} {{ task.assayOper }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 10px">
|
||||
<u-icon name="clock"></u-icon>
|
||||
<text style="margin-left: 5px">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
|
||||
<!-- 右侧样品列表 -->
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="padding: 5px; font-size: 16px">
|
||||
<u-row>
|
||||
<u-col span="2" style="text-align: center">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="5">
|
||||
<view>
|
||||
<text style="padding-left: 10px">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 10px">{{ sample.sampleName }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 10rpx" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="6"></u-col>
|
||||
<u-col span="3">
|
||||
<u-button class="btn-operation" type="primary" @click="previewPDF" v-if="currentTaskId">
|
||||
预览任务单
|
||||
</u-button>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u-button class="btn-operation" :disabled="taskList.length <= 0" type="success" @click="printTask">
|
||||
打印任务单
|
||||
</u-button>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import nx from '@/nx'
|
||||
|
||||
// refs
|
||||
const scrollTop = ref(0)
|
||||
const current = ref(0)
|
||||
const menuHeight = ref(0)
|
||||
const menuItemHeight = ref(0)
|
||||
const currentTask = ref({})
|
||||
const currentTaskId = ref('')
|
||||
const currentTaskNo = ref('')
|
||||
const conAssayTask = ref(null)
|
||||
const taskList = ref([])
|
||||
const sampleList = ref([])
|
||||
const menuScrollView = ref(null)
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
getAssayTask()
|
||||
})
|
||||
|
||||
checkPrintConfig()
|
||||
|
||||
// 方法
|
||||
function customBack() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/analysis/index/index'
|
||||
})
|
||||
}
|
||||
|
||||
function taskStyle(task) {
|
||||
return task.weightTaskStatus === 3 ? 'green' : ''
|
||||
}
|
||||
|
||||
// 切换任务
|
||||
async function switchTask(index) {
|
||||
if (index === current.value) return
|
||||
current.value = index
|
||||
|
||||
// 初始化高度
|
||||
if (menuHeight.value === 0 || menuItemHeight.value === 0) {
|
||||
await getElRect('menu-scroll-view', 'menuHeight')
|
||||
await getElRect('.u-tab-item', 'menuItemHeight')
|
||||
}
|
||||
|
||||
// 滚动居中
|
||||
scrollTop.value = index * menuItemHeight.value + menuItemHeight.value / 2 - menuHeight.value / 2
|
||||
|
||||
// 更新当前任务
|
||||
const task = taskList.value[index]
|
||||
currentTask.value = task
|
||||
currentTaskNo.value = task.taskNo
|
||||
currentTaskId.value = task.id
|
||||
getAssayTaskDetail(currentTaskNo.value)
|
||||
}
|
||||
|
||||
// 获取元素尺寸
|
||||
function getElRect(selector, targetRef) {
|
||||
return new Promise(resolve => {
|
||||
const query = uni.createSelectorQuery().in(getCurrentInstance().proxy)
|
||||
query
|
||||
.select(selector)
|
||||
.fields({ size: true }, res => {
|
||||
if (!res) {
|
||||
// 如果未获取到,稍后重试(可选)
|
||||
setTimeout(() => resolve(getElRect(selector, targetRef)), 10)
|
||||
return
|
||||
}
|
||||
if (targetRef === 'menuHeight') {
|
||||
menuHeight.value = res.height
|
||||
} else if (targetRef === 'menuItemHeight') {
|
||||
menuItemHeight.value = res.height
|
||||
}
|
||||
resolve(res)
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
function getAssayTask() {
|
||||
const param = {
|
||||
operateType: 'print',
|
||||
finishStatus: 'submited,finished'
|
||||
}
|
||||
|
||||
nx.$api.auncel.getAssayTaskList(param).then(res => {
|
||||
taskList.value = res || []
|
||||
if (taskList.value.length > 0) {
|
||||
const first = taskList.value[0]
|
||||
currentTask.value = first
|
||||
currentTaskNo.value = first.taskNo
|
||||
currentTaskId.value = first.id
|
||||
getAssayTaskDetail(currentTaskNo.value)
|
||||
} else {
|
||||
currentTask.value = {}
|
||||
currentTaskNo.value = ''
|
||||
currentTaskId.value = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取任务详情
|
||||
function getAssayTaskDetail(taskNo) {
|
||||
if (!taskNo) return
|
||||
nx.$api.assayTask.getAssayTaskDetailListByTaskNo({ taskNo }).then(res => {
|
||||
sampleList.value = res.result || []
|
||||
if (res.additionalProperties?.conAssayTask) {
|
||||
conAssayTask.value = res.additionalProperties.conAssayTask
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 打印任务
|
||||
function printTask() {
|
||||
const task = currentTask.value
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定补打“${task.taskNo}的原始记录单”?`,
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
nx.$print.getPrintTemplateAndPrint(currentTask.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 预览 PDF
|
||||
function previewPDF() {
|
||||
const url = `/pages/analysis/sample/pdf-preview?taskId=${currentTaskId.value}&reportKey=${conAssayTask.value?.assayTaskTemplateKey}&hideResultFlag=true`
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
// 检查打印服务配置(模拟 onShow)
|
||||
function checkPrintConfig() {
|
||||
const printList = uni.getStorageSync('KEY_PRINT_LIST')
|
||||
if (!printList || printList.length <= 0) {
|
||||
uni.showToast({
|
||||
title: '打印服务未配置,请在系统设置中配置打印服务!',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 5px;
|
||||
height: 100px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
line-height: 1;
|
||||
border-width: 2px;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
width: 95%;
|
||||
}
|
||||
</style>
|
||||
339
pages/analysis/sample/sample-receive.vue
Normal file
339
pages/analysis/sample/sample-receive.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="样品分析-收样" @leftClick="customBack"></navbar-back>
|
||||
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<!-- 任务列表 -->
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current === index ? 'u-tab-item-active' : '']"
|
||||
@tap.stop="switchTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view class="fs18">{{ task.taskNo }}</view>
|
||||
<view style="margin-top: 10px">{{ task.taskName }}</view>
|
||||
<view class="x-f" style="margin-top: 10px">
|
||||
<u-icon name="clock"></u-icon>
|
||||
<text style="margin-left: 5px">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
|
||||
<!-- 样品列表 -->
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="padding: 5px; font-size: 16px">
|
||||
<u-row>
|
||||
<u-col span="3" style="text-align: center">
|
||||
<u-row>
|
||||
<u-col span="6" style="text-align: center">
|
||||
<u-checkbox
|
||||
v-model="sample.checked"
|
||||
v-if="sample.sampleProcessNo === currentNode"
|
||||
@change="selectSample(sample)"
|
||||
></u-checkbox>
|
||||
</u-col>
|
||||
<u-col span="6" style="text-align: center">
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</u-col>
|
||||
<u-col span="9" class="sample_desc">
|
||||
<view>
|
||||
<view
|
||||
><text style="padding-left: 10px">{{ sample.sampleCode }}</text></view
|
||||
>
|
||||
<view>
|
||||
<text style="padding-left: 10px">
|
||||
{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sample_desc_warn" v-if="sample.sampleProcessNo !== currentNode">
|
||||
当前节点:{{ getProcessNameShow(sample.sampleProcessNo) }}
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 5px" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="8"></u-col>
|
||||
<u-col span="4">
|
||||
<u-button class="btn-operation" :disabled="taskList.length <= 0" type="success" @click="confirmReceipt">
|
||||
确认收样
|
||||
</u-button>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
|
||||
// 响应式数据
|
||||
const currentNode = ref('F30')
|
||||
const scrollTop = ref(0)
|
||||
const current = ref(0)
|
||||
const taskList = ref([])
|
||||
const sampleList = ref([])
|
||||
const dicSampleProcessCodeList = ref([])
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const customBack = () => {
|
||||
uni.reLaunch({ url: '/pages/analysis/index/index' })
|
||||
}
|
||||
|
||||
const selectSample = sample => {
|
||||
sample.checked = !sample.checked
|
||||
}
|
||||
|
||||
const taskStyle = task => {
|
||||
if ((task.weightTaskStatus === 0 || task.weightTaskStatus === 1) && task.reviewCount > 0) {
|
||||
return 'red'
|
||||
}
|
||||
if (task.weightTaskStatus === 2 && task.reviewCount > 0) {
|
||||
return 'green'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const confirmReceipt = () => {
|
||||
const errNodeList = sampleList.value.filter(item => item.sampleProcessNo !== currentNode.value)
|
||||
if (errNodeList.length > 0) {
|
||||
uni.showToast({
|
||||
title: '存在异常节点,联系管理员处理!',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const checkedSampleList = sampleList.value.filter(item => item.checked)
|
||||
if (checkedSampleList.length !== sampleList.value.length) {
|
||||
uni.showToast({ title: '样品未全部勾选,请检查!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认收样?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.cancel) return
|
||||
|
||||
const sampleIdList = checkedSampleList.map(item => item.busSubCsampleId)
|
||||
const data = {
|
||||
busAssayTaskId: taskList.value[current.value]?.id,
|
||||
sampleSourceType: 2,
|
||||
sampleProcessNo: currentNode.value,
|
||||
isGenSampleHandover: false,
|
||||
sampleIdList
|
||||
}
|
||||
|
||||
nx.$api.assayTask
|
||||
.execReceiveSample(data)
|
||||
.then(() => {
|
||||
getAssayTask()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('收样失败:', err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const switchTask = index => {
|
||||
if (index === current.value) return
|
||||
current.value = index
|
||||
|
||||
const task = taskList.value[index]
|
||||
if (!task) return
|
||||
|
||||
getAssayTaskDetail(task.taskNo)
|
||||
}
|
||||
|
||||
const getAssayTask = () => {
|
||||
const param = {
|
||||
finishStatus: 'waiting_receive',
|
||||
assayOper: userInfo.value.nickname
|
||||
}
|
||||
|
||||
nx.$api.auncel
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
taskList.value = res || []
|
||||
if (taskList.value.length > 0) {
|
||||
current.value = 0
|
||||
getAssayTaskDetail(taskList.value[0].taskNo)
|
||||
} else {
|
||||
sampleList.value = []
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('获取任务列表失败:', err)
|
||||
sampleList.value = []
|
||||
})
|
||||
}
|
||||
|
||||
const getAssayTaskDetail = taskNo => {
|
||||
const param = {
|
||||
taskNo,
|
||||
waiting_receive: '1'
|
||||
}
|
||||
|
||||
nx.$api.assayTask
|
||||
.getAssayTaskDetailListByTaskNo(param)
|
||||
.then(res => {
|
||||
sampleList.value = (res.result || []).map(item => ({ ...item, checked: false }))
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('获取任务详情失败:', err)
|
||||
sampleList.value = []
|
||||
})
|
||||
}
|
||||
|
||||
const getDicSampleProcessCodeList = () => {
|
||||
nx.$api.assayTask.queryQmsDicSampleProcessCodeList().then(res => {
|
||||
dicSampleProcessCodeList.value = res.records || []
|
||||
})
|
||||
}
|
||||
|
||||
const getProcessNameShow = val => {
|
||||
const item = dicSampleProcessCodeList.value.find(i => i.processCode === val)
|
||||
return item ? item.processName : val
|
||||
}
|
||||
|
||||
const getDataSourceTypeShow = val => {
|
||||
if (val === 2) return '【筛上】'
|
||||
if (val === 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onLoad(() => {
|
||||
getDicSampleProcessCodeList()
|
||||
getAssayTask()
|
||||
})
|
||||
|
||||
onBackPress(() => {
|
||||
customBack()
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 5px;
|
||||
height: 100px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
line-height: 1;
|
||||
border-bottom: 2px dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
height: 50px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sample_desc {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between !important;
|
||||
padding-right: 15px !important;
|
||||
}
|
||||
|
||||
.sample_desc_warn {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
299
pages/analysis/sample/sample-report-search.vue
Normal file
299
pages/analysis/sample/sample-report-search.vue
Normal file
@@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="审核查询" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current.value === index ? 'u-tab-item-active' : '']"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 18px">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 10px">
|
||||
<text>{{ task.taskName }} {{ task.assayOper }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 10px">
|
||||
<u-icon color="" name="clock"></u-icon>
|
||||
<text style="margin-left: 5px">{{ task.reportTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="padding: 5px; font-size: 16px">
|
||||
<u-row
|
||||
@click="showSampleDetail(sample.id, index)"
|
||||
:class="selectedIndex.value === index ? 'selected_Sample' : ''"
|
||||
>
|
||||
<u-col span="2" style="text-align: center" :style="sampleStyle(sample)">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="5">
|
||||
<view>
|
||||
<text style="padding-left: 10px">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 10px">
|
||||
{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }}
|
||||
</text>
|
||||
</view>
|
||||
<view v-if="sample.sampleWeight">
|
||||
<text style="padding-left: 10px">{{ sample.sampleWeight }} g</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="5">
|
||||
<view>
|
||||
<text style="padding-left: 10px">{{ sample.remarks }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 5px" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="3"></u-col>
|
||||
<u-col span="6">
|
||||
<u-button class="btn-operation" type="primary" @click="previewPDF" v-if="currentTaskId.value">
|
||||
任务单预览
|
||||
</u-button>
|
||||
</u-col>
|
||||
<u-col span="3"> </u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<sample-detail-popup
|
||||
ref="sampleDetailPopup"
|
||||
:showPopup="showDetailPopup"
|
||||
:detailPopupParam="detailPopupParam"
|
||||
></sample-detail-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
import SampleDetailPopup from '@/components/sample/sample-detail-popup.vue'
|
||||
import nx from '@/nx'
|
||||
|
||||
// 响应式数据
|
||||
const scrollTop = ref(0)
|
||||
const current = ref(0)
|
||||
const currentTask = ref({})
|
||||
const currentTaskId = ref('')
|
||||
const currentTaskNo = ref('')
|
||||
const conAssayTask = ref('')
|
||||
const reviewNum = ref(0)
|
||||
const selectedIndex = ref(-1)
|
||||
const taskList = ref([])
|
||||
const sampleList = ref([])
|
||||
const showDetailPopup = ref(false)
|
||||
const detailPopupParam = ref({ taskDetailId: '' })
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const customBack = () => {
|
||||
uni.reLaunch({ url: '/pages/analysis/index/index' })
|
||||
}
|
||||
|
||||
const taskStyle = task => {
|
||||
if ((task.weightTaskStatus === 0 || task.weightTaskStatus === 1) && task.reviewCount > 0) return 'red'
|
||||
if (task.weightTaskStatus === 2 && task.reviewCount > 0) return 'green'
|
||||
return ''
|
||||
}
|
||||
|
||||
const sampleStyle = sample => {
|
||||
if ((sample.weightSubmitStatus === 0 || sample.weightSubmitStatus === 1) && sample.reviewCount > 0) {
|
||||
return 'color: red'
|
||||
}
|
||||
if (sample.weightSubmitStatus === 2 && sample.reviewCount > 0) {
|
||||
return 'color: green'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const swichTask = async index => {
|
||||
if (index === current.value) return
|
||||
current.value = index
|
||||
selectedIndex.value = -1
|
||||
const task = taskList.value[index]
|
||||
currentTask.value = task
|
||||
currentTaskNo.value = task.taskNo
|
||||
currentTaskId.value = task.id
|
||||
getAssayTaskDetail(task.taskNo)
|
||||
}
|
||||
|
||||
const showSampleDetail = (detailId, index) => {
|
||||
selectedIndex.value = index
|
||||
console.log('detailId', detailId)
|
||||
detailPopupParam.value = { taskDetailId: detailId }
|
||||
showDetailPopup.value = true
|
||||
}
|
||||
|
||||
const getAssayTask = () => {
|
||||
taskList.value = []
|
||||
sampleList.value = []
|
||||
const param = {
|
||||
finishStatus: 'finished',
|
||||
wfStatus: 'running',
|
||||
assayOper: userInfo.value.realname
|
||||
}
|
||||
nx.$api.auncel.getAssayTaskList(param).then(res => {
|
||||
taskList.value = res
|
||||
if (taskList.value.length > 0) {
|
||||
current.value = 0
|
||||
currentTask.value = taskList.value[0]
|
||||
currentTaskNo.value = taskList.value[0].taskNo
|
||||
currentTaskId.value = taskList.value[0].id
|
||||
getAssayTaskDetail(currentTaskNo.value)
|
||||
} else {
|
||||
current.value = 0
|
||||
currentTask.value = {}
|
||||
currentTaskNo.value = ''
|
||||
currentTaskId.value = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getAssayTaskDetail = taskNo => {
|
||||
reviewNum.value = 0
|
||||
nx.$api.assayTaskTemplateKey
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo })
|
||||
.then(res => {
|
||||
sampleList.value = res.result || []
|
||||
if (res.additionalProperties?.conAssayTask) {
|
||||
conAssayTask.value = res.additionalProperties.conAssayTask
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const previewPDF = () => {
|
||||
const url = `/pages/analysis/sample/pdf-preview?taskId=${currentTaskId.value}&reportKey=${conAssayTask.value.assayTaskTemplateKey}`
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const getDataSourceTypeShow = val => {
|
||||
if (val === 2) return '【筛上】'
|
||||
if (val === 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
getAssayTask()
|
||||
uni.$on('sample-detail-popup_close', () => {
|
||||
showDetailPopup.value = false
|
||||
})
|
||||
})
|
||||
|
||||
onBackPress(() => {
|
||||
customBack()
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式部分保持不变 */
|
||||
.content-title {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.u-tab-item {
|
||||
padding: 5px;
|
||||
height: 100px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
line-height: 1;
|
||||
border-width: 2px;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
top: 20px;
|
||||
}
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.btn-operation {
|
||||
width: 95%;
|
||||
}
|
||||
.selected_Sample {
|
||||
background-color: #d7e9fa;
|
||||
}
|
||||
</style>
|
||||
413
pages/analysis/sample/sample-report.vue
Normal file
413
pages/analysis/sample/sample-report.vue
Normal file
@@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="数据上报" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="current === index ? 'u-tab-item-active' : ''"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 18px">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 10px">
|
||||
<text>{{ task.taskName }} {{ task.assayOper }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 10px">
|
||||
<u-icon color="" name="clock"></u-icon>
|
||||
<text style="margin-left: 5px">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="padding: 5px; font-size: 16px">
|
||||
<u-row
|
||||
@click="showSampleDetail(sample.id, index)"
|
||||
:class="selectedIndex === index ? 'selected_Sample' : ''"
|
||||
>
|
||||
<u-col span="2" style="text-align: center" :style="sampleStyle(sample)">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="5">
|
||||
<view>
|
||||
<text style="padding-left: 10px">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 10px"
|
||||
>{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }}</text
|
||||
>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="5">
|
||||
<view class="sample_desc_warn" v-if="sample.sampleProcessNo !== currentNode">
|
||||
当前节点:{{ getProcessNameShow(sample.sampleProcessNo) }}
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 5px" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="3"></u-col>
|
||||
<u-col span="3">
|
||||
<u-button class="btn-operation" type="primary" @click="previewPDF" v-if="currentTaskId">
|
||||
任务单预览
|
||||
</u-button>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u-button
|
||||
class="btn-operation"
|
||||
v-if="currentTaskId"
|
||||
type="warning"
|
||||
:disabled="taskReviewDisabled"
|
||||
@click="taskReview"
|
||||
>
|
||||
撤回任务单
|
||||
</u-button>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u-button class="btn-operation" :disabled="dataReportDisabled" type="success" @click="dataReport">
|
||||
数据上报
|
||||
</u-button>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<!-- 样品详情 -->
|
||||
<sample-detail-popup
|
||||
ref="sampleDetailPopup"
|
||||
:showPopup="showDetailPopup"
|
||||
:detailPopupParam="detailPopupParam"
|
||||
></sample-detail-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
import SampleDetailPopup from '@/components/sample/sample-detail-popup.vue'
|
||||
import nx from '@/nx'
|
||||
|
||||
// 响应式数据
|
||||
const scrollTop = ref(0)
|
||||
const current = ref(0)
|
||||
const currentTask = ref({})
|
||||
const currentTaskId = ref('')
|
||||
const currentTaskNo = ref('')
|
||||
const conAssayTask = ref('')
|
||||
const reviewNum = ref(0)
|
||||
const selectedIndex = ref(-1)
|
||||
const taskList = ref([])
|
||||
const sampleList = ref([])
|
||||
const showDetailPopup = ref(false)
|
||||
const detailPopupParam = ref({ taskDetailId: '' })
|
||||
const currentNode = ref('F31')
|
||||
const dicSampleProcessCodeList = ref([])
|
||||
|
||||
// 计算属性
|
||||
const taskReviewDisabled = computed(() => currentTask.value.finishStatus !== 'finished')
|
||||
const dataReportDisabled = computed(() => currentTask.value.finishStatus !== 'finished')
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const customBack = () => {
|
||||
uni.reLaunch({ url: '/pages/analysis/index/index' })
|
||||
}
|
||||
|
||||
const taskStyle = task => {
|
||||
if ((task.weightTaskStatus === 0 || task.weightTaskStatus === 1) && task.reviewCount > 0) return 'red'
|
||||
if (task.weightTaskStatus === 2 && task.reviewCount > 0) return 'green'
|
||||
return ''
|
||||
}
|
||||
|
||||
const sampleStyle = sample => {
|
||||
if ((sample.weightSubmitStatus === 0 || sample.weightSubmitStatus === 1) && sample.reviewCount > 0) {
|
||||
return 'color: red'
|
||||
}
|
||||
if (sample.weightSubmitStatus === 2 && sample.reviewCount > 0) {
|
||||
return 'color: green'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const swichTask = async index => {
|
||||
if (index === current.value) return
|
||||
current.value = index
|
||||
const task = taskList.value[index]
|
||||
currentTask.value = task
|
||||
currentTaskNo.value = task.taskNo
|
||||
currentTaskId.value = task.id
|
||||
getAssayTaskDetail(task.taskNo)
|
||||
}
|
||||
|
||||
const showSampleDetail = (detailId, index) => {
|
||||
selectedIndex.value = index
|
||||
detailPopupParam.value = { taskDetailId: detailId }
|
||||
showDetailPopup.value = true
|
||||
}
|
||||
|
||||
const getAssayTask = () => {
|
||||
taskList.value = []
|
||||
sampleList.value = []
|
||||
const param = {
|
||||
finishStatus: 'finished',
|
||||
wfStatus: '0,revoke',
|
||||
assayOper: userInfo.value.nickname
|
||||
}
|
||||
nx.$api.auncel
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
taskList.value = res
|
||||
if (taskList.value.length > 0) {
|
||||
current.value = 0
|
||||
currentTask.value = taskList.value[0]
|
||||
currentTaskNo.value = taskList.value[0].taskNo
|
||||
currentTaskId.value = taskList.value[0].id
|
||||
getAssayTaskDetail(currentTaskNo.value)
|
||||
} else {
|
||||
current.value = 0
|
||||
currentTask.value = {}
|
||||
currentTaskNo.value = ''
|
||||
currentTaskId.value = ''
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const getAssayTaskDetail = taskNo => {
|
||||
reviewNum.value = 0
|
||||
$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo })
|
||||
.then(res => {
|
||||
sampleList.value = res.result || []
|
||||
if (res.additionalProperties?.conAssayTask) {
|
||||
conAssayTask.value = res.additionalProperties.conAssayTask
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const previewPDF = () => {
|
||||
const url = `/pages/analysis/sample/pdf-preview?taskId=${currentTaskId.value}&reportKey=${conAssayTask.value.assayTaskTemplateKey}&hideResultFlag=true`
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const taskReview = () => {
|
||||
const errNodeList = sampleList.value.filter(item => item.sampleProcessNo !== currentNode.value)
|
||||
if (errNodeList.length > 0) {
|
||||
uni.showToast({ title: '存在异常节点,联系管理员处理!', icon: 'none', duration: 3000 })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '撤回当前任务指派单,是否继续?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
nx.$api.assayTask.rollbackAssayTask(currentTaskId.value).then(() => {
|
||||
currentTaskId.value = ''
|
||||
currentTask.value = {}
|
||||
uni.showToast({ title: '撤回成功!' })
|
||||
uni.navigateTo({ url: '/pages/analysis/sample/sample-work-list' })
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dataReport = () => {
|
||||
const errNodeList = sampleList.value.filter(item => item.sampleProcessNo !== currentNode.value)
|
||||
if (errNodeList.length > 0) {
|
||||
uni.showToast({ title: '存在异常节点,联系管理员处理!', icon: 'none', duration: 3000 })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定上报数据?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
nx.$api.assayTask.reportAssayTask(currentTaskId.value).then(res => {
|
||||
currentTaskId.value = ''
|
||||
currentTask.value = {}
|
||||
if (res.additionalProperties?.conAssayTask?.isPrint === 1) {
|
||||
printTask()
|
||||
}
|
||||
getAssayTask()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getDicSampleProcessCodeList = () => {
|
||||
nx.$api.assayTask.queryQmsDicSampleProcessCodeList().then(res => {
|
||||
dicSampleProcessCodeList.value = res.records
|
||||
})
|
||||
}
|
||||
|
||||
const getProcessNameShow = val => {
|
||||
const item = dicSampleProcessCodeList.value.find(i => i.processCode === val)
|
||||
return item ? item.processName : val
|
||||
}
|
||||
|
||||
const getDataSourceTypeShow = val => {
|
||||
if (val === 2) return '【筛上】'
|
||||
if (val === 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
|
||||
const printTask = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '数据上报成功!是否打印“原始记录单”?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
nx.$print.getPrintTemplateAndPrint(currentTask.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
getDicSampleProcessCodeList()
|
||||
getAssayTask()
|
||||
|
||||
// 监听 popup 关闭
|
||||
uni.$on('sample-detail-popup_close', () => {
|
||||
showDetailPopup.value = false
|
||||
})
|
||||
})
|
||||
|
||||
onBackPress(() => {
|
||||
customBack()
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式保持不变 */
|
||||
.content-title {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 5px;
|
||||
height: 100px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
line-height: 1;
|
||||
border-width: 2px;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.selected_Sample {
|
||||
background-color: #d7e9fa;
|
||||
}
|
||||
.sample_desc_warn {
|
||||
color: red;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
423
pages/analysis/sample/sample-review.vue
Normal file
423
pages/analysis/sample/sample-review.vue
Normal file
@@ -0,0 +1,423 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="样重复核" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']"
|
||||
:data-current="index"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 42rpx">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 20rpx">
|
||||
<text>{{ task.taskName }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 20rpx">
|
||||
<u-icon name="clock"></u-icon>
|
||||
<text style="margin-left: 10rpx">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<u-checkbox-group @change="checkboxGroupChange" style="width: 100%">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="width: 100%; padding: 10rpx; font-size: 36rpx">
|
||||
<u-row>
|
||||
<u-col span="2" style="text-align: center">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx"
|
||||
>{{ sample.dataSourceType | getDataSourceTypeShow }}{{ sample.sampleName }}</text
|
||||
>
|
||||
</view>
|
||||
<view>
|
||||
<text v-if="sample.sampleWeight" style="padding-left: 20rpx">{{ sample.sampleWeight }} g</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.cupNum }}号杯</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.remarks }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.measureTime | getWeightTimeShow }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="2">
|
||||
<view>
|
||||
<u-checkbox
|
||||
@change="checkboxChange"
|
||||
v-model="sample.checked"
|
||||
:label="sample.id"
|
||||
:name="sample.id"
|
||||
>
|
||||
</u-checkbox>
|
||||
<!-- <block v-if="sample.reviewCount > 0 && sample.weightSubmitStatus == 0">
|
||||
<u-button
|
||||
type="warning"
|
||||
@click="recovery(sample.id)"
|
||||
>恢复</u-button>
|
||||
</block>
|
||||
<block v-else>
|
||||
<u-button
|
||||
type="default"
|
||||
@click="review(sample.id)"
|
||||
>复核</u-button>
|
||||
</block> -->
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 10rpx" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</u-checkbox-group>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="4"><!-- <u-button class="btn-operation" type="primary">原始记录单打印</u-button> --></u-col>
|
||||
<u-col span="4"
|
||||
><!-- <u-button class="btn-operation" :disabled="reviewNum > 0" type="success" >数据上报</u-button> --></u-col
|
||||
>
|
||||
<u-col span="4">
|
||||
<!-- <u-button class="btn-operation" :disabled="reviewNum == 0" type="warning" @click="taskReview">任务单退回复核</u-button> -->
|
||||
<u-button class="btn-operation" :disabled="reviewList.length <= 0" type="warning" @click="taskReviewAll"
|
||||
>任务单退回复核</u-button
|
||||
>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
currentTaskId: '', //当前选择的任务单id
|
||||
currentTaskNo: '', //当前选中的任务编号
|
||||
reviewNum: 0, //复核数
|
||||
reviewList: [],
|
||||
taskList: [],
|
||||
sampleList: []
|
||||
}
|
||||
},
|
||||
onLoad(param) {
|
||||
if (param.currentTaskNo) {
|
||||
this.currentTaskNo = param.currentTaskNo
|
||||
}
|
||||
//获取任务列表
|
||||
this.getAssayTask()
|
||||
},
|
||||
methods: {
|
||||
//返回上一页
|
||||
customBack() {
|
||||
uni.redirectTo({
|
||||
url: '/pages/analysis/sample/sample-report'
|
||||
})
|
||||
},
|
||||
//开始秤样
|
||||
startWeighSample() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/auncel/auncel-weigh'
|
||||
})
|
||||
},
|
||||
//切换任务
|
||||
async swichTask(index) {
|
||||
if (index == this.current) return
|
||||
this.current = index
|
||||
// 如果为0,意味着尚未初始化
|
||||
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
await this.getElRect('menu-scroll-view', 'menuHeight')
|
||||
await this.getElRect('u-tab-item', 'menuItemHeight')
|
||||
}
|
||||
// 将菜单菜单活动item垂直居中
|
||||
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2
|
||||
|
||||
//获取任务详情
|
||||
this.currentTaskNo = this.taskList[index].taskNo
|
||||
this.currentTaskId = this.taskList[index].id
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query
|
||||
.select('.' + elClass)
|
||||
.fields({ size: true }, res => {
|
||||
// 如果节点尚未生成,res值为null,循环调用执行
|
||||
if (!res) {
|
||||
setTimeout(() => {
|
||||
this.getElRect(elClass)
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
this[dataVal] = res.height
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
getAssayTask() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
// this.$u.api
|
||||
// .getAssayTaskListBy({taskStatus:2})
|
||||
const param = {
|
||||
operateType: 'review',
|
||||
taskNo: this.currentTaskNo
|
||||
}
|
||||
this.$u.api
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
this.taskList = res.result
|
||||
this.currentTaskNo = this.taskList[0].taskNo
|
||||
this.currentTaskId = this.taskList[0].id
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
uni.hideLoading()
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
getAssayTaskDetail(taskNo) {
|
||||
this.reviewNum = 0
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
|
||||
.then(res => {
|
||||
this.sampleList = res.result
|
||||
this.showLoading = false
|
||||
|
||||
//统计需要复核的数量
|
||||
this.sampleList.forEach(item => {
|
||||
if (item.reviewCount > 0 && item.weightSubmitStatus == 0) {
|
||||
item.checked = true
|
||||
this.reviewNum += 1
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
review(id) {
|
||||
this.$u.api
|
||||
.assayTaskDetailReview(id)
|
||||
.then(res => {
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
this.showLoading = false
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
recovery(id) {
|
||||
this.$u.api
|
||||
.assayTaskDetailRecovery(id)
|
||||
.then(res => {
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
this.showLoading = false
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
//任务单退回复核
|
||||
taskReview() {
|
||||
this.$u.api
|
||||
.assayTaskReview(this.currentTaskId)
|
||||
.then(res => {
|
||||
this.showLoading = false
|
||||
|
||||
uni.redirectTo({
|
||||
url: '/pages/sample/sample-report'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
//任务单及样品退回复核
|
||||
taskReviewAll() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定复核当前选中的样品?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.cancel) {
|
||||
console.log('用户点击取消')
|
||||
return
|
||||
}
|
||||
console.log('确定。。。')
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '退回中...'
|
||||
})
|
||||
this.$u.api
|
||||
.reviewWeightTaskByTaskIdAndSampleIds(this.currentTaskId, this.reviewList.join(','))
|
||||
.then(res => {
|
||||
uni.hideLoading()
|
||||
uni.redirectTo({
|
||||
url: '/pages/sample/sample-report'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
checkboxGroupChange(v) {
|
||||
this.reviewList = v
|
||||
this.reviewNum = v.length
|
||||
//console.log('group change', v);
|
||||
},
|
||||
checkboxChange(e) {
|
||||
/* if(e.value) {
|
||||
this.review(e.name);
|
||||
} else {
|
||||
this.recovery(e.name);
|
||||
} */
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
getWeightTimeShow(time) {
|
||||
if (time == null) return ''
|
||||
return time.split(' ')[1]
|
||||
},
|
||||
getDataSourceTypeShow(val) {
|
||||
if (val == 2) return '【筛上】'
|
||||
if (val == 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
font-size: 46rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 250rpx);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 10rpx;
|
||||
height: 200rpx;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 4rpx;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 32rpx;
|
||||
left: 0;
|
||||
top: 39rpx;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 410rpx);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 160rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
height: 100rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
</style>
|
||||
425
pages/analysis/sample/sample-send.vue
Normal file
425
pages/analysis/sample/sample-send.vue
Normal file
@@ -0,0 +1,425 @@
|
||||
<!--送样-->
|
||||
<template>
|
||||
<view class="page">
|
||||
<navbar-back :autoBack="false" title="样品分析-送样" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']"
|
||||
:data-current="index"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 42rpx">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 20rpx">
|
||||
<text>{{ task.taskName }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 20rpx">
|
||||
<u-icon color="" name="clock"></u-icon>
|
||||
<text style="margin-left: 10rpx">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view style="padding: 10rpx; font-size: 36rpx">
|
||||
<u-row>
|
||||
<u-col span="3" style="text-align: center">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
<!-- <u-row>-->
|
||||
<!-- <u-col span="6" style="text-align: center;">-->
|
||||
<!-- <u-checkbox v-model="sample.checked" v-if="sample.sampleProcessNo == 'F39'" @change="selectSample(sample)"></u-checkbox>-->
|
||||
<!-- </u-col>-->
|
||||
<!-- <u-col span="6" style="text-align: center;">-->
|
||||
<!-- -->
|
||||
<!-- </u-col>-->
|
||||
<!-- </u-row>-->
|
||||
</u-col>
|
||||
<u-col span="9" class="sample_desc">
|
||||
<view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx"
|
||||
>{{ sample.dataSourceType | getDataSourceTypeShow }}{{ sample.sampleName }}</text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sample_desc_warn" v-if="sample.sampleProcessNo != 'F39'">
|
||||
状态异常:{{ sample.sampleProcessNo }}
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 10rpx" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="4"></u-col>
|
||||
<u-col span="4"></u-col>
|
||||
<u-col span="4">
|
||||
<u-button
|
||||
class="btn-operation"
|
||||
:disabled="this.taskList.length <= 0"
|
||||
type="success"
|
||||
@click="confirmReceipt"
|
||||
>确认送样</u-button
|
||||
>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
currentTask: '', //当前选中任务
|
||||
currentTaskNo: '', //当前选中的任务编号
|
||||
currentTaskType: '', //当前任务类型
|
||||
taskList: [],
|
||||
sampleList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
//获取任务列表
|
||||
this.getAssayTask()
|
||||
},
|
||||
methods: {
|
||||
//返回首页
|
||||
customBack() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/analysis/index/index'
|
||||
})
|
||||
},
|
||||
selectSample(sample) {
|
||||
if (sample.checked) sample.checked = false
|
||||
else sample.checked = true
|
||||
},
|
||||
taskStyle(task) {
|
||||
if (task.weightTaskStatus == 0 && task.reviewCount > 0) {
|
||||
return 'red'
|
||||
}
|
||||
if (task.weightTaskStatus == 1 && task.reviewCount > 0) {
|
||||
return 'red'
|
||||
}
|
||||
if (task.weightTaskStatus == 2 && task.reviewCount > 0) {
|
||||
return 'green'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
sampleStyle(sample) {
|
||||
//console.log(sample);
|
||||
if (sample.weightSubmitStatus == 0 && sample.reviewCount > 0) {
|
||||
return 'color: red'
|
||||
}
|
||||
if (sample.weightSubmitStatus == 1 && sample.reviewCount > 0) {
|
||||
//return 'color: #e0861a';
|
||||
return 'color: green'
|
||||
}
|
||||
if (sample.weightSubmitStatus == 2 && sample.reviewCount > 0) {
|
||||
return 'color: green'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
//确认送样
|
||||
confirmReceipt() {
|
||||
//检查是否选中所有
|
||||
// let checkedSampleList = this.sampleList.filter(item => item.checked);
|
||||
// if (checkedSampleList.length != this.sampleList.length) {
|
||||
// this.$helper.showToast({
|
||||
// title: '样品未全部勾选,请检查!'
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
//检查是否所有样品都在F39
|
||||
const checkList = this.sampleList.filter(item => item.sampleProcessNo != 'F39')
|
||||
if (checkList.length > 0) {
|
||||
this.$helper.showToast({
|
||||
title: '部分样品状态异常,请联系技术支持人员处理!'
|
||||
})
|
||||
return
|
||||
}
|
||||
const checkedSampleList = this.sampleList
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认送样?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.cancel) {
|
||||
return
|
||||
}
|
||||
const sampleIdList = checkedSampleList.map(item => item.busSubCsampleId)
|
||||
const data = {
|
||||
busAssayTaskId: this.currentTask.id,
|
||||
sampleSourceType: 2,
|
||||
sampleProcessNo: 'F39',
|
||||
isGenSampleHandover: false,
|
||||
sampleIdList: sampleIdList
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '正在提交...'
|
||||
})
|
||||
this.$u.api
|
||||
.execSendSample(data)
|
||||
.then(res => {
|
||||
let msg = '发生错误,请稍后再试!'
|
||||
if (!res.success) {
|
||||
if (res.message) {
|
||||
msg = res.message
|
||||
}
|
||||
this.$helper.showToast({
|
||||
title: msg
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$helper.showToast({
|
||||
title: '操作成功!'
|
||||
})
|
||||
this.getAssayTask()
|
||||
})
|
||||
.catch(err => {
|
||||
this.$helper.showToast({
|
||||
title: '提交失败!'
|
||||
})
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
//切换任务
|
||||
async swichTask(index) {
|
||||
if (index == this.current) return
|
||||
this.current = index
|
||||
// 如果为0,意味着尚未初始化
|
||||
// if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
// await this.getElRect('menu-scroll-view', 'menuHeight');
|
||||
// await this.getElRect('u-tab-item', 'menuItemHeight');
|
||||
// }
|
||||
// // 将菜单菜单活动item垂直居中
|
||||
// this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
|
||||
|
||||
//获取任务详情
|
||||
this.currentTask = this.taskList[index]
|
||||
this.currentTaskNo = this.taskList[index].taskNo
|
||||
this.currentTaskType = this.taskList[index].taskType
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query
|
||||
.select('.' + elClass)
|
||||
.fields({ size: true }, res => {
|
||||
// 如果节点尚未生成,res值为null,循环调用执行
|
||||
if (!res) {
|
||||
setTimeout(() => {
|
||||
this.getElRect(elClass)
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
this[dataVal] = res.height
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
getAssayTask() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
const param = {
|
||||
operateType: 'send',
|
||||
assayOper: this.userInfo.realname
|
||||
}
|
||||
this.$u.api
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
this.taskList = res.result
|
||||
if (!this.taskList || this.taskList.length == 0) {
|
||||
this.sampleList = []
|
||||
return
|
||||
}
|
||||
if (this.taskList && this.taskList.length > 0) {
|
||||
this.current = 0
|
||||
this.currentTask = this.taskList[0]
|
||||
this.currentTaskNo = this.taskList[0].taskNo
|
||||
this.currentTaskType = this.taskList[0].taskType
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
},
|
||||
getAssayTaskDetail(taskNo) {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
const param = {
|
||||
taskNo: taskNo,
|
||||
waiting_receive: '1' //标记是收样
|
||||
}
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo(param)
|
||||
.then(res => {
|
||||
const list = res.result
|
||||
list.forEach(item => {
|
||||
item.checked = false
|
||||
})
|
||||
this.sampleList = list
|
||||
this.showLoading = false
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
getDataSourceTypeShow(val) {
|
||||
if (val == 2) return '【筛上】'
|
||||
if (val == 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
font-size: 46rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 250rpx);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 10rpx;
|
||||
height: 200rpx;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 4rpx;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 32rpx;
|
||||
left: 0;
|
||||
top: 39rpx;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 410rpx);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 160rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
height: 100rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.sample_desc {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: nowrap; /* 禁止换行 */
|
||||
justify-content: space-between !important;
|
||||
padding-right: 15px !important;
|
||||
}
|
||||
|
||||
.sample_desc_warn {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
319
pages/analysis/sample/sample-weigh.vue
Normal file
319
pages/analysis/sample/sample-weigh.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<navbar-back :autoBack="false" title="样品称重" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']"
|
||||
:data-current="index"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="font-size: 42rpx">{{ task.taskNo }}</text>
|
||||
</view>
|
||||
<view style="margin-top: 20rpx">
|
||||
<text>{{ task.taskName }}</text>
|
||||
</view>
|
||||
<view class="x-f" style="margin-top: 20rpx">
|
||||
<u-icon name="clock"></u-icon>
|
||||
<text style="margin-left: 10rpx">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view v-if="currentTask.reviewCount == sample.reviewCount" style="padding: 10rpx; font-size: 36rpx">
|
||||
<u-row>
|
||||
<u-col span="2" style="text-align: center">
|
||||
<view>
|
||||
<text>【{{ sample.sort }}】</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view>
|
||||
<text style="padding-left: 20rpx">{{ sample.sampleCode }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text style="padding-left: 20rpx"
|
||||
>{{ sample.dataSourceType | getDataSourceTypeShow }}{{ sample.sampleName }}</text
|
||||
>
|
||||
</view>
|
||||
<block v-if="sample.sampleWeight && sample.weightSubmitStatus != 0">
|
||||
<view>
|
||||
<text v-if="sample.sampleWeight" style="padding-left: 20rpx">{{ sample.sampleWeight }} g</text>
|
||||
</view>
|
||||
</block>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 10rpx" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="4"></u-col>
|
||||
<u-col span="4"></u-col>
|
||||
<u-col span="4">
|
||||
<u-button
|
||||
class="btn-operation"
|
||||
:disabled="this.taskList.length <= 0"
|
||||
type="success"
|
||||
@click="startWeighSample"
|
||||
>开始秤样</u-button
|
||||
>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
currentTask: '', //当前选中任务
|
||||
currentTaskNo: '', //当前选中的任务编号
|
||||
currentTaskType: '', //当前任务类型
|
||||
taskList: [],
|
||||
sampleList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
//获取任务列表
|
||||
this.getAssayTask()
|
||||
},
|
||||
methods: {
|
||||
//返回首页
|
||||
customBack() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/analysis/index/index'
|
||||
})
|
||||
},
|
||||
taskStyle(task) {
|
||||
if (task.weightTaskStatus == 0 && task.reviewCount > 0) {
|
||||
return 'red'
|
||||
}
|
||||
if (task.weightTaskStatus == 1 && task.reviewCount > 0) {
|
||||
return 'red'
|
||||
}
|
||||
if (task.weightTaskStatus == 2 && task.reviewCount > 0) {
|
||||
return 'green'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
sampleStyle(sample) {
|
||||
//console.log(sample);
|
||||
if (sample.weightSubmitStatus == 0 && sample.reviewCount > 0) {
|
||||
return 'color: red'
|
||||
}
|
||||
if (sample.weightSubmitStatus == 1 && sample.reviewCount > 0) {
|
||||
//return 'color: #e0861a';
|
||||
return 'color: green'
|
||||
}
|
||||
if (sample.weightSubmitStatus == 2 && sample.reviewCount > 0) {
|
||||
return 'color: green'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
//开始秤样
|
||||
startWeighSample() {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/auncel/auncel-weigh?currentTaskNo=' + this.currentTaskNo + '¤tTaskType=' + this.currentTaskType
|
||||
})
|
||||
},
|
||||
//切换任务
|
||||
async swichTask(index) {
|
||||
if (index == this.current) return
|
||||
this.current = index
|
||||
// 如果为0,意味着尚未初始化
|
||||
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
await this.getElRect('menu-scroll-view', 'menuHeight')
|
||||
await this.getElRect('u-tab-item', 'menuItemHeight')
|
||||
}
|
||||
// 将菜单菜单活动item垂直居中
|
||||
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2
|
||||
|
||||
//获取任务详情
|
||||
this.currentTask = this.taskList[index]
|
||||
this.currentTaskNo = this.taskList[index].taskNo
|
||||
this.currentTaskType = this.taskList[index].taskType
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query
|
||||
.select('.' + elClass)
|
||||
.fields({ size: true }, res => {
|
||||
// 如果节点尚未生成,res值为null,循环调用执行
|
||||
if (!res) {
|
||||
setTimeout(() => {
|
||||
this.getElRect(elClass)
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
this[dataVal] = res.height
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
getAssayTask() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
const param = {
|
||||
operateType: 'weight',
|
||||
assayOper: this.userInfo.realname
|
||||
}
|
||||
this.$u.api
|
||||
.getAssayTaskList(param)
|
||||
.then(res => {
|
||||
this.taskList = res.result
|
||||
|
||||
if (this.taskList && this.taskList.length > 0) {
|
||||
this.currentTask = this.taskList[0]
|
||||
this.currentTaskNo = this.taskList[0].taskNo
|
||||
this.currentTaskType = this.taskList[0].taskType
|
||||
this.getAssayTaskDetail(this.currentTaskNo)
|
||||
}
|
||||
uni.hideLoading()
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
getAssayTaskDetail(taskNo) {
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
|
||||
.then(res => {
|
||||
this.sampleList = res.result
|
||||
this.showLoading = false
|
||||
})
|
||||
.catch(err => {
|
||||
this.showLoading = false
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
getDataSourceTypeShow(val) {
|
||||
if (val == 2) return '【筛上】'
|
||||
if (val == 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-title {
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
font-size: 46rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 250rpx);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
padding: 10rpx;
|
||||
height: 200rpx;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 4rpx;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 32rpx;
|
||||
left: 0;
|
||||
top: 39rpx;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 410rpx);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 160rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.btn-operation {
|
||||
height: 100rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
</style>
|
||||
1434
pages/analysis/sample/sample-work-detail.vue
Normal file
1434
pages/analysis/sample/sample-work-detail.vue
Normal file
File diff suppressed because it is too large
Load Diff
746
pages/analysis/sample/sample-work-edit-task.vue
Normal file
746
pages/analysis/sample/sample-work-edit-task.vue
Normal file
@@ -0,0 +1,746 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back titleWidth="800" :title="title"></navbar-back>
|
||||
<u-row>
|
||||
<u-col span="1"></u-col>
|
||||
<u-col span="10">
|
||||
<scroll-view scroll-y scroll-with-animation :scroll-top="scrollTop">
|
||||
<u-form :model="taskInstance" ref="uForm" label-width="210">
|
||||
<view class="form-item-my" v-for="(field, index) in formFields" :key="'field_' + index">
|
||||
<view
|
||||
class="label-my"
|
||||
:style="{ fontWeight: checkFeadToDetailField(field.headToDetailField) ? 'bold' : 'normal' }"
|
||||
>{{ field.label }}</view
|
||||
>
|
||||
<view class="content-my">
|
||||
<view v-if="field.type == 'title' && field.display" class="content-title">
|
||||
{{ field.textValue }}
|
||||
</view>
|
||||
<!--普通输入框-->
|
||||
<u-input
|
||||
v-if="
|
||||
field.type == 'input' &&
|
||||
(field.decimalCount == null || field.decimalCount == '' || Number(field.decimalCount == null) < 0)
|
||||
"
|
||||
v-model="field.value"
|
||||
clearable
|
||||
:placeholder="field.placeholder"
|
||||
:disabled="field.fillingWay != '1'"
|
||||
/>
|
||||
<!--数字,限制小数位数-->
|
||||
<u-input
|
||||
v-if="field.type == 'input' && field.decimalCount != null && Number(field.decimalCount == null) >= 0"
|
||||
type="number"
|
||||
clearable
|
||||
@blur="event => checkDecimal(event, field)"
|
||||
v-model="field.value"
|
||||
:placeholder="field.placeholder"
|
||||
:disabled="field.fillingWay != '1'"
|
||||
/>
|
||||
<!--select-->
|
||||
<view v-if="field.type == 'select'" class="x-bc select-my" @click="handleFieldClick(field)">
|
||||
<text v-if="field.valueText">{{ field.valueText }}</text>
|
||||
<text v-else>请选择</text>
|
||||
<u-icon name="arrow-down" size="20"></u-icon>
|
||||
</view>
|
||||
<u-picker
|
||||
v-if="field.type == 'select'"
|
||||
:show="field.showPicker"
|
||||
:columns="[field.options]"
|
||||
keyName="displayName"
|
||||
@cancel="field.showPicker = false"
|
||||
@confirm="event => pickerConfirm(event, field)"
|
||||
/>
|
||||
<!--日期-->
|
||||
<view v-if="field.type == 'date'" class="x-bc select-my" @click="handleFieldClick(field)">
|
||||
<text v-if="field.value">{{ field.value }}</text>
|
||||
<text v-else>请选择</text>
|
||||
<u-icon name="calendar-fill" size="20"></u-icon>
|
||||
</view>
|
||||
|
||||
<u-datetime-picker
|
||||
v-if="field.type == 'date'"
|
||||
:show="field.showPicker"
|
||||
v-model="curDate"
|
||||
mode="date"
|
||||
@cancel="field.showPicker = false"
|
||||
@confirm="event => pickerConfirm(event, field)"
|
||||
></u-datetime-picker>
|
||||
</view>
|
||||
</view>
|
||||
</u-form>
|
||||
</scroll-view>
|
||||
<u-button type="primary" @click="saveHeadData">保存</u-button>
|
||||
</u-col>
|
||||
<u-col span="1"> </u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import request from '@/nx/request'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { calcAnalysisValue } from '@/nx/helper/calcAnalysisValue'
|
||||
import nx from '@/nx'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
title: '编辑指派单',
|
||||
taskInstance: {},
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
currentTaskNo: '', //当前任务样品
|
||||
showDicPicker: false,
|
||||
curDate: Number(new Date()),
|
||||
//字段值对象
|
||||
formValue: {},
|
||||
//后端返回的字段结构
|
||||
sourceFormFields: [],
|
||||
//处理后的字段结构
|
||||
formFields: [],
|
||||
sampleList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dynamicFieldPlaceholder(field) {
|
||||
return '请输入'
|
||||
}
|
||||
},
|
||||
onLoad(param) {
|
||||
if (param.currentTaskNo) {
|
||||
this.currentTaskNo = param.currentTaskNo
|
||||
}
|
||||
this.title = '样品分析-任务指派单:' + this.currentTaskNo
|
||||
this.loadHeadFieldsAndValueByTaskNo()
|
||||
this.getSampleList(this.currentTaskNo)
|
||||
},
|
||||
methods: {
|
||||
handleFieldClick(field) {
|
||||
if (field.type == 'date') {
|
||||
if (field.fillingWay == '1') {
|
||||
field.showPicker = true
|
||||
}
|
||||
return
|
||||
}
|
||||
field.showPicker = true
|
||||
},
|
||||
checkFeadToDetailField(headToDetailField) {
|
||||
if (headToDetailField && headToDetailField.trim() != '') {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
getSampleList(taskNo) {
|
||||
this.$u.api
|
||||
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
|
||||
.then(res => {
|
||||
this.sampleList = res.result
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
},
|
||||
//返回上一页
|
||||
// customBack() {
|
||||
// uni.redirectTo({
|
||||
// url: "/pages/sample/sample-work-detail"
|
||||
// });
|
||||
// },
|
||||
navRightClick() {},
|
||||
//处理小数位数:补0或去除多余位数
|
||||
checkDecimal(e, field) {
|
||||
if (e == '') return
|
||||
const decimalCount = field.decimalCount
|
||||
if (decimalCount == null || decimalCount == '' || isNaN(decimalCount)) return
|
||||
const count = Number(field.decimalCount)
|
||||
const value = field.value
|
||||
const pos = value.indexOf('.') + 1
|
||||
let length = value.length - pos
|
||||
if (pos == 0) {
|
||||
length = 0
|
||||
}
|
||||
while (length < count) {
|
||||
if (field.value.indexOf('.') < 0) field.value += '.'
|
||||
field.value = field.value + '0'
|
||||
length++
|
||||
}
|
||||
if (count === 0) {
|
||||
field.value = parseInt(field.value) + ''
|
||||
} else if (length > count) {
|
||||
field.value = field.value.substring(0, pos + count)
|
||||
}
|
||||
},
|
||||
updateFieldValue(name, val) {
|
||||
for (const field of this.formFields) {
|
||||
if (field.prop == name) {
|
||||
field.value = val
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
assembleFields() {
|
||||
const me = this
|
||||
const formFields = []
|
||||
for (const field of this.sourceFormFields) {
|
||||
//日期类型的默认值
|
||||
if (field.type == 'date') {
|
||||
const value = field.value
|
||||
field.showPicker = false
|
||||
if (value == 'curDate') {
|
||||
field.value = me.$helper.dateFormat(new Date(), 'yyyy-MM-dd')
|
||||
}
|
||||
}
|
||||
if (field.type == 'select') {
|
||||
field.showPicker = false
|
||||
}
|
||||
formFields.push(field)
|
||||
}
|
||||
//先序列化再转json,避免json里定义的方法丢失
|
||||
this.formFields = JSON.parse(JSON.stringify(formFields, replacer), reviver)
|
||||
|
||||
// this.formFields = formFields;
|
||||
},
|
||||
//绑定数据
|
||||
bindFormValue() {
|
||||
const me = this
|
||||
//formValue
|
||||
for (const field of me.formFields) {
|
||||
const prop = field.prop
|
||||
if (prop) {
|
||||
const value = me.formValue[prop]
|
||||
if (value) {
|
||||
field.value = value
|
||||
if (field.type == 'select') {
|
||||
field.valueText = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//获取表单字段和值(老数据)
|
||||
loadHeadFieldsAndValueByTaskNo() {
|
||||
const me = this
|
||||
const currentTaskNo = me.currentTaskNo
|
||||
this.$u.api
|
||||
.queryHeadValueByTaskNo({ taskNo: currentTaskNo })
|
||||
.then(res => {
|
||||
const result = res.result
|
||||
const formConf = result.formConf
|
||||
me.sourceFormFields = eval(formConf)
|
||||
me.formValue = JSON.parse(result.formValue)
|
||||
//加载和处理表单字段
|
||||
me.assembleFields()
|
||||
//绑定数据
|
||||
me.bindFormValue()
|
||||
//读取字段里的动态选项
|
||||
me.loadFieldApiData()
|
||||
console.log('formFields', this.formFields)
|
||||
})
|
||||
.catch(err => {
|
||||
//如果没有查到数据,按配置读取新的表单字段
|
||||
me.getHeadFields()
|
||||
})
|
||||
},
|
||||
// 获取表单字段(新数据)
|
||||
getHeadFields() {
|
||||
const me = this
|
||||
const currentTaskNo = this.currentTaskNo
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
this.$u.api
|
||||
.queryHeadFieldsByTaskNo({ taskNo: currentTaskNo })
|
||||
.then(res => {
|
||||
me.sourceFormFields = me.analysisFormAndFields(res)
|
||||
//加载和处理表单字段
|
||||
me.assembleFields()
|
||||
//读取字段里的动态选项
|
||||
me.loadFieldApiData()
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
},
|
||||
analysisFormAndFields(formRet) {
|
||||
const fieldsArray = []
|
||||
for (const form of formRet) {
|
||||
let content = cloneDeep(form.content)
|
||||
content = eval('(' + content + ')')
|
||||
const column = content.column
|
||||
for (const field of column) {
|
||||
fieldsArray.push(field)
|
||||
}
|
||||
}
|
||||
return fieldsArray
|
||||
},
|
||||
//将抬头字段值同步保存到明细字段
|
||||
saveHeadValueToDetail(onComplete) {
|
||||
//循环抬头字段,提取需哟保存到明细的字段
|
||||
const conf = []
|
||||
for (const field of this.formFields) {
|
||||
const prop = field.prop
|
||||
const headToDetailField = field.headToDetailField
|
||||
if (prop && headToDetailField && headToDetailField.trim() != '') {
|
||||
const value = field.value
|
||||
const r = {
|
||||
prop: field.prop,
|
||||
value: value,
|
||||
headToDetailField: headToDetailField
|
||||
}
|
||||
conf.push(r)
|
||||
}
|
||||
}
|
||||
const data = {
|
||||
taskNo: this.currentTaskNo,
|
||||
conf: conf
|
||||
}
|
||||
this.$u.api.saveHeadValueToDetail(data).then(res => {
|
||||
if (onComplete) onComplete()
|
||||
})
|
||||
},
|
||||
handleSave() {
|
||||
//显示loading
|
||||
uni.showLoading({
|
||||
title: '正在保存...'
|
||||
})
|
||||
//组装数据
|
||||
const formValue = {}
|
||||
for (const field of this.formFields) {
|
||||
const prop = field.prop
|
||||
if (prop) {
|
||||
formValue[prop] = field.value
|
||||
}
|
||||
}
|
||||
const value = {
|
||||
taskNo: this.currentTaskNo,
|
||||
formValue: JSON.stringify(formValue),
|
||||
formConf: JSON.stringify(this.sourceFormFields, replacer)
|
||||
}
|
||||
this.$u.api
|
||||
.saveHeadValue(value)
|
||||
.then(async res => {
|
||||
await this.saveHeadValueToDetail(async () => {
|
||||
if (this.checkPropertyEquality()) {
|
||||
await this.processIds(this.sampleList, 100)
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
// this.$helper.showToast({
|
||||
// title: '保存成功!'
|
||||
// });
|
||||
uni.redirectTo({
|
||||
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + this.currentTaskNo
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 检查字段修改前和修改后字段值是否相等
|
||||
checkPropertyEquality() {
|
||||
for (const field of this.formFields) {
|
||||
const prop = field.prop
|
||||
if (prop && field['headToDetailField'] && field['headToDetailField'].trim() !== '') {
|
||||
const flag = this.formValue[prop] !== field.value ? true : false
|
||||
if (flag) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
//保存抬头字段
|
||||
saveHeadData() {
|
||||
if (this.checkPropertyEquality()) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '您修改的字段将影响样品分析结果,系统将重新计算',
|
||||
showCancel: false,
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
this.handleSave()
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.handleSave()
|
||||
}
|
||||
},
|
||||
// 通过样品id查询明细
|
||||
async getSampleDataById(taskDetailId) {
|
||||
let fieldGroup = []
|
||||
const { result, additionalProperties } = await this.$u.api.queryFieldsByTaskDetail({
|
||||
taskDetailId,
|
||||
isSearchSRange: '0'
|
||||
})
|
||||
fieldGroup = result
|
||||
const conAssayTaskId = additionalProperties.conAssayTaskId
|
||||
const busSubCSampleId = additionalProperties.busSubCSampleId
|
||||
const detail = additionalProperties.taskDetail
|
||||
//处理硫值、硫量:未保存过的数据,读取接口返回的硫值、硫量
|
||||
// this.loadSValue(detail);
|
||||
//按公式计算值,并检查原数据与计算后的数据是否一致
|
||||
try {
|
||||
calcAnalysisValue(fieldGroup)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
let valueList = []
|
||||
let cupNum = 0
|
||||
for (const g of fieldGroup) {
|
||||
for (const f of g.fields) {
|
||||
if (f.dicKey == 'bh' || f.dicKey == 'bh_up') cupNum = f.value
|
||||
valueList.push({
|
||||
id: f.detailId,
|
||||
type: f.pOrE,
|
||||
value: f.value,
|
||||
name: f.name,
|
||||
dataType: f.dataType
|
||||
})
|
||||
}
|
||||
}
|
||||
let params = {
|
||||
busSubCSampleId,
|
||||
conAssayTaskId,
|
||||
// measureTime : this.curSample.measureTime,
|
||||
elementParamValueList: valueList,
|
||||
busAssayTaskDetailId: taskDetailId
|
||||
}
|
||||
if (typeof cupNum != 'undefined' && cupNum != null && cupNum != '' && cupNum != 0 && cupNum != '0') {
|
||||
//提交杯号,保存到后台
|
||||
params.cupNum = cupNum
|
||||
}
|
||||
return params
|
||||
},
|
||||
// 提交样品
|
||||
async submitData(data) {
|
||||
try {
|
||||
await this.$u.api.saveDetailValue(data)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
async processIds(list, interval) {
|
||||
let index = 0
|
||||
const intervalId = setInterval(async () => {
|
||||
if (index < list.length) {
|
||||
const item = list[index]
|
||||
index++
|
||||
const params = await this.getSampleDataById(item['id'])
|
||||
await this.submitData(params)
|
||||
} else {
|
||||
clearInterval(intervalId) // 所有任务完成后清除定时器
|
||||
uni.redirectTo({
|
||||
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + this.currentTaskNo
|
||||
})
|
||||
}
|
||||
}, interval)
|
||||
},
|
||||
|
||||
async apiRequest(url) {
|
||||
return request({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
custom: {
|
||||
isApiEncryption: true
|
||||
}
|
||||
})
|
||||
},
|
||||
//读取字段里的API选项
|
||||
async loadFieldApiData() {
|
||||
const formFields = this.formFields
|
||||
let changeFlag = false
|
||||
for (const field of formFields) {
|
||||
const dicUrl = field.dicUrl
|
||||
const type = field.type
|
||||
if (dicUrl && dicUrl != '') {
|
||||
//读取API选项
|
||||
try {
|
||||
const res = await this.apiRequest(dicUrl)
|
||||
|
||||
const data = res
|
||||
const confLabel = field.props.label
|
||||
const confValue = field.props.value
|
||||
const emptyItem = { name: '', displayName: '' }
|
||||
emptyItem[confLabel] = ''
|
||||
emptyItem[confValue] = ''
|
||||
//设置valueText、displayName
|
||||
for (const item of data) {
|
||||
if (item[confValue] == field.value) {
|
||||
changeFlag = true
|
||||
field.valueText = item[confLabel]
|
||||
}
|
||||
}
|
||||
// data.unshift(emptyItem) //添加空数据
|
||||
field.options = data
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
if (changeFlag) {
|
||||
const formFields = this.formFields
|
||||
// this.formFields = JSON.parse(JSON.stringify(formFields));
|
||||
this.formFields = JSON.parse(JSON.stringify(formFields, replacer), reviver)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* 选择器组件确认事件
|
||||
* event: 事件默认参数
|
||||
* field: 当前字段
|
||||
* 处理逻辑:
|
||||
* field.value = 选中的值
|
||||
* field.valueText = 选中的文本
|
||||
* todo: 关联字段在动态字段的change事件处理
|
||||
* */
|
||||
pickerConfirm(event, field) {
|
||||
const me = this
|
||||
if (field.type == 'date') {
|
||||
field.value = nx.$dayjs(event.value).format(field.format)
|
||||
field.showPicker = false
|
||||
return
|
||||
}
|
||||
const confLabel = field.props.label
|
||||
const confValue = field.props.value
|
||||
const selected = event.value[0]
|
||||
const value = selected[confValue]
|
||||
const displayName = selected[confLabel]
|
||||
field.value = value
|
||||
field.valueText = displayName
|
||||
if (typeof field.change == 'function') {
|
||||
field.change({ value }, selected, me)
|
||||
}
|
||||
field.showPicker = false
|
||||
},
|
||||
checkLoadSValue() {
|
||||
const vKey = 'sRange'
|
||||
const vF = this.getFieldByKey(vKey)
|
||||
if (vF == null) return false
|
||||
const v = vF.value
|
||||
if (v == null || v == '') return true
|
||||
try {
|
||||
if (v == 0 || number(v) == 0) return true
|
||||
} catch (e) {}
|
||||
return false
|
||||
},
|
||||
//读取硫值、硫量
|
||||
loadSValue(detail) {
|
||||
let flag = this.checkLoadSValue()
|
||||
if (!flag) return
|
||||
const vKey = 'sValue'
|
||||
const rKey = 'sRange'
|
||||
const vF = this.getFieldByKey(vKey)
|
||||
if (vF != null) {
|
||||
vF.value = detail.svalue
|
||||
}
|
||||
const rF = this.getFieldByKey(rKey)
|
||||
if (rF != null) {
|
||||
rF.value = detail.srange
|
||||
}
|
||||
},
|
||||
getFieldByKey(key) {
|
||||
const group = this.fieldGroup
|
||||
let field = null
|
||||
for (let g of group) {
|
||||
for (let f of g.fields) {
|
||||
const dicKey = f.dicKey
|
||||
if (dicKey && dicKey == key) {
|
||||
field = f
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log('field', JSON.stringify(field));
|
||||
return field
|
||||
}
|
||||
}
|
||||
}
|
||||
// 自定义 replacer,将函数转换为字符串。json序列化和反序列化时避免函数丢失
|
||||
function replacer(key, value) {
|
||||
if (typeof value === 'function') {
|
||||
return value.toString()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 自定义 reviver,将字符串转换回函数.json序列化和反序列化时避免函数丢失
|
||||
const functionKeys = ['change', 'dicFormatter']
|
||||
function reviver(key, value) {
|
||||
if (functionKeys.includes(key)) {
|
||||
// 将字符串转换为函数
|
||||
return new Function('return ' + value)()
|
||||
}
|
||||
return value
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.navbar-right {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
|
||||
.content-main-left {
|
||||
height: calc(100vh - 205px);
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.form-item-my {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.label-my {
|
||||
width: 170px; /* 标签宽度 */
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.label-my sub {
|
||||
font-size: 0.6em; /* 调整下标字体大小 */
|
||||
vertical-align: sub; /* 调整下标垂直对齐 */
|
||||
}
|
||||
|
||||
.content-my {
|
||||
flex: 1;
|
||||
padding-left: 7px;
|
||||
.select-my {
|
||||
color: #303133;
|
||||
font-size: 15px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
height: 80px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
border-width: 2px;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.content-main-left-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.auncel-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auncel {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background-image: url(/static/images/auncel.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.auncel-title {
|
||||
width: 100%;
|
||||
height: 210px;
|
||||
}
|
||||
|
||||
.auncel-weight {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.weight {
|
||||
width: 100%;
|
||||
min-width: 200px;
|
||||
height: 100px;
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
//background-color: #2c405a;
|
||||
}
|
||||
|
||||
.weight-data {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #4cd964;
|
||||
text-align: right;
|
||||
line-height: 100px;
|
||||
letter-spacing: 2px;
|
||||
font-size: 75px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-data-yellow {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ffff00;
|
||||
text-align: right;
|
||||
line-height: 100px;
|
||||
letter-spacing: 2px;
|
||||
font-size: 75px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-data-warning {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
color: #ff3333;
|
||||
text-align: right;
|
||||
line-height: 100px;
|
||||
font-size: 75px;
|
||||
font-family: zzjc-lcd;
|
||||
}
|
||||
|
||||
.weight-unit {
|
||||
color: #ffffff;
|
||||
font-size: 65px;
|
||||
line-height: 100px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
379
pages/analysis/sample/sample-work-list.vue
Normal file
379
pages/analysis/sample/sample-work-list.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back :autoBack="false" title="样品分析" @leftClick="customBack"></navbar-back>
|
||||
<u-row class="content-title" gutter="16">
|
||||
<u-col span="4">
|
||||
<view class="content-title-name">
|
||||
<text>任务列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-title-name">
|
||||
<text>样品列表</text>
|
||||
</view>
|
||||
<u-gap height="5" bg-color="#0055A2"></u-gap>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row class="content-main-height" gutter="16" align="top">
|
||||
<u-col span="4">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
scroll-with-animation
|
||||
class="content-main-height content-main-left"
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
<view
|
||||
v-for="(task, index) in taskList"
|
||||
:key="index"
|
||||
class="u-tab-item"
|
||||
:class="[current === index ? 'u-tab-item-active' : '']"
|
||||
@tap.stop="swichTask(index)"
|
||||
>
|
||||
<u-row style="width: 100%">
|
||||
<u-col span="2" style="text-align: center">
|
||||
<u-icon :color="taskStyle(task)" name="tags-fill" size="34"></u-icon>
|
||||
</u-col>
|
||||
<u-col span="10">
|
||||
<view class="fs18">{{ task.taskNo }}</view>
|
||||
<view style="margin-top: 10px">{{ task.taskName }}</view>
|
||||
<view class="x-f" style="margin-top: 10px">
|
||||
<u-icon color="" name="clock"></u-icon>
|
||||
<text style="margin-left: 5px">{{ task.taskOperTime }}</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-col>
|
||||
<u-col span="8">
|
||||
<view class="content-main-height">
|
||||
<scroll-view scroll-y scroll-with-animation class="content-main-right">
|
||||
<block v-for="(sample, index) in sampleList" :key="index">
|
||||
<view v-if="currentTask.reviewCount === sample.reviewCount" style="padding: 5px; font-size: 16px">
|
||||
<u-row>
|
||||
<u-col span="3" style="text-align: center">
|
||||
<u-row>
|
||||
<u-col span="6" style="text-align: center">
|
||||
<u-checkbox
|
||||
v-model="sample.checked"
|
||||
v-if="
|
||||
sample.sampleProcessNo === currentNode &&
|
||||
sample.rollbackStatus !== 'running' &&
|
||||
sample.rollbackStatus !== 'finished'
|
||||
"
|
||||
@change="() => selectSample(sample)"
|
||||
></u-checkbox>
|
||||
</u-col>
|
||||
<u-col span="6" style="text-align: center">
|
||||
<view
|
||||
><text>【{{ sample.sort }}】</text></view
|
||||
>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</u-col>
|
||||
<u-col span="9">
|
||||
<view class="sample_desc">
|
||||
<view>
|
||||
<view
|
||||
><text style="padding-left: 10px">{{ sample.sampleCode }}</text></view
|
||||
>
|
||||
<view>
|
||||
<text style="padding-left: 10px">
|
||||
{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="sample_desc_warn" v-if="sample.sampleProcessNo !== currentNode">
|
||||
当前节点:{{ getProcessNameShow(sample.sampleProcessNo) }}
|
||||
</view>
|
||||
<view class="sample_desc_warn" v-if="sample.rollbackStatus === 'revoke'"> 样品退回被驳回 </view>
|
||||
<view class="sample_desc_warn" v-if="sample.rollbackStatus === 'running'"> 样品退回审批中 </view>
|
||||
<view class="sample_desc_warn" v-if="sample.rollbackStatus === 'finished'">
|
||||
样品已退回,请联系管理员处理
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-line style="padding: 5px" color="#bbb" />
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="content-main-right-operation">
|
||||
<u-row>
|
||||
<u-col span="3.5"></u-col>
|
||||
<u-col span="4">
|
||||
<u-button class="btn-operation" type="warning" @click="showRollbackModal">申请退回样品</u-button>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<u-button class="btn-operation" :disabled="taskList.length === 0" type="success" @click="startWork">
|
||||
开始分析
|
||||
</u-button>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<!-- 退回样品弹窗 -->
|
||||
<up-modal
|
||||
:show="showRollbackModalFlag"
|
||||
showCancelButton
|
||||
@confirm="applyRollbackSample"
|
||||
@cancel="showRollbackModalFlag = false"
|
||||
title="退回说明"
|
||||
width="500px"
|
||||
>
|
||||
<u--textarea v-model="rollbackContent" placeholder="请输入退回原因或说明"></u--textarea>
|
||||
</up-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
import nx from '@/nx'
|
||||
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
|
||||
|
||||
// 响应式数据
|
||||
const currentNode = ref('F31')
|
||||
const dicSampleProcessCodeList = ref([])
|
||||
const showRollbackModalFlag = ref(false)
|
||||
const rollbackContent = ref('')
|
||||
const scrollTop = ref(0)
|
||||
const current = ref(0)
|
||||
const currentTask = ref({})
|
||||
const currentTaskNo = ref('')
|
||||
const currentTaskType = ref('')
|
||||
const taskList = ref([])
|
||||
const sampleList = ref([])
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const customBack = () => {
|
||||
uni.reLaunch({ url: '/pages/analysis/index/index' })
|
||||
}
|
||||
|
||||
const selectSample = sample => {
|
||||
sample.checked = !sample.checked
|
||||
}
|
||||
|
||||
const taskStyle = task => {
|
||||
if ((task.weightTaskStatus === 0 || task.weightTaskStatus === 1) && task.reviewCount > 0) return 'red'
|
||||
if (task.weightTaskStatus === 2 && task.reviewCount > 0) return 'green'
|
||||
return ''
|
||||
}
|
||||
|
||||
const showRollbackModal = () => {
|
||||
const checkedSampleList = sampleList.value.filter(item => item.checked)
|
||||
if (checkedSampleList.length === 0) {
|
||||
uni.showToast({ title: '请选择要退回的样品!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
showRollbackModalFlag.value = true
|
||||
}
|
||||
|
||||
const applyRollbackSample = () => {
|
||||
const checkedSampleList = sampleList.value.filter(item => item.checked)
|
||||
if (!rollbackContent.value.trim()) {
|
||||
uni.showToast({ title: '请输入退回说明!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
showRollbackModalFlag.value = false
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '发起申请,退回已勾选的样品,是否继续?',
|
||||
cancelColor: '#0055A2',
|
||||
confirmColor: '#0055A2',
|
||||
success: res => {
|
||||
if (res.cancel) return
|
||||
const detailIdList = checkedSampleList.map(item => item.id)
|
||||
const data = {
|
||||
remark: rollbackContent.value,
|
||||
taskId: currentTask.value.id,
|
||||
detailIds: detailIdList.join(',')
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '正在发起申请...' })
|
||||
nx.$api.assayTask
|
||||
.createRollbackApply(data)
|
||||
.then(() => {
|
||||
getAssayTaskDetail(currentTaskNo.value)
|
||||
})
|
||||
.catch(console.error)
|
||||
.finally(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const checkWork = () => {
|
||||
let checkedSampleList = sampleList.value.filter(
|
||||
item => item.rollbackStatus === 'running' || item.rollbackStatus === 'finished'
|
||||
)
|
||||
if (checkedSampleList.length > 0) {
|
||||
uni.showToast({ title: '存在未处理的退回申请,请处理后再分析!', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
checkedSampleList = sampleList.value.filter(item => item.sampleProcessNo !== currentNode.value)
|
||||
if (checkedSampleList.length > 0) {
|
||||
uni.showToast({ title: '部分样品状态异常,请联系管理员处理!', icon: 'none', duration: 2300 })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const startWork = () => {
|
||||
if (!checkWork()) return
|
||||
uni.navigateTo({
|
||||
url: `/pages/analysis/sample/sample-work-detail?currentTaskNo=${currentTaskNo.value}`
|
||||
})
|
||||
}
|
||||
|
||||
const swichTask = async index => {
|
||||
if (index === current.value) return
|
||||
current.value = index
|
||||
rollbackContent.value = ''
|
||||
|
||||
const task = taskList.value[index]
|
||||
currentTask.value = task
|
||||
currentTaskNo.value = task.taskNo
|
||||
currentTaskType.value = task.taskType
|
||||
getAssayTaskDetail(task.taskNo)
|
||||
}
|
||||
|
||||
const getAssayTask = () => {
|
||||
rollbackContent.value = ''
|
||||
const param = {
|
||||
finishStatus: 'waiting',
|
||||
assayOper: userInfo.value.nickname
|
||||
}
|
||||
nx.$api.auncel.getAssayTaskList(param).then(res => {
|
||||
if (res) {
|
||||
taskList.value = res
|
||||
if (taskList.value.length > 0) {
|
||||
const first = taskList.value[0]
|
||||
currentTask.value = first
|
||||
currentTaskNo.value = first.taskNo
|
||||
currentTaskType.value = first.taskType
|
||||
getAssayTaskDetail(first.taskNo)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getAssayTaskDetail = taskNo => {
|
||||
sampleList.value = []
|
||||
nx.$api.assayTask.getAssayTaskDetailListByTaskNo({ taskNo }).then(res => {
|
||||
const list = res.result || []
|
||||
list.forEach(item => (item.checked = false))
|
||||
sampleList.value = list
|
||||
})
|
||||
}
|
||||
|
||||
const getDicSampleProcessCodeList = () => {
|
||||
nx.$api.assayTask.queryQmsDicSampleProcessCodeList().then(res => {
|
||||
dicSampleProcessCodeList.value = res.records || []
|
||||
})
|
||||
}
|
||||
|
||||
const getProcessNameShow = val => {
|
||||
const item = dicSampleProcessCodeList.value.find(i => i.processCode === val)
|
||||
return item ? item.processName : val
|
||||
}
|
||||
|
||||
const getDataSourceTypeShow = val => {
|
||||
if (val === 2) return '【筛上】'
|
||||
if (val === 3) return '【筛下】'
|
||||
return ''
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onLoad(() => {
|
||||
const { lockOrientation } = useScreenOrientation()
|
||||
lockOrientation('landscape')
|
||||
getDicSampleProcessCodeList()
|
||||
getAssayTask()
|
||||
})
|
||||
|
||||
onBackPress(() => {
|
||||
customBack()
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式保持不变 */
|
||||
.content-title {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.content-title-name {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.content-main-height {
|
||||
height: calc(100vh - 125px);
|
||||
}
|
||||
.content-main-left {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.u-tab-item {
|
||||
padding: 5px;
|
||||
height: 100px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
line-height: 1;
|
||||
border-width: 2px;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #0055a2;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
}
|
||||
.u-tab-item-active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 0;
|
||||
top: 20px;
|
||||
}
|
||||
.content-main-right {
|
||||
height: calc(100vh - 205px);
|
||||
}
|
||||
.content-main-right-operation {
|
||||
height: 80px;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.btn-operation {
|
||||
height: 50px;
|
||||
font-size: 18px;
|
||||
width: 95%;
|
||||
}
|
||||
.sample_desc {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.sample_desc_warn {
|
||||
color: red;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
240
pages/analysis/setting/print.vue
Normal file
240
pages/analysis/setting/print.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="打印服务">
|
||||
<view class="navbar-right" slot="right">
|
||||
<view class="message-box right-item" @click="refreshPrint">
|
||||
<up-icon name="reload" color="#fff" size="20" />
|
||||
</view>
|
||||
<view class="dot-box right-item" @click="addPrintShow">
|
||||
<up-icon name="plus" color="#fff" size="20" />
|
||||
</view>
|
||||
</view>
|
||||
</navbar-back>
|
||||
|
||||
<view>
|
||||
<u-swipe-action
|
||||
v-for="(print, index) in printList"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:options="options"
|
||||
@click="printSwipeClick"
|
||||
>
|
||||
<view class="item u-border-bottom">
|
||||
<image :src="`/static/images/print${print.isOpen ? '' : '-close'}.png`" mode="aspectFill" />
|
||||
<view style="width: 100%; padding-left: 30px; margin: 30px">
|
||||
<u-row>
|
||||
<u-col span="6">
|
||||
<text>服务名称:{{ print.printName }}</text>
|
||||
</u-col>
|
||||
<u-col span="6">
|
||||
<text>服务IP地址:{{ print.printIp }}</text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
<u-row>
|
||||
<u-col span="6">
|
||||
<text>服务端口:{{ print.printPort }}</text>
|
||||
</u-col>
|
||||
<u-col span="6">
|
||||
<text>连接状态:{{ print.isOpen ? '连接正常!!!' : '连接关闭!!!' }}</text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-swipe-action>
|
||||
</view>
|
||||
|
||||
<u-modal
|
||||
:show="printShow"
|
||||
title="添加打印服务"
|
||||
show-cancel-button
|
||||
width="50vw"
|
||||
confirm-text="添加"
|
||||
@confirm="addPrint"
|
||||
@cancel="printShow = false"
|
||||
>
|
||||
<view class="slot-content">
|
||||
<u-form :model="form">
|
||||
<u-form-item label="名称">
|
||||
<u-input v-model="form.printName" />
|
||||
</u-form-item>
|
||||
<u-form-item label="IP">
|
||||
<u-input v-model="form.printIp" />
|
||||
</u-form-item>
|
||||
<u-form-item label="端口">
|
||||
<u-input v-model="form.printPort" type="number" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
</u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const printShow = ref(false)
|
||||
const printList = ref([])
|
||||
|
||||
const form = ref({
|
||||
printName: '',
|
||||
printIp: '',
|
||||
printPort: 22333
|
||||
})
|
||||
|
||||
const options = [
|
||||
{
|
||||
text: '删除',
|
||||
style: {
|
||||
fontSize: '28rpx',
|
||||
backgroundColor: 'rgb(255,58,49)'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 方法
|
||||
const addPrintShow = () => {
|
||||
if (printList.value.length > 0) {
|
||||
uni.showToast({
|
||||
title: '已存在打印服务!',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
form.value = {
|
||||
printName: '',
|
||||
printIp: '',
|
||||
printPort: 22333
|
||||
}
|
||||
printShow.value = true
|
||||
}
|
||||
|
||||
const refreshPrint = () => {
|
||||
const printListData = uni.getStorageSync('KEY_PRINT_LIST') || []
|
||||
if (printListData.length > 0) {
|
||||
printListData.forEach(print => {
|
||||
uni.$print.open(print.printIp)
|
||||
})
|
||||
uni.showToast({
|
||||
title: '刷新成功!',
|
||||
icon: 'none'
|
||||
})
|
||||
} else {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '未配置打印服务,请点击右边的“+”配置打印服务!',
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const addPrint = () => {
|
||||
const { printName, printIp, printPort } = form.value
|
||||
|
||||
if (!printName.trim()) {
|
||||
uni.showToast({ title: '打印服务名称不允许为空!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!printIp.trim()) {
|
||||
uni.showToast({ title: '打印服务IP不允许为空!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const ipReg =
|
||||
/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
|
||||
if (!ipReg.test(printIp)) {
|
||||
uni.showToast({ title: '打印服务IP不正确!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
let storedList = uni.getStorageSync('KEY_PRINT_LIST') || []
|
||||
const exists = storedList.some(p => p.printIp === printIp)
|
||||
if (exists) {
|
||||
uni.showToast({ title: '打印服务已存在!', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
storedList.push({
|
||||
printName,
|
||||
printIp,
|
||||
printPort,
|
||||
isDefault: storedList.length === 0
|
||||
})
|
||||
|
||||
uni.$print.open(printIp)
|
||||
uni.setStorageSync('KEY_PRINT_LIST', storedList)
|
||||
refreshList()
|
||||
printShow.value = false
|
||||
}
|
||||
|
||||
const printSwipeClick = (index, optionsIndex) => {
|
||||
if (optionsIndex === 0) {
|
||||
const delPrint = printList.value[index]
|
||||
uni.$print.close(delPrint.printIp)
|
||||
|
||||
printList.value.splice(index, 1)
|
||||
|
||||
if (delPrint.isDefault && printList.value.length > 0) {
|
||||
printList.value[0].isDefault = true
|
||||
}
|
||||
|
||||
uni.setStorageSync('KEY_PRINT_LIST', printList.value)
|
||||
uni.showToast({ title: '删除成功!', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const refreshList = () => {
|
||||
const stored = uni.getStorageSync('KEY_PRINT_LIST') || []
|
||||
printList.value = stored.map(print => {
|
||||
const printer = uni.$print.printMap?.get(print.printIp)
|
||||
return {
|
||||
...print,
|
||||
isOpen: printer ? printer.isOpen : false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
refreshList()
|
||||
uni.$on('printStatus', refreshList)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off('printStatus', refreshList)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar-right {
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.right-item {
|
||||
margin: 0 6px;
|
||||
position: relative;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.slot-content {
|
||||
font-size: 14px;
|
||||
color: $u-content-color;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
image {
|
||||
width: 60px;
|
||||
flex: 0 0 60px;
|
||||
height: 60px;
|
||||
margin-right: 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
23
pages/analysis/setting/setting.vue
Normal file
23
pages/analysis/setting/setting.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="系统设置"></navbar-back>
|
||||
<view>
|
||||
<u-cell-group title="打印设置">
|
||||
<u-cell icon="order" title="打印服务" @click="nx.$router.go('/pages/analysis/setting/print')"></u-cell>
|
||||
</u-cell-group>
|
||||
<u-cell-group title="天平状态">
|
||||
<u-cell
|
||||
icon="hourglass-half-fill"
|
||||
title="查看天平"
|
||||
@click="nx.$router.go('/pages/analysis/auncel/auncel-status')"
|
||||
></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import nx from '@/nx'
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,202 +0,0 @@
|
||||
<template>
|
||||
<view class="form-container">
|
||||
<u-form :model="formData" ref="formRef" :rules="rules" label-width="180rpx">
|
||||
<u-form-item label="合同编号" prop="code">
|
||||
<u-input v-model="formData.code" placeholder="请输入合同编号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同名称" prop="name">
|
||||
<u-input v-model="formData.name" placeholder="请输入合同名称" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同状态" prop="status">
|
||||
<u-picker
|
||||
v-model="formData.status"
|
||||
:range="statusOptions"
|
||||
range-key="text"
|
||||
@change="onStatusChange"
|
||||
>
|
||||
<u-input
|
||||
v-model="statusText"
|
||||
disabled
|
||||
placeholder="请选择合同状态"
|
||||
suffix-icon="arrow-down"
|
||||
/>
|
||||
</u-picker>
|
||||
</u-form-item>
|
||||
<u-form-item label="签订日期" prop="signDate">
|
||||
<uni-datetime-picker v-model="formData.signDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同开始日期" prop="startDate">
|
||||
<uni-datetime-picker v-model="formData.startDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同结束日期" prop="endDate">
|
||||
<uni-datetime-picker v-model="formData.endDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同金额" prop="amount">
|
||||
<u-input v-model="formData.amount" placeholder="请输入合同金额" type="number" />
|
||||
</u-form-item>
|
||||
<u-form-item label="备注" prop="remark">
|
||||
<u-textarea v-model="formData.remark" placeholder="请输入备注" />
|
||||
</u-form-item>
|
||||
<u-form-item label="岗位ID" prop="postId">
|
||||
<u-input v-model="formData.postId" placeholder="请输入岗位ID" />
|
||||
</u-form-item>
|
||||
<!-- TODO: 附件上传 -->
|
||||
</u-form>
|
||||
<u-button
|
||||
type="primary"
|
||||
@click="submit"
|
||||
class="submit-btn"
|
||||
:loading="submitting"
|
||||
:disabled="submitting"
|
||||
>提交</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, toRaw } from 'vue'
|
||||
import DemoContractApi from '@/sheep/api/infra/democontract'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const formType = ref('create')
|
||||
const formData = reactive({
|
||||
id: undefined,
|
||||
code: '',
|
||||
name: '',
|
||||
status: 0,
|
||||
signDate: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
amount: undefined,
|
||||
remark: '',
|
||||
postId: ''
|
||||
})
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 0, text: '草稿' },
|
||||
{ value: 1, text: '审核中' },
|
||||
{ value: 2, text: '已通过' },
|
||||
{ value: 3, text: '已拒绝' }
|
||||
]
|
||||
|
||||
const statusText = computed(() => {
|
||||
const option = statusOptions.find(item => item.value === formData.status)
|
||||
return option ? option.text : '请选择合同状态'
|
||||
})
|
||||
|
||||
const rules = {
|
||||
code: [{ required: true, message: '合同编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
|
||||
amount: [{ required: true, message: '合同金额不能为空', trigger: 'blur' }],
|
||||
postId: [{ required: true, message: '岗位ID不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
|
||||
const formTitle = computed(() => {
|
||||
return formType.value === 'create' ? '新增合同' : '修改合同'
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.type) {
|
||||
formType.value = options.type
|
||||
}
|
||||
if (options.id) {
|
||||
getDemo(options.id)
|
||||
}
|
||||
})
|
||||
|
||||
const getDemo = async (id) => {
|
||||
const res = await DemoContractApi.getDemoContract(id)
|
||||
if (!res || res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
formData.id = data.id
|
||||
formData.code = data.code || ''
|
||||
formData.name = data.name || ''
|
||||
formData.status = Number.isNaN(Number(data.status)) ? 0 : Number(data.status)
|
||||
formData.signDate = data.signDate || ''
|
||||
formData.startDate = data.startDate || ''
|
||||
formData.endDate = data.endDate || ''
|
||||
formData.amount = data.amount !== undefined && data.amount !== null ? String(data.amount) : ''
|
||||
formData.remark = data.remark || ''
|
||||
formData.postId = data.postId !== undefined && data.postId !== null ? String(data.postId) : ''
|
||||
}
|
||||
|
||||
const onStatusChange = (e) => {
|
||||
formData.status = statusOptions[e.detail.value].value
|
||||
}
|
||||
|
||||
const validateForm = async () => {
|
||||
if (!formRef.value || typeof formRef.value.validate !== 'function') {
|
||||
throw new Error('表单未准备就绪')
|
||||
}
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
return true
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const buildPayload = () => {
|
||||
const raw = toRaw(formData)
|
||||
const payload = {
|
||||
...raw,
|
||||
status: Number(raw.status)
|
||||
}
|
||||
if (Number.isNaN(payload.status)) {
|
||||
payload.status = 0
|
||||
}
|
||||
if (payload.amount !== undefined && payload.amount !== null && payload.amount !== '') {
|
||||
const amountNumber = Number(payload.amount)
|
||||
payload.amount = Number.isNaN(amountNumber) ? payload.amount : amountNumber
|
||||
}
|
||||
if (formType.value === 'create') {
|
||||
delete payload.id
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (submitting.value) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await validateForm()
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
|
||||
const payload = buildPayload()
|
||||
const apiFn = formType.value === 'create' ? DemoContractApi.createDemoContract : DemoContractApi.updateDemoContract
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
const result = await apiFn(payload)
|
||||
if (!result || result.code !== 0) {
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: formType.value === 'create' ? '新增成功' : '修改成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 600)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-container {
|
||||
padding: 30rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.submit-btn {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,192 +0,0 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<view class="search-section">
|
||||
<u-search
|
||||
placeholder="请输入合同名称"
|
||||
v-model="queryParams.name"
|
||||
:show-action="true"
|
||||
action-text="搜索"
|
||||
@custom="handleQuery"
|
||||
@search="handleQuery"
|
||||
></u-search>
|
||||
|
||||
<!-- 高级搜索 -->
|
||||
<view class="advanced-search" v-if="showAdvanced">
|
||||
<u-form :model="queryParams" label-width="120rpx">
|
||||
<u-form-item label="合同编号">
|
||||
<u-input v-model="queryParams.code" placeholder="请输入合同编号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同金额">
|
||||
<u-input v-model="queryParams.amount" placeholder="请输入合同金额" type="number" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
|
||||
<view class="search-toggle">
|
||||
<u-button type="info" size="small" @click="showAdvanced = !showAdvanced">
|
||||
{{ showAdvanced ? '收起' : '高级搜索' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<view class="action-bar">
|
||||
<u-button type="primary" icon="plus" text="新增" @click="openForm('create')"></u-button>
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<u-list @scrolltolower="scrolltolower">
|
||||
<u-list-item v-for="item in list" :key="item.id">
|
||||
<u-cell :title="item.name" :label="item.code">
|
||||
<template #value>
|
||||
<view class="item-info">
|
||||
<text class="info-text">状态: {{ getStatusText(item.status) }}</text>
|
||||
<text class="info-text">金额: ¥{{ item.amount || 0 }}</text>
|
||||
<text class="info-text" v-if="item.signDate">签订: {{ sheep.$helper.timeFormat(item.signDate, 'yyyy-mm-dd') }}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" size="mini" @click="openForm('update', item.id)">编辑</u-button>
|
||||
<u-button type="error" size="mini" @click="handleDelete(item.id)">删除</u-button>
|
||||
</view>
|
||||
</template>
|
||||
</u-cell>
|
||||
</u-list-item>
|
||||
<u-loadmore :status="status" />
|
||||
</u-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import DemoContractApi from '@/sheep/api/infra/democontract'
|
||||
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const list = ref([])
|
||||
const showAdvanced = ref(false)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
code: '',
|
||||
name: '',
|
||||
status: '',
|
||||
signDate: [],
|
||||
startDate: [],
|
||||
endDate: [],
|
||||
amount: '',
|
||||
remark: '',
|
||||
postId: ''
|
||||
})
|
||||
const status = ref('loadmore')
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
status.value = 'loading'
|
||||
try {
|
||||
const { data } = await DemoContractApi.getDemoContractPage(queryParams)
|
||||
if (queryParams.pageNo === 1) {
|
||||
list.value = data.list
|
||||
} else {
|
||||
list.value = list.value.concat(data.list)
|
||||
}
|
||||
total.value = data.total
|
||||
if (list.value.length >= total.value) {
|
||||
status.value = 'nomore'
|
||||
} else {
|
||||
status.value = 'loadmore'
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
uni.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const openForm = (type, id) => {
|
||||
let url = `/pages/app/democontract/form?type=${type}`
|
||||
if (id) {
|
||||
url += `&id=${id}`
|
||||
}
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
const [err, res] = await uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?'
|
||||
})
|
||||
if (res && res.confirm) {
|
||||
await DemoContractApi.deleteDemoContract(id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
handleQuery() // 重新加载列表
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
// 根据实际状态值进行映射,这里需要根据字典或枚举值调整
|
||||
const statusMap = {
|
||||
0: '草稿',
|
||||
1: '审核中',
|
||||
2: '已通过',
|
||||
3: '已拒绝'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
const scrolltolower = () => {
|
||||
if (status.value === 'loadmore') {
|
||||
queryParams.pageNo++
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
scrolltolower()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-wrap {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.action-bar {
|
||||
padding: 20rpx;
|
||||
}
|
||||
.list-wrap {
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.item-desc {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,452 +0,0 @@
|
||||
<!-- 签到界面 -->
|
||||
<template>
|
||||
<s-layout title="签到有礼">
|
||||
<s-empty v-if="state.loading" icon="/static/data-empty.png" text="签到活动还未开始" />
|
||||
<view v-if="state.loading" />
|
||||
<view class="sign-wrap" v-else-if="!state.loading">
|
||||
<!-- 签到日历 -->
|
||||
<view class="content-box calendar">
|
||||
<view class="sign-everyday ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<text class="sign-everyday-title">签到日历</text>
|
||||
<view class="sign-num-box">
|
||||
已连续签到 <text class="sign-num">{{ state.signInfo.continuousDay }}</text> 天
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="list acea-row row-between-wrapper"
|
||||
style="
|
||||
padding: 0 30rpx;
|
||||
height: 240rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<view class="item" v-for="(item, index) in state.signConfigList" :key="index">
|
||||
<view
|
||||
:class="
|
||||
(index === state.signConfigList.length ? 'reward' : '') +
|
||||
' ' +
|
||||
(state.signInfo.continuousDay >= item.day ? 'rewardTxt' : '')
|
||||
"
|
||||
>
|
||||
第{{ item.day }}天
|
||||
</view>
|
||||
<view
|
||||
class="venus"
|
||||
:class="
|
||||
(index + 1 === state.signConfigList.length ? 'reward' : '') +
|
||||
' ' +
|
||||
(state.signInfo.continuousDay >= item.day ? 'venusSelect' : '')
|
||||
"
|
||||
>
|
||||
</view>
|
||||
<view class="num" :class="state.signInfo.continuousDay >= item.day ? 'on' : ''">
|
||||
+ {{ item.point }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到按钮 -->
|
||||
<view class="myDateTable">
|
||||
<view class="ss-flex ss-col-center ss-row-center sign-box ss-m-y-40">
|
||||
<button
|
||||
class="ss-reset-button sign-btn"
|
||||
v-if="!state.signInfo.todaySignIn"
|
||||
@tap="onSign"
|
||||
>
|
||||
签到
|
||||
</button>
|
||||
<button class="ss-reset-button already-btn" v-else disabled> 已签到 </button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到说明 -->
|
||||
<view class="bg-white ss-m-t-16 ss-p-t-30 ss-p-b-60 ss-p-x-40">
|
||||
<view class="activity-title ss-m-b-30">签到说明</view>
|
||||
<view class="activity-des">1.已累计签到{{ state.signInfo.totalDay }}天</view>
|
||||
<view class="activity-des">
|
||||
2.据说连续签到第 {{ state.maxDay }} 天可获得超额积分,要坚持签到哦~~
|
||||
</view>
|
||||
<view class="activity-des"> 3.积分可以在购物时抵现金结算的哦 ~~</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到结果弹窗 -->
|
||||
<su-popup :show="state.showModel" type="center" round="10" :isMaskClick="false">
|
||||
<view class="model-box ss-flex-col">
|
||||
<view class="ss-m-t-56 ss-flex-col ss-col-center">
|
||||
<text class="cicon-check-round"></text>
|
||||
<view class="score-title">
|
||||
<text v-if="state.signResult.point">{{ state.signResult.point }} 积分 </text>
|
||||
<text v-if="state.signResult.experience"> {{ state.signResult.experience }} 经验</text>
|
||||
</view>
|
||||
<view class="model-title ss-flex ss-col-center ss-m-t-22 ss-m-b-30">
|
||||
已连续打卡 {{ state.signResult.day }} 天
|
||||
</view>
|
||||
</view>
|
||||
<view class="model-bg ss-flex-col ss-col-center ss-row-right">
|
||||
<view class="title ss-m-b-64">签到成功</view>
|
||||
<view class="ss-m-b-40">
|
||||
<button class="ss-reset-button confirm-btn" @tap="onConfirm">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onReady } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import SignInApi from '@/sheep/api/member/signin';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/app/sign.png');
|
||||
|
||||
const state = reactive({
|
||||
loading: true,
|
||||
|
||||
signInfo: {}, // 签到信息
|
||||
|
||||
signConfigList: [], // 签到配置列表
|
||||
maxDay: 0, // 最大的签到天数
|
||||
|
||||
showModel: false, // 签到弹框
|
||||
signResult: {}, // 签到结果
|
||||
});
|
||||
|
||||
// 发起签到
|
||||
async function onSign() {
|
||||
const { code, data } = await SignInApi.createSignInRecord();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.showModel = true;
|
||||
state.signResult = data;
|
||||
// 重新获得签到信息
|
||||
await getSignInfo();
|
||||
}
|
||||
|
||||
// 签到确认刷新页面
|
||||
function onConfirm() {
|
||||
state.showModel = false;
|
||||
}
|
||||
|
||||
// 获得个人签到统计
|
||||
async function getSignInfo() {
|
||||
const { code, data } = await SignInApi.getSignInRecordSummary();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.signInfo = data;
|
||||
state.loading = false;
|
||||
}
|
||||
|
||||
// 获取签到配置
|
||||
async function getSignConfigList() {
|
||||
const { code, data } = await SignInApi.getSignInConfigList();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.signConfigList = data;
|
||||
if (data.length > 0) {
|
||||
state.maxDay = data[data.length - 1].day;
|
||||
}
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
getSignInfo();
|
||||
getSignConfigList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-box {
|
||||
border-top: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
}
|
||||
|
||||
// 日历
|
||||
.calendar {
|
||||
background: #fff;
|
||||
|
||||
.sign-everyday {
|
||||
height: 100rpx;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border: 2rpx solid rgba(223, 223, 223, 0.4);
|
||||
|
||||
.sign-everyday-title {
|
||||
font-size: 32rpx;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sign-num-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
|
||||
.sign-num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6000;
|
||||
padding: 0 10rpx;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 年月日
|
||||
.bar {
|
||||
height: 100rpx;
|
||||
|
||||
.date {
|
||||
font-size: 30rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.cicon-back {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
// 星期
|
||||
.week {
|
||||
.week-item {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 日历表
|
||||
.myDateTable {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.dateCell {
|
||||
width: calc(750rpx / 7);
|
||||
height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-sign {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
position: relative;
|
||||
|
||||
.is-sign-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.is-sign-image {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-title {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
top: -6rpx;
|
||||
font-size: 20rpx;
|
||||
color: red;
|
||||
}
|
||||
|
||||
// 签到按钮
|
||||
.sign-box {
|
||||
height: 140rpx;
|
||||
width: 100%;
|
||||
|
||||
.sign-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.already-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
width: 520rpx;
|
||||
// height: 590rpx;
|
||||
background: linear-gradient(177deg, #ff6000 0%, #fe832a 100%);
|
||||
// background: linear-gradient(177deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cicon-check-round {
|
||||
font-size: 70rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.score-title {
|
||||
font-size: 34rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #fcff00;
|
||||
}
|
||||
|
||||
.model-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.model-bg {
|
||||
width: 520rpx;
|
||||
height: 344rpx;
|
||||
background-size: 100% 100%;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: var(--ui-BG-Main-TC);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid #ff6000;
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
line-height: normal;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//签到说明
|
||||
.activity-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.activity-des {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.reward {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA4CAYAAAC1+AWFAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUEyNzZEMjZEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUEyNzZEMjVEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuGQ9m8AAAf3SURBVHja7Jxbb9xEFMe9Xu81u6VJL0kqSCToZ0CCNyp4CQ0FqVAkQAgeEE98EPgEwBOCSoCEQPTCG30BVSpfAaQ2KQ1tc4Fm77v2cv7jmc3UGdvjsTdrJEY68m527Zn5zZlzszeF8XhspWl37/xhZdwK/Di2jqA9ufqM9ndtKz+tTvIpSYdkl7+u52h8lpMjUFdIzvH3VZIPSM6SrHOAM292DkHJ7Rz/rP4/rGhQuQNm5xxUroAVNm//PhUnQ7JEco9LGlBy+znGhsX1O1vNajYbjwm1F0l+JfmN5BeSlwKfx4KybTtWw6TriXao39xpFga8v98q08tPSN7Dn0yvVSqVrEqlbBUKBQvxX78/sIbDoenlxhzs6ySDPNmsj0k+SguqWq0wUGwV6Yj35XIpTXD7Csk3eTPw76c5GUAARtUqlVTA0C5wOzd7WLQF19JoFEAASFRLCQwa9hXJ2rQj+GdJ3sCcQj4vkrw7TVAyMLTBwMiGHSP5luQLEjfkO0O+ZW+ZGPjnuIE8YQoDEyyVnIkdmmaDQxgOR+QU+mku85DkZe5RE23D19KCguYcBSjhEHxNLStBarZTJBdNtuGcqlNVx6q4CBo1iwavipDjsUk6Rdq+I0u1brZdkKpCrDVMYLlBUMVikQ0mGM4EBydWeiYpiaLfWq1GwEbKUAwQPc+Vz3NTlWgEqHq9xo7BpoKVpwbNDwtNbHto9XpdAjaOXWBtWNhWAOV53lQnBiPdarWs+fnjmRr/MLtVLjvUp2O57tCK2wwJ4qxCyECyhXX//n3r3tZWxp4yel4+pPiJRMHydAbR7XYzm5Trutbu3p41pFjq0aNHmV0XY0ywqJ4JrIoOKEwwq7a9s2M5RYe5/+3tnUwXIQGwSlJYyKfeMgVlkprAFu7t/c3OLZfLLLhst9vGmUEKYG/x+WvBQgT7ZTDfE8ZPB5RuCiO33d095o0cx2HhCeThw+1UAbEusIAXbPL5r8XBWuf5UVP2JNgWGLzrelMBhT72yFaVCFSRBK6+SNux1+sxmSYwaDQ0GfOTPGaTc1gPyw2R3nwdTJoRMiCow4U6HXNQKOJ1uz0KAvssGR4MBuw4Gg2ZHyqX/O0HzRKTwHcQw/mhCwLisj8x9EVH9FetViOhYTurkm+EQZgXlAqahf4wvtFoJGsaAshLJD/IcdYaJ1mSV9uvM/mD6fX6oaAANE6jcO6dOxtsUEVKP+yCzVINaJJDAwckTEAu/mG1cRyNXBZlQ8vanY7lkYa77shaWVmJhYVxiSQ7OB6ARLCKz6HNtVqVaR364+Mocy6vkvwEzUKR/6blF/snoDB4ROyixLu/3wodUKMxp5XeQLs2NzdZtGwXAazABolzZZHH4SGgJMCAjF0CaJClpSWr2Wxqb/NWS+0s5Dq+r2Eu30GePJZNkuehWSsyKFmrsPKYWFwqoZsHQlNWV1etra2/2ORlUGF5XhGLxaFiXK5XsM6cXmZbKEm+iPOD2UewXz+tI013SqRdfTmifwqMYOA3SO7GlTUOJ9AHBjLJwyXYamfOLDO7k+Q88c3lpcVEoCYaqkjThH3USI3AZ8PmLz7klcIJcRhFWRWx91VJtDCiSRPbpcVFZqy9iLxNngBGsUigTLytanyYS/Bavn0cMacjadWQ87knQodr3OoPxEm+i+2xFfG3il/qUAEzqVDimsePP0HG2o2EJbRibq7OPKYJqKBxlz2hvIAHc55UIIQ3vBaMs77nVcK2TBkns5WNAQZNTAoM13Y1qhjCrZuACoYNKlAHc+1OlINzuMi5KIPSK5xkS1wEHgzeQUfDkgLr9roJwHanBgoaFfCALc7hSly6A5V7h2RfqCc6hSofxEDZAOt1+4e2GzTN46/F9hQrr5u0q0AdhEKHzQGCXym22ufzv6abSCNivRzMC4O5ol+udZTA9Ap9AxZrjflWw7VOnzrJPF61WmNwvIkJsLW1K9g/dod4LCDchk5eXhYRe5JKaax6IA7DILDqJvZEBJ0O+br5hXlmxEU7eXKBJt2gnPEf2iIdNhlkEY1Gw6D6Opgshka40jcpK8dWUYXhN2mwEdCWEwsL1rFjTeV1EFosLp5ixv3BgwcMmsndueXlZV9L9UridqoafFwIYNKQ0509+3TU40WThpxtdXWFwzKrwWdxtykBrPFkb2dRdweApK1erxvDCltoP2IPv8eQGJbwiOK+4YxuCabS/jDNQgkINldDwSNhFVWBIaDJ/YatWlaqn4UWAYbqoTh8FZ8Fxlo0gdUOrg6LgxSxjsruIC5L+VyVUVNBYZmCMkYrMI0KLGrLxOMhzN9RlTqCEhUYjsfjI9MoPyAeKNMl1bgVzzmg6P+diWbd5BXUS3IFVbFV8XzWXBiwqGg+ac0+rESs6bGxU1I9nxVn4G9FnSylR1e13ElIpK0DzBQUb0hh3iS5nkZ7s3i0+zqHZdR0csmUoJBevJ0WVCaweLuYZjBRwFKC8jioH7OYZCawSLsGJLg5i/tswWwX79fp8wKE27cbKmDI/YRDwBHvFaBw7px0vUJEvxesHD7aLaBd5QPc4H+6jff87+I7yFnOq4DB7eMuTLvdYbfuFWEAzjnPryE3Zb9pzIPSUUzxtzvLJH9a4b+hqfPJvKB5zRsccidlv+awZvyzX11gOqDMVvU/9LPf0C15FKBmarOmACw3oPICSwb2OQlu4+Cxv8/yBCoPNitsAcdWDv9Vwb8CDACdCFE+P8dk8gAAAABJRU5ErkJggg==');
|
||||
width: 75rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.rewardTxt {
|
||||
width: 74rpx;
|
||||
height: 32rpx;
|
||||
background-color: #f4b409;
|
||||
border-radius: 16rpx;
|
||||
font-size: 20rpx;
|
||||
color: var(--ui-BG-Main-TC);
|
||||
line-height: 32rpx;
|
||||
text-align: center;
|
||||
padding: 2rpx;
|
||||
}
|
||||
|
||||
.venus {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDQ3N0E3RkJEMDFCMTFFODhGODVBOTc1NEM5Nzg2NzkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDQ3N0E3RkFEMDFCMTFFODhGODVBOTc1NEM5Nzg2NzkiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnZClGIAAAT7SURBVHja7JtLcxtFEMdnn1rJUhIrTiS7KLsK/EXwNck3gCMcuUFx4QvACW48buHGMZxJPkiowpjC2LElcKzXSqul/6OdoOB9zezsCCimqmvXtdqd+U33dPf0rq2Tn54zg81KjrGuB75x8FbuddsQWIvkS5IxySA5b5no2DUE94TkKPk7IHmf5JDkUQJdW7MNw623o+Ra698KmAdnDNLeIJwRSHvDcLVDVnYynU771fnLl9eFcLZts+VymQf5iJ6pzfFYJeKgT/IZyTskXdWOPM9jjYbPLMticRyz2Sxk8/lc9XFDksckH1IcDKtq8FOSD6rMIuCCoPHXrBIk/qYDC0MlyO1kTBOSj6uuwXerwPn+63DrrdFo8OsV2ns6nEy3Chwg8lpFyK4OwNrgNGpSvxfFoDzP5etJR8PzsiYETmk+X5BjmpkBrHPGU109TeKqv5X3rT3QQ3ObaPDGRjIZXWZpol+b/cebUUA4iuHw938UoNbk9+zsjP16eqoV4JfjH1uqgLjxe10DiaKIDYZDNqfU7OrqSifjkzxIuwDubV2juLi8ZK7j8oT74uJSJ+BRHqQtC6cS/7A9wtrDvb7v84A9Go2UMyQZSFsWrmz6td4GgyF38a7r8lgGefHiQjnjkYG064ZDmjWktecRnEOCDa9DpjqdTrnUDSk2vJXgsHGdTKa0t5vx/V0Yhvy4WMx5hdf3VqYJDQqTxW+QdmECPM8h8flvPPRFR/QXBEEuKEw9Yz/5AyoDtBkei5zriyw4pGVFmoOHPD7+mQ/ccR1mWzZpyuIacx2Hgzl0FJkIjjBTHBeLiO6LuDZH4zFbRkt63oLt7+8XAmJcIhFP0eTn2C9CgzBTrPjUp7XbW6VSJGjx5OSEBhsz2wGkxc0R967LuukuSWKaFEwMnXJQSL/fZ51Op/QSuL5OdVjY7beFBq2sAlHZ/A8aOTg4YKenv/EBr8Nl5ZUO6jPJRGCg0dJie/d3WbPZlMpPMwpZsN0Y2sOVb7PcOzou22CGe3u7fB3J3Cd+udvvScG9soT0Kt13tAZj4UVRwHmWtZClcj+azX6vxx0GN8ECUFyHjnsEp+KlM8b3TBTKBCDqkA/SIFV20jCbO3duk8OIcgHF7G9ttbinVYFLcTBgeAAP+vc4mAkJVywLibARpZvOjWWA36rApYSI1+DSMhltkJPpRGIyJrXAZeWiWiCnk9kNU4RGl8m5MN1VLFzwWKobLm83kQtZbnMb8lgYJ2aIYH//3g73lEHQ5ECAXYUSu7QWZeCKqmoCcqSyPkQgd8lHbne3uSMRbWenSwNtU476BxtT9oJQOZ3OKKloq6SmmXBlyoZKb3nG4wnXyt1ul9261UkN9ggjvd497mDOz885KGN3pfvKg1OuixY15JCHh2/ymFjUms2AMqD9BFB/qwUQg5ZtrVY9b7H/LxtmZSCbaCr9KgGmpEeG6qpzM2tQBHudb5eKNLfKiUMzgAIyL6uRreHklB9qX4MDlQfLpHUV4AY6AB+rzl4ZyIqa+0aHiX6UlDTwMcK2CqQoEGmEE5+RfFK4N636vWjKh0Cp5ceS38k8JXko8yHQ7W7PXKBPBvYwGegNF4/q12g05mV7HXClqgs1ffEr+/LmaTIx0nCb+uI3U5M64Tadi5aBrBXORLKdB1k7nKndhID8GqUaErze/coEXJ1OJm9CY2bw3wr+FGAAoa6PIUihovYAAAAASUVORK5CYII=');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin: 10rpx auto;
|
||||
}
|
||||
|
||||
.venusSelect {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzkwRkI4NEFEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzkwRkI4NDlEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkX00M0AAAfVSURBVHjaxFpdcBNVFD67STbppn9poU0LtSBYykBlgKrjqKOtihbq8ICKILyJwxM+6fjCk2/6pC+O4hs+6fgAbWFw1OID+ICMAwwKlRmKLW3Tpvlrskk3m13Pvdm0NNn//HA73+ymd3Pv+e4595xzT5bJ3DgJNWyMelUqNaD36a8N+9kaEeMR3yAERES952sxsbtya2lIbgQxqH72IT5EbEW8pZKuHkGlugyLyT3aBtW+qpJkKb/qgEeMIAYNnhlUn+Edz2NuokqtNVdTTbqVyhO0Q67qJMt2MnXbTq/cp+9+ZkyOYYFxN4EiLaF5SbokccyKkWRS1z4we4ZDfIE4hmhxvJLNu8HTtg85ekGRRcjOXwIp9pfT4aKIs4iP+f4zYrl78HPEqbLMJNAPXHBI/SQjSTd+PoD3LpCi15wMGVBlSiM+NfSihJ8Jjlt4Rheu5n7wtL+B93IJPO37aH8Z45+ohBdtKUtz7a+jJLK+/dN+BTX5p5MpWh6HF6XNE9iLwr9mSG6VZP65bPR6NVI1BwQZF3BtA+Bq2oG3Pt3HFCVnfUHaX6XQHCeXgVz8Nojz4+SDzTgo2yfIBV9G89utzi5DtRvDcnQ+JSciyd+rH+hdjb22tFOp5mreCUrocg0CPet5LATJvHbldWSiGllIzZpdeR2ZqFPtZTNJSIQeQGv3DucEbcrLmkRSXvP/cs4RZm6Ow/T1McffpyiSJTZ+jDfOZFDlOuARo5p9aKJ2IYkpCN+9AmLsIcSm/3Y0BkWpPCPRX9/nDVI1BTTAI0YRA9r99gWbv3MF90MGvG4WQrfHnRMslWcQMRL55SivbaJk064FjxhFDGj0gad5u22zkrMZiExcBQ7JcW4GlhfuQ3L+gSMTzc9fItcgYiTy8xHeTIOGmnMHtoEn2G971cP3MAUTk+B2MeBBkh4kGbp92ZEGyfxEDj1NLl56j9fbg4U9N6C179zNPZhK7cV7yRZkSYTI3atIjgUXywCLINf03L8gRGZtj0dA5CDyaMg5SPZk+OLhFZLMwtg7hTLDKGJAM09s6QGuY6+upxLTKcgkFmE5FUdFIYQEiHgv4VURl4BjgZon0SA9EeKKi1kZliU8Nnn84OabwIPw+huB8zdR+OqbwdcYMAwB4ux1yEYmtLp+I5WBdft/EApx8Cs9cu6mblyxXXTl9FpOFGDyjzGQMwlwIQlUEDAMagqvhBTVHl4ZplDezpsq+UdOTkMuIcByfBYElCWHmsgpLHQ/NwTe+gaTBH0XWkgGpPgDrfLHl4gTTOj8IVLdTqkF2aIslwF+2zCeGDjzIJ5OwtSVc6Cko9QMCUmWyZMiZJn8cI8E7Lwm11wJOTypBPuHoCG4yVrgz2VBmFBj69pGTvv1hVSN0XSxHE8LRUbaW9G01wdPvHgQQtcugJwMU5LFpIrWDjXMUGIsMNRR5DwcBPcMQV1L0NKchYHIkU2WkiVrTvgT6XEjyN/TY08R5KyAaWc6n3tagMvjhuCzQ+jlOmylVPRZjw/an9kPdYF1lucjIPLJmERoyP9j+8GflIIXPYW4XOKVJAmWZ27Y8nAMaq5t9yB4WjeumJ4hOaIErx/W978JXH2TbY9K5ZNKMhzC5dSjyTapQ5IyFxozvLJGz9Hp/Am+Y7utH8kan+yDhYVp8lVgdDWX33f+ji3gruNpnLPTxNl/8vKtbeTAeKDj0DmhuGShT3Jxkp4guGCv5cnT0QX0hgq4dOnBSshIxxehUbZJbu4OSJEpbXJvnxf0zoP6JMP/keVGTfZYEiCDQlttmXjE1hlTnJ3A+Ketuc53RwSz86AuSXFxitYzueBWUyGWE9E1pqiozoRhXdQJMLAaEyUMMdJyGp2Ux4Lm7iG5h5rkNhweFUpLFtonZANNTmOA3WzmFiGLGY3XlSel0DpOCzRs6gWXj4fkgwnIhKZIcKDhgsTLTCICfMs683gb1tbchiNjgt0TfYFkym7JQkyl8nkoixkMesiGzT1Q19q20t+0dTvwnU/A0iQSDYeoNinBQLOTE/2BjUcvCE5rMoJ2XcSYYBr3FOPmoHHLU+APdtJMpvg7bp8XAr19mKJ1weK9u5CJxUDpMt+HxfJ2HbsoVLzwa1aT4fw8dD3/Au43lv7YYjQF19gAHXv6UYMJa7UepQZFJzMT9fp9lJidorCvgbfkSRXbBB2UDRX5MdREnZYNwZECRcxQXLUnl8s5KPw6MNFsdBFzzdaaE8xGwrUx0eXZORrw3c1NNdEk0ZwUi2OQn7fvZBz9fEZKDjNzFLqn7dYApnVtNtKvecx5oxVfHCsmSt4ts/0rrxiO5NO6jvUWyC0guZgT+SPllu4Jzjr9AT0bjqKWQ1qH0RWQfvKcwzm+s6BB01X6RC1pHIf82w02NRmnsng7WjX28iJqLu5Ec4XXSE5XYg+S91A+UlHS/H2riXfq1n3N8mM2HKOOgutsodkNqZKIMxGQokvFw40jhnFMoZZ70CzyrpLd2S0kb00Oa5KMJDC8LAHL4QEmK4HGKYaSq+/bJFTyZ/GyX+VK3pzUStA1SRJrkTNZrWHG1e8IGuMZt5eqrUH9U8iwUbVci1w1BKnW65RWSVaVnBomAKoI3E9IQEEipX3jap9Q1hxmBElBocp/AmIYcQaRQSQQ36r/E8odvepOxoa5khfRT1pf+8q0/wUYAFU/P0XyeZQPAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 36rpx;
|
||||
font-family: 'Guildford Pro';
|
||||
}
|
||||
|
||||
.item {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 130rpx;
|
||||
}
|
||||
|
||||
.reward {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA4CAYAAAC1+AWFAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUEyNzZEMjZEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUEyNzZEMjVEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuGQ9m8AAAf3SURBVHja7Jxbb9xEFMe9Xu81u6VJL0kqSCToZ0CCNyp4CQ0FqVAkQAgeEE98EPgEwBOCSoCEQPTCG30BVSpfAaQ2KQ1tc4Fm77v2cv7jmc3UGdvjsTdrJEY68m527Zn5zZlzszeF8XhspWl37/xhZdwK/Di2jqA9ufqM9ndtKz+tTvIpSYdkl7+u52h8lpMjUFdIzvH3VZIPSM6SrHOAM292DkHJ7Rz/rP4/rGhQuQNm5xxUroAVNm//PhUnQ7JEco9LGlBy+znGhsX1O1vNajYbjwm1F0l+JfmN5BeSlwKfx4KybTtWw6TriXao39xpFga8v98q08tPSN7Dn0yvVSqVrEqlbBUKBQvxX78/sIbDoenlxhzs6ySDPNmsj0k+SguqWq0wUGwV6Yj35XIpTXD7Csk3eTPw76c5GUAARtUqlVTA0C5wOzd7WLQF19JoFEAASFRLCQwa9hXJ2rQj+GdJ3sCcQj4vkrw7TVAyMLTBwMiGHSP5luQLEjfkO0O+ZW+ZGPjnuIE8YQoDEyyVnIkdmmaDQxgOR+QU+mku85DkZe5RE23D19KCguYcBSjhEHxNLStBarZTJBdNtuGcqlNVx6q4CBo1iwavipDjsUk6Rdq+I0u1brZdkKpCrDVMYLlBUMVikQ0mGM4EBydWeiYpiaLfWq1GwEbKUAwQPc+Vz3NTlWgEqHq9xo7BpoKVpwbNDwtNbHto9XpdAjaOXWBtWNhWAOV53lQnBiPdarWs+fnjmRr/MLtVLjvUp2O57tCK2wwJ4qxCyECyhXX//n3r3tZWxp4yel4+pPiJRMHydAbR7XYzm5Trutbu3p41pFjq0aNHmV0XY0ywqJ4JrIoOKEwwq7a9s2M5RYe5/+3tnUwXIQGwSlJYyKfeMgVlkprAFu7t/c3OLZfLLLhst9vGmUEKYG/x+WvBQgT7ZTDfE8ZPB5RuCiO33d095o0cx2HhCeThw+1UAbEusIAXbPL5r8XBWuf5UVP2JNgWGLzrelMBhT72yFaVCFSRBK6+SNux1+sxmSYwaDQ0GfOTPGaTc1gPyw2R3nwdTJoRMiCow4U6HXNQKOJ1uz0KAvssGR4MBuw4Gg2ZHyqX/O0HzRKTwHcQw/mhCwLisj8x9EVH9FetViOhYTurkm+EQZgXlAqahf4wvtFoJGsaAshLJD/IcdYaJ1mSV9uvM/mD6fX6oaAANE6jcO6dOxtsUEVKP+yCzVINaJJDAwckTEAu/mG1cRyNXBZlQ8vanY7lkYa77shaWVmJhYVxiSQ7OB6ARLCKz6HNtVqVaR364+Mocy6vkvwEzUKR/6blF/snoDB4ROyixLu/3wodUKMxp5XeQLs2NzdZtGwXAazABolzZZHH4SGgJMCAjF0CaJClpSWr2Wxqb/NWS+0s5Dq+r2Eu30GePJZNkuehWSsyKFmrsPKYWFwqoZsHQlNWV1etra2/2ORlUGF5XhGLxaFiXK5XsM6cXmZbKEm+iPOD2UewXz+tI013SqRdfTmifwqMYOA3SO7GlTUOJ9AHBjLJwyXYamfOLDO7k+Q88c3lpcVEoCYaqkjThH3USI3AZ8PmLz7klcIJcRhFWRWx91VJtDCiSRPbpcVFZqy9iLxNngBGsUigTLytanyYS/Bavn0cMacjadWQ87knQodr3OoPxEm+i+2xFfG3il/qUAEzqVDimsePP0HG2o2EJbRibq7OPKYJqKBxlz2hvIAHc55UIIQ3vBaMs77nVcK2TBkns5WNAQZNTAoM13Y1qhjCrZuACoYNKlAHc+1OlINzuMi5KIPSK5xkS1wEHgzeQUfDkgLr9roJwHanBgoaFfCALc7hSly6A5V7h2RfqCc6hSofxEDZAOt1+4e2GzTN46/F9hQrr5u0q0AdhEKHzQGCXym22ufzv6abSCNivRzMC4O5ol+udZTA9Ap9AxZrjflWw7VOnzrJPF61WmNwvIkJsLW1K9g/dod4LCDchk5eXhYRe5JKaax6IA7DILDqJvZEBJ0O+br5hXlmxEU7eXKBJt2gnPEf2iIdNhlkEY1Gw6D6Opgshka40jcpK8dWUYXhN2mwEdCWEwsL1rFjTeV1EFosLp5ixv3BgwcMmsndueXlZV9L9UridqoafFwIYNKQ0509+3TU40WThpxtdXWFwzKrwWdxtykBrPFkb2dRdweApK1erxvDCltoP2IPv8eQGJbwiOK+4YxuCabS/jDNQgkINldDwSNhFVWBIaDJ/YatWlaqn4UWAYbqoTh8FZ8Fxlo0gdUOrg6LgxSxjsruIC5L+VyVUVNBYZmCMkYrMI0KLGrLxOMhzN9RlTqCEhUYjsfjI9MoPyAeKNMl1bgVzzmg6P+diWbd5BXUS3IFVbFV8XzWXBiwqGg+ac0+rESs6bGxU1I9nxVn4G9FnSylR1e13ElIpK0DzBQUb0hh3iS5nkZ7s3i0+zqHZdR0csmUoJBevJ0WVCaweLuYZjBRwFKC8jioH7OYZCawSLsGJLg5i/tswWwX79fp8wKE27cbKmDI/YRDwBHvFaBw7px0vUJEvxesHD7aLaBd5QPc4H+6jff87+I7yFnOq4DB7eMuTLvdYbfuFWEAzjnPryE3Zb9pzIPSUUzxtzvLJH9a4b+hqfPJvKB5zRsccidlv+awZvyzX11gOqDMVvU/9LPf0C15FKBmarOmACw3oPICSwb2OQlu4+Cxv8/yBCoPNitsAcdWDv9Vwb8CDACdCFE+P8dk8gAAAABJRU5ErkJggg==');
|
||||
width: 75rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.on {
|
||||
color: #f4b409;
|
||||
}
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<view class="content-container">
|
||||
<span v-for="(part, index) in formattedContent" :key="index"
|
||||
@click="handleClick(part)"
|
||||
:class="{'highlight-number': part.isNumber, 'phone-number': part.isPhone}">
|
||||
{{ part.text }}
|
||||
</span>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HighlightNumber',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedContent() {
|
||||
const phoneRegex = /(1[3-9]\d{9})/g;
|
||||
const numberRegex = /(\d+)/g;
|
||||
let text = this.content;
|
||||
let result = [];
|
||||
let match;
|
||||
|
||||
// Step 1: 提取手机号
|
||||
while ((match = phoneRegex.exec(text)) !== null) {
|
||||
if (match.index > 0) {
|
||||
const before = text.slice(0, match.index);
|
||||
result.push(...this.splitAndPush(before, false, false));
|
||||
}
|
||||
result.push({ text: match[0], isNumber: true, isPhone: true });
|
||||
text = text.slice(match.index + match[0].length);
|
||||
}
|
||||
|
||||
// Step 2: 提取普通数字
|
||||
while ((match = numberRegex.exec(text)) !== null) {
|
||||
if (match.index > 0) {
|
||||
const before = text.slice(0, match.index);
|
||||
result.push(...this.splitAndPush(before, false, false));
|
||||
}
|
||||
result.push({ text: match[0], isNumber: true });
|
||||
text = text.slice(match.index + match[0].length);
|
||||
}
|
||||
|
||||
// Step 3: 添加剩余文本
|
||||
if (text.length > 0) {
|
||||
result.push(...this.splitAndPush(text, false, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
splitAndPush(str, isNumber = false, isPhone = false) {
|
||||
return str.split('').map(char => ({ text: char, isNumber, isPhone }));
|
||||
},
|
||||
handleClick(part) {
|
||||
if (part.isPhone) {
|
||||
this.$emit('phone-click', { phoneNumber: part.text });
|
||||
} else if (part.isNumber) {
|
||||
this.$emit('number-click', { number: part.text });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.highlight-number {
|
||||
color: #ff5722;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.phone-number {
|
||||
color: #007AFF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -1,227 +0,0 @@
|
||||
<!-- 商品分类列表 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/category" title="分类">
|
||||
<view class="s-category">
|
||||
<view class="three-level-wrap ss-flex ss-col-top">
|
||||
<!-- 商品分类(左) -->
|
||||
<view class="side-menu-wrap" :style="[{ top: Number(statusBarHeight + 88) + 'rpx' }]">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<view
|
||||
class="menu-item ss-flex"
|
||||
v-for="(item, index) in state.categoryList"
|
||||
:key="item.id"
|
||||
:class="[{ 'menu-item-active': index === state.activeMenu }]"
|
||||
@tap="onMenu(index)"
|
||||
>
|
||||
<view class="menu-title ss-line-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 商品分类(右) -->
|
||||
<view class="goods-list-box" v-if="state.categoryList?.length">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<image
|
||||
v-if="state.categoryList[state.activeMenu].picUrl"
|
||||
class="banner-img"
|
||||
:src="sheep.$url.cdn(state.categoryList[state.activeMenu].picUrl)"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<first-one v-if="state.style === 'first_one'" :pagination="state.pagination" />
|
||||
<first-two v-if="state.style === 'first_two'" :pagination="state.pagination" />
|
||||
<second-one
|
||||
v-if="state.style === 'second_one'"
|
||||
:data="state.categoryList"
|
||||
:activeMenu="state.activeMenu"
|
||||
/>
|
||||
<uni-load-more
|
||||
v-if="
|
||||
(state.style === 'first_one' || state.style === 'first_two') &&
|
||||
state.pagination.total > 0
|
||||
"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '点击查看更多',
|
||||
}"
|
||||
@tap="loadMore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import secondOne from './components/second-one.vue';
|
||||
import firstOne from './components/first-one.vue';
|
||||
import firstTwo from './components/first-two.vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash-es';
|
||||
import { handleTree } from '@/sheep/helper/utils';
|
||||
|
||||
const state = reactive({
|
||||
style: 'second_one', // first_one(一级 - 样式一), first_two(二级 - 样式二), second_one(二级)
|
||||
categoryList: [], // 商品分类树
|
||||
activeMenu: 0, // 选中的一级菜单,在 categoryList 的下标
|
||||
|
||||
pagination: {
|
||||
// 商品分页
|
||||
list: [], // 商品列表
|
||||
total: [], // 商品总数
|
||||
pageNo: 1,
|
||||
pageSize: 6,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
const { safeArea } = sheep.$platform.device;
|
||||
const pageHeight = computed(() => safeArea.height - 44 - 50);
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
|
||||
// 加载商品分类
|
||||
async function getList() {
|
||||
// API已被移除,返回空数据
|
||||
state.categoryList = [];
|
||||
}
|
||||
|
||||
// 选中菜单
|
||||
const onMenu = (val) => {
|
||||
state.activeMenu = val;
|
||||
if (state.style === 'first_one' || state.style === 'first_two') {
|
||||
state.pagination.pageNo = 1;
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
getGoodsList();
|
||||
}
|
||||
};
|
||||
|
||||
// 加载商品列表
|
||||
async function getGoodsList() {
|
||||
// 加载列表
|
||||
state.loadStatus = 'loading';
|
||||
// API已被移除,返回空数据
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
|
||||
// 加载更多商品
|
||||
function loadMore() {
|
||||
if (state.loadStatus === 'noMore') {
|
||||
return;
|
||||
}
|
||||
state.pagination.pageNo++;
|
||||
getGoodsList();
|
||||
}
|
||||
|
||||
onLoad(async (params) => {
|
||||
await getList();
|
||||
|
||||
// 首页点击分类的处理:查找满足条件的分类
|
||||
const foundCategory = state.categoryList.find((category) => category.id === Number(params.id));
|
||||
// 如果找到则调用 onMenu 自动勾选相应分类,否则调用 onMenu(0) 勾选第一个分类
|
||||
onMenu(foundCategory ? state.categoryList.indexOf(foundCategory) : 0);
|
||||
});
|
||||
|
||||
function handleScrollToLower() {
|
||||
loadMore();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.s-category {
|
||||
:deep() {
|
||||
.side-menu-wrap {
|
||||
width: 200rpx;
|
||||
height: 100%;
|
||||
padding-left: 12rpx;
|
||||
background-color: #f6f6f6;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
transition: all linear 0.2s;
|
||||
|
||||
.menu-title {
|
||||
line-height: 32rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-left: 28rpx;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 64rpx;
|
||||
height: 12rpx;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--ui-BG-Main-gradient),
|
||||
var(--ui-BG-Main-light)
|
||||
) !important;
|
||||
position: absolute;
|
||||
left: -64rpx;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&.menu-item-active {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 0 0 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
background-color: #fff;
|
||||
width: calc(100vw - 200rpx);
|
||||
padding: 10px;
|
||||
margin-left: 200rpx;
|
||||
}
|
||||
|
||||
.banner-img {
|
||||
width: calc(100vw - 130px);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +0,0 @@
|
||||
<!-- 分类展示:first-one 风格 -->
|
||||
<template>
|
||||
<view class="ss-flex-col">
|
||||
<!-- 商品组件已被移除 -->
|
||||
<view class="empty-message" style="text-align: center; padding: 50px; color: #999;">
|
||||
暂无商品数据
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,66 +0,0 @@
|
||||
<!-- 分类展示:first-two 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<view class="ss-flex flex-wrap">
|
||||
<view class="goods-box" v-for="item in pagination?.list" :key="item.id">
|
||||
<view @click="sheep.$router.go('/pages/goods/index', { id: item.id })">
|
||||
<view class="goods-img">
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="goods-content">
|
||||
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.name }}</view>
|
||||
<view class="goods-price">¥{{ fen2yuan(item.price) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: calc((100% - 20rpx) / 2);
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 246rpx;
|
||||
border-radius: 10rpx 10rpx 0px 0px;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 20rpx 4rpx rgba(199, 199, 199, 0.22);
|
||||
padding: 20rpx 0 32rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #e1212b;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 1) {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<!-- 分类展示:second-one 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 一级分类的名字 -->
|
||||
<view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30">
|
||||
<view class="title-line-left" />
|
||||
<view class="title-text ss-p-x-20">{{ props.data[activeMenu].name }}</view>
|
||||
<view class="title-line-right" />
|
||||
</view>
|
||||
<!-- 二级分类的名字 -->
|
||||
<view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20">
|
||||
<view
|
||||
class="goods-item"
|
||||
v-for="item in props.data[activeMenu].children"
|
||||
:key="item.id"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/list', {
|
||||
categoryId: item.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFill" />
|
||||
<view class="ss-p-10">
|
||||
<view class="goods-title ss-line-1">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
activeMenu: [Number, String],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-box {
|
||||
.title-line-left,
|
||||
.title-line-right {
|
||||
width: 15px;
|
||||
height: 1px;
|
||||
background: #d2d2d2;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: calc((100% - 20px) / 3);
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:nth-of-type(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
width: calc((100vw - 140px) / 3);
|
||||
height: calc((100vw - 140px) / 3);
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
color: $red;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,547 +1,32 @@
|
||||
<!-- 首页 - 重新设计 -->
|
||||
<template>
|
||||
<view class="container">
|
||||
<s-layout
|
||||
title="首页"
|
||||
navbar="custom"
|
||||
tabbar="/pages/index/index"
|
||||
onShareAppMessage
|
||||
>
|
||||
<!-- 用户信息区域 - 使用 uview-plus Card 组件 -->
|
||||
<u-card
|
||||
v-if="isLogin"
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="user-info-section">
|
||||
<view class="user-avatar">
|
||||
<s-avatar
|
||||
:src="userInfo.avatar"
|
||||
:nickname="userInfo.nickname || userInfo.username"
|
||||
:size="60"
|
||||
:bg-color="getAvatarBgColor(userInfo.nickname || userInfo.username)"
|
||||
@click="handleUserClick"
|
||||
/>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<u-text
|
||||
:text="userInfo.nickname || '用户'"
|
||||
size="18"
|
||||
color="#333"
|
||||
bold
|
||||
/>
|
||||
<u-text
|
||||
:text="userInfo.mobile || '欢迎使用系统'"
|
||||
size="14"
|
||||
color="#999"
|
||||
margin="6rpx 0 0 0"
|
||||
/>
|
||||
</view>
|
||||
<view class="user-actions">
|
||||
<u-button
|
||||
text="个人中心"
|
||||
size="mini"
|
||||
type="primary"
|
||||
plain
|
||||
@click="navigateTo('/pages/index/user')"
|
||||
margin="0 0 10rpx 0"
|
||||
/>
|
||||
<u-button
|
||||
text="退出登录"
|
||||
size="mini"
|
||||
type="error"
|
||||
plain
|
||||
@click="handleLogout"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<!-- 未登录提示区域 - 使用 uview-plus Card 和 Button -->
|
||||
<u-card
|
||||
v-else
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="login-prompt">
|
||||
<view class="prompt-content">
|
||||
<u-text text="欢迎使用" size="20" color="#333" bold />
|
||||
<u-text
|
||||
text="请先登录以使用完整功能"
|
||||
size="14"
|
||||
color="#999"
|
||||
margin="10rpx 0 30rpx 0"
|
||||
/>
|
||||
<u-button
|
||||
text="立即登录"
|
||||
type="primary"
|
||||
size="normal"
|
||||
@click="goToLogin"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<!-- 功能模块入口 - 使用 uview-plus Grid 组件 -->
|
||||
<u-card
|
||||
v-if="isLogin"
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="function-modules">
|
||||
<view class="section-title">
|
||||
<u-text text="功能模块" size="16" color="#333" bold />
|
||||
</view>
|
||||
<u-grid :col="3" :border="false">
|
||||
<u-grid-item
|
||||
v-for="module in functionModules"
|
||||
:key="module.id"
|
||||
@click="handleModuleClick(module)"
|
||||
>
|
||||
<view class="module-content">
|
||||
<view class="module-icon" :style="{ backgroundColor: module.color + '20' }">
|
||||
<u-icon :name="module.icon" size="24" :color="module.color" />
|
||||
</view>
|
||||
<u-text
|
||||
:text="module.name"
|
||||
size="12"
|
||||
color="#666"
|
||||
margin="10rpx 0 0 0"
|
||||
/>
|
||||
</view>
|
||||
</u-grid-item>
|
||||
</u-grid>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
</s-layout>
|
||||
<view class="page bg-w pt30">
|
||||
<up-grid :border="false" :col="gridCol">
|
||||
<up-grid-item class="mb25 mt25" v-for="(item, listIndex) in list" :key="listIndex" @click="goSystem(item.url)">
|
||||
<image style="width: 80px; height: 80px" :src="`/static/images/menus/${item.icon}.png`"></image>
|
||||
<text class="grid-text">{{ item.name }}</text>
|
||||
</up-grid-item>
|
||||
</up-grid>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad, onPullDownRefresh, onShow } from '@dcloudio/uni-app';
|
||||
import { computed, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar({
|
||||
fail: () => {},
|
||||
});
|
||||
import { reactive, ref, computed, onMounted } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
let list = reactive([
|
||||
{ url: '/pages/lims/index/index', name: '设备管理系统', icon: 'dailyCheck' },
|
||||
{ url: '/pages/analysis/index/index', name: '化学分析系统', icon: 'maintain' }
|
||||
])
|
||||
|
||||
// 跳转到登录页
|
||||
function goToLogin() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}
|
||||
// const sysMenus = computed(() => nx.$store('user').sysMenus)
|
||||
function goSystem(url) {
|
||||
uni.reLaunch({ url })
|
||||
}
|
||||
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 用于防止重复验证
|
||||
let isValidating = false;
|
||||
|
||||
// 用户头像点击事件
|
||||
function handleUserClick() {
|
||||
if (!isLogin.value) {
|
||||
goToLogin();
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/user'
|
||||
});
|
||||
}
|
||||
|
||||
// 格式化最后登录时间
|
||||
const formatLastLoginTime = computed(() => {
|
||||
const now = new Date();
|
||||
return `${now.getMonth() + 1}月${now.getDate()}日 ${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
});
|
||||
|
||||
// 功能模块数据
|
||||
const functionModules = ref([
|
||||
{
|
||||
id: 'profile',
|
||||
name: '个人资料',
|
||||
icon: 'account',
|
||||
color: 'var(--ui-BG-Main)', // 主题色
|
||||
path: '/pages/index/user'
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: '系统设置',
|
||||
icon: 'setting',
|
||||
color: '#666666', // $dark-6
|
||||
action: 'showSettings'
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
name: '安全中心',
|
||||
icon: 'lock',
|
||||
color: '#d10019', // $red
|
||||
action: 'showSecurity'
|
||||
},
|
||||
{
|
||||
id: 'feedback',
|
||||
name: '意见反馈',
|
||||
icon: 'chat',
|
||||
color: '#8dc63f', // $green
|
||||
action: 'showFeedback'
|
||||
}
|
||||
]);
|
||||
|
||||
// 快捷操作数据
|
||||
const quickActions = ref([
|
||||
|
||||
{
|
||||
id: 'logout',
|
||||
title: '退出登录',
|
||||
desc: '安全退出当前账户',
|
||||
icon: 'logout',
|
||||
action: 'logout'
|
||||
}
|
||||
]);
|
||||
|
||||
onLoad((options) => {
|
||||
console.log('首页加载完成');
|
||||
checkLoginStatus();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 只有在页面从后台返回前台时才需要重新验证登录状态
|
||||
// 避免首次加载时的重复验证
|
||||
console.log('页面显示,检查是否需要验证登录状态');
|
||||
|
||||
// 延迟一点时间,确保不与onLoad的验证冲突
|
||||
setTimeout(() => {
|
||||
checkLoginStatus();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
onPullDownRefresh(async () => {
|
||||
console.log('下拉刷新');
|
||||
try {
|
||||
// 刷新用户信息
|
||||
if (isLogin.value) {
|
||||
await sheep.$store('user').getInfo();
|
||||
console.log('用户信息刷新成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('刷新用户信息失败,可能登录已过期', error);
|
||||
// 如果刷新失败,可能是登录过期,重新验证登录状态
|
||||
await checkLoginStatus();
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查登录状态
|
||||
async function checkLoginStatus() {
|
||||
// 防止重复验证
|
||||
if (isValidating) {
|
||||
console.log('正在验证中,跳过重复验证');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('检查登录状态...');
|
||||
|
||||
// 如果本地显示未登录,直接跳转到登录页
|
||||
if (!isLogin.value) {
|
||||
console.log('本地状态未登录,跳转到登录页');
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果本地显示已登录,需要验证token是否还有效
|
||||
console.log('本地状态已登录,验证token有效性...');
|
||||
isValidating = true;
|
||||
|
||||
try {
|
||||
// 尝试获取用户信息来验证token是否有效
|
||||
await sheep.$store('user').getInfo();
|
||||
console.log('Token有效,用户已登录');
|
||||
} catch (error) {
|
||||
console.log('Token已失效,需要重新登录', error);
|
||||
// 清除本地登录状态
|
||||
sheep.$store('user').logout(false);
|
||||
// 延迟显示登录弹窗,确保页面渲染完成
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '登录已过期,请重新登录',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 2000);
|
||||
}, 500);
|
||||
} finally {
|
||||
// 验证完成,重置标志
|
||||
setTimeout(() => {
|
||||
isValidating = false;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 导航到指定页面
|
||||
function navigateTo(path) {
|
||||
uni.navigateTo({
|
||||
url: path
|
||||
});
|
||||
}
|
||||
|
||||
// 处理退出登录
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
showCancel: true,
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '退出中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 调用退出登录API
|
||||
await sheep.$store('user').logout(true);
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
// 显示退出成功提示
|
||||
uni.showToast({
|
||||
title: '退出成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
|
||||
// 延迟跳转到登录页
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
console.error('退出登录失败:', error);
|
||||
uni.showToast({
|
||||
title: '退出失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理模块点击
|
||||
function handleModuleClick(module) {
|
||||
if (module.path) {
|
||||
navigateTo(module.path);
|
||||
} else if (module.action) {
|
||||
handleAction(module.action);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理快捷操作点击
|
||||
function handleActionClick(action) {
|
||||
handleAction(action.action);
|
||||
}
|
||||
|
||||
// 处理各种操作
|
||||
function handleAction(action) {
|
||||
switch (action) {
|
||||
case 'showSettings':
|
||||
uni.showToast({
|
||||
title: '系统设置功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showSecurity':
|
||||
uni.showToast({
|
||||
title: '安全中心功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showFeedback':
|
||||
uni.showToast({
|
||||
title: '意见反馈功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showAbout':
|
||||
uni.showModal({
|
||||
title: '关于应用',
|
||||
content: '应用版本:v1.0.0\n开发者:Yudao Team\n更新时间:2024年',
|
||||
showCancel: false
|
||||
});
|
||||
break;
|
||||
case 'scanCode':
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
uni.showModal({
|
||||
title: '扫描结果',
|
||||
content: res.result,
|
||||
showCancel: false
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '扫描失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'shareApp':
|
||||
uni.share({
|
||||
provider: 'weixin',
|
||||
scene: 'WXSceneSession',
|
||||
type: 0,
|
||||
href: '',
|
||||
title: '推荐一个好用的应用',
|
||||
summary: '快来试试这个应用吧!',
|
||||
imageUrl: ''
|
||||
});
|
||||
break;
|
||||
case 'clearCache':
|
||||
uni.showModal({
|
||||
title: '清理缓存',
|
||||
content: '确定要清理应用缓存吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 这里可以添加清理缓存的逻辑
|
||||
uni.clearStorageSync();
|
||||
uni.showToast({
|
||||
title: '缓存清理完成',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'logout':
|
||||
handleLogout();
|
||||
break;
|
||||
default:
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
const { gridCol } = useGridCol([400, 600], [2, 3, 4])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-info-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
/* 未登录提示区域 */
|
||||
.login-prompt {
|
||||
text-align: center;
|
||||
padding: 40rpx 20rpx;
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
/* 功能模块 */
|
||||
.function-modules {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.module-content {
|
||||
text-align: center;
|
||||
padding: 20rpx 10rpx;
|
||||
}
|
||||
|
||||
.module-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 10rpx;
|
||||
}
|
||||
|
||||
/* 快捷操作 */
|
||||
.quick-actions {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15rpx;
|
||||
background: #f0f0f0;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 系统信息 */
|
||||
.system-info {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15rpx 0;
|
||||
}
|
||||
</style>
|
||||
.grid-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<!-- 微信公众号的登录回调页 -->
|
||||
<template>
|
||||
<!-- 空登陆页 -->
|
||||
<view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
onLoad(async (options) => {
|
||||
// #ifdef H5
|
||||
// 将 search 参数赋值到 options 中,方便下面解析
|
||||
new URLSearchParams(location.search).forEach((value, key) => {
|
||||
options[key] = value;
|
||||
});
|
||||
// 执行登录 or 绑定,注意需要 await 绑定
|
||||
const event = options.event;
|
||||
const code = options.code;
|
||||
const state = options.state;
|
||||
if (event === 'login') { // 场景一:登录
|
||||
await sheep.$platform.useProvider().login(code, state);
|
||||
} else if (event === 'bind') { // 场景二:绑定
|
||||
await sheep.$platform.useProvider().bind(code, state);
|
||||
}
|
||||
|
||||
// 检测 H5 登录回调
|
||||
let returnUrl = uni.getStorageSync('returnUrl');
|
||||
if (returnUrl) {
|
||||
uni.removeStorage({key:'returnUrl'});
|
||||
location.replace(returnUrl);
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: '/',
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
81
pages/index/me-popup.vue
Normal file
81
pages/index/me-popup.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<u-popup :show="show" mode="right" @close="close" @open="open" closeable>
|
||||
<view style="width: 50vw" class="p20">
|
||||
<scroll-view scroll-y="true">
|
||||
<view class="x-f pb20 pt20">
|
||||
<u-avatar src=""></u-avatar>
|
||||
<view class="pl20">您好!{{ userInfo.nickname }}</view>
|
||||
</view>
|
||||
|
||||
<u-cell-group>
|
||||
<u-cell icon="grid-fill" title="切换系统" :is-link="true" @click="handleTo('/pages/index/index')" />
|
||||
<u-cell icon="info-circle-fill" title="关于我们" :is-link="true" @click="handleTo('/pages/me/aboutMe')" />
|
||||
</u-cell-group>
|
||||
|
||||
<u-button class="mt40" type="warning" plain text="退出当前账号" @click="handleLoginOut('general')" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<u-modal
|
||||
width="250px"
|
||||
:show="modalShow"
|
||||
title="提示"
|
||||
content="确定退出登录吗?"
|
||||
show-cancel-button
|
||||
async-close
|
||||
@confirm="confirm"
|
||||
@cancel="modalShow = false"
|
||||
/>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import nx from '@/nx'
|
||||
|
||||
// Props & Emits
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show', 'open', 'close'])
|
||||
|
||||
// 响应式数据
|
||||
const modalShow = ref(false)
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
// 方法
|
||||
const handleTo = url => {
|
||||
nx.$router.go(url)
|
||||
}
|
||||
|
||||
const handleLoginOut = () => {
|
||||
modalShow.value = true
|
||||
}
|
||||
|
||||
const confirm = async () => {
|
||||
try {
|
||||
await nx.$store('user').logout()
|
||||
} catch (error) {
|
||||
console.error('退出登录失败:', error)
|
||||
} finally {
|
||||
modalShow.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
emit('open')
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
emit('close')
|
||||
emit('update:show', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,266 +0,0 @@
|
||||
<!-- 菜单页面 -->
|
||||
<template>
|
||||
<s-layout
|
||||
title="菜单"
|
||||
tabbar="/pages/index/menu"
|
||||
navbar="custom"
|
||||
onShareAppMessage
|
||||
>
|
||||
<view class="menu-container">
|
||||
<!-- 轮播图 -->
|
||||
<view class="swiper-section">
|
||||
<swiper
|
||||
class="swiper-container"
|
||||
:indicator-dots="true"
|
||||
:autoplay="true"
|
||||
:interval="3000"
|
||||
:duration="500"
|
||||
indicator-color="rgba(255, 255, 255, 0.3)"
|
||||
indicator-active-color="#fff"
|
||||
circular
|
||||
>
|
||||
<swiper-item v-for="(item, index) in swiperList" :key="index">
|
||||
<view class="swiper-item">
|
||||
<image :src="item.image" mode="aspectFill" class="swiper-image" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 工作台 -->
|
||||
<view class="section">
|
||||
<view class="section-title">工作台</view>
|
||||
<view class="grid-container">
|
||||
<view
|
||||
class="grid-item"
|
||||
v-for="(item, index) in quickMenuList"
|
||||
:key="index"
|
||||
@click="handleMenuClick(item)"
|
||||
>
|
||||
<view class="item-icon" :style="{ background: item.bg }">
|
||||
<u-icon
|
||||
:name="item.icon"
|
||||
size="24"
|
||||
:color="item.color"
|
||||
/>
|
||||
</view>
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
export default {
|
||||
onShow() {
|
||||
const userStore = sheep.$store('user');
|
||||
if (!userStore.isLogin) {
|
||||
sheep.$router.redirect('/pages/login/index');
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 轮播图数据
|
||||
swiperList: [
|
||||
{
|
||||
image: '/static/images/swiper/bg1.png',
|
||||
title: '轮播图1'
|
||||
},
|
||||
{
|
||||
image: '/static/images/swiper/bg2.png',
|
||||
title: '轮播图2'
|
||||
},
|
||||
{
|
||||
image: '/static/images/swiper/bg3.png',
|
||||
title: '轮播图3'
|
||||
}
|
||||
],
|
||||
|
||||
// 工作台功能数据
|
||||
quickMenuList: [
|
||||
{
|
||||
title: 'Demo',
|
||||
icon: 'file-text',
|
||||
color: '#fff',
|
||||
bg: '#3c9cff',
|
||||
path: '/pages/app/democontract/index'
|
||||
},
|
||||
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 处理菜单点击
|
||||
handleMenuClick(item) {
|
||||
sheep.$router.go(item.path);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 轮播图样式 */
|
||||
.swiper-section {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.swiper-container {
|
||||
height: 300rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.swiper-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.swiper-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 30rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 快捷功能网格布局 */
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20rpx;
|
||||
|
||||
.grid-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 24rpx;
|
||||
color: #606266;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 管理功能列表布局 */
|
||||
.list-container {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.item-icon-small {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
flex: 1;
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 22rpx;
|
||||
color: #909399;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media screen and (max-width: 750rpx) {
|
||||
.grid-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
padding: 24rpx 16rpx;
|
||||
|
||||
.item-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,51 +0,0 @@
|
||||
<!-- 自定义页面:支持装修 -->
|
||||
<template>
|
||||
<s-layout
|
||||
:title="state.name"
|
||||
navbar="custom"
|
||||
:bgStyle="state.page"
|
||||
:navbarStyle="state.navigationBar"
|
||||
onShareAppMessage
|
||||
showLeftButton
|
||||
>
|
||||
<s-block v-for="(item, index) in state.components" :key="index" :styles="item.property.style">
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
// Diy API removed with promotion module
|
||||
|
||||
const state = reactive({
|
||||
name: '',
|
||||
components: [],
|
||||
navigationBar: {},
|
||||
page: {},
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
let id = options.id
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序预览自定义页面
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
id = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
const { code, data } = await DiyApi.getDiyPage(id);
|
||||
if (code === 0) {
|
||||
state.name = data.name;
|
||||
state.components = data.property?.components;
|
||||
state.navigationBar = data.property?.navigationBar;
|
||||
state.page = data.property?.page;
|
||||
}
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!-- 搜索界面 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="搜索">
|
||||
<view class="search-container">
|
||||
<view class="search-input-wrapper">
|
||||
<u-search
|
||||
v-model="searchText"
|
||||
placeholder="请输入关键字"
|
||||
:show-action="false"
|
||||
:focus="true"
|
||||
shape="square"
|
||||
@search="onSearch"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 搜索历史标题 -->
|
||||
<view class="search-section">
|
||||
<view class="section-header">
|
||||
<u-text text="搜索历史" size="16" bold color="#333" />
|
||||
<u-button
|
||||
text="清除搜索历史"
|
||||
size="mini"
|
||||
type="error"
|
||||
plain
|
||||
@click="onDelete"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 历史标签 -->
|
||||
<view class="history-tags" v-if="state.historyList.length">
|
||||
<u-tag
|
||||
v-for="(item, index) in state.historyList"
|
||||
:key="index"
|
||||
:text="item"
|
||||
type="info"
|
||||
plain
|
||||
size="medium"
|
||||
closable
|
||||
@click="onSearch(item)"
|
||||
@close="removeHistoryItem(index)"
|
||||
:custom-style="{ margin: '5rpx 10rpx 5rpx 0' }"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 无历史提示 -->
|
||||
<u-empty
|
||||
v-else
|
||||
mode="search"
|
||||
text="暂无搜索历史"
|
||||
textSize="14"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const searchText = ref('');
|
||||
const state = reactive({
|
||||
historyList: [],
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function onSearch(keyword = '') {
|
||||
const searchKeyword = keyword || searchText.value;
|
||||
if (!searchKeyword) {
|
||||
return;
|
||||
}
|
||||
saveSearchHistory(searchKeyword);
|
||||
// 前往商品列表(带搜索条件)
|
||||
sheep.$router.go('/pages/goods/list', { keyword: searchKeyword });
|
||||
}
|
||||
|
||||
// 移除单个历史记录
|
||||
function removeHistoryItem(index) {
|
||||
state.historyList.splice(index, 1);
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
// 保存搜索历史
|
||||
function saveSearchHistory(keyword) {
|
||||
// 如果关键词在搜索历史中,则把此关键词先移除
|
||||
if (state.historyList.includes(keyword)) {
|
||||
state.historyList.splice(state.historyList.indexOf(keyword), 1);
|
||||
}
|
||||
// 置顶关键词
|
||||
state.historyList.unshift(keyword);
|
||||
|
||||
// 最多保留 10 条记录
|
||||
if (state.historyList.length >= 10) {
|
||||
state.historyList.length = 10;
|
||||
}
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
uni.$u.modal({
|
||||
title: '提示',
|
||||
content: '确认清除搜索历史吗?',
|
||||
showCancelButton: true,
|
||||
confirmText: '确定',
|
||||
cancelText: '取消'
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
state.historyList = [];
|
||||
uni.removeStorageSync('searchHistory');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
state.historyList = uni.getStorageSync('searchHistory') || [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-container {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.history-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,258 +0,0 @@
|
||||
<!-- 个人中心 -->
|
||||
<template>
|
||||
<s-layout
|
||||
title="我的"
|
||||
tabbar="/pages/index/user"
|
||||
navbar="custom"
|
||||
onShareAppMessage
|
||||
>
|
||||
<view class="user-container">
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<view class="user-info">
|
||||
<view class="avatar-section" @click="handleAvatarClick">
|
||||
<s-avatar
|
||||
:src="userInfo.avatar"
|
||||
:nickname="userInfo.nickname || userInfo.username"
|
||||
:size="80"
|
||||
:bg-color="getAvatarBgColor(userInfo.nickname || userInfo.username)"
|
||||
/>
|
||||
<view class="user-details">
|
||||
<view class="user-name">{{ userInfo.nickname || userInfo.username || '未登录' }}</view>
|
||||
<view class="user-desc">{{ userInfo.mobile || '点击登录获取更多功能' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="menu-group">
|
||||
<view class="group-title">账户管理</view>
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToUserInfo">
|
||||
<view class="item-left">
|
||||
<view class="item-icon" style="background: var(--ui-BG-Main-opacity-1);">
|
||||
<u-icon name="account" size="20" color="var(--ui-BG-Main)"/>
|
||||
</view>
|
||||
<view class="item-title">个人信息</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="16" color="#c0c4cc"/>
|
||||
</view>
|
||||
<view class="menu-item" @click="goToAbout">
|
||||
<view class="item-left">
|
||||
<view class="item-icon" style="background: var(--ui-BG-Main-opacity-1);">
|
||||
<u-icon name="info-circle" size="20" color="var(--ui-BG-Main)"/>
|
||||
</view>
|
||||
<view class="item-title">关于我们</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="16" color="#c0c4cc"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录按钮 -->
|
||||
<view class="logout-section" v-if="isLogin">
|
||||
<view class="logout-btn" @click="handleLogout">
|
||||
退出登录
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onShow, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
|
||||
// 用户信息
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
|
||||
// 页面事件
|
||||
onShow(() => {
|
||||
// 先检查登录状态,未登录则会自动跳转到登录页
|
||||
if (!sheep.$store('user').isLogin) {
|
||||
return;
|
||||
}
|
||||
// 已登录时更新用户数据
|
||||
sheep.$store('user').updateUserData();
|
||||
});
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 头像点击事件
|
||||
function handleAvatarClick() {
|
||||
// 跳转到个人信息页面编辑头像
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/info'
|
||||
});
|
||||
}
|
||||
|
||||
// 方法
|
||||
function goToUserInfo() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/info'
|
||||
});
|
||||
}
|
||||
|
||||
function goToAbout() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/public/about'
|
||||
});
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
sheep.$store('user').logout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
background: var(--gradient-diagonal-primary, linear-gradient(135deg, #0055A2, rgba(0, 85, 162, 0.6)));
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 85, 162, 0.3);
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.user-details {
|
||||
margin-left: 20rpx;
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-desc {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
padding: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 菜单区域 */
|
||||
.menu-section {
|
||||
.menu-group {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.group-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.item-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-section {
|
||||
margin-top: 40rpx;
|
||||
|
||||
.logout-btn {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background-color: #fef0f0;
|
||||
transform: translateY(2rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
105
pages/lims/accept/detail.vue
Normal file
105
pages/lims/accept/detail.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<uni-card style="height: 100%" spacing="0">
|
||||
<view class="content">
|
||||
<up-row class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>使用部门:
|
||||
<text class="value">{{ acceptInfo.useDept }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>到货日期:
|
||||
<text class="value">{{ acceptInfo.deviceCode }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>安装调试:
|
||||
<text class="value">{{ acceptInfo.installStatus }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>验收结果:
|
||||
<text class="value">{{ acceptInfo.acceptResult }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</view>
|
||||
</uni-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onBeforeMount } from 'vue'
|
||||
import { acceptDetailList } from '@/nx/api/deviceInfo'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
import nx from '@/nx'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
const acceptSchema = [
|
||||
{
|
||||
label: '使用部门',
|
||||
field: 'useDept'
|
||||
},
|
||||
|
||||
{
|
||||
label: '到货日期',
|
||||
field: 'arrivalDate'
|
||||
},
|
||||
{
|
||||
label: '验收日期',
|
||||
field: 'acceptDate'
|
||||
},
|
||||
{
|
||||
label: '安装调试',
|
||||
field: 'installStatus'
|
||||
},
|
||||
{
|
||||
label: '验收结果',
|
||||
field: 'acceptResult'
|
||||
},
|
||||
{
|
||||
label: '验收状态',
|
||||
field: 'acceptStatus'
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark'
|
||||
}
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
getDetailInfo(id)
|
||||
})
|
||||
let acceptInfo = ref({})
|
||||
let commentWf = ref([])
|
||||
async function getDetailInfo(id) {
|
||||
const res = await acceptDetailList({
|
||||
deviceBusInfoId: id
|
||||
})
|
||||
if (res.length > 0) {
|
||||
let info = res[0]
|
||||
acceptInfo.value = info
|
||||
if (info.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(info.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
.u-col {
|
||||
padding: 10px;
|
||||
}
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
102
pages/lims/borrow/detail.vue
Normal file
102
pages/lims/borrow/detail.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<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 class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>借用人:
|
||||
<text class="value">{{ detailInfo.borrowOper }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>借用部门:
|
||||
<text class="value">{{ detailInfo.borrowDept }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>借用日期:
|
||||
<text class="value">{{ detailInfo.borrowDate }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="6"
|
||||
>计划归还日期:
|
||||
<text class="value">{{ detailInfo.planGivebackDate }}</text>
|
||||
</up-col>
|
||||
<up-col span="6"
|
||||
>借用原因:
|
||||
<text class="value">{{ detailInfo.borrowReason }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.remark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
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')
|
||||
}
|
||||
let commentWf = ref([])
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 80vh;
|
||||
width: 80vw;
|
||||
padding: 20px;
|
||||
.u-row {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 16;
|
||||
}
|
||||
}
|
||||
:deep(.uicon-close) {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
</style>
|
||||
109
pages/lims/borrow/list.vue
Normal file
109
pages/lims/borrow/list.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<borrow-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import BorrowDetailPopup from './detail.vue'
|
||||
import { borrowList } from '@/nx/api/deviceInfo'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '借用人',
|
||||
name: 'borrowOper',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '借用部门',
|
||||
name: 'borrowDept',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '借用日期',
|
||||
name: 'borrowDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '借用原因',
|
||||
name: 'borrowReason',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
label: '计划归还日期',
|
||||
name: 'planGivebackDate',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
name: 'remark',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
deviceBusInfoId: deviceId.value,
|
||||
borrowStatus: '已借出'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: borrowList
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
9
pages/lims/calibrationList/calibration.api.js
Normal file
9
pages/lims/calibrationList/calibration.api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/nx/request'
|
||||
|
||||
export function list(params) {
|
||||
return request({
|
||||
url: '/lims/bus/deviceBusCalibration/queryPageList',
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
144
pages/lims/calibrationList/detail.vue
Normal file
144
pages/lims/calibrationList/detail.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<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 class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>仪器设备名称:
|
||||
<text class="value">{{ detailInfo.deviceName }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>别名:
|
||||
<text class="value">{{ detailInfo.alias }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>生产厂家:
|
||||
<text class="value">{{ detailInfo.manufacturer }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>规格型号:
|
||||
<text class="value">{{ detailInfo.modelNo }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>设备编号:
|
||||
<text class="value">{{ detailInfo.deviceCode }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>统一编号:
|
||||
<text class="value">{{ detailInfo.deviceCode }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>检定/校准单位:
|
||||
<text class="value">{{ detailInfo.agent }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>检定/校准日期:
|
||||
<text class="value">{{ detailInfo.checkDate }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>证书编号:
|
||||
<text class="value">{{ detailInfo.certificateCode }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>确认结果:
|
||||
<text class="value">{{ detailInfo.checkResult }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>确认情况(理由):
|
||||
<up-parse style="background: #f3f4f6" :content="processedContent"></up-parse>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.checkRemark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
|
||||
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')
|
||||
}
|
||||
// / 处理富文本内容
|
||||
const processedContent = computed(() => {
|
||||
if (!detailInfo.value.checkContent) {
|
||||
return ''
|
||||
}
|
||||
return detailInfo.value.checkContent.replace(
|
||||
/<img([^>]+?)src="((?!http)[^"]*?)(file\/[^"]*)"/gi,
|
||||
(match, attributes, prefix, filePath) => {
|
||||
return `<img${attributes}src="${getImgBaseUrl()}/${filePath}"`
|
||||
}
|
||||
)
|
||||
})
|
||||
let commentWf = ref([])
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 80vh;
|
||||
width: 90vw;
|
||||
padding: 10px;
|
||||
.u-row {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
:deep(.uicon-close) {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
</style>
|
||||
101
pages/lims/calibrationList/index.vue
Normal file
101
pages/lims/calibrationList/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<calibration-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import CalibrationDetailPopup from './detail.vue'
|
||||
import { list } from './calibration.api'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '检定/校准日期',
|
||||
name: 'checkDate',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '确认人',
|
||||
name: 'checkPersonName',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
label: '检定/校准周期',
|
||||
name: 'frequencyRemark',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
label: '检定/校准单位',
|
||||
name: 'agent',
|
||||
width: 200
|
||||
},
|
||||
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
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,
|
||||
effectiveFlag: '1',
|
||||
wfStatus: 'finished'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: list
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
32
pages/lims/deviceBusDailyCheck/dailyCheck.data.js
Normal file
32
pages/lims/deviceBusDailyCheck/dailyCheck.data.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ref, reactive } from 'vue'
|
||||
export const detailSchema = [
|
||||
{ label: '设备名称', value: 'deviceName' },
|
||||
{ label: '别名', value: 'alias' },
|
||||
{ label: '设备型号', value: 'modelNo' },
|
||||
{ label: '设备编码', value: 'deviceCode' },
|
||||
{ label: '生产厂商', value: 'manufacturer' },
|
||||
{ label: '使用班组', value: 'deptName' }
|
||||
]
|
||||
export const column = reactive([
|
||||
{
|
||||
label: '点检人',
|
||||
name: 'checkUserName',
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
label: '点检日期',
|
||||
name: 'checkDate',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
name: 'content',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
138
pages/lims/deviceBusDailyCheck/detail.vue
Normal file
138
pages/lims/deviceBusDailyCheck/detail.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<up-popup :show="visible" mode="right" closeable @close="handleClose" @open="handleOpen">
|
||||
<uni-section titleFontSize="18px" type="line" title="设备点检信息"> </uni-section>
|
||||
<scroll-view scroll-y="true" style="height: 85vh; width: 90vw">
|
||||
<view class="content">
|
||||
<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 class="p5">
|
||||
<view class="pt5"
|
||||
>备注:
|
||||
<text style="color: #666">
|
||||
{{ detailInfo.content }}
|
||||
</text>
|
||||
</view>
|
||||
<up-row>
|
||||
<up-col span="6">
|
||||
<view class="pt5"
|
||||
>点检人:
|
||||
<text style="color: #666">
|
||||
{{ detailInfo.checkUserName }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="pt5"
|
||||
>点检日期:
|
||||
<text style="color: #666">
|
||||
{{ detailInfo.checkDate }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
<up-col span="6">
|
||||
<up-album multiple-size="70" single-size="70" :urls="attachment" row-count="4"> </up-album>
|
||||
</up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
<view>
|
||||
<up-row class="font-bold" style="background-color: #f5f5f5">
|
||||
<up-col span="5">点检项目</up-col>
|
||||
<up-col span="3">检查标准</up-col>
|
||||
<up-col span="2"> 频次</up-col>
|
||||
<up-col span="2">是否正常</up-col>
|
||||
</up-row>
|
||||
<view class="pt10">
|
||||
<up-row class="pb10" v-for="(item, index) in detailInfo.maintainItemList" :key="index">
|
||||
<up-col span="5">
|
||||
<view class="x-f">
|
||||
<text class="pl10">
|
||||
{{ item.itemName }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
|
||||
<up-col span="3">{{ item.standard }}</up-col>
|
||||
<up-col span="2">{{ item.frequencyRemark }}</up-col>
|
||||
<up-col span="2">
|
||||
<u-tag v-if="item.checkResult == '正常'" text="正常" size="mini" type="success" plain plainFill></u-tag>
|
||||
<u-tag v-else text="不正常" type="error" size="mini" plain plainFill></u-tag>
|
||||
</up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
<view class="p10"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { detailSchema } from './dailyCheck.data'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
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 attachment = ref([])
|
||||
async function getDetailInfo(id) {
|
||||
const res = await dailyCheckApi.queryById(id)
|
||||
res.maintainItemList.forEach(item => {
|
||||
if (item.checkResult == 'false' || !item.checkResult) {
|
||||
item.checkResult = false
|
||||
}
|
||||
if (item.checkResult == 'true') {
|
||||
item.checkResult = true
|
||||
}
|
||||
})
|
||||
|
||||
detailInfo.value = res
|
||||
attachment.value = []
|
||||
let files = res?.attachment?.split(',') || []
|
||||
if (files.length > 0) {
|
||||
attachment.value = files.map(item => getImgBaseUrl() + item)
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['close', 'open'])
|
||||
function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
function handleOpen() {
|
||||
getDetailInfo(props.checkInfo.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
padding-top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.uicon-close) {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
</style>
|
||||
293
pages/lims/deviceBusDailyCheck/index.vue
Normal file
293
pages/lims/deviceBusDailyCheck/index.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<view>
|
||||
<up-sticky>
|
||||
<navbar-back title="点检">
|
||||
<up-button
|
||||
v-if="detailInfo.id"
|
||||
type="primary"
|
||||
:plain="true"
|
||||
icon="list"
|
||||
size="small"
|
||||
text="设备点检记录"
|
||||
@click="handleCheckRecord"
|
||||
></up-button>
|
||||
</navbar-back>
|
||||
</up-sticky>
|
||||
<view class="container">
|
||||
<n-scanTemp
|
||||
v-if="!detailInfo.id"
|
||||
title="请扫描设备条码进行点检"
|
||||
icon="dailyCheck"
|
||||
@deviceId="id => getDailyCheckRecord(id)"
|
||||
/>
|
||||
<view v-else class="content">
|
||||
<view>
|
||||
<uni-section titleFontSize="22px" type="line" title="设备点检信息">
|
||||
<template v-slot:right>
|
||||
<up-button
|
||||
v-if="detailInfo.submitFlag == '1'"
|
||||
type="success"
|
||||
text="新建点检"
|
||||
@click="handleCreateDailyCheck"
|
||||
></up-button>
|
||||
</template>
|
||||
</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>
|
||||
<uni-section titleFontSize="22px" type="line" title="检查项"> </uni-section>
|
||||
<up-row class="p10 font-bold" style="background-color: #f5f5f5">
|
||||
<up-col span="4">点检项目</up-col>
|
||||
<up-col span="3">检查标准</up-col>
|
||||
<up-col span="2"> 频次</up-col>
|
||||
<up-col style="text-align: center" span="3">是否正常</up-col>
|
||||
</up-row>
|
||||
<view class="pt10">
|
||||
<up-row class="pb12" v-for="(item, index) in detailInfo.maintainItemList" :key="index">
|
||||
<up-col span="4">
|
||||
<view class="x-f">
|
||||
<u-badge type="warning " :value="index + 1"></u-badge>
|
||||
<text class="pl10">
|
||||
{{ item.itemName }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
|
||||
<up-col span="3">{{ item.standard }}</up-col>
|
||||
<up-col span="2">{{ item.frequencyRemark }}</up-col>
|
||||
<up-col span="3">
|
||||
<u-radio-group v-model="item.checkResult" placement="row">
|
||||
<u-radio activeColor="green" label="正常" name="正常"></u-radio>
|
||||
<u-radio activeColor="red" label="不正常" name="不正常"></u-radio>
|
||||
</u-radio-group>
|
||||
</up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<uni-section titleFontSize="22px" type="line" title="备注"> </uni-section>
|
||||
<up-textarea v-model="detailInfo.content" placeholder="请输入内容"></up-textarea>
|
||||
<view class="p10">附件照片:</view>
|
||||
<n-upload v-model="detailInfo.attachment" />
|
||||
<view class="p10">点检人:</view>
|
||||
<up-input v-model="detailInfo.checkUserName"></up-input>
|
||||
<view v-if="detailInfo.submitFlag == '1'">
|
||||
<view class="p10">点检日期:</view>
|
||||
<uni-datetime-picker type="datetime" v-model="detailInfo.checkDate" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt40 x-bc" v-if="detailInfo.submitFlag == '0'">
|
||||
<up-button
|
||||
style="width: 40%"
|
||||
loadingText="保存中..."
|
||||
type="warning"
|
||||
text="暂存"
|
||||
@click="handleSubmit('0')"
|
||||
></up-button>
|
||||
<up-button
|
||||
style="width: 40%"
|
||||
loadingText="提交中..."
|
||||
type="primary"
|
||||
text="提交"
|
||||
@click="handleSubmit('1')"
|
||||
></up-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<up-modal
|
||||
:show="modalShow"
|
||||
title="提示"
|
||||
:content="modalText"
|
||||
ref="uModal"
|
||||
:asyncClose="true"
|
||||
showCancelButton
|
||||
@confirm="confirm"
|
||||
@cancel="modalShow = false"
|
||||
></up-modal>
|
||||
<up-loading-page :loading="pageLoading"></up-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted, watch, toRefs, computed } from 'vue'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import { detailSchema } from './dailyCheck.data'
|
||||
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 pageLoading = ref(false)
|
||||
let detailInfo = ref({})
|
||||
|
||||
const { scanQRInfo } = toRefs(nx.$store('biz'))
|
||||
watch(scanQRInfo, newVal => {
|
||||
if (newVal && nx.$router.getCurrentPage().route == 'pages/lims/deviceBusDailyCheck/index') {
|
||||
try {
|
||||
const codeObj = JSON.parse(newVal)
|
||||
if (!pageLoading.value) {
|
||||
getDailyCheckRecord(codeObj.id)
|
||||
}
|
||||
scanQRInfo.value = ''
|
||||
} catch (error) {
|
||||
scanQRInfo.value = ''
|
||||
uni.showToast({
|
||||
title: '请扫描设备码',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
const modalText = computed(() => {
|
||||
return `确定${modalType.value == '0' ? '暂存' : '提交'}吗?${modalType.value == '0' ? '' : '提交后不能修改'} `
|
||||
})
|
||||
onShow(() => {
|
||||
scanQRInfo.value = ''
|
||||
})
|
||||
let goBack = ref(false)
|
||||
onLoad(options => {
|
||||
if (options.deviceId) {
|
||||
goBack.value = true
|
||||
getDailyCheckRecord(options.deviceId)
|
||||
}
|
||||
})
|
||||
|
||||
async function getDailyCheckRecord(id) {
|
||||
pageLoading.value = true
|
||||
const res = await dailyCheckApi
|
||||
.getCheckRecord({
|
||||
deviceId: id,
|
||||
dataType: 'dailyCheck'
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
if (!res.checkUserName) {
|
||||
res.checkUserName = nx.$store('user').userInfo.realname
|
||||
res.checkUserId = nx.$store('user').userInfo.id
|
||||
}
|
||||
detailInfo.value = res
|
||||
modalType.value = res.submitFlag
|
||||
|
||||
lockOrientation('landscape')
|
||||
}
|
||||
|
||||
const modalShow = ref(false)
|
||||
const modalType = ref('')
|
||||
// 是否有异常项
|
||||
const hasAbnormal = computed(() => {
|
||||
if (modalType.value == '1') {
|
||||
const items = detailInfo.value.maintainItemList || []
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const checkResult = items[i].checkResult
|
||||
if (checkResult == '不正常') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
function handleSubmit(type) {
|
||||
// 新增维护保养项验证
|
||||
if (type == '1') {
|
||||
const items = detailInfo.value.maintainItemList || []
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
const itemNumber = `第${i + 1}项`
|
||||
if (!item.checkResult?.trim()) {
|
||||
return uni.showToast({ title: `${itemNumber}设备检查状态未选择`, icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!detailInfo.value.checkUserName) {
|
||||
return uni.showToast({
|
||||
title: '请输入点检人',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
detailInfo.value.checkDate = nx.$dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
modalType.value = type
|
||||
modalShow.value = true
|
||||
console.log(detailInfo.value)
|
||||
}
|
||||
|
||||
const submitLoading = ref(false)
|
||||
async function confirm() {
|
||||
if (submitLoading.value) return
|
||||
submitLoading.value = true
|
||||
|
||||
|
||||
await dailyCheckApi.submit({ ...detailInfo.value, submitFlag: modalType.value }).finally(() => {
|
||||
submitLoading.value = false
|
||||
modalShow.value = false
|
||||
reset()
|
||||
})
|
||||
if (goBack.value && modalType.value == 1) {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
function reset() {
|
||||
detailInfo.value = {}
|
||||
}
|
||||
function handleCheckRecord() {
|
||||
let deviceInfo = {
|
||||
id: detailInfo.value.deviceId,
|
||||
deviceName: detailInfo.value.deviceName
|
||||
}
|
||||
nx.$store('biz').deviceInfo = deviceInfo
|
||||
nx.$router.go('/pages/lims/deviceBusDailyCheck/list')
|
||||
}
|
||||
// 新建点检
|
||||
function handleCreateDailyCheck() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定新建点检吗?',
|
||||
success: async function (res) {
|
||||
if (res.confirm) {
|
||||
const res = await dailyCheckApi.createDailyCheck({
|
||||
deviceId: detailInfo.value.deviceId,
|
||||
dataType: 'dailyCheck'
|
||||
})
|
||||
res.checkUserName = nx.$store('user').userInfo.realname
|
||||
res.checkUserId = nx.$store('user').userInfo.id
|
||||
res.checkDate = nx.$dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
detailInfo.value = res
|
||||
} else if (res.cancel) {
|
||||
console.log('用户点击取消')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-sticky {
|
||||
top: 0 !important;
|
||||
}
|
||||
.container {
|
||||
.content {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
padding-top: 10px;
|
||||
font-size: 18px;
|
||||
.title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
:deep(.u-checkbox) {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
84
pages/lims/deviceBusDailyCheck/list.vue
Normal file
84
pages/lims/deviceBusDailyCheck/list.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<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 :style="{ height: isComponent ? '70vh' : '72vh' }">
|
||||
<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>
|
||||
<daily-check-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import DailyCheckDetailPopup from './detail.vue'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import { column } from './dailyCheck.data'
|
||||
import nx from '@/nx'
|
||||
let props = defineProps({
|
||||
isComponent: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
dataType: 'dailyCheck',
|
||||
deviceId: deviceId.value
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: dailyCheckApi.list
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
86
pages/lims/deviceBusInfo/baseInfo.vue
Normal file
86
pages/lims/deviceBusInfo/baseInfo.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<up-sticky>
|
||||
<navbar-back :title="`(${deviceText})设备信息`"></navbar-back>
|
||||
</up-sticky>
|
||||
<up-tabs lineWidth="30" :list="tabs" @click="tabClick"></up-tabs>
|
||||
<scroll-view scroll-y="true" class="content">
|
||||
<BaseInfoCard v-if="activeIndex == 0" @getDeviceInfo="getDeviceInfo" />
|
||||
<AcceptDetail v-if="activeIndex == 1" />
|
||||
<DailyCheckList v-if="activeIndex == 2" isComponent />
|
||||
<MaintainList v-if="activeIndex == 3" isComponent />
|
||||
<UseRecordList v-if="activeIndex == 4" isComponent />
|
||||
<PeriodCheckList v-if="activeIndex == 5" />
|
||||
<CalibrationList v-if="activeIndex == 6" />
|
||||
<RepairList v-if="activeIndex == 7" />
|
||||
<BorrowList v-if="activeIndex == 8" />
|
||||
<GivebackList v-if="activeIndex == 9" />
|
||||
<StopList v-if="activeIndex == 10" />
|
||||
<DocumentList v-if="activeIndex == 11" />
|
||||
<ScrapInfo v-if="activeIndex == 12 && scrapFlag == 1" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import BaseInfoCard from '@/pages/lims/deviceBusInfo/baseInfoCard'
|
||||
import AcceptDetail from '@/pages/lims/accept/detail'
|
||||
import DailyCheckList from '@/pages/lims/deviceBusDailyCheck/list'
|
||||
import MaintainList from '@/pages/lims/deviceBusMaintain/list'
|
||||
import UseRecordList from '@/pages/lims/deviceBusUseRecord/list'
|
||||
import PeriodCheckList from '@/pages/lims/periodCheckList/index'
|
||||
import CalibrationList from '@/pages/lims/calibrationList/index'
|
||||
import RepairList from '@/pages/lims/repair/list'
|
||||
import BorrowList from '@/pages/lims/borrow/list'
|
||||
import GivebackList from '@/pages/lims/giveback/list'
|
||||
import StopList from '@/pages/lims/stop/list'
|
||||
import DocumentList from '@/pages/lims/documentList/index'
|
||||
import ScrapInfo from '@/pages/lims/scrap/detail'
|
||||
import { tabList } from './deviceBusInfo.data'
|
||||
|
||||
let activeIndex = ref(0)
|
||||
let deviceText = ref('')
|
||||
const detailId = ref('')
|
||||
|
||||
const { id, deviceName, scrapFlag } = nx.$store('biz').deviceInfo
|
||||
onMounted(() => {
|
||||
detailId.value = id
|
||||
})
|
||||
const getDeviceInfo = info => {
|
||||
deviceText.value = info.deviceName + (info.alias ? `[${info.alias}]` : '')
|
||||
}
|
||||
const tabs = computed(() => {
|
||||
return tabList.filter((item, index) => {
|
||||
if (scrapFlag != 1) {
|
||||
return item.name != '报废信息'
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function tabClick(e) {
|
||||
activeIndex.value = e.index
|
||||
if (activeIndex.value == 0) {
|
||||
getDeviceInfo()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-sticky {
|
||||
top: 0 !important;
|
||||
}
|
||||
.container {
|
||||
background-color: #f0f2f5;
|
||||
height: 100%;
|
||||
.content {
|
||||
height: 75vh;
|
||||
}
|
||||
}
|
||||
:deep(.u-tabs__wrapper__nav__item__text) {
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
119
pages/lims/deviceBusInfo/baseInfoCard.vue
Normal file
119
pages/lims/deviceBusInfo/baseInfoCard.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<uni-card spacing="0">
|
||||
<view class="detail-content">
|
||||
<up-row align="top">
|
||||
<template v-for="item in deviceSchema">
|
||||
<up-col :span="item.span ? item.span : 4">
|
||||
<view class="x-f detail-item" :style="item.itemStyle">
|
||||
<view class="detail-label" :style="item.labelStyle">{{ item.label }}:</view>
|
||||
<template v-if="item.field == 'photo'">
|
||||
<view style="height: 50px">
|
||||
<up-album multipleSize="50" singleSize="50" maxCount="2" :urls="imgs"></up-album>
|
||||
</view>
|
||||
</template>
|
||||
<text v-else class="detail-value" :style="item.valueStyle">{{ detailInfo[item.field] }}</text>
|
||||
</view>
|
||||
</up-col>
|
||||
</template>
|
||||
</up-row>
|
||||
</view>
|
||||
</uni-card>
|
||||
<up-loading-page :loading="pageLoading"></up-loading-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import { getDeviceBusInfoById } from '@/nx/api/deviceInfo'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
import { deviceSchema } from './deviceBusInfo.data'
|
||||
const detailId = ref('')
|
||||
const pageLoading = ref(false)
|
||||
const emit = defineEmits(['getDeviceInfo'])
|
||||
onMounted(() => {
|
||||
const { id } = nx.$store('biz').deviceInfo
|
||||
detailId.value = id
|
||||
getDeviceInfo()
|
||||
})
|
||||
let detailInfo = ref({})
|
||||
let imgs = ref([])
|
||||
// 获取设备详情
|
||||
async function getDeviceInfo() {
|
||||
pageLoading.value = true
|
||||
const res = await getDeviceBusInfoById(detailId.value).finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
detailInfo.value = (data => {
|
||||
const statuses = [
|
||||
data.repairFlag == 1 && '维修',
|
||||
data.demoteFlag == 1 && '降级',
|
||||
data.scrapFlag == 1 && '报废',
|
||||
data.disableFlag == 1 && '停用',
|
||||
data.lendFlag == 1 && '外借'
|
||||
].filter(Boolean) // 去掉 false 值
|
||||
|
||||
if (data.acceptFlag != 'finished') {
|
||||
data.deviceStatus = '--'
|
||||
data.inUseFlag = '--'
|
||||
} else {
|
||||
data.deviceStatus = statuses.length > 0 ? statuses.join('、') : '正常'
|
||||
if (data.inUseFlag == '1') {
|
||||
data.inUseFlag = '是'
|
||||
} else {
|
||||
data.inUseFlag = '否'
|
||||
}
|
||||
}
|
||||
switch (data.acceptFlag) {
|
||||
case '0':
|
||||
data.acceptFlag = '待验收'
|
||||
break
|
||||
case 'running':
|
||||
data.acceptFlag = '验收审批中'
|
||||
break
|
||||
case 'finished':
|
||||
data.acceptFlag = '已验收'
|
||||
break
|
||||
default:
|
||||
data.acceptFlag = '--'
|
||||
}
|
||||
return data
|
||||
})(res)
|
||||
imgs.value = getFileList(res.photo)
|
||||
emit('getDeviceInfo', detailInfo.value)
|
||||
}
|
||||
function getFileList(photo) {
|
||||
if (photo) {
|
||||
let fileIds = photo.split(',').map(item => getImgBaseUrl() + item)
|
||||
return fileIds
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-content {
|
||||
border: 0.5px solid #f0f0f0;
|
||||
width: 100%;
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 6px;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
border: 0.5px solid #f0f0f0;
|
||||
.detail-label {
|
||||
color: #000;
|
||||
}
|
||||
.detail-value {
|
||||
font-size: 14px;
|
||||
width: 72%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.u-row) {
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
104
pages/lims/deviceBusInfo/deviceBusInfo.data.js
Normal file
104
pages/lims/deviceBusInfo/deviceBusInfo.data.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { ref } from 'vue'
|
||||
export function getColumn(isFold) {
|
||||
return [
|
||||
{
|
||||
label: '设备名称',
|
||||
name: 'deviceName',
|
||||
width: isFold ? 200 : 130,
|
||||
fixed: true
|
||||
},
|
||||
{
|
||||
label: '别名',
|
||||
name: 'alias',
|
||||
width: isFold ? 120 : 90,
|
||||
fixed: true
|
||||
},
|
||||
{
|
||||
label: '设备状态',
|
||||
name: 'stateShow',
|
||||
width: isFold ? 120 : 90
|
||||
},
|
||||
|
||||
{
|
||||
label: '使用状态',
|
||||
name: 'inUseFlag',
|
||||
width: isFold ? 120 : 90
|
||||
},
|
||||
{
|
||||
label: '规格型号',
|
||||
name: 'modelNo',
|
||||
width: isFold ? 160 : 130
|
||||
},
|
||||
{
|
||||
label: '使用班组',
|
||||
name: 'deptName',
|
||||
width: isFold ? 140 : 100
|
||||
},
|
||||
{
|
||||
label: '负责人',
|
||||
name: 'managerUserName',
|
||||
width: isFold ? 100 : 90
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '设备信息', func: 'detail' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const tabList = [
|
||||
{ name: '基础信息' },
|
||||
{ name: '验收信息' },
|
||||
{ name: '点检' },
|
||||
{ name: '维护保养' },
|
||||
{ name: '使用记录' },
|
||||
{ name: '期间核查' },
|
||||
{ name: '检定/校准' },
|
||||
{ name: '维修记录' },
|
||||
{ name: '借用记录' },
|
||||
{ name: '归还记录' },
|
||||
{ name: '停用记录' },
|
||||
{ name: '设备文档' },
|
||||
{ name: '报废信息' }
|
||||
]
|
||||
|
||||
export const deviceSchema = [
|
||||
{ label: '设备名称', field: 'deviceName' },
|
||||
{ label: '别名', field: 'alias' },
|
||||
{ label: '设备用途', field: 'deviceUse' },
|
||||
{ label: '存放位置', field: 'position' },
|
||||
{ label: '等级分类', field: 'gradeClassify' },
|
||||
{ label: '设备状态', field: 'stateShow' },
|
||||
{ label: '验收状态', field: 'acceptFlag' },
|
||||
{ label: '使用状态', field: 'inUseFlag' },
|
||||
{ label: '数量', field: 'deviceNum' },
|
||||
{ label: '管理编号', field: 'deviceCode' },
|
||||
{ label: '资产编号', field: 'assetCode' },
|
||||
{ label: '出厂编号', field: 'factoryCode' },
|
||||
{ label: '规格型号', field: 'modelNo' },
|
||||
|
||||
{ label: '购入价格', field: 'purchasePrice' },
|
||||
{ label: '采购时间', field: 'purchaseDate' },
|
||||
{ label: '出厂日期', field: 'productiveDate' },
|
||||
{ label: '安装日期', field: 'deployDate' },
|
||||
{ label: '安装人员', field: 'deployEngineer' },
|
||||
|
||||
{ label: '验收人员', field: 'acceptUserName' },
|
||||
{ label: '负责人', field: 'managerUserName' },
|
||||
{ label: '所属班组', field: 'deptName' },
|
||||
{
|
||||
label: '安装位置',
|
||||
field: 'deployLocation'
|
||||
},
|
||||
{
|
||||
label: '技术指标',
|
||||
field: 'deviceParameters',
|
||||
valueStyle: { fontSize: 12 + 'px', height: 50 + 'px' }
|
||||
},
|
||||
{
|
||||
label: '设备图片',
|
||||
field: 'photo'
|
||||
}
|
||||
]
|
||||
213
pages/lims/deviceBusInfo/index.vue
Normal file
213
pages/lims/deviceBusInfo/index.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<up-sticky>
|
||||
<navbar-back title="设备查询" />
|
||||
</up-sticky>
|
||||
<view class="content">
|
||||
<up-transition :show="!isFold" mode="slide-left">
|
||||
<view class="con-left" v-show="!isFold">
|
||||
<uni-card style="height: 100%; overflow: auto" title="分类和产品" padding="0" margin="0" spacing="0">
|
||||
<DaTree
|
||||
ref="DaTreeRef"
|
||||
:data="roomTreeData"
|
||||
labelField="name"
|
||||
valueField="id"
|
||||
appendField="modelNo"
|
||||
:indent="10"
|
||||
@change="handleTreeChange"
|
||||
></DaTree>
|
||||
</uni-card>
|
||||
</view>
|
||||
</up-transition>
|
||||
<view class="con-right">
|
||||
<uni-card padding="0" margin="10">
|
||||
<view class="p10 x-f" style="width: 60%">
|
||||
<image
|
||||
class="mr15"
|
||||
style="width: 30px; height: 30px"
|
||||
:src="`/static/images/${isFold ? 'zhedie2' : 'zhedie1'}.png`"
|
||||
@click="isFold = !isFold"
|
||||
></image>
|
||||
<up-search
|
||||
v-model="keyword"
|
||||
shape="square"
|
||||
placeholder="请输入设备名称或者扫描设备二维码码查询"
|
||||
actionText="重置"
|
||||
:clearabled="false"
|
||||
:showAction="true"
|
||||
@change="handleInputSearch"
|
||||
@custom="handleReset"
|
||||
></up-search>
|
||||
</view>
|
||||
<view style="height: 73vh; width: 100%">
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted, watch, computed, toRefs } from 'vue'
|
||||
import DaTree from '@/components/da-tree/index.vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import { throttle } from '@/uview-plus'
|
||||
import { getColumn } from './deviceBusInfo.data'
|
||||
import { deviceList, treeData, getDeviceBusInfoById } from '@/nx/api/deviceInfo'
|
||||
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = computed(() => getColumn(isFold.value))
|
||||
const isFold = ref(false)
|
||||
const roomTreeData = ref([])
|
||||
|
||||
const { scanQRInfo } = toRefs(nx.$store('biz'))
|
||||
watch(scanQRInfo, newVal => {
|
||||
if (newVal && nx.$router.getCurrentPage().route == 'pages/lims/deviceBusInfo/index') {
|
||||
try {
|
||||
const codeObj = JSON.parse(newVal)
|
||||
handleDetail({ id: codeObj.id })
|
||||
scanQRInfo.value = ''
|
||||
} catch (error) {
|
||||
scanQRInfo.value = ''
|
||||
uni.showToast({
|
||||
title: '请扫描设备码',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
onShow(() => {
|
||||
scanQRInfo.value = ''
|
||||
})
|
||||
async function getTreeData() {
|
||||
const res = await treeData()
|
||||
roomTreeData.value = res
|
||||
}
|
||||
getTreeData()
|
||||
const { lockOrientation } = useScreenOrientation()
|
||||
onMounted(async () => {
|
||||
lockOrientation('landscape')
|
||||
await getInitData()
|
||||
console.log('listData', listData.value)
|
||||
})
|
||||
const searchParams = computed(() => {
|
||||
const params = {
|
||||
deviceName: keyword.value
|
||||
}
|
||||
if (selectedNode.value) {
|
||||
params.productId = selectedNode.value.key
|
||||
}
|
||||
return params
|
||||
})
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: deviceList,
|
||||
processData: data => {
|
||||
return data.map(item => {
|
||||
const statuses = [
|
||||
item.repairFlag == 1 && '维修',
|
||||
item.demoteFlag == 1 && '降级',
|
||||
item.scrapFlag == 1 && '报废',
|
||||
item.disableFlag == 1 && '停用',
|
||||
item.lendFlag == 1 && '外借'
|
||||
].filter(Boolean) // 去掉 false 值
|
||||
|
||||
if (item.acceptFlag != 'finished') {
|
||||
item.deviceStatus = '--'
|
||||
} else {
|
||||
item.deviceStatus = statuses.length > 0 ? statuses.join('、') : '正常'
|
||||
}
|
||||
if (item.inUseFlag == '1') {
|
||||
item.inUseFlag = '是'
|
||||
} else {
|
||||
item.inUseFlag = '否'
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
let selectedNode = ref(null)
|
||||
let DaTreeRef = ref()
|
||||
function handleTreeChange(allCheckedKeys, currentItem) {
|
||||
console.log(allCheckedKeys, currentItem)
|
||||
|
||||
selectedNode.value = currentItem
|
||||
getInitData()
|
||||
}
|
||||
let keyword = ref('')
|
||||
function handleInputSearch() {
|
||||
if (!keyword.value) return
|
||||
throttle(getInitData, 500)
|
||||
}
|
||||
function handleReset() {
|
||||
keyword.value = ''
|
||||
if (selectedNode.value) {
|
||||
DaTreeRef.value.setCheckedKeys(selectedNode.value.key, false)
|
||||
}
|
||||
selectedNode.value = ''
|
||||
getInitData()
|
||||
}
|
||||
async function handleDetail(row, index) {
|
||||
nx.$store('biz').deviceInfo = row
|
||||
await getDeviceBusInfoById(row.id)
|
||||
nx.$router.go('/pages/lims/deviceBusInfo/baseInfo')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-sticky {
|
||||
top: 0 !important;
|
||||
}
|
||||
.container {
|
||||
background-color: #f0f2f5;
|
||||
height: 100%;
|
||||
.content {
|
||||
display: flex;
|
||||
.con-left {
|
||||
height: 83vh;
|
||||
overflow: scroll;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
flex: 2;
|
||||
}
|
||||
.con-right {
|
||||
flex: 8;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
: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>
|
||||
142
pages/lims/deviceBusMaintain/detail.vue
Normal file
142
pages/lims/deviceBusMaintain/detail.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<up-popup :show="visible" mode="right" closeable @close="handleClose" @open="handleOpen">
|
||||
<uni-section titleFontSize="18px" type="line" title="设备维护保养信息"> </uni-section>
|
||||
<scroll-view scroll-y="true" style="height: 85vh; width: 90vw">
|
||||
<view class="content">
|
||||
<up-row class="flex-wrap pt10 pl10" style="background-color: #f5f7fa">
|
||||
<up-col class="mb8" :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 class="pt5">
|
||||
<up-row>
|
||||
<up-col span="6">
|
||||
<view
|
||||
>维护保养人:
|
||||
<text style="color: #666">
|
||||
{{ detailInfo.checkUserName }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="pt5"
|
||||
>维护保养日期:
|
||||
<text style="color: #666">
|
||||
{{ detailInfo.checkDate }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
<up-col span="6">
|
||||
<u-album multiple-size="70" single-size="70" :urls="attachment" row-count="4" />
|
||||
</up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
<view>
|
||||
<up-row class="p10 font-bold" style="background-color: #f5f5f5">
|
||||
<up-col span="5">维护保养内容</up-col>
|
||||
<up-col span="3"> 频次</up-col>
|
||||
<up-col span="4">维护保养标准</up-col>
|
||||
</up-row>
|
||||
<view>
|
||||
<view v-for="(item, index) in detailInfo.maintainItemList" :key="index">
|
||||
<up-row class="p10">
|
||||
<up-col span="5">
|
||||
<view class="x-f">
|
||||
<text>
|
||||
{{ item.itemName }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
<up-col span="3">{{ item.frequencyRemark }}</up-col>
|
||||
<up-col span="4">{{ item.standard }}</up-col>
|
||||
</up-row>
|
||||
<up-row class="fill-content">
|
||||
<up-col span="12"
|
||||
><view
|
||||
>维护保养情况:
|
||||
<text style="color: #666">
|
||||
{{ item.checkRemark }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
<view class="p10"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkInfo: {
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
const detailSchema = [
|
||||
{ label: '设备名称', value: 'deviceName' },
|
||||
{ label: '别名', value: 'alias' },
|
||||
{ label: '设备型号', value: 'modelNo' },
|
||||
{ label: '设备编码', value: 'deviceCode' },
|
||||
{ label: '使用班组', value: 'deptName' }
|
||||
]
|
||||
const visible = ref(props.show)
|
||||
// 监听外部传入的show属性变化
|
||||
watch(
|
||||
() => props.show,
|
||||
newVal => {
|
||||
visible.value = newVal
|
||||
}
|
||||
)
|
||||
const attachment = computed(() => {
|
||||
let files = detailInfo.value?.attachment?.split(',') || []
|
||||
if (files.length > 0) {
|
||||
return files.map(item => getImgBaseUrl() + item)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
let detailInfo = ref({})
|
||||
async function getDetailInfo(id) {
|
||||
const res = await dailyCheckApi.queryById(id)
|
||||
detailInfo.value = res
|
||||
}
|
||||
const emit = defineEmits(['close', 'open'])
|
||||
function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
function handleOpen() {
|
||||
getDetailInfo(props.checkInfo.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
.fill-content {
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 2px 2px 8px 2px rgba(0, 0, 0, 0.2);
|
||||
background-color: #fdf6ec;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
:deep(.uicon-close) {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
</style>
|
||||
262
pages/lims/deviceBusMaintain/index.vue
Normal file
262
pages/lims/deviceBusMaintain/index.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<view>
|
||||
<up-sticky>
|
||||
<navbar-back title="维护保养">
|
||||
<up-button
|
||||
v-if="detailInfo.id"
|
||||
type="primary"
|
||||
:plain="true"
|
||||
icon="list"
|
||||
size="small"
|
||||
text="设备维护保养记录"
|
||||
@click="handleCheckRecord"
|
||||
></up-button>
|
||||
</navbar-back>
|
||||
</up-sticky>
|
||||
<view class="container">
|
||||
<n-scanTemp
|
||||
v-if="!detailInfo.id"
|
||||
title="请扫描设备条码进行维护保养"
|
||||
icon="maintain"
|
||||
@deviceId="id => getDetailInfo(id)"
|
||||
/>
|
||||
<view v-else class="content">
|
||||
<view>
|
||||
<uni-section titleFontSize="22px" 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 class="check-header">
|
||||
<uni-section titleFontSize="22px" type="line" title="维护保养项"> </uni-section>
|
||||
<up-row class="p10 font-bold border-b" style="background-color: #f5f5f5">
|
||||
<up-col span="4">维护保养内容</up-col>
|
||||
<up-col span="4"> 频次</up-col>
|
||||
<up-col span="4">维护保养标准</up-col>
|
||||
</up-row>
|
||||
<view class="pt10">
|
||||
<view v-for="(item, index) in detailInfo.maintainItemList" :key="index">
|
||||
<up-row class="p20">
|
||||
<up-col span="4">
|
||||
<view class="x-f">
|
||||
<u-badge type="warning " :value="index + 1"></u-badge>
|
||||
<text class="pl10">
|
||||
{{ item.itemName }}
|
||||
</text>
|
||||
</view>
|
||||
</up-col>
|
||||
<up-col span="4">{{ item.frequencyRemark }}</up-col>
|
||||
<up-col span="4">{{ item.standard }}</up-col>
|
||||
</up-row>
|
||||
<up-row class="fill-content">
|
||||
<up-col span="12" class="x-f"
|
||||
><view class="p10"><text class="required-star">*</text>维护保养情况:</view>
|
||||
<up-input class="bg-w" v-model="item.checkRemark" placeholder="请输入维护保养情况"></up-input
|
||||
></up-col>
|
||||
</up-row>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<view class="p10">附件照片:</view>
|
||||
<n-upload v-model="detailInfo.attachment" />
|
||||
<view class="p10">维护保养人:</view>
|
||||
<up-input v-model="detailInfo.checkUserName"></up-input>
|
||||
<view v-if="detailInfo.submitFlag == '1'">
|
||||
<view class="p10">维护保养日期:</view>
|
||||
<uni-datetime-picker type="date" v-model="detailInfo.checkDate" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt40 x-bc" v-if="detailInfo.submitFlag == '0'">
|
||||
<up-button
|
||||
style="width: 40%"
|
||||
loadingText="保存中..."
|
||||
type="warning"
|
||||
text="暂存"
|
||||
@click="handleSubmit('0')"
|
||||
></up-button>
|
||||
<up-button
|
||||
style="width: 40%"
|
||||
loadingText="提交中..."
|
||||
type="primary"
|
||||
text="提交"
|
||||
@click="handleSubmit('1')"
|
||||
></up-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<up-modal
|
||||
:show="modalShow"
|
||||
title="提示"
|
||||
:content="modalText"
|
||||
ref="uModal"
|
||||
:asyncClose="true"
|
||||
showCancelButton
|
||||
@confirm="confirm"
|
||||
@cancel="modalShow = false"
|
||||
></up-modal>
|
||||
<up-loading-page :loading="pageLoading"></up-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, watch, toRefs } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import nx from '@/nx'
|
||||
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
|
||||
const { lockOrientation } = useScreenOrientation()
|
||||
|
||||
const pageLoading = ref(false)
|
||||
const detailSchema = [
|
||||
{ label: '设备名称', value: 'deviceName' },
|
||||
{ label: '设备型号', value: 'modelNo' },
|
||||
{ label: '设备编码', value: 'deviceCode' },
|
||||
{ label: '使用班组', value: 'deptName' }
|
||||
]
|
||||
let detailInfo = ref({})
|
||||
let checkList = ref([])
|
||||
const { scanQRInfo } = toRefs(nx.$store('biz'))
|
||||
watch(scanQRInfo, newVal => {
|
||||
if (newVal && nx.$router.getCurrentPage().route == 'pages/lims/deviceBusMaintain/index') {
|
||||
try {
|
||||
const codeObj = JSON.parse(newVal)
|
||||
if (!pageLoading.value) {
|
||||
getDetailInfo(codeObj.id)
|
||||
}
|
||||
scanQRInfo.value = ''
|
||||
} catch (error) {
|
||||
scanQRInfo.value = ''
|
||||
uni.showToast({
|
||||
title: '请扫描设备码',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
onShow(() => {
|
||||
scanQRInfo.value = ''
|
||||
})
|
||||
const modalText = computed(() => {
|
||||
return `确定${modalType.value == '0' ? '暂存' : '提交'}吗?${modalType.value == '0' ? '' : '提交后不能修改'} `
|
||||
})
|
||||
async function getDetailInfo(id) {
|
||||
pageLoading.value = true
|
||||
const res = await dailyCheckApi
|
||||
.getCheckRecord({
|
||||
deviceId: id,
|
||||
dataType: 'maintain',
|
||||
submitFlag: '0',
|
||||
cancelFlag: '0'
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
if (!res.checkUserName) {
|
||||
res.checkUserName = nx.$store('user').userInfo.realname
|
||||
res.checkUserId = nx.$store('user').userInfo.id
|
||||
}
|
||||
if (!res.checkDate) {
|
||||
res.checkDate = nx.$dayjs().format('YYYY-MM-DD 00:00:00')
|
||||
}
|
||||
detailInfo.value = res
|
||||
modalType.value = res.submitFlag
|
||||
lockOrientation('landscape')
|
||||
}
|
||||
|
||||
const modalShow = ref(false)
|
||||
const modalType = ref('')
|
||||
function handleSubmit(type) {
|
||||
if (!detailInfo.value.checkUserName) {
|
||||
return uni.showToast({
|
||||
title: '请输入维护保养人',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
if (!detailInfo.value.checkDate) {
|
||||
return uni.showToast({
|
||||
title: '请选择维护保养日期',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
if (type === '1') {
|
||||
// 新增维护保养项验证
|
||||
const items = detailInfo.value.maintainItemList || []
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
const itemNumber = `第${i + 1}项`
|
||||
if (!item.checkRemark?.trim()) {
|
||||
return uni.showToast({ title: `${itemNumber}维护保养情况未填写`, icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
modalType.value = type
|
||||
modalShow.value = true
|
||||
console.log(detailInfo.value)
|
||||
}
|
||||
|
||||
const submitLoading = ref(false)
|
||||
async function confirm() {
|
||||
if (submitLoading.value) return
|
||||
submitLoading.value = true
|
||||
await dailyCheckApi.submit({ ...detailInfo.value, submitFlag: modalType.value }).finally(() => {
|
||||
submitLoading.value = false
|
||||
modalShow.value = false
|
||||
reset()
|
||||
})
|
||||
}
|
||||
function reset() {
|
||||
detailInfo.value = {}
|
||||
}
|
||||
function handleCheckRecord() {
|
||||
let deviceInfo = {
|
||||
id: detailInfo.value.deviceId,
|
||||
deviceName: detailInfo.value.deviceName
|
||||
}
|
||||
nx.$store('biz').deviceInfo = deviceInfo
|
||||
nx.$router.go('/pages/lims/deviceBusMaintain/list')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-sticky {
|
||||
top: 0 !important;
|
||||
}
|
||||
.container {
|
||||
.required-star {
|
||||
color: red;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.content {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
padding-top: 10px;
|
||||
font-size: 18px;
|
||||
.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;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
100
pages/lims/deviceBusMaintain/list.vue
Normal file
100
pages/lims/deviceBusMaintain/list.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<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 :style="{ height: isComponent ? '70vh' : '72vh' }">
|
||||
<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>
|
||||
<maintain-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import MaintainDetailPopup from './detail.vue'
|
||||
import dailyCheckApi from '@/nx/api/dailyCheck'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
let props = defineProps({
|
||||
isComponent: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const column = reactive([
|
||||
{
|
||||
label: '维护保养人',
|
||||
name: 'checkUserName',
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
label: '维护保养日期',
|
||||
name: 'checkDate',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
dataType: 'maintain',
|
||||
deviceId: deviceId.value
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: dailyCheckApi.list
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
104
pages/lims/deviceBusUseRecord/detail.vue
Normal file
104
pages/lims/deviceBusUseRecord/detail.vue
Normal 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>
|
||||
330
pages/lims/deviceBusUseRecord/index.vue
Normal file
330
pages/lims/deviceBusUseRecord/index.vue
Normal 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/lims/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/lims/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>
|
||||
164
pages/lims/deviceBusUseRecord/list.vue
Normal file
164
pages/lims/deviceBusUseRecord/list.vue
Normal 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>
|
||||
36
pages/lims/deviceBusUseRecord/useRecord.api.js
Normal file
36
pages/lims/deviceBusUseRecord/useRecord.api.js
Normal 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 }
|
||||
})
|
||||
}
|
||||
16
pages/lims/documentList/document.api.js
Normal file
16
pages/lims/documentList/document.api.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from '@/nx/request'
|
||||
|
||||
export function getDeviceDocumentList(params) {
|
||||
return request({
|
||||
url: '/lims/document/device-relation/list',
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function getDocumentInfoById(id) {
|
||||
return request({
|
||||
url: '/lims/document/info/queryById',
|
||||
method: 'GET',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
117
pages/lims/documentList/index.vue
Normal file
117
pages/lims/documentList/index.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<up-modal :show="fileShow" :title="fileInfo.documentName" confirmText="关闭" @confirm="fileShow = false">
|
||||
<scroll-view scroll-y="true" style="max-height: 60vh">
|
||||
<uni-card>
|
||||
<up-parse :content="fileInfo.documentContent"></up-parse>
|
||||
</uni-card>
|
||||
</scroll-view>
|
||||
</up-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import { getDeviceDocumentList, getDocumentInfoById } from './document.api'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '文档名称',
|
||||
name: 'documentName',
|
||||
width: 260
|
||||
},
|
||||
{
|
||||
label: '文档描述',
|
||||
name: 'documentAbstract',
|
||||
width: 420
|
||||
},
|
||||
{
|
||||
label: '文档类型',
|
||||
name: 'documentType',
|
||||
width: 120
|
||||
},
|
||||
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '查看', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
deviceBusInfoId: deviceId.value,
|
||||
searchRelationType: 'device'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: getDeviceDocumentList,
|
||||
processData: data => {
|
||||
return data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
documentType: item.documentType === 'file' ? '文件' : '文本'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const fileShow = ref(false)
|
||||
const fileInfo = ref({})
|
||||
async function handleDetail(row) {
|
||||
console.log(row)
|
||||
|
||||
fileInfo.value = row
|
||||
if (row.documentType === '文本') {
|
||||
const { documentContent } = await getDocumentInfoById(row.documentBusInfoId)
|
||||
fileInfo.value.documentContent = documentContent
|
||||
fileShow.value = true
|
||||
} else {
|
||||
nx.$router.go('/pages/lims/documentList/preview', { documentUrl: row.documentUrl })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
32
pages/lims/documentList/preview.vue
Normal file
32
pages/lims/documentList/preview.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<up-icon size="20" class="close-icon" @click="handleClose" name="close-circle"></up-icon>
|
||||
<web-view :src="previewUrl"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
let previewUrl = ref('')
|
||||
onLoad(option => {
|
||||
let url = encodeURIComponent(
|
||||
CryptoJS.enc.Utf8.parse(getImgBaseUrl() + option.documentUrl).toString(CryptoJS.enc.Base64)
|
||||
)
|
||||
previewUrl.value = getImgBaseUrl() + '/preview/onlinePreview?url=' + url
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
z-index: 999999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
93
pages/lims/giveback/detail.vue
Normal file
93
pages/lims/giveback/detail.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<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 class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>归还人:
|
||||
<text class="value">{{ detailInfo.givebackOper }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>归还日期:
|
||||
<text class="value">{{ detailInfo.givebackDate }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>接收人:
|
||||
<text class="value">{{ detailInfo.givebackReceiveOper }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.remark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
|
||||
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')
|
||||
}
|
||||
let commentWf = ref([])
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 80vh;
|
||||
width: 75vw;
|
||||
padding: 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>
|
||||
99
pages/lims/giveback/list.vue
Normal file
99
pages/lims/giveback/list.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<giveback-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GivebackDetailPopup from './detail.vue'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import { givebackList } from '@/nx/api/deviceInfo'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '归还人',
|
||||
name: 'givebackOper',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '归还日期',
|
||||
name: 'givebackDate',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '接收人',
|
||||
name: 'givebackReceiveOper',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
name: 'remark',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
deviceBusInfoId: deviceId.value,
|
||||
givebackStatus: '已归还'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: givebackList
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
44
pages/lims/index/index.vue
Normal file
44
pages/lims/index/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<view>
|
||||
<navbar-back title="设备管理系统" :autoBack="false" leftIcon="" :leftText="`您好!${userInfo.realname}`">
|
||||
<u-icon @click="popupShow = true" size="28" color="#FFF" name="account-fill" />
|
||||
</navbar-back>
|
||||
<up-grid :border="false" :col="gridCol">
|
||||
<up-grid-item
|
||||
class="mb20 mt20"
|
||||
v-for="(item, listIndex) in list"
|
||||
:key="listIndex"
|
||||
@click="nx.$router.go(item.url)"
|
||||
>
|
||||
<image style="width: 80px; height: 80px" :src="`/static/images/menus/${item.icon}.png`"></image>
|
||||
<text class="grid-text">{{ item.name }}</text>
|
||||
</up-grid-item>
|
||||
</up-grid>
|
||||
<mePopup v-model:show="popupShow" />
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref, computed } from 'vue'
|
||||
import nx from '@/nx'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
import mePopup from '@/pages/index/me-popup.vue'
|
||||
|
||||
const { gridCol } = useGridCol([400, 600], [2, 3, 4])
|
||||
let popupShow = ref(false)
|
||||
let list = reactive([
|
||||
{ url: '/pages/lims/deviceBusDailyCheck/index', name: '点检', icon: 'dailyCheck' },
|
||||
{ url: '/pages/lims/deviceBusMaintain/index', name: '维护保养', icon: 'maintain' },
|
||||
{ url: '/pages/lims/deviceBusUseRecord/index', name: '使用', icon: 'useRecord' },
|
||||
{ url: '/pages/lims/deviceBusInfo/index', name: '设备查询', icon: 'baseInfo' },
|
||||
{ url: '/pages/lims/knowledge/index', name: '知识库查询', icon: 'knowledge' }
|
||||
])
|
||||
|
||||
const roleMenus = computed(() => nx.$store('user').roleMenus)
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grid-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
148
pages/lims/knowledge/index.vue
Normal file
148
pages/lims/knowledge/index.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<view>
|
||||
<up-sticky>
|
||||
<navbar-back title="知识库查询"></navbar-back>
|
||||
</up-sticky>
|
||||
|
||||
<uni-card spacing="0">
|
||||
<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: 72vh">
|
||||
<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>
|
||||
<up-modal :show="fileShow" :title="fileInfo.documentName" confirmText="关闭" @confirm="fileShow = false">
|
||||
<scroll-view scroll-y="true" style="max-height: 60vh">
|
||||
<uni-card>
|
||||
<up-parse :content="fileInfo.documentContent"></up-parse>
|
||||
</uni-card>
|
||||
</scroll-view>
|
||||
</up-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import { getDeviceDocumentList } from './knowledge.api'
|
||||
import { getDocumentInfoById } from '../documentList/document.api'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import { throttle } from '@/uview-plus'
|
||||
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '文档名称',
|
||||
name: 'documentName',
|
||||
width: 260
|
||||
},
|
||||
{
|
||||
label: '文档描述',
|
||||
name: 'documentAbstract',
|
||||
width: 420
|
||||
},
|
||||
{
|
||||
label: '文档类型',
|
||||
name: 'documentType',
|
||||
width: 120
|
||||
},
|
||||
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '查看', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
documentName: keyword.value
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: getDeviceDocumentList,
|
||||
processData: data => {
|
||||
return data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
documentType: item.documentType === 'file' ? '文件' : '文本'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
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()
|
||||
}
|
||||
const fileShow = ref(false)
|
||||
const fileInfo = ref({})
|
||||
async function handleDetail(row) {
|
||||
console.log(row)
|
||||
|
||||
fileInfo.value = row
|
||||
if (row.documentType === '文本') {
|
||||
const { documentContent } = await getDocumentInfoById(row.documentBusInfoId)
|
||||
fileInfo.value.documentContent = documentContent
|
||||
fileShow.value = true
|
||||
} else {
|
||||
nx.$router.go('/pages/lims/documentList/preview', { documentUrl: row.documentUrl })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-sticky {
|
||||
top: 0 !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>
|
||||
16
pages/lims/knowledge/knowledge.api.js
Normal file
16
pages/lims/knowledge/knowledge.api.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from '@/nx/request'
|
||||
|
||||
export function getDeviceDocumentList(params) {
|
||||
return request({
|
||||
url: 'lims/knowledge/document-relation/list',
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function getDocumentInfoById(id) {
|
||||
return request({
|
||||
url: '/lims/knowledge/base/queryById',
|
||||
method: 'GET',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
118
pages/lims/periodCheckList/detail.vue
Normal file
118
pages/lims/periodCheckList/detail.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<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 class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>核查对象:
|
||||
<text class="value">{{ detailInfo.deviceName }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>别名:
|
||||
<text class="value">{{ detailInfo.alias }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>核查日期:
|
||||
<text class="value">{{ detailInfo.checkDate }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>核查方法:
|
||||
<text class="value">{{ detailInfo.checkAccording }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row class="flex-wrap">
|
||||
<up-col span="12"
|
||||
>核查方法描述:
|
||||
<text class="value">{{ detailInfo.checkAccordingRemark }}</text>
|
||||
</up-col>
|
||||
<up-col span="12"
|
||||
>核查记录:
|
||||
<up-parse style="background: #f3f4f6" :content="processedContent"></up-parse>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.checkRemark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { getImgBaseUrl } from '@/defaultBaseUrl'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
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 processedContent = computed(() => {
|
||||
if (!detailInfo.value.checkContent) {
|
||||
return ''
|
||||
}
|
||||
return detailInfo.value.checkContent.replace(
|
||||
/<img([^>]+?)src="((?!http)[^"]*?)(file\/[^"]*)"/gi,
|
||||
(match, attributes, prefix, filePath) => {
|
||||
return `<img${attributes}src="${getImgBaseUrl()}/${filePath}"`
|
||||
}
|
||||
)
|
||||
})
|
||||
const emit = defineEmits(['close', 'open'])
|
||||
function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
let commentWf = ref([])
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 85vh;
|
||||
width: 80vw;
|
||||
padding: 10px;
|
||||
.u-row {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
:deep(.uicon-close) {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
</style>
|
||||
101
pages/lims/periodCheckList/index.vue
Normal file
101
pages/lims/periodCheckList/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<period-check-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import PeriodCheckDetailPopup from './detail.vue'
|
||||
import { list } from './period.api'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '核查日期',
|
||||
name: 'checkDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '核查人',
|
||||
name: 'checkPersonName',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '核查方法',
|
||||
name: 'checkAccording',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '期间核查频次',
|
||||
name: 'frequencyRemark',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
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,
|
||||
effectiveFlag: '1',
|
||||
wfStatus: 'finished',
|
||||
cancelFlag: '0'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: list
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
9
pages/lims/periodCheckList/period.api.js
Normal file
9
pages/lims/periodCheckList/period.api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/nx/request'
|
||||
|
||||
export function list(params) {
|
||||
return request({
|
||||
url: '/lims/bus/deviceBusPeriodCheck/queryPageList',
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
130
pages/lims/repair/detail.vue
Normal file
130
pages/lims/repair/detail.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<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 class="flex-wrap">
|
||||
<up-col :span="gridCol"
|
||||
>设备名称:
|
||||
<text class="value">{{ detailInfo.deviceName }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>别名:
|
||||
<text class="value">{{ detailInfo.alias }}</text>
|
||||
</up-col>
|
||||
<up-col span="4"
|
||||
>设备编号:
|
||||
<text class="value">{{ detailInfo.deviceCode }}</text>
|
||||
</up-col>
|
||||
<up-col :span="gridCol"
|
||||
>生产厂家:
|
||||
<text class="value">{{ detailInfo.manufacturer }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="6"
|
||||
>使用部门:
|
||||
<text class="value">{{ detailInfo.deviceDeptName }}</text>
|
||||
</up-col>
|
||||
<up-col span="6"
|
||||
>设备负责人:
|
||||
<text class="value">{{ detailInfo.deviceManagerUserName }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>故障分析及处理方案:
|
||||
<text class="value">{{ detailInfo.repairAnalysis }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>故障判定:
|
||||
<text class="value">{{ detailInfo.repairDetermine }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>维修事项及配件更换:
|
||||
<text class="value">{{ detailInfo.repairContent }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>维修后仪器设备状态:
|
||||
<text class="value">{{ detailInfo.repairResult }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.remark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</scroll-view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { useGridCol } from '@/nx/hooks/useGridCol'
|
||||
const { gridCol } = useGridCol([700], [6, 4])
|
||||
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')
|
||||
}
|
||||
let commentWf = ref({})
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 80vh;
|
||||
width: 80vw;
|
||||
padding: 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>
|
||||
104
pages/lims/repair/list.vue
Normal file
104
pages/lims/repair/list.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import DetailPopup from './detail.vue'
|
||||
import { repairList } from '@/nx/api/deviceInfo'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '申请人',
|
||||
name: 'applyUserName',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '报修时间',
|
||||
name: 'applyTime',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '设备责任人',
|
||||
name: 'deviceManagerUserName',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '设备使用部门',
|
||||
name: 'deviceDeptName',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '故障判定',
|
||||
name: 'repairDetermine',
|
||||
width: 240
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
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,
|
||||
wfStatus: 'finished'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: repairList
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
72
pages/lims/scrap/detail.vue
Normal file
72
pages/lims/scrap/detail.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<uni-card style="height: 100%" spacing="0">
|
||||
<scroll-view scroll-y="true" class="content">
|
||||
<up-row>
|
||||
<up-col span="6"
|
||||
>报废类型:
|
||||
<text class="value">{{ scrapInfo.scrapType }}</text>
|
||||
</up-col>
|
||||
<up-col span="6"
|
||||
>报废日期:
|
||||
<text class="value">{{ scrapInfo.scrapDate }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>报废原因:
|
||||
<text class="value">{{ scrapInfo.scrapReason }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
<view class="p20"></view>
|
||||
</scroll-view>
|
||||
</uni-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onBeforeMount } from 'vue'
|
||||
import { scrapDetailList } from '@/nx/api/deviceInfo'
|
||||
import nx from '@/nx'
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
getDetailInfo(id)
|
||||
})
|
||||
let scrapInfo = ref({})
|
||||
let commentWf = ref([])
|
||||
async function getDetailInfo(id) {
|
||||
const records = await scrapDetailList({
|
||||
deviceBusInfoId: id
|
||||
})
|
||||
if (records.length > 0) {
|
||||
scrapInfo.value = records[0]
|
||||
if (records[0].commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(records[0].commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
height: 80vh;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
padding: 10px;
|
||||
.u-row {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
89
pages/lims/stop/detail.vue
Normal file
89
pages/lims/stop/detail.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<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="12"
|
||||
>停用日期:
|
||||
<text class="value">{{ detailInfo.stopDate }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>借用原因:
|
||||
<text class="value">{{ detailInfo.stopReason }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<up-row>
|
||||
<up-col span="12"
|
||||
>备注:
|
||||
<text class="value">{{ detailInfo.remark }}</text>
|
||||
</up-col>
|
||||
</up-row>
|
||||
<wf-comment :commentWf="commentWf" />
|
||||
</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')
|
||||
}
|
||||
let commentWf = ref([])
|
||||
function handleOpen() {
|
||||
detailInfo.value = props.checkInfo
|
||||
if (props.checkInfo.commentJson) {
|
||||
try {
|
||||
commentWf.value = JSON.parse(props.checkInfo.commentJson)
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '解析数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
font-size: 18px;
|
||||
height: 80vh;
|
||||
width: 75vw;
|
||||
padding: 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>
|
||||
94
pages/lims/stop/list.vue
Normal file
94
pages/lims/stop/list.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card spacing="0">
|
||||
<view style="height: 72vh">
|
||||
<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>
|
||||
<stop-detail-popup :show="detailShow" :checkInfo="checkInfo" @close="detailShow = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { setCellHeaderStyle, setCellStyle } from '@/nx/config/zbTable'
|
||||
import { stopList } from '@/nx/api/deviceInfo'
|
||||
import StopDetailPopup from './detail.vue'
|
||||
import { useListData } from '@/nx/hooks/usePageListData'
|
||||
import nx from '@/nx'
|
||||
|
||||
const column = reactive([
|
||||
{
|
||||
label: '停用日期',
|
||||
name: 'stopDate',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
label: '停用原因',
|
||||
name: 'stopReason',
|
||||
width: 280
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
name: 'remark',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
renders: [{ name: '详情', func: 'detail' }]
|
||||
}
|
||||
])
|
||||
const deviceId = ref('')
|
||||
const deviceText = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
const { id, deviceName } = nx.$store('biz').deviceInfo
|
||||
deviceId.value = id
|
||||
deviceText.value = deviceName
|
||||
getInitData()
|
||||
})
|
||||
const searchParams = computed(() => ({
|
||||
deviceBusInfoId: deviceId.value,
|
||||
stopListStatus: '已停用'
|
||||
}))
|
||||
const { listData, loadingData, scrollToLower, loadStatus, getInitData } = useListData({
|
||||
searchParams,
|
||||
api: stopList
|
||||
})
|
||||
const zbTableRef = ref()
|
||||
function pullUpLoadingAction() {
|
||||
if (loadingData.value) return
|
||||
if (loadStatus.value === 'nomore') {
|
||||
zbTableRef.value.pullUpCompleteLoading('ok')
|
||||
} else {
|
||||
scrollToLower()
|
||||
}
|
||||
}
|
||||
|
||||
const detailShow = ref(false)
|
||||
const checkInfo = ref({})
|
||||
function handleDetail(row) {
|
||||
checkInfo.value = row
|
||||
detailShow.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.zb-table uni-button[type='primary']) {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,342 +0,0 @@
|
||||
<template>
|
||||
<s-layout class="login-container" title="登录/注册" :bgStyle="{
|
||||
background: '#fff'
|
||||
}">
|
||||
<view class="login-wrap">
|
||||
<!-- 1. 统一登录组件 (整合账号密码登录和短信登录) -->
|
||||
<unified-login
|
||||
v-if="authType === 'accountLogin' || authType === 'smsLogin'"
|
||||
:agreeStatus="state.protocol"
|
||||
@onConfirm="onConfirm"
|
||||
/>
|
||||
|
||||
<!-- 3. 忘记密码 resetPassword-->
|
||||
<!-- <reset-password v-if="authType === 'resetPassword'" /> -->
|
||||
|
||||
<!-- 4. 绑定手机号 changeMobile -->
|
||||
<change-mobile v-if="authType === 'changeMobile'" />
|
||||
|
||||
<!-- 5. 修改密码 changePassword-->
|
||||
<changePassword v-if="authType === 'changePassword'" />
|
||||
|
||||
<!-- 6. 微信小程序授权 -->
|
||||
<mp-authorization v-if="authType === 'mpAuthorization'" />
|
||||
|
||||
<!-- 7. 第三方登录 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="auto-login-box ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
>
|
||||
<!-- 7.1 微信小程序的快捷登录 -->
|
||||
<view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
|
||||
<view class="register-title">还没有账号?</view>
|
||||
<button
|
||||
class="ss-reset-button login-btn"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
style="color: var(--ui-BG-Main, #0055A2) !important"
|
||||
>
|
||||
快捷登录
|
||||
</button>
|
||||
<view class="circle" />
|
||||
</view>
|
||||
|
||||
<!-- 7.2 微信的公众号、App、小程序的登录,基于 openid + code -->
|
||||
<button
|
||||
v-if="
|
||||
['WechatOfficialAccount', 'WechatMiniProgram', 'App'].includes(sheep.$platform.name) &&
|
||||
sheep.$platform.isWechatInstalled
|
||||
"
|
||||
@tap="thirdLogin('wechat')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- 7.3 iOS 登录 TODO 芋艿:等后面搞 App 再弄 -->
|
||||
<button
|
||||
v-if="sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
@tap="thirdLogin('apple')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/apple.png')"
|
||||
/>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 用户协议的勾选 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="agreement-box ss-flex ss-flex-col ss-col-center"
|
||||
:class="{ shake: currentProtocol }"
|
||||
>
|
||||
<view class="agreement-title ss-m-b-20">
|
||||
请阅读并同意以下协议:
|
||||
</view>
|
||||
|
||||
<view class="agreement-options-container">
|
||||
<view class="agreement-option" @tap="onAgree">
|
||||
<view class="radio-container ss-flex ss-col-center">
|
||||
<view
|
||||
class="custom-radio"
|
||||
:class="{ 'custom-radio-checked': state.protocol === true }"
|
||||
>
|
||||
<view v-if="state.protocol === true" class="radio-dot"></view>
|
||||
</view>
|
||||
<view class="agreement-text ss-flex ss-col-center ss-m-l-8">
|
||||
我已阅读并同意遵守
|
||||
<view class="tcp-text" @tap.stop="onProtocol('用户协议')"> 《用户协议》 </view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')"> 《隐私协议》 </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="safe-box" />
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import unifiedLogin from '@/sheep/components/s-auth-modal/components/unified-login.vue';
|
||||
import changeMobile from '@/sheep/components/s-auth-modal/components/change-mobile.vue';
|
||||
import changePassword from '@/sheep/components/s-auth-modal/components/change-password.vue';
|
||||
import mpAuthorization from '@/sheep/components/s-auth-modal/components/mp-authorization.vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { navigateAfterLogin } from '@/sheep/helper/login-redirect';
|
||||
|
||||
const authType = ref('accountLogin');
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.authType) {
|
||||
authType.value = options.authType;
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
protocol: false, // false 表示未勾选,true 表示已同意
|
||||
});
|
||||
|
||||
const currentProtocol = ref(false);
|
||||
|
||||
// 同意协议
|
||||
function onAgree() {
|
||||
state.protocol = !state.protocol;
|
||||
uni.showToast({
|
||||
title: state.protocol ? '已勾选协议' : '已取消勾选',
|
||||
icon: state.protocol ? 'success' : 'none',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
|
||||
// 查看协议
|
||||
function onProtocol(title) {
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title,
|
||||
});
|
||||
}
|
||||
|
||||
// 点击登录 / 注册事件
|
||||
function onConfirm(e) {
|
||||
currentProtocol.value = e;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 第三方授权登陆(微信小程序、Apple)
|
||||
const thirdLogin = async (provider) => {
|
||||
if (state.protocol !== true) {
|
||||
currentProtocol.value = true;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
|
||||
sheep.$helper.toast('请先勾选协议');
|
||||
return;
|
||||
}
|
||||
const loginRes = await sheep.$platform.useProvider(provider).login();
|
||||
if (loginRes) {
|
||||
const userInfo = await sheep.$store('user').getInfo();
|
||||
// 如果用户已经有头像和昵称,不需要再次授权
|
||||
if (userInfo.avatar && userInfo.nickname) {
|
||||
// 登录成功后跳转到首页
|
||||
navigateAfterLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发小程序授权信息弹框
|
||||
// #ifdef MP-WEIXIN
|
||||
authType.value = 'mpAuthorization';
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
|
||||
// 微信小程序的“手机号快速验证”:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
|
||||
const getPhoneNumber = async (e) => {
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
sheep.$helper.toast('快捷登录失败');
|
||||
return;
|
||||
}
|
||||
let result = await sheep.$platform.useProvider().mobileLogin(e.detail);
|
||||
if (result) {
|
||||
// 登录成功后跳转到首页
|
||||
navigateAfterLogin();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/sheep/components/s-auth-modal/index.scss';
|
||||
.login-container {
|
||||
.login-wrap {
|
||||
padding-top: 100rpx;
|
||||
}
|
||||
}
|
||||
.shake {
|
||||
animation: shake 0.05s linear 4 alternate;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from {
|
||||
transform: translateX(-10rpx);
|
||||
}
|
||||
to {
|
||||
transform: translateX(10rpx);
|
||||
}
|
||||
}
|
||||
|
||||
.register-box {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
.register-btn {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.register-title {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.or-title {
|
||||
margin: 0 16rpx;
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
.login-btn {
|
||||
color: var(--ui-BG-Main, #0055A2) !important;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.circle {
|
||||
position: absolute;
|
||||
right: 0rpx;
|
||||
top: 18rpx;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--ui-BG-Main, #0055A2) !important;
|
||||
}
|
||||
}
|
||||
.safe-box {
|
||||
height: calc(constant(safe-area-inset-bottom) / 5 * 3);
|
||||
height: calc(env(safe-area-inset-bottom) / 5 * 3);
|
||||
}
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main, #0055A2) !important;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
font-size: 28rpx;
|
||||
color: $dark-9;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding-left: 60rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.protocol-status {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
margin-top: 8rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
display: inline-block;
|
||||
|
||||
&.protocol-agreed {
|
||||
color: #52c41a;
|
||||
background-color: rgba(82, 196, 26, 0.1);
|
||||
}
|
||||
|
||||
&.protocol-refused {
|
||||
color: #ff4d4f;
|
||||
background-color: rgba(255, 77, 79, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-options-container {
|
||||
width: 100%;
|
||||
padding-left: 100rpx;
|
||||
}
|
||||
|
||||
.agreement-option {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 自定义radio样式 - 同意 */
|
||||
.custom-radio {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.custom-radio-checked {
|
||||
border-color: var(--ui-BG-Main, #409eff);
|
||||
background-color: var(--ui-BG-Main, #409eff);
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
190
pages/login/login.vue
Normal file
190
pages/login/login.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="setting">
|
||||
<view></view>
|
||||
<view class="x-f" @click="handleUrlConfig"
|
||||
><u-icon color="#0055A2" size="25" name="setting"></u-icon><text>请求地址设置</text></view
|
||||
>
|
||||
</view>
|
||||
<view class="header">
|
||||
<image src="/static/images/login/logo.png"></image>
|
||||
</view>
|
||||
|
||||
<view class="list">
|
||||
<view class="list-call">
|
||||
<u-icon name="account" color="#0055a2" size="30"></u-icon>
|
||||
<input class="sl-input" v-model="loginInfo.username" placeholder="请输入账号" />
|
||||
</view>
|
||||
<view class="list-call">
|
||||
<u-icon name="lock" color="#0055a2" size="30"></u-icon>
|
||||
<input
|
||||
class="sl-input"
|
||||
v-model="loginInfo.password"
|
||||
type="text"
|
||||
maxlength="32"
|
||||
placeholder="请输入密码"
|
||||
password="true"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="login-btn">
|
||||
<u-button
|
||||
style="width: 60%"
|
||||
size="large"
|
||||
color="linear-gradient(-90deg, rgb(0, 85, 162), rgb(31 127 247))"
|
||||
shape="circle"
|
||||
text="登录"
|
||||
loadingText="登录中..."
|
||||
:loading="loading"
|
||||
@click="bindLogin"
|
||||
></u-button>
|
||||
</view>
|
||||
<!-- 验证码弹窗 -->
|
||||
<n-verify ref="verifyRef" @success="onCaptchaSuccess" @error="onCaptchaError" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import nx from '@/nx'
|
||||
import callCheckUpdate from '@/nx/utils/check-update'
|
||||
const loading = ref(false)
|
||||
const captchaEnable = true
|
||||
let loginInfo = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
captchaVerification: ''
|
||||
})
|
||||
onShow(() => {
|
||||
//检查APP更新
|
||||
// #ifdef APP-PLUS
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
setTimeout(() => {
|
||||
callCheckUpdate()
|
||||
}, 1500)
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
async function bindLogin() {
|
||||
if (!loginInfo.username || !loginInfo.password) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请输入账号和密码'
|
||||
})
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
// 如果启用验证码,先显示验证码
|
||||
if (captchaEnable) {
|
||||
showCaptcha()
|
||||
} else {
|
||||
// 直接登录
|
||||
performAccountLogin()
|
||||
}
|
||||
}
|
||||
const verifyRef = ref(null)
|
||||
// 显示验证码弹窗
|
||||
function showCaptcha() {
|
||||
if (verifyRef.value) {
|
||||
verifyRef.value.show()
|
||||
}
|
||||
}
|
||||
|
||||
function handleUrlConfig() {
|
||||
nx.$router.go('/pages/setting/UrlConfig')
|
||||
}
|
||||
|
||||
// 验证码验证成功回调
|
||||
function onCaptchaSuccess(data) {
|
||||
loginInfo.captchaVerification = data.captchaVerification
|
||||
performAccountLogin()
|
||||
}
|
||||
|
||||
// 执行账号登录
|
||||
async function performAccountLogin() {
|
||||
await nx
|
||||
.$store('user')
|
||||
.login(loginInfo)
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
loginInfo.captchaVerification = ''
|
||||
})
|
||||
}
|
||||
// 验证码验证失败回调
|
||||
function onCaptchaError(error) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: error?.message || '验证码验证失败'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
.setting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 16px;
|
||||
padding: 20px;
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.header image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 20px;
|
||||
padding-left: 20%;
|
||||
padding-right: 20%;
|
||||
}
|
||||
|
||||
.list-call {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
color: #333333;
|
||||
border-bottom: 0.5px solid #e2e2e2;
|
||||
}
|
||||
|
||||
.list-call .sl-input {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.button-login {
|
||||
color: #ffffff;
|
||||
font-size: 34px;
|
||||
width: 470px;
|
||||
height: 100px;
|
||||
background: linear-gradient(-90deg, rgb(0, 85, 162), rgb(21, 97, 192));
|
||||
border-radius: 50px;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
210
pages/login/reg.vue
Normal file
210
pages/login/reg.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="header">
|
||||
<image src="/static/images/login/logo.jpg"></image>
|
||||
</view>
|
||||
|
||||
<view class="list">
|
||||
<view class="list-call">
|
||||
<input class="sl-input" v-model="phone" type="number" maxlength="11" placeholder="手机号" />
|
||||
</view>
|
||||
<view class="list-call">
|
||||
<input class="sl-input" v-model="password" type="text" maxlength="32" placeholder="登录密码"
|
||||
:password="!showPassword" />
|
||||
</view>
|
||||
<view class="list-call">
|
||||
<input class="sl-input" v-model="code" type="number" maxlength="4" placeholder="验证码" />
|
||||
<view class="yzm" :class="{ yzms: second>0 }" @tap="getcode">{{yanzhengma}}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="button-login" hover-class="button-hover" @tap="bindLogin">
|
||||
<text>注册</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
reactive,
|
||||
onUnmounted,
|
||||
computed
|
||||
} from 'vue';
|
||||
const state = reactive({
|
||||
phone: '',
|
||||
password: '',
|
||||
code: '',
|
||||
showPassword: false,
|
||||
second: 0
|
||||
});
|
||||
|
||||
function clear() {
|
||||
clearInterval(js)
|
||||
js = null
|
||||
state.second = 0
|
||||
}
|
||||
|
||||
function display() {
|
||||
state.showPassword = !state.showPassword
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
clear()
|
||||
});
|
||||
// 注册
|
||||
function bindLogin() {
|
||||
|
||||
}
|
||||
|
||||
function getcode() {
|
||||
if (state.phone.length != 11) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '手机号不正确'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (state.second > 0) {
|
||||
return;
|
||||
}
|
||||
state.second = 60;
|
||||
//请求业务
|
||||
js = setInterval(function() {
|
||||
state.second--;
|
||||
if (state.second == 0) {
|
||||
state.clear()
|
||||
}
|
||||
}, 1000)
|
||||
// uni.request({
|
||||
// url: 'http://***/getcode.html', //仅为示例,并非真实接口地址。
|
||||
// data: {
|
||||
// phone: this.phone,
|
||||
// type: 'reg'
|
||||
// },
|
||||
// method: 'POST',
|
||||
// dataType: 'json',
|
||||
// success: (res) => {
|
||||
// if (res.data.code != 200) {
|
||||
// uni.showToast({
|
||||
// title: res.data.msg,
|
||||
// icon: 'none'
|
||||
// });
|
||||
// } else {
|
||||
// uni.showToast({
|
||||
// title: res.data.msg
|
||||
// });
|
||||
// js = setInterval(function() {
|
||||
// _this.second--;
|
||||
// if (_this.second == 0) {
|
||||
// _this.clear()
|
||||
// }
|
||||
// }, 1000)
|
||||
// }
|
||||
// },
|
||||
// fail() {
|
||||
// this.second == 0
|
||||
// }
|
||||
// });
|
||||
}
|
||||
const yanzhengma = computed(() => {
|
||||
if (state.second == 0) {
|
||||
return '获取验证码';
|
||||
} else {
|
||||
if (state.second < 10) {
|
||||
return '重新获取0' + state.second;
|
||||
} else {
|
||||
return '重新获取' + state.second;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 161rpx;
|
||||
height: 161rpx;
|
||||
border-radius: 50%;
|
||||
margin-top: 30rpx;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.header image {
|
||||
width: 161rpx;
|
||||
height: 161rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 50rpx;
|
||||
padding-left: 70rpx;
|
||||
padding-right: 70rpx;
|
||||
}
|
||||
|
||||
.list-call {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 100rpx;
|
||||
color: #333333;
|
||||
border-bottom: 0.5px solid #e2e2e2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.list-call .sl-input {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-size: 32rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.yzm {
|
||||
color: #FF7D13;
|
||||
font-size: 24rpx;
|
||||
line-height: 64rpx;
|
||||
padding-left: 10rpx;
|
||||
padding-right: 10rpx;
|
||||
width: auto;
|
||||
height: 64rpx;
|
||||
border: 1rpx solid #FFA800;
|
||||
border-radius: 50rpx;
|
||||
}
|
||||
|
||||
.yzms {
|
||||
color: #999999 !important;
|
||||
border: 1rpx solid #999999;
|
||||
}
|
||||
|
||||
.button-login {
|
||||
color: #FFFFFF;
|
||||
font-size: 34rpx;
|
||||
width: 470rpx;
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
background: linear-gradient(-90deg, rgba(193, 25, 32, 1), rgba(238, 38, 38, 1));
|
||||
border-radius: 50rpx;
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
|
||||
.button-hover {
|
||||
background: linear-gradient(-90deg, rgba(193, 25, 32, 0.8), rgba(238, 38, 38, 0.8));
|
||||
}
|
||||
</style>
|
||||
89
pages/me/aboutMe.vue
Normal file
89
pages/me/aboutMe.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<view>
|
||||
<view>
|
||||
<view class="aboutme">
|
||||
<view class="content">
|
||||
<image class="logo" src="../../static/images/logo_mas.png"></image>
|
||||
<view style="font-size: 40px; padding-top: 20px">{{ state.appName }}</view>
|
||||
<view style="padding-top: 20px; font-size: 22px">当前APP版本:{{ state.appVersion }}</view>
|
||||
<rich-text :nodes="state.aboutMe"></rich-text>
|
||||
</view>
|
||||
<view class="foot">
|
||||
<view class="foot-phone">技术支持:云南志者竟成科技有限公司</view>
|
||||
<!-- <view class="foot-email">电子邮箱:support@will-way.cn</view>-->
|
||||
<!-- <view class="copyright">Copyright © 2019-{{ copyrightYear }} 云南志者竟成科技有限公司</view>-->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { reactive } from 'vue'
|
||||
let state = reactive({
|
||||
pageHeight: 0,
|
||||
appVersion: '1.1.0',
|
||||
appName: '凉山矿业设备管理系统',
|
||||
copyrightYear: '2019',
|
||||
aboutMe:
|
||||
'<div style="text-align:left;font-size:20px;padding:18px"><p style="text-indent:2em;line-height:32px"></p></div>'
|
||||
})
|
||||
onLoad(() => {
|
||||
// #ifdef APP-PLUS
|
||||
let appId = plus.runtime.appid
|
||||
plus.runtime.getProperty(appId, function (wgtinfo) {
|
||||
state.appName = wgtinfo.name
|
||||
state.appVersion = wgtinfo.version
|
||||
console.log(wgtinfo)
|
||||
})
|
||||
// #endif
|
||||
state.copyrightYear = new Date().getFullYear()
|
||||
//获取屏幕高度,减去4px是解决在pda上会使用屏幕高度会出现滚动条
|
||||
state.pageHeight = uni.getSystemInfoSync().windowHeight - 4
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aboutme {
|
||||
height: calc(100vh - 70px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 5% 1%;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.foot {
|
||||
margin: 5% 1%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.foot-phone {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.foot-email {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
48
pages/me/index.vue
Normal file
48
pages/me/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="x-f pb20 pt20">
|
||||
<u-avatar src=""></u-avatar> <view class="user-name"><span>您好!</span>{{ userInfo.realname }}</view>
|
||||
</view>
|
||||
<u-cell-group>
|
||||
<u-cell icon="setting-fill" title="切换系统" :isLink="true" @click="handleTo('/pages/index/index')"></u-cell>
|
||||
<u-cell icon="info-circle-fill" title="关于我们" :isLink="true" @click="handleTo('/pages/me/aboutMe')"></u-cell>
|
||||
</u-cell-group>
|
||||
<u-button class="mt40" type="warning" :plain="true" text="退出当前账号" @click="handleLoginOut"></u-button>
|
||||
<my-tabBar :currentTab="1" />
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import nx from '@/nx'
|
||||
|
||||
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||
|
||||
function handleTo(url) {
|
||||
nx.$router.go(url)
|
||||
}
|
||||
function handleLoginOut() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定退出登录吗?`,
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
nx.$store('user').logout()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 30px;
|
||||
.user-name {
|
||||
padding-left: 30px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
::v-deep .u-button__text {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,540 +0,0 @@
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#f5f7fa' }" class="about-page" title="关于我们">
|
||||
<u-card :show-head="false" margin="20rpx" padding="40rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<view class="hero-content">
|
||||
<u-avatar
|
||||
v-if="appInfo.logo"
|
||||
:src="sheep.$url.cdn(appInfo.logo)"
|
||||
size="120"
|
||||
bg-color="transparent"
|
||||
class="hero-logo"
|
||||
/>
|
||||
<u-text
|
||||
:text="displayName"
|
||||
type="primary"
|
||||
size="40rpx"
|
||||
bold
|
||||
color="#fff"
|
||||
class="hero-name"
|
||||
/>
|
||||
<u-text
|
||||
:text="`${displayName} 致力于用开源的数字化能力,帮助企业快速构建电商、营销与业务协同平台。`"
|
||||
size="28rpx"
|
||||
color="rgba(255,255,255,0.9)"
|
||||
class="hero-slogan"
|
||||
/>
|
||||
<view class="hero-tags">
|
||||
<u-tag
|
||||
v-for="tag in heroTags"
|
||||
:key="tag"
|
||||
:text="tag"
|
||||
bg-color="rgba(255,255,255,0.16)"
|
||||
color="#fff"
|
||||
size="mini"
|
||||
shape="circle"
|
||||
plain
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-row gutter="16" class="metrics-row">
|
||||
<u-col span="4" v-for="metric in metrics" :key="metric.title">
|
||||
<u-card :show-head="false" margin="0" padding="32rpx 24rpx" border-radius="20rpx">
|
||||
<template #body>
|
||||
<view class="metric-content">
|
||||
<u-text
|
||||
:text="metric.value"
|
||||
size="48rpx"
|
||||
bold
|
||||
type="primary"
|
||||
class="metric-value"
|
||||
/>
|
||||
<u-text
|
||||
:text="metric.title"
|
||||
size="26rpx"
|
||||
bold
|
||||
color="#303133"
|
||||
class="metric-title"
|
||||
/>
|
||||
<u-text
|
||||
:text="metric.desc"
|
||||
size="22rpx"
|
||||
color="#909399"
|
||||
class="metric-desc"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="我们提供什么" size="32rpx" bold color="#303133" class="section-title" />
|
||||
<u-text text="围绕企业全链路经营的核心能力" size="24rpx" color="#909399" class="section-subtitle" />
|
||||
|
||||
<view class="capability-grid">
|
||||
<u-card
|
||||
v-for="item in capabilities"
|
||||
:key="item.title"
|
||||
:show-head="false"
|
||||
margin="0 0 24rpx 0"
|
||||
padding="28rpx"
|
||||
border-radius="20rpx"
|
||||
bg-color="linear-gradient(135deg, rgba(245, 247, 250, 1), rgba(245, 247, 250, 0.6))"
|
||||
>
|
||||
<template #body>
|
||||
<u-text :text="item.title" size="30rpx" bold color="#2c3e50" class="capability-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="capability-desc" />
|
||||
<view class="capability-tags">
|
||||
<u-tag
|
||||
v-for="tag in item.tags"
|
||||
:key="tag"
|
||||
:text="tag"
|
||||
bg-color="rgba(0, 85, 162, 0.08)"
|
||||
color="var(--ui-BG-Main)"
|
||||
size="mini"
|
||||
shape="circle"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="我们的理念" size="32rpx" bold color="#303133" class="section-title" />
|
||||
|
||||
<view class="value-list">
|
||||
<u-card
|
||||
v-for="item in values"
|
||||
:key="item.title"
|
||||
:show-head="false"
|
||||
margin="0 0 24rpx 0"
|
||||
padding="24rpx 26rpx"
|
||||
border-radius="20rpx"
|
||||
bg-color="rgba(0, 85, 162, 0.05)"
|
||||
>
|
||||
<template #body>
|
||||
<u-text :text="item.title" size="28rpx" bold color="#2c3e50" class="value-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="value-desc" />
|
||||
</template>
|
||||
</u-card>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="发展里程碑" size="32rpx" bold color="#303133" class="section-title" />
|
||||
|
||||
<view class="timeline">
|
||||
<view
|
||||
class="timeline-item"
|
||||
v-for="(item, index) in milestones"
|
||||
:key="item.year"
|
||||
>
|
||||
<view class="timeline-dot"></view>
|
||||
<view class="timeline-content">
|
||||
<u-text :text="item.year" size="28rpx" bold color="#2c3e50" class="timeline-year" />
|
||||
<u-text :text="item.title" size="26rpx" bold color="#303133" class="timeline-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="timeline-desc" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="联系与合作" size="32rpx" bold color="#303133" class="section-title" />
|
||||
<u-text text="欢迎与我们探讨更多共赢的可能" size="24rpx" color="#909399" class="section-subtitle" />
|
||||
|
||||
<view class="contact-list">
|
||||
<u-cell
|
||||
v-for="item in contacts"
|
||||
:key="item.label"
|
||||
:title="item.label"
|
||||
:label="item.value"
|
||||
:is-link="true"
|
||||
arrow-direction="right"
|
||||
bg-color="rgba(245, 247, 250, 0.8)"
|
||||
title-style="font-size: 26rpx; font-weight: 600; color: #2c3e50;"
|
||||
label-style="font-size: 24rpx; color: #606266; word-break: break-all; line-height: 34rpx;"
|
||||
@click="handleContact(item)"
|
||||
class="contact-cell"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-text
|
||||
text="移动商城 始终坚持开源共享,与开发者和企业伙伴一起打造可持续的数字化生态。"
|
||||
size="24rpx"
|
||||
color="#909399"
|
||||
align="center"
|
||||
class="footer-hint"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const appInfo = computed(() => sheep.$store('app').info || {});
|
||||
const displayName = computed(() => appInfo.value.name || '移动商城');
|
||||
|
||||
const heroTags = [
|
||||
'100% 开源可商用',
|
||||
'支持多端统一交付',
|
||||
'沉淀企业级最佳实践'
|
||||
];
|
||||
|
||||
const metrics = [
|
||||
{
|
||||
title: '开源历程',
|
||||
value: '7+',
|
||||
desc: '年持续迭代与社区共建'
|
||||
},
|
||||
{
|
||||
title: '行业方案',
|
||||
value: '20+',
|
||||
desc: '覆盖零售、制造、政企等场景'
|
||||
},
|
||||
{
|
||||
title: '交付效率',
|
||||
value: '30%',
|
||||
desc: '平均缩短客户上线周期'
|
||||
}
|
||||
];
|
||||
|
||||
const capabilities = [
|
||||
{
|
||||
title: '产品矩阵',
|
||||
desc: '以电商中台为核心,延展直播带货、会员营销、全渠道订单与履约管理。',
|
||||
tags: ['商城交易', '营销裂变', '数据运营']
|
||||
},
|
||||
{
|
||||
title: '技术架构',
|
||||
desc: '基于 Spring Cloud Alibaba + Vue3 + UniApp,支持多租户、微服务与多端同源。',
|
||||
tags: ['微服务', '多端一致', '低代码装修']
|
||||
},
|
||||
{
|
||||
title: '服务体系',
|
||||
desc: '提供从咨询、定制、交付到长期运营的全生命周期陪伴式服务。',
|
||||
tags: ['数字化顾问', '驻场交付', '持续运营']
|
||||
}
|
||||
];
|
||||
|
||||
const values = [
|
||||
{
|
||||
title: '使命',
|
||||
desc: '让每一家企业都能以更低成本、更快速度完成业务数字化升级。'
|
||||
},
|
||||
{
|
||||
title: '愿景',
|
||||
desc: '打造开放、可信赖、可持续的企业级数字商业生态。'
|
||||
},
|
||||
{
|
||||
title: '价值观',
|
||||
desc: '客户成功、极致体验、持续创新、坦诚协作。'
|
||||
}
|
||||
];
|
||||
|
||||
const milestones = [
|
||||
{
|
||||
year: '2018',
|
||||
title: '开源起航',
|
||||
desc: '发布首个电商项目原型,与社区开发者共同启动开源计划。'
|
||||
},
|
||||
{
|
||||
year: '2020',
|
||||
title: '企业级升级',
|
||||
desc: '引入多租户、权限与大促能力,满足成长型企业核心诉求。'
|
||||
},
|
||||
{
|
||||
year: '2022',
|
||||
title: '多端一体',
|
||||
desc: '统一 H5、App 与小程序技术栈,实现一次开发,多端交付。'
|
||||
},
|
||||
{
|
||||
year: '2024',
|
||||
title: '智能加速',
|
||||
desc: '引入智能客服、营销推荐等 AI 能力,提升运营效率。'
|
||||
},
|
||||
{
|
||||
year: '2025',
|
||||
title: '生态共建',
|
||||
desc: '联合生态伙伴共建行业方案,形成开放合作的生态体系。'
|
||||
}
|
||||
];
|
||||
|
||||
const contacts = [
|
||||
{
|
||||
label: '商务合作',
|
||||
value: 'business@iocoder.cn',
|
||||
type: 'email'
|
||||
},
|
||||
{
|
||||
label: '客服热线',
|
||||
value: '400-860-888',
|
||||
type: 'phone',
|
||||
actionValue: '400860888'
|
||||
},
|
||||
{
|
||||
label: '开源仓库',
|
||||
value: 'https://github.com/YunaiV/ruoyi-vue-pro',
|
||||
type: 'link'
|
||||
},
|
||||
{
|
||||
label: '文档中心',
|
||||
value: 'https://doc.iocoder.cn',
|
||||
type: 'link'
|
||||
},
|
||||
{
|
||||
label: '办公地址',
|
||||
value: '浙江省杭州市滨江区江南大道 777 号数字产业园',
|
||||
type: 'text'
|
||||
}
|
||||
];
|
||||
|
||||
function handleContact(item) {
|
||||
if (item.type === 'phone') {
|
||||
const phoneNumber = item.actionValue || item.value.replace(/[^0-9]/g, '');
|
||||
if (!phoneNumber) {
|
||||
return;
|
||||
}
|
||||
uni.makePhoneCall({
|
||||
phoneNumber,
|
||||
fail: () => {
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '号码已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type === 'link') {
|
||||
sheep.$router.go('/pages/public/webview', {
|
||||
title: item.label,
|
||||
url: encodeURIComponent(item.value)
|
||||
});
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '链接已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type === 'text') {
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '地址已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '信息已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.about-page {
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
background: #f5f7fa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Hero card 渐变背景
|
||||
:deep(.u-card) {
|
||||
&:first-child {
|
||||
background: linear-gradient(135deg, rgba(0, 85, 162, 0.95), rgba(0, 85, 162, 0.68));
|
||||
box-shadow: 0 16rpx 36rpx rgba(0, 85, 162, 0.25);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
|
||||
.hero-logo {
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.hero-slogan {
|
||||
margin-bottom: 28rpx;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.hero-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-row {
|
||||
margin: 20rpx;
|
||||
}
|
||||
|
||||
.metric-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
|
||||
.metric-value {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.metric-title {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.metric-desc {
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.capability-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.capability-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.capability-desc {
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
|
||||
.capability-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.value-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.value-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.value-desc {
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
margin-left: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 2rpx solid rgba(0, 85, 162, 0.2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: -31rpx;
|
||||
top: 6rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: var(--ui-BG-Main);
|
||||
box-shadow: 0 0 0 6rpx rgba(0, 85, 162, 0.15);
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding-left: 12rpx;
|
||||
|
||||
.timeline-year {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contact-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.contact-cell {
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.footer-hint {
|
||||
padding: 24rpx 36rpx 80rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.metrics-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.capability-grid,
|
||||
.value-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<!-- 错误界面 -->
|
||||
<template>
|
||||
<view class="error-page">
|
||||
<s-empty
|
||||
v-if="errCode === 'NetworkError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="网络连接失败"
|
||||
showAction
|
||||
actionText="重新连接"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
<s-empty
|
||||
v-else-if="errCode === 'TemplateError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="未找到模板,请前往后台启用对应模板"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
<s-empty
|
||||
v-else-if="errCode !== ''"
|
||||
icon="/static/internet-empty.png"
|
||||
:text="errMsg"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
import { ShoproInit } from '@/sheep';
|
||||
|
||||
const errCode = ref('');
|
||||
const errMsg = ref('');
|
||||
|
||||
onLoad((options) => {
|
||||
errCode.value = options.errCode;
|
||||
errMsg.value = options.errMsg;
|
||||
});
|
||||
|
||||
// 重新连接
|
||||
async function onReconnect() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/menu',
|
||||
});
|
||||
await ShoproInit();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.error-page {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +0,0 @@
|
||||
<!-- 页面已移除 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="页面已下线">
|
||||
<s-empty text="该页面已下线" icon="/static/internet-empty.png" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.set-wrap {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<!-- 文章展示 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" :title="state.title" class="set-wrap">
|
||||
<view class="ss-p-30 richtext"><mp-html :content="state.content"></mp-html></view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
// Article API removed with promotion module
|
||||
|
||||
const state = reactive({
|
||||
title: '',
|
||||
content: '',
|
||||
});
|
||||
|
||||
async function getRichTextContent(id, title) {
|
||||
// Article API has been removed as it was part of promotion module
|
||||
state.title = title || '内容';
|
||||
state.content = '暂无内容';
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.title) {
|
||||
state.title = options.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
}
|
||||
getRichTextContent(options.id, options.title);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
:deep() {
|
||||
image {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,236 +0,0 @@
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" class="set-wrap" title="系统设置">
|
||||
<view class="header-box ss-flex-col ss-row-center ss-col-center">
|
||||
<image
|
||||
class="logo-img ss-m-b-46"
|
||||
:src="sheep.$url.cdn(appInfo.logo)"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="name ss-m-b-24">{{ appInfo.name }}</view>
|
||||
</view>
|
||||
|
||||
<view class="container-list">
|
||||
<uni-list :border="false">
|
||||
<uni-list-item
|
||||
title="当前版本"
|
||||
:rightText="appInfo.version"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="onCheckUpdate"
|
||||
/>
|
||||
<uni-list-item
|
||||
title="本地缓存"
|
||||
:rightText="storageSize"
|
||||
showArrow
|
||||
:border="false"
|
||||
class="list-border"
|
||||
/>
|
||||
<uni-list-item
|
||||
title="关于我们"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '关于我们'
|
||||
})
|
||||
"
|
||||
/>
|
||||
<!-- 为了过审 只有 iOS-App 有注销账号功能 -->
|
||||
<uni-list-item
|
||||
v-if="isLogin && sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
title="注销账号"
|
||||
rightText=""
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@click="onLogoff"
|
||||
/>
|
||||
</uni-list>
|
||||
</view>
|
||||
<view class="set-footer ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="agreement-box ss-flex ss-col-center ss-m-b-40">
|
||||
<view class="ss-flex ss-col-center ss-m-b-10">
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '用户协议'
|
||||
})
|
||||
"
|
||||
>
|
||||
《用户协议》
|
||||
</view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '隐私协议'
|
||||
})
|
||||
"
|
||||
>
|
||||
《隐私协议》
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="copyright-text ss-m-b-10">{{ appInfo.copyright }}</view>
|
||||
<view class="copyright-text">{{ appInfo.copytime }}</view>
|
||||
</view>
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="ss-p-x-20 ss-p-b-40">
|
||||
<button
|
||||
class="loginout-btn ss-reset-button ui-BG-Main ui-Shadow-Main"
|
||||
@tap="onLogout"
|
||||
v-if="isLogin"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
import AuthUtil from '@/sheep/api/system/auth';
|
||||
|
||||
const appInfo = computed(() => sheep.$store('app').info);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
const storageSize = uni.getStorageInfoSync().currentSize + 'Kb';
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
});
|
||||
|
||||
function onCheckUpdate() {
|
||||
sheep.$platform.checkUpdate();
|
||||
// 小程序初始化时已检查更新
|
||||
// H5实时更新无需检查
|
||||
// App 1.跳转应用市场更新 2.手动热更新 3.整包更新
|
||||
}
|
||||
|
||||
// 注销账号
|
||||
function onLogoff() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认注销账号?',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const { code } = await AuthUtil.logout();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
sheep.$store('user').logout();
|
||||
sheep.$router.go('/pages/index/user');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 退出账号
|
||||
function onLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认退出账号?',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const { code } = await AuthUtil.logout();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
sheep.$store('user').logout();
|
||||
sheep.$router.go('/pages/index/user');
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 100rpx 0;
|
||||
|
||||
.logo-img {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 42rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-3;
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
line-height: 32rpx;
|
||||
color: $gray-b;
|
||||
}
|
||||
}
|
||||
|
||||
.set-footer {
|
||||
margin: 100rpx 0 0 0;
|
||||
|
||||
.copyright-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: $gray-c;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.agreement-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loginout-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.list-border {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__content-title) {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__extra-text) {
|
||||
color: #bbbbbb;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +0,0 @@
|
||||
<!-- 网页加载 -->
|
||||
<template>
|
||||
<view>
|
||||
<web-view :src="url" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const url = ref('');
|
||||
onLoad((options) => {
|
||||
url.value = decodeURIComponent(options.url);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
122
pages/setting/SelectBaseData.vue
Normal file
122
pages/setting/SelectBaseData.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<uni-section type="line" title="基础信息选择"> </uni-section>
|
||||
<uni-forms label-position="top">
|
||||
<uni-forms-item label="区域" name="areaCode">
|
||||
<uni-data-select
|
||||
:clear="false"
|
||||
v-model="areaCode"
|
||||
:localdata="areaCodeList"
|
||||
@change="handleAreaChange"
|
||||
></uni-data-select>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item v-if="areaCode" label="岗位" name="postCode">
|
||||
<uni-data-select
|
||||
:clear="false"
|
||||
v-model="postCode"
|
||||
:localdata="postCodeList"
|
||||
@change="handlePostChange"
|
||||
></uni-data-select>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item v-if="postCode && postCode === weighingPostCode" label="计量点" name="scaleNo">
|
||||
<uni-data-select :clear="false" v-model="scaleNo" :localdata="scaleNoList"></uni-data-select>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<u-button type="primary" @click="submitForm">保存</u-button>
|
||||
<u-loading-page :loading="loadingPage"></u-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { getAreaCodeConfig, getScaleNoConfig, getPostList } from '@/nxTemp/apis/sys.api'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
weighingPostCode: '监磅',
|
||||
areaCode: '',
|
||||
scaleNo: '',
|
||||
postCode: '',
|
||||
areaCodeList: [],
|
||||
scaleNoList: [],
|
||||
postCodeList: [],
|
||||
loadingPage: false
|
||||
}
|
||||
},
|
||||
async onLoad() {
|
||||
this.loadingPage = true
|
||||
try {
|
||||
this.areaCodeList = (await getAreaCodeConfig()).map(item => ({ text: item.areaName, value: item.areaCode }))
|
||||
this.postCodeList = (await getPostList()).map(item => ({ text: item.name, value: item.name }))
|
||||
if (this.areaCode) {
|
||||
this.getScaleNoList()
|
||||
}
|
||||
this.loadingPage = false
|
||||
} catch (error) {
|
||||
this.loadingPage = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.areaCode = this.$store.state.areaCode
|
||||
this.scaleNo = this.$store.state.scaleNo
|
||||
this.postCode = this.$store.state.postCode
|
||||
},
|
||||
methods: {
|
||||
async getScaleNoList() {
|
||||
if (this.postCode && this.postCode === this.weighingPostCode) {
|
||||
// this.scaleNo = ''
|
||||
this.scaleNoList = (await getScaleNoConfig({ areaCode: this.areaCode })).map(item => ({
|
||||
text: item.pointName,
|
||||
value: item.pointCode
|
||||
}))
|
||||
}
|
||||
},
|
||||
async handleAreaChange() {
|
||||
this.postCode = ''
|
||||
this.scaleNo = ''
|
||||
},
|
||||
async handlePostChange() {
|
||||
this.scaleNo = ''
|
||||
this.getScaleNoList()
|
||||
},
|
||||
async goIndex() {
|
||||
await this.$store.dispatch('getRoleMenus', this.$store.state.postCode)
|
||||
uni.reLaunch({
|
||||
url: 'pages/lims/index/index'
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
if (this.areaCode && this.postCode && this.postCode !== this.weighingPostCode) {
|
||||
this.$store.commit('SET_AREA_CODE', this.areaCode)
|
||||
this.$store.commit('SET_POST_CODE', this.postCode)
|
||||
this.goIndex()
|
||||
} else if (this.postCode && this.postCode === this.weighingPostCode) {
|
||||
if (this.scaleNo) {
|
||||
this.$store.commit('SET_AREA_CODE', this.areaCode)
|
||||
this.$store.commit('SET_POST_CODE', this.postCode)
|
||||
this.$store.commit('SET_SCALE_NO', this.scaleNo)
|
||||
this.goIndex()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请选择',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请选择',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 30rpx;
|
||||
}
|
||||
::v-deep .uni-section-header__decoration {
|
||||
background-color: $uni-color-primary !important;
|
||||
}
|
||||
</style>
|
||||
109
pages/setting/UrlConfig.vue
Normal file
109
pages/setting/UrlConfig.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<uni-section type="line" title="请求地址和租户ID配置" @click="handleClick"> </uni-section>
|
||||
<uni-forms label-position="top" label-width="100">
|
||||
<uni-forms-item label="系统请求地址">
|
||||
<uni-easyinput v-model="baseUrl" placeholder="请输入系统请求地址"></uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item v-if="showContent" label="租户ID">
|
||||
<uni-easyinput v-model="tenantId" placeholder="请输入租户ID"></uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item v-if="showContent" label="app更新地址">
|
||||
<uni-easyinput v-model="upgradeBaseUrl" placeholder="请输入"></uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<u-button type="primary" @click="submitForm">保存</u-button>
|
||||
<u-button v-if="showContent" type="primary" @click="clearCache">清除缓存</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
baseUrl: '',
|
||||
tenantId: '',
|
||||
upgradeBaseUrl: '',
|
||||
clickCount: 0, // 初始化点击次数为0
|
||||
threshold: 5, //设置点击次数的阈值
|
||||
thresholdTime: 2, // 设置连续点击的时间阈值(秒)
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showContent() {
|
||||
return this.clickCount >= this.threshold
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.baseUrl = uni.getStorageSync('base_url')
|
||||
this.tenantId = uni.getStorageSync('tenant_id')
|
||||
this.upgradeBaseUrl = uni.getStorageSync('upgradeBaseUrl')
|
||||
},
|
||||
methods: {
|
||||
clearCache() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要清空缓存?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.clearStorageSync()
|
||||
plus.runtime.restart()
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
if (!this.baseUrl) {
|
||||
uni.showToast({
|
||||
title: '请输入地址和租户',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.setStorageSync('base_url', this.baseUrl)
|
||||
uni.setStorageSync('tenant_id', this.tenantId)
|
||||
uni.setStorageSync('upgradeBaseUrl', this.upgradeBaseUrl)
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'none'
|
||||
})
|
||||
uni.navigateBack()
|
||||
},
|
||||
handleClick() {
|
||||
// 如果达到阈值,显示内容并重置计数
|
||||
if (this.showContent) {
|
||||
clearTimeout(this.timer)
|
||||
return
|
||||
}
|
||||
// 增加点击次数
|
||||
this.clickCount++
|
||||
uni.showToast({
|
||||
title: `再点击${this.threshold - this.clickCount}次可显示内容`,
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
})
|
||||
|
||||
clearTimeout(this.timer)
|
||||
// 清除之前的定时器(如果有),设置新的定时器
|
||||
this.timer = setTimeout(() => {
|
||||
this.resetClickCount()
|
||||
}, this.thresholdTime * 1000)
|
||||
},
|
||||
resetClickCount() {
|
||||
if (!this.showContent) {
|
||||
this.clickCount = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 30px;
|
||||
}
|
||||
</style>
|
||||
507
pages/setting/upgrade-popup.vue
Normal file
507
pages/setting/upgrade-popup.vue
Normal file
@@ -0,0 +1,507 @@
|
||||
<template>
|
||||
<view class="mask flex-center">
|
||||
<view class="content botton-radius">
|
||||
<view class="content-top">
|
||||
<text class="content-top-text">{{ title }}</text>
|
||||
<image
|
||||
class="content-top"
|
||||
style="top: 0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
src="../../static/images/app_update_bg_top.png"
|
||||
>
|
||||
</image>
|
||||
</view>
|
||||
<view class="content-header"></view>
|
||||
<view class="content-body">
|
||||
<view class="title">
|
||||
<text>{{ subTitle }}</text>
|
||||
<!-- <text style="padding-left:20rpx;font-size: 0.5em;color: #666;">v.{{version}}</text> -->
|
||||
</view>
|
||||
<view class="body">
|
||||
<scroll-view class="box-des-scroll" scroll-y="true">
|
||||
<text class="box-des">
|
||||
{{ contents }}
|
||||
</text>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="footer flex-center">
|
||||
<template v-if="isiOS">
|
||||
<button class="content-button" style="border: none; color: #fff" plain @click="jumpToAppStore">
|
||||
{{ downLoadBtnTextiOS }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="!downloadSuccess">
|
||||
<view class="progress-box flex-column" v-if="downloading">
|
||||
<progress
|
||||
class="progress"
|
||||
border-radius="35"
|
||||
:percent="downLoadPercent"
|
||||
activeColor="#3DA7FF"
|
||||
show-info
|
||||
stroke-width="10"
|
||||
/>
|
||||
<view style="width: 100%; font-size: 14px; display: flex; justify-content: space-around">
|
||||
<text>{{ downLoadingText }}</text>
|
||||
<text>({{ downloadedSize }}/{{ packageFileSize }}M)</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button v-else class="content-button" style="border: none; color: #fff" plain @click="downloadPackage">
|
||||
{{ downLoadBtnText }}
|
||||
</button>
|
||||
</template>
|
||||
<button
|
||||
v-else-if="downloadSuccess"
|
||||
class="content-button"
|
||||
style="border: none; color: #fff"
|
||||
plain
|
||||
:loading="installing"
|
||||
:disabled="installing"
|
||||
@click="installPackage"
|
||||
>
|
||||
{{ installing ? '正在安装……' : '下载完成,立即安装' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="installed && isWGT"
|
||||
class="content-button"
|
||||
style="border: none; color: #fff"
|
||||
plain
|
||||
@click="restart"
|
||||
>
|
||||
安装完毕,点击重启
|
||||
</button>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image
|
||||
v-if="!is_mandatory"
|
||||
class="close-img"
|
||||
src="../../static/images/app_update_close.png"
|
||||
@click.stop="closeUpdate"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
|
||||
const localFilePathKey = '__localFilePath__'
|
||||
const platform_iOS = 'iOS'
|
||||
let downloadTask = null
|
||||
|
||||
// 工具函数保持原样
|
||||
function compare(v1 = '0', v2 = '0') {
|
||||
v1 = String(v1).split('.')
|
||||
v2 = String(v2).split('.')
|
||||
const minVersionLens = Math.min(v1.length, v2.length)
|
||||
|
||||
let result = 0
|
||||
for (let i = 0; i < minVersionLens; i++) {
|
||||
const curV1 = Number(v1[i])
|
||||
const curV2 = Number(v2[i])
|
||||
|
||||
if (curV1 > curV2) {
|
||||
result = 1
|
||||
break
|
||||
} else if (curV1 < curV2) {
|
||||
result = -1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (result === 0 && v1.length !== v2.length) {
|
||||
const v1BiggerThenv2 = v1.length > v2.length
|
||||
const maxLensVersion = v1BiggerThenv2 ? v1 : v2
|
||||
for (let i = minVersionLens; i < maxLensVersion.length; i++) {
|
||||
const curVersion = Number(maxLensVersion[i])
|
||||
if (curVersion > 0) {
|
||||
v1BiggerThenv2 ? (result = 1) : (result = -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 响应式状态
|
||||
const installForBeforeFilePath = ref('')
|
||||
const installed = ref(false)
|
||||
const installing = ref(false)
|
||||
const downloadSuccess = ref(false)
|
||||
const downloading = ref(false)
|
||||
const downLoadPercent = ref(0)
|
||||
const downloadedSize = ref(0)
|
||||
const packageFileSize = ref(0)
|
||||
const tempFilePath = ref('')
|
||||
|
||||
const title = ref('更新日志')
|
||||
const contents = ref('')
|
||||
const is_mandatory = ref(false)
|
||||
const subTitle = ref('发现新版本')
|
||||
const downLoadBtnTextiOS = ref('立即跳转更新')
|
||||
const downLoadBtnText = ref('立即下载更新')
|
||||
const downLoadingText = ref('安装包下载中,请稍后')
|
||||
|
||||
const version = ref('')
|
||||
const url = ref('')
|
||||
const type = ref('')
|
||||
const platform = ref('')
|
||||
|
||||
// 计算属性
|
||||
const isWGT = computed(() => type.value === 'wgt')
|
||||
const isiOS = computed(() => !isWGT.value && platform.value.includes(platform_iOS))
|
||||
|
||||
// 方法
|
||||
function checkLocalStoragePackage() {
|
||||
const localFilePathRecord = uni.getStorageSync(localFilePathKey)
|
||||
if (localFilePathRecord) {
|
||||
const { version: recordVersion, savedFilePath, installed: recordInstalled } = localFilePathRecord
|
||||
|
||||
if (!recordInstalled && compare(recordVersion, version.value) === 0) {
|
||||
downloadSuccess.value = true
|
||||
installForBeforeFilePath.value = savedFilePath
|
||||
tempFilePath.value = savedFilePath
|
||||
} else {
|
||||
deleteSavedFile(savedFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function closeUpdate() {
|
||||
if (downloading.value) {
|
||||
if (is_mandatory.value) {
|
||||
uni.showToast({ title: '下载中,请稍后……', icon: 'none', duration: 500 })
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '是否取消下载?',
|
||||
cancelText: '否',
|
||||
confirmText: '是',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
downloadTask?.abort()
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (downloadSuccess.value && tempFilePath.value) {
|
||||
await saveFile(tempFilePath.value, version.value)
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
function downloadPackage() {
|
||||
downloading.value = true
|
||||
|
||||
downloadTask = uni.downloadFile({
|
||||
url: url.value,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
downloadSuccess.value = true
|
||||
tempFilePath.value = res.tempFilePath
|
||||
// 强制更新
|
||||
if (is_mandatory.value) {
|
||||
installPackage()
|
||||
}
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
downloading.value = false
|
||||
downLoadPercent.value = 0
|
||||
downloadedSize.value = 0
|
||||
packageFileSize.value = 0
|
||||
downloadTask = null
|
||||
}
|
||||
})
|
||||
|
||||
downloadTask.onProgressUpdate(res => {
|
||||
downLoadPercent.value = res.progress
|
||||
downloadedSize.value = (res.totalBytesWritten / 1024 ** 2).toFixed(2)
|
||||
packageFileSize.value = (res.totalBytesExpectedToWrite / 1024 ** 2).toFixed(2)
|
||||
})
|
||||
}
|
||||
|
||||
function installPackage() {
|
||||
// #ifdef APP-PLUS
|
||||
if (isWGT.value) {
|
||||
installing.value = true
|
||||
}
|
||||
|
||||
plus.runtime.install(
|
||||
tempFilePath.value,
|
||||
{
|
||||
force: false
|
||||
},
|
||||
async () => {
|
||||
installing.value = false
|
||||
installed.value = true
|
||||
|
||||
if (isWGT.value) {
|
||||
if (is_mandatory.value) {
|
||||
uni.showLoading({ icon: 'none', title: '安装成功,正在重启……' })
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
restart()
|
||||
}, 1000)
|
||||
}
|
||||
} else {
|
||||
const localFilePathRecord = uni.getStorageSync(localFilePathKey)
|
||||
uni.setStorageSync(localFilePathKey, {
|
||||
...localFilePathRecord,
|
||||
installed: true
|
||||
})
|
||||
}
|
||||
},
|
||||
async err => {
|
||||
if (installForBeforeFilePath.value) {
|
||||
await deleteSavedFile(installForBeforeFilePath.value)
|
||||
installForBeforeFilePath.value = ''
|
||||
}
|
||||
|
||||
installing.value = false
|
||||
installed.value = false
|
||||
|
||||
uni.showModal({
|
||||
title: `更新失败${isWGT.value ? '' : ',APK文件不存在'},请重新下载`,
|
||||
content: err.message,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if (!isWGT.value) {
|
||||
uni.navigateBack()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
function restart() {
|
||||
installed.value = false
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.restart()
|
||||
// #endif
|
||||
}
|
||||
|
||||
async function saveFile(tempFilePath, version) {
|
||||
try {
|
||||
const res = await uni.saveFile({ tempFilePath })
|
||||
uni.setStorageSync(localFilePathKey, {
|
||||
version,
|
||||
savedFilePath: res.savedFilePath
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('保存文件失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSavedFile(filePath) {
|
||||
try {
|
||||
await uni.removeSavedFile({ filePath })
|
||||
uni.removeStorageSync(localFilePathKey)
|
||||
} catch (err) {
|
||||
console.error('删除文件失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
function jumpToAppStore() {
|
||||
plus.runtime.openURL(url.value)
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onLoad(params => {
|
||||
const { local_storage_key } = params
|
||||
if (!local_storage_key) {
|
||||
console.error('local_storage_key为空')
|
||||
// uni.navigateBack()
|
||||
return
|
||||
}
|
||||
|
||||
const localPackageInfo = uni.getStorageSync(local_storage_key)
|
||||
if (!localPackageInfo) {
|
||||
console.error('安装包信息为空')
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
|
||||
const requiredKeys = ['version', 'url', 'type']
|
||||
for (const key of requiredKeys) {
|
||||
if (!localPackageInfo[key]) {
|
||||
console.error(`参数 ${key} 必填`)
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 更新响应式状态
|
||||
version.value = localPackageInfo.version
|
||||
url.value = localPackageInfo.url
|
||||
type.value = localPackageInfo.type
|
||||
platform.value = localPackageInfo.platform || ''
|
||||
title.value = localPackageInfo.title || '更新日志'
|
||||
contents.value = localPackageInfo.contents || ''
|
||||
is_mandatory.value = localPackageInfo.is_mandatory || false
|
||||
subTitle.value = localPackageInfo.subTitle || '发现新版本'
|
||||
downLoadBtnTextiOS.value = localPackageInfo.downLoadBtnTextiOS || '立即跳转更新'
|
||||
downLoadBtnText.value = localPackageInfo.downLoadBtnText || '立即下载更新'
|
||||
downLoadingText.value = localPackageInfo.downLoadingText || '安装包下载中,请稍后'
|
||||
|
||||
checkLocalStoragePackage()
|
||||
})
|
||||
|
||||
onBackPress(() => {
|
||||
if (is_mandatory.value) return true
|
||||
if (downloadTask) downloadTask.abort()
|
||||
return false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.botton-radius {
|
||||
border-bottom-left-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: 400px;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 0 30px;
|
||||
font-family: Source Han Sans CN;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.content-top {
|
||||
position: absolute;
|
||||
top: -55px;
|
||||
left: 0;
|
||||
width: 400px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.content-top-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #f8f8fa;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 30px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
padding-top: 80px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.content-body {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #3da7ff;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 90px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.box-des-scroll {
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.box-des {
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.progress-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 90%;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.close-img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
bottom: -70px;
|
||||
left: calc(50% - 20px);
|
||||
}
|
||||
|
||||
.content-button {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
border-radius: 20px;
|
||||
margin: 0 10px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: linear-gradient(to right, #1785ff, #3da7ff);
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,410 +0,0 @@
|
||||
<!-- 用户信息 -->
|
||||
<template>
|
||||
<s-layout title="用户信息" class="set-userinfo-wrap">
|
||||
<uni-forms
|
||||
:model="state.model"
|
||||
:rules="state.rules"
|
||||
labelPosition="left"
|
||||
border
|
||||
class="form-box"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<view class="ss-flex ss-row-center ss-col-center ss-p-t-60 ss-p-b-0 bg-white">
|
||||
<view class="header-box-content">
|
||||
<s-avatar
|
||||
:src="state.model?.avatar"
|
||||
:nickname="state.model?.nickname || state.model?.username"
|
||||
:size="160"
|
||||
:editable="true"
|
||||
:bg-color="getAvatarBgColor(state.model?.nickname || state.model?.username)"
|
||||
@click="onChangeAvatar"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bg-white ss-p-x-30">
|
||||
<!-- 昵称 + 性别 -->
|
||||
<uni-forms-item name="nickname" label="昵称">
|
||||
<uni-easyinput
|
||||
v-model="state.model.nickname"
|
||||
type="nickname"
|
||||
placeholder="设置昵称"
|
||||
:inputBorder="false"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="sex" label="性别">
|
||||
<view class="ss-flex ss-col-center ss-h-100">
|
||||
<radio-group @change="onChangeGender" class="ss-flex ss-col-center">
|
||||
<label class="radio" v-for="item in sexRadioMap" :key="item.value">
|
||||
<view class="ss-flex ss-col-center ss-m-r-32">
|
||||
<radio
|
||||
:value="item.value"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
:checked="parseInt(item.value) === state.model?.sex"
|
||||
/>
|
||||
<view class="gender-name">{{ item.name }}</view>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="mobile" label="手机号" @tap="onChangeMobile">
|
||||
<uni-easyinput
|
||||
v-model="userInfo.mobile"
|
||||
placeholder="请绑定手机号"
|
||||
:inputBorder="false"
|
||||
disabled
|
||||
:styles="{ disableColor: '#fff' }"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:clearable="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<su-radio v-if="userInfo.verification?.mobile" :modelValue="true" />
|
||||
<button v-else class="ss-reset-button ss-flex ss-col-center ss-row-center">
|
||||
<text class="_icon-forward" style="color: #bbbbbb; font-size: 26rpx"></text>
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
</view>
|
||||
</uni-forms>
|
||||
|
||||
<!-- 当前社交平台的绑定关系,只处理 wechat 微信场景 -->
|
||||
<view v-if="sheep.$platform.name !== 'H5'">
|
||||
<view class="title-box ss-p-l-30">第三方账号绑定</view>
|
||||
<view class="account-list ss-flex ss-row-between">
|
||||
<view v-if="'WechatOfficialAccount' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/WechatOfficialAccount.png')"
|
||||
/>
|
||||
<text class="list-name">微信公众号</text>
|
||||
</view>
|
||||
<view v-if="'WechatMiniProgram' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/WechatMiniProgram.png')"
|
||||
/>
|
||||
<text class="list-name">微信小程序</text>
|
||||
</view>
|
||||
<view v-if="'App' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
|
||||
/>
|
||||
<text class="list-name">微信开放平台</text>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="info ss-flex ss-col-center" v-if="state.thirdInfo">
|
||||
<image class="avatar ss-m-r-20" :src="sheep.$url.cdn(state.thirdInfo.avatar)" />
|
||||
<text class="name">{{ state.thirdInfo.nickname }}</text>
|
||||
</view>
|
||||
<view class="bind-box ss-m-l-20">
|
||||
<button
|
||||
v-if="state.thirdInfo.openid"
|
||||
class="ss-reset-button relieve-btn"
|
||||
@tap="unBindThirdOauth"
|
||||
>
|
||||
解绑
|
||||
</button>
|
||||
<button v-else class="ss-reset-button bind-btn" @tap="bindThirdOauth">绑定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<su-fixed bottom placeholder bg="none">
|
||||
<view class="footer-box ss-p-20">
|
||||
<button class="ss-rest-button logout-btn ui-Shadow-Main" @tap="onSubmit">保存</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, onBeforeMount } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { clone } from 'lodash-es';
|
||||
import UserApi from '@/sheep/api/system/user';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
import {
|
||||
chooseAndUploadFile,
|
||||
uploadFilesFromPath,
|
||||
} from '@/sheep/components/s-uploader/choose-and-upload-file';
|
||||
|
||||
const state = reactive({
|
||||
model: {}, // 个人信息
|
||||
rules: {},
|
||||
thirdInfo: {}, // 社交用户的信息
|
||||
});
|
||||
|
||||
const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal';
|
||||
|
||||
const sexRadioMap = [
|
||||
{
|
||||
name: '男',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: '女',
|
||||
value: '2',
|
||||
},
|
||||
];
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 选择性别
|
||||
function onChangeGender(e) {
|
||||
state.model.sex = e.detail.value;
|
||||
}
|
||||
|
||||
// 修改手机号
|
||||
const onChangeMobile = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index?authType=changeMobile'
|
||||
});
|
||||
};
|
||||
|
||||
// 选择微信的头像,进行上传
|
||||
async function onChooseAvatar(e) {
|
||||
debugger;
|
||||
const tempUrl = e.detail.avatarUrl || '';
|
||||
if (!tempUrl) return;
|
||||
const files = await uploadFilesFromPath(tempUrl);
|
||||
if (files.length > 0) {
|
||||
state.model.avatar = files[0].url;
|
||||
}
|
||||
}
|
||||
|
||||
// 手动选择头像,进行上传
|
||||
async function onChangeAvatar() {
|
||||
const files = await chooseAndUploadFile({ type: 'image' });
|
||||
if (files.length > 0) {
|
||||
state.model.avatar = files[0].url;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定第三方账号
|
||||
async function bindThirdOauth() {
|
||||
let result = await sheep.$platform.useProvider('wechat').bind();
|
||||
if (result) {
|
||||
await getUserInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// 解绑第三方账号
|
||||
function unBindThirdOauth() {
|
||||
uni.showModal({
|
||||
title: '解绑提醒',
|
||||
content: '解绑后您将无法通过微信登录此账号',
|
||||
cancelText: '再想想',
|
||||
confirmText: '确定',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const result = await sheep.$platform.useProvider('wechat').unbind(state.thirdInfo.openid);
|
||||
if (result) {
|
||||
await getUserInfo();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 保存信息
|
||||
async function onSubmit() {
|
||||
const { code } = await UserApi.updateUser({
|
||||
avatar: state.model.avatar,
|
||||
nickname: state.model.nickname,
|
||||
sex: state.model.sex,
|
||||
});
|
||||
if (code === 0) {
|
||||
await getUserInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// 获得用户信息
|
||||
const getUserInfo = async () => {
|
||||
// 个人信息
|
||||
const userInfo = await sheep.$store('user').getInfo();
|
||||
state.model = clone(userInfo);
|
||||
|
||||
// 获得社交用户的信息
|
||||
if (sheep.$platform.name !== 'H5') {
|
||||
const result = await sheep.$platform.useProvider('wechat').getInfo();
|
||||
state.thirdInfo = result || {};
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
getUserInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-file-picker {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.uni-file-picker__container {
|
||||
margin: -14rpx -12rpx;
|
||||
}
|
||||
|
||||
.file-picker__progress {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
.uni-list-item__content-title {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-icons {
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.is-disabled {
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.disabled) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gender-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.title-box {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
line-height: 100rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
background: var(--gradient-horizontal-primary, linear-gradient(90deg, #0055A2, rgba(0, 85, 162, 0.6)));
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: linear-gradient(90deg, #003F73, rgba(0, 63, 115, 0.6));
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dark {
|
||||
filter: grayscale(100%);
|
||||
filter: gray;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.content-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.header-box-content {
|
||||
position: relative;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.avatar-action {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 160rpx;
|
||||
height: 46rpx;
|
||||
background: rgba(#000000, 0.3);
|
||||
|
||||
.avatar-action-btn {
|
||||
width: 160rpx;
|
||||
height: 46rpx;
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定项
|
||||
.account-list {
|
||||
background-color: $white;
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.list-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.list-name {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.info {
|
||||
.avatar {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
|
||||
.bind-box {
|
||||
width: 100rpx;
|
||||
height: 50rpx;
|
||||
line-height: normal;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
|
||||
.bind-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 25rpx;
|
||||
background: #f4f4f4;
|
||||
color: #999999;
|
||||
}
|
||||
.relieve-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 25rpx;
|
||||
background: var(--ui-BG-Main-opacity-1);
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user