修改生产模块页面,增加顶部标签,新建分包pages-production,将生产页面移动至分包下面。

下一步制作曲线页面,统计页面,继续做好设备,安防等页面。
This commit is contained in:
liaodeyun 2025-09-11 17:58:34 +08:00
parent adcdb86db7
commit 6bac6dc89d
19 changed files with 3091 additions and 279 deletions

View File

@ -6,169 +6,169 @@ import { loadEnv } from 'vite'
// //
const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env')) const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env'))
const { const {
VITE_APP_TITLE, VITE_APP_TITLE,
VITE_UNI_APPID, VITE_UNI_APPID,
VITE_WX_APPID, VITE_WX_APPID,
VITE_APP_PUBLIC_BASE, VITE_APP_PUBLIC_BASE,
VITE_FALLBACK_LOCALE, VITE_FALLBACK_LOCALE,
} = env } = env
export default defineManifestConfig({ export default defineManifestConfig({
name: VITE_APP_TITLE, name: VITE_APP_TITLE,
appid: VITE_UNI_APPID, appid: VITE_UNI_APPID,
description: '', description: '',
versionName: '2.2.0', versionName: '2.3.0',
versionCode: '20250903', versionCode: '202509011',
transformPx: false, transformPx: false,
locale: VITE_FALLBACK_LOCALE, // 'zh-Hans' locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
/* 5+App特有相关 */ /* 5+App特有相关 */
'app-plus': { 'app-plus': {
usingComponents: true, usingComponents: true,
nvueStyleCompiler: 'uni-app', nvueStyleCompiler: 'uni-app',
compilerVersion: 3, compilerVersion: 3,
android: { android: {
webView: { webView: {
domStorageEnabled: true, domStorageEnabled: true,
databaseEnabled: true, databaseEnabled: true,
allowFileAccess: true allowFileAccess: true
} }
},
compatible: {
ignoreVersion: true,
},
webview: {
debug: true,
crossDomain: true //
},
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {
Maps: {},
Messaging: {},
Camera: {},
},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
minSdkVersion: 26,
targetSdkVersion: 30,
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
],
// app访 by
schemes: "szcxapp"
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {
maps: {
amap: {
name: 'amap_15931993294Bqxlq8EgG',
appkey_ios: 'c913e46ffdf548ebc56ac1cf4d883e7e',
appkey_android: 'c913e46ffdf548ebc56ac1cf4d883e7e',
},
},
},
/* 图标配置 */
icons: {
android: {
hdpi: 'src/static/app/icons/72x72.png',
xhdpi: 'src/static/app/icons/96x96.png',
xxhdpi: 'src/static/app/icons/144x144.png',
xxxhdpi: 'src/static/app/icons/192x192.png',
},
ios: {
appstore: 'src/static/app/icons/1024x1024.png',
ipad: {
app: 'src/static/app/icons/76x76.png',
'app@2x': 'src/static/app/icons/152x152.png',
notification: 'src/static/app/icons/20x20.png',
'notification@2x': 'src/static/app/icons/40x40.png',
'proapp@2x': 'src/static/app/icons/167x167.png',
settings: 'src/static/app/icons/29x29.png',
'settings@2x': 'src/static/app/icons/58x58.png',
spotlight: 'src/static/app/icons/40x40.png',
'spotlight@2x': 'src/static/app/icons/80x80.png',
},
iphone: {
'app@2x': 'src/static/app/icons/120x120.png',
'app@3x': 'src/static/app/icons/180x180.png',
'notification@2x': 'src/static/app/icons/40x40.png',
'notification@3x': 'src/static/app/icons/60x60.png',
'settings@2x': 'src/static/app/icons/58x58.png',
'settings@3x': 'src/static/app/icons/87x87.png',
'spotlight@2x': 'src/static/app/icons/80x80.png',
'spotlight@3x': 'src/static/app/icons/120x120.png',
},
},
},
},
}, },
compatible: { /* 快应用特有相关 */
ignoreVersion: true, quickapp: {},
}, /* 小程序特有相关 */
webview: { 'mp-weixin': {
debug: true, appid: VITE_WX_APPID,
crossDomain: true // setting: {
urlCheck: false,
minified: true
},
usingComponents: true,
// __usePrivacyCheck__: true,
}, },
splashscreen: { 'mp-alipay': {
alwaysShowBeforeRender: true, usingComponents: true,
waiting: true, styleIsolation: 'shared',
autoclose: true, },
delay: 0, 'mp-baidu': {
}, usingComponents: true,
/* 模块配置 */ },
modules: { 'mp-toutiao': {
Maps: {}, usingComponents: true,
Messaging: {}, },
Camera: {}, h5: {
}, router: {
/* 应用发布信息 */ base: VITE_APP_PUBLIC_BASE,
distribute: { },
/* android打包配置 */ sdkConfigs: {
android: { maps: {
minSdkVersion: 26, amap: {
targetSdkVersion: 30, key: '21f194a0d33197f874f7bbdd198419be',
abiFilters: ['armeabi-v7a', 'arm64-v8a'], securityJsCode: 'a46b425f31a4de445b2966d998fba851',
permissions: [ serviceHost: '',
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>', },
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>', },
'<uses-permission android:name="android.permission.VIBRATE"/>', },
'<uses-permission android:name="android.permission.READ_LOGS"/>', },
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>', 'app-harmony': {
'<uses-feature android:name="android.hardware.camera.autofocus"/>', distribute: {
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>', bundleName: 'uniapp.demo.test',
'<uses-permission android:name="android.permission.CAMERA"/>', },
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>', },
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>', uniStatistics: {
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>', enable: false,
'<uses-permission android:name="android.permission.WAKE_LOCK"/>', },
'<uses-permission android:name="android.permission.FLASHLIGHT"/>', vueVersion: '3',
'<uses-feature android:name="android.hardware.camera"/>', })
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
],
// app访 by
schemes : "szcxapp"
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {
maps: {
amap: {
name: 'amap_15931993294Bqxlq8EgG',
appkey_ios: 'c913e46ffdf548ebc56ac1cf4d883e7e',
appkey_android: 'c913e46ffdf548ebc56ac1cf4d883e7e',
},
},
},
/* 图标配置 */
icons: {
android: {
hdpi: 'src/static/app/icons/72x72.png',
xhdpi: 'src/static/app/icons/96x96.png',
xxhdpi: 'src/static/app/icons/144x144.png',
xxxhdpi: 'src/static/app/icons/192x192.png',
},
ios: {
appstore: 'src/static/app/icons/1024x1024.png',
ipad: {
app: 'src/static/app/icons/76x76.png',
'app@2x': 'src/static/app/icons/152x152.png',
notification: 'src/static/app/icons/20x20.png',
'notification@2x': 'src/static/app/icons/40x40.png',
'proapp@2x': 'src/static/app/icons/167x167.png',
settings: 'src/static/app/icons/29x29.png',
'settings@2x': 'src/static/app/icons/58x58.png',
spotlight: 'src/static/app/icons/40x40.png',
'spotlight@2x': 'src/static/app/icons/80x80.png',
},
iphone: {
'app@2x': 'src/static/app/icons/120x120.png',
'app@3x': 'src/static/app/icons/180x180.png',
'notification@2x': 'src/static/app/icons/40x40.png',
'notification@3x': 'src/static/app/icons/60x60.png',
'settings@2x': 'src/static/app/icons/58x58.png',
'settings@3x': 'src/static/app/icons/87x87.png',
'spotlight@2x': 'src/static/app/icons/80x80.png',
'spotlight@3x': 'src/static/app/icons/120x120.png',
},
},
},
},
},
/* 快应用特有相关 */
quickapp: {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
minified: true
},
usingComponents: true,
// __usePrivacyCheck__: true,
},
'mp-alipay': {
usingComponents: true,
styleIsolation: 'shared',
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
h5: {
router: {
base: VITE_APP_PUBLIC_BASE,
},
sdkConfigs: {
maps: {
amap: {
key: '21f194a0d33197f874f7bbdd198419be',
securityJsCode: 'a46b425f31a4de445b2966d998fba851',
serviceHost: '',
},
},
},
},
'app-harmony': {
distribute: {
bundleName: 'uniapp.demo.test',
},
},
uniStatistics: {
enable: false,
},
vueVersion: '3',
})

View File

@ -48,4 +48,12 @@ export function queryJldDataByZc(params : object) { // 获取站场计量点实
method: 'GET', method: 'GET',
data: params data: params
}) })
}
export function queryJldDataByJldID(params : object) { //
return http({
url: 'http://10.75.166.6:9999/Gyk/jldls/cxcSssjLssjJldls/getLssjByJldId',
method: 'GET',
data: params
})
} }

View File

