优化强制更新功能

This commit is contained in:
guanj
2026-06-18 16:34:25 +08:00
parent edc5dc55aa
commit f008bcb4b8
22 changed files with 1774 additions and 1650 deletions

View File

@@ -1,11 +1,11 @@
<script> <script>
import { queryDictDataCache } from './common/api/dictionary.js' import { queryDictDataCache } from './common/api/dictionary.js'
import { getImageUrl } from '@/common/api/basic' import { getImageUrl } from '@/common/api/basic'
import { checkAppUpdate } from './common/js/update.js'
export default { export default {
onLaunch: function () { onLaunch: function () {
checkAppUpdate()
// uni.onPushMessage((res) => { // uni.onPushMessage((res) => {
// console.log("收到推送消息:",res) //监听推送消息 // console.log("收到推送消息:",res) //监听推送消息
// }) // })

View File

@@ -242,4 +242,4 @@ export function getLastData() {
url: '/cs-system-boot/appVersion/getLastData?versionType=APP', url: '/cs-system-boot/appVersion/getLastData?versionType=APP',
method: 'POST', method: 'POST',
}) })
} }

View File

@@ -1,7 +1,7 @@
const debug = true // true 是连地服务端本地false 是连接线上 const debug = true // true 是连地服务端本地false 是连接线上
const development = { const development = {
domain: 'http://192.168.2.126:10215', domain: 'http://192.168.1.103:10215',
} }
const production = { const production = {

View File

@@ -1,52 +1,102 @@
import { getLastData } from '../api/user.js' import { getLastData } from '../api/user.js'
/** 规范化版本号,如 v1.6.83 → [1, 6, 83] */
const normalizeVersion = (version) => {
if (!version) return []
return String(version)
.replace(/^v/i, '')
.split('.')
.map((part) => parseInt(part, 10) || 0)
}
/**
* 比较版本号
* @returns 1 远端较新需更新 | 0 相同 | -1 本地较新
*/
const compareVersion = (remote, local) => {
const remoteParts = normalizeVersion(remote)
const localParts = normalizeVersion(local)
const len = Math.max(remoteParts.length, localParts.length)
for (let i = 0; i < len; i++) {
const rv = remoteParts[i] || 0
const lv = localParts[i] || 0
if (rv > lv) return 1
if (rv < lv) return -1
}
return 0
}
/** 自定义调试基座 / HBuilder 标准基座,不走线上更新 */
const isCustomDebugBase = (appName = '') => {
return /自定义基座|custom debug|HBuilder|DCloud/i.test(appName)
}
export const checkAppUpdate = () => { export const checkAppUpdate = () => {
// #ifndef APP-PLUS
return
// #endif
// 开发环境跳过检查 // 开发环境跳过检查
const isDev = process.env.NODE_ENV === 'development' const isDev = process.env.NODE_ENV === 'development'
// if (isDev) { if (isDev) {
console.log('开发环境,不执行更新检查') console.log('开发环境,不执行更新检查')
return return
// } }
// 自定义基座调试,跳过更新检查
// try {
// const { appName } = uni.getSystemInfoSync()
// if (isCustomDebugBase(appName)) {
// console.log('自定义基座调试中,不执行更新检查')
// return
// }
// } catch (e) {}
// 获取当前应用信息 // 获取当前应用信息
plus.runtime.getProperty(plus.runtime.appid, (info) => { plus.runtime.getProperty(plus.runtime.appid, (info) => {
// if (isCustomDebugBase(info?.name)) {
// console.log('自定义基座调试中,不执行更新检查')
// return
// }
const currentVersion = info.version const currentVersion = info.version
getLastData() getLastData()
.then((res) => { .then((res) => {
// let res = {
// data: {
// versionName: 'v1.6.83',
// forceUpdate: '1',
// androidPath: 'https://app.liuyingyong.cn/build/download/3c26e400-3a33-11f1-8997-a16e76fa35b3',
// // androidPath: 'http://112.4.144.18:8040/shiningCloud/file/canneng_wulian.apk',
// iosPath: 'xxxx',
// },
// }
if (!res?.data) {
console.log('未获取到版本信息')
return
}
// let res = { // 适配新的接口返回格式,默认强制更新
// data: { const { versionName, androidPath, iosPath, forceUpdate = '1' } = res.data
// versionName: 'v1.6.83', console.log('🚀 ~ checkAppUpdate ~ versionName, currentVersion:', versionName, currentVersion)
// forceUpdate: '1',
// androidPath: 'https://app.liuyingyong.cn/build/download/3c26e400-3a33-11f1-8997-a16e76fa35b3',
// // androidPath: 'http://112.4.144.18:8040/shiningCloud/file/canneng_wulian.apk',
// iosPath: 'xxxx',
// },
// }
if (!res?.data) {
console.log('未获取到版本信息')
return
}
// 适配新的接口返回格式 // 版本相同或本地较新则不需要更新
const { versionName, androidPath, iosPath, forceUpdate = '1' } = res.data if (compareVersion(versionName, currentVersion) <= 0) {
console.log('已是最新版本')
return
}
// 版本相同则不需要更新 const isForce = forceUpdate !== '0' // 默认强制更新,'0' 为可选更新
if (versionName.includes(currentVersion)) {
console.log('已是最新版本')
return
}
const isForce = forceUpdate === '1' // 字符串 '1' 表示强制更新 const iosUrl = iosPath
const iosUrl = iosPath handleUpdate({ version: versionName, androidPath, iosUrl, isForce })
})
handleUpdate({ version: versionName, androidPath, iosUrl, isForce }) .catch((err) => {
}) console.error('获取版本接口失败', err)
.catch((err) => { })
console.error('获取版本接口失败', err)
})
}) })
} }
@@ -58,9 +108,9 @@ const handleUpdate = ({ version, androidPath, iosUrl, isForce }) => {
const isIOS = plus.os.name === 'iOS' const isIOS = plus.os.name === 'iOS'
if (isAndroid) { if (isAndroid) {
handleAndroidUpdate({ androidPath, isForce }) handleAndroidUpdate({ version, androidPath, isForce })
} else if (isIOS) { } else if (isIOS) {
handleIOSUpdate({ iosUrl, isForce }) handleIOSUpdate({ version, iosUrl, isForce })
} else { } else {
console.warn('未知操作系统') console.warn('未知操作系统')
} }
@@ -69,7 +119,7 @@ const handleUpdate = ({ version, androidPath, iosUrl, isForce }) => {
/** /**
* 处理安卓更新 * 处理安卓更新
*/ */
const handleAndroidUpdate = ({ androidPath, isForce }) => { const handleAndroidUpdate = ({ version, androidPath, isForce }) => {
if (!androidPath?.length) { if (!androidPath?.length) {
console.error('未找到安卓安装包') console.error('未找到安卓安装包')
uni.showToast({ uni.showToast({
@@ -80,18 +130,18 @@ const handleAndroidUpdate = ({ androidPath, isForce }) => {
} }
const downloadUrl = androidPath const downloadUrl = androidPath
const content = isForce ? `发现新版本 ${version},请立即更新后继续使用` : `发现新版本 ${version},是否立即更新?`
uni.showModal({ uni.showModal({
title: '更新提示', title: '更新提示',
content: '发现新版本,是否立即更新?', content,
showCancel: !isForce, // 强制更新隐藏取消按钮 showCancel: false,
confirmText: '去更新', confirmText: '去更新',
cancelText: '暂不更新',
success: (modalRes) => { success: (modalRes) => {
if (modalRes.confirm) { if (modalRes.confirm) {
downloadAndInstallApk(downloadUrl) downloadAndInstallApk(downloadUrl)
} else if (isForce) { } else if (isForce) {
// 强制更新用户取消,退出应用 // 强制更新用户按返回键关闭弹窗时退出
plus.runtime.quit() plus.runtime.quit()
} }
}, },
@@ -101,7 +151,7 @@ const handleAndroidUpdate = ({ androidPath, isForce }) => {
/** /**
* 处理iOS更新 * 处理iOS更新
*/ */
const handleIOSUpdate = ({ iosUrl, isForce }) => { const handleIOSUpdate = ({ version, iosUrl, isForce }) => {
if (!iosUrl) { if (!iosUrl) {
console.error('未找到iOS下载链接') console.error('未找到iOS下载链接')
uni.showToast({ uni.showToast({
@@ -111,22 +161,23 @@ const handleIOSUpdate = ({ iosUrl, isForce }) => {
return return
} }
const content = isForce
? `发现新版本 ${version},请前往 App Store 更新后继续使用`
: `发现新版本 ${version},请前往 App Store 更新`
uni.showModal({ uni.showModal({
title: '更新提示', title: '更新提示',
content: '发现新版本,请前往 App Store 更新', content,
showCancel: !isForce, showCancel: false,
confirmText: '去更新', confirmText: '去更新',
cancelText: '暂不更新',
success: (modalRes) => { success: (modalRes) => {
if (modalRes.confirm) { if (modalRes.confirm) {
plus.runtime.openURL(iosUrl) plus.runtime.openURL(iosUrl)
}
// 强制更新时,无论确认还是取消都退出应用
if (isForce) {
setTimeout(() => { setTimeout(() => {
plus.runtime.quit() plus.runtime.quit()
}, 300) // 给跳转留一点时间 }, 300)
} else if (isForce) {
// 强制更新且用户取消,退出应用
} }
}, },
}) })
@@ -143,14 +194,22 @@ const downloadAndInstallApk = (url) => {
close: false, // 不允许用户关闭 close: false, // 不允许用户关闭
padlock: true, // 锁定屏幕 padlock: true, // 锁定屏幕
}) })
let progressClosed = false
const closeProgress = () => {
if (progressClosed) return
progressClosed = true
progressWaiting.close()
}
const options = { const options = {
filename: '_doc/update/canneng_wulian.apk', filename: '_doc/update/canneng_wulian.apk',
timeout: 120, timeout: 300,
} }
console.log('🚀 ~ downloadAndInstallApk ~ url:', url)
const downloadTask = plus.downloader.createDownload(url, options, (downloadedFile, status) => { const downloadTask = plus.downloader.createDownload(url, options, (downloadedFile, status) => {
progressWaiting.close() closeProgress()
if (status === 200) { if (status === 200) {
installApk(downloadedFile.filename, url) installApk(downloadedFile.filename, url)
@@ -161,9 +220,10 @@ const downloadAndInstallApk = (url) => {
// // 更新进度 // // 更新进度
downloadTask.addEventListener('statechanged', (task) => { downloadTask.addEventListener('statechanged', (task) => {
if (progressClosed) return
if (task.state === 3 && task.totalSize > 0) { if (task.state === 3 && task.totalSize > 0) {
const percent = ((task.downloadedSize / task.totalSize) * 100).toFixed(0) const percent = ((task.downloadedSize / task.totalSize) * 100).toFixed(0)
console.log("🚀 ~ downloadAndInstallApk ~ percent:", percent) console.log('🚀 ~ downloadAndInstallApk ~ percent:', percent)
// 直接更新 waiting 的标题,不会闪烁 // 直接更新 waiting 的标题,不会闪烁
progressWaiting.setTitle(`正在下载更新 ${percent}%`) progressWaiting.setTitle(`正在下载更新 ${percent}%`)
} }
@@ -197,11 +257,13 @@ const installApk = (filePath, downloadUrl) => {
uni.showModal({ uni.showModal({
title: '安装失败', title: '安装失败',
content: `安装失败:${error.message}\n请检查是否已开启安装权限`, content: `安装失败:${error.message}\n请检查是否已开启安装权限`,
showCancel: false,
confirmText: '重试', confirmText: '重试',
cancelText: '取消',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
downloadAndInstallApk(downloadUrl) downloadAndInstallApk(downloadUrl)
} else {
plus.runtime.quit()
} }
}, },
}) })
@@ -216,11 +278,13 @@ const handleDownloadError = (downloadUrl) => {
uni.showModal({ uni.showModal({
title: '下载失败', title: '下载失败',
content: '网络异常或下载链接失效,是否重试?', content: '网络异常或下载链接失效,是否重试?',
showCancel: false,
confirmText: '重试', confirmText: '重试',
cancelText: '取消',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
downloadAndInstallApk(downloadUrl) downloadAndInstallApk(downloadUrl)
} else {
plus.runtime.quit()
} }
}, },
}) })

View File

@@ -16,15 +16,15 @@ export default {
}, },
computed: { computed: {
svgHtml() { svgHtml() {
if (this.name == '电压暂降') { if (this.name == '暂降') {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100"> return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,56 L56,54 L58,55 L60,54 L63,56 L66,60 L68,62 L70,60 L72,56 L75,54 L77,52 L79,54 L81,56 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#2563eb" stroke-width="4"/> <path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,56 L56,54 L58,55 L60,54 L63,56 L66,60 L68,62 L70,60 L72,56 L75,54 L77,52 L79,54 L81,56 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#2563eb" stroke-width="4"/>
</svg>` </svg>`
} else if (this.name == '电压暂升') { } else if (this.name == '暂升') {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100"> return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,43 L56,34 L58,26 L60,34 L63,43 L66,54 L68,60 L70,54 L72,43 L75,34 L77,26 L79,34 L81,43 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#e6a23c" stroke-width="4"/> <path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,43 L56,34 L58,26 L60,34 L63,43 L66,54 L68,60 L70,54 L72,43 L75,34 L77,26 L79,34 L81,43 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#e6a23c" stroke-width="4"/>
</svg>` </svg>`
} else if (this.name == '电压中断') { } else if (this.name == '中断') {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100"> return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,50 L57,50 L60,50 L63,50 L66,50 L69,50 L72,50 L75,50 L78,50 L81,50 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#6b7280" stroke-width="4"/> <path d="M8,50 L15,50 L18,38 L21,28 L24,38 L27,50 L30,62 L33,72 L36,62 L39,50 L42,38 L45,28 L48,38 L51,50 L54,50 L57,50 L60,50 L63,50 L66,50 L69,50 L72,50 L75,50 L78,50 L81,50 L84,50 L87,38 L90,28 L93,38 L96,50" fill="none" stroke="#6b7280" stroke-width="4"/>
</svg>` </svg>`

View File

@@ -46,6 +46,7 @@
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.INTERNET\"/>", "<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
@@ -124,7 +125,10 @@
"setting" : { "setting" : {
"urlCheck" : false "urlCheck" : false
}, },
"usingComponents" : true "usingComponents" : true,
"unipush" : {
"enable" : false
}
}, },
"mp-alipay" : { "mp-alipay" : {
"usingComponents" : true "usingComponents" : true
@@ -139,7 +143,7 @@
"/api" : { "/api" : {
"https" : true, "https" : true,
// "target" : "https://pqmcn.com:8092/api", // "target" : "https://pqmcn.com:8092/api",
"target" : "http://192.168.2.126:10215", "target" : "http://192.168.1.103:10215",
"changOrigin" : true, "changOrigin" : true,
"pathRewrite" : { "pathRewrite" : {
"/api" : "" "/api" : ""
@@ -152,7 +156,7 @@
"base" : "" "base" : ""
}, },
"unipush" : { "unipush" : {
"enable" : false "enable" : true
}, },
"sdkConfigs" : { "sdkConfigs" : {
"maps" : {} "maps" : {}

View File

@@ -118,7 +118,7 @@
{ {
"path": "pages/mine/setupMessage", "path": "pages/mine/setupMessage",
"style": { "style": {
"navigationBarTitleText": "个性化推荐" "navigationBarTitleText": "推送通知配置"
} }
}, },

View File

@@ -1,157 +1,142 @@
<template> <template>
<view> <view>
<Cn-page :loading="loading" beforeRender> <Cn-page :loading="loading" beforeRender>
<view slot="body"> <view slot="body">
<view class="transfer"> <view class="transfer">
<!-- <div class="transfer-img" ref="qrCodeUrl" /> --> <!-- <div class="transfer-img" ref="qrCodeUrl" /> -->
<!-- <uqrcode ref="uqrcode" canvas-id="qrcode" :value="content" :options="{ margin: 10 }"></uqrcode> --> <!-- <uqrcode ref="uqrcode" canvas-id="qrcode" :value="content" :options="{ margin: 10 }"></uqrcode> -->
<uqrcode <uqrcode ref="uqrcode" canvas-id="qrcode" :value="content" size="200" @complete="complete"
ref="uqrcode" :loading="false"></uqrcode>
canvas-id="qrcode" <view style="height: 200rpx"></view>
:value="content" <view class="transfer-text">请让接收人员扫码接收</view>
size="200" <view class="transfer-btn">
@complete="complete" <button class="transfer-btn-item" style="background-color: #fff; color: #111" @click="back">
:loading="false" 返回
></uqrcode> </button>
<view style="height: 200rpx"></view> <button class="transfer-btn-item ml20" @click="scan">扫一扫</button>
<view class="transfer-text">请让接收人员扫码接收</view> </view>
<view class="transfer-btn"> </view>
<button class="transfer-btn-item" style="background-color: #fff; color: #111" @click="back"> </view>
返回 </Cn-page>
</button> <uni-popup ref="alertDialog" type="dialog">
<button class="transfer-btn-item ml20" @click="scan">扫一扫</button> <uni-popup-dialog style="width: 90%; margin: 5%" type="info" cancelText="禁止" confirmText="允许" title="权限说明"
</view> content='是否允许"灿能物联"使用相机?' @confirm="handleScon('camera')" @close="dialogClose"></uni-popup-dialog>
</view> </uni-popup>
</view> <uni-popup ref="message" type="message">
</Cn-page> <uni-popup-message type="info" :duration="0" style="width: 90%; margin: 5%">
<uni-popup ref="alertDialog" type="dialog"> <view style="color: #909399; font-style: 16px">相机权限使用说明:</view>
<uni-popup-dialog <view style="color: #6c6c6c; margin-top: 3rpx; "> 用于相机扫描二维码!</view>
style="width: 90%; margin: 5%" </uni-popup-message>
type="info" </uni-popup>
cancelText="禁止" </view>
confirmText="允许" </template>
title="权限说明" <script>
content='是否允许"灿能物联"使用相机?' // import QRCode from 'qrcodejs2'
@confirm="handleScon('camera')" // import UQRCode from '@/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js';
@close="dialogClose"
></uni-popup-dialog> import { transferDevice } from '@/common/api/device'
</uni-popup>
<uni-popup ref="message" type="message"> export default {
<uni-popup-message type="info" :duration="0" style="width: 90%; margin: 5%"> data() {
<view style="color: #909399; font-style: 16px">相机权限使用说明:</view> return {
<view style="color: #6c6c6c; margin-top: 3rpx; "> 用于相机扫描二维码!</view> loading: true,
</uni-popup-message> content: '',
</uni-popup> options: {},
</view> }
</template> },
<script> methods: {
// import QRCode from 'qrcodejs2' complete() {
// import UQRCode from '@/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js'; this.loading = false
},
import { transferDevice } from '@/common/api/device' back() {
uni.navigateBack({ delta: 1 })
export default { },
data() { home() {
return { uni.navigateBack({ delta: 1 })
loading: true, },
content: '', transferDevice(userId) {
options: {}, transferDevice(this.options.id, userId).then((res) => {
} uni.showToast({
}, title: '移交成功',
methods: { icon: 'none',
complete() { })
this.loading = false uni.navigateBack()
}, })
back() { },
uni.navigateBack({ delta: 1 }) scan() {
}, if (
home() { plus.os.name == 'Android' &&
uni.navigateBack({ delta: 1 }) plus.navigator.checkPermission('android.permission.CAMERA') === 'undetermined'
}, ) {
transferDevice(userId) { //未授权
transferDevice(this.options.id, userId).then((res) => { this.$refs.message.open()
uni.showToast({ this.$refs.alertDialog.open('bottom')
title: '移交成功', } else {
icon: 'none', this.handleScon()
}) }
uni.navigateBack() },
}) handleScon() {
}, this.$refs.message.close()
scan() { uni.scanCode({
if ( // onlyFromCamera:false,
plus.os.name == 'Android' && success: (res) => {
plus.navigator.checkPermission('android.permission.CAMERA') === 'undetermined' console.log(res)
) { let data = JSON.parse(res.result)
//未授权 if (data.type === 'userId') {
this.$refs.message.open() this.transferDevice(data.id)
this.$refs.alertDialog.open('bottom') } else {
} else { uni.showToast({
this.handleScon() title: '请扫描正确的二维码',
} icon: 'none',
}, })
handleScon() { }
this.$refs.message.close() },
uni.scanCode({ })
onlyFromCamera:true, },
success: (res) => { dialogClose() { this.$refs.message.close() },
console.log(res) },
let data = JSON.parse(res.result) onLoad(options) {
if (data.type === 'userId') { this.options = options
this.transferDevice(data.id) this.content = JSON.stringify({
} else { type: 'transferDevice',
uni.showToast({ id: options.id,
title: '请扫描正确的二维码', })
icon: 'none', },
}) }
} </script>
}, <style lang="scss">
}) .transfer {
}, padding: 200rpx 34rpx 0;
dialogClose() {this.$refs.message.close()}, display: flex;
}, flex-direction: column;
onLoad(options) { align-items: center;
this.options = options
this.content = JSON.stringify({ .transfer-img {}
type: 'transferDevice',
id: options.id, .transfer-text {
}) font-size: 28rpx;
}, color: #999999;
} margin-top: 30rpx;
</script> }
<style lang="scss">
.transfer { .transfer-btn {
padding: 200rpx 34rpx 0; display: flex;
display: flex; flex-direction: row;
flex-direction: column; justify-content: space-between;
align-items: center; margin-top: 100rpx;
width: 100%;
.transfer-img {
} .transfer-btn-item {
flex: 1;
.transfer-text { height: 80rpx;
font-size: 28rpx; border-radius: 12rpx;
color: #999999; background-color: $uni-theme-color;
margin-top: 30rpx; font-size: 28rpx;
} color: #fff;
display: flex;
.transfer-btn { justify-content: center;
display: flex; align-items: center;
flex-direction: row; }
justify-content: space-between; }
margin-top: 100rpx; }
width: 100%; </style>
.transfer-btn-item {
flex: 1;
height: 80rpx;
border-radius: 12rpx;
background-color: $uni-theme-color;
font-size: 28rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
}
}
</style>

View File

@@ -222,21 +222,21 @@ export default {
}, },
judgment(val, key) { judgment(val, key) {
switch (val) { switch (val) {
case '电压暂降': case '暂降':
return { return {
type: 'sag', type: 'sag',
icon: 'icon-a-svg4', icon: 'icon-a-svg4',
color: '#2563eb', color: '#2563eb',
size: '25', size: '25',
} }
case '电压暂升': case '暂升':
return { return {
type: 'swell', type: 'swell',
icon: 'icon-a-svg5', icon: 'icon-a-svg5',
color: '#e6a23c', color: '#e6a23c',
size: '25', size: '25',
} }
case '电压中断': case '中断':
return { return {
type: 'interrupt', type: 'interrupt',
icon: 'icon-zhongduan2', icon: 'icon-zhongduan2',

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="index-zhuyonghu"> <view class="index-zhuyonghu">
<template v-if="devCount.engineeringListLength > 0"> <template v-if="devCount.engineeringListLength > 0">
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" />
<text>所有工程设备统计</text> <text>所有工程设备统计</text>
</view> </view>
@@ -33,7 +33,7 @@
</view> </view>
<!-- <view class="mt20"></view> --> <!-- <view class="mt20"></view> -->
</template> </template>
<!-- <view class="canneng-index-title mb20">当前工程设备统计</view> <!-- <view class="canneng-index-title mb20 ml20">当前工程设备统计</view>
<view class="header"> <view class="header">
<view class="header-item" @click="jump('nowEngineering')"> <view class="header-item" @click="jump('nowEngineering')">
<view class="header-item-value" <view class="header-item-value"
@@ -50,7 +50,7 @@
<view class="header-item-label">离线设备</view> <view class="header-item-label">离线设备</view>
</view> </view>
</view> --> </view> -->
<view class="canneng-index-title mt20 canneng-index-title-with-icon"> <view class="canneng-index-title mt20 ml20 canneng-index-title-with-icon">
<uni-icons type="settings" size="18" color="#376cf3" /> <uni-icons type="settings" size="18" color="#376cf3" />
<text>常用功能</text> <text>常用功能</text>
</view> </view>

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="index-zhuyonghu"> <view class="index-zhuyonghu">
<template v-if="devCount.engineeringListLength > 1"> <template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" />
<text>所有工程设备统计</text> <text>所有工程设备统计</text>
</view> </view>
@@ -21,7 +21,7 @@
</view> </view>
<view class="mt20"></view> <view class="mt20"></view>
</template> </template>
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" />
<text>当前工程设备统计</text> <text>当前工程设备统计</text>
</view> </view>

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="index-zhuanzhi"> <view class="index-zhuanzhi">
<template v-if="devCount.engineeringListLength > 1"> <template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" />
<text>所有工程设备统计</text> <text>所有工程设备统计</text>
</view> </view>
@@ -35,7 +35,7 @@
</view> </view>
<view class="mt20"></view> <view class="mt20"></view>
</template> </template>
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" />
<text>当前工程设备统计</text> <text>当前工程设备统计</text>
</view> </view>

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="index-zhuyonghu"> <view class="index-zhuyonghu">
<template v-if="devCount.engineeringListLength > 1"> <template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" />
<text>所有工程设备统计</text> <text>所有工程设备统计</text>
</view> </view>
@@ -21,7 +21,7 @@
</view> </view>
<view class="mt20"></view> <view class="mt20"></view>
</template> </template>
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" />
<text>当前工程设备统计</text> <text>当前工程设备统计</text>
</view> </view>
@@ -49,7 +49,7 @@
<view class="header-item-label">稳态事件数</view> <view class="header-item-label">稳态事件数</view>
</view> </view>
</view> </view>
<view class="canneng-index-title mt20 canneng-index-title-with-icon"> <view class="canneng-index-title mt20 ml20 canneng-index-title-with-icon">
<uni-icons type="settings" size="18" color="#376cf3" /> <uni-icons type="settings" size="18" color="#376cf3" />
<text>常用功能</text> <text>常用功能</text>
</view> </view>

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="index-zhuanzhi"> <view class="index-zhuanzhi">
<template v-if="devCount.engineeringListLength > 1"> <template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="18" color="#376cf3" />
<text>所有工程设备统计</text> <text>所有工程设备统计</text>
</view> </view>
@@ -33,7 +33,7 @@
</view> </view>
<view class="mt20"></view> <view class="mt20"></view>
</template> </template>
<view class="canneng-index-title mb20 canneng-index-title-with-icon"> <view class="canneng-index-title mb20 ml20 canneng-index-title-with-icon">
<uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" /> <uni-icons custom-prefix="iconfont" type="icon-zaixianjianceshebei" size="18" color="#376cf3" />
<text>当前工程设备统计</text> <text>当前工程设备统计</text>
</view> </view>

File diff suppressed because it is too large Load Diff

View File

@@ -1,307 +1,312 @@
<template> <template>
<view class="target-info-page"> <view class="target-info-page">
<scroll-view class="target-info-scroll" scroll-y :show-scrollbar="false"> <scroll-view class="target-info-scroll" scroll-y :show-scrollbar="false">
<view class="monitor-card card"> <view class="monitor-card card">
<view class="card-header"> <view class="card-header">
<view class="event-icon"> <view class="event-icon">
<Cn-icon-transient name="监测点" /> <Cn-icon-transient name="监测点" />
</view> </view>
<view class="card-header-info"> <view class="card-header-info">
<text class="point-name ellipsis">{{ pointInfo.pointName || '-' }}</text> <text class="point-name ellipsis">{{ pointInfo.pointName || '-' }}</text>
<view class="meta-row"> <view class="meta-row">
<text v-if="pointInfo.engineeringName" class="meta-item ellipsis"> <text v-if="pointInfo.engineeringName" class="meta-item ellipsis">
工程{{ pointInfo.engineeringName }} 工程{{ pointInfo.engineeringName }}
</text> </text>
<text class="meta-item ellipsis">项目{{ pointInfo.projectName || '-' }}</text> <text class="meta-item ellipsis">项目{{ pointInfo.projectName || '-' }}</text>
<text class="meta-item ellipsis">设备{{ pointInfo.deviceName || '-' }}</text> <text class="meta-item ellipsis">设备{{ pointInfo.deviceName || '-' }}</text>
</view> </view>
<text class="meta-time ellipsis">最新数据时间{{ pointInfo.dataTime || '-' }}</text> <text class="meta-time ellipsis">最新数据时间{{ pointInfo.dataTime || '-' }}</text>
</view> </view>
</view> </view>
<view class="legend-row"> <view class="legend-row">
<view class="legend-item" v-for="phase in phaseColors" :key="phase.name"> <view class="legend-item" v-for="phase in phaseColors" :key="phase.name">
<view class="legend-dot" :style="{ background: phase.color }" /> <view class="legend-dot" :style="{ background: phase.color }" />
<text class="legend-text">{{ phase.name }}</text> <text class="legend-text">{{ phase.name }}</text>
</view> </view>
</view> </view>
<view v-if="moreChildren.length" class="params-section"> <view v-if="moreChildren.length" class="params-section">
<view <view v-for="(rowItems, rowIdx) in chunkedChildren(moreChildren)" :key="rowIdx" class="double-row">
v-for="(rowItems, rowIdx) in chunkedChildren(moreChildren)" <view v-for="(child, childIdx) in rowItems" :key="child.targetId || childIdx"
:key="rowIdx" class="param-group">
class="double-row" <view class="param-title">
> <text>{{ child.name }}{{ child.unit ? ` (${child.unit})` : '' }}</text>
<view v-for="(child, childIdx) in rowItems" :key="child.targetId || childIdx" class="param-group"> </view>
<view class="param-title"> <view v-if="hasTPhaseData(child)" class="phase-single">
<text>{{ child.name }}{{ child.unit ? ` (${child.unit})` : '' }}</text> <text class="phase-value-vertical phase-value-vertical--neutral">{{ child.T }}</text>
</view> </view>
<view v-if="hasTPhaseData(child)" class="phase-single"> <view v-else class="phase-vertical">
<text class="phase-value-vertical phase-value-vertical--neutral">{{ child.T }}</text> <view class="phase-item-vertical">
</view> <text class="phase-value-vertical" :style="{ color: phaseColors[0].color }">{{
<view v-else class="phase-vertical"> child.A }}</text>
<view class="phase-item-vertical"> </view>
<text class="phase-value-vertical" :style="{ color: phaseColors[0].color }">{{ child.A }}</text> <view class="phase-divider" />
</view> <view class="phase-item-vertical">
<view class="phase-divider" /> <text class="phase-value-vertical" :style="{ color: phaseColors[1].color }">{{
<view class="phase-item-vertical"> child.B }}</text>
<text class="phase-value-vertical" :style="{ color: phaseColors[1].color }">{{ child.B }}</text> </view>
</view> <view class="phase-divider" />
<view class="phase-divider" /> <view class="phase-item-vertical">
<view class="phase-item-vertical"> <text class="phase-value-vertical" :style="{ color: phaseColors[2].color }">{{
<text class="phase-value-vertical" :style="{ color: phaseColors[2].color }">{{ child.C }}</text> child.C }}</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view v-else class="empty-wrap"> <view v-else class="empty-wrap">
<Cn-empty msg="暂无更多指标" :paddingTop="120"></Cn-empty> <Cn-empty msg="暂无更多指标" :paddingTop="120"></Cn-empty>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
<script> <script>
export default { export default {
data() { data() {
return { return {
pointInfo: {}, pointInfo: {},
phaseColors: [ phaseColors: [
{ name: 'A相', color: '#F1B22E' }, { name: 'A相', color: '#F1B22E' },
{ name: 'B相', color: '#2BA471' }, { name: 'B相', color: '#2BA471' },
{ name: 'C相', color: '#D54941' }, { name: 'C相', color: '#D54941' },
], ],
} }
}, },
computed: { computed: {
moreChildren() { moreChildren() {
const children = this.pointInfo.children || [] const children = this.pointInfo.children || []
const selected = this.getSelectedIndicators() // 治理测点「更多指标」页展示全部
return children.filter( if (this.pointInfo.lineType === 0 && this.pointInfo.showAllIndicators) {
(child) => !selected.some((name) => this.matchIndicator(child.name, name)), return children
) }
}, const selected = this.getSelectedIndicators()
}, return children.filter(
onLoad() { (child) => !selected.some((name) => this.matchIndicator(child.name, name)),
const point = uni.getStorageSync('monitorPointDetail') )
if (point) { },
this.pointInfo = { },
...point, onLoad() {
selectedIndicators: this.getSelectedIndicatorsFromStorage(point.selectedIndicators), const point = uni.getStorageSync('monitorPointDetail')
} if (point) {
if (point.pointName) { this.pointInfo = {
uni.setNavigationBarTitle({ title: point.pointName }) ...point,
} selectedIndicators: this.getSelectedIndicatorsFromStorage(point.selectedIndicators),
} }
}, if (point.pointName) {
onUnload() { uni.setNavigationBarTitle({ title: point.pointName })
uni.removeStorageSync('monitorPointDetail') }
}, }
methods: { },
getSelectedIndicatorsFromStorage(fallback = []) { onUnload() {
const cached = uni.getStorageSync(this.$cacheKey.monitorSelectedIndicators) uni.removeStorageSync('monitorPointDetail')
if (Array.isArray(cached) && cached.length) { },
return cached methods: {
} getSelectedIndicatorsFromStorage(fallback = []) {
return Array.isArray(fallback) ? fallback : [] const cached = uni.getStorageSync(this.$cacheKey.monitorSelectedIndicators)
}, if (Array.isArray(cached) && cached.length) {
getSelectedIndicators() { return cached
return this.getSelectedIndicatorsFromStorage(this.pointInfo.selectedIndicators) }
}, return Array.isArray(fallback) ? fallback : []
matchIndicator(name, selected) { },
if (!name || !selected) return false getSelectedIndicators() {
const base = (name || '').replace(/\(.*\)/, '').trim() return this.getSelectedIndicatorsFromStorage(this.pointInfo.selectedIndicators)
const selectedBase = (selected || '').replace(/\(.*\)/, '').trim() },
return base === selectedBase matchIndicator(name, selected) {
}, if (!name || !selected) return false
hasTPhaseData(child) { const base = (name || '').replace(/\(.*\)/, '').trim()
return child.T !== undefined && child.T !== null && child.T !== '-' const selectedBase = (selected || '').replace(/\(.*\)/, '').trim()
}, return base === selectedBase
chunkedChildren(children) { },
const result = [] hasTPhaseData(child) {
for (let i = 0; i < children.length; i += 2) { return child.T !== undefined && child.T !== null && child.T !== '-'
result.push(children.slice(i, i + 2)) },
} chunkedChildren(children) {
return result const result = []
}, for (let i = 0; i < children.length; i += 2) {
}, result.push(children.slice(i, i + 2))
} }
</script> return result
},
<style lang="scss" scoped> },
.target-info-page { }
min-height: 100vh; </script>
background: #f7f8fa;
} <style lang="scss" scoped>
.target-info-page {
.target-info-scroll { min-height: 100vh;
height: 100vh; background: #f7f8fa;
box-sizing: border-box; }
padding: 20rpx 0 40rpx;
} .target-info-scroll {
height: 100vh;
.card { box-sizing: border-box;
background: #ffffff; padding: 20rpx 0 40rpx;
border-radius: 10px; }
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 1px 1px;
margin: 0 10px; .card {
overflow: hidden; background: #ffffff;
} border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 1px 1px;
.monitor-card { margin: 0 10px;
border: 1rpx solid #eef2f6; overflow: hidden;
} }
.card-header { .monitor-card {
padding: 20rpx 20rpx 12rpx; border: 1rpx solid #eef2f6;
display: flex; }
align-items: center;
border-bottom: 1rpx solid #eef2f6; .card-header {
} padding: 20rpx 20rpx 12rpx;
display: flex;
.event-icon { align-items: center;
width: 90rpx; border-bottom: 1rpx solid #eef2f6;
height: 90rpx; }
border-radius: 16rpx;
display: flex; .event-icon {
justify-content: center; width: 90rpx;
align-items: center; height: 90rpx;
margin-right: 20rpx; border-radius: 16rpx;
background-color: #376cf320; display: flex;
flex-shrink: 0; justify-content: center;
} align-items: center;
margin-right: 20rpx;
.card-header-info { background-color: #376cf320;
flex: 1; flex-shrink: 0;
min-width: 0; }
}
.card-header-info {
.point-name { flex: 1;
display: block; min-width: 0;
font-size: 30rpx; }
font-weight: 700;
color: #333333; .point-name {
margin-bottom: 8rpx; display: block;
} font-size: 30rpx;
font-weight: 700;
.meta-row { color: #333333;
display: grid; margin-bottom: 8rpx;
grid-template-columns: 1fr 1fr; }
gap: 6rpx 12rpx;
} .meta-row {
display: grid;
.meta-item { grid-template-columns: 1fr 1fr;
font-size: 24rpx; gap: 6rpx 12rpx;
color: #666666; }
line-height: 1.2;
} .meta-item {
font-size: 24rpx;
.meta-time { color: #666666;
display: block; line-height: 1.2;
margin-top: 8rpx; }
font-size: 24rpx;
color: #666666; .meta-time {
line-height: 1.2; display: block;
} margin-top: 8rpx;
font-size: 24rpx;
.legend-row { color: #666666;
display: flex; line-height: 1.2;
gap: 20rpx; }
align-items: center;
padding: 16rpx 20rpx; .legend-row {
} display: flex;
gap: 20rpx;
.legend-item { align-items: center;
display: flex; justify-content: end;
align-items: center; padding: 16rpx 20rpx;
gap: 8rpx; }
}
.legend-item {
.legend-dot { display: flex;
width: 16rpx; align-items: center;
height: 16rpx; gap: 8rpx;
border-radius: 50%; }
}
.legend-dot {
.legend-text { width: 16rpx;
font-size: 24rpx; height: 16rpx;
color: #666666; border-radius: 50%;
} }
.params-section { .legend-text {
padding: 0 16rpx 20rpx; font-size: 24rpx;
} color: #666666;
}
.empty-wrap {
position: relative; .params-section {
min-height: 320rpx; padding: 0 16rpx 20rpx;
} }
.double-row { .empty-wrap {
display: grid; position: relative;
grid-template-columns: repeat(2, 1fr); min-height: 320rpx;
gap: 16rpx; }
margin-bottom: 16rpx;
.double-row {
&:last-child { display: grid;
margin-bottom: 0; grid-template-columns: repeat(2, 1fr);
} gap: 16rpx;
} margin-bottom: 16rpx;
.param-group { &:last-child {
min-width: 0; margin-bottom: 0;
background: #f3f3f3; }
border-radius: 16rpx; }
padding: 16rpx 8rpx 12rpx;
} .param-group {
min-width: 0;
.param-title { background: #f3f3f3;
font-size: 24rpx; border-radius: 16rpx;
color: #666666; padding: 16rpx 8rpx 12rpx;
margin-bottom: 8rpx; }
padding-left: 4rpx;
} .param-title {
font-size: 24rpx;
.phase-vertical { color: #666666;
display: flex; margin-bottom: 8rpx;
align-items: stretch; padding-left: 4rpx;
} }
.phase-single { .phase-vertical {
display: flex; display: flex;
justify-content: center; align-items: stretch;
align-items: center; }
padding: 4rpx;
} .phase-single {
display: flex;
.phase-item-vertical { justify-content: center;
flex: 1; align-items: center;
display: flex; padding: 4rpx;
justify-content: center; }
align-items: center;
padding: 4rpx; .phase-item-vertical {
} flex: 1;
display: flex;
.phase-value-vertical { justify-content: center;
font-size: 28rpx; align-items: center;
font-weight: 700; padding: 4rpx;
}
&--neutral {
color: #333333; .phase-value-vertical {
} font-size: 28rpx;
} font-weight: 700;
.phase-divider { &--neutral {
width: 1px; color: #333333;
background: #d2d2d2; }
margin: 8rpx 0; }
}
.phase-divider {
.ellipsis { width: 1px;
overflow: hidden; background: #d2d2d2;
text-overflow: ellipsis; margin: 8rpx 0;
white-space: nowrap; }
}
</style> .ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -72,6 +72,7 @@ import Engineering from './comp/engineering.vue'
import list from '../../common/js/list' import list from '../../common/js/list'
import { getDevCount } from '../../common/api/device.js' import { getDevCount } from '../../common/api/device.js'
import { queryEngineering } from '@/common/api/engineering.js' import { queryEngineering } from '@/common/api/engineering.js'
import { checkAppUpdate } from '@/common/js/update.js'
export default { export default {
mixins: [list], mixins: [list],
@@ -252,6 +253,11 @@ export default {
this.showBackTop = e.scrollTop > 200 this.showBackTop = e.scrollTop > 200
}, },
onLoad() { onLoad() {
// #ifdef APP-PLUS
checkAppUpdate()
// #endif
// 页面加载时,动态配置导航栏按钮 // 页面加载时,动态配置导航栏按钮
if (!uni.getStorageSync(this.$cacheKey.access_token)) { if (!uni.getStorageSync(this.$cacheKey.access_token)) {
uni.reLaunch({ uni.reLaunch({
@@ -289,6 +295,8 @@ export default {
// #endif // #endif
}, },
onShow() { onShow() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo) this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
let access_token = uni.getStorageSync(this.$cacheKey.access_token) let access_token = uni.getStorageSync(this.$cacheKey.access_token)
@@ -319,7 +327,7 @@ export default {
}, },
} }
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.popup-content { .popup-content {
background: #fff; background: #fff;
} }

View File

@@ -101,7 +101,7 @@ export default {
this.getDevCount() this.getDevCount()
this.$nextTick(() => { this.$nextTick(() => {
if (params.type !== '') { if (params.type !== '') {
this.current = params.type - 0 this.current = (params.type - 0)||0
} }
if (params.engineeringName != '') { if (params.engineeringName != '') {
this.$refs.cnFilterCriteria && this.$refs.cnFilterCriteria.external(params) this.$refs.cnFilterCriteria && this.$refs.cnFilterCriteria.external(params)

View File

@@ -259,7 +259,7 @@ export default {
handleScon() { handleScon() {
this.$refs.message.close() this.$refs.message.close()
uni.scanCode({ uni.scanCode({
onlyFromCamera: true, // onlyFromCamera: false,
success: (res) => { success: (res) => {
console.log('条码类型:' + res.scanType) console.log('条码类型:' + res.scanType)
console.log('条码内容:' + res.result) console.log('条码内容:' + res.result)

View File

@@ -1,286 +1,286 @@
<template> <template>
<Cn-page :loading="loading"> <Cn-page :loading="loading">
<view class="detail" slot="body"> <view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx"> <view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> --> <!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.date }}</view> <view>{{ detail.date }}</view>
</view> </view>
<view class="detail-content" style="padding-top: 0px;padding-bottom: 0px"> <view class="detail-content" style="padding-top: 0px;padding-bottom: 0px">
<view class="detail-content-title pb20 pt20 pl20">终端告警列表</view> <view class="detail-content-title pb20 pt20 pl20">终端告警列表</view>
</view> </view>
<view class="event-list"> <view class="event-list">
<uni-card class="event-item" :class="item.type" v-for="(item, index) in list" :key="index"> <uni-card class="event-item" :class="item.type" v-for="(item, index) in list" :key="index">
<!-- 头部图标 + 信息 + 操作 --> <!-- 头部图标 + 信息 + 操作 -->
<view class="event-header"> <view class="event-header">
<view class="event-icon" <view class="event-icon"
:class="item.devType == 'Direct_Connected_Device' ? 'zl-bgc' : 'jc-bgc'"> :class="item.devType == 'Direct_Connected_Device' ? 'zl-bgc' : 'jc-bgc'">
<Cn-icon-transient :name="item.devType == 'Direct_Connected_Device' ? '治理设备' : '监测设备'" /> <Cn-icon-transient :name="item.devType == 'Direct_Connected_Device' ? '治理设备' : '监测设备'" />
<view class="badge1" v-if="item.status == 0"> </view> <view class="badge1" v-if="item.status == 0"> </view>
</view> </view>
<view class="event-info"> <view class="event-info">
<view class="event-title"> <view class="event-title">
<text class="event-id">{{ item.devName }}</text> <text class="event-id">{{ item.devName }}</text>
<text class="event-tag" <text class="event-tag"
:class="item.devType == 'Direct_Connected_Device' ? 'zl-tag' : 'jc-tag'">{{ :class="item.devType == 'Direct_Connected_Device' ? 'zl-tag' : 'jc-tag'">{{
item.devType == 'Direct_Connected_Device' ? '治理设备' : '监测设备' }}</text> item.devType == 'Direct_Connected_Device' ? '治理设备' : '监测设备' }}</text>
</view> </view>
<view class="event-desc"> <view class="event-desc">
<text>工程{{ item.engineeringName }}</text> <text>工程{{ item.engineeringName }}</text>
<text>项目{{ item.projectName }}</text> <text>项目{{ item.projectName }}</text>
<!-- <text v-if="item.dataDetails.onlineRate.isAbnormal">在线率{{ <!-- <text v-if="item.dataDetails.onlineRate.isAbnormal">在线率{{
item.dataDetails.onlineRate.value }}% 限值{{ item.dataDetails.onlineRate.threshold item.dataDetails.onlineRate.value }}% 限值{{ item.dataDetails.onlineRate.threshold
}}% </text> --> }}% </text> -->
<!-- <text>事件时间{{ item.startTime }}</text> --> <!-- <text>事件时间{{ item.startTime }}</text> -->
</view> </view>
</view> </view>
</view> </view>
<!-- 详情区域 --> <!-- 详情区域 -->
<view class="event-detail"> <view class="event-detail">
<view v-if="item.dataDetails.onlineRate.isAbnormal"> <view v-if="item.dataDetails.onlineRate.isAbnormal">
<text>在线率 <text>在线率
<text class="integrity-value">{{ item.dataDetails.onlineRate.value }}%</text>  <text class="integrity-value">{{ item.dataDetails.onlineRate.value }}%</text> 
</text> </text>
</view> </view>
<view v-if="hasIntegrityAbnormal(item)" class="mt10"> <view v-if="hasIntegrityAbnormal(item)" class="mt10">
数据完整性 数据完整性
<view class="integrity-grid"> <view class="integrity-grid">
<view <view
v-for="(rowItems, rowIdx) in chunkedPoints(getAbnormalPoints(item))" v-for="(rowItems, rowIdx) in chunkedPoints(getAbnormalPoints(item))"
:key="rowIdx" :key="rowIdx"
class="grid-row"> class="grid-row">
<view <view
v-for="(point, pIdx) in rowItems" v-for="(point, pIdx) in rowItems"
:key="pIdx" :key="pIdx"
class="param-group"> class="param-group">
<view class="param-title"> <view class="param-title">
<text>{{ point.monitorName }}</text> <text>{{ point.monitorName }}</text>
</view> </view>
<view class="integrity-value-row"> <view class="integrity-value-row">
<text class="integrity-value">{{ point.value }}%</text> <text class="integrity-value">{{ point.value }}%</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view v-if="item.warnCounts" class="mt10 "> <view v-if="item.warnCounts" class="mt10 ">
终端告警 {{ item.warnCounts }} 详情如下 终端告警 {{ item.warnCounts }} 详情如下
<view class="textBox mt10"> <view class="textBox mt10">
<view v-for="val in item.warnDetails" class="textBox mb5" style=" font-size: 26rpx;"> <view v-for="val in item.warnDetails" class="textBox mb5" style=" font-size: 26rpx;">
{{ val.warnEventTime + '发生' + val.warnEventDesc }} {{ val.warnEventTime + '发生' + val.warnEventDesc }}
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</uni-card> </uni-card>
</view> </view>
</view> </view>
</Cn-page> </Cn-page>
</template> </template>
<script> <script>
import { updateStatus, queryAlarmDetail } from '@/common/api/message' import { updateStatus, queryAlarmDetail } from '@/common/api/message'
export default { export default {
data() { data() {
return { return {
loading: true, loading: true,
detail: {}, detail: {},
limit: '', limit: '',
collapseValue: '0', collapseValue: '0',
list: [], list: [],
} }
}, },
onLoad(options) { onLoad(options) {
this.loading = true this.loading = true
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%')) this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.init() this.init()
if (this.detail.isRead != 1) { if (this.detail.isRead != 1) {
updateStatus({ updateStatus({
eventIds: [this.detail.eventId], eventIds: [this.detail.eventId],
}) })
} }
}, },
methods: { methods: {
hasIntegrityAbnormal(item) { hasIntegrityAbnormal(item) {
const points = item?.dataDetails?.integrity?.monitorPoints const points = item?.dataDetails?.integrity?.monitorPoints
if (!Array.isArray(points) || !points.length) return false if (!Array.isArray(points) || !points.length) return false
return points.some((p) => p.isAbnormal === true) return points.some((p) => p.isAbnormal === true)
}, },
getAbnormalPoints(item) { getAbnormalPoints(item) {
return item?.dataDetails?.integrity?.monitorPoints?.filter((p) => p.isAbnormal === true) || [] return item?.dataDetails?.integrity?.monitorPoints?.filter((p) => p.isAbnormal === true) || []
}, },
chunkedPoints(points) { chunkedPoints(points) {
const result = [] const result = []
for (let i = 0; i < points.length; i += 3) { for (let i = 0; i < points.length; i += 3) {
result.push(points.slice(i, i + 3)) result.push(points.slice(i, i + 3))
} }
return result return result
}, },
init() { init() {
queryAlarmDetail({ queryAlarmDetail({
devList: this.detail.devIds, devList: this.detail.devIds,
time: this.detail.date, time: this.detail.date,
}) })
.then((res) => { .then((res) => {
this.list = res.data this.list = res.data
this.loading = false this.loading = false
}) })
.catch(() => { .catch(() => {
this.loading = false this.loading = false
}) })
}, },
}, },
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../index.scss'; @import '../index.scss';
.detail { .detail {
padding: 20rpx 0; padding: 20rpx 0;
.detail-content { .detail-content {
padding: 20rpx; padding: 20rpx;
background: #fff; background: #fff;
margin-bottom: 20rpx; margin-bottom: 20rpx;
font-size: 28rpx; font-size: 28rpx;
.detail-content-title { .detail-content-title {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
position: relative; position: relative;
padding-left: 20rpx; padding-left: 20rpx;
font-size: 30rpx; font-size: 30rpx;
color: #111; color: #111;
font-weight: 700; font-weight: 700;
&::before { &::before {
content: ''; content: '';
position: absolute; position: absolute;
left: 0; left: 0;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
width: 8rpx; width: 8rpx;
height: 28rpx; height: 28rpx;
background: $uni-theme-color; background: $uni-theme-color;
border-radius: 3rpx; border-radius: 3rpx;
} }
} }
} }
.collapseTop { .collapseTop {
padding: 10rpx 0; padding: 10rpx 0;
margin-left: 15px; margin-left: 15px;
.name { .name {
font-size: 28rpx; font-size: 28rpx;
font-weight: 700; font-weight: 700;
color: #333333; color: #333333;
} }
} }
.frequency { .frequency {
display: flex; display: flex;
font-size: 28rpx; font-size: 28rpx;
// color: #666666; // color: #666666;
} }
} }
.textBox { .textBox {
// border-bottom: 1px solid #eee; // border-bottom: 1px solid #eee;
font-size: 28rpx; font-size: 28rpx;
color: #666666; color: #666666;
text-indent: 2em; text-indent: 2em;
} }
.event-list { .event-list {
// background: #fff; // background: #fff;
padding-bottom: 10rpx; padding-bottom: 10rpx;
// .event-icon { // .event-icon {
// background-color: #376cf320; // background-color: #376cf320;
// } // }
.zl-bgc { .zl-bgc {
background-color: #376cf320; background-color: #376cf320;
} }
.jc-bgc { .jc-bgc {
background-color: #376cf320; background-color: #376cf320;
} }
.zl-tag { .zl-tag {
background-color: #007aff20; background-color: #007aff20;
color: #007aff; color: #007aff;
} }
.jc-tag { .jc-tag {
background-color: #007aff20; background-color: #007aff20;
color: #007aff; color: #007aff;
} }
} }
/deep/ .uni-collapse-item__title-box { /deep/ .uni-collapse-item__title-box {
padding: 0 15px 0 0; padding: 0 15px 0 0;
height: 56rpx; height: 56rpx;
line-height: 56rpx; line-height: 56rpx;
font-size: 26rpx !important; font-size: 26rpx !important;
color: #666666; color: #666666;
span { span {
font-size: 26rpx !important; font-size: 26rpx !important;
} }
} }
.textBox { .textBox {
max-height: 150rpx; max-height: 150rpx;
overflow-y: auto; overflow-y: auto;
} }
.integrity-grid { .integrity-grid {
margin-top: 16rpx; margin-top: 16rpx;
} }
.grid-row { .grid-row {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 16rpx; gap: 16rpx;
margin-bottom: 16rpx; margin-bottom: 16rpx;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
.param-group { .param-group {
min-width: 0; min-width: 0;
background: #f3f3f3; background: #f3f3f3;
border-radius: 16rpx; border-radius: 16rpx;
padding: 16rpx 8rpx 12rpx; padding: 16rpx 8rpx 12rpx;
text-align: center; text-align: center;
} }
.param-title { .param-title {
font-size: 24rpx; font-size: 24rpx;
color: #666666; color: #666666;
// margin-bottom: 8rpx; // margin-bottom: 8rpx;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.integrity-value-row { .integrity-value-row {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 2rpx; padding: 2rpx;
} }
.integrity-value { .integrity-value {
font-size: 26rpx; font-size: 26rpx;
font-weight: 700; font-weight: 700;
color: #333333; color: #333333;
} }
</style> </style>

View File

@@ -76,11 +76,11 @@
border-radius: 20rpx; border-radius: 20rpx;
} }
/* 电压暂降 - 蓝色系 */ /* 暂降 - 蓝色系 */
.sag .event-icon { .sag .event-icon {
background-color: #2563eb20; background-color: #2563eb20;
} }
/* 电压暂升 - 橙色系 */ /* 暂升 - 橙色系 */
.swell .event-icon { .swell .event-icon {
background-color: #e6a23c20; background-color: #e6a23c20;
} }

View File

@@ -115,9 +115,9 @@ export default {
filterValue: 0, filterValue: 0,
dataList: [ dataList: [
{ value: 0, label: '暂态数量', key: '' }, { value: 0, label: '暂态数量', key: '' },
{ value: 0, label: '暂降', key: '电压暂降' }, { value: 0, label: '暂降', key: '暂降' },
{ value: 0, label: '中断', key: '电压中断' }, { value: 0, label: '中断', key: '中断' },
{ value: 0, label: '暂升', key: '电压暂升' }, { value: 0, label: '暂升', key: '暂升' },
], ],
status: 'noMore', //more加载前 loading加载中 noMore加载后 status: 'noMore', //more加载前 loading加载中 noMore加载后
sort: 0, sort: 0,
@@ -177,21 +177,21 @@ export default {
}, },
judgment(val, key) { judgment(val, key) {
switch (val) { switch (val) {
case '电压暂降': case '暂降':
return { return {
type: 'sag', type: 'sag',
icon: 'icon-a-svg4', icon: 'icon-a-svg4',
color: '#2563eb', color: '#2563eb',
size: '25', size: '25',
} }
case '电压暂升': case '暂升':
return { return {
type: 'swell', type: 'swell',
icon: 'icon-a-svg5', icon: 'icon-a-svg5',
color: '#e6a23c', color: '#e6a23c',
size: '25', size: '25',
} }
case '电压中断': case '中断':
return { return {
type: 'interrupt', type: 'interrupt',
icon: 'icon-zhongduan2', icon: 'icon-zhongduan2',