提交app

This commit is contained in:
guanj
2026-03-17 14:00:55 +08:00
parent b71200cb97
commit 00e34c168f
82 changed files with 13202 additions and 4602 deletions

View File

@@ -37,6 +37,7 @@ export default {
<style lang="scss">
/*每个页面公共css */
@import './common/css/base.scss';
@import "@/static/iconfont/iconfont.css";
/deep/ uni-tabbar .uni-tabbar__badge {
width: auto;
@@ -47,4 +48,10 @@ export default {
font-size: 20rpx;
padding: 0 8rpx;
}
// .uni-page-refresh__path {
// stroke: #007aff !important; /* 改成你想要的颜色,比如红色 #ff0000 */
// }
// .uni-page-refresh__icon {
// fill: #007aff !important; /* 改成你想要的颜色,比如红色 #ff0000 */
// }
</style>

View File

@@ -1,197 +1,221 @@
import request from '../js/request'
import config from '../js/config'
// 获取设备
export function getDeviceList(params) {
return request({
url: '/cs-device-boot/EquipmentDelivery/queryEquipmentByProject',
method: 'post',
data: params,
})
}
/**
* 设备统计
* @param {*} id 工程id
* @returns
*/
export function getDevCount(id) {
return request({
url: '/cs-device-boot/deviceUser/devCount',
method: 'post',
data: { id },
})
}
// 获取直连设备模板信息
export function getModel(nDid) {
return request({
url: '/access-boot/device/model',
method: 'post',
data: {
nDid,
},
})
}
// 直连设备接入
export function addDevice(params) {
return request({
url: '/access-boot/device/access',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
// 直连设备注册
export function registerDevice(nDid, type) {
return request({
url: '/access-boot/device/register',
method: 'post',
data: {
nDid,
type,
},
})
}
// 查询拓扑图模板
export const getTopoTemplate = () => {
return request({
url: '/cs-device-boot/topologyTemplate/queryImage',
method: 'POST',
})
}
// 查询拓扑图模板监测点
export const queryByTopoId = (id) => {
return request({
url: '/cs-device-boot/lineTemplate/queryByTopoId',
method: 'POST',
data: {
topoId: id,
},
})
}
// 查询设备拓扑图
export const queryTopologyDiagram = (devId) => {
return request({
url: '/cs-device-boot/lineTopologyDiagram/queryTopologyDiagram',
method: 'POST',
data: {
devId,
},
})
}
// 设备扫码移交
export const transferDevice = (id, userId) => {
return request({
url: '/cs-device-boot/deviceUser/transfer',
method: 'POST',
data: {
ids: id,
userId: userId || uni.getStorageSync('userInfo').userIndex,
},
})
}
// 设备扫码分享
export const shareDevice = (id, userId) => {
return request({
url: '/cs-device-boot/deviceUser/share',
method: 'POST',
data: {
ids: id,
},
})
}
// 设备删除
export const deleteDevice = (id) => {
return request({
url: '/cs-device-boot/deviceUser/delete',
method: 'POST',
data: {
eid: id,
},
})
}
// 设备查询通过id获取
export const queryDeivceById = (id) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/queryEquipmentById',
method: 'POST',
data: {
ids: id,
},
})
}
// 字典树接口通过id
export const queryByid = (id) => {
return request({
url: '/system-boot/dictTree/queryByid',
method: 'post',
data: {
id,
},
})
}
//设备修改监测点信息
export const updateDevice = (params) => {
return request({
url: '/cs-device-boot/lineTopologyDiagram/auditList',
method: 'POST',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
// 设备用户列表
export const queryDeviceUser = (devId) => {
return request({
url: '/cs-device-boot/deviceUser/queryUserById',
method: 'POST',
data: {
devId,
},
})
}
// 取消分享
export const cancelShare = (params) => {
return request({
url: '/cs-device-boot/deviceUser/cancelShare',
method: 'POST',
data: params,
})
}
// 取消调试
export const cancelDebug = (params) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/deleteTest',
method: 'POST',
data: params,
})
}
// 完成调试
export const finishDebug = (params) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/testcompletion',
method: 'POST',
data: params,
})
}
import request from '../js/request'
import config from '../js/config'
// 获取设备
export function getDeviceList(params) {
return request({
url: '/cs-device-boot/EquipmentDelivery/queryEquipmentByProject',
method: 'post',
data: params,
})
}
/**
* 设备统计
* @param {*} id 工程id
* @returns
*/
export function getDevCount(id) {
return request({
url: '/cs-device-boot/deviceUser/devCount',
method: 'post',
data: { id },
})
}
// 获取直连设备模板信息
export function getModel(nDid) {
return request({
url: '/access-boot/device/model',
method: 'post',
data: {
nDid,
},
})
}
// 直连设备接入
export function addDevice(params) {
return request({
url: '/access-boot/device/access',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
// 直连设备注册
export function registerDevice(nDid, type) {
return request({
url: '/access-boot/device/register',
method: 'post',
data: {
nDid,
type,
},
})
}
// 查询拓扑图模板
export const getTopoTemplate = () => {
return request({
url: '/cs-device-boot/topologyTemplate/queryImage',
method: 'POST',
})
}
// 查询拓扑图模板监测点
export const queryByTopoId = (id) => {
return request({
url: '/cs-device-boot/lineTemplate/queryByTopoId',
method: 'POST',
data: {
topoId: id,
},
})
}
// 查询设备拓扑图
export const queryTopologyDiagram = (devId) => {
return request({
url: '/cs-device-boot/lineTopologyDiagram/queryTopologyDiagram',
method: 'POST',
data: {
devId,
},
})
}
// 设备扫码移交
export const transferDevice = (id, userId) => {
return request({
url: '/cs-device-boot/deviceUser/transfer',
method: 'POST',
data: {
ids: id,
userId: userId || uni.getStorageSync('userInfo').userIndex,
},
})
}
// 设备扫码分享
export const shareDevice = (id, userId) => {
return request({
url: '/cs-device-boot/deviceUser/share',
method: 'POST',
data: {
ids: id,
},
})
}
// 设备删除
export const deleteDevice = (id) => {
return request({
url: '/cs-device-boot/deviceUser/delete',
method: 'POST',
data: {
eid: id,
},
})
}
// 设备查询通过id获取
export const queryDeivceById = (id) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/queryEquipmentById',
method: 'POST',
data: {
ids: id,
},
})
}
// 字典树接口通过id
export const queryByid = (id) => {
return request({
url: '/system-boot/dictTree/queryByid',
method: 'post',
data: {
id,
},
})
}
//设备修改监测点信息
export const updateDevice = (params) => {
return request({
url: '/cs-device-boot/lineTopologyDiagram/auditList',
method: 'POST',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
// 设备用户列表
export const queryDeviceUser = (devId) => {
return request({
url: '/cs-device-boot/deviceUser/queryUserById',
method: 'POST',
data: {
devId,
},
})
}
// 取消分享
export const cancelShare = (params) => {
return request({
url: '/cs-device-boot/deviceUser/cancelShare',
method: 'POST',
data: params,
})
}
// 取消调试
export const cancelDebug = (params) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/deleteTest',
method: 'POST',
data: params,
})
}
// 完成调试
export const finishDebug = (params) => {
return request({
url: '/cs-device-boot/EquipmentDelivery/testcompletion',
method: 'POST',
data: params,
})
}
// 置顶设备、工程
export const engineeringPinToTop = (params) => {
return request({
url: '/cs-device-boot/csUserPins/engineeringPinToTop',
method: 'POST',
data: params,
header: {
'Content-Type': 'application/json',
},
})
}
// 查询工程树
export const lineTree = (params) => {
return request({
url: '/cs-device-boot/csLedger/lineTree',
method: 'POST',
data: {
type:'engineering'
},
// header: {
// 'Content-Type': 'application/json',
// },
})
}

View File

@@ -1,10 +1,16 @@
import request from '../js/request'
// apf-》获取模块状态
export function getModuleState(params) {
return request({
url: '/cs-harmonic-boot/data/getModuleState',
method: 'post',
data: params,
})
import request from '../js/request'
// apf-》获取模块状态
export function getModuleState(params) {
return request({
url: '/cs-harmonic-boot/data/getModuleState',
method: 'post',
data: params,
})
}
export function getBaseRealData(id) {
return request({
url: 'cs-harmonic-boot/realData/getBaseRealData?lineId='+id,
method: 'post',
})
}

View File

@@ -1,17 +1,45 @@
import request from '../js/request';
import config from '../js/config';
/**
* 已读暂态
* @returns {*}
*/
export function updateStatus(params) {
return request({
url: '/cs-harmonic-boot/eventUser/updateStatus',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
import request from '../js/request';
import config from '../js/config';
/**
* 已读暂态
* @returns {*}
*/
export function updateStatus(params) {
return request({
url: '/cs-harmonic-boot/eventUser/updateStatus',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
/**
* 稳态详情
* @returns {*}
*/
export function queryHarmonicDetail(params) {
return request({
url: '/cs-harmonic-boot/csHarmonic/queryHarmonicDetail',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}
/**
* 运行告警事件详
* @returns {*}
*/
export function queryAlarmDetail(params) {
return request({
url: '/cs-harmonic-boot/csAlarm/queryAlarmDetail',
method: 'post',
header: {
'Content-Type': 'application/json',
},
data: params,
})
}

View File

@@ -1,287 +1,290 @@
page {
background: #f3f4f5;
}
// mt0,mr0,mb0,ml0 --> mt100,mr100,mb100,ml100
@for $i from 0 through 100 {
.mt#{$i} {
margin-top: #{$i}rpx;
}
.mr#{$i} {
margin-right: #{$i}rpx;
}
.mb#{$i} {
margin-bottom: #{$i}rpx;
}
.ml#{$i} {
margin-left: #{$i}rpx;
}
.pt#{$i} {
padding-top: #{$i}rpx;
}
.pr#{$i} {
padding-right: #{$i}rpx;
}
.pb#{$i} {
padding-bottom: #{$i}rpx;
}
.pl#{$i} {
padding-left: #{$i}rpx;
}
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
.space-between {
display: flex;
justify-content: space-between;
}
.hide-txt {
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: 1;
}
.clamp-txt {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
word-break: break-all;
}
.fixed-btn {
position: fixed;
right: 80rpx;
bottom: 200rpx;
width: 126rpx;
height: 126rpx;
background: $uni-theme-white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
image {
height: 66rpx;
width: 66rpx;
}
}
.grid-card {
border-radius: 12rpx;
margin-bottom: 20rpx;
.grid-card-title {
padding: 0 0 20rpx;
font-size: 28rpx;
color: #111;
font-weight: 700;
}
.grid-card-content-4,
.grid-card-content-2,
.grid-card-content-1,
.grid-card-content-3,
.grid-card-content-5,
.grid-card-content-6 {
display: grid;
border-left: 2rpx solid #ccc;
border-top: 2rpx solid #ccc;
font-size: 24rpx;
grid-template-columns: 1fr 1fr 1fr 1fr;
.item {
padding: 0 4rpx;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
border-bottom: 2rpx solid #ccc;
border-right: 2rpx solid #ccc;
padding: 4rpx;
background: $uni-theme-white;
}
.item-title {
background: unset;
padding: 8rpx 4rpx;
}
}
.grid-card-content-1 {
grid-template-columns: 1fr;
}
.grid-card-content-2 {
grid-template-columns: 1fr 2fr;
}
.grid-card-content-3 {
grid-template-columns: 1fr 2fr 2fr;
}
.grid-card-content-5 {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
.grid-card-content-6 {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}
}
.index {
.header {
margin: 0 20rpx;
display: grid;
grid-gap: 20rpx;
grid-template-columns: 1fr 1fr 1fr ;
.header-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10rpx 10rpx 20rpx;
color: #fff;
font-size: 28rpx;
background: $uni-theme-color;
border-radius: 12rpx;
.header-item-value {
font-size: 44rpx;
}
.header-item-label {
font-size: 24rpx;
}
}
}
}
.nav {
position: sticky;
top: 0;
left: 0;
padding: 20rpx 20rpx 0;
padding-left: 0;
display: flex;
flex-wrap: wrap;
background: rgb(243, 244, 245);
z-index: 2;
.nav-menu {
padding: 6rpx 20rpx;
margin-left: 20rpx;
margin-bottom: 20rpx;
font-size: 24rpx;
border-radius: 8rpx;
background: #ebeaec;
color: #666;
&-active {
background: #dfe5f7;
color: $uni-theme-color;
}
&-btn {
background: $uni-theme-color;
color: #fff;
}
}
}
.btn {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
background: $uni-theme-color;
color: #fff;
height: 80rpx;
border-radius: 12rpx;
}
.btn-small {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 40rpx;
background: $uni-theme-color;
color: #fff;
height: 60rpx;
font-size: 24rpx;
border-radius: 12rpx;
}
.device {
/deep/ .uni-card:first-of-type {
margin-top: 0 !important;
}
}
.content {
/deep/ .uni-forms-item:last-of-type {
margin-bottom: 0 !important;
}
position: relative;
}
.status-point-success {
display: inline-block;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: greenyellow;
overflow: hidden;
}
.status-point-error {
display: inline-block;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: red;
overflow: hidden;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
height: 80rpx;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
border-bottom: 1px solid #eee;
.popup-header-title {
font-size: 32rpx;
color: #333;
}
.popup-header-close {
font-size: 32rpx;
color: #666;
}
}
image {
will-change: transform;//解决加载时瞬间拉伸问题
width: auto;//解决加载时瞬间拉伸问题
height: auto;//解决加载时瞬间拉伸问题
image-rendering:-moz-crisp-edges;
image-rendering:-o-crisp-edges;
image-rendering:-webkit-optimize-contrast;
image-rendering: crisp-edges;
-ms-interpolation-mode:nearest-neighbor;
}
page {
background: #f3f4f5;
}
// mt0,mr0,mb0,ml0 --> mt100,mr100,mb100,ml100
@for $i from 0 through 100 {
.mt#{$i} {
margin-top: #{$i}rpx;
}
.mr#{$i} {
margin-right: #{$i}rpx;
}
.mb#{$i} {
margin-bottom: #{$i}rpx;
}
.ml#{$i} {
margin-left: #{$i}rpx;
}
.pt#{$i} {
padding-top: #{$i}rpx;
}
.pr#{$i} {
padding-right: #{$i}rpx;
}
.pb#{$i} {
padding-bottom: #{$i}rpx;
}
.pl#{$i} {
padding-left: #{$i}rpx;
}
.pd#{$i} {
padding: #{$i}rpx;
}
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
.space-between {
display: flex;
justify-content: space-between;
}
.hide-txt {
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: 1;
}
.clamp-txt {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
word-break: break-all;
}
.fixed-btn {
position: fixed;
right: 80rpx;
bottom: 200rpx;
width: 126rpx;
height: 126rpx;
background: $uni-theme-white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
image {
height: 66rpx;
width: 66rpx;
}
}
.grid-card {
border-radius: 12rpx;
margin-bottom: 20rpx;
.grid-card-title {
padding: 0 0 20rpx;
font-size: 28rpx;
color: #111;
font-weight: 700;
}
.grid-card-content-4,
.grid-card-content-2,
.grid-card-content-1,
.grid-card-content-3,
.grid-card-content-5,
.grid-card-content-6 {
display: grid;
border-left: 2rpx solid #ccc;
border-top: 2rpx solid #ccc;
font-size: 24rpx;
grid-template-columns: 1fr 1fr 1fr 1fr;
.item {
padding: 0 4rpx;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
border-bottom: 2rpx solid #ccc;
border-right: 2rpx solid #ccc;
padding: 4rpx;
background: $uni-theme-white;
}
.item-title {
background: unset;
padding: 8rpx 4rpx;
}
}
.grid-card-content-1 {
grid-template-columns: 1fr;
}
.grid-card-content-2 {
grid-template-columns: 1fr 2fr;
}
.grid-card-content-3 {
grid-template-columns: 1fr 2fr 2fr;
}
.grid-card-content-5 {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
.grid-card-content-6 {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}
}
.index {
.header {
margin: 0 20rpx;
display: grid;
grid-gap: 20rpx;
grid-template-columns: 1fr 1fr 1fr ;
.header-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10rpx 10rpx 20rpx;
color: #fff;
font-size: 28rpx;
background: $uni-theme-color;
border-radius: 12rpx;
.header-item-value {
font-size: 44rpx;
}
.header-item-label {
font-size: 24rpx;
}
}
}
}
.nav {
position: sticky;
top: 0;
left: 0;
padding: 20rpx 20rpx 0;
padding-left: 0;
display: flex;
flex-wrap: wrap;
background: rgb(243, 244, 245);
z-index: 2;
.nav-menu {
padding: 6rpx 20rpx;
margin-left: 20rpx;
margin-bottom: 20rpx;
font-size: 24rpx;
border-radius: 8rpx;
background: #ebeaec;
color: #666;
&-active {
background: #dfe5f7;
color: $uni-theme-color;
}
&-btn {
background: $uni-theme-color;
color: #fff;
}
}
}
.btn {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
background: $uni-theme-color;
color: #fff;
height: 80rpx;
border-radius: 12rpx;
}
.btn-small {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 40rpx;
background: $uni-theme-color;
color: #fff;
height: 60rpx;
font-size: 24rpx;
border-radius: 12rpx;
}
.device {
/deep/ .uni-card:first-of-type {
margin-top: 0 !important;
}
}
.content {
/deep/ .uni-forms-item:last-of-type {
margin-bottom: 0 !important;
}
position: relative;
}
.status-point-success {
display: inline-block;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: greenyellow;
overflow: hidden;
}
.status-point-error {
display: inline-block;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: red;
overflow: hidden;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
height: 80rpx;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
border-bottom: 1px solid #eee;
.popup-header-title {
font-size: 32rpx;
color: #333;
}
.popup-header-close {
font-size: 32rpx;
color: #666;
}
}
image {
will-change: transform;//解决加载时瞬间拉伸问题
width: auto;//解决加载时瞬间拉伸问题
height: auto;//解决加载时瞬间拉伸问题
image-rendering:-moz-crisp-edges;
image-rendering:-o-crisp-edges;
image-rendering:-webkit-optimize-contrast;
image-rendering: crisp-edges;
-ms-interpolation-mode:nearest-neighbor;
}

View File

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

View File

@@ -1,92 +1,98 @@
export default {
onPullDownRefresh() {
this.store && this.store.reload()
},
onReachBottom() {
if (this.store.status != 'noMore') {
this.store.next && this.store.next()
}
},
data() {
return {
store: {},
}
},
methods: {
DataSource(url) {
var me = this
return {
data: [],
status: 'noMore',
empty: false,
total: 0,
header: {
'Content-Type': 'application/json;charset=UTF-8',
},
params: {
pageNum: 1,
pageSize: 20,
},
timer: null,
callBack: null,
firstCallBack: null,
loadedCallback: null,
reload() {
if (this.status == 'loading') return
this.data = []
this.empty = false
this.params.pageNum = 1
this.next()
},
search() {
// 节流搜索
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.reload()
}, 300)
},
next() {
this.status = 'loading'
me.$request({
url: url,
data: this.params,
header: this.header,
method: 'POST',
debounce: false,
}).then((res) => {
console.warn(res)
let resultData = res.data?.list || res.data?.records || res.data || []
if (this.params.pageNum == 1) {
this.data = resultData
if (resultData.length == 0 || resultData == 0) {
this.empty = true
this.status = 'noMore'
} else if (resultData.length < this.params.pageSize) {
this.status = 'noMore'
} else if (res.total == resultData.length) {
this.status = 'noMore'
} else {
this.status = 'more'
}
} else {
this.data.push(...resultData)
if (resultData.length < this.params.pageSize) {
this.status = 'noMore'
} else {
this.status = 'more'
}
}
if (this.params.pageNum == 1) {
this.firstCallBack && this.firstCallBack()
}
this.loadedCallback && this.loadedCallback()
this.params.pageNum++
this.total = res.total
this.loading = false
uni.stopPullDownRefresh()
})
},
}
},
},
}
export default {
onPullDownRefresh() {
if (this.store.isListAtTop) {
this.store && this.store.reload()
}
},
onReachBottom() {
if (this.store.status != 'noMore') {
this.store.next && this.store.next()
}
},
data() {
return {
store: {},
}
},
methods: {
DataSource(url) {
var me = this
return {
data: [],
copyData: {},
status: 'noMore',
empty: false,
isListAtTop: true,
total: 0,
header: {
'Content-Type': 'application/json;charset=UTF-8',
},
params: {
pageNum: 1,
pageSize: 20,
},
timer: null,
callBack: null,
firstCallBack: null,
loadedCallback: null,
reload() {
if (this.status == 'loading') return
this.data = []
this.empty = false
this.params.pageNum = 1
this.next()
},
search() {
// 节流搜索
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.reload()
}, 300)
},
next() {
this.status = 'loading'
me.$request({
url: url,
data: this.params,
header: this.header,
method: 'POST',
debounce: false,
}).then((res) => {
console.warn(res)
let resultData = res.data?.list || res.data?.records || res.data || []
this.copyData = res.data
if (this.params.pageNum == 1) {
this.data = resultData
if (resultData.length == 0 || resultData == 0) {
this.empty = true
this.status = 'noMore'
} else if (resultData.length < this.params.pageSize) {
this.status = 'noMore'
} else if (res.total == resultData.length) {
this.status = 'noMore'
} else {
this.status = 'more'
}
} else {
this.data.push(...resultData)
if (resultData.length < this.params.pageSize) {
this.status = 'noMore'
} else {
this.status = 'more'
}
}
if (this.params.pageNum == 1) {
this.firstCallBack && this.firstCallBack()
}
this.loadedCallback && this.loadedCallback()
this.params.pageNum++
this.total = res.total || res.data?.total
this.loading = false
uni.stopPullDownRefresh()
})
},
}
},
},
}

View File

@@ -1,5 +1,5 @@
export const MQTT_IP = 'pqmcn.com:8085/mqtt'//mqtt地址端口, 使用emqx时一定要加mqtt
// export const MQTT_IP = '192.168.1.24:8085/mqtt'//mqtt地址端口, 使用emqx时一定要加mqtt
// export const MQTT_IP = 'pqmcn.com:8085/mqtt'//mqtt地址端口, 使用emqx时一定要加mqtt
export const MQTT_IP = '192.168.1.103:8083/mqtt'//mqtt地址端口, 使用emqx时一定要加mqtt
const MQTT_USERNAME = 't_user'//mqtt用户名
const MQTT_PASSWORD = 'njcnpqs'//密码

View File

@@ -39,7 +39,7 @@ export default (options = {}) => {
},
method: options.method || 'GET',
success: async (res) => {
console.log(res)
// console.log(res)
if (arr.indexOf(options.url) > -1) {
setTimeout(() => {
arr.splice(arr.indexOf(options.url), 1)

View File

@@ -107,39 +107,61 @@ function formatTime(time, option) {
}
// 获取当天日期(年月日)
function getToday() {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0'); // 月份从0开始需+1
const day = String(today.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0') // 月份从0开始需+1
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// 获取当天日期 往前推30天
function getBeforeDays(days = 30) {
const today = new Date();
// 计算往前推N天的时间戳1天=86400000毫秒
const beforeDate = new Date(today.getTime() - days * 24 * 60 * 60 * 1000);
return formatDate(beforeDate);
const today = new Date()
// 计算往前推N天的时间戳1天=86400000毫秒
const beforeDate = new Date(today.getTime() - days * 24 * 60 * 60 * 1000)
return formatDate(beforeDate)
}
function formatDate(date) {
const year = date.getFullYear();
// 月份从0开始补零到2位
const month = String(date.getMonth() + 1).padStart(2, '0');
// 日期补零到2位
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
const year = date.getFullYear()
// 月份从0开始补零到2位
const month = String(date.getMonth() + 1).padStart(2, '0')
// 日期补零到2位
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// 获取3个月前的日期
function getThreeMonthsAgo() {
const threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); // 月份减3
const year = threeMonthsAgo.getFullYear();
const month = String(threeMonthsAgo.getMonth() + 1).padStart(2, '0');
const day = String(threeMonthsAgo.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
const threeMonthsAgo = new Date()
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3) // 月份减3
const year = threeMonthsAgo.getFullYear()
const month = String(threeMonthsAgo.getMonth() + 1).padStart(2, '0')
const day = String(threeMonthsAgo.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
//获取月最后一天
function getMonthFirstAndLastDay(monthStr) {
// 1. 校验输入格式正则匹配YYYY-MM
const reg = /^\d{4}-\d{2}$/
if (!reg.test(monthStr)) {
throw new Error('输入格式错误,请传入"YYYY-MM"格式的字符串例如2026-03')
}
// 2. 拆分年、月
const [year, month] = monthStr.split('-').map(Number)
// 3. 生成当月第一天直接拼接01
const firstDay = `${year}-${String(month).padStart(2, '0')}-01`
// 4. 生成当月最后一天核心利用Date的特性下个月0号 = 当月最后一天)
// 注意月份是0开始的0=1月11=12月所以month+1是下一个月
const lastDayDate = new Date(year, month, 0) // 下个月0号 = 当月最后一天
const lastDay = `${year}-${String(month).padStart(2, '0')}-${String(lastDayDate.getDate()).padStart(2, '0')}`
return {
firstDay,
lastDay,
}
}
const h5Helper = {
isAndroid: function () {
@@ -167,7 +189,7 @@ const getUserLocation = (call) => {
success: function (address) {
call(address)
},
fail: (err) => {
fail: (err) => {
uni.showModal({
title: '提示',
content: '定位失败,请打开定位权限',
@@ -309,12 +331,14 @@ const getDictData = (key) => {
resolve(dictData.filter((item) => item.code === key)[0]?.children || [])
} else {
// 查询字典
queryDictDataCache().then((res) => {
uni.setStorageSync(cacheKey.dictData, res.data)
resolve(res.data.filter((item) => item.code === key)[0]?.children || [])
}).catch(err=>{
reject(err)
})
queryDictDataCache()
.then((res) => {
uni.setStorageSync(cacheKey.dictData, res.data)
resolve(res.data.filter((item) => item.code === key)[0]?.children || [])
})
.catch((err) => {
reject(err)
})
}
})
}
@@ -333,5 +357,6 @@ export default {
getDictData,
getToday,
getBeforeDays,
getThreeMonthsAgo
getThreeMonthsAgo,
getMonthFirstAndLastDay,
}

View File

@@ -1,80 +1,235 @@
<template>
<uni-card
:title="device.equipmentName"
:sub-title="device.mac"
:extra="device.isPrimaryUser == 1 ? '主设备' : '分享设备'"
padding="0"
@click="jump(device)"
:thumbnail="deviceIcon(device.runStatus)"
>
<template v-slot:title>
<slot name="title"></slot>
</template>
<view class="device-body">
<view class="device-body-item">
<text>工程名称</text>
<text>{{ device.engineeringName }}</text>
</view>
<view class="device-body-item mt6">
<text>项目名称</text>
<text>{{ device.projectName }}</text>
</view>
<view class="device-body-item mt6" v-if="device.process == 2 || device.process == 3">
<text>调试阶段</text>
<text>{{ device.process == 2 ? '功能调试' : '出厂调试' }}</text>
</view>
</view>
</uni-card>
</template>
<script>
export default {
data() {
return {}
},
props: {
device: {
type: Object,
default: () => {},
},
},
methods: {
deviceIcon(e) {
let str = ''
switch (e) {
case 1:
str = '/static/device_bad.png'
break
case 2:
str = '/static/device_success.png'
break
default:
str = '/static/device_success.png'
break
}
return str
},
jump() {
console.log(12321,this.device);
uni.navigateTo({
url:
'/pages/device/APF/detail?id=' +
this.device.equipmentId +
'&isPrimaryUser=' +
this.device.isPrimaryUser +
'&process=' +
this.device.process + '&ndid=' + this.device.ndid,
})
},
},
}
</script>
<style lang="scss">
.device-body {
padding: 20rpx;
.device-body-item {
display: flex;
justify-content: space-between;
}
}
</style>
<template>
<uni-card
:title="device.equipmentName"
:sub-title="device.mac"
:extra="device.isPrimaryUser == 1 ? '主设备' : '分享设备'"
padding="0"
@click="jump(device)"
:thumbnail="deviceIcon(device.runStatus)"
>
<template v-slot:title>
<!-- 卡片标题 -->
<view class="uni-card__header" @click="jump(device)">
<view class="uni-card__header-box">
<view class="uni-card__header-avatar">
<view class="event-icon">
<!-- 动态图标根据类型切换 -->
<uni-icons
custom-prefix="iconfont"
:type="device.devType == 'Direct_Connected_Device' ? 'icon-shebei2' : 'icon-shebei1'"
:color="device.runStatus == 1 ? '#ff3b30' : '#67c23a'"
size="45"
></uni-icons>
</view>
</view>
<view class="uni-card__header-content">
<text class="uni-card__header-content-title uni-ellipsis">
{{ device.equipmentName }}
</text>
<text class="uni-card__header-content-subtitle uni-ellipsis">
{{ device.mac }}
</text>
<view class="tagBox">
<text class="event-tag" :class="device.runStatus == 1 ? 'lx-tag' : 'zx-tag'">{{
device.runStatus == 1 ? '离线' : '在线'
}}</text>
<text
class="event-tag"
:class="device.devType == 'Direct_Connected_Device' ? 'zl-tag' : 'jc-tag'"
>
{{ device.devType == 'Direct_Connected_Device' ? '治理设备' : '监测设备' }}
</text>
<text class="event-tag" :class="device.isPrimaryUser == 1 ? 'z-tag' : 'fx-tag'">{{
device.isPrimaryUser == 1 ? '主设备' : '分享设备'
}}</text></view
>
</view>
</view>
<view class="uni-card__header-extra" style="position: relative" @click.stop>
<text class="uni-card__header-extra-text"></text>
<slot name="title"></slot>
</view>
</view>
<!-- <slot name="title"></slot> -->
</template>
<view class="device-body">
<view class="device-body-item">
<text>工程名称</text>
<text>{{ device.engineeringName }}</text>
</view>
<view class="device-body-item mt6">
<text>项目名称</text>
<text>{{ device.projectName }}</text>
</view>
<view class="device-body-item mt6" v-if="device.process == 2 || device.process == 3">
<text>调试阶段</text>
<text>{{ device.process == 2 ? '功能调试' : '出厂调试' }}</text>
</view>
</view>
</uni-card>
</template>
<script>
export default {
data() {
return {}
},
props: {
device: {
type: Object,
default: () => {},
},
},
methods: {
deviceIcon(e) {
let str = ''
switch (e) {
case 1:
str = '/static/device_bad.png'
break
case 2:
str = '/static/device_success.png'
break
default:
str = '/static/device_success.png'
break
}
return str
},
jump() {
if (this.device.devType == 'Direct_Connected_Device') {
uni.navigateTo({
url:
'/pages/device/APF/detail?id=' +
this.device.equipmentId +
'&isPrimaryUser=' +
this.device.isPrimaryUser +
'&process=' +
this.device.process +
'&ndid=' +
this.device.ndid,
})
} else {
if (this.device.lineList.length == 0) {
return this.$util.toast('暂无监测点!')
}
uni.navigateTo({
url:
'/pages/device/realTime/index?id=' +
this.device.equipmentId +
'&isPrimaryUser=' +
this.device.isPrimaryUser +
'&process=' +
this.device.process +
'&ndid=' +
this.device.ndid +
'&lineList=' +
JSON.stringify(this.device.lineList) +
'&engineeringName=' +
this.device.engineeringName +
'&equipmentName=' +
this.device.equipmentName +
'&runStatus=' +
this.device.runStatus,
})
}
},
},
}
</script>
<style lang="scss">
.device-body {
padding: 20rpx;
.device-body-item {
display: flex;
justify-content: space-between;
}
}
.uni-card {
/deep/ .uni-card__header-box {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
overflow: hidden;
}
/deep/ .uni-card__header {
display: flex;
border-bottom: 2rpx #ebeef5 solid;
flex-direction: row;
align-items: center;
padding: 20rpx;
overflow: hidden;
}
}
.uni-card .uni-card__header .uni-card__header-content .uni-card__header-content-title {
font-size: 15px;
color: #3a3a3a;
}
.uni-card .uni-card__header .uni-card__header-content .uni-card__header-content-subtitle {
font-size: 12px;
// margin-top: 5px;
color: #909399;
}
.uni-card .uni-card__header .uni-card__header-avatar .uni-card__header-avatar-image {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 5px;
margin-right: 10px;
}
.uni-card .uni-card__header .uni-card__header-content {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
overflow: hidden;
}
.tagBox {
display: flex;
gap: 15rpx;
}
.event-icon {
position: relative;
// width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
.event-tag {
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 8rpx;
margin-top: 5rpx;
// height: 38rpx;
}
// 在线
.zx-tag {
background-color: #67c23a20;
color: #67c23a;
}
.lx-tag {
background-color: #ff3b3020;
color: #ff3b30;
}
.z-tag {
background-color: #2563eb20;
color: #2563eb;
}
.fx-tag {
background-color: #90939920;
color: #909399;
}
.zl-tag {
background-color: #007aff20;
color: #007aff;
}
.jc-tag {
background-color: #36cfc920;
color: #36cfc9;
}
</style>

View File

@@ -0,0 +1,233 @@
<template>
<view class="nav choose">
<view class="nav-menu" :class="{ 'nav-menu-active': select.engineeringName }" @click="selectEngineering"
>{{
// select.engineeringName + '>' + select.projectName + '>' + select.deviceName + '>' + select.lineName ||
// '全部工程'
select.engineeringName || select.projectName || select.deviceName || select.lineName
? [select.engineeringName, select.projectName, select.deviceName, select.lineName]
.filter((item) => item && item !== '')
.join('>')
: '全部工程'
}}
<uni-icons type="bottom" size="14" :color="select.engineeringName ? '#376cf3' : '#666'"></uni-icons>
</view>
<!-- 弹框组件 -->
<Cn-qianTree
ref="qiantree"
:selectParent="true"
:multiple="false"
:range="list"
:foldAll="true"
@confirm="treeConfirm"
@cancel="treeCancel"
></Cn-qianTree>
<picker
mode="date"
:value="select.date"
:start="startDate"
fields="month"
:end="endDate"
@change="bindDateChange"
v-if="showDatetime"
>
<view class="nav-menu"
>{{ select.date }}
<uni-icons type="bottom" size="14"></uni-icons>
</view>
</picker>
<uni-datetime-picker v-if="!showDatetime" v-model="select.range" type="daterange" :end="endDate">
<view class="nav-menu"
>{{ select.range[0] + '至' + select.range[1] }}
<uni-icons type="bottom" size="14"></uni-icons>
</view>
</uni-datetime-picker>
<slot />
</view>
</template>
<script>
import { lineTree } from '@/common/api/device'
export default {
components: {},
props: {
level: { type: Number, default: 3 },
showDatetime: { type: Boolean, default: true },
},
data() {
const currentDate = this.getDate({
format: true,
})
return {
select: {
engineeringName: '',
engineeringId: '', //工程ID
projectName: '',
projectId: '', //項目ID
deviceName: '',
deviceId: '', //设备ID
lineName: '',
lineId: '', //测点ID
date: currentDate,
range: ['', ''],
},
list: [],
}
},
created() {
if (!this.showDatetime) {
this.select.range = [this.endDate.slice(0, -3) + '-01', this.endDate]
}
},
onShow() {},
mounted() {},
methods: {
getTree() {
this.clear()
lineTree().then((res) => {
let list = {
id: '',
pid: '0',
pids: '0',
name: '全部项目',
path: null,
provinceId: null,
cityId: null,
area: null,
remark: null,
sort: 0,
level: 0,
comFlag: null,
type: null,
lineType: null,
conType: null,
process: null,
isTop: 0,
children: [],
ndid: null,
}
this.list = this.filterTreeByLevel([list, ...res.data])
})
},
// 递归过滤函数去除level > 2的节点
filterTreeByLevel(tree) {
// 遍历每一个节点
return tree.map((node) => {
// 复制当前节点(避免修改原数据)
const newNode = { ...node }
// 如果当前节点有子节点并且当前节点的level <= 2因为level=2的节点的子节点是level=3需要过滤
if (newNode.children && newNode.children.length > 0) {
// 递归过滤子节点只保留子节点中level <= 2的
newNode.children = this.filterTreeByLevel(
newNode.children.filter((child) => child.level <= this.level),
)
}
return newNode
})
},
getDate(type) {
const date = new Date()
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
if (type === 'start') {
year = year - 10
} else if (type === 'end') {
year = year
}
month = month > 9 ? month : '0' + month
day = day > 9 ? day : '0' + day
return this.showDatetime ? `${year}-${month}` : `${year}-${month}-${day}`
},
bindDateChange(e) {
this.select.date = e.detail.value
},
selectEngineering() {
this.$refs.qiantree._show()
},
getProjectList() {},
// 确定回调事件
treeConfirm(e) {
this.clear()
this.setSelect(e[0].rank, e[0].name, e[0].id)
e[0].parents.forEach((item) => {
this.setSelect(item.rank, item.name, item.id)
})
},
// 清空
clear() {
this.select.engineeringName = ''
this.select.engineeringId = '' //工程ID
this.select.projectName = ''
this.select.projectId = '' //項目ID
this.select.deviceName = ''
this.select.deviceId = '' //设备ID
this.select.lineName = ''
this.select.lineId = '' //测点ID
},
setSelect(rank, name, id) {
switch (rank) {
case 0:
this.select.engineeringId = id
this.select.engineeringName = name
break
case 1:
this.select.projectId = id
this.select.projectName = name
break
case 2:
this.select.deviceId = id
this.select.deviceName = name
break
case 3:
this.select.lineId = id
this.select.lineName = name
break
}
},
// 取消回调事件
treeCancel(e) {
console.log(e)
},
},
computed: {
startDate() {
return this.getDate('start')
},
endDate() {
return this.getDate('end')
},
},
watch: {
select: {
handler(val, oldVal) {
if (this.loading) return
this.$emit('select', val)
},
deep: true,
immediate: true,
},
level: {
handler(val, oldVal) {
this.getTree()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped>
/deep/ .uni-date-editor {
width: 360rpx;
}
</style>

View File

@@ -1,63 +1,63 @@
<template>
<view class="Cn-page" :class="{ 'no-padding': noPadding }">
<template v-if="showLoginMsk && !login">
<navigator url="/pages/user/login" hover-class="none" class="page-login-btn">登录</navigator>
</template>
<template v-else>
<zero-loading v-if="loading" mask></zero-loading>
<slot name="body" v-if="beforeRender|| !loading "></slot>
</template>
</view>
</template>
<script>
export default {
name: 'Cn-page',
props: {
loading: {
type: Boolean,
default: false,
},
beforeRender: {
type: Boolean,
default: false,
},
showLoginMsk: Boolean,
noPadding: Boolean,
},
data() {
return {
login: false,
}
},
created() {
this.login = uni.getStorageSync('Authorization')
},
}
</script>
<style lang="scss">
.Cn-page {
box-sizing: border-box;
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
}
.no-padding {
padding-bottom: 0;
}
.page-login-btn {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 80rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background: #449dff;
color: #fff;
border-radius: 4rpx;
}
</style>
<template>
<view class="Cn-page" :class="{ 'no-padding': noPadding }">
<template v-if="showLoginMsk && !login">
<navigator url="/pages/user/login" hover-class="none" class="page-login-btn">登录</navigator>
</template>
<template v-else>
<zero-loading v-if="loading" mask></zero-loading>
<slot name="body" v-if="beforeRender|| !loading "></slot>
</template>
</view>
</template>
<script>
export default {
name: 'Cn-page',
props: {
loading: {
type: Boolean,
default: false,
},
beforeRender: {
type: Boolean,
default: false,
},
showLoginMsk: Boolean,
noPadding: Boolean,
},
data() {
return {
login: false,
}
},
created() {
this.login = uni.getStorageSync('Authorization')
},
}
</script>
<style lang="scss">
.Cn-page {
box-sizing: border-box;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.no-padding {
padding-bottom: 0;
}
.page-login-btn {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 80rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background: #449dff;
color: #fff;
border-radius: 4rpx;
}
</style>

View File

@@ -0,0 +1,532 @@
<template xlang="wxml">
<view class="tki-tree">
<view class="tki-tree-mask" :class="{ show: showTree }" @tap="_cancel"></view>
<view class="tki-tree-cnt" :class="{ show: showTree }">
<view class="tki-tree-bar">
<view class="tki-tree-bar-cancel" :style="{ color: cancelColor }" hover-class="hover-c" @tap="_cancel"
>取消</view
>
<view class="tki-tree-bar-title" :style="{ color: titleColor }">{{ title }}</view>
<view
class="tki-tree-bar-confirm"
:style="{ color: confirmColor }"
hover-class="hover-c"
@tap="_confirm"
>确定</view
>
</view>
<view class="tki-tree-bar1">
<uni-search-bar
class="uni-input"
radius="5"
placeholder="请输入关键字搜索"
clearButton="auto"
cancelButton="none"
@input="input"
/>
</view>
<view class="tki-tree-view">
<scroll-view class="tki-tree-view-sc" :scroll-y="true">
<block v-for="(item, index) in treeList" :key="index">
<view
class="tki-tree-item"
:style="[
{
paddingLeft: item.rank * 15 + 'px',
zIndex: item.rank * -1 + 50,
},
]"
:class="{
border: border === true,
show: item.show,
last: item.lastRank,
showchild: item.showChild,
open: item.open,
}"
>
<view class="tki-tree-label" @tap.stop="_treeItemTap(item, index)">
<image
class="tki-tree-icon"
:src="item.lastRank ? lastIcon : item.showChild ? currentIcon : defaultIcon"
></image>
{{ item.name }}
</view>
<view
class="tki-tree-check"
@tap.stop="_treeItemSelect(item, index)"
v-if="selectParent ? true : item.lastRank"
>
<view
class="tki-tree-check-yes"
v-if="item.checked"
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
>
<view
class="tki-tree-check-yes-b"
:style="{ 'background-color': confirmColor }"
></view>
</view>
<view
class="tki-tree-check-no"
v-else
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
></view>
</view>
</view>
</block>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'tki-tree',
props: {
lazy: {
type: Boolean,
default: false,
},
range: {
type: Array,
default: function () {
return []
},
},
idKey: {
type: String,
default: 'id',
},
rangeKey: {
type: String,
default: 'name',
},
title: {
type: String,
default: '',
},
multiple: {
// 是否可以多选
type: Boolean,
default: false,
// default: true
},
selectParent: {
//是否可以选父级
type: Boolean,
default: false,
},
foldAll: {
//折叠时关闭所有已经打开的子集,再次打开时需要一级一级打开
type: Boolean,
default: false,
},
confirmColor: {
// 确定按钮颜色
type: String,
default: '#376cf3', // #07bb07
},
cancelColor: {
// 取消按钮颜色
type: String,
default: '', // #757575
},
titleColor: {
// 标题颜色
type: String,
default: '', // #757575
},
currentIcon: {
// 展开时候的ic
type: String,
default:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0QTM0MzQ1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0QTM0MzU1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRDRBMzQzMjVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRDRBMzQzMzVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PidwepsAAAK0SURBVHja7JxbTsJAFIYHww7ciStgCeoGvGxAiOsgURegoL5720AXYLiIr0aJviq3Zx3PhIEnKG3ndtr+f3KixrSUj/ZjzjClIqUUiFm2gAAQAREQEUAEREAERAQQAREQAREBREAEREBEEqa67h9RFDWllDv0awWYlqlQHmu1WjMRRMoV1QFttA12y3xRtdNczq8EsE4/f8FumX2q77ROvNXk8UGMEKdUz6tYJHljaZAbuyUH+UR1to5BEohTuqwPCeS4pAA/qY6o/kyHOAMCeRK3owJnj+rH1jjxhqpVsstaebCz6TmnHWyXyY+xHjSBWBY/bvSgadtXBj9u9KCN3rnIfkzkQVsTEEX0Y2IP2oKo/HhMICcFAThUcwVZNGU6FdbX/XURzkbVF4+ybGhjPrFdgP66QdXNurGtSdk6Xdb9nAJ8oDo3OQlsQZzkdPw41ONBo6vI5scDefRjZg+6gpg3Pxp50CXEvPjR2IOuIXL3oxUPuobI3Y9WPOgDIlc/WvOgL4iL/vqFCcD7LH0xB4hj7cfQ/fWH9qCT+FhG0tN+DBk1PzjOM0SVllixcsBT1AvYc/kAPhc0hRg/3uvxoCgKRN9+dOrBUBB9+9GpB0NC9OVH5x4MDdG1H714kANEV3705kEOEBf9dcPi/lQnsuvLg1wgSu3Ha0v7Uh4MMgUXeuG71H407a+VBy9CPQkOdw+MtB+nGbd/D+FBbhBNxo9SjwcngJjNj0E9yBFiFj8G9SBXiGn8GNyDnCEm8SMLD3KHGOdHNh7kDjHOj2w8mAeIi/5arX+c6b/fxHz9oADEdGdjR/fXCw/OOB5oVfCOgnepz8IB14PMw03jCmTE+QBx5z0gAmKSqK9OUF+hcAeIhu/QYr4Qie8rjW83hhMBERARQAREQAREBBABERCLnH8BBgA+TQI7U4t53AAAAABJRU5ErkJggg==',
},
defaultIcon: {
// 折叠时候的ic
type: String,
default:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAACE0lEQVR4Xu3c200DMRCF4XEltJAOkEugA+ggpUAHoQMqiFMCdEAJUMEiS4mEELlIO7bPOeN9i6K1rG/952myyea1WiCtXmEuYBPR4RBMxInoIOCwhOtJLKVszWyXc/5y2BvNEq6I+/3+kFK6M7OHnPM7jcLKjbZAvD/uaZtzflm5P4rbWyJWgDcze1LPuzVihfxUz7sH4ilJ2bx7Isrm3RtRMu8RiHJ5j0SUyXs0okTeCIj0eSMh0uaNhkiZNyIiXd7IiDR5oyNS5M2ACJ83EyJs3myIkHkzIsLlzYwIkzc7IkTeCojD81ZCHJa3GuKQvBURu+etjNgtb3XELnlHQGyedyTEZnlHQ2ySd0RE97wjI7rlHR3RJe+JeIrbLOecD6ePpZQ6W1kn2epo4MUrPOKyLN8ppYq1+y1VStncOjIdGnFZlo+U0uOtWOeOY2TE12Ouq//pEA7xXL7XfvcufR8K0Svfv6CREN3yDYfYIt9QiK3yjYTYLF95xB75SiP2ylcZsVu+cogj8pVCHJWvEuKwfOkREfKlRkTJlxkRJl86RMR8qRBR82VChM0XHpEhX2hElnyREWnyhUNkzBcKkTVfJETafIcjKuQ7FFEl35GIMvl2R1TMtyuiar49EWXzbY5oZpv/hibXTF2h3+s60FRKeT6+3TjMS3nrA3ZFRD8xrfY3ER1kJ+JEdBBwWGKeRAfEH1wS5WFZSDB/AAAAAElFTkSuQmCC',
},
lastIcon: {
// 没有子集的ic
type: String,
default: '',
},
border: {
// 是否有分割线
type: Boolean,
default: false,
},
},
data() {
return {
showTree: false,
treeList: [],
selectIndex: -1,
returnedItem: [], //定义一个空数组
pids: [],
ancestorsIds: [],
childNums: [],
dataTree: [],
inputTimer: null, // 节流定时器标识
searchValue: '',
}
},
computed: {},
methods: {
_show() {
this.showTree = true
if (this.searchValue != '') {
this.searchValue = ''
this.input('')
}
},
_hide() {
this.showTree = false
},
_cancel() {
this._hide()
this.$emit('cancel', '')
},
_confirm() {
// 处理所选数据
let rt = [],
obj = {}
this.treeList.forEach((v, i) => {
if (this.treeList[i].checked) {
// rt.push(this.treeList[i].id)
rt.push(this.treeList[i])
}
})
this._hide()
this.$emit('confirm', rt)
},
//扁平化树结构
_renderTreeList(list = [], rank = 0, parentId = [], parents = []) {
list.forEach((item) => {
this.treeList.push({
id: item[this.idKey],
name: item[this.rangeKey],
source: item,
parentId, // 父级id数组
parents, // 父级id数组
rank, // 层级
showChild: false, //子级是否显示
open: false, //是否打开
show: rank === 0, // 自身是否显示
hideArr: [],
orChecked: item.checked ? item.checked : false,
checked: item.checked ? item.checked : false,
childNum: 0,
})
if (Array.isArray(item.children) && item.children.length > 0) {
let parentid = [...parentId],
parentArr = [...parents]
delete parentArr.children
parentid.push(item[this.idKey])
parentArr.push({
[this.idKey]: item[this.idKey],
[this.rangeKey]: item[this.rangeKey],
rank: rank,
})
// lazy
if (!this.lazy) {
this._renderTreeList(item.children, rank + 1, parentid, parentArr)
}
} else {
this.treeList[this.treeList.length - 1].lastRank = true
}
})
},
// 处理默认选择
_defaultSelect() {
this.treeList.forEach((v, i) => {
if (v.checked) {
this.treeList.forEach((v2, i2) => {
if (v.parentId.toString().indexOf(v2.parentId.toString()) >= 0) {
v2.show = true
if (v.parentId.includes(v2.id)) {
v2.showChild = true
v2.open = true
}
}
})
}
})
},
getOwn(id, arr) {
//利用foreach循环遍历
arr.forEach((item) => {
//判断递归结束条件
if (item[this.idKey] == id) {
// 存储数据到空数组
this.returnedItem = item
} else if (item.children != null) //判断chlidren是否有数据
{
//递归调用
this.getOwn(id, item.children)
}
})
return this.returnedItem
},
setShow(id, arr, isShow) {
arr.forEach((item, index) => {
if (item.parentId.includes(id)) {
this.treeList[index].showChild = isShow
this.treeList[index].show = isShow
} else if (item.children !== undefined) {
this.setShow(id, item.children, isShow)
}
})
},
// 点击
_treeItemTap(item, index) {
// console.log(item)
if (item.lastRank === true) {
//点击最后一级时触发事件
this.treeList[index].checked = !this.treeList[index].checked
this._fixMultiple(index)
return
}
let id = item.id
item.showChild = !item.showChild
// qingqian
if (item.showChild) {
// const range = this.range
const range = this.dataTree
const parentIdArr = item.parentId
// 找到当前元素
const own = this.getOwn(id, range)
const checkedChildren = own.children
// 子元素插入的索引位置
const nextIndex = this.treeList.findIndex((itemT) => itemT.id === item.id)
console.log(checkedChildren)
if (checkedChildren === undefined || checkedChildren.length < 1) {
return
}
// 子节点数量
this.treeList[index].childNum = checkedChildren.length
const newRank = item.rank + 1
checkedChildren.forEach((itemC) => {
const childObj = {
id: itemC[this.idKey],
name: itemC[this.rangeKey],
source: {},
parentId: [item.id], // 父级id数组
parents: [item], // 父级id数组
rank: newRank, // 层级
showChild: false, //子级是否显示
open: false, //是否打开
show: 1, // 自身是否显示
hideArr: [],
orChecked: this.treeList[index].checked,
checked: this.treeList[index].checked,
}
if (!this.treeList.some((itemT) => itemT.id === itemC[this.idKey])) {
this.treeList.splice(nextIndex + 1, 0, childObj)
}
})
}
// 展开/隐藏子级/孙级
let list = this.treeList
item.open = item.showChild ? true : !item.open
list.forEach((childItem, i) => {
if (item.showChild === false) {
//隐藏所有子级
if (!childItem.parentId.includes(id)) {
return
}
//TODO: 修改
if (!this.foldAll) {
if (childItem.lastRank !== true && !childItem.open) {
childItem.showChild = false
this.setShow(childItem.id, this.treeList, false)
}
// 为隐藏的内容添加一个标记
if (childItem.show) {
childItem.hideArr[item.rank] = id
}
} else {
if (childItem.lastRank !== true) {
childItem.showChild = false
// 继续隐藏子级的的子级
this.setShow(childItem.id, this.treeList, false)
}
}
if (childItem.children !== undefined) {
childItem.children.forEach((childItem1, i1) => {
if (!childItem1.parentId.includes(childItem.id)) {
return
}
childItem.children[i1].showChild = false
childItem.children[i1].show = false
})
}
childItem.show = false
} else {
// 打开子集
if (childItem.parentId[childItem.parentId.length - 1] === id) {
childItem.show = true
}
// 打开被隐藏的子集
if (childItem.parentId.includes(id) && !this.foldAll) {
// console.log(childItem.hideArr)
if (childItem.hideArr[item.rank] === id) {
childItem.show = true
if (childItem.open && childItem.showChild) {
childItem.showChild = true
} else {
childItem.showChild = false
}
childItem.hideArr[item.rank] = null
}
}
}
})
},
// 通过父id处理子级
syncChecked(trees, pid, checked) {
trees.forEach((item, index) => {
if (item.parentId.includes(pid)) {
this.treeList[index].checked = checked
this.syncChecked(trees, item.id, checked)
} else if (item.children !== undefined) {
this.syncChecked(item.children, pid, checked)
}
})
},
// 获取父级往上所有层级的id 并同步状态
setAncestors(pids, checked) {
this.treeList.forEach((item, index) => {
if (pids.includes(item.id)) {
if (checked && this.childNums[item.id] !== undefined && item.childNum === this.childNums[item.id]) {
// 子级全部选中, 父级才选中
this.treeList[index].checked = true
} else {
this.treeList[index].checked = false
}
this.setAncestors(item.parentId, checked)
}
})
},
_treeItemSelect(item, index) {
this.treeList[index].checked = !this.treeList[index].checked
// 选父级, 子级自动全选
this.syncChecked(this.treeList, item.id, this.treeList[index].checked)
if (item.rank > 0) {
item.parentId.forEach((pid, indexP) => {
const parent = this.treeList.filter((i) => i.id === pid)
const childNum = parent.length > 0 ? parent[0].childNum : 0
if (this.childNums[pid] === undefined) {
this.childNums[pid] = 1
} else if (this.childNums[pid] < childNum) {
this.childNums[pid]++
}
})
//子级选择/选满/取消选择, 父级往上同步状态
this.setAncestors(item.parentId, this.treeList[index].checked)
}
this._fixMultiple(index)
},
// 处理单选多选
_fixMultiple(index) {
if (!this.multiple) {
// 如果是单选
this.treeList.forEach((v, i) => {
if (i != index) {
this.treeList[i].checked = false
} else {
this.treeList[i].checked = true
}
})
}
},
// 重置数据
_reTreeList() {
this.treeList.forEach((v, i) => {
this.treeList[i].checked = v.orChecked
})
},
_initTree(range = this.range) {
this.treeList = []
this.dataTree = JSON.parse(JSON.stringify(range))
this._renderTreeList(range)
this.$nextTick(() => {
this._defaultSelect(range)
})
},
// 筛选
input(val) {
// 清除上一次的定时器,避免频繁执行
if (this.inputTimer) {
clearTimeout(this.inputTimer)
}
// 设置新的定时器,指定延迟后执行过滤逻辑
this.inputTimer = setTimeout(() => {
const keyword = val
// 执行树形过滤和初始化
this._initTree(this.filterNodes(this.range, keyword))
// 清空定时器标识
clearTimeout(this.inputTimer)
this.inputTimer = null
}, 500)
// this._initTree(this.filterNodes(this.range, val.detail.value))
},
filterNodes(node, query) {
const keyword = query.trim() // 获取搜索关键字并转换为小写
const nodes = node
// 使用递归函数过滤树形数据
const filteredNodes = []
for (const node of nodes) {
if (node.name.includes(keyword)) {
// 如果节点的标签包含关键字,将其添加到结果中
filteredNodes.push(node)
} else if (node.children && node.children.length > 0) {
// 如果节点有子节点,则递归过滤子节点
const filteredChildren = this.filterNodes(node.children, keyword)
if (filteredChildren.length > 0) {
// 如果子节点中有匹配的结果,则添加父节点
const clonedNode = { ...node, children: filteredChildren }
filteredNodes.push(clonedNode)
}
}
}
return filteredNodes
},
},
watch: {
range(list) {
this._initTree(list)
},
multiple() {
if (this.range.length) {
this._reTreeList()
}
},
selectParent() {
if (this.range.length) {
this._reTreeList()
}
},
},
mounted() {
this._initTree()
},
}
</script>
<style lang="scss" scoped>
@import './style.css';
/deep/ .uni-searchbar__box {
justify-content: left !important;
}
</style>

View File

@@ -0,0 +1,195 @@
.tki-tree-mask {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
z-index: 9998;
background-color: rgba(0, 0, 0, 0.6);
opacity: 0;
transition: all 0.3s ease;
visibility: hidden;
}
.tki-tree-mask.show {
visibility: visible;
opacity: 1;
}
.tki-tree-cnt {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
z-index: 9999;
top: 160rpx;
transition: all 0.3s ease;
transform: translateY(100%);
}
.tki-tree-cnt.show {
transform: translateY(0);
}
.tki-tree-bar {
background-color: #fff;
height: 72rpx;
padding-left: 20rpx;
padding-right: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
border-bottom-width: 1rpx !important;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
font-size: 32rpx;
color: #757575;
line-height: 1;
}
.tki-tree-bar1 {
background-color: #fff;
}
.tki-tree-bar-confirm {
color: #07bb07;
}
.tki-tree-view {
position: absolute;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
top: 160rpx;
background-color: #fff;
padding-top: 20rpx;
padding-right: 20rpx;
padding-bottom: 20rpx;
padding-left: 20rpx;
}
.tki-tree-view-sc {
height: 100%;
overflow: hidden;
}
.tki-tree-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 26rpx;
/* color: #757575; */
line-height: 1;
height: 0;
opacity: 0;
transition: 0.2s;
position: relative;
overflow: hidden;
}
.tki-tree-item.show {
height: 80rpx;
opacity: 1;
}
.tki-tree-item.showchild:before {
transform: rotate(90deg);
}
.tki-tree-item.last:before {
opacity: 0;
}
.tki-tree-icon {
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
}
.tki-tree-label {
flex: 1;
display: flex;
align-items: center;
height: 100%;
line-height: 1.2;
}
.tki-tree-check {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.tki-tree-check-yes,
.tki-tree-check-no {
width: 20px;
height: 20px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
border-top-width: 1rpx;
border-left-width: 1rpx;
border-bottom-width: 1rpx;
border-right-width: 1rpx;
border-style: solid;
border-color: #07bb07;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.tki-tree-check-yes-b {
width: 12px;
height: 12px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
background-color: #07bb07;
}
.tki-tree-check .radio {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.tki-tree-check .radio .tki-tree-check-yes-b {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.hover-c {
opacity: 0.6;
}
.search {
position: absolute;
left: 35rpx;
}
/* 基础输入框样式 */
.uni-input {
width: 95%;
height: 60rpx;
line-height: 60rpx;
border-radius: 20rpx;
/* font-size: 32rpx;
color: #333;
background: #f5f5f5;
width: 100%;
padding: 0 24rpx 0 60rpx;
box-sizing: border-box;
border: none; */
}
/* 聚焦态样式 */
.uni-input:focus {
background: #fff;
border: 2rpx solid #007aff;
}
/* 占位符样式 */
.uni-input::placeholder {
color: #999;
font-size: 28rpx;
}
/* 兼容微信小程序 */
.uni-input::-webkit-input-placeholder {
color: #999;
font-size: 28rpx;
}

View File

@@ -0,0 +1,177 @@
<template>
<view class="content">
<picker
mode="multiSelector"
@change="bindPickerChange"
@columnchange="handleColumnChange"
:value="multiIndex"
:range="onlyYear ? yearsArray : multiArray"
>
<view class="time-season">
<text>
<text v-if="showMonth">{{ year + '-' + season }} </text>
<text v-else-if="year">{{ year + (year ? '' : '') + (onlyYear ? '' : season) }} </text>
<text v-else class="prompt">请选择时间 </text>
</text>
</view>
</picker>
</view>
</template>
<script>
export default {
props: {
yearsMin: {
type: Number,
default: 5,
},
yearsMax: {
type: Number,
default: 0,
},
onlyYear: {
type: Boolean,
default: false,
},
showMonth: {
type: Boolean,
default: false,
},
},
data() {
const now = new Date()
this.currentYear = now.getFullYear() // 保存当前年份
this.currentMonth = now.getMonth() + 1 // 保存当前月份1-12
this.currentSeason = Math.ceil(this.currentMonth / 3) // 保存当前季度1-4
const yearArr = this.handleYear()
const seasonArr = ['一季度', '二季度', '三季度', '四季度']
const monthArr = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
return {
multiArray: [yearArr, this.showMonth ? monthArr : seasonArr],
multiIndex: [this.handleYear(true), 0],
yearsArray: [yearArr],
year: '',
season: '',
// 原始完整的时间数组(用于恢复)
fullMonthArr: [...monthArr],
fullSeasonArr: [...seasonArr]
}
},
created() {
const now = new Date()
this.year = now.getFullYear().toString()
const month = now.getMonth() + 1
// 初始化时就过滤当前年份的时间范围
this.updateTimeRange(this.currentYear)
if (this.showMonth) {
this.multiIndex = [5, month - 1]
this.season = month < 10 ? `0${month}` : `${month}`
} else {
let key = 0
if (month >= 1 && month <= 3) {
key = 0
this.season = '一季度'
} else if (month >= 4 && month <= 6) {
key = 1
this.season = '二季度'
} else if (month >= 7 && month <= 9) {
key = 2
this.season = '三季度'
} else {
key = 3
this.season = '四季度'
}
this.multiIndex = [5, key]
}
this.$emit('timeSeasonC', {
year: this.year,
season: this.season,
})
},
methods: {
// 处理年份
handleYear(nowYearIndex) {
let optionsArray = []
let years = new Date().getFullYear()
for (let i = years - this.yearsMin; i <= years + this.yearsMax; i++) {
optionsArray.push(i)
}
if (nowYearIndex) {
return optionsArray.indexOf(years)
}
return optionsArray
},
// 列变化事件:切换年份时动态调整时间范围
handleColumnChange(e) {
// 只有切换第一列(年份列)时才处理
if (e.detail.column === 0) {
const selectedYear = this.multiArray[0][e.detail.value]
// 更新时间范围(月份/季度)
this.updateTimeRange(selectedYear)
// 修正索引:如果当前选中的时间超出新范围,重置为最后一个选项
const timeLength = this.multiArray[1].length
if (this.multiIndex[1] >= timeLength) {
this.multiIndex[1] = timeLength - 1
}
}
},
// 核心:根据选中年份更新时间范围
updateTimeRange(selectedYear) {
if (this.onlyYear) return // 只选年份时无需处理
if (selectedYear === this.currentYear) {
// 当前年份:只显示到当前月/当前季度
if (this.showMonth) {
// 过滤月份只保留1到当前月
this.multiArray[1] = this.fullMonthArr.slice(0, this.currentMonth)
} else {
// 过滤季度只保留1到当前季度
this.multiArray[1] = this.fullSeasonArr.slice(0, this.currentSeason)
}
} else {
// 非当前年份:恢复完整的时间数组
this.multiArray[1] = this.showMonth ? [...this.fullMonthArr] : [...this.fullSeasonArr]
}
},
// 确认选择时间
bindPickerChange: function (e) {
this.multiIndex = e.detail.value
this.year = this.multiArray[0][this.multiIndex[0]]
if (this.onlyYear) {
this.$emit('timeSeasonC', { year: this.year })
} else {
this.season = this.multiArray[1][this.multiIndex[1]]
this.$emit('timeSeasonC', {
year: this.year,
season: this.season,
})
}
},
},
}
</script>
<style>
.content {
width: 100%;
height: 62rpx !important;
box-sizing: border-box;
background-color: #fff;
padding: 10rpx 20rpx;
line-height: 38rpx;
border: 1px solid #e5e5e5;
border-radius: 10rpx;
}
.time-season {
font-size: 28rpx;
}
.prompt {
font-size: 24rpx;
color: grey;
}
</style>

44
main.js
View File

@@ -1,21 +1,23 @@
import App from './App'
import util from './common/js/util'
import request from './common/js/request'
import config from './common/js/config'
import cacheKey from './common/js/cacheKey'
import Vue from 'vue'
import share from "@/common/js/share.js";
Vue.config.productionTip = false
Vue.prototype.$request = request
Vue.prototype.$util = util
Vue.prototype.$config = config
Vue.prototype.$cacheKey = cacheKey
Vue.mixin(share);
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
import App from './App'
import util from './common/js/util'
import request from './common/js/request'
import config from './common/js/config'
import cacheKey from './common/js/cacheKey'
import Vue from 'vue'
import share from '@/common/js/share.js'
import uView from 'uview-ui'
Vue.config.productionTip = false
Vue.prototype.$request = request
Vue.prototype.$util = util
Vue.prototype.$config = config
Vue.prototype.$cacheKey = cacheKey
Vue.mixin(share)
Vue.use(uView)
uni.$u.config.unit = 'rpx'
App.mpType = 'app'
const app = new Vue({
...App,
})
app.$mount()

View File

@@ -2,8 +2,8 @@
"name" : "灿能物联",
"appid" : "__UNI__88BC25B",
"description" : "",
"versionName" : "1.6.81",
"versionCode" : 169,
"versionName" : "1.6.82",
"versionCode" : 170,
"transformPx" : false,
"sassImplementationName" : "node-sass",
/* 5+App */
@@ -138,8 +138,8 @@
"proxy" : {
"/api" : {
"https" : true,
"target" : "https://pqmcn.com:8092/api",
// "target" : "http://192.168.1.103:10215",
// "target" : "https://pqmcn.com:8092/api",
"target" : "http://192.168.2.126:10215",
"changOrigin" : true,
"pathRewrite" : {
"/api" : ""

1442
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,31 @@
{
"id": "yk-authpup",
"name": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"displayName": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"version": "1.0.5",
"description": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"keywords": [
"uniapp",
"华为上架",
"权限",
"权限申请",
"权限判断"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
},
"dependencies": {
"crypto-js": "^4.2.0",
"echarts": "^5.6.0",
"html2canvas": "^1.4.1",
"image-tool": "^1.0.2",
"image-tools": "^1.4.0",
"jsrsasign": "^11.1.0",
"mqtt": "^3.0.0",
"pinyin-pro": "^3.26.0"
}
}
{
"id": "yk-authpup",
"name": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"displayName": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"version": "1.0.5",
"description": "解决软件在运行时,未见向用户告知权限申请的目的,华为等上架被拒问题",
"keywords": [
"uniapp",
"华为上架",
"权限",
"权限申请",
"权限判断"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
},
"dependencies": {
"crypto-js": "^4.2.0",
"echarts": "^5.6.0",
"html2canvas": "^1.4.1",
"image-tool": "^1.0.2",
"image-tools": "^1.4.0",
"jsrsasign": "^11.1.0",
"mqtt": "^3.0.0",
"pinyin-pro": "^3.26.0",
"uview-ui": "^2.0.38"
}
}

View File

@@ -4,6 +4,7 @@
{
"path": "pages/index/index",
"style": {
"enablePullDownRefresh": true, // 开启下拉刷新
"navigationStyle": "custom",
"pullToRefresh": {
"support":true,
@@ -13,11 +14,36 @@
}
},
{
"path": "pages/index/message",
"path": "pages/index/message1",
"style": {
"navigationBarTitleText": "消息"
"navigationBarTitleText": "消息",
"app-plus": {
"bounce": "none",
//关闭窗口回弹效果
"titleNView": {
// 窗口的标题
"titleAlign": "center",
"padding-right": "20rpx",
"buttons": [
{
"text": "一键已读",
"fontSize": "28rpx",
"select": false,
"width": "auto"
}
]
}
}
}
},
{
"path": "pages/index/report",
"style": {
"navigationBarTitleText": "报表"
}
},
{
"path": "pages/index/mine",
"style": {
@@ -133,6 +159,13 @@
"enablePullDownRefresh": true
}
},
{
"path": "pages/device/realTime/index",
"style": {
"navigationBarTitleText": "实时数据"
// "enablePullDownRefresh": true
}
},
{
"path": "pages/device/DVR/detail",
"style": {
@@ -221,12 +254,36 @@
}
}
},
{
"path": "pages/message1/comp/preview",
"style": {
"navigationBarTitleText": "预览"
}
},
{
"path": "pages/message/messageDetail",
"style": {
"navigationBarTitleText": "暂态事件详情"
}
},
{
"path": "pages/message1/comp/transientDetails",
"style": {
"navigationBarTitleText": "暂态事件详情"
}
},
{
"path": "pages/message1/comp/steadyStateDetails",
"style": {
"navigationBarTitleText": "稳态事件详情"
}
},
{
"path": "pages/message1/comp/alarmDetails",
"style": {
"navigationBarTitleText": "运行告警详情"
}
},
{
"path": "pages/device/transfer",
"style": {
@@ -353,6 +410,12 @@
"navigationBarTitleText": "服务内容配置"
}
},
{
"path": "pages/mine/transientSetting",
"style": {
"navigationBarTitleText": "暂态事件配置"
}
},
{
"path": "pages/user/erweima",
"style": {
@@ -441,11 +504,17 @@
"text": "首页"
},
{
"pagePath": "pages/index/message",
"pagePath": "pages/index/message1",
"iconPath": "static/notice.png",
"selectedIconPath": "static/notice2.png",
"text": "消息"
},
},
{
"pagePath": "pages/index/report",
"iconPath": "static/reportIco.png",
"selectedIconPath": "static/reportIco1.png",
"text": "报表"
},
{
"pagePath": "pages/index/mine",
"iconPath": "static/mine.png",
@@ -468,7 +537,8 @@
"easycom": {
"autoscan": true,
"custom": {
"^rc-(.*)": "@/components/Cn-$1/Cn-$1.vue"
"^rc-(.*)": "@/components/Cn-$1/Cn-$1.vue",
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
}
},
"condition": {

View File

@@ -1,379 +1,480 @@
<template>
<Cn-page :loading="loading">
<template slot="body">
<view class="device">
<view class="nav" :style="{ top: navTabHeight + 'px' }">
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.engineeringName }"
@click="selectEngineering"
>{{ select.engineeringName || '全部工程' }}
<uni-icons
type="bottom"
size="14"
:color="select.engineeringName ? '#376cf3' : '#666'"
></uni-icons>
</view>
<picker
@change="projectNameChange"
@cancel="select.selectProject = false"
:value="select.projectNameIndex"
:range="filterProjectList"
range-key="text"
v-if="select.engineeringId"
>
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.projectName }"
@click="select.selectProject = true"
>
{{
select.projectName
? select.projectName.length > 6
? select.projectName.substring(0, 6) + '...'
: select.projectName
: '全部项目'
}}
<uni-icons
type="top"
size="14"
:color="select.projectName ? '#376cf3' : '#666'"
v-if="select.selectProject"
></uni-icons>
<uni-icons
type="bottom"
size="14"
:color="select.projectName ? '#376cf3' : '#666'"
v-else
></uni-icons>
</view>
</picker>
<picker
@change="runStatusChange"
@cancel="select.runStatusSelect = false"
:value="select.runStatusIndex"
:range="projectType"
range-key="text"
>
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.runStatusName }"
@click="select.runStatusSelect = true"
>
{{
select.runStatusName
? select.runStatusName.length > 4
? select.runStatusName.substring(0, 4) + '...'
: select.runStatusName
: '全部状态'
}}
<uni-icons
type="top"
size="14"
:color="select.runStatusName ? '#376cf3' : '#666'"
v-if="select.runStatusSelect"
></uni-icons>
<uni-icons
type="bottom"
size="14"
:color="select.runStatusName ? '#376cf3' : '#666'"
v-else
></uni-icons>
</view>
</picker>
<view style="flex: 1"></view>
<!-- <picker @change="runStatusChange" :value="select.projectTypeIndex" :range="projectType" range-key="text">
<view class="nav-menu" :class="{ 'nav-menu-active': select.projectType }"
>{{ select.projectType || '类型' }}
</view>
</picker> -->
</view>
<view class="content device" :style="{ minHeight: minHeight }">
<Cn-device-card v-for="(item, index) in store.data" :device="item" :key="index"> </Cn-device-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else></Cn-empty>
</view>
</view>
</template>
</Cn-page>
</template>
<script>
import { getProjectList } from '@/common/api/project'
import { queryDictData } from '@/common/api/dictionary'
import list from '@/common/js/list'
export default {
mixins: [list],
data() {
return {
loading: true,
transfer: false,
share: false,
checkList: [],
select: {
projectName: '',
projectNameIndex: 0,
projectSelect: false,
engineeringName: '',
engineeringId: '',
runStatusName: '',
runStatusIndex: 0,
runStatusSelect: false,
},
minHeight: 0,
navTabHeight: 0,
engineeringList: [],
projectList: [],
projectType: [
{
text: '全部',
value: '',
},
{
text: '离线',
value: 1,
},
{
text: '在线',
value: 2,
},
],
pageOptions: {},
}
},
watch: {
select: {
handler(val, oldVal) {
if (this.loading) return
this.store.params.projectId =
val.projectNameIndex === 0 ? '' : this.filterProjectList[val.projectNameIndex].value
this.store.params.runStatus = val.runStatusIndex === 0 ? '' : this.projectType[val.runStatusIndex].value
this.store.params.engineerId = val.engineeringId || ''
this.store.reload()
},
deep: true,
},
},
computed: {
filterProjectList() {
return this.projectList.filter(
(item) => item.engineeringId === this.select.engineeringId || item.value === '-1',
)
},
},
onLoad(options) {
let engineering = uni.getStorageSync('engineering')
this.pageOptions = options
switch (options.type) {
case 'onLineDevs':
this.select.runStatusIndex = 2
this.select.runStatusName = '在线'
break
case 'offLineDevs':
this.select.runStatusIndex = 1
this.select.runStatusName = '离线'
break
case 'currentOnLineDevs':
this.select.runStatusIndex = 2
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
this.select.runStatusName = '在线'
break
case 'currentOffLineDevs':
this.select.runStatusIndex = 1
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
this.select.runStatusName = '离线'
break
case 'allEngineering':
this.select.engineeringName = ''
this.select.engineeringId = ''
break
case 'nowEngineering':
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
break
default:
break
}
this.init()
},
onShow() {
let engineering = uni.getStorageSync('onceSelectEngineering')
if (engineering) {
uni.removeStorageSync('onceSelectEngineering')
this.select.engineeringId = engineering.id
this.select.engineeringName = engineering.name
this.select.projectNameIndex = 0
}
},
methods: {
selectEngineering() {
uni.navigateTo({
url: '/pages/home/selectEngineering?from=once',
})
},
deviceIcon(e) {
let str = ''
switch (e) {
case 1:
str = '/static/device_bad.png'
break
case 2:
str = '/static/device.png'
break
default:
str = '/static/device.png'
break
}
return str
},
switchChange(e) {
console.log(e)
let index = this.checkList.indexOf(e.equipmentId)
if (index > -1) {
this.checkList.splice(index, 1)
} else {
this.checkList.push(e.equipmentId)
}
},
cancel() {
this.transfer = false
this.share = false
this.checkList = []
},
submit() {
console.log(this.checkList)
if (this.checkList.length === 0) {
this.$util.toast('请选择设备')
return
}
if (this.transfer) {
uni.navigateTo({ url: '/pages/device/transfer?id=' + this.checkList.join(',') })
} else if (this.share) {
uni.navigateTo({ url: '/pages/device/share?id=' + this.checkList.join(',') })
}
this.cancel()
},
async init() {
console.warn('init')
this.getEngineeringList()
this.getProjectList()
this.getDeviceList()
},
getDeviceList() {
this.store = this.DataSource('/cs-device-boot/EquipmentDelivery/queryEquipmentByProject')
this.store.params.engineerId = this.select.engineeringId
this.store.params.runStatus =
this.select.runStatusIndex === 0 ? '' : this.projectType[this.select.runStatusIndex].value
this.store.params.pageSize = 50
this.store.firstCallBack = (res) => {
this.loading = false
uni.createSelectorQuery()
.select('.uni-navbar')
.boundingClientRect((rect1) => {
if (!rect1) return
this.navTabHeight = rect1.height
uni.createSelectorQuery()
.select('.nav')
.boundingClientRect((rect2) => {
if (!rect2) return
// #ifdef H5
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height + 50) + 'px)'
// #endif
// #ifdef APP-PLUS
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height) + 'px)'
console.log(this.minHeight)
// #endif
})
.exec()
})
.exec()
}
this.store.reload()
},
getProjectList() {
this.projectList = uni.getStorageSync('projectList')
},
getEngineeringList() {
this.engineeringList = uni.getStorageSync('engineeringList')
},
queryDictData() {
queryDictData('项目类型').then((res) => {
this.projectType = [
{
text: '全部类型',
value: '',
},
...res.data.map((item) => {
return {
text: item.anotherName,
value: item.id,
}
}),
]
})
},
submitFeedBack() {
uni.navigateTo({ url: '/pages/home/feedback' })
},
runStatusChange(e) {
this.select.runStatusSelect = false
this.select.runStatusIndex = e.detail.value
if (e.detail.value === 0) {
this.select.runStatusName = ''
return
}
this.select.runStatusName = this.projectType[e.detail.value].text
},
projectNameChange(e) {
this.select.selectProject = false
console.log(e)
this.select.projectNameIndex = e.detail.value
if (e.detail.value === 0) {
this.select.projectName = ''
return
}
this.select.projectName = this.projectList[e.detail.value].text
},
registerDevice() {
uni.showModal({
title: '提示',
content: '请选择设备类型',
confirmText: '直连设备',
cancelText: '网关接入',
cancelColor: '#007aff',
success: ({ confirm, cancel }) => {
if (confirm) {
uni.navigateTo({
url: '/pages/device/new',
})
} else if (cancel) {
uni.navigateTo({
url: '/pages/gateway/list',
})
}
},
})
},
registerGateway() {
uni.navigateTo({
url: '/pages/gateway/new',
})
},
navMenuClick(index) {
this.navMenuActive = index
},
jump(item) {
console.log(12321,item);
uni.navigateTo({
url: '/pages/device/APF/detail?id=' + item.equipmentId + '&isPrimaryUser=' + item.isPrimaryUser + '&ndid=' + item.ndid,
})
},
},
}
</script>
<style lang="scss"></style>
<template>
<Cn-page :loading="loading">
<template slot="body">
<view class="device">
<view class="nav" :style="{ top: navTabHeight + 'px' }">
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.engineeringName }"
@click="selectEngineering"
>{{ select.engineeringName || '全部工程' }}
<uni-icons
type="bottom"
size="14"
:color="select.engineeringName ? '#376cf3' : '#666'"
></uni-icons>
</view>
<picker
@change="projectNameChange"
@cancel="select.selectProject = false"
:value="select.projectNameIndex"
:range="filterProjectList"
range-key="text"
v-if="select.engineeringId"
>
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.projectName }"
@click="select.selectProject = true"
>
{{
select.projectName
? select.projectName.length > 6
? select.projectName.substring(0, 6) + '...'
: select.projectName
: '全部项目'
}}
<uni-icons
type="top"
size="14"
:color="select.projectName ? '#376cf3' : '#666'"
v-if="select.selectProject"
></uni-icons>
<uni-icons
type="bottom"
size="14"
:color="select.projectName ? '#376cf3' : '#666'"
v-else
></uni-icons>
</view>
</picker>
<picker
@change="runStatusChange"
@cancel="select.runStatusSelect = false"
:value="select.runStatusIndex"
:range="projectType"
range-key="text"
>
<view
class="nav-menu"
:class="{ 'nav-menu-active': select.runStatusName }"
@click="select.runStatusSelect = true"
>
{{
select.runStatusName
? select.runStatusName.length > 4
? select.runStatusName.substring(0, 4) + '...'
: select.runStatusName
: '全部状态'
}}
<uni-icons
type="top"
size="14"
:color="select.runStatusName ? '#376cf3' : '#666'"
v-if="select.runStatusSelect"
></uni-icons>
<uni-icons
type="bottom"
size="14"
:color="select.runStatusName ? '#376cf3' : '#666'"
v-else
></uni-icons>
</view>
</picker>
</view>
<view class="nav" style="padding: 0 10px !important">
<view style="flex: 1"></view>
<template v-if="transfer || share">
<view class="nav-menu nav-menu-btn" @click="cancel">取消</view>
<view class="nav-menu nav-menu-btn" @click="submit">确定</view>
</template>
<template v-else-if="store.data.length">
<view
class="nav-menu nav-menu-btn"
@click="selectDevice('transfer')"
v-if="
userInfo.authorities === 'app_vip_user' || userInfo.authorities === 'engineering_user'
"
>移交
</view>
<view
class="nav-menu nav-menu-btn"
@click="selectDevice('share')"
v-if="userInfo.authorities === 'app_vip_user'"
>分享
</view>
</template>
</view>
<view class="content device" :style="{ minHeight: minHeight }">
<Cn-device-card v-for="(item, index) in store.data" :device="item" :key="index">
<template v-slot:title>
<!-- 卡片标题 -->
<switch
v-if="transfer || share"
:checked="checkList.indexOf(item.equipmentId) > -1"
style="transform: scale(0.8); position: relative; left: 20rpx"
@change="switchChange(item)"
/>
<view class="star-icon" v-else @click="toggleStar(item)">
<uni-icons
custom-prefix="custom-icon"
:type="item.isTop == 1 ? 'star-filled' : 'star'"
:color="item.isTop == 1 ? '#ffcc00' : ''"
size="25"
></uni-icons>
</view>
</template>
</Cn-device-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else></Cn-empty>
</view>
</view>
</template>
</Cn-page>
</template>
<script>
import { getProjectList } from '@/common/api/project'
import { queryDictData } from '@/common/api/dictionary'
import list from '@/common/js/list'
import { engineeringPinToTop } from '@/common/api/device'
export default {
mixins: [list],
data() {
return {
loading: true,
transfer: false,
share: false,
checkList: [],
select: {
projectName: '',
projectNameIndex: 0,
projectSelect: false,
engineeringName: '',
engineeringId: '',
runStatusName: '',
runStatusIndex: 0,
runStatusSelect: false,
},
userInfo: {},
minHeight: 0,
navTabHeight: 0,
engineeringList: [],
projectList: [],
projectType: [
{
text: '全部',
value: '',
},
{
text: '离线',
value: 1,
},
{
text: '在线',
value: 2,
},
],
pageOptions: {},
}
},
watch: {
select: {
handler(val, oldVal) {
if (this.loading) return
this.store.params.projectId =
val.projectNameIndex === 0 ? '' : this.filterProjectList[val.projectNameIndex].value
this.store.params.runStatus = val.runStatusIndex === 0 ? '' : this.projectType[val.runStatusIndex].value
this.store.params.engineerId = val.engineeringId || ''
this.store.reload()
},
deep: true,
},
},
computed: {
filterProjectList() {
return this.projectList.filter(
(item) => item.engineeringId === this.select.engineeringId || item.value === '-1',
)
},
},
created() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
},
onLoad(options) {
let engineering = uni.getStorageSync('engineering')
this.pageOptions = options
switch (options.type) {
case 'onLineDevs':
this.select.runStatusIndex = 2
this.select.runStatusName = '在线'
break
case 'offLineDevs':
this.select.runStatusIndex = 1
this.select.runStatusName = '离线'
break
case 'currentOnLineDevs':
this.select.runStatusIndex = 2
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
this.select.runStatusName = '在线'
break
case 'currentOffLineDevs':
this.select.runStatusIndex = 1
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
this.select.runStatusName = '离线'
break
case 'allEngineering':
this.select.engineeringName = ''
this.select.engineeringId = ''
break
case 'nowEngineering':
this.select.engineeringName = engineering.name
this.select.engineeringId = engineering.id
break
default:
break
}
this.init()
},
onShow() {
let engineering = uni.getStorageSync('onceSelectEngineering')
if (engineering) {
uni.removeStorageSync('onceSelectEngineering')
this.select.engineeringId = engineering.id
this.select.engineeringName = engineering.name
this.select.projectNameIndex = 0
this.getProjectList()
}
},
methods: {
selectEngineering() {
uni.navigateTo({
url: '/pages/home/selectEngineering?from=once',
})
},
deviceIcon(e) {
let str = ''
switch (e) {
case 1:
str = '/static/device_bad.png'
break
case 2:
str = '/static/device.png'
break
default:
str = '/static/device.png'
break
}
return str
},
switchChange(e) {
console.log(e)
let index = this.checkList.indexOf(e.equipmentId)
if (index > -1) {
this.checkList.splice(index, 1)
} else {
this.checkList.push(e.equipmentId)
}
},
cancel() {
this.transfer = false
this.share = false
this.checkList = []
},
submit() {
console.log(this.checkList)
if (this.checkList.length === 0) {
this.$util.toast('请选择设备')
return
}
if (this.transfer) {
uni.navigateTo({ url: '/pages/device/transfer?id=' + this.checkList.join(',') })
} else if (this.share) {
uni.navigateTo({ url: '/pages/device/share?id=' + this.checkList.join(',') })
}
this.cancel()
},
async init() {
console.warn('init')
this.getEngineeringList()
this.getProjectList()
this.getDeviceList()
},
getDeviceList() {
this.store = this.DataSource('/cs-device-boot/EquipmentDelivery/queryEquipmentByProject')
this.store.params.engineerId = this.select.engineeringId
this.store.params.runStatus =
this.select.runStatusIndex === 0 ? '' : this.projectType[this.select.runStatusIndex].value
this.store.params.pageSize = 50
this.store.firstCallBack = (res) => {
this.loading = false
uni.createSelectorQuery()
.select('.uni-navbar')
.boundingClientRect((rect1) => {
if (!rect1) return
this.navTabHeight = rect1.height
uni.createSelectorQuery()
.select('.nav')
.boundingClientRect((rect2) => {
if (!rect2) return
// #ifdef H5
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height + 50) + 'px)'
// #endif
// #ifdef APP-PLUS
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height) + 'px)'
console.log(this.minHeight)
// #endif
})
.exec()
})
.exec()
}
this.store.reload()
},
getProjectList() {
getProjectList({
pageNum: 1,
pageSize: 9999,
engineeringId: this.select.engineeringId,
}).then((res) => {
console.log(res)
let arr = [
{
text: '全部项目',
value: '-1',
},
...res.data.records.map((item) => {
return {
text: item.name,
value: item.id,
...item,
}
}),
]
this.projectList = arr
uni.setStorageSync('projectList', arr)
})
},
getEngineeringList() {
this.engineeringList = uni.getStorageSync('engineeringList')
},
queryDictData() {
queryDictData('项目类型').then((res) => {
this.projectType = [
{
text: '全部类型',
value: '',
},
...res.data.map((item) => {
return {
text: item.anotherName,
value: item.id,
}
}),
]
})
},
submitFeedBack() {
uni.navigateTo({ url: '/pages/home/feedback' })
},
runStatusChange(e) {
this.select.runStatusSelect = false
this.select.runStatusIndex = e.detail.value
if (e.detail.value === 0) {
this.select.runStatusName = ''
return
}
this.select.runStatusName = this.projectType[e.detail.value].text
},
projectNameChange(e) {
this.select.selectProject = false
console.log(e)
this.select.projectNameIndex = e.detail.value
if (e.detail.value === 0) {
this.select.projectName = ''
return
}
this.select.projectName = this.projectList[e.detail.value].text
},
registerDevice() {
uni.showModal({
title: '提示',
content: '请选择设备类型',
confirmText: '直连设备',
cancelText: '网关接入',
cancelColor: '#007aff',
success: ({ confirm, cancel }) => {
if (confirm) {
uni.navigateTo({
url: '/pages/device/new',
})
} else if (cancel) {
uni.navigateTo({
url: '/pages/gateway/list',
})
}
},
})
},
registerGateway() {
uni.navigateTo({
url: '/pages/gateway/new',
})
},
navMenuClick(index) {
this.navMenuActive = index
},
jump(item) {
console.log(12321, item)
uni.navigateTo({
url:
'/pages/device/APF/detail?id=' +
item.equipmentId +
'&isPrimaryUser=' +
item.isPrimaryUser +
'&ndid=' +
item.ndid,
})
},
selectDevice(type) {
if (this.store.data.findIndex((item) => item.isPrimaryUser === '1') === -1) {
this.$util.toast('没有可操作的设备')
} else {
this[type] = true
}
},
switchChange(e) {
let index = this.checkList.indexOf(e.equipmentId)
if (index > -1) {
this.checkList.splice(index, 1)
} else {
this.checkList.push(e.equipmentId)
}
},
toggleStar(item) {
engineeringPinToTop({
targetId: item.equipmentId,
targetType: 1,
userId: uni.getStorageSync(this.$cacheKey.userInfo).userIndex,
}).then((res) => {
if (res.code == 'A0000') {
this.$util.toast('操作成功!')
this.store.search()
}else{
this.$util.toast(res.message)
}
})
},
},
}
</script>
<style lang="scss"></style>

View File

@@ -0,0 +1,988 @@
<template>
<Cn-page :loading="loading" noPadding>
<view slot="body">
<view class="realTime">
<!-- 头部卡片 -->
<view class="info-card-wrap">
<view class="info-card">
<view class="info-item">
监测点名称<view
><picker
@change="lineChange"
@cancel="selectProject = false"
:value="lineId"
:range="lineList"
range-key="name"
>
<view class="nav-menu" @click="selectProject = true">
{{ lineList[lineKey].name }}
<uni-icons type="bottom" size="18" color="#fff"></uni-icons>
</view> </picker
></view>
</view>
<view class="info-item">
所属工程<view>{{ engineeringName }}</view>
</view>
<view class="info-item">
所属项目<view>{{ equipmentName }}</view>
</view>
<view class="info-item status">
通讯状态<view
class="status-normal"
:style="{ color: runStatus == 1 ? '#ff3b30' : '#00ff88' }"
>{{ runStatus == 1 ? '离线' : '在线' }}
</view>
</view>
</view>
</view>
<view>
<view class="time-bar">
<view class="time">
<u-icon name="clock" size="36"></u-icon>
<text>{{ realTime }}</text>
</view>
<button class="mini-btn" type="primary" :disabled="disabled" size="mini" @click="handleRefresh">
{{ disabled ? `刷新(${countdown}s)` : '刷新' }}
</button>
</view>
<!-- 仪表盘 -->
<view>
<view class="chartBox">
<view>
<view class="chart">
<l-echart
ref="echartV1"
@finished="initChart('echartV1', 'echartsDataV1')"
></l-echart>
</view>
<view class="chart">
<l-echart
ref="echartV2"
@finished="initChart('echartV2', 'echartsDataV2')"
></l-echart>
</view>
<view class="chart">
<l-echart
ref="echartV3"
@finished="initChart('echartV3', 'echartsDataV3')"
></l-echart>
</view>
<view class="text"> 电压有效值 </view>
</view>
<view class="middle" style="width: 100%">
<l-echart
class="echart1"
ref="echart0"
@finished="initChart('echart0', 'echartsData0')"
></l-echart>
<l-echart
class="echart1"
ref="echart1"
@finished="initChart('echart1', 'echartsData1')"
></l-echart>
<view class="text text_center"> 基波电压/电流<br />幅值(相位) </view>
</view>
<view>
<view class="chart">
<l-echart
ref="echartA1"
@finished="initChart('echartA1', 'echartsDataA1')"
></l-echart>
</view>
<view class="chart">
<l-echart
ref="echartA2"
@finished="initChart('echartA2', 'echartsDataA2')"
></l-echart>
</view>
<view class="chart">
<l-echart
ref="echartA3"
@finished="initChart('echartA3', 'echartsDataA3')"
></l-echart>
</view>
<view class="text"> 电压有效值 </view>
</view>
</view>
</view>
<!-- 底部数据表格 -->
<view class="data-table">
<view class="table-header">
<text></text>
<text>A相</text>
<text>B相</text>
<text>C相</text>
</view>
<view class="table-row" v-for="value in realTimeData">
<text>{{ value.name }}</text>
<text>{{ value.A }}</text>
<text>{{ value.B }}</text>
<text>{{ value.C }}</text>
</view>
</view>
</view>
</view>
</view>
</Cn-page>
</template>
<script>
const echarts = require('../../../uni_modules/lime-echart/static/echarts.min')
import { MQTT_IP, MQTT_OPTIONS } from '@/common/js/mqtt.js'
import mqtt from 'mqtt/dist/mqtt.js'
import { getBaseRealData } from '@/common/api/harmonic.js'
export default {
components: {},
props: {},
data() {
return {
loading: true,
// 使用上面定义的图表配置项
option: {},
echartsData0: {},
echartsData1: {},
echartsDataV1: {},
echartsDataV2: {},
echartsDataV3: {},
echartsDataA1: {},
echartsDataA2: {},
echartsDataA3: {},
// 图表实例,用于后续操作
echart0: null,
echart1: null,
echartV1: null,
echartV2: null,
echartV3: null,
echartA1: null,
echartA2: null,
echartA3: null,
ptName: 'star',
client: null,
timer: null,
numTimer: null,
userInfo: {},
realTime: '',
realTimeData: [
{ name: '电压有效值(kV)', A: '/', B: '/', C: '/' },
{ name: '电流有效值(A)', A: '/', B: '/', C: '/' },
{ name: '基波电压幅值(kV)', A: '/', B: '/', C: '/' },
{ name: '基波电压相位(°)', A: '/', B: '/', C: '/' },
{ name: '基波电流幅值(A)', A: '/', B: '/', C: '/' },
{ name: '基波电流相位(°)', A: '/', B: '/', C: '/' },
{ name: '电压偏差(%)', A: '/', B: '/', C: '/' },
{ name: '电压总谐波畸变率(%)', A: '/', B: '/', C: '/' },
],
disabled: false,
countdown: 60,
lineId: '00B78D00A87A1',
lineKey: 0,
lineList: [],
engineeringName: '',
equipmentName: '',
runStatus: 1,
}
},
onLoad(options) {
console.log('🚀 ~ options:', options)
this.lineKey = 0
this.lineList = JSON.parse(options.lineList)
this.lineId = this.lineList[0].lineId
this.engineeringName = options.engineeringName
this.equipmentName = options.equipmentName
this.runStatus = options.runStatus
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
this.echartsData0 = this.initEcharts0()
this.echartsData1 = this.initEcharts1()
this.echartsDataV1 = this.initEcharts('#DAA520', 0, 'A相(kV)')
this.echartsDataV2 = this.initEcharts('#2E8B57', 0, 'B相(kV)')
this.echartsDataV3 = this.initEcharts('#A52a2a', 0, 'C相(kV)')
this.echartsDataA1 = this.initEcharts('#DAA520', 1, 'A相(A)')
this.echartsDataA2 = this.initEcharts('#2E8B57', 1, 'B相(A)')
this.echartsDataA3 = this.initEcharts('#A52a2a', 1, 'C相(A)')
this.$nextTick(() => {
this.setMqtt(0)
this.initMqtt()
})
},
onUnload() {
const charts = [
this.echart0,
this.echart1,
this.echartV1,
this.echartV2,
this.echartV3,
this.echartA1,
this.echartA2,
this.echartA3,
]
charts.forEach((chart) => {
if (chart && chart.dispose) {
chart.dispose()
chart = null
}
})
clearInterval(this.timer)
this.client.end()
},
methods: {
initEcharts0() {
return {
tooltip: { show: false },
toolbox: {
show: false,
},
series: [
// 外圈电压 内圈电流
{
zlevel: 2,
name: '基波电流相位',
type: 'gauge',
// 表盘最小值
min: 180,
// 表盘最大值
max: -180,
// 表盘分割数
splitNumber: 6,
// 圆心位置
center: ['50%', '50%'],
// 半径
radius: '60%',
startAngle: 180,
endAngle: -179.99,
// 指针方向
clockWise: true,
title: false,
// 表盘外框
axisLine: {
show: true,
lineStyle: {
color: [
[0.25, '#9D322D'],
[0.5, '#9D322D'],
[0.75, '#9D322D'],
[1, '#9D322D'],
],
width: 2,
},
},
// 表盘细分数
axisTick: {
show: true,
splitNumber: 5,
distance: 0,
length: 4,
lineStyle: {
color: '#9D322D',
width: 1,
type: 'solid',
},
},
// 分割线
splitLine: {
show: true,
length: 10,
distance: 0,
lineStyle: {
color: '#9D322D',
width: 2,
type: 'solid',
},
},
// 分割线标识
axisLabel: {
show: true,
distance: 2,
fontSize: 10,
formatter: function (v) {
switch (v + '') {
case '-180':
return ''
default:
return v
}
},
},
// 指针设置
pointer: {
icon: 'path://m368.01136,209.80637l173.00807,-193.72679c19.14653,-21.43943 50.16392,-21.43943 69.31045,0l172.93149,193.72679c1.22537,1.37213 1.22537,3.51607 0,4.8882l-47.63657,53.34133c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-113.65381,-127.26452c-1.91465,-2.14395 -5.20785,-0.60031 -5.20785,2.40122l0,731.94254c0,1.88667 -1.37855,3.43031 -3.06345,3.43031l-67.39579,0c-1.6849,0 -3.06345,-1.54364 -3.06345,-3.43031l0,-731.94254c0,-3.08728 -3.2932,-4.54517 -5.20785,-2.40122l-113.65381,127.26452c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-47.63657,-53.34133c-1.22537,-1.37213 -1.22537,-3.51607 0,-4.88819l0,-0.00001M539,861.23064h73v800h-73z',
length: '90%',
width: 15,
opacity: 1,
},
detail: {
show: false,
},
//数值位置
data: [
{
value: 0,
name: 'A相',
itemStyle: {
color: '#DAA520',
},
},
{
value: 0,
name: 'B相',
itemStyle: {
color: '#2E8B57',
},
},
{
value: 0,
name: 'C相',
itemStyle: {
color: '#A52a2a',
},
},
],
},
],
}
},
initEcharts1() {
let _this = this
let color = '#DAA520'
return {
tooltip: { show: false },
toolbox: {
show: false,
},
series: [
{
zlevel: 1,
name: '基波电压相位',
type: 'gauge',
// 表盘最小值
min: 180,
// 表盘最大值
max: -180,
// 表盘分割数
splitNumber: 6,
// 圆心位置
center: ['50%', '50%'],
// 半径
radius: '100%',
startAngle: 180,
endAngle: -179.99,
// 指针方向
clockWise: true,
title: false,
// 表盘外框
axisLine: {
show: true,
lineStyle: {
color: [
[0.25, '#9D322D'],
[0.5, '#9D322D'],
[0.75, '#9D322D'],
[1, '#9D322D'],
],
width: 1.5,
},
},
// 表盘细分数
axisTick: {
show: true,
splitNumber: 5,
distance: 0,
length: 6,
lineStyle: {
color: '#9D322D',
width: 1,
type: 'solid',
},
},
// 分割线
splitLine: {
show: true,
length: 10,
distance: 0,
lineStyle: {
color: '#9D322D',
width: 2,
type: 'solid',
},
},
// 分割线标识
axisLabel: {
show: true,
distance: 2,
fontSize: 10,
formatter: function (v) {
switch (v + '') {
case '-180':
return ''
default:
return v
}
},
},
// 指针设置
pointer: {
icon: 'path://m368.01136,209.80637l173.00807,-193.72679c19.14653,-21.43943 50.16392,-21.43943 69.31045,0l172.93149,193.72679c1.22537,1.37213 1.22537,3.51607 0,4.8882l-47.63657,53.34133c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-113.65381,-127.26452c-1.91465,-2.14395 -5.20785,-0.60031 -5.20785,2.40122l0,731.94254c0,1.88667 -1.37855,3.43031 -3.06345,3.43031l-67.39579,0c-1.6849,0 -3.06345,-1.54364 -3.06345,-3.43031l0,-731.94254c0,-3.08728 -3.2932,-4.54517 -5.20785,-2.40122l-113.65381,127.26452c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-47.63657,-53.34133c-1.22537,-1.37213 -1.22537,-3.51607 0,-4.88819l0,-0.00001M539,861.23064h73v800h-73z',
length: '90%',
width: 15,
opacity: 1,
},
detail: {
show: false,
},
data: [
{
value: 0,
name: 'A相', //'A相',
itemStyle: {
color: '#DAA520',
},
},
{
value: 0,
name: 'B相', //'B相',
itemStyle: {
color: '#2E8B57',
},
},
{
value: 0,
name: 'C相', //'C相',
itemStyle: {
color: '#A52a2a',
},
},
],
},
],
}
},
initEcharts(color, key, name) {
return {
tooltip: { show: false },
toolbox: {
show: false,
},
series: [
{
type: 'gauge',
startAngle: key == 0 ? 180 : 90,
endAngle: key == 0 ? 90 : 0,
min: 0,
max: key == 0 ? 12 : 200,
radius: '150%',
center: key == 0 ? ['90%', '75%'] : ['10%', '75%'],
splitNumber: 2, //刻度数量
axisLine: {
show: true,
lineStyle: {
width: 3,
color: [
[0.3, color],
[0.7, color],
[1, color],
],
},
},
color: color,
// 表盘细分数
axisTick: {
show: true,
distance: -2,
length: 8,
lineStyle: {
color: color,
width: 1,
type: 'solid',
},
},
axisLabel: {
textStyle: {
color: color,
fontSize: 10,
},
},
splitLine: {
show: true,
distance: -2,
length: 12,
lineStyle: {
color: color,
width: 2,
type: 'solid',
},
},
//标题位置
title: {
fontWeight: 'bolder',
fontSize: 10,
offsetCenter: key == 0 ? ['-80%', '-100%'] : ['85%', '-100%'],
},
//数值位置
detail: {
fontSize: 10,
valueAnimation: true,
formatter: '{value}',
offsetCenter: key == 0 ? ['-40%', '25%'] : ['40%', '25%'],
color: color,
},
// 指针设置
pointer: {
length: '80%',
width: 2,
},
data: [
{
value: 0,
name: name,
itemStyle: {
color: color,
},
},
],
},
],
}
},
// 初始化图表
async initChart(key, value) {
// console.log('🚀 ~ key,value:', key, value)
if (!this.$refs[key]) return
try {
this[key] = await this.$refs[key].init(echarts)
this[key].setOption(this[value])
} catch (error) {
console.error('图表初始化失败:', error)
}
},
// 更新图表数据
updateChart(newOption) {
if (this.chartInstance) {
this.chartInstance.setOption(newOption)
} else if (this.$refs.chartRef) {
this.$refs.chartRef.setOption(newOption)
}
},
// 调整图表大小
resizeChart() {
if (this.$refs.chartRef) {
this.$refs.chartRef.resize()
}
},
// 页面卸载时销毁图表实例
beforeUnmount() {
if (this.$refs.chartRef) {
this.$refs.chartRef.dispose()
}
},
// 刷新
handleRefresh() {
this.disabled = true
this.client.end()
this.setMqtt(1)
this.initMqtt()
if (this.numTimer) {
clearInterval(this.numTimer)
}
// 设置定时器,每秒执行一次
this.numTimer = setInterval(() => {
this.countdown--
// 倒计时结束
if (this.countdown <= 0) {
clearInterval(this.numTimer) // 清除定时器
this.disabled = false // 启用按钮
this.countdown = 60 // 重置倒计时
this.numTimer = null // 清空定时器标识
}
}, 1000)
},
async setMqtt(e) {
this.clear()
await getBaseRealData(this.lineId)
.then((res) => {
if (res.code == 'A0000') {
this.$util.toast(e == 0 ? '连接成功!' : '刷新成功!')
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
this.loading = false
this.timer = setInterval(() => {
getBaseRealData(this.lineId).then((res) => {
console.log(res, '获取基础实时数据')
})
}, 30000)
} else {
this.countdown = 60 // 重置倒计时
this.disabled = false
this.loading = false
}
})
.catch(() => {
this.loading = false
})
},
initMqtt() {
MQTT_OPTIONS.clientId = uni.getStorageSync('devCode')
// #ifdef APP-PLUS
this.client = mqtt.connect('wx://' + MQTT_IP, MQTT_OPTIONS)
// #endif
// #ifdef H5
this.client = mqtt.connect('ws://' + MQTT_IP, MQTT_OPTIONS)
// #endif
this.client
.on('connect', () => {
console.log('连接成功')
this.client.subscribe(`/Web/RealData/+`, (err) => {
if (!err) {
console.log(`订阅成功:/Web/RealData/+`)
}
})
// this.client.subscribe(`/zl/devData/${this.devId}/${id}`, (err) => {
// if (!err) {
// console.log(`订阅成功:/zl/devData/${this.devId}/${id}`)
// // 默认推送
// this.client.publish(`/zl/askDevData/${this.devId}/${id}`)
// this.client.publish(`/zl/askTemperData/${this.devId}`)
// if (this.timer) {
// clearInterval(this.timer)
// this.timer = null
// }
// this.timer = setInterval(() => {
// console.log('askDevData')
// this.client.publish(`/zl/askDevData/${this.devId}/${id}`)
// this.client.publish(`/zl/askTemperData/${this.devId}`)
// }, 1000 * 60)
// }
// })
})
.on('reconnect', function (error) {
console.log(error)
// console.log('正在重连...', that.topic)
})
.on('error', function (error) {
console.log('连接失败...', error)
})
.on('end', function () {
console.log('连接断开')
})
.on('message', (topic, message) => {
// console.log('接收推送信息:', JSON.parse(message.toString()), topic)
// console.log('🚀 ~ .on ~ topic:', topic)
if (topic === `/Web/RealData/${this.userInfo.userIndex}`) {
let list = JSON.parse(message.toString())
if (list.lineId == this.lineId) {
// console.log(list)
this.realTime = list.dataTime
let pt = list.pt || 0
let ct = list.ct || 0
let data = {
vRmsA: ((list.vRmsA * pt) / 1000).toFixed(2),
vRmsB: ((list.vRmsB * pt) / 1000).toFixed(2),
vRmsC: ((list.vRmsC * pt) / 1000).toFixed(2),
iRmsA: (list.iRmsA * ct).toFixed(2),
iRmsB: (list.iRmsB * ct).toFixed(2),
iRmsC: (list.iRmsC * ct).toFixed(2),
v1A: ((list.v1A * pt) / 1000).toFixed(2),
v1B: ((list.v1B * pt) / 1000).toFixed(2),
v1C: ((list.v1C * pt) / 1000).toFixed(2),
v1AngA: list.v1AngA.toFixed(2),
v1AngB: list.v1AngB.toFixed(2),
v1AngC: list.v1AngC.toFixed(2),
i1A: (list.i1A * ct).toFixed(2),
i1B: (list.i1B * ct).toFixed(2),
i1C: (list.i1C * ct).toFixed(2),
i1AngA: list.i1AngA.toFixed(2),
i1AngB: list.i1AngB.toFixed(2),
i1AngC: list.i1AngC.toFixed(2),
vDevA: list.vDevA.toFixed(2),
vDevB: list.vDevB.toFixed(2),
vDevC: list.vDevC.toFixed(2),
vThdA: list.vThdA.toFixed(2),
vThdB: list.vThdB.toFixed(2),
vThdC: list.vThdC.toFixed(2),
}
this.realTimeData = [
{ name: '电压有效值(kV)', A: data.vRmsA, B: data.vRmsB, C: data.vRmsC },
{ name: '电流有效值(A)', A: data.iRmsA, B: data.iRmsB, C: data.iRmsC },
{ name: '基波电压幅值(kV)', A: data.v1A, B: data.v1B, C: data.v1C },
{ name: '基波电压相位(°)', A: data.v1AngA, B: data.v1AngB, C: data.v1AngC },
{ name: '基波电流幅值(A)', A: data.i1A, B: data.i1B, C: data.i1C },
{ name: '基波电流相位(°)', A: data.i1AngA, B: data.i1AngB, C: data.i1AngC },
{ name: '电压偏差(%)', A: data.vDevA, B: data.vDevB, C: data.vDevC },
{ name: '电压总谐波畸变率(%)', A: data.vThdA, B: data.vThdB, C: data.vThdC },
]
// 电压
let vMax =
Math.ceil(
(Math.max(
...[
Math.floor(data.vRmsA * 100) / 100 || 1,
Math.floor(data.vRmsB * 100) / 100 || 1,
Math.floor(data.vRmsC * 100) / 100 || 1,
],
) *
1.2) /
10,
) * 10
this.echartsDataV1.series[0].max = vMax
this.echartsDataV2.series[0].max = vMax
this.echartsDataV3.series[0].max = vMax
this.echartsDataV1.series[0].data[0].value = data.vRmsA
this.echartsDataV2.series[0].data[0].value = data.vRmsB
this.echartsDataV3.series[0].data[0].value = data.vRmsC
// 电流
let aMax =
Math.ceil(
(Math.max(
...[
Math.floor(data.iRmsA * 100) / 100 || 1,
Math.floor(data.iRmsB * 100) / 100 || 1,
Math.floor(data.iRmsC * 100) / 100 || 1,
],
) *
1.2) /
10,
) * 10
this.echartsDataA1.series[0].max = aMax
this.echartsDataA2.series[0].max = aMax
this.echartsDataA3.series[0].max = aMax
this.echartsDataA1.series[0].data[0].value = data.iRmsA
this.echartsDataA2.series[0].data[0].value = data.iRmsB
this.echartsDataA3.series[0].data[0].value = data.iRmsC
this.echartsData0.series[0].data[0].value = data.i1AngA
this.echartsData0.series[0].data[1].value = data.i1AngB
this.echartsData0.series[0].data[2].value = data.i1AngC
this.echartsData1.series[0].data[0].value = data.v1AngA
this.echartsData1.series[0].data[1].value = data.v1AngB
this.echartsData1.series[0].data[2].value = data.v1AngC
const charts = [
{ instance: this.echart0, data: this.echartsData0 },
{ instance: this.echart1, data: this.echartsData1 },
{ instance: this.echartV1, data: this.echartsDataV1 },
{ instance: this.echartV2, data: this.echartsDataV2 },
{ instance: this.echartV3, data: this.echartsDataV3 },
{ instance: this.echartA1, data: this.echartsDataA1 },
{ instance: this.echartA2, data: this.echartsDataA2 },
{ instance: this.echartA3, data: this.echartsDataA3 },
]
charts.forEach(({ instance, data }) => {
if (instance && instance.setOption) {
instance.setOption(data, true)
}
})
// this.echart0.setOption(this.echartsData0, true)
// this.echart1.setOption(this.echartsData1, true)
// this.echartV1.setOption(this.echartsDataV1, true)
// this.echartV2.setOption(this.echartsDataV2, true)
// this.echartV3.setOption(this.echartsDataV3, true)
// this.echartA1.setOption(this.echartsDataA1, true)
// this.echartA2.setOption(this.echartsDataA2, true)
// this.echartA3.setOption(this.echartsDataA3, true)
}
}
})
},
// 清空数据
clear() {
this.realTime = ''
this.realTimeData = [
{ name: '电压有效值(kV)', A: '/', B: '/', C: '/' },
{ name: '电流有效值(A)', A: '/', B: '/', C: '/' },
{ name: '基波电压幅值(kV)', A: '/', B: '/', C: '/' },
{ name: '基波电压相位(°)', A: '/', B: '/', C: '/' },
{ name: '基波电流幅值(A)', A: '/', B: '/', C: '/' },
{ name: '基波电流相位(°)', A: '/', B: '/', C: '/' },
{ name: '电压偏差(%)', A: '/', B: '/', C: '/' },
{ name: '电压总谐波畸变率(%)', A: '/', B: '/', C: '/' },
]
this.echartsDataV1.series[0].data[0].value = 0
this.echartsDataV2.series[0].data[0].value = 0
this.echartsDataV3.series[0].data[0].value = 0
this.echartsDataA1.series[0].data[0].value = 0
this.echartsDataA2.series[0].data[0].value = 0
this.echartsDataA3.series[0].data[0].value = 0
this.echartsData0.series[0].data[0].value = 0
this.echartsData0.series[0].data[1].value = 0
this.echartsData0.series[0].data[2].value = 0
this.echartsData1.series[0].data[0].value = 0
this.echartsData1.series[0].data[1].value = 0
this.echartsData1.series[0].data[2].value = 0
const charts = [
{ instance: this.echart0, data: this.echartsData0 },
{ instance: this.echart1, data: this.echartsData1 },
{ instance: this.echartV1, data: this.echartsDataV1 },
{ instance: this.echartV2, data: this.echartsDataV2 },
{ instance: this.echartV3, data: this.echartsDataV3 },
{ instance: this.echartA1, data: this.echartsDataA1 },
{ instance: this.echartA2, data: this.echartsDataA2 },
{ instance: this.echartA3, data: this.echartsDataA3 },
]
charts.forEach(({ instance, data }) => {
if (instance && instance.setOption) {
instance.setOption(data, true)
}
})
},
// 监测点变化
lineChange(e) {
this.clear()
this.lineKey = e.detail.value
this.lineId = this.lineList[e.detail.value].lineId
this.client.end()
this.setMqtt(0)
this.initMqtt()
},
},
computed: {},
watch: {},
}
</script>
<style lang="scss" scoped>
.realTime {
.info-card-wrap {
padding: 20rpx;
background-color: #fff;
}
// padding: 20rpx;
.info-card {
background-image: url('/static/background.png');
background-position: center center; /* 水平+垂直居中 */
background-repeat: no-repeat; /* 不平铺 */
background-size: 100% 100%; /* 覆盖整个容器(可选,根据需求调整) */
color: #fff;
padding: 20rpx 40rpx;
border-radius: 20rpx;
.info-item {
display: flex;
align-items: center;
font-size: 28rpx;
margin-bottom: 10rpx;
view {
// font-size: 28rpx;
.nav-menu {
font-size: 32rpx;
}
}
&:last-child {
margin-bottom: 0rpx;
}
}
.status {
.status-normal {
// font-weight: bold;
}
}
}
.data-table {
margin-top: 20rpx;
background-color: #fff;
overflow: hidden;
.table-header,
.table-row {
display: flex;
justify-content: space-between;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #eee;
text {
flex: 1;
text-align: center;
font-size: 28rpx;
// color: #333;
&:first-child {
text-align: left;
flex: 2;
}
}
}
.table-header {
// background-color: #fafafa;
font-weight: bold;
}
}
.time-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
background-color: #fff;
margin: 20rpx 0 0;
border-radius: 10rpx;
.time {
display: flex;
align-items: center;
flex: 1;
font-size: 28rpx;
text {
margin-left: 20rpx;
}
}
}
}
.chartBox {
background-color: #fff;
padding: 0 0rpx 20rpx;
display: flex;
.middle {
display: flex;
position: relative;
height: 600rpx;
flex: 1;
.echart1 {
position: absolute;
height: 500rpx;
top: 0;
}
}
.chart {
width: 170rpx;
height: 200rpx;
}
.text {
text-align: center;
font-size: 30rpx;
}
.text_center {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%) translateY(50%);
}
}
.mini-btn {
font-size: 24rpx;
border-radius: 45rpx;
height: 45rpx;
line-height: 45rpx;
}
</style>

View File

@@ -1,113 +1,113 @@
<template>
<Cn-page :loading='loading' noPadding>
<view slot='body'>
<view class='detail device'>
<view class="header">
<image src="/static/test2.pic.png" mode="widthFix" style="width: 100%;" />
</view>
<view class="title">基本信息</view>
<view class="des">
<text>名称监测网关</text>
<text class="ml20">项目XXX项目1</text>
</view>
<view class="des">
<text>设备型号PQS-882</text>
<text class="ml20">版本号v1.0.0</text>
</view>
<view class="title mb20 m340">设备列表
<view class="footer-btn" v-if="userInfo.authorities != '2' && userInfo.authorities != '5'" @click="newDevice">注册设备
</view>
</view>
<uni-card :title="'设备' + item" :sub-title="'XXX项目1'" extra="用能" v-for="item in 2" :key="item"
@click="goDevice" padding="0" thumbnail="/static/device.png">
</uni-card>
<uni-load-more status="nomore"></uni-load-more>
<view style="height:20rpx"></view>
</view>
</view>
</Cn-page>
</template>
<script>
export default {
data () {
return {
loading: false,
userInfo: {},
deviceList: [
{
name: '设备1',
des: '设备描述1',
type: 'APF',
project: '监测',
},
{
name: '设备2',
des: '设备描述2',
type: 'DVR',
project: '监测'
},
]
}
},
methods: {
newDevice () {
uni.navigateTo({
url: '/pages/gateway/newDevice'
})
},
goDevice (item) {
uni.navigateTo({
url: `/pages/device/${item.type}/detail`
})
}
},
onLoad (options) {
this.userInfo = uni.getStorageSync('userInfo')
},
}
</script>
<style lang='scss'>
.detail {
// background: $uni-theme-white;
.header {
position: relative;
}
.title {
display: flex;
justify-content: space-between;
padding: 20rpx 20rpx 0;
font-size: 36rpx;
color: #111;
font-weight: 700;
.footer-btn {
padding: 0 20rpx;
height: 50rpx;
background-color: #007aff;
font-size: 24rpx;
color: #fff;
text-align: center;
line-height: 50rpx;
border-radius: 10rpx;
}
}
.des {
padding: 20rpx 20rpx 0;
font-size: 28rpx;
color: #333;
}
.content {
box-sizing: border-box;
}
}
<template>
<Cn-page :loading='loading' noPadding>
<view slot='body'>
<view class='detail device'>
<view class="header">
<image src="/static/test2.pic.png" mode="widthFix" style="width: 100%;" />
</view>
<view class="title">基本信息</view>
<view class="des">
<text>名称监测网关</text>
<text class="ml20">项目XXX项目1</text>
</view>
<view class="des">
<text>设备型号PQS-882</text>
<text class="ml20">版本号v1.0.0</text>
</view>
<view class="title mb20 m340">设备列表
<view class="footer-btn" v-if="userInfo.authorities != '2' && userInfo.authorities != '5'" @click="newDevice">注册设备
</view>
</view>
<uni-card :title="'设备' + item" :sub-title="'XXX项目1'" extra="用能" v-for="item in 2" :key="item"
@click="goDevice" padding="0" thumbnail="/static/device.png">
</uni-card>
<uni-load-more status="nomore"></uni-load-more>
<view style="height:20rpx"></view>
</view>
</view>
</Cn-page>
</template>
<script>
export default {
data () {
return {
loading: false,
userInfo: {},
deviceList: [
{
name: '设备1',
des: '设备描述1',
type: 'APF',
project: '监测',
},
{
name: '设备2',
des: '设备描述2',
type: 'DVR',
project: '监测'
},
]
}
},
methods: {
newDevice () {
uni.navigateTo({
url: '/pages/gateway/newDevice'
})
},
goDevice (item) {
uni.navigateTo({
url: `/pages/device/${item.type}/detail`
})
}
},
onLoad (options) {
this.userInfo = uni.getStorageSync('userInfo')
},
}
</script>
<style lang='scss'>
.detail {
// background: $uni-theme-white;
.header {
position: relative;
}
.title {
display: flex;
justify-content: space-between;
padding: 20rpx 20rpx 0;
font-size: 36rpx;
color: #111;
font-weight: 700;
.footer-btn {
padding: 0 20rpx;
height: 50rpx;
background-color: #007aff;
font-size: 24rpx;
color: #fff;
text-align: center;
line-height: 50rpx;
border-radius: 10rpx;
}
}
.des {
padding: 20rpx 20rpx 0;
font-size: 28rpx;
color: #333;
}
.content {
box-sizing: border-box;
}
}
</style>

View File

@@ -1,97 +1,135 @@
<template>
<Cn-page :loading="loading">
<view slot="body">
<view class="select-enineering">
<uni-indexed-list
:options="engineeringListFilter"
:showSelect="false"
@click="confirm"
></uni-indexed-list>
</view>
</view>
</Cn-page>
</template>
<script>
import { pinyin } from 'pinyin-pro'
import { queryEngineering } from '@/common/api/engineering'
export default {
data() {
return {
loading: false,
engineeringList: [],
options: {},
}
},
computed: {
engineeringListFilter() {
let result = []
this.engineeringList.forEach((item) => {
let arr = pinyin(item.name[0], { toneType: 'none', type: 'array' })
let letter = arr[0][0].toUpperCase()
console.log(letter)
let index = result.findIndex((item) => item.letter === letter)
if (index === -1) {
result.push({
letter,
data: [item.name],
})
} else {
result[index].data.push(item.name)
}
})
return result
},
},
onLoad(options) {
this.options = options
this.engineeringList = uni.getStorageSync('engineeringList')
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
if (!(this.userInfo.authorities === 'app_vip_user' || this.userInfo.authorities === 'engineering_user')) {
// 修改buttons
// #ifdef APP-PLUS
var webView = this.$mp.page.$getAppWebview()
// 修改buttons
webView.setTitleNViewButtonStyle(0, {
width: '0',
})
// #endif
}
},
onShow() {
queryEngineering().then((res) => {
this.engineeringList = res.data
})
},
onNavigationBarButtonTap(e) {
if (this.userInfo.authorities === 'app_vip_user' || this.userInfo.authorities === 'engineering_user') {
uni.navigateTo({
url: `/pages/engineering/new`,
})
} else {
uni.showToast({
title: '暂无权限',
icon: 'none',
})
}
},
methods: {
confirm(e) {
console.log(e)
let engineering = this.engineeringList.find((item) => item.name === e.item.name)
if (this.options.from === 'once') {
// 创建项目的时候选择工程 用完即删
uni.setStorageSync('onceSelectEngineering', engineering)
} else {
uni.setStorageSync('engineering', engineering)
}
uni.navigateBack()
},
},
}
</script>
<style lang="scss">
.index {
padding: 34rpx;
}
</style>
<template>
<Cn-page :loading="loading">
<view slot="body">
<view class="select-enineering">
<view class="all" @click="all">全部工程</view>
<uni-indexed-list
:style="{ top: showAll ? '110rpx' : '0' }"
:options="engineeringListFilter"
:showSelect="false"
@click="confirm"
>
</uni-indexed-list>
</view>
</view>
</Cn-page>
</template>
<script>
import { pinyin } from 'pinyin-pro'
import { queryEngineering } from '@/common/api/engineering'
export default {
props: {},
data() {
return {
loading: false,
engineeringList: [],
options: {},
showAll: true,
}
},
computed: {
engineeringListFilter() {
let result = []
this.engineeringList.forEach((item) => {
let arr = pinyin(item.name[0], { toneType: 'none', type: 'array' })
let letter = arr[0][0].toUpperCase()
console.log(letter)
let index = result.findIndex((item) => item.letter === letter)
if (index === -1) {
result.push({
letter,
data: [item.name],
})
} else {
result[index].data.push(item.name)
}
})
return result
},
},
onLoad(options) {
this.options = options
this.showAll = this.options.showAll ? true : false
this.engineeringList = uni.getStorageSync('engineeringList')
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
if (!(this.userInfo.authorities === 'app_vip_user' || this.userInfo.authorities === 'engineering_user')) {
// 修改buttons
// #ifdef APP-PLUS
var webView = this.$mp.page.$getAppWebview()
// 修改buttons
webView.setTitleNViewButtonStyle(0, {
width: '0',
})
// #endif
}
},
onShow() {
queryEngineering().then((res) => {
this.engineeringList = res.data.sort((a, b) => {
return a.name.localeCompare(b.name, 'zh', { sensitivity: 'accent' })
})
})
},
onNavigationBarButtonTap(e) {
if (this.userInfo.authorities === 'app_vip_user' || this.userInfo.authorities === 'engineering_user') {
uni.navigateTo({
url: `/pages/engineering/new`,
})
} else {
uni.showToast({
title: '暂无权限',
icon: 'none',
})
}
},
methods: {
all() {
uni.setStorageSync('onceSelectEngineering', {
createBy: '',
createTime: '',
updateBy: '',
updateTime: '',
id: '',
name: '',
userId: null,
province: '',
provinceName: '',
city: '',
cityName: '',
description: '',
status: '1',
})
uni.navigateBack()
},
confirm(e) {
console.log(e)
let engineering = this.engineeringList.find((item) => item.name === e.item.name)
if (this.options.from === 'once') {
// 创建项目的时候选择工程 用完即删
uni.setStorageSync('onceSelectEngineering', engineering)
} else {
uni.setStorageSync('engineering', engineering)
}
uni.navigateBack()
},
},
}
</script>
<style lang="scss">
.index {
padding: 34rpx;
}
.all {
padding-left: 30rpx;
display: flex;
align-items: center;
cursor: pointer;
height: 50px;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #dedede;
background-color: #ffffff;
}
</style>

246
pages/index/comp/apply.vue Normal file
View File

@@ -0,0 +1,246 @@
<template>
<view>
<view class="filterCriteria">
<!-- 筛选条件 -->
<Cn-filterCriteria @select="select" :showDatetime="false"> </Cn-filterCriteria>
<view class="choose1">
<view>
<checkbox-group @change="changeBox"
><checkbox value="true" :checked="checkedAll" />全选
</checkbox-group></view
>
<view class="nav-menu nav-menu-btn" @click="selectDevice('transfer')">申请报表 </view>
</view>
</view>
<view class="smallLabel mt20">
<view> {{ store.total }} 条事件 | 已选择 {{ checkedTotal }} 条事件 </view>
<view style="width: 180rpx">
<picker @change="bindPickerChange" :value="sort" :range="array">
<view class="uni-input"
>{{ array[sort] }}排序
<uni-icons custom-prefix="iconfont" type="icon-paixu1" size="10" color="#2563EB"></uni-icons>
</view>
</picker>
</view>
</view>
<!-- 卡片 -->
<view class="event-list" :style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }">
<!-- 循环渲染事件项 -->
<uni-card
class="event-item"
:class="judgment(item.showName)"
v-for="(item, index) in store.data"
:key="index"
@click="clackCard(item)"
>
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- 动态图标根据类型切换 -->
<uni-icons
:custom-prefix="judgment(item.showName) == 'interrupt' ? 'custom-icon' : 'iconfont'"
:type="
judgment(item.showName) == 'sag'
? 'icon-xiajiang'
: judgment(item.showName) == 'swell'
? 'icon-shangsheng'
: 'minus'
"
:color="
judgment(item.showName) == 'sag'
? '#2563eb '
: judgment(item.showName) == 'swell'
? '#e6a23c'
: '#909399'
"
:size="judgment(item.showName) == 'interrupt' ? '50' : '25'"
></uni-icons>
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.equipmentName }}</text>
<text class="event-tag" :class="`${judgment(item.showName)}-tag`">{{ item.showName }}</text>
</view>
<view class="event-desc">
<text>工程名称{{ item.engineeringName }}</text>
<text>项目名称{{ item.projectName }}</text>
<text>监测点名称{{ item.lineName }}</text>
</view>
</view>
<view class="event-action">
<!-- 选择 -->
<checkbox-group @change="changeChild($event, item)"
><checkbox value="true" :checked="item.checked" />
</checkbox-group>
</view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text>
发生时间{{ item.startTime }}幅值{{ item.evtParamVVaDepth }}持续时间{{
item.evtParamTm
}}相别{{ item.evtParamPhase }}
</text>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else style="top: 20%"></Cn-empty>
</view>
</view>
</template>
<script>
import list from '@/common/js/list'
export default {
components: {},
props: {
navHeight: {
type: Number,
default: 0,
},
},
mixins: [list],
data() {
return {
selectValue: {},
height: 0,
checkedAll: false,
checkedTotal: 0,
sort: 0,
array: ['发生时间', '暂降深度', '持续时间'],
}
},
mounted() {},
methods: {
setHeight() {
uni.createSelectorQuery()
.select('.filterCriteria')
.boundingClientRect((rect) => {
console.log('🚀 ~ rect:', rect)
//
// #ifdef H5
this.height = rect?.height + 100 || 0
// #endif
// #ifdef APP-PLUS
this.height = rect?.height + 100 || 0
// #endif
})
.exec()
},
async select(val) {
this.selectValue = val
await this.init()
this.setHeight()
},
init() {
this.store = this.DataSource('/cs-harmonic-boot/eventUser/queryEventpage')
this.store.params.type = 0
this.store.params.pageSize = 10000
this.store.params.sortField = this.sort
this.store.params.engineeringid = this.selectValue.engineeringId
this.store.params.projectId = this.selectValue.projectId
this.store.params.deviceId = this.selectValue.deviceId
this.store.params.lineId = this.selectValue.lineId
this.store.params.startTime = this.selectValue.range[0]
this.store.params.endTime = this.selectValue.range[1]
this.store.loadedCallback = () => {
this.checkedTotal = 0
this.store.data = this.store.data.map((item) => {
item.checked = false
return item
})
}
this.store.reload()
},
// 全选
changeBox(e) {
this.checkedAll = !this.checkedAll
if (e.target.value.length > 0) {
this.store.data = this.store.data.map((item) => {
item.checked = true
return item
})
this.checkedTotal = this.store.total
} else {
this.store.data = this.store.data.map((item) => {
item.checked = false
return item
})
this.checkedTotal = 0
}
},
changeChild(e, item) {
item.checked = !item.checked
this.checkedAll = this.store.data.every((item) => item.checked === true)
this.checkedTotal = this.store.data.filter((item) => item.checked === true).length
},
// 点击卡片
clackCard() {},
// 切换排序
bindPickerChange(e) {
this.sort = e.detail.value
this.init()
},
judgment(val) {
switch (val) {
case '电压暂降':
return 'sag'
case '电压暂升':
return 'swell'
case '电压中断':
return 'interrupt'
}
},
},
computed: {},
watch: {},
}
</script>
<style lang="scss" scoped>
@import '@/pages/message1/index.scss';
.filterCriteria {
.nav {
background-color: #fff;
}
.choose1 {
background-color: #fff;
padding: 0 20rpx;
display: flex;
justify-content: space-between;
/deep/ .uni-checkbox-input {
width: 30rpx;
height: 30rpx;
}
font-size: 26rpx;
}
}
.nav-menu {
padding: 6rpx 20rpx;
margin-left: 20rpx;
margin-bottom: 20rpx;
font-size: 24rpx;
border-radius: 8rpx;
background: #ebeaec;
color: #666;
&-active {
background: #dfe5f7;
color: $uni-theme-color;
}
&-btn {
background: $uni-theme-color;
color: #fff;
}
}
/deep/.uni-card__content {
padding: 20rpx !important;
}
.smallLabel {
justify-content: space-between;
font-size: 24rpx !important;
}
</style>

View File

@@ -61,33 +61,22 @@
</view>
<view class="content device" :style="{ minHeight: minHeight }">
<Cn-device-card v-for="(item, index) in deviceListFilter" :device="item" :key="index">
<template v-slot:title v-if="transfer || share">
<template v-slot:title>
<!-- 卡片标题 -->
<view class="uni-card__header">
<view class="uni-card__header-box">
<view class="uni-card__header-avatar">
<image
class="uni-card__header-avatar-image"
:src="deviceIcon(item.runStatus)"
mode="aspectFit"
/>
</view>
<view class="uni-card__header-content">
<text class="uni-card__header-content-title uni-ellipsis">
{{ item.equipmentName }}
</text>
<text class="uni-card__header-content-subtitle uni-ellipsis">
{{ item.mac }}
</text>
</view>
</view>
<view class="uni-card__header-extra" style="position: relative">
<switch
:checked="checkList.indexOf(item.equipmentId) > -1"
style="transform: scale(0.8); position: relative; left: 20rpx"
@change="switchChange(item)"
/>
</view>
<switch
v-if="transfer || share"
:checked="checkList.indexOf(item.equipmentId) > -1"
style="transform: scale(0.8); position: relative; left: 20rpx"
@change="switchChange(item)"
/>
<view class="star-icon" v-else @click="toggleStar(item)">
<uni-icons
custom-prefix="custom-icon"
:type="item.isTop == 1 ? 'star-filled' : 'star'"
:color="item.isTop == 1 ? '#ffcc00' : ''"
size="25"
></uni-icons>
</view>
</template>
</Cn-device-card>
@@ -102,7 +91,7 @@
<script>
import { getProjectList } from '@/common/api/project'
import { queryDictData } from '@/common/api/dictionary'
import { engineeringPinToTop } from '@/common/api/device'
export default {
props: {
store: {
@@ -133,7 +122,6 @@ export default {
},
computed: {
deviceListFilter() {
let arr = this.store.data.filter((item) => {
if (this.select.projectName && this.select.projectType) {
return item.project === this.select.projectName && item.type === this.select.projectType
@@ -145,9 +133,8 @@ export default {
return true
}
})
if (this.transfer || this.share) {
return arr.filter((item) => {
return item.isPrimaryUser === '1' //&& item.process == 4
})
@@ -161,6 +148,20 @@ export default {
},
mounted() {},
methods: {
toggleStar(item) {
engineeringPinToTop({
targetId: item.equipmentId,
targetType: 1,
userId: uni.getStorageSync(this.$cacheKey.userInfo).userIndex,
}).then((res) => {
if (res.code == 'A0000') {
this.$util.toast('操作成功!')
this.store.search()
} else {
this.$util.toast(res.message)
}
})
},
selectDevice(type) {
if (this.deviceListFilter.findIndex((item) => item.isPrimaryUser === '1') === -1) {
this.$util.toast('没有可操作的设备')
@@ -184,7 +185,6 @@ export default {
return str
},
switchChange(e) {
console.log(e)
let index = this.checkList.indexOf(e.equipmentId)
if (index > -1) {
this.checkList.splice(index, 1)
@@ -333,18 +333,22 @@ export default {
this.navMenuActive = index
},
jump(item) {
uni.navigateTo({
url: '/pages/device/APF/detail?id=' + item.equipmentId + '&isPrimaryUser=' + item.isPrimaryUser+ '&ndid=' + item.ndid,
url:
'/pages/device/APF/detail?id=' +
item.equipmentId +
'&isPrimaryUser=' +
item.isPrimaryUser +
'&ndid=' +
item.ndid,
})
},
},
}
</script>
<style lang="scss">
.index-device{
.index-device {
.nav-menu {
}
}
</style>

View File

@@ -0,0 +1,224 @@
<template>
<view class="index-device">
<view class="nav" :style="{ top: navTabHeight + 'px' }"> </view>
<view class="content device project-list" :style="{ minHeight: minHeight }">
<uni-card v-for="(item, index) in store.data" :key="index">
<view class="card-header">
<view class="project-icon">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" color="#2563eb" size="45"></uni-icons>
</view>
<view class="project-info">
<view class="project-name">{{ item.engineeringName }}</view>
<view class="project-stats">
<view class="stat-item" @click="jump('nowEngineering', item)">
<text class="stat-value blue">{{ item.devTotal }}</text>
<text class="stat-label">设备总数</text>
</view>
<view class="stat-item" @click="jump('currentOnLineDevs', item)">
<text class="stat-value green">{{ item.onlineDevTotal }}</text>
<text class="stat-label">在线设备</text>
</view>
<view class="stat-item" @click="jump('currentOffLineDevs', item)">
<text class="stat-value red">{{ item.offlineDevTotal }}</text>
<text class="stat-label">离线设备</text>
</view>
<view class="stat-item" @click="jump('event', item)">
<text class="stat-value red">{{ item.alarmTotal }}</text>
<text class="stat-label">告警数量</text>
</view>
</view>
</view>
<view class="star-icon" @click="toggleStar(item)">
<uni-icons
custom-prefix="custom-icon"
:type="item.isTop == 1 ? 'star-filled' : 'star'"
:color="item.isTop == 1 ? '#ffcc00' : ''"
size="25"
></uni-icons>
</view>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || deviceListFilter.length > 0"
:status="store.status"
></uni-load-more>
<Cn-empty v-else></Cn-empty>
</view>
</view>
</template>
<script>
import { engineeringPinToTop } from '@/common/api/device'
export default {
props: {
store: {
type: Object,
default: {},
},
},
data() {
return {
loading: false,
minHeight: 0,
navTabHeight: 0,
userInfo: {},
}
},
computed: {
deviceListFilter() {
let arr = this.store.data
return arr
},
},
created() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
},
mounted() {
console.log(12333, this.store)
},
methods: {
toggleStar(item) {
engineeringPinToTop({
targetId: item.engineeringId,
targetType: 2,
userId: uni.getStorageSync(this.$cacheKey.userInfo).userIndex,
}).then((res) => {
if (res.code == 'A0000') {
this.$util.toast('操作成功!')
this.$emit('refresh')
} else {
this.$util.toast(res.message)
}
})
},
getDeviceList() {
this.store.params.pageSize = 50
this.store.firstCallBack = (res) => {
uni.createSelectorQuery()
.select('.uni-navbar')
.boundingClientRect((rect1) => {
if (!rect1) return
this.navTabHeight = rect1.height
uni.createSelectorQuery()
.select('.nav')
.boundingClientRect((rect2) => {
if (!rect2) return
// #ifdef H5
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height + 50) + 'px)'
// #endif
// #ifdef APP-PLUS
this.minHeight = 'calc(100vh - ' + (this.navTabHeight + rect2.height) + 'px)'
console.log(this.minHeight)
// #endif
})
.exec()
})
.exec()
}
this.store.reload()
},
async init() {
console.warn('init')
this.getDeviceList()
},
jump(type, item) {
if (type == 'event') {
uni.switchTab({
url: '/pages/index/message1',
})
} else {
uni.setStorageSync('engineering', { name: item.engineeringName, id: item.engineeringId })
uni.navigateTo({
url: '/pages/device/list?type=' + type,
})
}
},
},
}
</script>
<style lang="scss" scoped>
.index-device {
/* 列表容器 */
// .project-card {
// background-color: #ffffff;
// border-radius: 16rpx;
// margin-bottom: 16rpx;
// padding: 24rpx;
// box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
// }
.card-header {
display: flex;
align-items: center;
}
.project-icon {
width: 110rpx;
height: 110rpx;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
background-color: #2563eb20;
}
.project-info {
flex: 1;
line-height: 36rpx;
}
.project-name {
font-size: 15px;
color: #3a3a3a;
margin-bottom: 10rpx;
}
.project-stats {
display: flex;
// justify-content: space-between;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
border-right: 2rpx solid #ccc;
&:last-child {
border-right: none;
}
}
.stat-value {
font-size: 30rpx;
font-weight: 700;
}
.stat-label {
font-size: 24rpx;
color: #6a6a6a;
}
.blue {
color: #007aff;
}
.green {
color: #34c759;
}
.red {
color: #ff3b30;
}
.icon-star {
color: #cccccc;
}
}
/deep/ .uni-card {
padding: 0 !important;
}
</style>

View File

@@ -1,24 +1,38 @@
<template>
<view class="index-zhuyonghu">
<template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20">所有工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('allEngineering')">
<view class="header-item-value">{{ devCount.onLineDevCount + devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('onLineDevs')">
<view class="header-item-value">{{ devCount.onLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('offLineDevs')">
<view class="header-item-value">{{ devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
</view>
<view class="mt20"></view>
</template>
<view class="canneng-index-title mb20">当前工程设备统计</view>
<view class="index-zhuyonghu">
<template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20">所有工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('allEngineering')">
<view class="header-item-value">{{ devCount.onLineDevCount + devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('onLineDevs')">
<view class="header-item-value">{{ devCount.onLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('offLineDevs')">
<view class="header-item-value">{{ devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{ devCount.alarmCount || 0 }}</view>
<view class="header-item-label">告警数量</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{
devCount.eventCount + devCount.runCount + devCount.harmonicCount || 0
}}</view>
<view class="header-item-label">事件数量</view>
</view>
<view class="header-item" @click="eningerNum">
<view class="header-item-value">{{ devCount.eningerCount || 0 }}</view>
<view class="header-item-label">工程个数</view>
</view>
</view>
<!-- <view class="mt20"></view> -->
</template>
<!-- <view class="canneng-index-title mb20">当前工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('nowEngineering')">
<view class="header-item-value"
@@ -34,135 +48,147 @@
<view class="header-item-value">{{ devCount.currentOffLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
</view>
<view class="canneng-index-title mt20">常用功能</view>
<view style="padding: 20rpx 20rpx 0">
<Cn-grid title="" :auto-fill="false">
<Cn-grid-item src="/static/device2.png" text="设备注册" @click="registerDevice(4)"></Cn-grid-item>
<Cn-grid-item
src="/static/device2.png"
text="功能调试"
@click="registerDevice(2)"
v-if="config.feature"
></Cn-grid-item>
<Cn-grid-item
src="/static/device2.png"
text="出厂调试"
@click="registerDevice(3)"
v-if="config.factory"
></Cn-grid-item>
<Cn-grid-item background="#fff" v-if="!config.feature"></Cn-grid-item>
<Cn-grid-item background="#fff" v-if="!config.factory"></Cn-grid-item>
<Cn-grid-item background="#fff"></Cn-grid-item>
<!-- <Cn-grid-item src="/static/gateway2.png" text="网关注册" @click="registerGateway"></Cn-grid-item> -->
<!-- <Cn-grid-item src="/static/feedback2.png" text="问题反馈" @click="submitFeedBack"></Cn-grid-item>-->
</Cn-grid>
</view>
<uni-popup ref="popup" type="dialog" @maskClick="maskClick">
<uni-popup-dialog
mode="base"
type="info"
content="请选择设备类型"
:duration="0"
confirmText="直连设备"
cancelText="网关接入"
cancelColor="#007aff"
@close="close"
@confirm="confirm"
></uni-popup-dialog>
</uni-popup>
</view>
</view> -->
<view class="canneng-index-title mt20">常用功能</view>
<view style="padding: 20rpx 20rpx 0">
<Cn-grid title="" :auto-fill="false">
<Cn-grid-item src="/static/device2.png" text="设备注册" @click="registerDevice(4)"></Cn-grid-item>
<!-- <Cn-grid-item
src="/static/device2.png"
text="功能调试"
@click="registerDevice(2)"
v-if="config.feature"
></Cn-grid-item>
<Cn-grid-item
src="/static/device2.png"
text="出厂调试"
@click="registerDevice(3)"
v-if="config.factory"
></Cn-grid-item> -->
<!-- <Cn-grid-item background="#fff" v-if="!config.feature"></Cn-grid-item>
<Cn-grid-item background="#fff" v-if="!config.factory"></Cn-grid-item> -->
<Cn-grid-item background="#fff"></Cn-grid-item>
<Cn-grid-item background="#fff"></Cn-grid-item>
<Cn-grid-item background="#fff"></Cn-grid-item>
<!-- <Cn-grid-item src="/static/gateway2.png" text="网关注册" @click="registerGateway"></Cn-grid-item> -->
<!-- <Cn-grid-item src="/static/feedback2.png" text="问题反馈" @click="submitFeedBack"></Cn-grid-item>-->
</Cn-grid>
</view>
<uni-popup ref="popup" type="dialog" @maskClick="maskClick">
<uni-popup-dialog
mode="base"
type="info"
content="请选择设备类型"
:duration="0"
confirmText="直连设备"
cancelText="网关接入"
cancelColor="#007aff"
@close="close"
@confirm="confirm"
></uni-popup-dialog>
</uni-popup>
</view>
</template>
<script>
export default {
data() {
return {
loading: true,
config: {
feature: true,
factory: true,
},
type: 0,
}
},
props: {
devCount: {
type: Object,
default: {},
},
},
created() {
this.init()
},
methods: {
init() {
console.log('工程init')
let serverConfig = uni.getStorageSync(this.$cacheKey.serverConfig)
serverConfig && (this.config = serverConfig)
},
submitFeedBack() {
uni.navigateTo({ url: '/pages/home/feedback' })
},
registerDevice(type) {
this.type = type
this.$refs.popup.open()
// uni.showModal({
// title: '提示',
// content: '请选择设备类型',
// confirmText: '直连设备',
// cancelText: '网关接入',
// cancelColor: '#007aff',
// success: ({ confirm, cancel }) => {
// if (confirm) {
// if (this.devCount.engineeringListLength > 0) {
// uni.navigateTo({
// url: '/pages/device/new?type=' + type,
// })
// } else {
// uni.navigateTo({
// url: '/pages/engineering/new?from=index&type=' + type,
// })
// }
// } else if (cancel) {
// // uni.navigateTo({
// // url: '/pages/gateway/list',
// // })
// this.$util.toast('功能正在开发,敬请期待')
// }
// },
// })
},
maskClick() {
this.$refs.popup.close()
},
close() {
this.$util.toast('功能正在开发,敬请期待')
this.$refs.popup.close()
},
confirm(value) {
if (this.devCount.engineeringListLength > 0) {
uni.navigateTo({
url: '/pages/device/new?type=' + this.type,
})
} else {
uni.navigateTo({
url: '/pages/engineering/new?from=index&type=' + this.type,
})
}
data() {
return {
loading: true,
config: {
feature: true,
factory: true,
},
type: 0,
}
},
props: {
devCount: {
type: Object,
default: {},
},
},
created() {
this.init()
},
methods: {
init() {
console.log('工程init')
let serverConfig = uni.getStorageSync(this.$cacheKey.serverConfig)
serverConfig && (this.config = serverConfig)
},
submitFeedBack() {
uni.navigateTo({ url: '/pages/home/feedback' })
},
registerDevice(type) {
this.type = type
this.$refs.popup.open()
// uni.showModal({
// title: '提示',
// content: '请选择设备类型',
// confirmText: '直连设备',
// cancelText: '网关接入',
// cancelColor: '#007aff',
// success: ({ confirm, cancel }) => {
// if (confirm) {
// if (this.devCount.engineeringListLength > 0) {
// uni.navigateTo({
// url: '/pages/device/new?type=' + type,
// })
// } else {
// uni.navigateTo({
// url: '/pages/engineering/new?from=index&type=' + type,
// })
// }
// } else if (cancel) {
// // uni.navigateTo({
// // url: '/pages/gateway/list',
// // })
// this.$util.toast('功能正在开发,敬请期待')
// }
// },
// })
},
maskClick() {
this.$refs.popup.close()
},
close() {
this.$util.toast('功能正在开发,敬请期待')
this.$refs.popup.close()
},
confirm(value) {
if (this.devCount.engineeringListLength > 0) {
uni.navigateTo({
url: '/pages/device/new?type=' + this.type,
})
} else {
uni.navigateTo({
url: '/pages/engineering/new?from=index&type=' + this.type,
})
}
this.$refs.popup.close()
},
registerGateway() {
uni.navigateTo({
url: '/pages/gateway/new',
})
},
jump(type) {
uni.navigateTo({
url: '/pages/device/list?type=' + type,
})
},
},
this.$refs.popup.close()
},
registerGateway() {
uni.navigateTo({
url: '/pages/gateway/new',
})
},
jump(type) {
uni.navigateTo({
url: '/pages/device/list?type=' + type,
})
},
jumpMessage() {
uni.switchTab({
url: '/pages/index/message1',
})
},
eningerNum() {
uni.navigateTo({
url: '/pages/engineering/list',
})
},
},
}
</script>
<style lang="scss"></style>

View File

@@ -1,122 +1,122 @@
<template>
<view class="index-zhuanzhi">
<template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20">所有工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('allEngineering')">
<view class="header-item-value">{{ devCount.onLineDevCount + devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('onLineDevs')">
<view class="header-item-value">{{ devCount.onLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('offLineDevs')">
<view class="header-item-value">{{ devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{ devCount.alarmCount || 0 }}</view>
<view class="header-item-label">告警数量</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{
devCount.eventCount + devCount.runCount + devCount.harmonicCount || 0
}}</view>
<view class="header-item-label">事件数量</view>
</view>
<view class="header-item" @click="eningerNum">
<view class="header-item-value">{{ devCount.eningerCount || 0 }}</view>
<view class="header-item-label">工程个数</view>
</view>
</view>
<view class="mt20"></view>
</template>
<view class="canneng-index-title mb20">当前工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('nowEngineering')">
<view class="header-item-value">{{
devCount.currentOnLineDevCount + devCount.currentOffLineDevCount || 0
}}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('currentOnLineDevs')">
<view class="header-item-value">{{ devCount.currentOnLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('currentOffLineDevs')">
<view class="header-item-value">{{ devCount.currentOffLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{ devCount.currentAlarmCount || 0 }}</view>
<view class="header-item-label">告警数量</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{
devCount.currentEventCount + devCount.currentRunCount + devCount.currentHarmonicCount || 0
}}</view>
<view class="header-item-label">事件数量</view>
</view>
<view class="header-item" @click="projectNum(true)">
<view class="header-item-value">{{ devCount.currentProjectCount || 0 }}</view>
<view class="header-item-label">项目个数</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loading: false,
}
},
props: {
devCount: {
type: Object,
default: {},
},
},
methods: {
jumpMessage(){
uni.switchTab({
url: '/pages/index/message',
})
},
eningerNum(){
uni.navigateTo({
url: '/pages/engineering/list',
})
},
projectWarning() {
uni.navigateTo({
url: '/pages/zhuanzhi/warning',
})
},
projectNum() {
console.log(now)
if (now) {
let engineering = uni.getStorageSync('engineering')
uni.navigateTo({
url: '/pages/project/list?engineeringName=' + engineering.name + '&engineeringId=' + engineering.id,
})
} else {
uni.navigateTo({
url: '/pages/project/list',
})
}
},
jump(type) {
uni.navigateTo({
url: '/pages/device/list?type=' + type,
})
},
},
created() {},
}
</script>
<style lang="scss">
.index-zhuanzhi {
}
</style>
<template>
<view class="index-zhuanzhi">
<template v-if="devCount.engineeringListLength > 1">
<view class="canneng-index-title mb20">所有工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('allEngineering')">
<view class="header-item-value">{{ devCount.onLineDevCount + devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('onLineDevs')">
<view class="header-item-value">{{ devCount.onLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('offLineDevs')">
<view class="header-item-value">{{ devCount.offLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{ devCount.alarmCount || 0 }}</view>
<view class="header-item-label">告警数量</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{
devCount.eventCount + devCount.runCount + devCount.harmonicCount || 0
}}</view>
<view class="header-item-label">事件数量</view>
</view>
<view class="header-item" @click="eningerNum">
<view class="header-item-value">{{ devCount.eningerCount || 0 }}</view>
<view class="header-item-label">工程个数</view>
</view>
</view>
<view class="mt20"></view>
</template>
<view class="canneng-index-title mb20">当前工程设备统计</view>
<view class="header">
<view class="header-item" @click="jump('nowEngineering')">
<view class="header-item-value">{{
devCount.currentOnLineDevCount + devCount.currentOffLineDevCount || 0
}}</view>
<view class="header-item-label">设备总数</view>
</view>
<view class="header-item" @click="jump('currentOnLineDevs')">
<view class="header-item-value">{{ devCount.currentOnLineDevCount || 0 }}</view>
<view class="header-item-label">在线设备</view>
</view>
<view class="header-item" @click="jump('currentOffLineDevs')">
<view class="header-item-value">{{ devCount.currentOffLineDevCount || 0 }}</view>
<view class="header-item-label">离线设备</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{ devCount.currentAlarmCount || 0 }}</view>
<view class="header-item-label">告警数量</view>
</view>
<view class="header-item" @click="jumpMessage">
<view class="header-item-value">{{
devCount.currentEventCount + devCount.currentRunCount + devCount.currentHarmonicCount || 0
}}</view>
<view class="header-item-label">事件数量</view>
</view>
<view class="header-item" @click="projectNum(true)">
<view class="header-item-value">{{ devCount.currentProjectCount || 0 }}</view>
<view class="header-item-label">项目个数</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loading: false,
}
},
props: {
devCount: {
type: Object,
default: {},
},
},
methods: {
jumpMessage(){
uni.switchTab({
url: '/pages/index/message',
})
},
eningerNum(){
uni.navigateTo({
url: '/pages/engineering/list',
})
},
projectWarning() {
uni.navigateTo({
url: '/pages/zhuanzhi/warning',
})
},
projectNum(now) {
console.log(now)
if (now) {
let engineering = uni.getStorageSync('engineering')
uni.navigateTo({
url: '/pages/project/list?engineeringName=' + engineering.name + '&engineeringId=' + engineering.id,
})
} else {
uni.navigateTo({
url: '/pages/project/list',
})
}
},
jump(type) {
uni.navigateTo({
url: '/pages/device/list?type=' + type,
})
},
},
created() {},
}
</script>
<style lang="scss">
.index-zhuanzhi {
}
</style>

View File

@@ -0,0 +1,124 @@
<template>
<view class="dateReport">
<view class="pd20">
<uni-segmented-control
:current="curSub"
class="subsection"
active-color="#376cf3"
:values="subsectionList"
@clickItem="sectionChange"
/>
</view>
<!-- 卡片 -->
<view
class="event-list"
v-if="eventList.length != 0"
:style="{ height: 'calc(100vh - ' + (navHeight + 56) + 'px)', overflow: 'auto' }"
>
<!-- 循环渲染事件项 -->
<uni-card class="event-item" :class="item.type" v-for="(item, index) in eventList" :key="index">
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-info">
<view class="event-title">
<text class="event-id" >{{ item.id }}</text>
<text class="event-tags">{{ item.tag }}</text>
</view>
</view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text>频率偏差越限111次</text>
<text>不平衡度越限3次</text>
<text>电压畸变率越限5次</text>
<text>偶次电压越限5次</text>
</view>
<view class="downloadReport">
<uni-icons type="download" size="16" color="#376cf3"></uni-icons>下载报告
</view>
</uni-card>
<uni-load-more :status="status"></uni-load-more>
</view>
<Cn-empty v-else></Cn-empty>
</view>
</template>
<script>
export default {
components: {},
props: {
indexList: {
type: Array,
default: () => [],
},
total: {
type: Number,
default: 0,
},
navHeight: {
type: Number,
default: 0,
},
},
data() {
return {
status: 'noMore',
curSub: 0,
subsectionList: ['周报', '月报'],
eventList: [
{
id: '测试监测点',
tag: '2026-01-23至2026-01-23',
status: '1',
},
],
}
},
created() {},
methods: {
sectionChange(index) {
this.curSub = index.currentIndex
},
scrolltolower() {
if (this.total != this.indexList.length) {
this.$emit('scrolltolower')
} else {
// this.status = 'noMore'
}
},
},
computed: {},
watch: {},
}
</script>
<style lang="scss" scoped>
@import '@/pages/message1/index.scss';
.event-title {
justify-content: space-between;
}
.event-tags {
font-size: 24rpx;
}
.event-detail {
display: grid;
grid-template-columns: 1fr 1fr;
}
.downloadReport {
width: 100%;
background: #376cf320;
height: 60rpx;
line-height: 60rpx;
color: #376cf3;
font-weight: 600;
border-radius: 15rpx;
margin-top: 10rpx;
display: flex;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,179 @@
<template>
<view class="dateReport">
<view class="pd20">
<uni-segmented-control
:current="curSub"
class="subsection"
active-color="#376cf3"
:values="subsectionList"
@clickItem="sectionChange"
/>
</view>
<!-- 申请报表 -->
<view v-if="curSub == 0">
<!-- apply -->
<Apply :navHeight="navHeight"/>
</view>
<!-- 申请记录 -->
<view v-if="curSub == 1">
<view
class="record event-list"
v-if="eventList.length != 0"
:style="{ height: 'calc(100vh - ' + (navHeight + 56) + 'px)', overflow: 'auto' }"
>
<!-- 循环渲染事件项 -->
<uni-card class="event-item" :class="item.type" v-for="(item, index) in eventList" :key="index">
<view class="title" :class="item.status == 1 ? 'completed' : 'incomplete'">
<view> {{ item.status == 1 ? '已完成' : '未完成' }} </view>
</view>
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.id }}</text>
<text class="event-tags">{{ item.tag }}</text>
</view>
</view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text>申请人xxx</text>
<text>电话号码18888888888</text>
</view>
<view class="downloadReport">
<!-- <u-icon :name="item.status == 1 ? 'download' : 'upload'" color="#376cf3" size="40"></u-icon> -->
<uni-icons
:type="item.status == 1 ? 'download' : 'upload'"
size="16"
color="#376cf3"
></uni-icons>
{{ item.status == 1 ? '下载报告' : '生成报告' }}
</view>
</uni-card>
<uni-load-more :status="status"></uni-load-more>
</view>
<Cn-empty v-else></Cn-empty>
</view>
</view>
</template>
<script>
import Apply from './apply.vue'
export default {
components: { Apply },
props: {
indexList: {
type: Array,
default: () => [],
},
total: {
type: Number,
default: 0,
},
// status: {
// type: String,
// default: 'more',
// },
navHeight: {
type: Number,
default: 0,
},
},
data() {
return {
value: ['0'],
content: 123,
curSub: 0,
subsectionList: ['申请报表', '申请记录'],
form: {
type: 0,
lindId: '',
},
status: 'noMore',
eventList: [
{
id: '测试监测点',
tag: '2026-01-23至2026-01-23',
status: '1',
},
{
id: '测试监测点',
tag: '2026-01-23至2026-01-23',
status: '0',
},
],
}
},
created() {},
mounted() {},
computed: {},
methods: {
scrolltolower() {
this.$emit('scrolltolower')
},
getHeight() {},
sectionChange(index) {
this.curSub = index.currentIndex
},
},
watch: {},
}
</script>
<style lang="scss" scoped>
@import '@/pages/message1/index.scss';
.event-title {
justify-content: space-between;
}
.event-tags {
font-size: 24rpx;
}
.event-detail {
display: grid;
grid-template-columns: 1fr 1fr;
}
.downloadReport {
width: 100%;
background: #376cf320;
height: 60rpx;
line-height: 60rpx;
color: #376cf3;
font-weight: 600;
border-radius: 15rpx;
margin-top: 10rpx;
display: flex;
justify-content: center;
}
.title {
position: absolute;
left: 0px;
width: calc(100% - 36rpx);
// margin: 0 10px;
top: 0;
border-radius: 4px 4px 0 0;
padding: 2px 10px;
font-size: 24rpx;
font-weight: 600px;
display: flex;
justify-content: space-between;
font-weight: 700;
}
.completed {
background: #e1f3d8;
color: #67c23a;
}
.incomplete {
background: #fde2e2;
color: #f56c6c;
}
/deep/ .record {
.uni-card__content {
padding: 55rpx 20rpx 20rpx !important;
}
}
</style>

View File

@@ -16,9 +16,18 @@
</template>
<template slot="right">
<text class="hide-txt mr5" style="font-size: 28rpx"
>{{ select.engineeringName || emptyEngineeringName }}
>{{
userInfo.authorities === 'engineering_user'
? '创建工程'
: select.engineeringName || emptyEngineeringName
}}
</text>
<uni-icons type="bottom" size="16" color="#111" v-if="select.engineeringName"></uni-icons>
<uni-icons
type="bottom"
size="16"
color="#111"
v-if="select.engineeringName && userInfo.authorities != 'engineering_user'"
></uni-icons>
</template>
</uni-nav-bar>
<view class="index">
@@ -32,7 +41,13 @@
<ZhuYongHu :devCount="devCount" v-if="userInfo.authorities === 'app_vip_user'" />
<!-- 游客 -->
<YouKe :devCount="devCount" v-if="userInfo.authorities === 'tourist'"></YouKe>
<template v-show="engineeringList.length">
<!-- 工程列表 -->
<template v-show="engineeringList.length" v-if="userInfo.authorities === 'engineering_user'">
<view class="canneng-index-title mt20">工程列表</view>
<Engineering ref="engineering" :store="store" @refresh="store.search()"/>
</template>
<!-- 设备列表 -->
<template v-else v-show="engineeringList.length">
<view class="canneng-index-title mt20">设备列表</view>
<Device ref="device" :store="store" />
</template>
@@ -47,6 +62,7 @@ import ZhuYongHu from './comp/indexZhuYongHu.vue'
import ZhuanZhi from './comp/indexZhuanZhi.vue'
import YouKe from './comp/indexYouKe.vue'
import Device from './comp/device.vue'
import Engineering from './comp/engineering.vue'
import list from '../../common/js/list'
import { getDevCount } from '../../common/api/device.js'
import { queryEngineering } from '@/common/api/engineering.js'
@@ -60,6 +76,7 @@ export default {
ZhuanZhi,
YouKe,
Device,
Engineering,
},
data() {
return {
@@ -88,7 +105,11 @@ export default {
},
methods: {
selectEngineering() {
if (this.select.engineeringName) {
if (this.userInfo.authorities === 'engineering_user') {
uni.navigateTo({
url: '/pages/engineering/new',
})
} else if (this.select.engineeringName) {
uni.navigateTo({
url: '/pages/home/selectEngineering',
})
@@ -140,6 +161,7 @@ export default {
console.log(this.$refs.device, 'this.$refs.device')
this.$nextTick(() => {
this.$refs.device && this.$refs.device.init()
this.$refs.engineering && this.$refs.engineering.init()
})
}
this.$refs.gongCheng?.init()
@@ -175,7 +197,7 @@ export default {
if (messagePage) {
uni.setTabBarBadge({
index: 1,
text:messagePage ? (messagePage > 99 ? '99+' : messagePage + '') : '',
text: messagePage ? (messagePage > 99 ? '99+' : messagePage + '') : '',
})
} else {
uni.removeTabBarBadge({
@@ -197,8 +219,10 @@ export default {
// #endif
})
},
// 动态配置导航栏按钮
},
onLoad() {
// 页面加载时,动态配置导航栏按钮
if (!uni.getStorageSync(this.$cacheKey.access_token)) {
uni.reLaunch({
url: '/pages/user/login',
@@ -216,7 +240,11 @@ export default {
}, 1000)
}
this.timer = setInterval(this.getDevCount, 1000 * 60) // 定时请求
this.store = this.DataSource('/cs-device-boot/EquipmentDelivery/queryEquipmentByProject')
this.store =
uni.getStorageSync(this.$cacheKey.userInfo).authorities === 'engineering_user'
? this.DataSource('/cs-harmonic-boot/homePage/getEngineeringHomePage')
: this.DataSource('/cs-device-boot/EquipmentDelivery/queryEquipmentByProject')
// #ifdef APP-PLUS
setTimeout(() => {
// 获取nav高度

287
pages/index/message1.vue Normal file
View File

@@ -0,0 +1,287 @@
<template>
<Cn-page :loading="loading" class="messageBox" style="padding-top: 10px">
<view slot="body" class="message">
<view class="tabsBox">
<uni-segmented-control
:current="current"
:values="items"
style-type="text"
active-color="#376cf3"
@clickItem="onClickItem"
/>
<!-- 角标 -->
<view class="badge-container">
<span
v-for="(item, index) in items"
:key="index"
class="badge"
:style="{ left: getBadgeRightPosition(index) }"
v-if="badgeCounts[index] > 0"
>
{{ badgeCounts[index] > 99 ? '99+' : badgeCounts[index] }}
</span>
</view>
<!-- 筛选条件 -->
<Cn-filterCriteria
ref="cnFilterCriteria"
:level="current === 0 ? 3 : current === 1 ? 3 : 2"
@select="select"
>
</Cn-filterCriteria>
</view>
<view class="content">
<Transient
ref="TransientRef"
v-if="current === 0"
:navHeight="navHeight"
:selectValue="selectValue"
@getDevCount="getDevCount"
/>
<SteadyState
ref="SteadyStateRef"
v-if="current === 1"
:navHeight="navHeight"
:selectValue="selectValue"
@getDevCount="getDevCount"
/>
<Alarm
ref="AlarmRef"
v-if="current === 2"
:navHeight="navHeight"
:selectValue="selectValue"
@getDevCount="getDevCount"
/>
<Run
ref="RunRef"
v-if="current === 3"
:navHeight="navHeight"
:selectValue="selectValue"
@getDevCount="getDevCount"
/>
</view>
</view>
</Cn-page>
</template>
<script>
import Transient from '@/pages/message1/transient.vue'
import SteadyState from '@/pages/message1/steadyState.vue'
import Alarm from '@/pages/message1/alarm.vue'
import Run from '@/pages/message1/run.vue'
import { getDevCount } from '../../common/api/device.js'
import { updateStatus } from '@/common/api/message'
export default {
components: { Transient, SteadyState, Alarm, Run },
props: {},
data() {
return {
items: ['暂态事件', '稳态事件', '运行告警', '运行事件'],
badgeCounts: [0, 0, 0, 0],
current: 0,
colorIndex: 0,
item: '',
loading: false,
width: 0,
navHeight: 0,
selectValue: {},
devCount: [],
// 筛选数据
}
},
onLoad() {},
mounted() {
this.setHeight()
},
onPullDownRefresh() {
this.refresh()
},
onNavigationBarButtonTap(e) {
uni.showModal({
title: '提示',
content: '确定要全部标记为已读吗?',
success: (res) => {
if (res.confirm) {
updateStatus({
// '暂态事件', 0
// '稳态事件', 1
// '运行告警', 3
// '运行事件' 2
type: this.current == 2 ? 3 : this.current == 3 ? 2 : this.current,
eventIds: [],
}).then(() => {
this.refresh()
this.getDevCount()
})
}
},
})
},
onShow() {
this.getDevCount()
this.$nextTick(() => {
this.refresh()
this.$refs.cnFilterCriteria.getProjectList()
})
},
methods: {
setHeight() {
uni.createSelectorQuery()
.select('.tabsBox')
.boundingClientRect((rect) => {
this.width = rect.width
//
// #ifdef H5
this.navHeight = rect.height + 70
// #endif
// #ifdef APP-PLUS
this.navHeight = rect.height + 20
// #endif
})
.exec()
},
refresh() {
switch (this.current) {
case 0:
// this.$refs.TransientRef.getConfig()
// this.$refs.TransientRef.filterValue = ''
this.$refs.TransientRef.store.reload()
break
case 1:
this.$refs.SteadyStateRef.store.reload()
break
case 2:
this.$refs.AlarmRef.store.reload()
break
case 3:
this.$refs.RunRef.store.reload()
break
}
},
onClickItem(e) {
if (this.current !== e.currentIndex) {
this.current = e.currentIndex
}
},
select(value) {
this.selectValue = value
setTimeout(() => {
this.setHeight()
}, 100)
},
getDevCount() {
if (uni.getStorageSync('projectList')[1] != undefined) {
getDevCount(uni.getStorageSync('projectList')[1].engineeringId).then((res) => {
this.devCount = res.data
this.badgeCounts = [
this.devCount.eventCount,
this.devCount.harmonicCount,
this.devCount.alarmCount,
this.devCount.runCount,
]
uni.setStorage({
key: this.$cacheKey.messageCount,
data: this.devCount,
})
let messagePage =
this.devCount.runCount +
this.devCount.eventCount +
this.devCount.alarmCount +
this.devCount.harmonicCount
let minePage = this.devCount.feedBackCount
if (messagePage) {
uni.setTabBarBadge({
index: 1,
text: messagePage ? (messagePage > 99 ? '99+' : messagePage + '') : '',
})
} else {
uni.removeTabBarBadge({
index: 1,
})
}
if (minePage) {
uni.setTabBarBadge({
index: 2,
text: minePage + '',
})
} else {
uni.removeTabBarBadge({
index: 2,
})
}
// #ifdef APP-PLUS
plus.runtime.setBadgeNumber(messagePage + minePage)
// #endif
})
}
},
// 根据索引动态计算右侧偏移位置,使徽章对准每个标签的右上角
getBadgeRightPosition(index) {
return (index + 1) * (this.width / 4) + 'px'
},
},
computed: {},
}
</script>
<style lang="scss">
.messageBox {
overflow: hidden;
/deep/.tabsBox {
position: relative;
background-color: #fff;
.segmented-control {
height: 49px;
background-color: #fff;
border-bottom: 1px solid #cccccc70;
}
.segmented-control__text {
font-size: 27rpx !important;
color: rgb(96, 98, 102);
}
.segmented-control__item--text {
font-weight: bold;
}
.choose {
// padding: 20rpx;
// display: flex;
// justify-content: space-between;
// align-items: center;
background: #fff;
}
}
.subsection {
width: 90%;
margin: 20rpx auto;
}
.badge-container {
position: absolute;
top: 10rpx; /* 徽章向上偏移,与控件重叠 */
right: 0;
width: 100%;
height: 0;
pointer-events: none; /* 确保徽章不干扰点击事件 */
}
.badge {
position: absolute;
// min-width: 18px;
height: 16px;
padding: 0 4px;
background-color: #ff3b30; /* 红色徽章 */
color: white;
font-size: 22rpx;
line-height: 16px;
text-align: center;
border-radius: 9px;
transform: translateX(-150%); /* 使徽章中心对齐右上角 */
}
}
</style>

View File

@@ -1,382 +1,383 @@
<template>
<view :loading="loading">
<view class="mine">
<view class="mine-header" @click="jump('basic')">
<image mode="aspectFill" class="mine-header-head" :src="userInfo.avatar" v-if="userInfo.avatar" />
<image mode="aspectFill" class="mine-header-head" src="/static/head.png" v-else />
<view class="mine-header-name hide-txt">
<view>{{ userInfo.nickname }}</view>
<view></view>
<view class="tag">{{ roleName }}</view>
</view>
<image
src="/static/erweima.png"
style="height: 50rpx; width: 50rpx; border-radius: 12rpx"
mode="scaleToFill"
/>
<uni-icons type="forward" color="#aaa" size="16"></uni-icons>
</view>
<view class="mine-nav" v-if="userInfo.authorities === 'tourist'" @click="jump('upgrade')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/server.png" />
<view class="mine-nav-label">角色升级</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<!-- <view class="mine-nav" @click="jump('audit')" v-if="userInfo.authorities === 'app_vip_user'">
<image mode="aspectFill" class="mine-nav-icon" src="/static/server.png" />
<view class="mine-nav-label">角色审核</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<!-- <view class="mine-nav" @click="jump('user')" v-if="userInfo.authorities === 'app_vip_user'">
<image mode="aspectFill" class="mine-nav-icon" src="/static/subordinate.png" />
<view class="mine-nav-label">分享用户列表</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<view
class="mine-nav"
@click="jump('scan')"
v-if="userInfo.authorities === 'app_vip_user' || userInfo.authorities === 'engineering_user'"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/scan.png" />
<view class="mine-nav-label">扫一扫</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('engineering')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/gongcheng.png" />
<view class="mine-nav-label">工程管理</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('project')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/project.png" />
<view class="mine-nav-label">项目管理</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('feedback')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/feedback.png" />
<view class="mine-nav-label">反馈列表</view>
<uni-badge :text="messageCount.feedBackCount"></uni-badge>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<!-- <view
class="mine-nav"
@click="jump('gateway')"
style="border-bottom: none; box-shadow: 0 4rpx 8rpx #e7e7e74c"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/gateway.png" />
<view class="mine-nav-label">网关列表</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<view class="mine-nav" @click="jump('setupMessage')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/message4.png" />
<view class="mine-nav-label">推送通知设置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view
class="mine-nav"
@click="jump('engineering/setting')"
v-if="userInfo.authorities === 'engineering_user'"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/like.png" />
<view class="mine-nav-label">关注工程配置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('serverSetting')" v-if="userInfo.authorities === 'engineering_user'">
<image mode="aspectFill" class="mine-nav-icon" src="/static/server2.png" />
<view class="mine-nav-label">调试内容配置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('setup')" style="border-bottom: none">
<image mode="aspectFill" class="mine-nav-icon" src="/static/setup.png" />
<view class="mine-nav-label">设置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog
ref="inputClose"
mode="input"
title="角色升级"
placeholder="请输入六位邀请码"
@confirm="upgrade"
></uni-popup-dialog>
</uni-popup>
</view>
<uni-popup ref="alertDialog" type="dialog">
<uni-popup-dialog
style="width: 90%; margin: 5%"
type="info"
cancelText="禁止"
confirmText="允许"
title="权限说明"
content='是否允许"灿能物联"使用相机?'
@confirm="handleScon('camera')"
@close="dialogClose"
></uni-popup-dialog>
</uni-popup>
<uni-popup ref="message" type="message">
<uni-popup-message type="info" :duration="0" style="width: 90%; margin: 5%">
<view style="color: #909399; font-style: 16px">相机权限使用说明:</view>
<view style="color: #6c6c6c; margin-top: 3rpx; "> 用于相机扫描二维码!</view>
</uni-popup-message>
</uni-popup>
<yk-authpup ref="authpup" type="top" @changeAuth="changeAuth" permissionID="CAMERA"></yk-authpup>
</view>
</template>
<script>
import { roleUpdate, autoLogin } from '@/common/api/user'
import { transferDevice, shareDevice } from '@/common/api/device'
import ykAuthpup from "@/components/yk-authpup/yk-authpup";
export default {
components: {
ykAuthpup
},
data() {
return {
loading: true,
userInfo: {},
messageCount: {},
timer: null,
}
},
computed: {
roleName() {
let roleName = ''
switch (this.userInfo.authorities) {
case 'tourist':
roleName = '游客'
break
case 'engineering_user':
roleName = '工程用户'
break
case 'app_vip_user':
roleName = '正式用户'
break
case 'market_user':
roleName = '营销用户'
break
case 'operation_manager':
roleName = '运维管理员'
break
}
return roleName
},
},
onLoad(options) {
this.init()
},
methods: {
init() {},
upgrade(code) {
console.log(code)
roleUpdate({
referralCode: code,
userId: this.userInfo.userIndex,
}).then((res) => {
uni.showToast({
title: '升级成功',
icon: 'none',
})
uni.removeStorageSync('access_token')
// 直接登录
autoLogin(this.userInfo.user_name).then((res) => {
this.$util.loginSuccess(res.data).then((userInfo) => {
this.userInfo = userInfo
})
})
})
},
changeAuth(){
//这里是权限通过后执行自己的代码逻辑
console.log('权限已授权,可执行自己的代码逻辑了');
// this.handleScon()
this.handleScon()
},
jump(type) {
switch (type) {
case 'scan':
if (
plus.os.name == 'Android'
// && plus.navigator.checkPermission('android.permission.CAMERA') === 'undetermined'
) {
//未授权
// this.$refs.alertDialog.open('bottom')
this.$refs['authpup'].open()
// this.$refs.message.open()
} else {
console.log(2)
this.handleScon()
}
break
case 'login':
uni.navigateTo({
url: `/pages/user/login`,
})
break
case 'gateway':
uni.navigateTo({
url: `/pages/gateway/list`,
})
break
case 'upgrade':
this.$refs.inputDialog.open()
break
case 'basic':
uni.navigateTo({
url: `/pages/user/basic`,
})
break
case 'project':
uni.navigateTo({
url: `/pages/project/list`,
})
break
case 'engineering':
uni.navigateTo({
url: `/pages/engineering/list`,
})
break
case 'engineering/setting':
uni.navigateTo({
url: `/pages/engineering/setting`,
})
break
case 'feedback':
uni.navigateTo({
url: `/pages/message/feedback`,
})
break
default:
uni.navigateTo({
url: `/pages/mine/${type}`,
})
break
}
},
handleScon(){
this.$refs.message.close()
uni.scanCode({
onlyFromCamera:true,
success: (res) => {
console.log('条码类型:' + res.scanType)
console.log('条码内容' + res.result)
let content = JSON.parse(res.result)
switch (content.type) {
case 'transferDevice':
this.transferDevice(content.id.split(','))
break
case 'shareDevice':
this.shareDevice(content.id.split(','))
break
default:
this.$util.toast('无效二维码')
break
}
},
})
},
dialogClose(){this.$refs.message.close()},
transferDevice(id) {
transferDevice(id).then((res) => {
uni.navigateTo({ url: '/pages/mine/result?type=transferDevice&id=' + id })
})
},
shareDevice(id) {
shareDevice(id).then((res) => {
uni.navigateTo({ url: '/pages/mine/result?type=shareDevice&id=' + id })
})
},
},
onShow() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
this.loading = false
this.messageCount = uni.getStorageSync(this.$cacheKey.messageCount) || {}
this.timer = setInterval(() => {
this.messageCount = uni.getStorageSync(this.$cacheKey.messageCount) || {}
}, 1000) // 定时请求
},
onHide() {
clearInterval(this.timer)
},
}
</script>
<style lang="scss">
.mine {
.mine-header {
padding: 200rpx 34rpx 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 8rpx #e7e7e74c;
.mine-header-head {
margin-right: 30rpx;
height: 128rpx;
width: 128rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
.mine-header-name {
margin-right: 30rpx;
flex: 1;
font-size: 36rpx;
color: #111;
font-weight: 700;
.tag {
display: flex;
align-items: center;
margin-top: 10rpx;
font-size: 24rpx;
color: #aaa;
.engineering-button {
margin-left: 10rpx;
font-size: 24rpx;
padding: 5rpx 12rpx;
color: #fff;
font-weight: 400;
border-radius: 16rpx;
background: $uni-theme-color;
}
}
}
}
.mine-nav {
padding: 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
border-bottom: 1rpx solid #e8e8e8;
&-icon {
margin-right: 30rpx;
height: 44rpx;
width: 44rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
&-label {
margin-right: 30rpx;
flex: 1;
font-size: 28rpx;
color: #111;
}
}
}
/deep/ .uni-popup-message__box {
border-radius: 10rpx !important;
background-color: #fff;
}
</style>
</style>
<template>
<view :loading="loading">
<view class="mine">
<view class="mine-header" @click="jump('basic')">
<image mode="aspectFill" class="mine-header-head" :src="userInfo.avatar" v-if="userInfo.avatar" />
<image mode="aspectFill" class="mine-header-head" src="/static/head.png" v-else />
<view class="mine-header-name hide-txt">
<view>{{ userInfo.nickname }}</view>
<view></view>
<view class="tag">{{ roleName }}</view>
</view>
<image
src="/static/erweima.png"
style="height: 50rpx; width: 50rpx; border-radius: 12rpx"
mode="scaleToFill"
/>
<uni-icons type="forward" color="#aaa" size="16"></uni-icons>
</view>
<view class="mine-nav" v-if="userInfo.authorities === 'tourist'" @click="jump('upgrade')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/server.png" />
<view class="mine-nav-label">角色升级</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<!-- <view class="mine-nav" @click="jump('audit')" v-if="userInfo.authorities === 'app_vip_user'">
<image mode="aspectFill" class="mine-nav-icon" src="/static/server.png" />
<view class="mine-nav-label">角色审核</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<!-- <view class="mine-nav" @click="jump('user')" v-if="userInfo.authorities === 'app_vip_user'">
<image mode="aspectFill" class="mine-nav-icon" src="/static/subordinate.png" />
<view class="mine-nav-label">分享用户列表</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<view
class="mine-nav"
@click="jump('scan')"
v-if="userInfo.authorities === 'app_vip_user' || userInfo.authorities === 'engineering_user'"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/scan.png" />
<view class="mine-nav-label">扫一扫</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('engineering')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/gongcheng.png" />
<view class="mine-nav-label">工程管理</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('project')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/project.png" />
<view class="mine-nav-label">项目管理</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('feedback')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/feedback.png" />
<view class="mine-nav-label">反馈列表</view>
<uni-badge :text="messageCount.feedBackCount"></uni-badge>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<!-- <view
class="mine-nav"
@click="jump('gateway')"
style="border-bottom: none; box-shadow: 0 4rpx 8rpx #e7e7e74c"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/gateway.png" />
<view class="mine-nav-label">网关列表</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view> -->
<view class="mine-nav" @click="jump('setupMessage')">
<image mode="aspectFill" class="mine-nav-icon" src="/static/message4.png" />
<view class="mine-nav-label">推送通知设置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view
class="mine-nav"
@click="jump('engineering/setting')"
v-if="userInfo.authorities === 'engineering_user'"
>
<image mode="aspectFill" class="mine-nav-icon" src="/static/like.png" />
<view class="mine-nav-label">关注工程配置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('transientSetting')" >
<!-- 调试内容配置 serverSetting-->
<image mode="aspectFill" class="mine-nav-icon" src="/static/server2.png" />
<view class="mine-nav-label">暂态事件</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<view class="mine-nav" @click="jump('setup')" style="border-bottom: none">
<image mode="aspectFill" class="mine-nav-icon" src="/static/setup.png" />
<view class="mine-nav-label">设置</view>
<uni-icons type="forward" color="#aaa" size="20"></uni-icons>
</view>
<uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog
ref="inputClose"
mode="input"
title="角色升级"
placeholder="请输入六位邀请码"
@confirm="upgrade"
></uni-popup-dialog>
</uni-popup>
</view>
<uni-popup ref="alertDialog" type="dialog">
<uni-popup-dialog
style="width: 90%; margin: 5%"
type="info"
cancelText="禁止"
confirmText="允许"
title="权限说明"
content='是否允许"灿能物联"使用相机?'
@confirm="handleScon('camera')"
@close="dialogClose"
></uni-popup-dialog>
</uni-popup>
<uni-popup ref="message" type="message">
<uni-popup-message type="info" :duration="0" style="width: 90%; margin: 5%">
<view style="color: #909399; font-style: 16px">相机权限使用说明:</view>
<view style="color: #6c6c6c; margin-top: 3rpx; "> 用于相机扫描二维码!</view>
</uni-popup-message>
</uni-popup>
<yk-authpup ref="authpup" type="top" @changeAuth="changeAuth" permissionID="CAMERA"></yk-authpup>
</view>
</template>
<script>
import { roleUpdate, autoLogin } from '@/common/api/user'
import { transferDevice, shareDevice } from '@/common/api/device'
import ykAuthpup from "@/components/yk-authpup/yk-authpup";
export default {
components: {
ykAuthpup
},
data() {
return {
loading: true,
userInfo: {},
messageCount: {},
timer: null,
}
},
computed: {
roleName() {
let roleName = ''
switch (this.userInfo.authorities) {
case 'tourist':
roleName = '游客'
break
case 'engineering_user':
roleName = '工程用户'
break
case 'app_vip_user':
roleName = '正式用户'
break
case 'market_user':
roleName = '营销用户'
break
case 'operation_manager':
roleName = '运维管理员'
break
}
return roleName
},
},
onLoad(options) {
this.init()
},
methods: {
init() {},
upgrade(code) {
console.log(code)
roleUpdate({
referralCode: code,
userId: this.userInfo.userIndex,
}).then((res) => {
uni.showToast({
title: '升级成功',
icon: 'none',
})
uni.removeStorageSync('access_token')
// 直接登录
autoLogin(this.userInfo.user_name).then((res) => {
this.$util.loginSuccess(res.data).then((userInfo) => {
this.userInfo = userInfo
})
})
})
},
changeAuth(){
//这里是权限通过后执行自己的代码逻辑
console.log('权限已授权,可执行自己的代码逻辑了');
// this.handleScon()
this.handleScon()
},
jump(type) {
switch (type) {
case 'scan':
if (
plus.os.name == 'Android'
// && plus.navigator.checkPermission('android.permission.CAMERA') === 'undetermined'
) {
//未授权
// this.$refs.alertDialog.open('bottom')
this.$refs['authpup'].open()
// this.$refs.message.open()
} else {
console.log(2)
this.handleScon()
}
break
case 'login':
uni.navigateTo({
url: `/pages/user/login`,
})
break
case 'gateway':
uni.navigateTo({
url: `/pages/gateway/list`,
})
break
case 'upgrade':
this.$refs.inputDialog.open()
break
case 'basic':
uni.navigateTo({
url: `/pages/user/basic`,
})
break
case 'project':
uni.navigateTo({
url: `/pages/project/list`,
})
break
case 'engineering':
uni.navigateTo({
url: `/pages/engineering/list`,
})
break
case 'engineering/setting':
uni.navigateTo({
url: `/pages/engineering/setting`,
})
break
case 'feedback':
uni.navigateTo({
url: `/pages/message/feedback`,
})
break
default:
uni.navigateTo({
url: `/pages/mine/${type}`,
})
break
}
},
handleScon(){
this.$refs.message.close()
uni.scanCode({
onlyFromCamera:true,
success: (res) => {
console.log('条码类型' + res.scanType)
console.log('条码内容:' + res.result)
let content = JSON.parse(res.result)
switch (content.type) {
case 'transferDevice':
this.transferDevice(content.id.split(','))
break
case 'shareDevice':
this.shareDevice(content.id.split(','))
break
default:
this.$util.toast('无效二维码')
break
}
},
})
},
dialogClose(){this.$refs.message.close()},
transferDevice(id) {
transferDevice(id).then((res) => {
uni.navigateTo({ url: '/pages/mine/result?type=transferDevice&id=' + id })
})
},
shareDevice(id) {
shareDevice(id).then((res) => {
uni.navigateTo({ url: '/pages/mine/result?type=shareDevice&id=' + id })
})
},
},
onShow() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
this.loading = false
this.messageCount = uni.getStorageSync(this.$cacheKey.messageCount) || {}
this.timer = setInterval(() => {
this.messageCount = uni.getStorageSync(this.$cacheKey.messageCount) || {}
}, 1000) // 定时请求
},
onHide() {
clearInterval(this.timer)
},
}
</script>
<style lang="scss">
.mine {
.mine-header {
padding: 200rpx 34rpx 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 8rpx #e7e7e74c;
.mine-header-head {
margin-right: 30rpx;
height: 128rpx;
width: 128rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
.mine-header-name {
margin-right: 30rpx;
flex: 1;
font-size: 36rpx;
color: #111;
font-weight: 700;
.tag {
display: flex;
align-items: center;
margin-top: 10rpx;
font-size: 24rpx;
color: #aaa;
.engineering-button {
margin-left: 10rpx;
font-size: 24rpx;
padding: 5rpx 12rpx;
color: #fff;
font-weight: 400;
border-radius: 16rpx;
background: $uni-theme-color;
}
}
}
}
.mine-nav {
padding: 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
border-bottom: 1rpx solid #e8e8e8;
&-icon {
margin-right: 30rpx;
height: 44rpx;
width: 44rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
&-label {
margin-right: 30rpx;
flex: 1;
font-size: 28rpx;
color: #111;
}
}
}
/deep/ .uni-popup-message__box {
border-radius: 10rpx !important;
background-color: #fff;
}
</style>

171
pages/index/report.vue Normal file
View File

@@ -0,0 +1,171 @@
<template>
<view :loading="loading" class="report" style="padding-top: 10px">
<view class="navReport">
<view class="tabsBox">
<uni-segmented-control
:current="curTabs"
:values="items"
style-type="text"
active-color="#376cf3"
@clickItem="onClickItem"
/>
</view>
</view>
<!-- 稳态报表 -->
<SteadyState
v-if="curTabs == 0"
:indexList="indexList"
:total="total"
:status="status"
:navHeight="navHeight"
@scrolltolower="scrolltolower"
/>
<!-- 暂态报表 -->
<Transient
v-if="curTabs == 1"
:indexList="indexList"
:total="total"
:status="status"
:navHeight="navHeight"
@scrolltolower="scrolltolower"
/>
</view>
</template>
<script>
import SteadyState from './comp/steadyState.vue'
import Transient from './comp/transient.vue'
export default {
components: {
SteadyState,
Transient,
},
props: {},
data() {
return {
curTabs: 0,
total: 6,
loading: false,
items: ['稳态报表', '暂态报表'],
status: 'more', //more加载前 loading加载中 noMore加载后
navHeight: 0,
indexList: [
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '1',
status: '1',
},
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '2',
status: '1',
},
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '1',
status: '1',
},
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '1',
status: '0',
},
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '1',
status: '0',
},
{
name: '测试监测点',
item: '2022-01-01至2022-01-01',
type: '1',
status: '0',
},
],
}
},
created() {},
mounted() {
uni.createSelectorQuery()
.select('.navReport')
.boundingClientRect((rect) => {
//
// #ifdef H5
this.navHeight = rect.height + 60
// #endif
// #ifdef APP-PLUS
this.navHeight = rect.height + 10
// #endif
})
.exec()
},
methods: {
onClickItem(e) {
if (this.curTabs !== e.currentIndex) {
this.curTabs = e.currentIndex
}
},
scrolltolower() {
if (this.total != this.indexList.length) {
this.status = 'loading'
this.info()
} else {
this.status = 'noMore'
}
},
info() {
setTimeout(() => {
this.status = 'more'
}, 1000)
},
},
computed: {},
watch: {},
}
</script>
<style lang="scss">
.report {
overflow: hidden;
/deep/ .u-tabs__wrapper__nav {
background: #fff;
}
/deep/ .u-tabs__wrapper__nav__item {
flex: 1;
}
/deep/ .u-tabs__wrapper__nav__line {
left: 80rpx;
}
/deep/ .u-u-subsection {
width: 80% !important;
}
/deep/.tabsBox {
border-bottom: 1px solid #cccccc70;
.segmented-control {
height: 49px;
background-color: #fff;
}
.segmented-control__text {
font-size: 27rpx !important;
color: rgb(96, 98, 102);
}
.segmented-control__item--text {
font-weight: bold;
}
}
}
</style>

View File

@@ -1,91 +1,91 @@
<template>
<Cn-page :loading="loading">
<view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.startTime }}</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">基础信息</view>
<view class="mb5"> 设备名称{{ detail.equipmentName }}</view>
<view class="mb5"> 项目名称{{ detail.projectName }} </view>
<view class="mb5"> 工程名称{{ detail.engineeringName }} </view>
<view class="mb5"> 事件名称{{ detail.showName }}</view>
<view class="mb5" v-for="(item, textIndex) in detail.dataSet" :key="textIndex">
{{ item.showName + '' + (item.value == 3.1415926 ? '-' : item.value) + (item.unit || '') }}
</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">瞬时波形图</view>
<image
style="width: 100%"
:src="detail.instantPics"
mode="widthFix"
v-if="detail.instantPics"
@click="previewImage(detail.instantPics)"
/>
<text v-else>暂无</text>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">RMS波形图</view>
<image
style="width: 100%"
:src="detail.rmsPics"
mode="widthFix"
v-if="detail.rmsPics"
@click="previewImage(detail.rmsPics)"
/>
<text v-else>暂无</text>
</view>
</view>
</Cn-page>
</template>
<script>
import { updateStatus } from '@/common/api/message'
export default {
data() {
return {
loading: true,
detail: {},
}
},
onLoad(options) {
console.log(options.detail)
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.detail.rmsPics && (this.detail.rmsPics = this.$config.static + this.detail.rmsPics)
this.detail.instantPics && (this.detail.instantPics = this.$config.static + this.detail.instantPics)
this.loading = false
if (this.detail.status != 1) {
updateStatus({
eventIds: [this.detail.id],
})
}
},
methods: {
previewImage(url) {
uni.previewImage({
urls: [url],
})
},
},
}
</script>
<style lang="scss">
.detail {
padding: 20rpx 0;
.detail-content {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
font-size: 26rpx;
.detail-content-title {
font-size: 32rpx;
color: #111;
font-weight: 700;
}
}
}
</style>
<template>
<Cn-page :loading="loading">
<view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.startTime }}</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">基础信息</view>
<view class="mb5"> 设备名称{{ detail.equipmentName }}</view>
<view class="mb5"> 项目名称{{ detail.projectName }} </view>
<view class="mb5"> 工程名称{{ detail.engineeringName }} </view>
<view class="mb5"> 事件名称{{ detail.showName }}</view>
<view class="mb5" v-for="(item, textIndex) in detail.dataSet" :key="textIndex">
{{ item.showName + '' + (item.value == 3.1415926 ? '-' : item.value) + (item.unit || '') }}
</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">瞬时波形图</view>
<image
style="width: 100%"
:src="detail.instantPics"
mode="widthFix"
v-if="detail.instantPics"
@click="previewImage(detail.instantPics)"
/>
<text v-else>暂无</text>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">RMS波形图</view>
<image
style="width: 100%"
:src="detail.rmsPics"
mode="widthFix"
v-if="detail.rmsPics"
@click="previewImage(detail.rmsPics)"
/>
<text v-else>暂无</text>
</view>
</view>
</Cn-page>
</template>
<script>
import { updateStatus } from '@/common/api/message'
export default {
data() {
return {
loading: true,
detail: {},
}
},
onLoad(options) {
console.log(options.detail)
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.detail.rmsPics && (this.detail.rmsPics = this.$config.static + this.detail.rmsPics)
this.detail.instantPics && (this.detail.instantPics = this.$config.static + this.detail.instantPics)
this.loading = false
if (this.detail.status != 1) {
updateStatus({
eventIds: [this.detail.id],
})
}
},
methods: {
previewImage(url) {
uni.previewImage({
urls: [url],
})
},
},
}
</script>
<style lang="scss">
.detail {
padding: 20rpx 0;
.detail-content {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
font-size: 26rpx;
.detail-content-title {
font-size: 32rpx;
color: #111;
font-weight: 700;
}
}
}
</style>

126
pages/message1/alarm.vue Normal file
View File

@@ -0,0 +1,126 @@
<template>
<view style="position: relative">
<!-- 运行告警 -->
<!-- 卡片 -->
<view class="event-list" :style="{ height: 'calc(100vh - ' + (navHeight + 10) + 'px)', overflow: 'auto' }">
<!-- 循环渲染事件项 -->
<uni-card
class="event-item"
:class="item.type"
v-for="(item, index) in this.store.data"
:key="index"
@click="jump(item)"
>
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- 动态图标根据类型切换 -->
<uni-icons
custom-prefix="iconfont"
type="icon-terminal-box-fill"
size="22"
color="#FF0000"
></uni-icons>
<view class="badge1" v-if="item.isRead == 0"> </view>
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.date }}发生告警终端{{ item.warnNums }}</text>
</view>
</view>
<view class="event-action"> 🔍 </view>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else style="top: 20%"></Cn-empty>
</view>
</view>
</template>
<script>
import list from '@/common/js/list'
export default {
components: {},
props: {
navHeight: {
type: Number,
default: 0,
},
selectValue: {
type: Object,
// default: () => {},
},
},
mixins: [list],
data() {
return {
status: 'noMore', //more加载前 loading加载中 noMore加载后
}
},
mounted() {},
methods: {
init() {
this.store = this.DataSource('/cs-harmonic-boot/csAlarm/queryAlarmList')
this.store.params.pageSize = 10000
this.store.params.engineerId = this.selectValue.engineeringId
this.store.params.projectId = this.selectValue.projectId
this.store.params.devId = this.selectValue.deviceId
this.store.params.lineId = this.selectValue.lineId
this.store.params.time = this.selectValue.date
this.store.loadedCallback = () => {
this.loading = false
}
this.store.reload()
},
jump(item) {
let str = JSON.stringify(item).replace(/%/g, '百分比')
item.status = '1'
uni.navigateTo({ url: '/pages/message1/comp/alarmDetails?detail=' + encodeURIComponent(str) })
},
},
computed: {},
watch: {
selectValue: {
handler(val, oldVal) {
if (Object.keys(val).length === 0) return
this.init()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped>
@import './index.scss';
/* 列表容器 */
.event-list {
margin-top: 20rpx;
/* 头部:图标 + 信息 + 操作 */
.event-header {
margin-bottom: 0rpx;
}
.event-title {
margin-bottom: 0rpx;
}
/* 图标区域(按类型区分背景色) */
.event-icon {
width: 70rpx;
height: 70rpx;
background-color: #ff000020;
}
/* 信息区域 */
.event-info {
flex: 1;
}
}
</style>

231
pages/message1/comp/F47.vue Normal file
View File

@@ -0,0 +1,231 @@
<template>
<!-- ITIC -->
<view>
<l-echart v-if="status != 'loading'" ref="echartRef" @finished="initChart"></l-echart>
<uni-load-more v-else :status="status"></uni-load-more>
</view>
</template>
<script>
const echarts = require('../../../uni_modules/lime-echart/static/echarts.min')
export default {
components: {},
props: {
store: {
type: [Object],
},
},
data() {
return {
option: {
backgroundColor: '#fff',
grid: {
left: '10px',
right: '40rpx',
bottom: '40rpx',
top: '10px',
containLabel: true,
},
legend: {
data: ['分割线', '可容忍事件', '不可容忍事件'],
right: '10px',
bottom: '10px',
textStyle: {
fontSize: 10,
},
itemWidth: 10,
itemHeight: 10,
itemGap: 8,
padding: [5, 5, 5, 10],
},
yAxis: {
type: 'log',
min: '0.001',
max: '1000',
name: 's',
inverse: true,
axisLabel: {
rotate: -90,
},
splitLine: { show: false },
},
xAxis: {
type: 'value',
splitNumber: 10,
minInterval: 3,
position: 'top',
rotate: 90,
axisLabel: {
rotate: -90,
},
name: '%',
},
series: [
{
name: '分割线',
type: 'line',
data: [
[0, 0.05],
[50, 0.05],
[50, 0.2],
[70, 0.2],
[70, 0.5],
[80, 0.5],
[80, 10],
[80, 1000],
],
showSymbol: false,
tooltips: {
show: false,
},
color: '#DAA520',
},
{
name: '可容忍事件',
type: 'scatter',
symbol: 'circle',
// data: this.pointF,
data: [],
color: 'green',
},
{
name: '不可容忍事件',
type: 'scatter',
symbol: 'circle',
// data: this.pointFun,
data: [],
color: 'red',
},
],
},
status: 'loading',
echartRef: null,
pointF: [],
pointFun: [],
data: [],
}
},
mounted() {
// this.initChart()
// console.log('🚀 ~ props.data:', this.props.data)
},
methods: {
init() {},
async initChart() {
if (!this.$refs.echartRef) return
try {
this.echartRef = await this.$refs.echartRef.init(echarts)
this.bindChartClickEvent()
this.echartRef.setOption(this.option, true)
} catch (error) {
console.error('图表初始化失败:', error)
}
},
gongfunction() {
var standF = 0
var unstandF = 0
this.pointF = []
this.pointFun = []
var total = 0
total = this.data.length
if (total == 0) {
} else {
for (var i = 0; i < this.data.length; i++) {
var point = []
var xx = this.data[i].evtParamTm.replace(/s/g, '')
var yy = this.data[i].evtParamVVaDepth.replace(/%/g, '')
var time = this.data[i].startTime.replace('T', ' ')
point = [yy, xx, time, this.data[i]]
if (xx < 0.05) {
standF++
this.pointF.push({
value: point,
itemStyle: { normal: { color: 'green' } },
})
} else if (xx < 0.2) {
if (yy > 50) {
standF++
this.pointF.push({
value: point,
itemStyle: { normal: { color: 'green' } },
})
} else {
unstandF++
this.pointFun.push({
value: point,
itemStyle: { normal: { color: 'red' } },
})
}
} else if (xx < 0.5) {
if (yy > 70) {
standF++
this.pointF.push({
value: point,
itemStyle: { normal: { color: 'green' } },
})
} else {
unstandF++
this.pointFun.push({
value: point,
itemStyle: { normal: { color: 'red' } },
})
}
} else {
if (yy > 80) {
standF++
this.pointF.push({
value: point,
itemStyle: { normal: { color: 'green' } },
})
} else {
unstandF++
this.pointFun.push({
value: point,
itemStyle: { normal: { color: 'red' } },
})
}
}
}
}
this.option.series[1].data = this.pointF
this.option.series[2].data = this.pointFun
if (this.echartRef) {
this.echartRef.setOption(this.option, true)
} else {
this.initChart()
}
},
bindChartClickEvent() {
if (!this.echartRef) return
this.echartRef.on('click', (params) => {
console.log('🚀 ~ params:', params.value[3])
// 点击查看详情
let item = params.value[3]
let str = JSON.stringify(item).replace(/%/g, '百分比')
uni.navigateTo({ url: '/pages/message1/comp/transientDetails?detail=' + encodeURIComponent(str) })
})
},
},
computed: {},
watch: {
store: {
handler(val, oldVal) {
this.status = val.status
this.data = val.data
this.gongfunction()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,341 @@
<template>
<!-- ITIC -->
<view>
<l-echart v-if="status != 'loading'" ref="echartRef" @finished="initChart"></l-echart>
<uni-load-more v-else :status="status"></uni-load-more>
</view>
</template>
<script>
const echarts = require('../../../uni_modules/lime-echart/static/echarts.min')
export default {
components: {},
props: {
store: {
type: [Object],
},
},
data() {
return {
option: {
backgroundColor: '#fff',
grid: {
left: '10px',
right: '40rpx',
bottom: '40rpx',
top: '10px',
containLabel: true,
},
legend: {
data: ['上限', '下限', '可容忍事件', '不可容忍事件'],
right: '10px',
bottom: '10px',
textStyle: {
fontSize: 10,
},
itemWidth: 10,
itemHeight: 10,
itemGap: 8,
padding: [5, 5 ,5 ,10],
},
color: ['#FF8C00', '#00BFFF', 'green', 'red'],
yAxis: {
type: 'log',
min: '0.001',
max: '1000',
name: 's',
inverse: true,
axisLabel: {
rotate: -90,
},
splitLine: { show: false },
},
xAxis: {
type: 'value',
splitNumber: 10,
minInterval: 3,
position: 'top',
rotate: 90,
axisLabel: {
rotate: 90,
},
name: '%',
},
series: [
{
name: '上限',
type: 'line',
data: [
[200, 0.001],
[140, 0.003],
[120, 0.003],
[120, 0.5],
[110, 0.5],
[110, 10],
[110, 1000],
],
showSymbol: false,
tooltips: {
show: false,
},
color: '#FF8C00',
},
{
name: '下限',
type: 'line',
data: [
[0, 0.02],
[70, 0.02],
[70, 0.5],
[80, 0.5],
[80, 10],
[90, 10],
[90, 1000],
],
showSymbol: false,
tooltips: {
show: false,
},
color: '#00BFFF',
},
{
name: '可容忍事件',
type: 'scatter',
symbol: 'circle',
// data: this.pointI,
data: [],
color: 'green',
},
{
name: '不可容忍事件',
type: 'scatter',
symbol: 'circle',
// data: this.pointIun,
data: [],
color: 'red',
},
],
},
status: 'loading',
echartRef: null,
pointI: [],
pointIun: [],
data: [],
}
},
mounted() {
// this.initChart()
// console.log('🚀 ~ props.data:', this.props.data)
},
methods: {
init() {},
async initChart() {
if (!this.$refs.echartRef) return
try {
this.echartRef = await this.$refs.echartRef.init(echarts)
this.bindChartClickEvent()
this.echartRef.setOption(this.option, true)
} catch (error) {
console.error('图表初始化失败:', error)
}
},
// gongfunction() {
// var standI = 0
// var unstandI = 0
// this.pointI = []
// this.pointIun = []
// var total = 0
// total = this.data.length
// if (total == 0) {
// } else {
// for (var i = 0; i < this.data.length; i++) {
// var point = []
// var xx = this.data[i].evtParamTm.replace(/s/g, '')
// var yy = this.data[i].evtParamVVaDepth.replace(/%/g, '')
// var time = this.data[i].startTime.replace('T', ' ')
// point = [yy, xx, time, this.data[i]]
// if (xx <= 0.003) {
// var line = 0
// line = 230 - 30000 * xx
// if (yy > line) {
// unstandI++
// this.pointIun.push({
// value: point,
// itemStyle: { normal: { color: 'red' } },
// })
// } else {
// standI++
// this.pointI.push({
// value: point,
// itemStyle: { normal: { color: 'green' } },
// })
// }
// } else if (xx <= 0.02) {
// if (yy > 120) {
// unstandI++
// this.pointIun.push({
// value: point,
// itemStyle: { normal: { color: 'red' } },
// })
// } else {
// standI++
// this.pointI.push({
// value: point,
// itemStyle: { normal: { color: 'green' } },
// })
// }
// } else if (xx <= 0.5) {
// if (yy > 120 || yy < 70) {
// unstandI++
// this.pointIun.push({
// value: point,
// itemStyle: { normal: { color: 'red' } },
// })
// } else {
// standI++
// this.pointI.push({
// value: point,
// itemStyle: { normal: { color: 'green' } },
// })
// }
// } else if (xx <= 10) {
// if (yy > 110 || yy < 80) {
// unstandI++
// this.pointIun.push({
// value: point,
// itemStyle: { normal: { color: 'red' } },
// })
// } else {
// standI++
// this.pointI.push({
// value: point,
// itemStyle: { normal: { color: 'green' } },
// })
// }
// } else {
// if (yy > 110 || yy < 90) {
// unstandI++
// this.pointIun.push({
// value: point,
// itemStyle: { normal: { color: 'red' } },
// })
// } else {
// standI++
// this.pointI.push({
// value: point,
// itemStyle: { normal: { color: 'green' } },
// })
// }
// }
// }
// }
// this.option.series[2].data = this.pointI
// this.option.series[3].data = this.pointIun
// if (this.echartRef) {
// this.echartRef.setOption(this.option, true)
// } else {
// this.initChart()
// }
// },
gongfunction() {
// 初始化计数与数据数组
let normalCount = 0
let abnormalCount = 0
this.normalPoints = []
this.abnormalPoints = []
if (!this.data || this.data.length === 0) {
this.updateChartOption()
return
}
// 缓存长度,遍历数据
const len = this.data.length
for (let i = 0; i < len; i++) {
const item = this.data[i]
// 建议确认正则意图,/s/g 仅移除字母 s若去空格应为 /\s/g
const xx = parseFloat(item.evtParamTm.replace(/s/g, ''))
const yy = parseFloat(item.evtParamVVaDepth.replace(/%/g, ''))
const time = item.startTime.replace('T', ' ')
const pointData = [yy, xx, time, item]
const isNormal = this.checkPointStatus(xx, yy)
const pointObj = {
value: pointData,
itemStyle: { normal: { color: isNormal ? 'green' : 'red' } },
}
if (isNormal) {
normalCount++
this.normalPoints.push(pointObj)
} else {
abnormalCount++
this.abnormalPoints.push(pointObj)
}
}
this.updateChartOption()
},
// 提取判断逻辑为独立方法
checkPointStatus(xx, yy) {
if (xx <= 0.003) {
const line = 230 - 30000 * xx
return yy <= line
} else if (xx <= 0.02) {
return yy <= 120
} else if (xx <= 0.5) {
return yy > 70 && yy < 120
} else if (xx <= 10) {
return yy > 80 && yy < 110
} else {
return yy > 90 && yy < 110
}
},
updateChartOption() {
// 建议避免硬编码 series 索引,可通过 seriesName 查找
this.option.series[2].data = this.normalPoints
this.option.series[3].data = this.abnormalPoints
if (this.echartRef) {
this.echartRef.setOption(this.option, true)
} else {
this.initChart()
}
},
bindChartClickEvent() {
if (!this.echartRef) return
this.echartRef.on('click', (params) => {
console.log('🚀 ~ params:', params.value[3])
// 点击查看详情
let item = params.value[3]
let str = JSON.stringify(item).replace(/%/g, '百分比')
uni.navigateTo({ url: '/pages/message1/comp/transientDetails?detail=' + encodeURIComponent(str) })
})
},
},
computed: {},
watch: {
store: {
handler(val, oldVal) {
this.status = val.status
this.data = val.data
this.gongfunction()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,134 @@
<template>
<Cn-page :loading="loading">
<view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.date }}</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">终端告警列表</view>
<uni-collapse accordion>
<uni-collapse-item :title="item.devName" v-for="item in list">
<template v-slot:title>
<view class="collapseTop">
<view class="mb5 name"> {{ item.devName }}</view>
<view class="mb5 frequency">
<view class="mr20"> 告警次数: {{ item.warnCounts }} </view>
<view> 通讯中断: {{ item.interruptCounts }} </view>
</view>
</view>
</template>
<view>
<view class="mb10 ml12 frequency">
<view>项目名称{{ item.projectName }} </view></view
>
<view class="mb10 ml12 frequency">
<view>工程名称{{ item.engineeringName }}</view></view
>
<view class="mb10 ml12 frequency">
<view>通讯信息</view>
<view style="flex: 1">
<view v-if="item.interruptCounts == 0">通讯正常</view>
<view v-else>通讯中断{{ item.interruptCounts }}具体如下所示:</view
><view v-for="date in item.interruptDetails" class="mt15 textBox">{{
date
}}</view></view
></view
>
<view class="mb10 ml12 frequency">
<view>告警信息</view>
<view style="flex: 1">
<view v-if="item.warnCounts == 0">暂无终端告警信息</view>
<view v-else>终端告警{{ item.warnCounts }}具体如下所示:</view
><view v-for="val in item.warnDetails" class="mt15 textBox">
{{ val.warnEventTime + '发生' + val.warnEventDesc }}
</view></view
></view
>
</view>
</uni-collapse-item>
</uni-collapse>
</view>
</view>
</Cn-page>
</template>
<script>
import { updateStatus, queryAlarmDetail } from '@/common/api/message'
export default {
data() {
return {
loading: true,
detail: {},
limit: '',
accordionVal: '1',
list: [],
}
},
onLoad(options) {
this.loading = false
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.init()
if (this.detail.isRead != 1) {
updateStatus({
eventIds: [this.detail.eventId],
})
}
},
methods: {
init() {
this.loading = false
queryAlarmDetail({
devList: this.detail.devIds,
time: this.detail.date,
})
.then((res) => {
this.list = res.data
this.loading = false
})
.catch(() => {
this.loading = false
})
},
},
}
</script>
<style lang="scss">
.detail {
padding: 20rpx 0;
.detail-content {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
font-size: 28rpx;
.detail-content-title {
font-size: 32rpx;
color: #111;
font-weight: 700;
}
}
.collapseTop {
padding: 10rpx 0;
margin-left: 15px;
.name {
font-size: 26rpx;
font-weight: 700;
color: #333333;
}
}
.frequency {
display: flex;
font-size: 24rpx;
// color: #666666;
}
}
.textBox {
border-bottom: 1px solid #eee;
}
</style>

View File

@@ -0,0 +1,326 @@
<template>
<view class="preview-container">
<!-- 右上角关闭按钮 -->
<view class="close-btn" @click="close">
<text class="close-icon"></text>
</view>
<!-- 下载按钮 -->
<view class="download-btn" @click="downloadImage">
<text class="download-icon"></text>
</view>
<!-- 图片预览区域使用movable-area和movable-view实现缩放移动 -->
<movable-area class="movable-area" :style="{ width: '100vw', height: '100vh' }">
<movable-view
class="movable-view"
direction="all"
:scale="true"
:scale-min="0.5"
:scale-max="3"
:scale-value="scaleValue"
@scale="onScale"
:x="x"
:y="y"
:style="{
width: rotatedWidth + 'px',
height: rotatedHeight + 'px'
}"
>
<view
class="image-wrapper"
:style="{
width: imgWidth + 'px',
height: imgHeight + 'px',
transform: 'rotate(90deg)',
transformOrigin: 'center center'
}"
>
<image
:src="imageUrl"
class="preview-img"
mode="aspectFill"
:style="{
width: imgWidth + 'px',
height: imgHeight + 'px'
}"
@load="onImageLoad"
></image>
</view>
</movable-view>
</movable-area>
<!-- 图片信息提示 -->
<view class="scale-tip" v-if="showScaleTip">
当前缩放: {{ Math.round(scaleValue * 100) }}%
</view>
</view>
</template>
<script>
export default {
onLoad(options) {
this.imageUrl = decodeURIComponent(options.url) // 接收传递的图片URL需要解码
},
data() {
return {
imageUrl: '',
// 缩放相关
scaleValue: 1,
x: 0,
y: 0,
// 图片原始尺寸
imgWidth: 0,
imgHeight: 0,
// 旋转后的尺寸(交换宽高)
rotatedWidth: 0,
rotatedHeight: 0,
// 提示显示控制
showScaleTip: false,
tipTimer: null,
// 屏幕尺寸
windowWidth: 0,
windowHeight: 0
}
},
mounted() {
// 获取屏幕尺寸
const systemInfo = uni.getSystemInfoSync();
this.windowWidth = systemInfo.windowWidth;
this.windowHeight = systemInfo.windowHeight;
},
methods: {
// 图片加载完成后获取尺寸
onImageLoad(e) {
const { width, height } = e.detail;
// 保存原始尺寸
this.imgWidth = width;
this.imgHeight = height;
// 旋转90度后宽度和高度互换
this.rotatedWidth = height; // 旋转后宽度 = 原高度
this.rotatedHeight = width; // 旋转后高度 = 原宽度
// 计算初始位置居中
this.resetPosition();
console.log('图片加载:', {width, height}, '旋转后:', {rotatedWidth: this.rotatedWidth, rotatedHeight: this.rotatedHeight});
},
// 重置位置到中心
resetPosition() {
// 计算居中的偏移量
this.x = (this.windowWidth - this.rotatedWidth) / 2;
this.y = (this.windowHeight - this.rotatedHeight) / 2;
console.log('重置位置:', {x: this.x, y: this.y, windowWidth: this.windowWidth, windowHeight: this.windowHeight});
},
// 缩放事件处理
onScale(e) {
this.scaleValue = e.detail.scale;
// 显示缩放提示
this.showScaleTip = true;
if (this.tipTimer) {
clearTimeout(this.tipTimer);
}
this.tipTimer = setTimeout(() => {
this.showScaleTip = false;
}, 1500);
},
// 关闭预览
close() {
uni.navigateBack();
},
// 下载图片
downloadImage() {
uni.showLoading({
title: '下载中...'
});
// 先获取图片信息(如果是网络图片需要先下载)
uni.downloadFile({
url: this.imageUrl,
success: (res) => {
if (res.statusCode === 200) {
// 保存图片到相册
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
uni.hideLoading();
console.error('保存失败', err);
// 处理用户拒绝权限的情况
if (err.errMsg.includes('auth deny')) {
uni.showModal({
title: '提示',
content: '需要您授权保存图片到相册',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (settingRes) => {
console.log('打开设置页面', settingRes);
}
});
}
}
});
} else {
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
}
});
} else {
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (err) => {
uni.hideLoading();
console.error('下载失败', err);
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
});
},
// 双击重置缩放和位置(可选功能)
onDoubleTap() {
this.scaleValue = 1;
this.resetPosition();
}
},
// 页面返回前清理定时器
onUnload() {
if (this.tipTimer) {
clearTimeout(this.tipTimer);
}
}
}
</script>
<style lang="scss" scoped>
.preview-container {
position: relative;
width: 100vw;
height: 100vh;
background: #000;
overflow: hidden;
}
.movable-area {
width: 100vw;
height: 100vh;
background: #000;
}
.movable-view {
display: flex;
align-items: center;
justify-content: center;
}
.image-wrapper {
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s ease;
}
.preview-img {
display: block;
}
// 关闭按钮
.close-btn {
position: fixed;
top: 30rpx;
right: 30rpx;
width: 60rpx;
height: 60rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
.close-icon {
color: #fff;
font-size: 40rpx;
line-height: 1;
}
}
// 下载按钮
.download-btn {
position: fixed;
top: 30rpx;
right: 110rpx;
width: 60rpx;
height: 60rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
.download-icon {
color: #fff;
font-size: 40rpx;
line-height: 1;
}
}
// 缩放提示
.scale-tip {
position: fixed;
bottom: 100rpx;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.6);
color: #fff;
padding: 10rpx 30rpx;
border-radius: 40rpx;
font-size: 28rpx;
z-index: 1000;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
animation: fadeInOut 1.5s ease;
}
@keyframes fadeInOut {
0% { opacity: 0; }
15% { opacity: 1; }
85% { opacity: 1; }
100% { opacity: 0; }
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<Cn-page :loading="loading">
<view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.statisticsDate }}</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">基础信息</view>
<view class="mb5"> 监测点名称{{ detail.lineName }}</view>
<view class="mb5"> 设备名称{{ detail.devName }} </view>
<view class="mb5"> 项目名称{{ detail.projectName }} </view>
<view class="mb5"> 工程名称{{ detail.engineeringName }} </view>
<view class="mb5" style="display: flex">
越限详情
<view style="flex: 1">{{ detail.overLimitDesc }}</view></view
>
</view>
<view class="detail-content">
<view class="detail-content-title mb20"
>指标越限详情<text class="prompt">仅显示较为严重的10次</text></view
>
<uni-collapse accordion>
<uni-collapse-item :title="item.targetName" v-for="item in list">
<view class="data-table">
<view class="table-header">
<text>时间</text>
<text>数据类型</text>
<text v-if="!item.harmDetailList[0].hasT">A相</text>
<text v-if="!item.harmDetailList[0].hasT">B相</text>
<text v-if="!item.harmDetailList[0].hasT">C相</text>
<text v-if="item.harmDetailList[0].hasT">总相</text>
<text>限值</text>
</view>
<view class="table-row" v-for="value in item.harmDetailList">
<text>{{ value.statisticsTime }}</text>
<text>{{ value.valueType }}</text>
<text v-if="!value.hasT">{{ value.dataA }}</text>
<text v-if="!value.hasT">{{ value.dataB }}</text>
<text v-if="!value.hasT">{{ value.dataC }}</text>
<text v-if="value.hasT">{{ value.dataT }}</text>
<text>{{ value.overLimitData }}</text>
</view>
</view>
</uni-collapse-item>
</uni-collapse>
</view>
</view>
</Cn-page>
</template>
<script>
import { updateStatus, queryHarmonicDetail } from '@/common/api/message'
export default {
data() {
return {
loading: true,
detail: {},
list: [],
}
},
onLoad(options) {
// console.log(options.detail)
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.init()
if (this.detail.isRead != 1) {
updateStatus({
eventIds: [this.detail.eventId],
})
}
},
methods: {
init() {
queryHarmonicDetail({
lineId: this.detail.lineId,
time: this.detail.statisticsDate,
})
.then((res) => {
this.list = res.data
this.loading = false
})
.catch(() => {
this.loading = false
})
// }
},
},
}
</script>
<style lang="scss">
.detail {
padding: 20rpx 0;
.detail-content {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
font-size: 28rpx;
.detail-content-title {
font-size: 32rpx;
color: #111;
font-weight: 700;
}
}
.limit {
display: flex;
align-items: center;
justify-content: end;
width: 450rpx;
margin-left: auto;
}
.prompt {
font-size: 24rpx;
color: #111;
font-weight: 500;
}
}
.data-table {
margin-top: 20rpx;
background-color: #fff;
overflow: hidden;
.table-header,
.table-row {
display: flex;
justify-content: space-between;
padding: 20rpx 0rpx;
border-bottom: 1rpx solid #eee;
text {
flex: 1;
text-align: center;
font-size: 24rpx;
// color: #333;
&:first-child {
// text-align: left;
flex: 1.2;
}
}
}
.table-header {
padding: 0rpx;
}
}
/deep/ .uni-collapse-item__title-text{
font-weight: 700;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<Cn-page :loading="loading">
<view class="detail" slot="body">
<view class="detail-content" style="font-size: 32rpx">
<!-- <view class="detail-content-title mb20">发生时间</view> -->
<view>{{ detail.startTime }}</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">基础信息</view>
<view class="mb5"> 设备名称{{ detail.equipmentName }}</view>
<view class="mb5"> 项目名称{{ detail.projectName }} </view>
<view class="mb5"> 工程名称{{ detail.engineeringName }} </view>
<view class="mb5"> 暂态类型{{ detail.showName }}</view>
<view class="mb5" v-for="(item, textIndex) in detail.dataSet" :key="textIndex">
{{ item.showName + '' + (item.value == 3.1415926 ? '-' : item.value) + (item.unit || '') }}
</view>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">瞬时波形图</view>
<image
style="width: 100%"
:src="detail.instantPics"
mode="widthFix"
v-if="detail.instantPics"
@click="previewImage(detail.instantPics)"
/>
<text v-else>暂无</text>
</view>
<view class="detail-content">
<view class="detail-content-title mb20">RMS波形图</view>
<image
style="width: 100%"
:src="detail.rmsPics"
mode="widthFix"
v-if="detail.rmsPics"
@click="previewImage(detail.rmsPics)"
/>
<text v-else>暂无</text>
</view>
</view>
</Cn-page>
</template>
<script>
import { updateStatus } from '@/common/api/message'
export default {
data() {
return {
loading: true,
detail: {},
}
},
onLoad(options) {
// console.log(options.detail)
this.detail = JSON.parse(decodeURIComponent(options.detail).replace(/百分比/g, '%'))
this.detail.rmsPics && (this.detail.rmsPics = this.$config.static + this.detail.rmsPics)
this.detail.instantPics && (this.detail.instantPics = this.$config.static + this.detail.instantPics)
this.loading = false
if (this.detail.status != 1) {
updateStatus({
eventIds: [this.detail.id],
})
}
},
methods: {
previewImage(url) {
// uni.previewImage({
// urls: [url],
// })
uni.navigateTo({
url: `/pages/message1/comp/preview?url=${encodeURIComponent(url)}`,
})
},
},
}
</script>
<style lang="scss">
.detail {
padding: 20rpx 0;
.detail-content {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
font-size: 28rpx;
.detail-content-title {
font-size: 32rpx;
color: #111;
font-weight: 700;
}
}
}
</style>

182
pages/message1/index.scss Normal file
View File

@@ -0,0 +1,182 @@
/* 整体容器:横向排列,间距均匀 */
.statistics {
display: flex;
gap: 20rpx; /* 盒子之间的间距 */
/* 通用盒子样式 */
.box {
flex: 1; /* 四个盒子等分宽度 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 130rpx;
background-color: #ffffff;
border-radius: 16rpx;
}
/* 第一个盒子的特殊样式(蓝色背景) */
.box:first-child {
flex: 1.7;
background-color: $uni-theme-color;
color: #ffffff;
}
/* 数字样式 */
.num {
font-size: 38rpx;
font-weight: 600;
line-height: 1.2;
margin-bottom: 8rpx;
}
/* 标签文字样式 */
.label {
font-size: 24rpx;
color: inherit; /* 继承父元素颜色,适配蓝色背景 */
}
}
/* 列表容器 */
.event-list {
background-color: #f5f7fa;
box-sizing: border-box;
/* 通用项样式 */
/deep/ .uni-card:first-of-type {
margin-top: 0 !important;
}
/deep/ .uni-card {
padding: 0 !important;
}
/* 头部:图标 + 信息 + 操作 */
.event-header {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
/* 图标区域(按类型区分背景色) */
.event-icon {
position: relative;
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
.badge1 {
position: absolute;
top: -10rpx;
right: -10rpx;
width: 20rpx;
height: 20rpx;
background-color: #ff3b30; /* 红色徽章 */
border-radius: 20rpx;
}
/* 电压暂降 - 蓝色系 */
.sag .event-icon {
background-color: #2563eb20;
}
/* 电压暂升 - 橙色系 */
.swell .event-icon {
background-color: #e6a23c20;
}
.interrupt .event-icon {
background-color: #90939920;
}
.event-icon image {
width: 48rpx;
height: 48rpx;
}
/* 信息区域 */
.event-info {
flex: 1;
}
.event-title {
display: flex;
align-items: center;
margin-bottom: 5rpx;
flex-wrap: wrap; /* 适配小屏,防止文字溢出 */
}
.event-id {
font-size: 15px;
font-weight: 700;
color: #333333;
margin-right: 16rpx;
}
/* 标签样式(按类型区分) */
.event-tag {
font-size: 20rpx;
padding: 0rpx 10rpx;
border-radius: 8rpx;
color: #ffffff;
height: 38rpx;
}
.sag-tag {
background-color: #ecf5ff;
color: #2563eb;
}
.swell-tag {
background-color: #fdf6ec;
color: #e6a23c;
}
.interrupt-tag {
background-color: #f4f4f5;
color: #909399;
}
/* 描述文本 */
.event-desc {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.event-desc text {
font-size: 26rpx;
color: #666666;
line-height: 1.2;
}
/* 操作按钮 */
.event-action {
display: flex;
justify-content: center;
align-items: center;
color: #999999;
font-size: 40rpx;
/* 点击反馈 */
touch-action: manipulation;
}
.event-action:active {
color: #376cf3;
transform: scale(0.95);
}
/* 详情文本 */
.event-detail {
font-size: 24rpx;
color: #666666;
line-height: 1.5;
padding-top: 10rpx;
border-top: 1rpx solid #f0f0f0;
word-wrap: break-word; /* 自动换行,防止长文本溢出 */
}
}
.smallLabel {
padding: 0 20rpx 20rpx 20rpx;
display: flex;
align-items: center;
justify-content: flex-end;
.segmented-control {
flex: 1;
margin-right: 20rpx;
height: 58rpx;
}
.uni-input {
font-size: 24rpx;
color: #2563eb;
margin-right: 10rpx;
}
}

127
pages/message1/run.vue Normal file
View File

@@ -0,0 +1,127 @@
<template>
<view style="position: relative">
<!-- 运行事件 -->
<!-- 卡片 -->
<view
class="event-list" :style="{ height: 'calc(100vh - ' + (navHeight + 10) + 'px)', overflow: 'auto' }">
<!-- 循环渲染事件项 -->
<uni-card
class="event-item"
:class="item.type"
v-for="(item, index) in store.data"
:key="index"
@click="jump(item)"
>
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- 动态图标根据类型切换 -->
<uni-icons custom-prefix="iconfont" type="icon-shebei" size="30" color="#10B981"></uni-icons>
<view class="badge1" v-if="item.status == 0"> </view>
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.equipmentName }}</text>
</view>
<view class="event-desc">
<text>工程名称{{ item.engineeringName }}</text>
<text>项目名称{{ item.projectName }}</text>
</view>
</view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text> {{ item.startTime }}发生{{ item.showName }} </text>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else style="top: 20%"></Cn-empty>
</view>
</view>
</template>
<script>
import list from '@/common/js/list'
import { updateStatus } from '@/common/api/message'
export default {
components: {},
props: {
navHeight: {
type: Number,
default: 0,
},
selectValue: {
type: Object,
// default: () => {},
},
},
mixins: [list],
data() {
return {}
},
mounted() {},
methods: {
// 查詢
init() {
this.store = this.DataSource('/cs-harmonic-boot/eventUser/queryEventpage')
this.store.params.type = 2
this.store.params.pageSize = 10000
this.store.params.sortField = this.sort
this.store.params.engineeringid = this.selectValue.engineeringId
this.store.params.projectId = this.selectValue.projectId
this.store.params.deviceId = this.selectValue.deviceId
this.store.params.lineId = this.selectValue.lineId
this.store.params.startTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).firstDay
this.store.params.endTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).lastDay
this.store.loadedCallback = () => {
this.loading = false
}
this.store.reload()
},
jump(item) {
if (item.status != '1') {
item.status = '1'
updateStatus({
eventIds: [item.id],
})
this.$emit('getDevCount')
}
},
},
computed: {},
watch: {
selectValue: {
handler(val, oldVal) {
if (Object.keys(val).length === 0) return
this.init()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped>
@import './index.scss';
/* 列表容器 */
.event-list {
margin-top: 20rpx;
/* 头部:图标 + 信息 + 操作 */
/* 图标区域(按类型区分背景色) */
.event-icon {
width: 90rpx;
height: 90rpx;
background-color: #10b98120;
}
}
</style>

View File

@@ -0,0 +1,174 @@
<template>
<view style="position: relative">
<!-- 稳态 -->
<view class="transientBox">
<view class="statistics pd20">
<view class="box" v-for="item in list">
<text class="num">{{ item.value }}</text>
<text class="label">{{ item.label }}</text>
</view>
</view>
</view>
<!-- 卡片 -->
<view
class="event-list" :style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }">
<!-- 循环渲染事件项 -->
<uni-card
class="event-item"
:class="item.type"
v-for="(item, index) in store.data"
:key="index"
@click="jump(item)"
>
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- 动态图标根据类型切换 -->
<uni-icons
custom-prefix="iconfont"
type="icon-kouanjiancedian"
size="40"
color="#E6A23C"
></uni-icons>
<view class="badge1" v-if="item.isRead == 0"> </view>
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.lineName }}</text>
</view>
<view class="event-desc">
<text>工程名称{{ item.engineeringName }}</text>
<text>项目名称{{ item.projectName }}</text>
<text>设备名称{{ item.devName }}</text>
<text>统计日期{{ item.statisticsDate }}</text>
</view>
</view>
<view class="event-action"> 🔍 </view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text> {{ item.overLimitDesc }} </text>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else style="top: 20%"></Cn-empty>
</view>
</view>
</template>
<script>
import list from '@/common/js/list'
export default {
components: {},
props: {
navHeight: {
type: Number,
default: 0,
},
selectValue: {
type: Object,
// default: () => {},
},
},
mixins: [list],
data() {
return {
height: 0,
list: [
{ value: 0, label: '稳态数量' },
{ value: 0, label: '越限天数' },
{ value: 0, label: '越限测点数' },
],
status: 'noMore', //more加载前 loading加载中 noMore加载后
}
},
mounted() {
uni.createSelectorQuery()
.select('.transientBox')
.boundingClientRect((rect) => {
//
// #ifdef H5
this.height = rect.height
// #endif
// #ifdef APP-PLUS
this.height = rect.height
// #endif
})
.exec()
},
methods: {
// "devId": "",
// "engineerId": "",
// "lineId": "",
// "projectId": "",
// "time": ""
// 查詢
init() {
this.store = this.DataSource('/cs-harmonic-boot/csHarmonic/queryHarmonicList')
this.store.params.pageSize = 10000
this.store.params.engineerId = this.selectValue.engineeringId
this.store.params.projectId = this.selectValue.projectId
this.store.params.devId = this.selectValue.deviceId
this.store.params.lineId = this.selectValue.lineId
this.store.params.time = this.selectValue.date
this.store.loadedCallback = () => {
this.list[0].value = this.store.copyData.harmonicNums
this.list[1].value = this.store.copyData.overDays
this.list[2].value = this.store.copyData.overLineNums
this.loading = false
}
this.store.reload()
},
jump(item) {
let str = JSON.stringify(item).replace(/%/g, '百分比')
item.status = '1'
uni.navigateTo({ url: '/pages/message1/comp/steadyStateDetails?detail=' + encodeURIComponent(str) })
},
},
computed: {},
watch: {
selectValue: {
handler(val, oldVal) {
if (Object.keys(val).length === 0) return
this.init()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped>
@import './index.scss';
.box:first-child {
flex: 1.3 !important;
}
/* 列表容器 */
.event-list {
/* 头部:图标 + 信息 + 操作 */
.event-header {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
/* 图标区域(按类型区分背景色) */
.event-icon {
background-color: #e6a23c20;
}
.event-tags {
font-size: 24rpx;
}
}
</style>

View File

@@ -0,0 +1,251 @@
<template>
<view style="position: relative">
<!-- 暂态 -->
<view class="transientBox">
<view class="statistics pd20">
<view class="box" v-for="item in list" @click="filterValue = item.key">
<!-- <text class="num">{{ item.value }}</text> -->
<text class="num">{{
store &&
store.data &&
store.data.filter((k) => (item.key == '' ? item : k.showName == item.key)).length
}}</text>
<text class="label">{{ item.label }}</text>
</view>
</view>
<view class="smallLabel">
<uni-segmented-control
:current="curSub"
active-color="#376cf3"
:values="subsectionList"
@clickItem="sectionChange"
v-if="subsectionList.length > 1"
/>
<view style="width: 180rpx">
<picker @change="bindPickerChange" :value="sort" :range="array" v-if="curSub == 0">
<view class="uni-input"
>{{ array[sort] }}排序
<uni-icons
custom-prefix="iconfont"
type="icon-paixu1"
size="10"
color="#2563EB"
></uni-icons>
</view>
</picker>
</view>
</view>
</view>
<!-- 卡片 -->
<view
class="event-list"
v-if="curSub == 0"
:style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }"
>
<!-- 循环渲染事件项 -->
<uni-card
class="event-item"
:class="judgment(item.showName)"
v-for="(item, index) in (store.data || []).filter((k) =>
filterValue == '' ? k : k.showName == filterValue,
)"
:key="index"
@click="jump(item)"
>
<!-- 头部:图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- 动态图标:根据类型切换 -->
<uni-icons
:custom-prefix="judgment(item.showName) == 'interrupt' ? 'custom-icon' : 'iconfont'"
:type="
judgment(item.showName) == 'sag'
? 'icon-xiajiang'
: judgment(item.showName) == 'swell'
? 'icon-shangsheng'
: 'minus'
"
:color="
judgment(item.showName) == 'sag'
? '#2563eb '
: judgment(item.showName) == 'swell'
? '#e6a23c'
: '#909399'
"
:size="judgment(item.showName) == 'interrupt' ? '50' : '25'"
></uni-icons>
<!-- 0未读 1已读 -->
<view class="badge1" v-if="item.status == 0"> </view>
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.equipmentName }}</text>
<text class="event-tag" :class="`${judgment(item.showName)}-tag`">{{ item.showName }}</text>
</view>
<view class="event-desc">
<text>工程名称:{{ item.engineeringName }}</text>
<text>项目名称:{{ item.projectName }}</text>
<text>监测点名称:{{ item.lineName }}</text>
</view>
</view>
<view class="event-action"> 🔍 </view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<text>
发生时间:{{ item.startTime }},幅值:{{ item.evtParamVVaDepth }},持续时间:{{
item.evtParamTm
}},相别:{{ item.evtParamPhase }}
</text>
</view>
</uni-card>
<uni-load-more
v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"
></uni-load-more>
<Cn-empty v-else style="top: 20%"></Cn-empty>
</view>
<!-- ITIC 列表 -->
<ITIC
v-if="subsectionList[curSub] == 'ITIC'"
:store="store"
:style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }"
></ITIC>
<!-- F47 列表 -->
<F47
v-if="subsectionList[curSub] == 'F47'"
:store="store"
:style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }"
></F47>
</view>
</template>
<script>
import list from '@/common/js/list'
import { queryUserPushConfig } from '@/common/api/mine'
import ITIC from './comp/ITIC.vue'
import F47 from './comp/F47.vue'
export default {
components: { ITIC, F47 },
props: {
navHeight: {
type: Number,
default: 0,
},
selectValue: {
type: Object,
// default: () => {},
},
},
mixins: [list],
data() {
return {
height: 0,
filterValue: '',
list: [
{ value: 0, label: '暂态数量', key: '' },
{ value: 0, label: '暂降', key: '电压暂降' },
{ value: 0, label: '中断', key: '电压中断' },
{ value: 0, label: '暂升', key: '电压暂升' },
],
curSub: 0,
subsectionList: [], //'列表', 'ITIC', 'F47'
status: 'noMore', //more加载前 loading加载中 noMore加载后
sort: 0,
// config: {},
array: ['发生时间', '暂降深度', '持续时间'],
}
},
mounted() {
uni.createSelectorQuery()
.select('.transientBox')
.boundingClientRect((rect) => {
//
// #ifdef H5
this.height = rect?.height + 12 || 0
// #endif
// #ifdef APP-PLUS
this.height = rect?.height + 12 || 0
// #endif
})
.exec()
},
methods: {
// 查詢
init() {
this.store = this.DataSource('/cs-harmonic-boot/eventUser/queryEventpage')
this.store.params.type = 0
this.store.params.pageSize = 10000
this.store.params.sortField = this.sort
this.store.params.engineeringid = this.selectValue.engineeringId
this.store.params.projectId = this.selectValue.projectId
this.store.params.deviceId = this.selectValue.deviceId
this.store.params.lineId = this.selectValue.lineId
this.store.params.startTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).firstDay
this.store.params.endTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).lastDay
this.store.loadedCallback = () => {
this.getConfig()
this.filterValue = ''
this.loading = false
}
this.store.reload()
},
getConfig() {
queryUserPushConfig().then((res) => {
// this.config = res.data
this.subsectionList = [
'列表',
res.data.iticFunction == 1 ? 'ITIC' : '',
res.data.f47Function == 1 ? 'F47' : '',
].filter((item) => item)
})
},
judgment(val) {
switch (val) {
case '电压暂降':
return 'sag'
case '电压暂升':
return 'swell'
case '电压中断':
return 'interrupt'
}
},
// 点击查看详情
jump(item) {
let str = JSON.stringify(item).replace(/%/g, '百分比')
item.status = '1'
uni.navigateTo({ url: '/pages/message1/comp/transientDetails?detail=' + encodeURIComponent(str) })
},
// 切换排序
bindPickerChange(e) {
this.sort = e.detail.value
this.init()
},
sectionChange(e) {
this.curSub = e.currentIndex
},
},
onShow() {
this.curSub = 0
},
computed: {},
watch: {
selectValue: {
handler(val, oldVal) {
if (Object.keys(val).length === 0) return
this.init()
},
deep: true,
immediate: true,
},
},
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,125 @@
<template>
<Cn-page :loading="loading">
<view class="mine" slot="body">
<view class="mine-nav" style="margin-top: 20rpx">
<view class="mine-nav-label">ITIC</view>
<switch
style="transform: scale(0.8)"
color="#376cf3"
@change="change('iticFunction')"
:checked="config.iticFunction === 1"
/>
</view>
<view class="mine-nav" style="border-bottom: none">
<view class="mine-nav-label">F47</view>
<switch
style="transform: scale(0.8)"
color="#376cf3"
@change="change('f47Function')"
:checked="config.f47Function === 1"
/>
</view>
</view>
</Cn-page>
</template>
<script>
import { queryUserPushConfig, updatePushConfig } from '@/common/api/mine'
export default {
data() {
return {
loading: true,
config: {},
}
},
onLoad() {
this.init()
},
methods: {
init() {
queryUserPushConfig().then((res) => {
this.config = res.data
this.loading = false
})
},
change(type) {
this.config[type] = this.config[type] === 1 ? 0 : 1
updatePushConfig(this.config).then((res) => {
let str = ''
switch (type) {
case 'iticFunction':
str = 'ITIC'
break
case 'f47Function':
str = 'F47'
break
}
this.$util.toast(`${str}配置${this.config[type] === 1 ? '开启' : '关闭'}成功`)
})
},
},
onLoad() {
this.init()
},
}
</script>
<style lang="scss">
.mine {
.title {
padding: 0 20rpx;
font-size: 28rpx;
font-weight: 500;
}
.mine-header {
padding: 200rpx 34rpx 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 8rpx #e7e7e74c;
.mine-header-head {
margin-right: 30rpx;
height: 128rpx;
width: 128rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
.mine-header-name {
margin-right: 30rpx;
flex: 1;
font-size: 36rpx;
color: #111;
font-weight: 700;
}
}
.mine-nav {
padding: 34rpx;
display: flex;
align-items: center;
background: $uni-theme-white;
border-bottom: 1rpx solid #e8e8e8;
&-icon {
margin-right: 30rpx;
height: 44rpx;
width: 44rpx;
border-radius: $uni-theme-radius;
overflow: hidden;
}
&-label {
margin-right: 30rpx;
flex: 1;
font-size: 28rpx;
color: #111;
}
}
}
</style>

View File

@@ -1,57 +1,57 @@
<template>
<Cn-page :loading='loading'>
<view slot='body'>
<view class='index'>
<view class="content device" :style="{ minHeight: minHeight }">
<uni-card :title="item.name" :sub-title="item.project" :extra="item.type" padding="0"
v-for="(item, index) in deviceList" :key="index" thumbnail="/static/device.png">
<!-- <text>{{ item.project }} {{ item.type }}</text> -->
</uni-card>
<uni-load-more status="nomore"></uni-load-more>
</view>
</view>
</view>
</Cn-page>
</template>
<script>
export default {
data () {
return {
loading: false,
deviceList: [
{
name: '设备APF-1',
des: '设备描述1',
type: '监测',
project: 'XXX项目1',
},
{
name: '设备APF-2',
des: '设备描述1',
type: '监测',
project: 'XXX项目1',
},
{
name: '设备APF-3',
des: '设备描述2',
type: '用能',
project: 'XXX项目2'
},
{
name: '设备DVR-1',
des: '设备描述3',
type: '监测',
project: 'XXX项目3'
}
]
}
},
methods: {
}
}
</script>
<style lang='scss'>
.index {
padding: 20rpx 0;
}
</style>
<template>
<Cn-page :loading='loading'>
<view slot='body'>
<view class='index'>
<view class="content device" :style="{ minHeight: minHeight }">
<uni-card :title="item.name" :sub-title="item.project" :extra="item.type" padding="0"
v-for="(item, index) in deviceList" :key="index" thumbnail="/static/device.png">
<!-- <text>{{ item.project }} {{ item.type }}</text> -->
</uni-card>
<!-- <uni-load-more status="nomore"></uni-load-more> -->
</view>
</view>
</view>
</Cn-page>
</template>
<script>
export default {
data () {
return {
loading: false,
deviceList: [
{
name: '设备APF-1',
des: '设备描述1',
type: '监测',
project: 'XXX项目1',
},
{
name: '设备APF-2',
des: '设备描述1',
type: '监测',
project: 'XXX项目1',
},
{
name: '设备APF-3',
des: '设备描述2',
type: '用能',
project: 'XXX项目2'
},
{
name: '设备DVR-1',
des: '设备描述3',
type: '监测',
project: 'XXX项目3'
}
]
}
},
methods: {
}
}
</script>
<style lang='scss'>
.index {
padding: 20rpx 0;
}
</style>

BIN
static/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,61 @@
@font-face {
font-family: 'iconfont'; /* Project id 5126335 */
src: url('/static/iconfont/iconfont.ttf') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-zaixianjianceshebei:before {
content: "\e683";
}
.icon-shebei1:before {
content: "\e74b";
}
.icon-shebei2:before {
content: "\e68c";
}
.icon-paixu1:before {
content: "\e60c";
}
.icon-gongcheng:before {
content: "\e61d";
}
.icon-zhongduan1:before {
content: "\e6c2";
}
.icon-shangsheng:before {
content: "\e6cd";
}
.icon-xiajiang:before {
content: "\e799";
}
.icon-shebei:before {
content: "\e665";
}
.icon-zhongduan:before {
content: "\e6cf";
}
.icon-terminal-box-fill:before {
content: "\e776";
}
.icon-kouanjiancedian:before {
content: "\e672";
}

Binary file not shown.

BIN
static/reportIco.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
static/reportIco1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

1
static/rotate_left.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742364146698" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10793" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 992A480 480 0 0 1 32 512a32 32 0 0 1 64 0 415.36 415.36 0 1 0 64-224h121.6a32 32 0 0 1 0 64h-192A32 32 0 0 1 57.6 320V128a32 32 0 1 1 64 0v104.96A480 480 0 1 1 512 992z" fill="#ffffff" p-id="10794"></path></svg>

After

Width:  |  Height:  |  Size: 550 B

1
static/rotate_right.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742364153829" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11007" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 992a480 480 0 1 1 390.4-759.04V128a32 32 0 0 1 64 0v192a32 32 0 0 1-32 32h-192a32 32 0 0 1 0-64h120.32a416 416 0 1 0 64 224 32 32 0 0 1 64 0A480 480 0 0 1 512 992z" fill="#ffffff" p-id="11008"></path></svg>

After

Width:  |  Height:  |  Size: 545 B

1
static/zoom_in.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742365851977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1652" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 1024C229.686652 1024 0 794.313348 0 512S229.686652 0 512 0s512 229.686652 512 512-229.686652 512-512 512z m0-967.541933C260.809348 56.458067 56.458067 260.809348 56.458067 512S260.809348 967.541933 512 967.541933 967.541933 763.190652 967.541933 512 763.190652 56.458067 512 56.458067z" fill="#ffffff" p-id="1653"></path><path d="M221.73627 483.776719h580.52746v56.458068H221.73627z" fill="#ffffff" p-id="1654"></path><path d="M483.776719 802.26373V221.73627h56.458068v580.52746z" fill="#ffffff" p-id="1655"></path></svg>

After

Width:  |  Height:  |  Size: 859 B

1
static/zoom_out.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742365916587" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2693" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 0.006C229.233 0.006 0.006 229.233 0.006 512c0 282.766 229.228 511.993 511.994 511.993S1023.993 794.765 1023.993 512C1023.993 229.233 794.766 0.006 512 0.006z m319.607 831.601c-41.53 41.529-89.874 74.128-143.689 96.891-55.686 23.553-114.873 35.495-175.918 35.495s-120.233-11.942-175.918-35.495c-53.816-22.763-102.161-55.361-143.69-96.891-41.53-41.53-74.128-89.874-96.891-143.689C71.948 632.232 60.006 573.045 60.006 512s11.942-120.233 35.495-175.918c22.762-53.816 55.361-102.16 96.891-143.69s89.874-74.128 143.69-96.891C391.767 71.948 450.955 60.006 512 60.006s120.232 11.942 175.918 35.495c53.815 22.763 102.159 55.361 143.689 96.891 41.529 41.53 74.128 89.875 96.891 143.69 23.553 55.685 35.495 114.873 35.495 175.918s-11.942 120.232-35.495 175.918c-22.763 53.815-55.361 102.159-96.891 143.689z" p-id="2694" fill="#ffffff"></path><path d="M795.465 512c0 23.483-19.037 42.52-42.52 42.52h-481.89c-23.483 0-42.52-19.036-42.52-42.52 0-23.483 19.037-42.52 42.52-42.52h481.89c23.483 0 42.52 19.037 42.52 42.52z" p-id="2695" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

27
uni.css Normal file
View File

@@ -0,0 +1,27 @@
@charset "UTF-8";
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
/* 全局配置 */

View File

@@ -13,42 +13,48 @@
*/
/* 颜色变量 */
@import 'uview-ui/theme.scss'; // 核心样式依赖此文
$u-primary: #376cf3;
$u-warning: #e8b956;
$u-success: #4cd964;
$u-error: #dd524d;
$u-info: #909399;
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
$uni-text-color: #333; //基本色
$uni-text-color-inverse: #fff; //反色
$uni-text-color-grey: #999; //辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f6f6f6;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
$uni-bg-color: #ffffff;
$uni-bg-color-grey: #f6f6f6;
$uni-bg-color-hover: #f1f1f1; //点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16;
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
@@ -70,17 +76,16 @@ $uni-spacing-col-lg: 12px;
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
$uni-font-size-subtitle: 26px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
/* 全局配置 */
$uni-theme-color: #376cf3; // 全局主题色
$uni-theme-yellow: #e8b956; // 全局主题色
$uni-theme-white: #fff; // 全局主题色
$uni-theme-radius: 12rpx; // 全局主题色
$uni-theme-radius: 12rpx; // 全局主题色

View File

@@ -0,0 +1,48 @@
## 1.4.82025-09-16
- 修复 modelValue 修改会两次触发 change 事件的 Bug
## 1.4.72025-09-11
- 修复 modelValue 修改不会触发更新的 Bug
## 1.4.62025-09-02
- 修复 modelValue 修改不会触发 change 事件的 Bug
## 1.4.52025-09-02
- 修复 非手风琴模式 不能设置 modeValue 为 [] 的 Bug (question/205130)
## 1.4.42024-03-20
- 修复 titleBorder类型修正
## 1.4.32022-01-25
- 修复 初始化的时候 open 属性失效的bug
## 1.4.22022-01-21
- 修复 微信小程序resize后组件收起的bug
## 1.4.12021-11-22
- 修复 vue3中个别scss变量无法找到的问题
## 1.4.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse)
## 1.3.32021-08-17
- 优化 show-arrow 属性默认为true
## 1.3.22021-08-17
- 新增 show-arrow 属性,控制是否显示右侧箭头
## 1.3.12021-07-30
- 优化 vue3下小程序事件警告的问题
## 1.3.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.2.22021-07-21
- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug
## 1.2.12021-07-21
- 优化 组件示例
## 1.2.02021-07-21
- 新增 组件折叠动画
- 新增 value\v-model 属性 ,动态修改面板折叠状态
- 新增 title 插槽 ,可定义面板标题
- 新增 border 属性 ,显示隐藏面板内容分隔线
- 新增 title-border 属性 ,显示隐藏面板标题分隔线
- 修复 resize 方法失效的Bug
- 修复 change 事件返回参数不正确的Bug
- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法
## 1.1.72021-05-12
- 新增 组件示例地址
## 1.1.62021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.1.52021-02-05
- 调整为uni_modules目录规范

View File

@@ -0,0 +1,402 @@
<template>
<view class="uni-collapse-item">
<!-- onClick(!isOpen) -->
<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
<view class="uni-collapse-item__title-wrap">
<slot name="title">
<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
<text class="uni-collapse-item__title-text">{{ title }}</text>
</view>
</slot>
</view>
<view v-if="showArrow"
:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
class="uni-collapse-item__title-arrow">
<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" />
</view>
</view>
<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
:style="{height: (isOpen?height:0) +'px'}">
<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* CollapseItem 折叠面板子组件
* @description 折叠面板子组件
* @property {String} title 标题文字
* @property {String} thumb 标题左侧缩略图
* @property {String} name 唯一标志符
* @property {Boolean} open = [true|false] 是否展开组件
* @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
* @property {String} border = ['auto'|'show'|'none'] 是否显示分隔线
* @property {Boolean} disabled = [true|false] 是否展开面板
* @property {Boolean} showAnimation = [true|false] 开启动画
* @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
*/
export default {
name: 'uniCollapseItem',
props: {
// 列表标题
title: {
type: String,
default: ''
},
name: {
type: [Number, String],
default: ''
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// #ifdef APP-PLUS
// 是否显示动画,app 端默认不开启动画,卡顿严重
showAnimation: {
type: Boolean,
default: false
},
// #endif
// #ifndef APP-PLUS
// 是否显示动画
showAnimation: {
type: Boolean,
default: true
},
// #endif
// 是否展开
open: {
type: Boolean,
default: false
},
// 缩略图
thumb: {
type: String,
default: ''
},
// 标题分隔线显示类型
titleBorder: {
type: String,
default: 'auto'
},
border: {
type: Boolean,
default: true
},
showArrow: {
type: Boolean,
default: true
}
},
data() {
// TODO 随机生生元素ID解决百度小程序获取同一个元素位置信息的bug
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
isOpen: false,
isheight: null,
height: 0,
elId,
nameSync: 0
}
},
watch: {
open(val) {
this.isOpen = val
this.onClick(val, 'init')
}
},
updated(e) {
this.$nextTick(() => {
this.init(true)
})
},
created() {
this.collapse = this.getCollapse()
this.oldHeight = 0
this.onClick(this.open, 'init')
},
// #ifndef VUE3
// TODO vue2
destroyed() {
if (this.__isUnmounted) return
this.uninstall()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.__isUnmounted = true
this.uninstall()
},
// #endif
mounted() {
if (!this.collapse) return
if (this.name !== '') {
this.nameSync = this.name
} else {
this.nameSync = this.collapse.childrens.length + ''
}
if (this.collapse.names.indexOf(this.nameSync) === -1) {
this.collapse.names.push(this.nameSync)
} else {
console.warn(`name 值 ${this.nameSync} 重复`);
}
if (this.collapse.childrens.indexOf(this) === -1) {
this.collapse.childrens.push(this)
}
this.init()
},
methods: {
init(type) {
// #ifndef APP-NVUE
this.getCollapseHeight(type)
// #endif
// #ifdef APP-NVUE
this.getNvueHwight(type)
// #endif
},
uninstall() {
if (this.collapse) {
this.collapse.childrens.forEach((item, index) => {
if (item === this) {
this.collapse.childrens.splice(index, 1)
}
})
this.collapse.names.forEach((item, index) => {
if (item === this.nameSync) {
this.collapse.names.splice(index, 1)
}
})
}
},
onClick(isOpen, type) {
if (this.disabled) return
this.isOpen = isOpen
if (this.isOpen && this.collapse) {
this.collapse.setAccordion(this)
}
if (type !== 'init') {
this.collapse.onChange(isOpen, this)
}
},
getCollapseHeight(type, index = 0) {
const views = uni.createSelectorQuery().in(this)
views
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
// TODO 百度中可能获取不到节点信息 ,需要循环获取
if (index >= 10) return
if (!data) {
index++
this.getCollapseHeight(false, index)
return
}
// #ifdef APP-NVUE
this.height = data.height + 1
// #endif
// #ifndef APP-NVUE
this.height = data.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.isOpen, 'init')
})
.exec()
},
getNvueHwight(type) {
const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
if (option && option.result && option.size) {
// #ifdef APP-NVUE
this.height = option.size.height + 1
// #endif
// #ifndef APP-NVUE
this.height = option.size.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.open, 'init')
}
})
},
/**
* 获取父元素实例
*/
getCollapse(name = 'uniCollapse') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
}
}
}
</script>
<style lang="scss">
.uni-collapse-item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
&__title {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
transition: border-bottom-color .3s;
// transition-property: border-bottom-color;
// transition-duration: 5s;
&-wrap {
width: 100%;
flex: 1;
}
&-box {
padding: 0 15px;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 48px;
line-height: 48px;
background-color: #fff;
color: #303133;
font-size: 13px;
font-weight: 500;
/* #ifdef H5 */
cursor: pointer;
outline: none;
/* #endif */
&.is-disabled {
.uni-collapse-item__title-text {
color: #999;
}
}
}
&.uni-collapse-item-border {
border-bottom: 1px solid #ebeef5;
}
&.is-open {
border-bottom-color: transparent;
}
&-img {
height: 22px;
width: 22px;
margin-right: 10px;
}
&-text {
flex: 1;
font-size: 14px;
/* #ifndef APP-NVUE */
white-space: nowrap;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
}
&-arrow {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
margin-right: 10px;
transform: rotate(0deg);
&-active {
transform: rotate(-180deg);
}
}
}
&__wrap {
/* #ifndef APP-NVUE */
will-change: height;
box-sizing: border-box;
/* #endif */
background-color: #fff;
overflow: hidden;
position: relative;
height: 0;
&.is--transition {
// transition: all 0.3s;
transition-property: height, border-bottom-width;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
will-change: height;
/* #endif */
}
&-content {
position: absolute;
font-size: 13px;
color: #303133;
// transition: height 0.3s;
border-bottom-color: transparent;
border-bottom-style: solid;
border-bottom-width: 0;
&.uni-collapse-item--border {
border-bottom-width: 1px;
border-bottom-color: red;
border-bottom-color: #ebeef5;
}
&.open {
position: relative;
}
}
}
&--animation {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
}
</style>

View File

@@ -0,0 +1,147 @@
<template>
<view class="uni-collapse">
<slot />
</view>
</template>
<script>
/**
* Collapse 折叠面板
* @description 展示可以折叠 / 展开的内容区域
* @tutorial https://ext.dcloud.net.cn/plugin?id=23
* @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式参数类型为string否则为array)
* @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
* @event {Function} change 切换面板时触发如果是手风琴模式返回类型为string否则为array
*/
export default {
name: 'uniCollapse',
emits:['change','activeItem','input','update:modelValue'],
props: {
value: {
type: [String, Array],
default: ''
},
modelValue: {
type: [String, Array],
default: ''
},
accordion: {
// 是否开启手风琴效果
type: [Boolean, String],
default: false
},
},
data() {
return {}
},
computed: {
// TODO 兼容 vue2 和 vue3
dataValue() {
let value = (typeof this.value === 'string' && this.value === '') ||
(Array.isArray(this.value) && this.value.length === 0)
let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
(Array.isArray(this.modelValue) && this.modelValue.length === 0)
if (value) {
return this.modelValue
}
if (modelValue) {
return this.value
}
return this.value
}
},
watch: {
dataValue: {
handler(newVal) {
this.setOpen(newVal)
},
deep: true
}
},
created() {
this.childrens = []
this.names = []
},
mounted() {
this.$nextTick(()=>{
this.setOpen(this.dataValue)
})
},
methods: {
setOpen(val) {
const str = typeof val === 'string'
const arr = Array.isArray(val)
this.childrens.forEach((vm) => {
if (str) {
if (val === vm.nameSync) {
if (!this.accordion) {
console.warn('accordion 属性为 false ,v-model 类型应该为 array')
return
}
vm.isOpen = true
}
}
if (arr) {
const isOpen = val.findIndex(v => v === vm.nameSync) !== -1
if (this.accordion && isOpen) {
console.warn('accordion 属性为 true ,v-model 类型应该为 string')
return
}
vm.isOpen = isOpen
}
})
this.emit(val)
},
setAccordion(self) {
if (!this.accordion) return
this.childrens.forEach((vm, index) => {
if (self !== vm) {
vm.isOpen = false
}
})
},
resize() {
this.childrens.forEach((vm, index) => {
// #ifndef APP-NVUE
vm.getCollapseHeight()
// #endif
// #ifdef APP-NVUE
vm.getNvueHwight()
// #endif
})
},
onChange(isOpen, self) {
let activeItem = []
if (this.accordion) {
activeItem = isOpen ? self.nameSync : ''
} else {
this.childrens.forEach((vm, index) => {
if (vm.isOpen) {
activeItem.push(vm.nameSync)
}
})
}
this.$emit('change', activeItem)
this.emit(activeItem)
},
emit(val){
this.$emit('input', val)
this.$emit('update:modelValue', val)
}
}
}
</script>
<style lang="scss" >
.uni-collapse {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,106 @@
{
"id": "uni-collapse",
"displayName": "uni-collapse 折叠面板",
"version": "1.4.8",
"description": "Collapse 组件,可以折叠 / 展开的内容区域。",
"keywords": [
"uni-ui",
"折叠",
"折叠面板",
"手风琴"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "",
"uni-app": "^4.07",
"uni-app-x": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"uni-app": {
"vue": {
"vue2": "√",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "√",
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "√",
"baidu": "√",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "√",
"lark": "-"
},
"quickapp": {
"huawei": "√",
"union": "√"
}
},
"uni-app-x": {
"web": {
"safari": "-",
"chrome": "-"
},
"app": {
"android": "-",
"ios": "-",
"harmony": "-"
},
"mp": {
"weixin": "-"
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
## Collapse 折叠面板
> **组件名uni-collapse**
> 代码块: `uCollapse`
> 关联组件:`uni-collapse-item`、`uni-icons`。
折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -1,41 +1,111 @@
## 2.2.422025-08-20
- 修复 datetime-picker 小程序样式警告
## 2.2.412025-08-20
- 修复 uni-datetime-picker组件时间对比报错问题
## 2.2.402025-04-14
- 修复 绑定字符串值的时,日历面板选中状态未重置到默认值的问题
## 2.2.392025-04-14
- 修复 在 iOS 微信小程序上type='daterange'时,传入'YYYY-MM-DD'格式不生效的问题
## 2.2.382024-10-15
- 修复 微信小程序中的getSystemInfo警告
## 2.2.352024-09-21
- 修复 没有选中日期时点击确定直接报错的Bug [详情](https://ask.dcloud.net.cn/question/198168)
## 2.2.342024-04-24
- 新增 日期点击事件,在点击日期时会触发该事件。
## 2.2.332024-04-15
- 修复 抖音小程序事件传递失效bug
## 2.2.322024-02-20
- 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844)
## 2.2.312024-02-20
- 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841)
## 2.2.302024-01-31
- 修复 隐藏“秒”时在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788)
## 2.2.292024-01-20
- 新增 show事件弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694)
## 2.2.282024-01-18
- 去除 noChange事件当进行日期范围选择时若只选了一天则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.272024-01-10
- 优化 增加noChange事件当进行日期范围选择时若有空值则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.262024-01-08
- 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834)
## 2.2.252023-10-18
- 修复 PC端初次修改时间开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
## 2.2.242023-06-02
- 修复 部分情况修改时间开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
- 优化 当前月可以选择上月、下月的日期的Bug
## 2.2.232023-05-02
- 修复 部分情况修改时间开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
- 修复 部分平台及设备第一次点击无法显示弹框的Bug
- 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.222023-03-30
- 修复 日历 picker 修改年月后自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937)
- 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.212023-02-20
- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
## 2.2.202023-02-17
- 优化 值为空依然选中当天问题
- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
- 优化 字节小程序日期时间范围选择底部日期换行的Bug
## 2.2.192023-02-09
- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
## 2.2.182023-02-08
- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
- 优化 PC端输入日期格式错误时返回当前日期时间
- 优化 PC端输入日期时间超出 start、end 限制的Bug
- 优化 移动端日期时间范围用法时间展示不完整问题
## 2.2.172023-02-04
- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
## 2.2.162023-02-02
- 修复 字节小程序报错的Bug
## 2.2.152023-02-02
- 修复 某些情况切换月份错误的Bug
## 2.2.142023-01-30
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
## 2.2.132023-01-10
- 修复 多次加载组件造成内存占用的Bug
## 2.2.122022-12-01
- 修复 vue3 下 i18n 国际化初始值不正确的Bug
## 2.2.112022-09-19
- 修复支付宝小程序样式错乱[详情](https://github.com/dcloudio/uni-app/issues/3861)
- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
## 2.2.102022-09-19
- 修复反向选择日期范围,日期显示异常[详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
## 2.2.92022-09-16
- 可以使用 uni-scss 控制主题色
## 2.2.82022-09-08
- 修复 close事件无效的 bug
- 修复 close事件无效的Bug
## 2.2.72022-09-05
- 修复 移动端 maskClick 无效的 bug详见:[https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false](https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false)
- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
## 2.2.62022-06-30
- 优化 组件样式调整了组件图标大小、高度、颜色等与uni-ui风格保持一致
## 2.2.52022-06-24
- 修复 日历顶部年月及底部确认未国际化 bug
- 修复 日历顶部年月及底部确认未国际化的Bug
## 2.2.42022-03-31
- 修复 Vue3 下动态赋值,单选类型未响应的 bug
- 修复 Vue3 下动态赋值,单选类型未响应的Bug
## 2.2.32022-03-28
- 修复 Vue3 下动态赋值未响应的 bug
- 修复 Vue3 下动态赋值未响应的Bug
## 2.2.22021-12-10
- 修复 clear-icon 属性在小程序平台不生效的 bug
- 修复 clear-icon 属性在小程序平台不生效的Bug
## 2.2.12021-12-10
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug
## 2.2.02021-11-19
- 优化 组件UI并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
## 2.1.52021-11-09
- 优化 组件UI并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
## 2.1.52021-11-09
- 新增 提供组件设计资源,组件样式调整
## 2.1.42021-09-10
- 修复 hide-second 在移动端的 bug
- 修复 单选赋默认值时,赋值日期未高亮的 bug
- 修复 赋默认值时,移动端未正确显示时间的 bug
- 修复 hide-second 在移动端的Bug
- 修复 单选赋默认值时,赋值日期未高亮的Bug
- 修复 赋默认值时,移动端未正确显示时间的Bug
## 2.1.32021-09-09
- 新增 hide-second 属性,支持只使用时分,隐藏秒
## 2.1.22021-09-03
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
- 优化 调整字号大小,美化日历界面
- 修复 因国际化导致的 placeholder 失效的 bug
- 修复 因国际化导致的 placeholder 失效的Bug
## 2.1.12021-08-24
- 新增 支持国际化
- 优化 范围选择器在 pc 端过宽的问题
@@ -43,50 +113,50 @@
- 新增 适配 vue3
## 2.0.192021-08-09
- 新增 支持作为 uni-forms 子组件相关功能
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
## 2.0.182021-08-05
- 修复 type 属性动态赋值无效的 bug
- 修复 type 属性动态赋值无效的Bug
- 修复 ‘确认’按钮被 tabbar 遮盖 bug
- 修复 组件未赋值时范围选左、右日历相同的 bug
- 修复 组件未赋值时范围选左、右日历相同的Bug
## 2.0.172021-08-04
- 修复 范围选未正确显示当前值的 bug
- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug
- 修复 范围选未正确显示当前值的Bug
- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
## 2.0.162021-07-21
- 新增 return-type 属性支持返回 date 日期对象
## 2.0.152021-07-14
- 修复 单选日期类型,初始赋值后不在当前日历的 bug
- 修复 单选日期类型,初始赋值后不在当前日历的Bug
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
- 优化 移动端移除显示框的清空按钮,无实际用途
## 2.0.142021-07-14
- 修复 组件赋值为空,界面未更新的 bug
- 修复 start 和 end 不能动态赋值的 bug
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug
- 修复 组件赋值为空,界面未更新的Bug
- 修复 start 和 end 不能动态赋值的Bug
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug
## 2.0.132021-07-08
- 修复 范围选择不能动态赋值的 bug
- 修复 范围选择不能动态赋值的Bug
## 2.0.122021-07-08
- 修复 范围选择的初始时间在一个月内时造成无法选择的bug
## 2.0.112021-07-08
- 优化 弹出层在超出视窗边缘定位不准确的问题
## 2.0.102021-07-08
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
- 优化 弹出层在超出视窗边缘被遮盖的问题
## 2.0.92021-07-07
- 新增 maskClick 事件
- 修复 特殊情况日历 rpx 布局错误的 bugrpx -> px
- 修复 特殊情况日历 rpx 布局错误的Bugrpx -> px
- 修复 范围选择时清空返回值不合理的bug['', ''] -> []
## 2.0.82021-07-07
- 新增 日期时间显示框支持插槽
## 2.0.72021-07-01
- 优化 添加 uni-icons 依赖
## 2.0.62021-05-22
- 修复 图标在小程序上不显示的 bug
- 修复 图标在小程序上不显示的Bug
- 优化 重命名引用组件,避免潜在组件命名冲突
## 2.0.52021-05-20
- 优化 代码目录扁平化
## 2.0.42021-05-12
- 新增 组件示例地址
## 2.0.32021-05-10
- 修复 ios 下不识别 '-' 日期格式的 bug
- 修复 ios 下不识别 '-' 日期格式的Bug
- 优化 pc 下弹出层添加边框和阴影
## 2.0.22021-05-08
- 修复 在 admin 中获取弹出层定位错误的bug
@@ -97,7 +167,7 @@
> 注意此版本不向后兼容不再支持单独时间选择type=time及相关的 hide-second 属性(时间选可使用内置组件 picker
## 1.0.62021-03-18
- 新增 hide-second 属性,时间支持仅选择时、分
- 修复 选择跟显示的日期不一样的 bug
- 修复 chang事件触发2次的 bug
- 修复 分、秒 end 范围错误的 bug
- 修复 选择跟显示的日期不一样的Bug
- 修复 chang事件触发2次的Bug
- 修复 分、秒 end 范围错误的Bug
- 优化 更好的 nvue 适配

View File

@@ -13,10 +13,10 @@
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
</view>
<view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view>
<view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
</view>
</template>
@@ -41,10 +41,6 @@
return []
}
},
lunar: {
type: Boolean,
default: false
},
checkHover: {
type: Boolean,
default: false
@@ -61,8 +57,8 @@
}
</script>
<style lang="scss" >
$uni-primary: #007aff !default;
<style lang="scss" >
$uni-primary: #007aff !default;
.uni-calendar-item__weeks-box {
flex: 1;
@@ -83,11 +79,6 @@
color: darken($color: $uni-primary, $amount: 40%);
}
.uni-calendar-item__weeks-lunar-text {
font-size: 12px;
color: #333;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
@@ -116,7 +107,6 @@
}
.uni-calendar-item__weeks-box .uni-calendar-item--disable {
// background-color: rgba(249, 249, 249, $uni-opacity-disabled);
cursor: default;
}
@@ -124,7 +114,7 @@
color: #D1D1D1;
}
.uni-calendar-item--isDay {
.uni-calendar-item--today {
position: absolute;
top: 10px;
right: 17%;

View File

@@ -1,31 +1,37 @@
<template>
<view class="uni-calendar" @mouseleave="leaveCale">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="clean();maskClick()"></view>
<view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="maskClick"></view>
<view v-if="insert || show" class="uni-calendar__content"
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
<view v-if="left" class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text
class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text>
</picker>
<view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<view v-if="!insert" class="dialog-close" @click="clean">
<view v-if="!insert" class="dialog-close" @click="maskClick">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
<!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</text> -->
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
@@ -49,45 +55,44 @@
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
:selected="selected" :lunar="lunar" :checkHover="range" @change="choiceDate"
@handleMouse="handleMouse">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
:checkHover="range" @change="choiceDate" @handleMouse="handleMouse">
</calendar-item>
</view>
</view>
</view>
<view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"
<view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top"
style="padding: 0 80px;">
<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
<time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time"
<time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time"
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
</time-picker>
</view>
<view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top">
<view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top">
<view class="uni-date-changed--time-start">
<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
</view>
<time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false"
<time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
</time-picker>
</view>
<view style="line-height: 50px;">
</view>
<view style="line-height: 50px;">
<uni-icons type="arrowthinright" color="#999"></uni-icons>
</view>
<view class="uni-date-changed--time-end">
<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
<time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false"
<time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
</time-picker>
</view>
</view>
<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
<!-- <view class="uni-calendar__header-btn-box">
<text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text>
</view> -->
<view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
</view>
</view>
@@ -95,22 +100,27 @@
</template>
<script>
import Calendar from './util.js';
import {
Calendar,
getDate,
getTime
} from './util.js';
import calendarItem from './calendar-item.vue'
import timePicker from './time-picker.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
import i18nMessages from './i18n/index.js'
const {
t
} = initVueI18n(messages)
} = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间,默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
@@ -120,16 +130,26 @@
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @property {[String} defaultValue 选择器打开时默认显示的时间
* @event {Function} change 日期改变,`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
* @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
calendarItem,
timePicker
},
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
props: {
date: {
type: String,
@@ -151,10 +171,6 @@
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
@@ -163,11 +179,19 @@
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
typeHasTime: {
hasTime: {
type: Boolean,
default: false
},
@@ -183,14 +207,6 @@
type: Boolean,
default: true
},
left: {
type: Boolean,
default: true
},
right: {
type: Boolean,
default: true
},
checkHover: {
type: Boolean,
default: true
@@ -209,6 +225,10 @@
fulldate: ''
}
}
},
defaultValue: {
type: [String, Object, Array],
default: ''
}
},
data() {
@@ -216,7 +236,7 @@
show: false,
weeks: [],
calendar: {},
nowDate: '',
nowDate: {},
aniMaskShow: false,
firstEnter: true,
time: '',
@@ -234,7 +254,7 @@
watch: {
date: {
immediate: true,
handler(newVal, oldVal) {
handler(newVal) {
if (!this.range) {
this.tempSingleDate = newVal
setTimeout(() => {
@@ -245,33 +265,44 @@
},
defTime: {
immediate: true,
handler(newVal, oldVal) {
handler(newVal) {
if (!this.range) {
this.time = newVal
} else {
// console.log('-----', newVal);
this.timeRange.startTime = newVal.start
this.timeRange.endTime = newVal.end
}
}
},
startDate(val) {
this.cale.resetSatrtDate(val)
// 字节小程序 watch 早于 created
if (!this.cale) {
return
}
this.cale.setStartDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val) {
this.cale.resetEndDate(val)
// 字节小程序 watch 早于 created
if (!this.cale) {
return
}
this.cale.setEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
// 字节小程序 watch 早于 created
if (!this.cale) {
return
}
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
},
pleStatus: {
immediate: true,
handler(newVal, oldVal) {
handler(newVal) {
const {
before,
after,
@@ -294,11 +325,16 @@
this.cale.lastHover = false
}
} else {
// 字节小程序 watch 早于 created
if (!this.cale) {
return
}
this.cale.setDefaultMultiple(before, after)
if (which === 'left') {
if (which === 'left' && before) {
this.setDate(before)
this.weeks = this.cale.weeks
} else {
} else if (after) {
this.setDate(after)
this.weeks = this.cale.weeks
}
@@ -309,15 +345,13 @@
}
},
computed: {
reactStartTime() {
timepickerStartTime() {
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
const res = activeDate === this.startDate ? this.selectableTimes.start : ''
return res
return activeDate === this.startDate ? this.selectableTimes.start : ''
},
reactEndTime() {
timepickerEndTime() {
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
const res = activeDate === this.endDate ? this.selectableTimes.end : ''
return res
return activeDate === this.endDate ? this.selectableTimes.end : ''
},
/**
* for i18n
@@ -368,17 +402,13 @@
created() {
// 获取日历方法实例
this.cale = new Calendar({
// date: new Date(),
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
// multipleStatus: this.pleStatus
})
// 选中某一天
// this.cale.setDate(this.date)
this.init(this.date)
// this.setDay
},
methods: {
leaveCale() {
@@ -407,14 +437,9 @@
const [yearB, monthB] = B.split('-')
return yearA === yearB && monthA === monthB
},
// 取消穿透
clean() {
this.close()
},
// 蒙版点击事件
maskClick() {
this.close()
this.$emit('maskClose')
},
@@ -433,33 +458,51 @@
this.tempSingleDate = ''
}
this.calendar.fullDate = ''
this.setDate()
this.setDate(new Date())
},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.init(value)
this.setDate(value)
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
// 字节小程序 watch 早于 created
if (!this.cale) {
return
}
this.cale.setDate(date || new Date())
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
this.nowDate = this.cale.getInfo(date)
this.calendar = {
...this.nowDate
}
if (!date) {
// 优化date为空默认不选中今天
this.calendar.fullDate = ''
if (this.defaultValue && !this.range) {
// 暂时只支持移动端非范围选择
const defaultDate = new Date(this.defaultValue)
const fullDate = getDate(defaultDate)
const year = defaultDate.getFullYear()
const month = defaultDate.getMonth() + 1
const date = defaultDate.getDate()
const day = defaultDate.getDay()
this.calendar = {
fullDate,
year,
month,
date,
day
},
this.tempSingleDate = fullDate
this.time = getTime(defaultDate, this.hideSecond)
}
}
},
// choiceDate(weeks) {
// if (weeks.disable) return
// this.calendar = weeks
// // 设置多选
// this.cale.setMultiple(this.calendar.fullDate, true)
// this.weeks = this.cale.weeks
// this.tempSingleDate = this.calendar.fullDate
// this.tempRange.before = this.cale.multipleStatus.before
// this.tempRange.after = this.cale.multipleStatus.after
// this.change()
// },
/**
* 打开日历弹窗
*/
@@ -467,7 +510,6 @@
// 弹窗模式并且清理数据
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
@@ -499,8 +541,8 @@
/**
* 变化触发
*/
change() {
if (!this.insert) return
change(isSingleChange) {
if (!this.insert && !isSingleChange) return
this.setEmit('change')
},
/**
@@ -521,12 +563,20 @@
* @param {Object} name
*/
setEmit(name) {
if (!this.range) {
if (!this.calendar.fullDate) {
this.calendar = this.cale.getInfo(new Date())
this.tempSingleDate = this.calendar.fullDate
}
if (this.hasTime && !this.time) {
this.time = getTime(new Date(), this.hideSecond)
}
}
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
@@ -537,7 +587,6 @@
time: this.time,
timeRange: this.timeRange,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
@@ -545,64 +594,34 @@
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
this.calendar.userChecked = true
// 设置多选
this.cale.setMultiple(this.calendar.fullDate, true)
this.weeks = this.cale.weeks
this.tempSingleDate = this.calendar.fullDate
const beforeStatus = this.cale.multipleStatus.before
const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
const afterDate = new Date(this.cale.multipleStatus.after).getTime()
if (beforeDate > afterDate && afterDate) {
this.tempRange.before = this.cale.multipleStatus.after
this.tempRange.after = this.cale.multipleStatus.before
} else {
this.tempRange.before = this.cale.multipleStatus.before
this.tempRange.after = this.cale.multipleStatus.after
}
this.change()
},
/**
* 回到今天
*/
backtoday() {
let date = this.cale.getDate(new Date()).fullDate
// this.cale.setDate(date)
this.init(date)
this.change()
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
this.tempSingleDate = this.calendar.fullDate
const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
const afterDate = new Date(this.cale.multipleStatus.after).getTime()
if (beforeDate > afterDate && afterDate) {
this.tempRange.before = this.cale.multipleStatus.after
this.tempRange.after = this.cale.multipleStatus.before
} else {
return false
this.tempRange.before = this.cale.multipleStatus.before
this.tempRange.after = this.cale.multipleStatus.after
}
this.change(true)
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
changeMonth(type) {
let newDate
if (type === 'pre') {
newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
} else if (type === 'next') {
newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
}
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.setDate(newDate)
this.monthSwitch()
},
/**
@@ -618,8 +637,8 @@
}
</script>
<style lang="scss" >
$uni-primary: #007aff !default;
<style lang="scss">
$uni-primary: #007aff !default;
.uni-calendar {
/* #ifndef APP-NVUE */
@@ -862,6 +881,9 @@
.uni-date-changed--time-date {
color: #999;
line-height: 50px;
/* #ifdef MP-TOUTIAO */
font-size: 16px;
/* #endif */
margin-right: 5px;
// opacity: 0.6;
}
@@ -920,5 +942,6 @@
.uni-datetime-picker--btn:active {
opacity: 0.7;
}
/* #endif */
</style>

View File

@@ -1,15 +1,15 @@
{
"uni-datetime-picker.selectDate": "select date",
"uni-datetime-picker.selectTime": "select time",
"uni-datetime-picker.selectDateTime": "select datetime",
"uni-datetime-picker.selectDateTime": "select date and time",
"uni-datetime-picker.startDate": "start date",
"uni-datetime-picker.endDate": "end date",
"uni-datetime-picker.startTime": "start time",
"uni-datetime-picker.endTime": "end time",
"uni-datetime-picker.ok": "ok",
"uni-datetime-picker.clear": "clear",
"uni-datetime-picker.cancel": "cancel",
"uni-datetime-picker.year": "-",
"uni-datetime-picker.cancel": "cancel",
"uni-datetime-picker.year": "-",
"uni-datetime-picker.month": "",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",

View File

@@ -77,21 +77,20 @@
</view>
</view>
</view>
<!-- #ifdef H5 -->
<!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
<!-- #endif -->
</view>
</template>
<script>
// #ifdef H5
import keypress from './keypress'
// #endif
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
import i18nMessages from './i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
import {
fixIosDateFormat
} from './util'
/**
* DatetimePicker 时间选择器
@@ -108,11 +107,6 @@
export default {
name: 'UniDatetimePicker',
components: {
// #ifdef H5
keypress
// #endif
},
data() {
return {
indicatorStyle: `height: 50px;`,
@@ -146,6 +140,14 @@
endSecond: 59,
}
},
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
props: {
type: {
type: String,
@@ -185,18 +187,34 @@
}
},
watch: {
// #ifndef VUE3
value: {
handler(newVal, oldVal) {
handler(newVal) {
if (newVal) {
this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
this.parseValue(fixIosDateFormat(newVal))
this.initTime(false)
} else {
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal) {
if (newVal) {
this.parseValue(fixIosDateFormat(newVal))
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
type: {
handler(newValue) {
if (newValue === 'date') {
@@ -217,13 +235,13 @@
},
start: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式
this.parseDatetimeRange(fixIosDateFormat(newVal), 'start')
},
immediate: true
},
end: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
},
immediate: true
},
@@ -527,7 +545,7 @@
const day = now.getDate()
dateBase = year + '/' + month + '/' + day + ' '
}
if (Number(value) && typeof value !== NaN) {
if (Number(value)) {
value = parseInt(value)
dateBase = 0
}
@@ -539,8 +557,8 @@
* @param {Object} defaultTime
*/
parseValue(value) {
if (!value) {
return
if (!value) {
return
}
if (this.type === 'time' && typeof value === "string") {
this.parseTimeType(value)
@@ -598,7 +616,7 @@
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
return
}
if (Number(point) && Number(point) !== NaN) {
if (Number(point)) {
point = parseInt(point)
}
// datetime 的 end 没有时分秒, 则不限制
@@ -645,14 +663,6 @@
return new Date(year, month, 0).getDate();
},
//兼容 iOS、safari 日期格式
fixIosDateFormat(value) {
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
return value
},
/**
* 生成时间戳
* @param {Object} time
@@ -736,7 +746,7 @@
*/
initTimePicker() {
if (this.disabled) return
const value = this.fixIosDateFormat(this.value)
const value = fixIosDateFormat(this.time)
this.initPickerValue(value)
this.visible = !this.visible
},
@@ -770,8 +780,8 @@
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
<style lang="scss">
$uni-primary: #007aff !default;
.uni-datetime-picker {
/* #ifndef APP-NVUE */
@@ -890,8 +900,8 @@
}
.uni-datetime-picker-text {
font-size: 14px;
line-height: 50px
font-size: 14px;
line-height: 50px
}
.uni-datetime-picker-sign {
@@ -927,4 +937,4 @@
.time-hide-second {
width: 180px;
}
</style>
</style>

View File

@@ -1,27 +1,24 @@
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range,
// multipleStatus
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
this.date = this.getDateObj(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
// 起始时间
this.startDate = startDate
// 范围结束
// 终止时间
this.endDate = endDate
// 是否范围选择
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
// this._getWeek(this.date.fullDate)
// this.multipleStatus = multipleStatus
this.lastHover = false
}
/**
@@ -29,8 +26,8 @@ class Calendar {
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
const selectDate = this.getDateObj(date)
this.getWeeks(selectDate.fullDate)
}
/**
@@ -44,93 +41,82 @@ class Calendar {
}
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
setStartDate(startDate) {
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
setEndDate(endDate) {
this.endDate = endDate
}
getPreMonthObj(date) {
date = fixIosDateFormat(date)
date = new Date(date)
const oldMonth = date.getMonth()
date.setMonth(oldMonth - 1)
const newMonth = date.getMonth()
if (oldMonth !== 0 && newMonth - oldMonth === 0) {
date.setMonth(newMonth - 1)
}
return this.getDateObj(date)
}
getNextMonthObj(date) {
date = fixIosDateFormat(date)
date = new Date(date)
const oldMonth = date.getMonth()
date.setMonth(oldMonth + 1)
const newMonth = date.getMonth()
if (newMonth - oldMonth > 1) {
date.setMonth(newMonth - 1)
}
return this.getDateObj(date)
}
/**
* 获取任意时间
* 获取指定格式Date对象
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
getDateObj(date) {
date = fixIosDateFormat(date)
date = new Date(date)
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
fullDate: getDate(date),
year: date.getFullYear(),
month: addZero(date.getMonth() + 1),
date: addZero(date.getDate()),
day: date.getDay()
}
}
/**
* 获取上月剩余天数
* 获取上一个月日期集合
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
getPreMonthDays(amount, dateObj) {
const result = []
for (let i = amount - 1; i >= 0; i--) {
const month = dateObj.month - 1
result.push({
date: new Date(dateObj.year, month, -i).getDate(),
month,
disable: true
})
}
return dateArr
return result
}
/**
* 获取本月天数
* 获取本月日期集合
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let isinfo = false
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
getCurrentMonthDays(amount, dateObj) {
const result = []
const fullDate = this.date.fullDate
for (let i = 1; i <= amount; i++) {
const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
const isToday = fullDate === currentDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
const info = this.selected && this.selected.find((item) => {
if (this.dateEqual(currentDate, item.date)) {
return item
}
})
@@ -139,62 +125,53 @@ class Calendar {
let disableBefore = true
let disableAfter = true
if (this.startDate) {
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
disableBefore = this.dateCompare(this.startDate, nowDate)
disableBefore = dateCompare(this.startDate, currentDate)
}
if (this.endDate) {
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
disableAfter = this.dateCompare(nowDate, this.endDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
month: full.month,
disable: !(disableBefore && disableAfter),
isDay,
userChecked: false
}
if (info) {
data.extraInfo = info
disableAfter = dateCompare(currentDate, this.endDate)
}
dateArr.push(data)
let multiples = this.multipleStatus.data
let multiplesStatus = -1
if (this.range && multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, currentDate)
})
}
const checked = multiplesStatus !== -1
result.push({
fullDate: currentDate,
year: dateObj.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
month: dateObj.month,
disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(
currentDate, this.endDate)),
isToday,
userChecked: false,
extraInfo: info
})
}
return dateArr
return result
}
/**
* 获取下月天数
* 获取下一个月日期集合
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
_getNextMonthDays(amount, dateObj) {
const result = []
const month = dateObj.month + 1
for (let i = 1; i <= amount; i++) {
result.push({
date: i,
month: Number(full.month) + 1,
month,
disable: true
})
}
return dateArr
return result
}
/**
@@ -205,59 +182,38 @@ class Calendar {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
return res ? res : this.getDateObj(date)
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
before = new Date(fixIosDateFormat(before))
after = new Date(fixIosDateFormat(after))
return before.valueOf() === after.valueOf()
}
/**
* 比较真实起始日期
*/
isLogicBefore(currentDay, before, after) {
let logicBefore = before
if (before && after) {
logicBefore = this.dateCompare(before, after) ? before : after
}
return this.dateEqual(logicBefore, currentDay)
isLogicBefore(currentDate, before, after) {
let logicBefore = before
if (before && after) {
logicBefore = dateCompare(before, after) ? before : after
}
return this.dateEqual(logicBefore, currentDate)
}
isLogicAfter(currentDate, before, after) {
let logicAfter = after
if (before && after) {
logicAfter = dateCompare(before, after) ? after : before
}
return this.dateEqual(logicAfter, currentDate)
}
isLogicAfter(currentDay, before, after) {
let logicAfter = after
if (before && after) {
logicAfter = this.dateCompare(before, after) ? after : before
}
return this.dateEqual(logicAfter, currentDay)
}
/**
* 获取日期范围内所有日期
@@ -276,7 +232,7 @@ class Calendar {
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
}
return arr
}
@@ -285,11 +241,12 @@ class Calendar {
* 获取多选状态
*/
setMultiple(fullDate) {
if (!this.range) return
let {
before,
after
} = this.multipleStatus
if (!this.range) return
} = this.multipleStatus
if (before && after) {
if (!this.lastHover) {
this.lastHover = true
@@ -302,46 +259,48 @@ class Calendar {
this.lastHover = false
} else {
if (!before) {
this.multipleStatus.before = fullDate
this.multipleStatus.before = fullDate
this.multipleStatus.after = undefined;
this.lastHover = false
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
.before);
}
}
this.lastHover = true
}
}
this._getWeek(fullDate)
this.getWeeks(fullDate)
}
/**
* 鼠标 hover 更新多选状态
*/
setHoverMultiple(fullDate) {
let {
before,
after
//抖音小程序点击会触发hover事件需要避免一下
// #ifndef MP-TOUTIAO
if (!this.range || this.lastHover) return
const {
before
} = this.multipleStatus
if (!this.range) return
if (this.lastHover) return
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
}
this._getWeek(fullDate)
this.getWeeks(fullDate)
// #endif
}
/**
@@ -351,12 +310,12 @@ class Calendar {
this.multipleStatus.before = before
this.multipleStatus.after = after
if (before && after) {
if (this.dateCompare(before, after)) {
if (dateCompare(before, after)) {
this.multipleStatus.data = this.geDateAll(before, after);
this._getWeek(after)
this.getWeeks(after)
} else {
this.multipleStatus.data = this.geDateAll(after, before);
this._getWeek(before)
this.getWeeks(before)
}
}
}
@@ -365,46 +324,98 @@ class Calendar {
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
getWeeks(dateData) {
const {
fullDate,
year,
month,
date,
day
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
} = this.getDateObj(dateData)
const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
const currentMonthDayAmount = new Date(year, month, 0).getDate()
const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
const weeks = new Array(6)
for (let i = 0; i < calendarDays.length; i++) {
const index = Math.floor(i / 7)
if (!weeks[index]) {
weeks[index] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
weeks[index][i % 7] = calendarDays[i]
}
this.canlender = canlender
this.calendar = calendarDays
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
function getDateTime(date, hideSecond) {
return `${getDate(date)} ${getTime(date, hideSecond)}`
}
export default Calendar
function getDate(date) {
date = fixIosDateFormat(date)
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${addZero(month)}-${addZero(day)}`
}
function getTime(date, hideSecond) {
date = fixIosDateFormat(date)
date = new Date(date)
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
}
function addZero(num) {
if (num < 10) {
num = `0${num}`
}
return num
}
function getDefaultSecond(hideSecond) {
return hideSecond ? '00:00' : '00:00:00'
}
function dateCompare(startDate, endDate) {
startDate = new Date(fixIosDateFormat(typeof startDate === 'string' ? startDate.trim() : startDate))
endDate = new Date(fixIosDateFormat(typeof endDate === 'string' ? endDate.trim() : endDate))
return startDate <= endDate
}
function checkDate(date) {
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
return date.match(dateReg)
}
//ios低版本15及以下无法匹配 没有 ’秒‘ 时的情况,所以需要在末尾 秒 加上 问号
const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/;
function fixIosDateFormat(value) {
if (typeof value === 'string' && dateTimeReg.test(value)) {
value = value.replace(/-/g, '/')
}
return value
}
export {
Calendar,
getDateTime,
getDate,
getTime,
addZero,
getDefaultSecond,
dateCompare,
checkDate,
fixIosDateFormat
}

View File

@@ -1,7 +1,7 @@
{
"id": "uni-datetime-picker",
"displayName": "uni-datetime-picker 日期选择器",
"version": "2.2.11",
"version": "2.2.42",
"description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
"keywords": [
"uni-datetime-picker",
@@ -12,12 +12,14 @@
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
"HBuilderX": "",
"uni-app": "^4.07",
"uni-app-x": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
@@ -35,53 +37,71 @@
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
"uni-app": {
"vue": {
"vue2": "",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "√",
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "√",
"baidu": "√",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "√",
"lark": "-"
},
"quickapp": {
"huawei": "√",
"union": "√"
}
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
"uni-app-x": {
"web": {
"safari": "-",
"chrome": "-"
},
"app": {
"android": "-",
"ios": "-",
"harmony": "-"
},
"mp": {
"weixin": "-"
}
}
}
}
}
}
}

View File

@@ -1,3 +1,25 @@
## 2.0.122025-08-26
- 优化 uni-app x 下 size 类型问题
## 2.0.112025-08-18
- 修复 图标点击事件返回
## 2.0.92024-01-12
fix: 修复图标大小默认值错误的问题
## 2.0.82023-12-14
- 修复 项目未使用 ts 情况下打包报错的bug
## 2.0.72023-12-14
- 修复 size 属性为 string 时不加单位导致尺寸异常的bug
## 2.0.62023-12-11
- 优化 兼容老版本icon类型如 top bottom 等
## 2.0.52023-12-11
- 优化 兼容老版本icon类型如 top bottom 等
## 2.0.42023-12-06
- 优化 uni-app x 下示例项目图标排序
## 2.0.32023-12-06
- 修复 nvue下引入组件报错的bug
## 2.0.22023-12-05
-优化 size 属性支持单位
## 2.0.12023-12-05
- 新增 uni-app x 支持定义图标
## 1.3.52022-01-24
- 优化 size 属性可以传入不带单位的字符串数值
## 1.3.42022-01-24

View File

@@ -0,0 +1,91 @@
<template>
<text class="uni-icons" :style="styleObj">
<slot>{{unicode}}</slot>
</text>
</template>
<script>
import { fontData, IconsDataItem } from './uniicons_file'
/**
* Icons 图标
* @description 用于展示 icon 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: "uni-icons",
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {};
},
computed: {
unicode() : string {
let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
if (codes !== null) {
return codes.unicode
}
return ''
},
iconSize() : string {
const size = this.size
if (typeof size == 'string') {
const reg = /^[0-9]*$/g
return reg.test(size as string) ? '' + size + 'px' : '' + size;
// return '' + this.size
}
return this.getFontSize(size as number)
},
styleObj() : UTSJSONObject {
if (this.fontFamily !== '') {
return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
}
return { color: this.color, fontSize: this.iconSize }
}
},
created() { },
methods: {
/**
* 字体大小
*/
getFontSize(size : number) : string {
return size + 'px';
},
},
}
</script>
<style scoped>
@font-face {
font-family: UniIconsFontFamily;
src: url('./uniicons.ttf');
}
.uni-icons {
font-family: UniIconsFontFamily;
font-size: 18px;
font-style: normal;
color: #333;
}
</style>

View File

@@ -1,24 +1,28 @@
<template>
<!-- #ifdef APP-NVUE -->
<text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"></text>
<template>
<!-- #ifdef APP-NVUE -->
<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
<slot></slot>
</text>
<!-- #endif -->
</template>
<script>
import icons from './icons.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val) )? val + 'px' : val;
}
import { fontData } from './uniicons_file_vue.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
}
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import iconUrl from './uniicons.ttf'
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('"+iconUrl+"')"
'src': "url('" + iconUrl + "')"
});
// #endif
@@ -28,13 +32,13 @@
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits:['click'],
emits: ['click'],
props: {
type: {
type: String,
@@ -47,50 +51,60 @@
size: {
type: [Number, String],
default: 16
},
customPrefix:{
type: String,
default: ''
},
customPrefix: {
type: String,
default: ''
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {
icons: icons.glyphs
icons: fontData
}
},
computed: {
unicode() {
let code = this.icons.find(v => v.font_class === this.type)
if (code) {
return code.unicode
}
return ''
},
iconSize() {
return getVal(this.size)
},
styleObj() {
if (this.fontFamily !== '') {
return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
}
return `color: ${this.color}; font-size: ${this.iconSize};`
}
},
computed:{
unicode(){
let code = this.icons.find(v=>v.font_class === this.type)
if(code){
return unescape(`%u${code.unicode}`)
}
return ''
},
iconSize(){
return getVal(this.size)
}
},
methods: {
_onClick() {
this.$emit('click')
_onClick(e) {
this.$emit('click', e)
}
}
}
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
<style lang="scss">
/* #ifndef APP-NVUE */
@import './uniicons.css';
@font-face {
font-family: uniicons;
src: url('./uniicons.ttf') format('truetype');
src: url('./uniicons.ttf');
}
/* #endif */
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
</style>
</style>

View File

@@ -1,3 +1,12 @@
.uniui-cart-filled:before {
content: "\e6d0";
}
.uniui-gift-filled:before {
content: "\e6c4";
}
.uniui-color:before {
content: "\e6cf";
}
@@ -58,10 +67,6 @@
content: "\e6c3";
}
.uniui-gift-filled:before {
content: "\e6c4";
}
.uniui-fire-filled:before {
content: "\e6c5";
}
@@ -82,6 +87,18 @@
content: "\e698";
}
.uniui-arrowthinleft:before {
content: "\e6d2";
}
.uniui-arrowthinup:before {
content: "\e6d3";
}
.uniui-arrowthindown:before {
content: "\e6d4";
}
.uniui-back:before {
content: "\e6b9";
}
@@ -94,55 +111,43 @@
content: "\e6bb";
}
.uniui-arrowthinright:before {
content: "\e6bb";
}
.uniui-arrow-left:before {
content: "\e6bc";
}
.uniui-arrowthinleft:before {
content: "\e6bc";
}
.uniui-arrow-up:before {
content: "\e6bd";
}
.uniui-arrowthinup:before {
content: "\e6bd";
}
.uniui-arrow-down:before {
content: "\e6be";
}
.uniui-arrowthindown:before {
content: "\e6be";
.uniui-arrowthinright:before {
content: "\e6d1";
}
.uniui-down:before {
content: "\e6b8";
}
.uniui-bottom:before {
content: "\e6b8";
}
.uniui-arrowdown:before {
content: "\e6b8";
.uniui-arrowright:before {
content: "\e6d5";
}
.uniui-right:before {
content: "\e6b5";
}
.uniui-arrowright:before {
content: "\e6b5";
}
.uniui-top:before {
.uniui-up:before {
content: "\e6b6";
}
.uniui-arrowup:before {
.uniui-top:before {
content: "\e6b6";
}
@@ -150,8 +155,8 @@
content: "\e6b7";
}
.uniui-arrowleft:before {
content: "\e6b7";
.uniui-arrowup:before {
content: "\e6d6";
}
.uniui-eye:before {
@@ -638,10 +643,6 @@
content: "\e627";
}
.uniui-cart-filled:before {
content: "\e629";
}
.uniui-checkbox:before {
content: "\e62b";
}

View File

@@ -0,0 +1,664 @@
export type IconsData = {
id : string
name : string
font_family : string
css_prefix_text : string
description : string
glyphs : Array<IconsDataItem>
}
export type IconsDataItem = {
font_class : string
unicode : string
}
export const fontData = [
{
"font_class": "arrow-down",
"unicode": "\ue6be"
},
{
"font_class": "arrow-left",
"unicode": "\ue6bc"
},
{
"font_class": "arrow-right",
"unicode": "\ue6bb"
},
{
"font_class": "arrow-up",
"unicode": "\ue6bd"
},
{
"font_class": "auth",
"unicode": "\ue6ab"
},
{
"font_class": "auth-filled",
"unicode": "\ue6cc"
},
{
"font_class": "back",
"unicode": "\ue6b9"
},
{
"font_class": "bars",
"unicode": "\ue627"
},
{
"font_class": "calendar",
"unicode": "\ue6a0"
},
{
"font_class": "calendar-filled",
"unicode": "\ue6c0"
},
{
"font_class": "camera",
"unicode": "\ue65a"
},
{
"font_class": "camera-filled",
"unicode": "\ue658"
},
{
"font_class": "cart",
"unicode": "\ue631"
},
{
"font_class": "cart-filled",
"unicode": "\ue6d0"
},
{
"font_class": "chat",
"unicode": "\ue65d"
},
{
"font_class": "chat-filled",
"unicode": "\ue659"
},
{
"font_class": "chatboxes",
"unicode": "\ue696"
},
{
"font_class": "chatboxes-filled",
"unicode": "\ue692"
},
{
"font_class": "chatbubble",
"unicode": "\ue697"
},
{
"font_class": "chatbubble-filled",
"unicode": "\ue694"
},
{
"font_class": "checkbox",
"unicode": "\ue62b"
},
{
"font_class": "checkbox-filled",
"unicode": "\ue62c"
},
{
"font_class": "checkmarkempty",
"unicode": "\ue65c"
},
{
"font_class": "circle",
"unicode": "\ue65b"
},
{
"font_class": "circle-filled",
"unicode": "\ue65e"
},
{
"font_class": "clear",
"unicode": "\ue66d"
},
{
"font_class": "close",
"unicode": "\ue673"
},
{
"font_class": "closeempty",
"unicode": "\ue66c"
},
{
"font_class": "cloud-download",
"unicode": "\ue647"
},
{
"font_class": "cloud-download-filled",
"unicode": "\ue646"
},
{
"font_class": "cloud-upload",
"unicode": "\ue645"
},
{
"font_class": "cloud-upload-filled",
"unicode": "\ue648"
},
{
"font_class": "color",
"unicode": "\ue6cf"
},
{
"font_class": "color-filled",
"unicode": "\ue6c9"
},
{
"font_class": "compose",
"unicode": "\ue67f"
},
{
"font_class": "contact",
"unicode": "\ue693"
},
{
"font_class": "contact-filled",
"unicode": "\ue695"
},
{
"font_class": "down",
"unicode": "\ue6b8"
},
{
"font_class": "bottom",
"unicode": "\ue6b8"
},
{
"font_class": "download",
"unicode": "\ue68d"
},
{
"font_class": "download-filled",
"unicode": "\ue681"
},
{
"font_class": "email",
"unicode": "\ue69e"
},
{
"font_class": "email-filled",
"unicode": "\ue69a"
},
{
"font_class": "eye",
"unicode": "\ue651"
},
{
"font_class": "eye-filled",
"unicode": "\ue66a"
},
{
"font_class": "eye-slash",
"unicode": "\ue6b3"
},
{
"font_class": "eye-slash-filled",
"unicode": "\ue6b4"
},
{
"font_class": "fire",
"unicode": "\ue6a1"
},
{
"font_class": "fire-filled",
"unicode": "\ue6c5"
},
{
"font_class": "flag",
"unicode": "\ue65f"
},
{
"font_class": "flag-filled",
"unicode": "\ue660"
},
{
"font_class": "folder-add",
"unicode": "\ue6a9"
},
{
"font_class": "folder-add-filled",
"unicode": "\ue6c8"
},
{
"font_class": "font",
"unicode": "\ue6a3"
},
{
"font_class": "forward",
"unicode": "\ue6ba"
},
{
"font_class": "gear",
"unicode": "\ue664"
},
{
"font_class": "gear-filled",
"unicode": "\ue661"
},
{
"font_class": "gift",
"unicode": "\ue6a4"
},
{
"font_class": "gift-filled",
"unicode": "\ue6c4"
},
{
"font_class": "hand-down",
"unicode": "\ue63d"
},
{
"font_class": "hand-down-filled",
"unicode": "\ue63c"
},
{
"font_class": "hand-up",
"unicode": "\ue63f"
},
{
"font_class": "hand-up-filled",
"unicode": "\ue63e"
},
{
"font_class": "headphones",
"unicode": "\ue630"
},
{
"font_class": "heart",
"unicode": "\ue639"
},
{
"font_class": "heart-filled",
"unicode": "\ue641"
},
{
"font_class": "help",
"unicode": "\ue679"
},
{
"font_class": "help-filled",
"unicode": "\ue674"
},
{
"font_class": "home",
"unicode": "\ue662"
},
{
"font_class": "home-filled",
"unicode": "\ue663"
},
{
"font_class": "image",
"unicode": "\ue670"
},
{
"font_class": "image-filled",
"unicode": "\ue678"
},
{
"font_class": "images",
"unicode": "\ue650"
},
{
"font_class": "images-filled",
"unicode": "\ue64b"
},
{
"font_class": "info",
"unicode": "\ue669"
},
{
"font_class": "info-filled",
"unicode": "\ue649"
},
{
"font_class": "left",
"unicode": "\ue6b7"
},
{
"font_class": "link",
"unicode": "\ue6a5"
},
{
"font_class": "list",
"unicode": "\ue644"
},
{
"font_class": "location",
"unicode": "\ue6ae"
},
{
"font_class": "location-filled",
"unicode": "\ue6af"
},
{
"font_class": "locked",
"unicode": "\ue66b"
},
{
"font_class": "locked-filled",
"unicode": "\ue668"
},
{
"font_class": "loop",
"unicode": "\ue633"
},
{
"font_class": "mail-open",
"unicode": "\ue643"
},
{
"font_class": "mail-open-filled",
"unicode": "\ue63a"
},
{
"font_class": "map",
"unicode": "\ue667"
},
{
"font_class": "map-filled",
"unicode": "\ue666"
},
{
"font_class": "map-pin",
"unicode": "\ue6ad"
},
{
"font_class": "map-pin-ellipse",
"unicode": "\ue6ac"
},
{
"font_class": "medal",
"unicode": "\ue6a2"
},
{
"font_class": "medal-filled",
"unicode": "\ue6c3"
},
{
"font_class": "mic",
"unicode": "\ue671"
},
{
"font_class": "mic-filled",
"unicode": "\ue677"
},
{
"font_class": "micoff",
"unicode": "\ue67e"
},
{
"font_class": "micoff-filled",
"unicode": "\ue6b0"
},
{
"font_class": "minus",
"unicode": "\ue66f"
},
{
"font_class": "minus-filled",
"unicode": "\ue67d"
},
{
"font_class": "more",
"unicode": "\ue64d"
},
{
"font_class": "more-filled",
"unicode": "\ue64e"
},
{
"font_class": "navigate",
"unicode": "\ue66e"
},
{
"font_class": "navigate-filled",
"unicode": "\ue67a"
},
{
"font_class": "notification",
"unicode": "\ue6a6"
},
{
"font_class": "notification-filled",
"unicode": "\ue6c1"
},
{
"font_class": "paperclip",
"unicode": "\ue652"
},
{
"font_class": "paperplane",
"unicode": "\ue672"
},
{
"font_class": "paperplane-filled",
"unicode": "\ue675"
},
{
"font_class": "person",
"unicode": "\ue699"
},
{
"font_class": "person-filled",
"unicode": "\ue69d"
},
{
"font_class": "personadd",
"unicode": "\ue69f"
},
{
"font_class": "personadd-filled",
"unicode": "\ue698"
},
{
"font_class": "personadd-filled-copy",
"unicode": "\ue6d1"
},
{
"font_class": "phone",
"unicode": "\ue69c"
},
{
"font_class": "phone-filled",
"unicode": "\ue69b"
},
{
"font_class": "plus",
"unicode": "\ue676"
},
{
"font_class": "plus-filled",
"unicode": "\ue6c7"
},
{
"font_class": "plusempty",
"unicode": "\ue67b"
},
{
"font_class": "pulldown",
"unicode": "\ue632"
},
{
"font_class": "pyq",
"unicode": "\ue682"
},
{
"font_class": "qq",
"unicode": "\ue680"
},
{
"font_class": "redo",
"unicode": "\ue64a"
},
{
"font_class": "redo-filled",
"unicode": "\ue655"
},
{
"font_class": "refresh",
"unicode": "\ue657"
},
{
"font_class": "refresh-filled",
"unicode": "\ue656"
},
{
"font_class": "refreshempty",
"unicode": "\ue6bf"
},
{
"font_class": "reload",
"unicode": "\ue6b2"
},
{
"font_class": "right",
"unicode": "\ue6b5"
},
{
"font_class": "scan",
"unicode": "\ue62a"
},
{
"font_class": "search",
"unicode": "\ue654"
},
{
"font_class": "settings",
"unicode": "\ue653"
},
{
"font_class": "settings-filled",
"unicode": "\ue6ce"
},
{
"font_class": "shop",
"unicode": "\ue62f"
},
{
"font_class": "shop-filled",
"unicode": "\ue6cd"
},
{
"font_class": "smallcircle",
"unicode": "\ue67c"
},
{
"font_class": "smallcircle-filled",
"unicode": "\ue665"
},
{
"font_class": "sound",
"unicode": "\ue684"
},
{
"font_class": "sound-filled",
"unicode": "\ue686"
},
{
"font_class": "spinner-cycle",
"unicode": "\ue68a"
},
{
"font_class": "staff",
"unicode": "\ue6a7"
},
{
"font_class": "staff-filled",
"unicode": "\ue6cb"
},
{
"font_class": "star",
"unicode": "\ue688"
},
{
"font_class": "star-filled",
"unicode": "\ue68f"
},
{
"font_class": "starhalf",
"unicode": "\ue683"
},
{
"font_class": "trash",
"unicode": "\ue687"
},
{
"font_class": "trash-filled",
"unicode": "\ue685"
},
{
"font_class": "tune",
"unicode": "\ue6aa"
},
{
"font_class": "tune-filled",
"unicode": "\ue6ca"
},
{
"font_class": "undo",
"unicode": "\ue64f"
},
{
"font_class": "undo-filled",
"unicode": "\ue64c"
},
{
"font_class": "up",
"unicode": "\ue6b6"
},
{
"font_class": "top",
"unicode": "\ue6b6"
},
{
"font_class": "upload",
"unicode": "\ue690"
},
{
"font_class": "upload-filled",
"unicode": "\ue68e"
},
{
"font_class": "videocam",
"unicode": "\ue68c"
},
{
"font_class": "videocam-filled",
"unicode": "\ue689"
},
{
"font_class": "vip",
"unicode": "\ue6a8"
},
{
"font_class": "vip-filled",
"unicode": "\ue6c6"
},
{
"font_class": "wallet",
"unicode": "\ue6b1"
},
{
"font_class": "wallet-filled",
"unicode": "\ue6c2"
},
{
"font_class": "weibo",
"unicode": "\ue68b"
},
{
"font_class": "weixin",
"unicode": "\ue691"
}
] as IconsDataItem[]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

View File

@@ -0,0 +1,649 @@
export const fontData = [
{
"font_class": "arrow-down",
"unicode": "\ue6be"
},
{
"font_class": "arrow-left",
"unicode": "\ue6bc"
},
{
"font_class": "arrow-right",
"unicode": "\ue6bb"
},
{
"font_class": "arrow-up",
"unicode": "\ue6bd"
},
{
"font_class": "auth",
"unicode": "\ue6ab"
},
{
"font_class": "auth-filled",
"unicode": "\ue6cc"
},
{
"font_class": "back",
"unicode": "\ue6b9"
},
{
"font_class": "bars",
"unicode": "\ue627"
},
{
"font_class": "calendar",
"unicode": "\ue6a0"
},
{
"font_class": "calendar-filled",
"unicode": "\ue6c0"
},
{
"font_class": "camera",
"unicode": "\ue65a"
},
{
"font_class": "camera-filled",
"unicode": "\ue658"
},
{
"font_class": "cart",
"unicode": "\ue631"
},
{
"font_class": "cart-filled",
"unicode": "\ue6d0"
},
{
"font_class": "chat",
"unicode": "\ue65d"
},
{
"font_class": "chat-filled",
"unicode": "\ue659"
},
{
"font_class": "chatboxes",
"unicode": "\ue696"
},
{
"font_class": "chatboxes-filled",
"unicode": "\ue692"
},
{
"font_class": "chatbubble",
"unicode": "\ue697"
},
{
"font_class": "chatbubble-filled",
"unicode": "\ue694"
},
{
"font_class": "checkbox",
"unicode": "\ue62b"
},
{
"font_class": "checkbox-filled",
"unicode": "\ue62c"
},
{
"font_class": "checkmarkempty",
"unicode": "\ue65c"
},
{
"font_class": "circle",
"unicode": "\ue65b"
},
{
"font_class": "circle-filled",
"unicode": "\ue65e"
},
{
"font_class": "clear",
"unicode": "\ue66d"
},
{
"font_class": "close",
"unicode": "\ue673"
},
{
"font_class": "closeempty",
"unicode": "\ue66c"
},
{
"font_class": "cloud-download",
"unicode": "\ue647"
},
{
"font_class": "cloud-download-filled",
"unicode": "\ue646"
},
{
"font_class": "cloud-upload",
"unicode": "\ue645"
},
{
"font_class": "cloud-upload-filled",
"unicode": "\ue648"
},
{
"font_class": "color",
"unicode": "\ue6cf"
},
{
"font_class": "color-filled",
"unicode": "\ue6c9"
},
{
"font_class": "compose",
"unicode": "\ue67f"
},
{
"font_class": "contact",
"unicode": "\ue693"
},
{
"font_class": "contact-filled",
"unicode": "\ue695"
},
{
"font_class": "down",
"unicode": "\ue6b8"
},
{
"font_class": "bottom",
"unicode": "\ue6b8"
},
{
"font_class": "download",
"unicode": "\ue68d"
},
{
"font_class": "download-filled",
"unicode": "\ue681"
},
{
"font_class": "email",
"unicode": "\ue69e"
},
{
"font_class": "email-filled",
"unicode": "\ue69a"
},
{
"font_class": "eye",
"unicode": "\ue651"
},
{
"font_class": "eye-filled",
"unicode": "\ue66a"
},
{
"font_class": "eye-slash",
"unicode": "\ue6b3"
},
{
"font_class": "eye-slash-filled",
"unicode": "\ue6b4"
},
{
"font_class": "fire",
"unicode": "\ue6a1"
},
{
"font_class": "fire-filled",
"unicode": "\ue6c5"
},
{
"font_class": "flag",
"unicode": "\ue65f"
},
{
"font_class": "flag-filled",
"unicode": "\ue660"
},
{
"font_class": "folder-add",
"unicode": "\ue6a9"
},
{
"font_class": "folder-add-filled",
"unicode": "\ue6c8"
},
{
"font_class": "font",
"unicode": "\ue6a3"
},
{
"font_class": "forward",
"unicode": "\ue6ba"
},
{
"font_class": "gear",
"unicode": "\ue664"
},
{
"font_class": "gear-filled",
"unicode": "\ue661"
},
{
"font_class": "gift",
"unicode": "\ue6a4"
},
{
"font_class": "gift-filled",
"unicode": "\ue6c4"
},
{
"font_class": "hand-down",
"unicode": "\ue63d"
},
{
"font_class": "hand-down-filled",
"unicode": "\ue63c"
},
{
"font_class": "hand-up",
"unicode": "\ue63f"
},
{
"font_class": "hand-up-filled",
"unicode": "\ue63e"
},
{
"font_class": "headphones",
"unicode": "\ue630"
},
{
"font_class": "heart",
"unicode": "\ue639"
},
{
"font_class": "heart-filled",
"unicode": "\ue641"
},
{
"font_class": "help",
"unicode": "\ue679"
},
{
"font_class": "help-filled",
"unicode": "\ue674"
},
{
"font_class": "home",
"unicode": "\ue662"
},
{
"font_class": "home-filled",
"unicode": "\ue663"
},
{
"font_class": "image",
"unicode": "\ue670"
},
{
"font_class": "image-filled",
"unicode": "\ue678"
},
{
"font_class": "images",
"unicode": "\ue650"
},
{
"font_class": "images-filled",
"unicode": "\ue64b"
},
{
"font_class": "info",
"unicode": "\ue669"
},
{
"font_class": "info-filled",
"unicode": "\ue649"
},
{
"font_class": "left",
"unicode": "\ue6b7"
},
{
"font_class": "link",
"unicode": "\ue6a5"
},
{
"font_class": "list",
"unicode": "\ue644"
},
{
"font_class": "location",
"unicode": "\ue6ae"
},
{
"font_class": "location-filled",
"unicode": "\ue6af"
},
{
"font_class": "locked",
"unicode": "\ue66b"
},
{
"font_class": "locked-filled",
"unicode": "\ue668"
},
{
"font_class": "loop",
"unicode": "\ue633"
},
{
"font_class": "mail-open",
"unicode": "\ue643"
},
{
"font_class": "mail-open-filled",
"unicode": "\ue63a"
},
{
"font_class": "map",
"unicode": "\ue667"
},
{
"font_class": "map-filled",
"unicode": "\ue666"
},
{
"font_class": "map-pin",
"unicode": "\ue6ad"
},
{
"font_class": "map-pin-ellipse",
"unicode": "\ue6ac"
},
{
"font_class": "medal",
"unicode": "\ue6a2"
},
{
"font_class": "medal-filled",
"unicode": "\ue6c3"
},
{
"font_class": "mic",
"unicode": "\ue671"
},
{
"font_class": "mic-filled",
"unicode": "\ue677"
},
{
"font_class": "micoff",
"unicode": "\ue67e"
},
{
"font_class": "micoff-filled",
"unicode": "\ue6b0"
},
{
"font_class": "minus",
"unicode": "\ue66f"
},
{
"font_class": "minus-filled",
"unicode": "\ue67d"
},
{
"font_class": "more",
"unicode": "\ue64d"
},
{
"font_class": "more-filled",
"unicode": "\ue64e"
},
{
"font_class": "navigate",
"unicode": "\ue66e"
},
{
"font_class": "navigate-filled",
"unicode": "\ue67a"
},
{
"font_class": "notification",
"unicode": "\ue6a6"
},
{
"font_class": "notification-filled",
"unicode": "\ue6c1"
},
{
"font_class": "paperclip",
"unicode": "\ue652"
},
{
"font_class": "paperplane",
"unicode": "\ue672"
},
{
"font_class": "paperplane-filled",
"unicode": "\ue675"
},
{
"font_class": "person",
"unicode": "\ue699"
},
{
"font_class": "person-filled",
"unicode": "\ue69d"
},
{
"font_class": "personadd",
"unicode": "\ue69f"
},
{
"font_class": "personadd-filled",
"unicode": "\ue698"
},
{
"font_class": "personadd-filled-copy",
"unicode": "\ue6d1"
},
{
"font_class": "phone",
"unicode": "\ue69c"
},
{
"font_class": "phone-filled",
"unicode": "\ue69b"
},
{
"font_class": "plus",
"unicode": "\ue676"
},
{
"font_class": "plus-filled",
"unicode": "\ue6c7"
},
{
"font_class": "plusempty",
"unicode": "\ue67b"
},
{
"font_class": "pulldown",
"unicode": "\ue632"
},
{
"font_class": "pyq",
"unicode": "\ue682"
},
{
"font_class": "qq",
"unicode": "\ue680"
},
{
"font_class": "redo",
"unicode": "\ue64a"
},
{
"font_class": "redo-filled",
"unicode": "\ue655"
},
{
"font_class": "refresh",
"unicode": "\ue657"
},
{
"font_class": "refresh-filled",
"unicode": "\ue656"
},
{
"font_class": "refreshempty",
"unicode": "\ue6bf"
},
{
"font_class": "reload",
"unicode": "\ue6b2"
},
{
"font_class": "right",
"unicode": "\ue6b5"
},
{
"font_class": "scan",
"unicode": "\ue62a"
},
{
"font_class": "search",
"unicode": "\ue654"
},
{
"font_class": "settings",
"unicode": "\ue653"
},
{
"font_class": "settings-filled",
"unicode": "\ue6ce"
},
{
"font_class": "shop",
"unicode": "\ue62f"
},
{
"font_class": "shop-filled",
"unicode": "\ue6cd"
},
{
"font_class": "smallcircle",
"unicode": "\ue67c"
},
{
"font_class": "smallcircle-filled",
"unicode": "\ue665"
},
{
"font_class": "sound",
"unicode": "\ue684"
},
{
"font_class": "sound-filled",
"unicode": "\ue686"
},
{
"font_class": "spinner-cycle",
"unicode": "\ue68a"
},
{
"font_class": "staff",
"unicode": "\ue6a7"
},
{
"font_class": "staff-filled",
"unicode": "\ue6cb"
},
{
"font_class": "star",
"unicode": "\ue688"
},
{
"font_class": "star-filled",
"unicode": "\ue68f"
},
{
"font_class": "starhalf",
"unicode": "\ue683"
},
{
"font_class": "trash",
"unicode": "\ue687"
},
{
"font_class": "trash-filled",
"unicode": "\ue685"
},
{
"font_class": "tune",
"unicode": "\ue6aa"
},
{
"font_class": "tune-filled",
"unicode": "\ue6ca"
},
{
"font_class": "undo",
"unicode": "\ue64f"
},
{
"font_class": "undo-filled",
"unicode": "\ue64c"
},
{
"font_class": "up",
"unicode": "\ue6b6"
},
{
"font_class": "top",
"unicode": "\ue6b6"
},
{
"font_class": "upload",
"unicode": "\ue690"
},
{
"font_class": "upload-filled",
"unicode": "\ue68e"
},
{
"font_class": "videocam",
"unicode": "\ue68c"
},
{
"font_class": "videocam-filled",
"unicode": "\ue689"
},
{
"font_class": "vip",
"unicode": "\ue6a8"
},
{
"font_class": "vip-filled",
"unicode": "\ue6c6"
},
{
"font_class": "wallet",
"unicode": "\ue6b1"
},
{
"font_class": "wallet-filled",
"unicode": "\ue6c2"
},
{
"font_class": "weibo",
"unicode": "\ue68b"
},
{
"font_class": "weixin",
"unicode": "\ue691"
}
]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

View File

@@ -1,7 +1,7 @@
{
"id": "uni-icons",
"displayName": "uni-icons 图标",
"version": "1.3.5",
"version": "2.0.12",
"description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
"keywords": [
"uni-ui",
@@ -11,16 +11,14 @@
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.2.14"
"HBuilderX": "^3.2.14",
"uni-app": "^4.08",
"uni-app-x": "^4.61"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
@@ -37,48 +35,75 @@
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"dependencies": [
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
"uni-app": {
"vue": {
"vue2": "",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "-",
"android": {
"extVersion": "",
"minVersion": "29"
},
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "√",
"baidu": "√",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "√",
"lark": "-"
},
"quickapp": {
"huawei": "√",
"union": "√"
}
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
"uni-app-x": {
"web": {
"safari": "",
"chrome": ""
},
"app": {
"android": {
"extVersion": "",
"minVersion": "29"
},
"ios": "",
"harmony": ""
},
"mp": {
"weixin": ""
}
}
}
}