@ -2,8 +2,8 @@
"name": "数智产销", "name": "数智产销",
"appid": "__UNI__9F097F0", "appid": "__UNI__9F097F0",
"description": "", "description": "",
"versionName": "2.2.0", "versionName": "2.3.0",
"versionCode": "20250903", "versionCode": "202509011",
"transformPx": false, "transformPx": false,
"app-plus": { "app-plus": {
"usingComponents": true, "usingComponents": true,

View File

@ -0,0 +1,274 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '历史数据',
},
}
</route>
<template>
<PageLayout navTitle="历史数据">
<view class="stats-container">
<uni-title v-if="type === 'trq'" :title="name.unit + '天然气---单位(万方)'" color="blue" type="h2"></uni-title>
<uni-title v-if="type === 'yy'" :title="name.dw + '原油---单位(吨)'" color="blue" type="h2"></uni-title>
</view>
<view class="dateSelect">
<cxc-szcx-dateRangeSelect :mode="'range'" v-model="dateRange"></cxc-szcx-dateRangeSelect>
</view>
<cxc-szcx-lineChart v-if="type === 'trq'" :dataList="dataList" x-field="rqDate" y-field="rq" legend-field="unit"
:reference-value="0"></cxc-szcx-lineChart>
<cxc-szcx-lineChart v-if="type === 'yy'" :dataList="dataList" x-field="scrq" y-field="rcwy" legend-field="dw"
:reference-value="0"></cxc-szcx-lineChart>
<view style="margin: 0 15px">
<view class="stats-container">
<view class="stats-item">最大值: {{ dataStats.max }}</view>
<view class="stats-item">最小值: {{ dataStats.min }}</view>
<view class="stats-item">平均值: {{ dataStats.average }}</view>
</view>
<wd-table :data="dataList" :height="800" v-if="type === 'trq'" :index="true">
<wd-table-col prop="unit" label="名称" :width="screenWidth / 3" align="center"></wd-table-col>
<wd-table-col prop="rqDate" label="日期" :width="screenWidth / 4" align="center"></wd-table-col>
<wd-table-col prop="rq" label="日气量" :width="screenWidth / 5" align="center"></wd-table-col>
</wd-table>
<wd-table :data="dataList" :height="800" v-if="type === 'yy'" :index="true">
<wd-table-col prop="dw" label="名称" :width="screenWidth / 3" align="center"></wd-table-col>
<wd-table-col prop="scrq" label="日期" :width="screenWidth / 4" align="center"></wd-table-col>
<wd-table-col prop="rcwy" label="日油量" :width="screenWidth / 5" align="center"></wd-table-col>
</wd-table>
</view>
</PageLayout>
</template>
<script setup>
import {
queryJinriShengchansj,
queryJinriYuanyouShengchansj
} from '@/api/production';
import {
formatDate,
getDateAfterDays,
getDateAfterMonths
} from '@/utils/dateTime';
const name = ref({});
const type = ref('');
const dateRange = ref([]);
const dataList = ref([]);
const endDate = ref('');
const startDate = ref('');
const dataStats = ref({
min: 0,
max: 0,
avg: 0
});
const screenWidth = ref(0)
const calculateTableHeight = () => {
const systemInfo = uni.getSystemInfoSync();
screenWidth.value = systemInfo.screenWidth;
}
const getJinriShengchansj = (tempDateRange) => {
// console.log(tempDateRange);
//
if (!tempDateRange || tempDateRange.some((date) => !convertToDate(date))) {
console.error('收到无效日期范围:', tempDateRange);
return;
}
let queryParms = {};
dataList.value = [];
queryParms.gas = name.value.gas;
queryParms.unit = name.value.unit;
queryParms.rqDate_begin = formatDate(tempDateRange[0]);
queryParms.rqDate_end = formatDate(tempDateRange[1]);
queryParms.pageSize = 500;
//
if (!queryParms.rqDate_begin || !queryParms.rqDate_end) {
console.error('参数格式化失败:', queryParms);
return;
}
// console.log(queryParms);
queryJinriShengchansj(queryParms).then((res) => {
if (res.success) {
// console.log(res);
dataList.value = res.result.records;
dataStats.value = calculateStats(dataList.value, 'rq');
}
});
};
const getJinriYuanyouShengchansj = (tempDateRange) => {
console.log(tempDateRange);
//
if (!tempDateRange || tempDateRange.some((date) => !convertToDate(date))) {
console.error('收到无效日期范围:', tempDateRange);
return;
}
let queryParms = {};
dataList.value = [];
queryParms.dw = name.value.dw;
queryParms.scrq_begin = formatDate(tempDateRange[0]);
queryParms.scrq_end = formatDate(tempDateRange[1]);
queryParms.pageSize = 500;
//
if (!queryParms.scrq_begin || !queryParms.scrq_end) {
console.error('参数格式化失败:', queryParms);
return;
}
console.log(queryParms);
queryJinriYuanyouShengchansj(queryParms).then((res) => {
if (res.success) {
console.log(res);
dataList.value = res.result.records;
dataStats.value = calculateStats(dataList.value, 'rcwy').reverse();
}
});
};
function calculateStats(data, field) {
// field null undefined
const validData = data.filter((item) => item[field] !== null && item[field] !== undefined);
if (validData.length === 0) {
return {
max: null,
min: null,
average: null
};
}
//
let max = validData[0][field];
let min = validData[0][field];
let sum = 0;
//
for (let i = 0; i < validData.length; i++) {
const value = validData[i][field];
//
if (value > max) {
max = value;
}
//
if (value < min) {
min = value;
}
//
sum += value;
}
//
const average = (sum / validData.length).toFixed(4);
return {
max: max,
min: min,
average: average
};
}
function convertToDate(str) {
try {
const date = new Date(str);
//
if (isNaN(date.getTime())) {
return false;
}
return true;
} catch (error) {
return false;
//TODO handle the exception
}
}
//
watch(
[() => type.value, dateRange], // type dateRange
([newType, newDateRange]) => {
if (!newDateRange || !convertToDate(newDateRange[0]) || !convertToDate(newDateRange[1])) {
// console.error(':', newDateRange);
return;
}
// console.log(newType, newDateRange);
switch (
newType // 使 newType
) {
case 'trq':
getJinriShengchansj(newDateRange);
break;
case 'yy':
getJinriYuanyouShengchansj(newDateRange);
break;
default:
console.warn('未知类型:', newType);
}
}, {
immediate: true,
deep: true
}
);
onMounted(() => {
// nextTick();
// getJinriShengchansj(dateRange.value);
calculateTableHeight();
//
uni.onWindowResize(() => {
calculateTableHeight();
});
});
onLoad((options) => {
name.value = JSON.parse(options.data);
type.value = options.type;
// console.log(name.value, type.value);
const now = new Date();
if (now.getHours() < 11) {
endDate.value = getDateAfterDays(now, -1); //11
startDate.value = getDateAfterMonths(endDate.value, -1); //11
} else {
endDate.value = now;
startDate.value = getDateAfterMonths(endDate.value, -1); //11
}
dateRange.value = [startDate.value, endDate.value];
// console.log(1111, dateRange.value);
});
</script>
<style scoped>
.stats-container {
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
background-color: #f5f5f5;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 10px;
margin: 15 15px;
}
.dateSelect {
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
background-color: #f5f5f5;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 5px;
margin: 10px;
}
.stats-item {
font-size: 12px;
color: #00aa00;
font-weight: bold;
}
.stats-item+.stats-item {
border-left: 1px solid #ccc;
padding-left: 16px;
}
</style>

View File

@ -0,0 +1,633 @@
<template>
<view class="content">
<!-- 标题行 -->
<view class="header-row">
<view class="title">天然气产量</view>
<view class="more" @click="selectMore">更多>></view>
</view>
</view>
<view class="container">
<view v-for="(item, index) in shishiArrSelect" :key="index" class="card-item"
@click="handleCardClick(item.gas)">
<view class="card">
<text class="title">{{ item.gas }}</text>
<view class="content">
<text class="label">气量</text>
<text class="value">{{ formatNumber(item.dailyVolume) || '-' }}</text>
</view>
<view class="content">
<text class="label">年累计</text>
<text class="value">{{ formatNumber(item.yearVolume) || '-' }}</text>
</view>
<view class="progress-bar">
<!-- 动态设置宽度和颜色 -->
<view class="progress"
:style="{ width: `${Math.abs(item.yearPerCent)}%`, 'background-color': item.yearPerCent < 0 ? '#ff4444' : '#007aff' }">
</view>
<!-- 显示带符号的百分比 -->
<text v-if="!(item.yearPlan === '' || item.yearPlan === '0')"
class="progress-text">{{ item.yearPerCent }}%</text>
</view>
</view>
</view>
</view>
<wd-popup v-model="popupSelect" position="top" background-color="#fff">
<view style="margin-top: 50px">
<view class="titlepopup">选择显示更多生产数据</view>
<view class="popupBtn">
<button size="mini" @click="selectDefault" style="padding: 8px 16px;">默认</button>
<button size="mini" @click="selectAll" style="padding: 8px 16px;">全选</button>
<button size="mini" @click="selectNo" style="padding: 8px 16px;">全不选</button>
</view>
<view class="popupcheckbox">
<uni-data-checkbox style="font-size: 14px" @change="onselectionchange" :localdata="shishiArr"
v-model="shishiArrDisplay" multiple :map="{ value: 'gas', text: 'gas' }"></uni-data-checkbox>
</view>
</view>
</wd-popup>
<!-- 数据弹窗 -->
<wd-popup v-model="popup" position="bottom" closable custom-style="height: 400px;" @close="handleClose">
<wd-cell-group :title="tableTitle">
<wd-table :data="filteredData" :height="800">
<wd-table-col prop="unit" label="名称" :width="screenWidth * 57 / 140 " align="center"></wd-table-col>
<wd-table-col prop="rq" label="日气量" :width="screenWidth / 5" align="center"></wd-table-col>
<wd-table-col prop="yearVolume" label="年累计" :width="screenWidth / 4" align="center"></wd-table-col>
<wd-table-col prop="" label="操作" :width="screenWidth / 7" align="center">
<template #value="{row}">
<text style="color: red;" @click="goHistory(row)">历史</text>
</template>
</wd-table-col>
</wd-table>
</wd-cell-group>
</wd-popup>
</template>
<script setup>
import {
queryJinriShengchansj,
queryYearShengchansj,
queryJinriTrqShengchansj
} from '@/api/production';
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
const shishiArr = ref([]);
const shishiArrSelect = ref([]);
const shishiArrDisplay = ref(['气井气', '商品量', '站线综合输差']);
const dataJinriUnit = ref([]);
const tableTitle = ref('')
const filteredData = ref([]);
const popup = ref(false);
const popupSelect = ref(false);
const strDate = ref('');
const dataJinri = ref([]);
const dataJinriSum = ref([]);
const dataJinriSumUnit = ref([]);
const qjqNdjs = ref(7500);
const splNdjs = ref(7220);
const zhqNdjs = ref(300);
const zhscNdjs = ref(50);
const screenWidth = ref(0)
const handleClose = () => {
popup.value = false
}
//
// const handleCardClick = (gas) => {
// let queryParms = {};
// filteredData.value = [];
// queryParms.day = strDate.value;
// queryParms.gas = gas;
// queryParms.unit = '23,';
// console.log(queryParms);
// queryJinriTrqShengchansj(queryParms).then((res) => {
// if (res.success) {
// console.log(res);
// filteredData.value = res.result;
// }
// });
// popup.value.open();
// };
function selectMore() {
popupSelect.value = true
}
function selectAll() {
shishiArrDisplay.value = [];
// console.log(9, shishiArr.value)
shishiArr.value.forEach((item) => {
// console.log(10, item)
shishiArrDisplay.value.push(item.gas);
});
onselectionchange()
popup.value = false;
}
function selectNo() {
shishiArrDisplay.value = [];
onselectionchange()
popup.value = false;
}
function selectDefault() {
shishiArrDisplay.value = ['气井气', '商品量', '站线综合输差'];
onselectionchange()
popup.value = false;
}
const onselectionchange = () => {
shishiArrSelect.value = [];
shishiArrDisplay.value.forEach((item) => {
shishiArrSelect.value.push(shishiArr.value.filter((item1) => item1.gas === item)[0]);
});
};
const handleCardClick = (gas) => {
tableTitle.value = gas + '数据详情 单位(万立方米)'
filteredData.value = dataJinriSumUnit.value.filter((item) => item.gas === gas);
filteredData.value = filteredData.value.map(item => {
return {
...item,
rq: formatNumber(item.rq),
yearVolume: formatNumber(item.yearVolume)
}
})
popup.value = true;
};
//
const closePopup = () => {
popup.value = false;
};
const calculateTableHeight = () => {
const systemInfo = uni.getSystemInfoSync();
screenWidth.value = systemInfo.screenWidth;
}
onMounted(() => {
getJinriTrqShengchansj();
getJinriShengchansj();
calculateTableHeight();
//
uni.onWindowResize(() => {
calculateTableHeight();
});
});
//
const formatNumber = (num) => {
let temp = 0;
try {
temp = parseFloat(num);
} catch (error) {
//TODO handle the exception
}
return temp.toFixed(4).replace(/\.?0+$/, '');
};
//
function calcZhsc(tempArray) {
let totalJinqi = {
gas: '总进气',
dailyVolume: 0,
yearVolume: 0,
yearPlan: '100',
yearPerCent: 0
};
let totalChuqi = {
gas: '总出气',
dailyVolume: 0,
yearVolume: 0,
yearPlan: '100',
yearPerCent: 0
};
const compositeZx = {
gas: '站线综合输差',
dailyVolume: 0,
yearVolume: 0,
yearPlan: '100',
yearPerCent: 0
}; //
const compositeJc = {
gas: '进出综合输差',
dailyVolume: 0,
yearVolume: 0,
yearPlan: '100',
yearPerCent: 0
};
const trqSpl = {
gas: '商品量',
dailyVolume: 0,
yearVolume: 0,
yearPlan: '7220',
yearPerCent: 0
};
//
tempArray.forEach((item) => {
if (item.gas === '站输差' || item.gas === '线输差') {
compositeZx.dailyVolume += parseFloat(item.dailyVolume) || 0;
compositeZx.yearVolume += parseFloat(item.yearVolume) || 0;
}
if (item.gas === '气井气' || item.gas === '外部气' || item.gas === '返回气') {
totalJinqi.dailyVolume += parseFloat(item.dailyVolume) || 0;
totalJinqi.yearVolume += parseFloat(item.yearVolume) || 0;
}
if (item.gas === '自耗气' || item.gas === '用户气' || item.gas === '返采油厂干气') {
totalChuqi.dailyVolume += parseFloat(item.dailyVolume) || 0;
totalChuqi.yearVolume += parseFloat(item.yearVolume) || 0;
}
if (item.gas === '气井气' || item.gas === '站输差' || item.gas === '线输差') {
trqSpl.dailyVolume += parseFloat(item.dailyVolume) || 0;
trqSpl.yearVolume += parseFloat(item.yearVolume) || 0;
}
if (item.gas === '自耗气') {
trqSpl.dailyVolume -= parseFloat(item.dailyVolume) || 0;
trqSpl.yearVolume -= parseFloat(item.yearVolume) || 0;
}
});
compositeZx.yearPerCent = calcPercent(compositeZx.yearPlan, compositeZx.yearVolume);
trqSpl.yearPerCent = calcPercent(trqSpl.yearPlan, trqSpl.yearVolume);
compositeJc.dailyVolume = (-totalJinqi.dailyVolume + totalChuqi.dailyVolume).toFixed(4);
compositeJc.yearVolume = (-totalJinqi.yearVolume + totalChuqi.yearVolume).toFixed(4);
compositeJc.yearPerCent = calcPercent(compositeJc.yearPlan, compositeJc.yearVolume);
tempArray.push(compositeZx);
tempArray.push(trqSpl);
return tempArray;
}
const getJinriTrqShengchansj = () => {
const now = new Date();
if (now.getHours() < 11) {
strDate.value = formatDate(getDateAfterDays(now, -1)).toString(); //11
} else {
strDate.value = formatDate(now).toString();
}
let queryParms = {};
shishiArr.value = [];
queryParms.day = strDate.value;
queryJinriTrqShengchansj(queryParms).then((res) => {
if (res.success) {
console.log(1, res);
let temp = res.result;
temp.forEach((item) => {
if (item.gas === '气井气') {
item.yearPlan = 7500;
item.yearPerCent = calcPercent(item.yearPlan, item.yearVolume);
}
});
shishiArr.value = calcZhsc(temp);
nextTick();
onselectionchange();
}
});
};
function goHistory(val) {
uni.navigateTo({
url: '/pages/production/ribaoshuju/rbsjLsxq?data=' + JSON.stringify(val) + '&type=trq'
});
}
function calcPercent(yearJihua, yearShiji) {
// 0
// 100
let plan = parseFloat(yearJihua === '' ? 0 : yearJihua);
let shiji = parseFloat(yearShiji);
let percent = 0;
//
if (plan > 0) {
percent = (shiji / plan) * 100;
percent = Math.min(percent, 100); // 100%
}
return parseFloat(percent.toFixed(2)); //
}
const getJinriShengchansj = () => {
const now = new Date();
if (now.getHours() < 11) {
strDate.value = formatDate(getDateAfterDays(now, -1)).toString(); //11
} else {
strDate.value = formatDate(now).toString();
}
let queryParms = {};
dataJinri.value = [];
dataJinriSum.value = [];
dataJinriSumUnit.value = [];
queryParms.rqDate = strDate.value;
queryParms.pageSize = 100;
queryJinriShengchansj(queryParms).then((res) => {
if (res.success) {
dataJinri.value = res.result.records;
dataJinriSumUnit.value = sumByUnit(dataJinri.value); //gas unit rq cq totalGas
// // 使 nextTick DOM
nextTick();
getYearShengchansj(); //
}
});
};
const getYearShengchansj = () => {
const now = new Date();
let year = formatDate(now).split('-')[0];
let queryParms = {};
queryParms.yearStart = year;
queryParms.yearEnd = year;
queryYearShengchansj(queryParms).then((res) => {
if (res.success) {
try {
let yearData = res.result[year];
dataJinriSumUnit.value.forEach((item) => {
yearData.forEach((itemYear) => {
// // console.log(item, itemYear);
if (item.setId === itemYear.setId) {
item.yearVolume = itemYear.yearSum || 0;
}
});
});
dataJinriSum.value = sumByGas(dataJinriSumUnit.value);
shishiArr.value.forEach((item) => {
dataJinriSum.value.forEach((itemjinri) => {
if (item.gas === itemjinri.gas) {
item.dailyVolume = itemjinri.rq.toFixed(4);
item.yearVolume = itemjinri.yearVolume.toFixed(4);
}
});
});
} catch (error) {
// console.log(error);
}
}
});
};
function sumByGas(records) {
const summaryMap = {};
try {
records.forEach((record) => {
const gas = record.gas;
if (!summaryMap[gas]) {
// gas
summaryMap[gas] = {
gas: gas,
rq: 0,
sq: 0,
totalGas: 0,
yearVolume: 0
};
}
// gas
summaryMap[gas].rq += record.rq || 0;
summaryMap[gas].sq += record.sq || 0;
summaryMap[gas].totalGas += record.totalGas || 0;
summaryMap[gas].yearVolume += record.yearVolume || 0;
});
return Object.values(summaryMap);
} catch (error) {
//TODO handle the exception
// console.log(error);
}
}
function sumByUnit(records) {
console.log(records);
const summaryMap = {};
try {
records.forEach((record) => {
const unit = record.unit;
const gas = record.gas;
// setId
const setId = record.setId
if (gas != "区输差") {
if (!summaryMap[setId]) {
// gas
summaryMap[setId] = {
unit: unit,
setId: setId,
gas: record.gas,
rq: 0,
sq: 0,
totalGas: 0,
yearVolume: 0
};
}
// unit
summaryMap[setId].rq += record.rq || 0;
summaryMap[setId].sq += record.sq || 0;
summaryMap[setId].totalGas += record.totalGas || 0;
summaryMap[setId].yearVolume += record.yearVolume || 0;
}
});
return Object.values(summaryMap);
} catch (error) {
//TODO handle the exception
// console.log(error);
}
}
</script>
<style lang="scss" scoped>
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10 10rpx;
border-bottom: 1rpx solid #eee;
margin-left: 10px;
margin-right: 10px;
}
.title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.titlepopup {
font-size: 35rpx;
font-weight: bold;
color: #333;
text-align: center;
padding: 20px;
margin-top: 10px;
}
/* 给包裹按钮的容器设置样式 */
.popupBtn {
display: grid;
/* 创建三个等宽的列 */
grid-template-columns: repeat(3, 1fr);
/* 设置列与列之间的间距 */
gap: 2px;
margin-bottom: 20px;
}
/* 添加按钮按下效果 */
.popupBtn button:active {
opacity: 0.8;
transform: scale(0.98);
transition: all 0.1s ease;
}
/* 给按钮设置通用样式 */
.popupBtn button {
border: none;
padding: 0px 10px;
background-color: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
}
/* 鼠标悬停在按钮上时的样式 */
.popupBtn button:hover {
background-color: #0056b3;
}
.more {
font-size: 26rpx;
color: #666;
cursor: pointer;
}
.more::after {
content: '';
width: 8rpx;
height: 8rpx;
border-top: 2rpx solid #666;
border-right: 2rpx solid #666;
transform: rotate(45deg);
margin-left: 8rpx;
vertical-align: middle;
}
/* 鼠标悬停效果 */
.more:hover {
color: #007aff;
}
.popupcheckbox {
/* 使用 Flexbox 布局 */
display: flex;
flex-wrap: wrap;
/* 允许换行 */
gap: 2px;
/* 设置复选框之间的间距 */
margin-left: 20px;
margin-bottom: 10px;
}
.popupcheckbox .uni-data-checkbox__item {
flex-basis: calc((100% / var(4)) - 10px);
box-sizing: border-box;
}
.container {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
gap: 20rpx;
}
.empty {
padding: 40rpx;
text-align: center;
color: #888;
font-size: 16rpx;
}
.card-item {
flex: 1 1 200rpx; // 300rpx formatNumber
min-width: 240rpx;
max-width: calc(50% - 10rpx); //
@media (min-width: 768px) {
max-width: calc(33.33% - 14rpx); // 3
}
}
.card {
background: #ececec;
border-radius: 16rpx;
padding: 15rpx;
box-shadow: 0 4rpx 12rpx rgba(179, 179, 179, 0.1);
.title {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 24rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #0000ff;
font-weight: 500;
}
}
}
.progress-bar {
position: relative;
height: 20px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
transition: all 0.3s;
}
.progress-text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
// color: red;
/* 保持红色 */
font-size: 12px;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
/* 提升可读性 */
}
</style>

View File

@ -0,0 +1,317 @@
<template>
<!-- <view style="margin-left: 20rpx;"> <uni-title :title="strDate + ':生产数据'" type="h1" color="red" /> -->
<!-- </view> -->
<view class="content">
<!-- 标题行 -->
<view class="header-row">
<view class="title">原油产量</view>
<view class="more"></view>
</view>
</view>
<view class="container">
<view v-for="(item, index) in shishiArr" :key="index" class="card-item" @click="handleCardClick(item.gas)">
<view class="card">
<!-- <text class="title">{{ item.gas }}</text> -->
<view class="content">
<text class="label">日油量</text>
<text class="value">{{ item.rcwy || '-' }}</text>
</view>
<view class="content">
<text class="label">月累计</text>
<text class="value">{{ item.yl || '-' }}</text>
</view>
<view class="content">
<text class="label">年累计</text>
<text class="value">{{ item.nl || '-' }}</text>
</view>
<view class="progress-bar">
<!-- 动态设置宽度和颜色 -->
<view class="progress" :style="{
width: `${Math.abs(item.yearPerCent)}%`,
'background-color': item.yearPerCent < 0 ? '#ff4444' : '#007aff'
}"></view>
<!-- 显示带符号的百分比 -->
<text v-if="!(item.yearPlan === '' || item.yearPlan === '0')"
class="progress-text">{{ item.yearPerCent }}%</text>
</view>
</view>
</view>
</view>
<!-- 数据弹窗 -->
<wd-popup v-model="popup" position="bottom" closable custom-style="height: 400px;" @close="handleClose">
<wd-cell-group title="原油数据详情 单位(吨)">
<wd-table :data="dataJinri" :height="800">
<wd-table-col prop="dw" label="名称" :width="screenWidth * 29 / 140" align="center"></wd-table-col>
<wd-table-col prop="rcwy" label="日油量" :width="screenWidth / 5" align="center"></wd-table-col>
<wd-table-col prop="yl" label="月累计" :width="screenWidth / 5" align="center"></wd-table-col>
<wd-table-col prop="nl" label="年累计" :width="screenWidth / 4" align="center"></wd-table-col>
<wd-table-col prop="" label="操作" :width="screenWidth / 7" align="center">
<template #value="{row}">
<text style="color: red;" @click="goHistory(row)">历史</text>
</template>
</wd-table-col>
</wd-table>
</wd-cell-group>
</wd-popup>
</template>
<script setup>
import {
queryJinriYuanyouShengchansj
} from '@/api/production';
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
const shishiArr = ref([{
gas: '原油产量',
rcwy: '',
yl: '',
nl: '',
yearPlan: '1500',
yearPerCent: ''
}]);
const screenWidth = ref(0)
const dataJinri = ref([]);
const dataJinriSum = ref([]);
const popup = ref(false);
//
const handleCardClick = () => {
popup.value = true
};
//
const closePopup = () => {
popup.value = false
};
onMounted(() => {
getJinriYuanyouShengchansj();
// getYearShengchansj();
calculateTableHeight();
//
uni.onWindowResize(() => {
calculateTableHeight();
});
});
const strDate = ref('');
//
const formatNumber = (num) => {
if (typeof num !== 'number') return '-';
return num.toFixed(4).replace(/\.?0+$/, '');
};
const handleClose = () => {
popup.value = false
}
function goHistory(val) {
uni.navigateTo({
url: '/pages/production/ribaoshuju/rbsjLsxq?data=' + JSON.stringify(val) + '&type=yy'
});
}
const calculateTableHeight = () => {
const systemInfo = uni.getSystemInfoSync();
screenWidth.value = systemInfo.screenWidth;
}
const getJinriYuanyouShengchansj = () => {
const now = new Date();
if (now.getHours() < 11) {
strDate.value = formatDate(getDateAfterDays(now, -1)).toString(); //11
} else {
strDate.value = formatDate(now).toString();
}
let queryParms = {};
dataJinri.value = [];
dataJinriSum.value = [];
queryParms.scrq = strDate.value;
queryParms.pageSize = 100;
queryJinriYuanyouShengchansj(queryParms).then((res) => {
if (res.success) {
dataJinri.value = res.result.records;
dataJinriSum.value = sumByOil(dataJinri.value); //gas unit rq cq totalGas
// // console.log(dataJinriSum.value);
nextTick();
shishiArr.value.forEach((item) => {
dataJinriSum.value.forEach((itemjinri) => {
item.rcwy = itemjinri.rcwy.toFixed(4);
item.nl = itemjinri.nl.toFixed(4);
item.yl = itemjinri.yl.toFixed(4);
item.yearPerCent = calcPercent(item.yearPlan, item.nl);
});
});
dataJinri.value = dataJinri.value.map(item => {
return {
...item,
rcwy: formatNumber(item.rcwy),
yl: formatNumber(item.yl),
nl: formatNumber(item.nl)
}
})
// getYearShengchansj(); //
}
});
};
function calcPercent(yearJihua, yearShiji) {
// 0
// 100
let plan = parseFloat(yearJihua === '' ? 0 : yearJihua);
let shiji = parseFloat(yearShiji);
let percent = 0;
//
if (plan > 0) {
percent = (shiji / plan) * 100;
percent = Math.min(percent, 100); // 100%
}
return parseFloat(percent.toFixed(2)); //
}
function sumByOil(records) {
// console.log(records);
const summaryMap = {};
try {
records.forEach((record) => {
const gas = record.gas;
if (!summaryMap[gas]) {
// gas
summaryMap[gas] = {
rcwy: 0,
yl: 0,
nl: 0
};
}
// gas
summaryMap[gas].rcwy += record.rcwy || 0;
summaryMap[gas].yl += record.yl || 0;
summaryMap[gas].nl += record.nl || 0;
});
return Object.values(summaryMap);
} catch (error) {
//TODO handle the exception
// console.log(error);
}
}
</script>
<style lang="scss" scoped>
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 0;
border-bottom: 1rpx solid #eee;
margin-left: 10px;
margin-right: 10px;
}
.title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.more {
font-size: 26rpx;
color: #666;
cursor: pointer;
}
.more::after {
content: '';
width: 8rpx;
height: 8rpx;
border-top: 2rpx solid #666;
border-right: 2rpx solid #666;
transform: rotate(45deg);
margin-left: 8rpx;
vertical-align: middle;
}
/* 鼠标悬停效果 */
.more:hover {
color: #007aff;
}
.container {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
gap: 20rpx;
}
.card-item {
flex: 1 1 200rpx; // 300rpx selectedGas formatNumber
min-width: 200rpx;
max-width: calc(50% - 10rpx); //
@media (min-width: 768px) {
max-width: calc(33.33% - 14rpx); // 3
}
}
.card {
background: #ececec;
border-radius: 16rpx;
padding: 15rpx;
box-shadow: 0 4rpx 12rpx rgba(197, 197, 197, 0.1);
.title {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 24rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #0000ff;
font-weight: 500;
}
}
}
.progress-bar {
position: relative;
height: 20px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
transition: all 0.3s;
}
.progress-text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: red;
/* 保持红色 */
font-size: 12px;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
/* 提升可读性 */
}
</style>

View File

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,507 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '历史数据图表',
},
}
</route>
<template>
<PageLayout navTitle="历史数据图表" backRouteName="index" routeMethod="pushTab">
<view class="chart-container">
<view class="chart-header">
<view class="chart-title-container">
<text class="chart-title">{{ chartTitle }}</text>
</view>
<view class="filter-section">
<view class="date-picker">
<uni-datetime-picker type="datetimerange" v-model="lssjTimeRang" @change="getTime"
:placeholder="['开始时间', '结束时间']" />
</view>
<view class="checkbox-container">
<label class="checkbox">
<checkbox :checked="tableVisibled" @change="onChangeTable" />
<text>{{ checkBoxText }}</text>
</label>
</view>
</view>
</view>
<!-- 图表容器 -->
<view id="myCharts" class="chart"></view>
<!-- 数据表格 -->
<view v-if="tableVisibled" class="table-container">
<uni-table border stripe emptyText="暂无数据" :loading="loading">
<uni-tr>
<uni-th width="60" align="center">序号</uni-th>
<uni-th align="center">日期时间</uni-th>
<uni-th align="center">温度()</uni-th>
<uni-th align="center">压力(MPa)</uni-th>
<uni-th align="center">压差(kPa)</uni-th>
<uni-th align="center">瞬时量(Nm³/d)</uni-th>
<uni-th align="center">今日量()</uni-th>
<uni-th align="center">今日时间()</uni-th>
<uni-th align="center">仪表状态</uni-th>
</uni-tr>
<uni-tr v-for="(item, index) in dataSource" :key="index">
<uni-td align="center">{{ index + 1 }}</uni-td>
<uni-td align="center">{{ formatTableTime(item.createTime) }}</uni-td>
<uni-td align="center">{{ item.wd }}</uni-td>
<uni-td align="center">{{ item.yl }}</uni-td>
<uni-td align="center">{{ item.yc }}</uni-td>
<uni-td align="center">{{ item.ssll }}</uni-td>
<uni-td align="center">{{ item.jrl }}</uni-td>
<uni-td align="center">{{ item.jrsj }}</uni-td>
<uni-td align="center">{{ item.zt }}</uni-td>
</uni-tr>
</uni-table>
<view class="pagination">
<uni-pagination :current="ipagination.current" :pageSize="ipagination.pageSize"
:total="ipagination.total" @change="handlePageChange" showTotal />
</view>
</view>
</view>
</PageLayout>
</template>
<script setup>
import {
ref,
onMounted,
onUnmounted,
watch,
nextTick
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
formatDate
} from '@/utils/dateTime.ts'
// props
const props = defineProps({
lineData: {
type: Object,
default: () => ({})
}
})
//
const urljlByJldID = ref('https://szcx.zyyt.sinopec.com/Gyk/jldls/cxcSssjLssjJldls/getLssjByJldId')
const colorData = [{
color: '#000000'
},
{
color: '#00007f'
},
{
color: '#ff0000'
},
{
color: '#005500'
},
{
color: '#55007f'
},
{
color: '#ffff00'
}
]
const option = ref({})
const legendData = ref([])
const xData = ref([])
const yaxis = ref({
type: 'value',
name: '',
min: 0,
max: 500,
position: 'left',
axisLabel: {
formatter: '{value}'
},
axisLine: {
lineStyle: {
color: '#61A0A8'
}
}
})
const yAxis = ref([])
const series = ref([])
const serie = ref({
field: '',
name: '',
showSymbol: false,
type: 'line',
data: [],
yAxisIndex: 1,
lineStyle: {
color: '#61A0A8',
width: 1
},
connectNulls: false
})
const tableVisibled = ref(false)
const checkBoxText = ref('显示历史报表')
const dataSource = ref([])
const loading = ref(false)
const ipagination = ref({
current: 1,
pageSize: 10,
total: 0
})
const jldID = ref('')
const jldName = ref('')
const chartTitle = ref('')
const lineName = ref([])
const startTime = ref('')
const endTime = ref('')
const lssjTimeRang = ref([])
const myCharts = ref(null)
//
const getDefaultTimeRange = () => {
const now = new Date()
const start = new Date(now)
start.setHours(8, 0, 0, 0)
const end = new Date(now)
end.setDate(end.getDate() + 1)
end.setHours(7, 59, 59, 0)
return [start.getTime(), end.getTime()]
}
//
const initChart = () => {
series.value = []
yAxis.value = []
xData.value = []
let positionIndex = 0
for (let i = 0; i < lineName.value.length; i++) {
const yaxisTemp = JSON.parse(JSON.stringify(yaxis.value))
legendData.value.push(`${lineName.value[i].name}(${lineName.value[i].unit})`)
if (lineName.value[i].field === 'ssll') {
yaxisTemp.position = 'left'
} else {
yaxisTemp.position = 'right'
yaxisTemp.offset = lineName.value[i].field === 'jrl' ? 60 * positionIndex : 55 * positionIndex
positionIndex += 1
}
yaxisTemp.name = `${lineName.value[i].name}(${lineName.value[i].unit})`
yaxisTemp.min = lineName.value[i].min
yaxisTemp.max = lineName.value[i].max
yaxisTemp.axisLine.lineStyle.color = colorData[i].color
yAxis.value.push(yaxisTemp)
const serieTemp = JSON.parse(JSON.stringify(serie.value))
serieTemp.name = `${lineName.value[i].name}(${lineName.value[i].unit})`
serieTemp.field = lineName.value[i].field
serieTemp.lineStyle.color = colorData[i].color
serieTemp.yAxisIndex = i
series.value.push(serieTemp)
}
option.value = {
color: ['#000000', '#00007f', '#ff0000', '#005500', '#55007f', '#ffff00'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
dataZoom: [{
connectNulls: false,
id: 'dataZoomX',
type: "inside",
roam: false,
show: true,
xAxisIndex: [0],
filterMode: 'empty',
throttle: 50,
zoomOnMouseWheel: true,
moveOnMouseMove: true
}, {
id: 'dataZoomY',
type: 'inside',
filterMode: 'empty',
zoomLock: false,
throttle: 50,
zoomOnMouseWheel: true,
moveOnMouseMove: true
}],
toolbox: {
feature: {
dataView: {
show: false,
readOnly: false
},
restore: {
show: false
},
saveAsImage: {
show: false
}
}
},
legend: {
data: legendData.value
},
grid: {
top: '10%',
left: '3%',
right: '10%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
axisTick: {
alignWithLabel: true
},
data: xData.value
},
yAxis: yAxis.value,
series: series.value
}
}
//
const getTime = (e) => {
if (e && e.length === 2) {
startTime.value = formatDate(e[0], 'yyyy-MM-dd hh:mm:ss')
endTime.value = formatDate(e[1], 'yyyy-MM-dd hh:mm:ss')
updateChart()
}
}
//
const onChangeTable = (e) => {
tableVisibled.value = !tableVisibled.value
checkBoxText.value = tableVisibled.value ? '显示历史趋势' : '显示历史报表'
}
//
const handlePageChange = (e) => {
ipagination.value.current = e.current
ipagination.value.pageSize = e.pageSize
}
//
const formatTableTime = (time) => {
if (!time) return ''
return formatDate(time, 'yyyy-MM-dd hh:mm:ss')
}
//
const maxArrValue = (data) => {
if (!data || data.length === 0) return 0
const list = []
for (let i in data) {
if (data[i] !== null) {
list.push(parseInt(data[i]))
}
}
if (list.length === 0) return 0
list.sort((num1, num2) => num1 - num2)
return list[list.length - 1]
}
//
const updateChart = () => {
dataSource.value = []
xData.value = []
for (let j = 0; j < series.value.length; j++) {
series.value[j].data = []
}
loading.value = true
getAction(url.list, {
jldId: jldID.value,
startTime: startTime.value,
endTime: endTime.value
}).then(res => {
loading.value = false
if (res.success) {
const ssData = JSON.parse(res.result)
dataSource.value = ssData
ipagination.value.total = ssData.length
for (let i = 0; i < ssData.length; i++) {
const ctime = formatDate(ssData[i].createTime, 'yyyy-MM-dd hh:mm:ss')
xData.value.push(ctime)
for (let j = 0; j < series.value.length; j++) {
let tempValue = ssData[i][lineName.value[j].field]
tempValue = tempValue === 0 ? null : tempValue
series.value[j].data.push(tempValue)
}
}
for (let mm = 0; mm < series.value.length; mm++) {
const maxVal = maxArrValue(series.value[mm].data)
yAxis.value[mm].max = Math.round(maxVal * 1.2)
}
option.value.xAxis.data = xData.value
if (myCharts.value) {
myCharts.value.setOption(option.value)
}
}
}).catch(() => {
loading.value = false
})
}
// ECharts
const initECharts = () => {
nextTick(() => {
// Uniapp使uni.createCanvasContext使ucharts
// 使Uniapp
const canvas = document.getElementById('myCharts')
if (canvas) {
// ECharts
myCharts.value = echarts.init(canvas)
myCharts.value.setOption(option.value)
}
})
}
// lineData
watch(() => props.lineData, (val) => {
jldID.value = val.jldID
jldName.value = val.jldName
chartTitle.value = `${val.jldName}历史趋势`
lineName.value = JSON.parse(JSON.stringify(val.lineName))
startTime.value = val.startTime
endTime.value = val.endTime
try {
if (val.startTime && val.endTime) {
const sDt = new Date(val.startTime)
const eDt = new Date(val.endTime)
lssjTimeRang.value = [sDt.getTime(), eDt.getTime()]
} else {
lssjTimeRang.value = getDefaultTimeRange()
}
} catch (e) {
console.error('时间格式错误', e)
lssjTimeRang.value = getDefaultTimeRange()
}
initChart()
updateChart()
}, {
deep: true,
immediate: true
})
//
onLoad(() => {
//
lssjTimeRang.value = getDefaultTimeRange()
// Uniappecharts使
setTimeout(() => {
initECharts()
}, 500)
})
//
onUnmounted(() => {
if (myCharts.value) {
myCharts.value.dispose()
}
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 100%;
padding: 20rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.chart-header {
margin-bottom: 20rpx;
}
.chart-title-container {
width: 100%;
text-align: center;
margin-bottom: 20rpx;
}
.chart-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.filter-section {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-bottom: 20rpx;
}
.date-picker {
width: 100%;
}
.checkbox-container {
display: flex;
align-items: center;
}
.checkbox {
display: flex;
align-items: center;
font-size: 28rpx;
}
.chart {
width: 100%;
height: 600rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
margin-bottom: 20rpx;
}
.table-container {
width: 100%;
overflow-x: auto;
}
.pagination {
margin-top: 20rpx;
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,231 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '历史数据图表',
},
}
</route>
<template>
<PageLayout :navbarShow="false" class="page-layout">
<view style="color: blue;font-size:18px;text-align: center;margin: 10px;width: 100%;">
<wd-text :text="jldData.jldname+ '实时趋势'" style="color: blue;font-size:18px;"></wd-text>
</view>
<view>
<uni-datetime-picker type="datetimerange" v-model="lssjTimeRange" @change="getTime" start-placeholder="开始时间"
end-placeholder="结束时间" />
</view>
<view style="text-align: center;padding: 5px;">
<wd-button custom-class="custom-value" size="small" plain clickable @click="handleForward">前一天</wd-button>
<wd-button custom-class="custom-value" size="small" plain clickable @click="handleNext">后一天</wd-button>
<wd-button custom-class="custom-value" size="small" plain clickable @click="handleQuery">查询数据</wd-button>
<wd-button custom-class="custom-value" size="small" plain clickable @click="handleDrawLine">生成曲线</wd-button>
</view>
<view>
<scroll-view direction="vertical" style="height: 200px;">
<wd-table :data="chartData" height="200px">
<wd-table-col prop="createTime" label="日期" align="center"></wd-table-col>
<wd-table-col prop="jldName" label="计量点名称" align="center"></wd-table-col>
<wd-table-col prop="yl" label="压力(MPa)" align="center"></wd-table-col>
<wd-table-col prop="wd" label="温度(℃)" align="center"></wd-table-col>
<wd-table-col prop="yc" label="差压(kPa)" align="center"></wd-table-col>
<wd-table-col prop="ssll" label="瞬时流量(m³/d)" align="center">
</wd-table-col>
<wd-table-col prop="jrl" label="今日流量(m³)" align="center">
</wd-table-col>
</wd-table>
</scroll-view>
</view>
<view v-if="drawLineFlag">
<cxc-szcx-multiLineChart :data-list="chartData" x-field="createTime" :y-fields="lineFields"
title="文增伴生气外输数据趋势" height="300"></cxc-szcx-multiLineChart>
</view>
</PageLayout>
</template>
<script setup>
import {
queryJldDataByJldID
} from '@/api/production'
import {
formatDate
} from '@/utils/dateTime.ts';
import {
ref,
onMounted,
computed,
nextTick,
watchEffect,
onUnmounted,
} from 'vue';
const lineFields = ref([{
name: '压力',
field: 'yl',
min: '0',
max: '2',
unit: 'MPa'
},
{
name: '温度',
field: 'wd',
min: '-10',
max: '40',
unit: '℃'
},
{
name: '差压',
field: 'yc',
min: '0',
max: '60',
unit: 'kPa'
},
{
name: '瞬时流量',
field: 'ssll',
min: '0',
max: '50000',
unit: 'm³/d'
},
{
name: '今日流量',
field: 'jrl',
min: '0',
max: '50000',
unit: 'm³'
}
])
const props = defineProps({
jldData: {
type: Object,
default: () => {}
}
})
const chartData = ref([])
const drawLineFlag = ref(false)
function handleQuery() {
const jldid = props.jldData.id;
queryJldDataByJldID({
jldId: jldid,
startTime: startTime.value,
endTime: endTime.value
}).then(res => {
chartData.value = JSON.parse(res.result)
chartData.value.forEach(item => {
item.createTime = formatDate(item.createTime, "YYYY-MM-DD HH:mm ss")
})
console.log(123, chartData.value)
})
}
function handleDrawLine() {
drawLineFlag.value = true
}
// -
function handleForward() {
// 使
lssjTimeRange.value = getDefaultTimeRange(-1);
}
//
function handleNext() {
//
const currentStart = new Date(startTime.value);
// 8
const today8AM = new Date();
today8AM.setHours(8, 0, 0, 0);
//
if (currentStart < today8AM || isSameDay(currentStart, today8AM)) {
lssjTimeRange.value = getDefaultTimeRange(1);
} else {
//
uni.showToast({
title: '已是最新数据',
icon: 'none',
duration: 1500
});
}
}
// 8:007:59
const getDefaultTimeRange = (days) => {
// 使
var baseDate;
if (startTime.value && new Date(startTime.value).toString() !== 'Invalid Date') {
baseDate = new Date(startTime.value);
} else {
// 使 fallback
baseDate = new Date();
}
// +
const targetDate = new Date(baseDate);
targetDate.setDate(baseDate.getDate() + days);
// 8:00:00
const start = new Date(targetDate);
start.setHours(8, 0, 0, 0);
// +17:59:59
const end = new Date(targetDate);
end.setDate(targetDate.getDate() + 1);
end.setHours(7, 59, 59, 0);
//
const formattedStart = formatDate(start, 'YYYY-MM-DD HH:mm:ss');
const formattedEnd = formatDate(end, 'YYYY-MM-DD HH:mm:ss');
//
startTime.value = formattedStart;
endTime.value = formattedEnd;
return [formattedStart, formattedEnd];
};
//
const isSameDay = (date1, date2) => {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
};
onMounted(() => {
//
setTimeout(() => {
lssjTimeRange.value = getDefaultTimeRange(0)
}, 500)
console.log(lssjTimeRange.value);
})
const startTime = ref('')
const endTime = ref('')
const lssjTimeRange = ref([])
//
const getTime = (e) => {
if (e && e.length === 2) {
startTime.value = formatDate(e[0], 'YYYY-MM-DD HH:mm:ss')
endTime.value = formatDate(e[1], 'YYYY-MM-DD HH:mm:ss')
// updateChart()
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,7 @@
<template>
<trqSssjVue></trqSssjVue>
</template>
<script setup>
import trqSssjVue from './trqSssj';
</script>

View File

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,360 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '生产数据',
},
}
</route>
<template>
<PageLayout :navbarShow="false" class="page-layout">
<!-- 标题行 -->
<wd-text :text="stationName ? stationName + '实时数据' : '实时数据'"
style="color: blue;font-size:18px;text-align: center;margin: 5px;"></wd-text>
<wd-cell :title="'时间:' + nowDateTime " center>
<wd-button custom-class="custom-value" size="small" plain clickable
@click="handleStop">{{caijiText}}</wd-button>
<wd-button custom-class="custom-value" size="small" plain clickable
@click="handleRightClick">选择站场</wd-button>
</wd-cell>
<scroll-view direction="vertical" class="scroll-container">
<view class="container">
<view v-for="(item, index) in jlData" :key="index" class="card">
<view class="field-item">
<text class="titlejl">{{ item.jldname }}</text>
<view class="status-circle"
:style="{ backgroundColor: item.yxzt==='运行' ? '#4CAF50' : '#F44336' }">
{{item.yxzt}}
</view>
</view>
<view class="field-list">
<!-- 压力 -->
<view class="field-item">
<text class="field-label">压力(MPa)</text>
<text class="field-value">{{ formatNumber(item.yl) || '-' }}</text>
</view>
<!-- 差压 -->
<view class="field-item">
<text class="field-label">差压(kPa)</text>
<text class="field-value">{{ formatNumber(item.yc) || '-' }}</text>
</view>
<!-- 温度 -->
<view class="field-item">
<text class="field-label">温度()</text>
<text class="field-value">{{ formatNumber(item.wd) || '-' }}</text>
</view>
<!-- 瞬时流量 -->
<view class="field-item">
<text class="field-labelssll" @click="openSssjChart(item)">瞬时流量(m³/d)</text>
<text class="field-value">{{ formatNumber(item.ssll) || '-' }}</text>
</view>
<!-- 今日流量 -->
<view class="field-item">
<text class="field-labelssll" @click="openlssjChart(item)">今日流量(m³)</text>
<text class="field-value">{{ formatNumber(item.jrl) || '-' }}</text>
</view>
<!-- 昨日流量 -->
<view class="field-item">
<text class="field-label">昨日流量()</text>
<text class="field-value">{{ formatNumber(item.zrl) || '-' }}</text>
</view>
<!-- 昨日时间 -->
<view class="field-item">
<text class="field-label">昨日时间(min)</text>
<text class="field-value">{{ formatNumber(item.zrsj) || '-' }}</text>
</view>
<!-- 今日时间 -->
<view class="field-item">
<text class="field-label">今日时间(min)</text>
<text class="field-value">{{ formatNumber(item.jrsj) || '-' }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
<wd-popup v-model="selectZc" position="bottom" custom-style="width:100%;height: 300px;" @close="handleClose">
<view style="font-size: 18px;text-align: center;color: blue;margin: 10px;">选择采输气站场</view>
<cxc-szcx-stationJl-select v-model="stationID" returnCodeOrID="id" @change="onChange">
</cxc-szcx-stationJl-select>
</wd-popup>
<wd-popup v-model="lssjFlag" position="bottom" custom-style="width:100%;height: 300px;" @close="handleClose">
<lssj-chart></lssj-chart>
</wd-popup>
<wd-popup v-model="sssjFlag" position="bottom" custom-style="width:100%;height: 70vh;" @close="handleClose">
<sssj-chart :jldData="jldData"></sssj-chart>
</wd-popup>
</PageLayout>
</template>
<script setup>
import {
queryJldZcList,
queryJldDataByZc
} from '@/api/production'
import {
ref,
onMounted,
computed,
nextTick,
watchEffect,
onUnmounted,
} from 'vue';
import {
onHide,
onShow
} from '@dcloudio/uni-app'
import {
formatDate
} from '@/utils/dateTime.ts';
import SssjChart from './chart/sssjChart'
import LssjChart from './chart/lssjChart'
const res = wx.getSystemInfoSync();
const statusHeight = res.statusBarHeight; //
const cusnavbarheight = (statusHeight + 30) + "px";
const selectZc = ref(false)
const lssjFlag = ref(false)
const sssjFlag = ref(false)
const nowDateTime = ref("")
const caijiText = ref("停止采集")
const stationList = ref([])
const stationID = ref("")
const stationName = ref(" ")
const jlData = ref([])
const jldData = ref({})
const sssjUrl = ref('wss://szcx.zyyt.sinopec.com/Gyk/websocket/')
const jlByzc = ref('https://szcx.zyyt.sinopec.com/Gyk/sssj/GetJlByZc')
function openSssjChart(e) {
sssjFlag.value = true;
console.log(11, e)
jldData.value = e
console.log(selectZc.value)
}
function openlssjChart(e) {
sssjFlag.value = true;
console.log(selectZc.value)
}
function handleRightClick() {
selectZc.value = true;
console.log(selectZc.value)
}
function handleClose() {
console.log(selectZc.value)
}
function onChange(e, data) {
console.log(2, e, data.value);
stationID.value = e
stationName.value = data.value.title
getScData(); //
}
function handleStop() {
if (caijiText.value === "停止采集") {
console.log('清除定时器,停止采集')
caijiText.value = "开始采集"
clearInterval(timer2.value);
timer2.value = null;
} else {
console.log('开始采集,打开定时器')
caijiText.value = "停止采集"
websocketheart()
}
}
const websock = ref(null);
const timer2 = ref(null);
//
const websocketheart = () => {
//
if (timer2.value) {
clearInterval(timer2.value);
timer2.value = null;
}
timer2.value = setInterval(() => {
nowDateTime.value = formatDate(new Date(), "YYYY-MM-DD HH:mm:ss")
if (websock.value && websock.value.readyState === 1) {
//
// connectSocketInit()
}
if (stationID.value) {
getScData();
}
}, 1000);
};
onMounted(() => {
websocketheart()
stationID.value = "1267633458481725442"
})
onUnmounted(() => {
clearInterval(timer2.value);
})
onHide(() => {
console.log('页面隐藏,清除定时器')
clearInterval(timer2.value);
timer2.value = null;
})
onShow(() => {
//
websocketheart()
})
const getScData = () => {
uni.request({
url: jlByzc.value + '?zhanc=' + stationID.value + '&jldLx=0',
method: 'GET',
success: (res) => {
console.log(res)
jlData.value = JSON.parse(res.data.result).JlData;
}
})
}
//
const formatNumber = (num) => {
let temp = 0;
try {
temp = parseFloat(num);
} catch (error) {
//TODO handle the exception
}
return temp.toFixed(4).replace(/\.?0+$/, '');
};
</script>
<style lang="scss" scoped>
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10 10rpx;
border-bottom: 1rpx solid #eee;
margin-left: 10px;
margin-right: 10px;
}
.title {
font-weight: bold;
color: #333;
}
.titlejl {
font-size: 20rpx;
vertical-align: middle;
font-weight: bold;
color: #0055ff;
margin-bottom: 15px;
}
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
padding: 16px;
}
.card {
background: #fff;
border-radius: 8px;
padding: 5px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.field-list {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
/* 允许子元素换行 */
gap: 3px;
}
.field-item {
display: flex;
height: 30px;
justify-content: space-between;
align-items: center;
padding: 5px 5px;
background: #f8f9fa;
border-radius: 4px;
flex-basis: calc(50% - 10px);
/* 每个元素占据约一半宽度,减去间隙 */
box-sizing: border-box;
/* 包含内边距和边框 */
}
/* 当屏幕宽度较小时,每个元素占据整行 */
@media (max-width: 200px) {
.field-item {
flex-basis: 100%;
}
}
.field-label {
color: #666;
font-size: 10px;
flex: 1;
margin-right: 2px;
width: 80px;
font-weight: 500;
}
.field-labelssll {
color: #0000ff;
text-decoration: underline;
font-size: 10px;
flex: 1;
margin-right: 2px;
width: 80px;
font-weight: 500;
}
.field-value {
color: #1890ff;
font-weight: 500;
font-size: 12px;
text-align: right;
width: 60px;
overflow: hidden;
text-overflow: ellipsis;
}
.status-circle {
width: 70rpx;
height: 30rpx;
font-size: 12px;
vertical-align: middle;
}
.page-layout {
display: flex;
flex-direction: column;
height: 100vh;
}
.scroll-container {
flex: 1;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -9,51 +9,35 @@
</route> </route>
<template> <template>
<PageLayout :navbarShow="false"> <PageLayout :navbarShow="false">
<view> <view class="nav">
<view class="placeholder"></view> <view class="nav_box">
<view style="width: 100%; display: grid; place-items: center"> <scroll-view direction="horizontal">
<uni-title :title="dateDate + ':生产情况'" type="h1" color="blue" /> <uni-segmented-control :current="current" :values="items" styleType="string" mode="segmented"
</view> @clickItem="onClickItem"></uni-segmented-control>
<view style="margin: 0 10px;"> </scroll-view>
<uni-segmented-control style="margin-top: 10px;margin-bottom: 10px" :current="current" :values="items"
@clickItem="onClickItem" styleType="button" activeColor="#0055ff"></uni-segmented-control>
</view>
<view class="content">
<view v-if="current === 0">
<view style="padding: 0 10px">
<view class="progress-bartime">
<!-- 动态设置宽度和颜色 -->
<view class="progressTime"
:style="{ width: `${timePercent}%`, 'background-color': '#0055ff' }">
</view>
<!-- 显示带符号的百分比 -->
<text class="progress-text">全年时间进度:{{ timePercent }}%</text>
</view>
</view>
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
<trq-data></trq-data>
<yy-data></yy-data>
</scroll-view>
</view>
<view v-if="current === 1">
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
<sssjForm></sssjForm>
</scroll-view>
</view>
</view> </view>
</view> </view>
<view class="content">
<view v-if="current === 0">
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
<sssjData></sssjData>
</scroll-view>
</view>
<view v-if="current === 1">
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
<trq-data></trq-data>
<yy-data></yy-data>
</scroll-view>
</view>
</view>
</PageLayout> </PageLayout>
</template> </template>
<script setup> <script setup>
import trqData from './ribaoshuju/trqRbsj';
import yyData from './ribaoshuju/yyRbsj';
import sssjForm from './shishishuju/index';
import {
formatDate,
getDateAfterDays,
getYearProgress
} from '@/utils/dateTime';
import { import {
ref, ref,
onMounted, onMounted,
@ -62,14 +46,19 @@
watchEffect, watchEffect,
onUnmounted onUnmounted
} from 'vue'; } from 'vue';
const items = ref(['日报数据', '实时数据']) //
import trqData from '@/pages-production/ribaoshuju/trqRbsj';
import yyData from '@/pages-production/ribaoshuju/yyRbsj';
import sssjData from '@/pages-production/shishishuju/trqSssj';
const items = ref(['油气实时', '油气历史', '管线运行', '安防监控', '设备运行'])
const current = ref(0) const current = ref(0)
const res = wx.getSystemInfoSync(); const res = wx.getSystemInfoSync();
const statusHeight = res.statusBarHeight; // const statusHeight = res.statusBarHeight; //
const cusnavbarheight = statusHeight + 44 + 'px'; const cusnavbarheight = (statusHeight + 50) + "px";
const scrollViewHeight = ref(0); // const scrollViewHeight = ref(0); //
const timePercent = ref(0);
const dateDate = ref(''); const activeColor = ref("#0000ff")
const inActiveColor = ref("#000000")
function onClickItem(e) { function onClickItem(e) {
if (current.value != e.currentIndex) { if (current.value != e.currentIndex) {
@ -77,31 +66,16 @@
} }
} }
const strDate = () => {
const now = new Date();
if (now.getHours() < 11) {
return formatDate(getDateAfterDays(now, -1)); //11
} else {
return formatDate(now);
}
};
onMounted(() => { onMounted(() => {
dateDate.value = strDate();
timePercent.value = getYearProgress();
calculateScrollViewHeight(); calculateScrollViewHeight();
// // 使 Uniapp
window.addEventListener('resize', calculateScrollViewHeight); uni.onWindowResize(calculateScrollViewHeight);
}); });
onUnmounted(() => { onUnmounted(() => {
// // Uniapp
window.removeEventListener('resize', calculateScrollViewHeight); uni.offWindowResize(calculateScrollViewHeight);
}); });
onHide(()=>{ // by
current.value = 0;
})
const calculateScrollViewHeight = () => { const calculateScrollViewHeight = () => {
// //
const screenHeight = uni.getSystemInfoSync().windowHeight; const screenHeight = uni.getSystemInfoSync().windowHeight;
@ -111,18 +85,7 @@
query query
.select('.nav') .select('.nav')
.boundingClientRect(); .boundingClientRect();
query
.select('.placeholder')
.boundingClientRect();
query
.select('.uni-title')
.boundingClientRect();
query
.select('.uni-segmented-control')
.boundingClientRect();
query
.select('.progress-bartime')
.boundingClientRect();
// //
query.exec((res) => { query.exec((res) => {
let totalHeight = 0; let totalHeight = 0;
@ -132,62 +95,38 @@
} }
}); });
// scroll-view // scroll-view
scrollViewHeight.value = screenHeight - totalHeight - 80; scrollViewHeight.value = screenHeight - totalHeight;
}); });
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.nav { .nav {
width: calc(100% - 60rpx); width: calc(100%);
padding: 0 30rpx;
height: v-bind(cusnavbarheight); height: v-bind(cusnavbarheight);
font-size: 24rpx; font-size: 24rpx;
color: #ffffff; //color: #333333;
position: fixed; position: fixed;
top: 0;
left: 0;
z-index: 99; z-index: 99;
background-image: url('../../static/my/navbg.png'); background-image: url('@/static/navbg.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 750rpx 458rpx; background-size: 750rpx 458rpx;
} }
.placeholder { .nav_box {
height: v-bind(cusnavbarheight); font-size: 18;
}
.progress-bartime {
position: relative;
height: 25px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20rpx;
}
.progress {
height: 100%;
transition: all 0.3s;
}
.progressTime {
height: 100%;
transition: all 0.3s;
padding: 0 20px;
}
.progress-text {
position: absolute; position: absolute;
left: 50%; bottom: 6rpx;
top: 50%; width: calc(100% - 60rpx);
transform: translate(-50%, -50%); left: 30rpx;
color: red; right: 30rpx;
/* 保持红色 */ }
font-size: 16px;
font-weight: bold; .content {
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); position: absolute;
/* 提升可读性 */ top: v-bind(cusnavbarheight);
width: 100%;
min-height: calc(100vh - v-bind(cusnavbarheight));
} }
</style> </style>

View File

@ -0,0 +1,373 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '历史数据图表',
},
}
</route>
<template>
<PageLayout :navbarShow="false">
<view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
</view>
</PageLayout>
</template>
<script setup>
import * as echarts from 'echarts';
import {
ref,
watch,
onMounted,
onUnmounted
} from 'vue'; // refAPI
import {
formatDate,
getDateAfterDays,
getDateAfterMonths
} from '@/utils/dateTime';
const props = defineProps({
dataList: {
type: Array,
default: () => [],
required: true
},
xField: {
type: String,
default: 'createTime' // xField'rqDate'createTimecreateTime
},
yFields: {
type: Object, // yFields线StringArray
default: () => [],
required: true
}
});
//
const colorData = ref([{
color: '#000000'
},
{
color: '#00007f'
},
{
color: '#ff0000'
},
{
color: '#005500'
},
{
color: '#55007f'
},
{
color: '#ffff00'
}
]);
const chart = ref(null);
const chartOption = ref({}); // undefined
const chartTitle = ref('历史趋势'); // watch
// 1. chartOption.value
const generateOptions = () => {
console.log(23)
const serie = []; //
const yData = []; // Y
const xData = []; // X
const legendData = []; //
let positionIndex = 0;
const yDataNum = props.yFields.length; // Y = 线
// X
for (let i = 0; i < yDataNum; i++) {
xData.push('');
}
// 线Y
for (let i = 0; i < props.yFields.length; i++) {
const yFieldItem = props.yFields[i]; //
// Y
const yaxisTemp = {
type: 'value',
nameRotate: 270,
nameLocation: 'middle',
nameGap: 20,
name: '',
position: 'left',
axisLabel: {
formatter: '{value}'
},
axisLine: {
lineStyle: {
color: '#61A0A8',
width: 1
}
}
};
// +
const legendName = `${yFieldItem.name}(${yFieldItem.unit})`;
legendData.push(legendName);
console.log(1, legendData)
// Yssll
if (yFieldItem.field === 'ssll') {
yaxisTemp.position = 'left';
yaxisTemp.nameGap = -15;
} else {
yaxisTemp.position = 'right';
yaxisTemp.offset = 40 * positionIndex;
positionIndex++;
}
// Yv[i].maxyFieldItem.max
yaxisTemp.name = legendName;
yaxisTemp.min = yFieldItem.min ?? 0; // min0
yaxisTemp.max = yFieldItem.max ?? 100; // max100
yaxisTemp.axisLine.lineStyle.color = colorData.value[i]?.color || '#61A0A8'; //
yData.push(yaxisTemp);
//
const serieTemp = {
field: yFieldItem.field,
name: legendName,
showSymbol: false,
type: 'line',
lineStyle: {
width: 2
},
data: [],
yAxisIndex: i // iY
};
// this.serie.pushseriepush
serie.push(serieTemp);
}
//
return {
color: ['#000000', '#00007f', '#ff0000', '#005500', '#55007f', '#ffff00'],
title: {
textAlign: 'auto',
text: chartTitle.value
},
tooltip: {
trigger: 'axis',
alwaysShowContent: true,
axisPointer: {
type: 'cross'
}
},
toolbox: {
showTitle: true,
orient: 'vertical',
feature: {
dataView: {
show: true,
readOnly: false
},
restore: {
show: true
},
saveAsImage: {
show: true
}
},
left: '0%',
top: '10%'
},
legend: {
data: legendData,
itemGap: 5,
padding: [35, 5, 25, 5],
type: 'scroll'
},
grid: {
top: '15%',
left: '5%',
right: `${10 + positionIndex * 10}%`, // Y
bottom: 20,
containLabel: true
},
xAxis: {
type: 'category',
axisTick: {
alignWithLabel: true
},
data: xData
},
yAxis: yData,
series: serie
};
};
// 2. 访
const processSeriesData = () => {
const ssData = props.dataList;
// 访chartOption.value
const xData = [...chartOption.value.xAxis.data]; // X series
const serie = [...chartOption.value.series]; //
const yData = [...chartOption.value.yAxis]; // Y
//
xData.length = 0;
serie.forEach(item => (item.data.length = 0));
//
ssData.forEach(item => {
// Xprops.xFieldcreateTime
const ctime = item[props.xField];
xData.push(ctime);
// 线
serie.forEach((serieItem, j) => {
const field = props.yFields[j].field;
const tempValue = item[field];
// 0
serieItem.data.push(tempValue ?? 0);
});
});
// Ythis.seriethis.maxArrValue
serie.forEach((serieItem, mm) => {
const maxValue = maxArrValue(serieItem.data);
yData[mm].max = Math.round(maxValue * 1.2) || 100; // 0100
});
// .value
chartOption.value = {
...chartOption.value, //
xAxis: {
...chartOption.value.xAxis,
data: xData
},
series: serie,
yAxis: yData
};
console.log(22, chartOption.value)
};
// 3. NaN
const maxArrValue = (data) => {
// Number
const validNumbers = data
.map(item => Number(item))
.filter(item => !isNaN(item));
// 0
return validNumbers.length > 0 ?
Math.max(...validNumbers) :
0;
};
// 4. 使
watch(
() => props.dataList,
(newVal) => {
if (newVal.length === 0) return; //
console.log(33, props.dataList)
console.log(44, props.yFields)
initChart()
updateChart();
}, {
deep: true,
immediate: true
} //
);
// 5. chartOption.value
const initChart = async () => {
//
chartOption.value = generateOptions();
if (!chart.value) return;
// EChartssetTimeoutasync/await
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
// resize
window.addEventListener('resize', () => {
myChart.resize();
});
};
// 6. 访chartOption.value
const updateChart = () => {
if (!myChart.value || !chartOption.value) {
console.warn('ECharts实例未初始化');
return;
}
processSeriesData();
myChart.value.setOption(chartOption.value, true);
};
//
onMounted(() => {
setTimeout(() => {
initChart();
}, 500)
});
//
onUnmounted(() => {
if (chart.value) {
const myChart = chart.value.getInstance();
myChart.dispose();
chart.value = null;
}
window.removeEventListener('resize', () => {}); //
});
</script>
<style scoped>
.chart-container {
width: 100%;
height: 30vh;
min-height: 200px;
padding: 20rpx;
background: linear-gradient(145deg, #f8f9fa 0%, #ffffff 100%);
border-radius: 16rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
position: relative;
overflow: hidden;
}
/* 加载占位符(数据为空时显示) */
.chart-container::before {
content: '数据加载中...';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #666;
font-size: 28rpx;
z-index: 1;
/* 数据加载完成后隐藏 */
opacity: v-bind('chartOption.value.series?.length > 0 ? 0 : 1');
}
.chart-wrapper {
width: 100%;
height: 100%;
transition: opacity 0.3s ease;
}
/* 移动端优化调整右侧间距避免Y轴标签被截断 */
@media screen and (max-width: 768px) {
.chart-container {
height: 30vh;
min-height: 200px;
padding: 10rpx;
border-radius: 12rpx;
}
}
</style>

View File

@ -0,0 +1,97 @@
{
"id": "cxc-szcx-multiLineChart",
"displayName": "cxc-szcx-multiLineChart",
"version": "1.0.0",
"description": "cxc-szcx-multiLineChart",
"keywords": [
"cxc-szcx-multiLineChart"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0",
"uni-app": "^3.1.0",
"uni-app-x": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": "",
"darkmode": "-",
"i18n": "-",
"widescreen": "-"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "-",
"aliyun": "-",
"alipay": "-"
},
"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,34 @@
# cxc-szcx-multiLineChart
# 这个多字段趋势图组件具有以下特点和功能:
类型安全:使用 TypeScript 定义了清晰的接口,确保数据类型正确
灵活配置:通过 props 参数可以配置需要展示的字段、时间字段、标题等
多字段展示:支持同时展示多个数据字段的趋势线(如 yl, wd, yc 等)
参考线功能:每个字段都可以设置参考值,自动生成对应的参考线
交互功能:
支持图例筛选,可单独显示 / 隐藏某个字段
提供数据缩放功能,可查看局部数据
悬停时显示详细数据信息
窗口大小变化时自动调整图表
使用示例:
<template>
<view class="container">
<multi-field-trend-chart
:data-list="dataList"
time-field="createTime"
:fields="[
{ field: 'yl', name: '压力', referenceValue: 0.73 },
{ field: 'wd', name: '温度', color: '#ff6347' },
{ field: 'yc', name: '压力差' },
{ field: 'ssll', name: '瞬时流量' },
{ field: 'jrl', name: '累计流量' }
]"
title="文增伴生气外输数据趋势"
height="600rpx"
/>
</view>
</template>
<script setup lang="ts">
import MultiFieldTrendChart from '@/components/MultiFieldTrendChart.vue';
import { dataList } from '@/data/trendData'; // 导入您的数据
</script>