Merge branch 'master' of http://ngtools.cn:53000/ldeyun/cxc-szcx-uniapp
# Conflicts: # pages/views/shengchan/ribaoshuju/rbsjLsxq.vue # pages/views/shengchan/ribaoshuju/trqRbsj.vue # uni_modules/cxc-szcx-dateRangeSelect/components/cxc-szcx-dateRangeSelect/cxc-szcx-dateRangeSelect.vue
This commit is contained in:
commit
9556ecb346
@ -1,4 +1,4 @@
|
||||
# 开发环境
|
||||
# 请求接口地址
|
||||
VITE_REQUEST_BASE_URL = https://36.112.48.190
|
||||
#VITE_REQUEST_BASE_URL = http://10.75.15.249:8080
|
||||
#VITE_REQUEST_BASE_URL = http://10.75.166.6:8080
|
||||
|
@ -1,13 +1,15 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="stats-container">
|
||||
<uni-title :title="name.unit + '历史数据---单位(万方)'" color="blue" type="h2"></uni-title>
|
||||
<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 v-model="dateRange"></cxc-szcx-dateRangeSelect>
|
||||
<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 :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>
|
||||
@ -15,7 +17,7 @@
|
||||
<view class="stats-item">平均值: {{ dataStats.average }}</view>
|
||||
</view>
|
||||
|
||||
<view class="table">
|
||||
<view v-if="type === 'trq'" class="table-container">
|
||||
<!-- 表头 -->
|
||||
<view class="tr header">
|
||||
<view class="th1">序号</view>
|
||||
@ -23,7 +25,7 @@
|
||||
<view class="th">日期</view>
|
||||
<view class="th">日气量</view>
|
||||
</view>
|
||||
<scroll-view scroll-X="true" scroll-Y="true" class="table-container">
|
||||
<scroll-view scroll-Y="true" class="scroll-wrapper">
|
||||
<!-- 表格内容 -->
|
||||
<view class="tr" v-for="(item, index) in dataList" :key="index" :class="{ even: index % 2 === 0 }">
|
||||
<view class="td1">{{ index + 1 }}</view>
|
||||
@ -38,6 +40,30 @@
|
||||
<view v-if="!dataList.length" class="empty">暂无相关数据</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view v-if="type === 'yy'" class="table-container">
|
||||
<!-- 表头 -->
|
||||
<view class="tr header">
|
||||
<view class="th1">序号</view>
|
||||
<view class="th">名称</view>
|
||||
<view class="th">日期</view>
|
||||
<view class="th">日油量</view>
|
||||
</view>
|
||||
<scroll-view scroll-Y="true" class="scroll-wrapper">
|
||||
<!-- 表格内容 -->
|
||||
<view class="tr" v-for="(item, index) in dataList" :key="index" :class="{ even: index % 2 === 0 }">
|
||||
<view class="td1">{{ index + 1 }}</view>
|
||||
<view class="td">{{ item.dw }}</view>
|
||||
<view class="td">{{ item.scrq }}</view>
|
||||
<view class="td" :class="{ negative: item.rcwy < 0 }">
|
||||
{{ item.rcwy }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空数据提示 -->
|
||||
<view v-if="!dataList.length" class="empty">暂无相关数据</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -53,7 +79,11 @@ const dateRange = ref([]);
|
||||
const dataList = ref([]);
|
||||
const endDate = ref('');
|
||||
const startDate = ref('');
|
||||
const dataStats = ref({ min: 0, max: 0, avg: 0 });
|
||||
const dataStats = ref({
|
||||
min: 0,
|
||||
max: 0,
|
||||
avg: 0
|
||||
});
|
||||
const getJinriShengchansj = (tempDateRange) => {
|
||||
// console.log(tempDateRange);
|
||||
// 添加日期有效性检查
|
||||
@ -99,7 +129,7 @@ const getJinriYuanyouShengchansj = (tempDateRange) => {
|
||||
queryParms.pageSize = 500;
|
||||
|
||||
// 添加参数有效性检查
|
||||
if (!queryParms.rqDate_begin || !queryParms.rqDate_end) {
|
||||
if (!queryParms.scrq_begin || !queryParms.scrq_end) {
|
||||
console.error('参数格式化失败:', queryParms);
|
||||
return;
|
||||
}
|
||||
@ -108,7 +138,7 @@ const getJinriYuanyouShengchansj = (tempDateRange) => {
|
||||
if (res.success) {
|
||||
console.log(res);
|
||||
dataList.value = res.result.records;
|
||||
dataStats.value = calculateStats(dataList.value, 'rcwy');
|
||||
dataStats.value = calculateStats(dataList.value, 'rcwy').reverse();
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -151,6 +181,7 @@ function calculateStats(data, field) {
|
||||
average: average
|
||||
};
|
||||
}
|
||||
|
||||
function convertToDate(str) {
|
||||
try {
|
||||
const date = new Date(str);
|
||||
@ -187,7 +218,10 @@ watch(
|
||||
console.warn('未知类型:', newType);
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
onMounted(() => {
|
||||
// nextTick();
|
||||
@ -219,67 +253,64 @@ onLoad((options) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
min-width: 100%;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
|
||||
.tr {
|
||||
display: flex;
|
||||
border-bottom: 2rpx solid #e8e8e8;
|
||||
|
||||
&.header {
|
||||
background-color: #fafafa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.even {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
.th,
|
||||
.td {
|
||||
flex: 1;
|
||||
min-width: 80rpx;
|
||||
padding: 10rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
height: 30rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.th1,
|
||||
.td1 {
|
||||
flex: 1;
|
||||
max-width: 40rpx;
|
||||
padding: 10rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
height: 30rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.td.negative {
|
||||
color: #ff4444;
|
||||
font-weight: 500;
|
||||
}
|
||||
.scroll-wrapper {
|
||||
width: 100%;
|
||||
height: 35vh;
|
||||
}
|
||||
|
||||
.tr {
|
||||
display: flex;
|
||||
min-height: 14px;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.th,
|
||||
.td {
|
||||
flex: 1;
|
||||
min-width: 80px;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.th1,
|
||||
.td1 {
|
||||
flex: 1;
|
||||
max-width: 40px;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.td.negative {
|
||||
color: #ff4444;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 40rpx;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
font-size: 16rpx;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
@ -291,6 +322,7 @@ onLoad((options) => {
|
||||
padding: 10px;
|
||||
margin: 15 15px;
|
||||
}
|
||||
|
||||
.dateSelect {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
@ -302,6 +334,7 @@ onLoad((options) => {
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
font-size: 12px;
|
||||
color: #00aa00;
|
||||
|
@ -1,13 +1,23 @@
|
||||
<template>
|
||||
<view :class="{ gray: store.isgray == 1 }">
|
||||
<view class="progress-bar">
|
||||
<!-- 动态设置宽度和颜色 -->
|
||||
<view class="progressTime" :style="{ width: `${timePercent}%`, 'background-color': '#00aaff' }"></view>
|
||||
<!-- 显示带符号的百分比 -->
|
||||
<text class="progress-text">全年时间进度:{{ timePercent }}%</text>
|
||||
<view style="padding: 0 10px">
|
||||
<view class="progress-bartime">
|
||||
<!-- 动态设置宽度和颜色 -->
|
||||
<view class="progressTime" :style="{ width: `${timePercent}%`, 'background-color': '#00aaff' }"></view>
|
||||
<!-- 显示带符号的百分比 -->
|
||||
<text class="progress-text">全年时间进度:{{ timePercent }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<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 shishiArr" :key="index" class="card-item" @click="handleCardClick(item.gas)">
|
||||
<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">
|
||||
@ -27,6 +37,20 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<uni-popup ref="popupSelect" type="top" background-color="#fff">
|
||||
<view style="margin-top: 50px">
|
||||
<view class="titlepopup">选择显示更多生产数据</view>
|
||||
<uni-data-checkbox
|
||||
style="font-size: 14px"
|
||||
@change="onselectionchange"
|
||||
:localdata="shishiArr"
|
||||
v-model="shishiArrDisplay"
|
||||
multiple
|
||||
:map="{ value: 'gas', text: 'gas' }"
|
||||
></uni-data-checkbox>
|
||||
</view>
|
||||
</uni-popup>
|
||||
<!-- 数据弹窗 -->
|
||||
<uni-popup ref="popup" type="bottom" background-color="#fff">
|
||||
<view class="popup-content">
|
||||
@ -79,29 +103,51 @@ import { getYearProgress } from '@/utils/dateTime.js';
|
||||
const store = useStore();
|
||||
|
||||
const shishiArr = ref([]);
|
||||
const shishiArrSelect = ref([]);
|
||||
const shishiArrDisplay = ref(['气井气', '站线综合输差']);
|
||||
const dataJinriUnit = ref([]);
|
||||
const selectedGas = ref('');
|
||||
const filteredData = ref([]);
|
||||
const popup = ref(null);
|
||||
const popupSelect = ref(null);
|
||||
const strDate = ref('');
|
||||
const timePercent = ref(0);
|
||||
|
||||
const dataJinri = ref([]);
|
||||
const dataJinriSum = ref([]);
|
||||
const dataJinriSumUnit = ref([]);
|
||||
|
||||
// 点击卡片处理
|
||||
// const handleCardClick = (gas) => {
|
||||
// selectedGas.value = 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.open();
|
||||
}
|
||||
const onselectionchange = () => {
|
||||
shishiArrSelect.value = [];
|
||||
shishiArrDisplay.value.forEach((item) => {
|
||||
shishiArrSelect.value.push(shishiArr.value.filter((item1) => item1.gas === item)[0]);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCardClick = (gas) => {
|
||||
selectedGas.value = 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;
|
||||
}
|
||||
});
|
||||
|
||||
filteredData.value = dataJinriSumUnit.value.filter((item) => item.gas === gas);
|
||||
popup.value.open();
|
||||
};
|
||||
|
||||
@ -110,8 +156,9 @@ const closePopup = () => {
|
||||
popup.value.close();
|
||||
};
|
||||
onMounted(() => {
|
||||
getJinriShengchansj();
|
||||
getJinriTrqShengchansj();
|
||||
timePercent.value = getYearProgress();
|
||||
getJinriShengchansj();
|
||||
});
|
||||
|
||||
// 数字格式化
|
||||
@ -127,7 +174,7 @@ const formatNumber = (num) => {
|
||||
};
|
||||
|
||||
// 自动计算综合输差
|
||||
watchEffect(() => {
|
||||
function calcZhsc(tempArray) {
|
||||
let totalJinqi = {
|
||||
gas: '总进气',
|
||||
dailyVolume: 0,
|
||||
@ -156,7 +203,7 @@ watchEffect(() => {
|
||||
yearPlan: '100',
|
||||
yearPerCent: 0
|
||||
}; // 综合输差
|
||||
shishiArr.value.forEach((item) => {
|
||||
tempArray.forEach((item) => {
|
||||
if (item.gas === '站输差' || item.gas === '线输差') {
|
||||
compositeZx.dailyVolume += parseFloat(item.dailyVolume) || 0;
|
||||
compositeZx.yearVolume += parseFloat(item.yearVolume) || 0;
|
||||
@ -176,13 +223,16 @@ watchEffect(() => {
|
||||
compositeJc.dailyVolume = (-totalJinqi.dailyVolume + totalChuqi.dailyVolume).toFixed(4);
|
||||
compositeJc.yearVolume = (-totalJinqi.yearVolume + totalChuqi.yearVolume).toFixed(4);
|
||||
compositeJc.yearPerCent = calcPercent(compositeJc.yearPlan, compositeJc.yearVolume);
|
||||
shishiArr.value.push(compositeZx);
|
||||
shishiArr.value.push(compositeJc);
|
||||
|
||||
tempArray.push(compositeZx);
|
||||
// tempArray.push(compositeJc);
|
||||
|
||||
return tempArray;
|
||||
|
||||
// console.log(composite);
|
||||
});
|
||||
}
|
||||
|
||||
const getJinriShengchansj = () => {
|
||||
const getJinriTrqShengchansj = () => {
|
||||
const now = new Date();
|
||||
if (now.getHours() < 11) {
|
||||
strDate.value = formatDate(getDateAfterDays(now, -1)).toString(); //11点之前 头一天的数据
|
||||
@ -196,7 +246,7 @@ const getJinriShengchansj = () => {
|
||||
// // console.log(queryParms);
|
||||
queryJinriTrqShengchansj(queryParms).then((res) => {
|
||||
if (res.success) {
|
||||
console.log(res);
|
||||
// console.log(1, res);
|
||||
let temp = res.result;
|
||||
|
||||
temp.forEach((item) => {
|
||||
@ -205,15 +255,22 @@ const getJinriShengchansj = () => {
|
||||
item.yearPerCent = calcPercent(item.yearPlan, item.yearVolume);
|
||||
}
|
||||
});
|
||||
shishiArr.value = temp;
|
||||
// console.log(2, temp);
|
||||
shishiArr.value = calcZhsc(temp);
|
||||
nextTick();
|
||||
onselectionchange();
|
||||
console.log(3, shishiArr.value);
|
||||
console.log(4, shishiArrSelect.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function goHistory(val) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/views/shengchan/ribaoshuju/rbsjLsxq?data=' + JSON.stringify(val) + '&type=trq'
|
||||
});
|
||||
}
|
||||
|
||||
function calcPercent(yearJihua, yearShiji) {
|
||||
// 计算进度百分比,避免除数为 0
|
||||
// 确保进度百分比不超过 100
|
||||
@ -227,9 +284,180 @@ function calcPercent(yearJihua, yearShiji) {
|
||||
}
|
||||
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;
|
||||
// console.log(queryParms);
|
||||
queryJinriShengchansj(queryParms).then((res) => {
|
||||
if (res.success) {
|
||||
// console.log(res);
|
||||
dataJinri.value = res.result.records;
|
||||
dataJinriSumUnit.value = sumByUnit(dataJinri.value); //包含gas unit rq cq totalGas
|
||||
// console.log(dataJinriSumUnit.value);
|
||||
// 使用 nextTick 等待 DOM 更新
|
||||
nextTick();
|
||||
getYearShengchansj(); //再获取今年以来的数据
|
||||
}
|
||||
});
|
||||
};
|
||||
const getYearShengchansj = () => {
|
||||
const now = new Date();
|
||||
let year = formatDate(now).split('-')[0];
|
||||
let queryParms = {};
|
||||
queryParms.yearStart = year;
|
||||
queryParms.yearEnd = year;
|
||||
|
||||
// // console.log(2, queryParms.value);
|
||||
queryYearShengchansj(queryParms).then((res) => {
|
||||
if (res.success) {
|
||||
try {
|
||||
// // console.log(res.result);
|
||||
let yearData = res.result[year];
|
||||
// console.log(dataJinriSumUnit.value.length, dataJinriSumUnit.value, yearData.length,
|
||||
// yearData);
|
||||
dataJinriSumUnit.value.forEach((item) => {
|
||||
yearData.forEach((itemYear) => {
|
||||
// // console.log(item, itemYear);
|
||||
if (item.unit === itemYear.unit) {
|
||||
item.yearVolume = itemYear.yearSum || 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
dataJinriSum.value = sumByGas(dataJinriSumUnit.value);
|
||||
// console.log(dataJinriSum.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) {
|
||||
// console.log(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;
|
||||
if (!unit.includes('区')) {
|
||||
if (!summaryMap[unit]) {
|
||||
// 初始化该 gas 类型的汇总对象
|
||||
summaryMap[unit] = {
|
||||
unit: unit,
|
||||
gas: record.gas,
|
||||
rq: 0,
|
||||
sq: 0,
|
||||
totalGas: 0,
|
||||
yearVolume: 0
|
||||
};
|
||||
}
|
||||
// 无论是否是第一次遇到该 unit 类型,都进行累加操作
|
||||
summaryMap[unit].rq += record.rq || 0;
|
||||
summaryMap[unit].sq += record.sq || 0;
|
||||
summaryMap[unit].totalGas += record.totalGas || 0;
|
||||
summaryMap[unit].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;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.titlepopup {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -337,10 +565,10 @@ function calcPercent(yearJihua, yearShiji) {
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
background: #ececec;
|
||||
border-radius: 16rpx;
|
||||
padding: 10rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
padding: 15rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(179, 179, 179, 0.1);
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
@ -384,14 +612,23 @@ function calcPercent(yearJihua, yearShiji) {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bartime {
|
||||
position: relative;
|
||||
height: 20px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
.progress {
|
||||
height: 100%;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.progressTime {
|
||||
height: 100%;
|
||||
transition: all 0.3s;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
@ -399,9 +636,11 @@ function calcPercent(yearJihua, yearShiji) {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: red; /* 保持红色 */
|
||||
color: red;
|
||||
/* 保持红色 */
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); /* 提升可读性 */
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
/* 提升可读性 */
|
||||
}
|
||||
</style>
|
||||
|
@ -2,10 +2,18 @@
|
||||
<view :class="{ gray: store.isgray == 1 }">
|
||||
<!-- <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>
|
||||
<!-- <text class="title">{{ item.gas }}</text> -->
|
||||
<view class="content">
|
||||
<text class="label">日油量</text>
|
||||
<text class="value">{{ item.rcwy || '-' }}</text>
|
||||
@ -200,6 +208,41 @@ function sumByOil(records) {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15rpx 0;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -304,10 +347,10 @@ function sumByOil(records) {
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
background: #ececec;
|
||||
border-radius: 16rpx;
|
||||
padding: 10rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
padding: 15rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(197, 197, 197, 0.1);
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
|
@ -5,97 +5,114 @@
|
||||
<view class="compact-input" @click="openPicker">
|
||||
{{ dateRange[0] || '开始日期' }}
|
||||
</view>
|
||||
<text class="separator">至</text>
|
||||
<view class="compact-input" @click="openPicker">
|
||||
<text v-if="mode==='range'" class="separator">至</text>
|
||||
<view v-if="mode==='range'" class="compact-input" @click="openPicker">
|
||||
{{ dateRange[1] || '结束日期' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wu-calendar
|
||||
ref="calendar"
|
||||
mode="range"
|
||||
:date="dateRange"
|
||||
@confirm="calendarConfirm"
|
||||
slideSwitchMode="horizontal"
|
||||
type="month"
|
||||
:fold="false"
|
||||
:insert="false"
|
||||
:rangeSameDay="true"
|
||||
:lunar="true"
|
||||
:monthShowCurrentMonth="false"
|
||||
:range-end-repick="true"
|
||||
:item-height="45"
|
||||
:rangeEndRepick="true"
|
||||
></wu-calendar>
|
||||
<wu-calendar ref="calendar" :mode="mode" :date="dateRange" @confirm="calendarConfirm"
|
||||
slideSwitchMode="horizontal" type="month" :fold="false" :insert="false" :rangeSameDay="true" :lunar="true"
|
||||
:monthShowCurrentMonth="false" :range-end-repick="true" :item-height="45"
|
||||
:rangeEndRepick="true"></wu-calendar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatDate, getDateAfterDays, getDateAfterMonths } from '@/utils/dateTime.js';
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [null, null]
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
dateRange: []
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.dateRange.push(formatDate(new Date(newVal[0])));
|
||||
this.dateRange.push(formatDate(new Date(newVal[1])));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openPicker() {
|
||||
this.$refs.calendar.open();
|
||||
import {
|
||||
formatDate,
|
||||
getDateAfterDays,
|
||||
getDateAfterMonths
|
||||
} from '@/utils/dateTime.js';
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [null, null]
|
||||
},
|
||||
// 日期选择模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
},
|
||||
calendarConfirm(e) {
|
||||
this.dateRange = [];
|
||||
try {
|
||||
this.dateRange.push(formatDate(new Date(e.range.before)));
|
||||
this.dateRange.push(formatDate(new Date(e.range.after)));
|
||||
} catch (error) {
|
||||
return;
|
||||
//TODO handle the exception
|
||||
|
||||
data() {
|
||||
return {
|
||||
dateRange: null
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
console.log(44, JSON.stringify(newVal))
|
||||
let dateType = Object.prototype.toString.call(newVal);
|
||||
if (this.mode == 'single' && dateType != '[object String]') {
|
||||
return console.error(`类型错误,mode=${this.mode}时,date=String`);
|
||||
} else if (this.mode != 'single' && dateType != '[object Array]') {
|
||||
return console.error(`类型错误,mode=${this.mode}时,date=Array`);
|
||||
}
|
||||
if (this.mode == 'single') {
|
||||
this.dateRange = ''
|
||||
this.dateRange = newVal;
|
||||
}
|
||||
// 根据类型默认选中不同的值
|
||||
if (this.mode == 'multiple') {
|
||||
this.dateRange = []
|
||||
this.dateRange = newVal;
|
||||
} else if (this.mode == 'range') {
|
||||
this.dateRange = []
|
||||
this.dateRange.push(formatDate(new Date(newVal[0])));
|
||||
this.dateRange.push(formatDate(new Date(newVal[1])));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openPicker() {
|
||||
this.$refs.calendar.open();
|
||||
},
|
||||
calendarConfirm(e) {
|
||||
this.dateRange = [];
|
||||
try {
|
||||
this.dateRange.push(formatDate(new Date(e.range.before)));
|
||||
this.dateRange.push(formatDate(new Date(e.range.after)));
|
||||
} catch (error) {
|
||||
return;
|
||||
//TODO handle the exception
|
||||
}
|
||||
this.$emit('update:modelValue', this.dateRange);
|
||||
}
|
||||
this.$emit('update:modelValue', this.dateRange);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
/* 紧凑型输入框 */
|
||||
.compact-input {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 16px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.compact-input.active {
|
||||
border-color: #409eff;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
/* 紧凑型输入框 */
|
||||
.compact-input {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 16px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.compact-input.active {
|
||||
border-color: #409eff;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
</style>
|
@ -1,139 +1,162 @@
|
||||
# cxc-szcx-dateRangeSelect
|
||||
# # 紧凑型日期时间选择器组件说明书
|
||||
|
||||
## 一、组件概述
|
||||
`compact-datetime-picker` 是一个基于 Vue 开发的紧凑型日期时间选择器组件,用于在页面中选择日期范围。组件提供了自然周期和相对周期两种选择模式,并且支持快捷选择本周、本月、本季、本年、近一周、近一月、近一季、近一年等常见时间范围。
|
||||
### 紧凑型日期时间选择器组件说明
|
||||
|
||||
## 二、组件依赖
|
||||
- **Vue**:组件基于 Vue 框架开发。
|
||||
- **uni-popup**:来自 `uni-app` 的弹窗组件,用于显示日期选择器的弹出层。
|
||||
|
||||
## 三、组件使用方法
|
||||
#### 一、组件功能
|
||||
该组件是基于 UniApp 开发的紧凑型日期选择器,支持以下功能:
|
||||
1. **三种选择模式**:
|
||||
- 单选(single)
|
||||
- 范围选择(range)
|
||||
- 多选(multiple)
|
||||
2. **弹出式日历选择**:
|
||||
- 支持月份滑动切换
|
||||
- 显示农历日期
|
||||
- 支持同天范围选择
|
||||
3. **数据双向绑定**:
|
||||
- 通过 `modelValue` 实现父子组件数据同步
|
||||
4. **自定义样式**:
|
||||
- 紧凑型输入框设计
|
||||
- 支持自定义主题颜色
|
||||
- 响应式布局适配
|
||||
|
||||
### 1. 引入组件
|
||||
在需要使用该组件的 Vue 文件中,引入 `compact-datetime-picker` 组件。
|
||||
|
||||
#### 二、组件Props
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 说明 |
|
||||
|--------------|------------|----------|--------------------------|
|
||||
| `modelValue` | Array/Date | `[null]` | 双向绑定的值(根据模式变化) |
|
||||
| `mode` | String | `single` | 选择模式(single/range/multiple) |
|
||||
|
||||
|
||||
#### 三、组件数据
|
||||
|
||||
| 名称 | 类型 | 说明 |
|
||||
|------------|--------|--------------------------|
|
||||
| `dateRange` | Array | 当前选中的日期范围(内部状态) |
|
||||
|
||||
|
||||
#### 四、组件方法
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
|----------------|------------|--------------------------|
|
||||
| `openPicker` | 无 | 打开日历选择器 |
|
||||
| `calendarConfirm` | `e` | 日历确认回调(处理选中数据) |
|
||||
|
||||
|
||||
#### 五、事件说明
|
||||
|
||||
| 事件名 | 说明 | 返回值 |
|
||||
|--------------|--------------------------|----------------------|
|
||||
| `update:modelValue` | 数据更新时触发 | 当前选中的日期数组 |
|
||||
|
||||
|
||||
#### 六、样式说明
|
||||
```vue
|
||||
<style scoped>
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.compact-input {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 16px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.compact-input.active {
|
||||
border-color: #409eff;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**关键样式点**:
|
||||
1. 输入框宽度固定为 120px
|
||||
2. 紧凑型设计(height: 20px)
|
||||
3. 活动状态样式增强
|
||||
4. 水平排列布局
|
||||
|
||||
|
||||
#### 七、使用示例
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<compact-datetime-picker v-model="dateRange" />
|
||||
<!-- 单选模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="singleDate"
|
||||
mode="single"
|
||||
/>
|
||||
|
||||
<!-- 范围选择模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="rangeDate"
|
||||
mode="range"
|
||||
/>
|
||||
|
||||
<!-- 多选模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="multipleDate"
|
||||
mode="multiple"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CompactDateTimePicker from '@/components/compact-datetime-picker.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CompactDateTimePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dateRange: [null, null]
|
||||
singleDate: null,
|
||||
rangeDate: [null, null],
|
||||
multipleDate: []
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### 2. 组件属性
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `modelValue` | `Array` | `[null, null]` | 双向绑定的日期范围数组,第一个元素为开始日期,第二个元素为结束日期。 |
|
||||
|
||||
## 四、组件内部实现
|
||||
#### 八、注意事项
|
||||
1. **日期格式要求**:
|
||||
- 单选模式:`YYYY-MM-DD` 字符串格式
|
||||
- 范围模式:`[startDate, endDate]` 数组格式
|
||||
- 多选模式:`[date1, date2, ...]` 数组格式
|
||||
2. **依赖组件**:
|
||||
- 需安装 `wu-calendar` 组件(第三方库)
|
||||
3. **异常处理**:
|
||||
```javascript
|
||||
calendarConfirm(e) {
|
||||
this.dateRange = [];
|
||||
try {
|
||||
this.dateRange.push(formatDate(new Date(e.range.before)));
|
||||
this.dateRange.push(formatDate(new Date(e.range.after)));
|
||||
} catch (error) {
|
||||
return; // 异常处理
|
||||
}
|
||||
this.$emit('update:modelValue', this.dateRange);
|
||||
}
|
||||
```
|
||||
4. **性能优化**:
|
||||
- 建议使用 `:range-end-repick="true"` 开启范围重新选择
|
||||
- 通过 `:item-height="45"` 控制日历行高
|
||||
|
||||
### 1. 模板部分
|
||||
- **输入框区域**:显示开始日期和结束日期的输入框,点击输入框可打开日期选择弹窗。
|
||||
- **日期选择弹窗**:
|
||||
- **快捷按钮区域**:包含自然周期和相对周期的切换按钮,以及不同模式下的快捷操作按钮。
|
||||
- **日历选择区域**:显示当前月份的日历,可选择日期。
|
||||
- **操作按钮**:包含取消和确定按钮,用于关闭弹窗和确认选择的日期范围。
|
||||
|
||||
### 2. 脚本部分
|
||||
#### 九、扩展功能建议
|
||||
1. 添加时间选择功能
|
||||
2. 支持自定义日期禁用范围
|
||||
3. 增加国际化支持
|
||||
4. 添加动画过渡效果
|
||||
5. 支持预设常用日期范围
|
||||
|
||||
#### 2.1 数据属性
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
isNatural: true, // 是否为自然周期模式
|
||||
activeType: 'start', // 当前活跃的日期选择类型(开始或结束)
|
||||
currentMonth: new Date(), // 当前显示的月份
|
||||
tempStart: null, // 临时开始日期
|
||||
tempEnd: null, // 临时结束日期
|
||||
weekDays: ['日', '一', '二', '三', '四', '五', '六'], // 星期几的显示文本
|
||||
quickButtonszr: [
|
||||
{ label: '本周', type: 'week' },
|
||||
{ label: '本月', type: 'month' },
|
||||
{ label: '本季', type: 'quarter' },
|
||||
{ label: '本年', type: 'year' }
|
||||
], // 自然周期模式下的快捷按钮
|
||||
quickButtonsxd: [
|
||||
{ label: '近一周', type: 'week' },
|
||||
{ label: '近一月', type: 'month' },
|
||||
{ label: '近一季', type: 'quarter' },
|
||||
{ label: '近一年', type: 'year' }
|
||||
] // 相对周期模式下的快捷按钮
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 计算属性
|
||||
- `formattedStart`:格式化后的开始日期。
|
||||
- `formattedEnd`:格式化后的结束日期。
|
||||
- `calendarDays`:生成当前月份的日历数据。
|
||||
- `monthText`:当前月份的显示文本。
|
||||
|
||||
#### 2.3 监听属性
|
||||
```javascript
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.tempStart = newVal[0];
|
||||
this.tempEnd = newVal[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
监听 `modelValue` 的变化,初始化临时日期。
|
||||
|
||||
#### 2.4 方法
|
||||
- **打开弹窗并初始化临时值**:`openPicker(type)`,打开日期选择弹窗,并根据活跃类型初始化临时日期。
|
||||
- **日期点击处理**:`handleDayClick(day)`,处理日期点击事件,更新临时开始或结束日期。
|
||||
- **确认选择**:`confirmSelection()`,确认选择的日期范围,触发 `update:modelValue` 事件并关闭弹窗。
|
||||
- **关闭弹窗**:`closePicker()`,关闭日期选择弹窗,并重置临时日期。
|
||||
- **重置临时日期**:`resetTempDates()`,将临时日期重置为当前的 `modelValue`。
|
||||
- **生成日历数据**:`generateCalendar(date)`,根据给定的月份生成日历数据。
|
||||
- **日期选择处理**:`selectDate(date)`,处理日期选择,更新 `modelValue`。
|
||||
- **切换月份**:`changeMonth(offset)`,切换当前显示的月份。
|
||||
- **判断日期状态**:
|
||||
- `isSelected(date)`:判断日期是否被选中。
|
||||
- `isEnd(date)`:判断日期是否为结束日期。
|
||||
- `isToday(date)`:判断日期是否为今天。
|
||||
- `isStart(date)`:判断日期是否为开始日期。
|
||||
- `isInRange(date)`:判断日期是否在选择的范围内。
|
||||
- `isSameDay(d1, d2)`:判断两个日期是否为同一天。
|
||||
- **快捷范围设置**:`setQuickRange(type)`,根据快捷按钮的类型设置日期范围,并关闭弹窗。
|
||||
- **自然周期计算**:`calcNaturalRange(type, currentDate)`,计算自然周期模式下的日期范围。
|
||||
- **相对周期计算**:`calcRelativeRange(type, currentDate)`,计算相对周期模式下的日期范围。
|
||||
- **周计算(周一为起点)**:
|
||||
- `getWeekStart(date)`:获取一周的开始日期。
|
||||
- `getWeekEnd(startDate)`:获取一周的结束日期。
|
||||
- **月末处理**:`lastDayOfMonth(date)`,获取给定月份的最后一天。
|
||||
- **月末日调整**:`adjustMonthEnd(start, end)`,调整开始日期到合适的月末日期。
|
||||
- **闰年判断**:`isLeapYear(year)`,判断给定年份是否为闰年。
|
||||
- **模式切换**:`toggleMode(isNatural)`,切换自然周期和相对周期模式。
|
||||
- **日期格式化**:`formatDate(date)`,将日期格式化为 `YYYY-MM-DD` 的字符串。
|
||||
|
||||
### 3. 样式部分
|
||||
- **输入框区域**:设置输入框的样式,包括边框、背景色、字体大小等。
|
||||
- **紧凑弹窗**:设置弹窗的整体样式,包括背景色、边框半径等。
|
||||
- **紧凑头部**:设置月份标题和导航箭头的样式。
|
||||
- **紧凑日期网格**:设置星期几和日期的显示样式,以及不同状态下的日期背景色和字体颜色。
|
||||
- **快捷按钮区域**:设置模式切换按钮和快捷操作按钮的样式。
|
||||
- **底部操作**:设置取消和确定按钮的样式。
|
||||
|
||||
## 五、注意事项
|
||||
- 组件使用了 `uni-popup` 组件,确保项目中已正确引入和配置。
|
||||
- 日期范围的选择逻辑较为复杂,特别是在自然周期和相对周期的切换以及快捷范围设置时,需注意日期的计算和显示。
|
||||
- 组件的样式是基于 `scoped` 作用域的,可根据项目需求进行调整和扩展。
|
||||
如果需要进一步定制化,可以修改以下部分:
|
||||
1. 日历组件配置(`wu-calendar` 参数)
|
||||
2. 日期格式化函数(`formatDate`)
|
||||
3. 输入框样式和交互逻辑
|
||||
4. 异常处理机制
|
@ -1 +1,90 @@
|
||||
# cxc-szcx-dictSelect
|
||||
### 组件说明:`uni-data-checkbox` 多选字典项选择组件
|
||||
|
||||
#### 一、组件概述
|
||||
该组件基于 Vue 3 和 `uni-data-checkbox` 实现了一个多选的字典项选择器。它通过传入字典编码(`dictCode`)从后端接口获取字典项数据,并将其展示为多选框列表。组件支持双向数据绑定,可将选中的值同步给父组件,同时在选中值发生变化时触发 `change` 事件。
|
||||
|
||||
#### 二、组件使用的技术栈
|
||||
- **框架**:Vue 3(使用组合式 API)
|
||||
- **UI 组件**:`uni-data-checkbox`(用于展示多选框列表)
|
||||
- **请求库**:假设使用了自定义的 `getDictItemsApi` 函数进行后端数据请求
|
||||
|
||||
#### 三、组件输入(Props)
|
||||
|
||||
| 属性名 | 类型 | 是否必填 | 默认值 | 说明 |
|
||||
| ---- | ---- | ---- | ---- | ---- |
|
||||
| `dictCode` | String | 是 | 无 | 用于从后端获取字典项数据的字典编码 |
|
||||
| `modelValue` | String | 否 | '' | 双向绑定的选中值,多个值以逗号分隔 |
|
||||
|
||||
#### 四、组件输出(Emits)
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| ---- | ---- | ---- |
|
||||
| `update:modelValue` | 当选中值发生变化时,用于更新父组件的 `modelValue` | 选中值的字符串,多个值以逗号分隔 |
|
||||
| `change` | 当选中值发生变化时触发 | 选中值的字符串,多个值以逗号分隔 |
|
||||
|
||||
#### 五、组件内部数据
|
||||
|
||||
| 变量名 | 类型 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| `dictItems` | Ref<Array> | 存储从后端获取的字典项数据 |
|
||||
| `selectedValuesArray` | Ref<Array> | 存储当前选中的值,以数组形式存储 |
|
||||
|
||||
#### 六、组件方法
|
||||
|
||||
##### 1. `loadDictItems`
|
||||
- **功能**:异步从后端接口获取字典项数据,并将其赋值给 `dictItems`。
|
||||
- **实现逻辑**:
|
||||
- 调用 `getDictItemsApi` 函数,传入 `props.dictCode` 进行数据请求。
|
||||
- 若请求成功,将响应数据的 `result` 字段赋值给 `dictItems.value`。
|
||||
- 若请求失败,在控制台输出错误信息。
|
||||
|
||||
##### 2. 监听 `selectedValuesArray` 的变化
|
||||
- **功能**:当 `selectedValuesArray` 发生变化时,将选中的值转换为字符串,并触发 `update:modelValue` 和 `change` 事件通知父组件。
|
||||
- **实现逻辑**:
|
||||
- 使用 `watch` 函数监听 `selectedValuesArray` 的变化。
|
||||
- 当变化发生时,将新的选中值数组使用 `join(',')` 方法转换为字符串。
|
||||
- 触发 `update:modelValue` 和 `change` 事件,将转换后的字符串作为参数传递。
|
||||
|
||||
##### 3. 监听 `props.modelValue` 的变化
|
||||
- **功能**:当父组件传递的 `modelValue` 发生变化时,更新 `selectedValuesArray`。
|
||||
- **实现逻辑**:
|
||||
- 使用 `watch` 函数监听 `props.modelValue` 的变化。
|
||||
- 当变化发生时,将新的 `modelValue` 使用 `split(',')` 方法转换为数组,并赋值给 `selectedValuesArray.value`。
|
||||
- `immediate: true` 表示在组件初始化时就会执行一次监听回调,确保初始值能正确同步。
|
||||
|
||||
#### 七、组件生命周期钩子
|
||||
|
||||
##### `onMounted`
|
||||
- **功能**:在组件挂载后调用 `loadDictItems` 函数,从后端获取字典项数据。
|
||||
|
||||
#### 八、使用示例
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<MyDictCheckbox
|
||||
:dictCode="myDictCode"
|
||||
v-model="selectedValues"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<p>当前选中的值: {{ selectedValues }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import MyDictCheckbox from './MyDictCheckbox.vue';
|
||||
|
||||
const myDictCode = 'exampleDictCode';
|
||||
const selectedValues = ref('');
|
||||
|
||||
const handleChange = (newValue) => {
|
||||
console.log('选中值发生变化:', newValue);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 九、注意事项
|
||||
- 确保 `getDictItemsApi` 函数能正确获取后端数据,且响应数据的格式符合 `{ result: [...] }`。
|
||||
- 若 `modelValue` 初始值为空字符串,`selectedValuesArray` 会初始化为空数组。
|
||||
- 由于使用了 `watch` 的 `deep: true` 选项,在 `selectedValuesArray` 内部元素发生变化时也会触发监听回调,可能会影响性能,需注意。
|
@ -1,35 +1,106 @@
|
||||
# trq-depart-select
|
||||
# 1.0.1
|
||||
更新,添加返回值,将整个机构对象返回父组件
|
||||
```javascript
|
||||
const onpopupclosed = ((e) => {
|
||||
selectDepartID.value = tempSelectDepartID.value
|
||||
$emit('change', selectDepartID.value, departInfo)
|
||||
})
|
||||
### 组件说明:单位选择组件
|
||||
|
||||
#### 一、组件概述
|
||||
此组件为基于 UniApp 和 Vue 3 构建的单位选择组件,借助 `uni-data-picker` 组件实现单位的选择功能。组件支持依据传入的 `returnCodeOrID` 属性,返回单位的 `orgCode` 或者 `id`。组件会从后端获取单位列表数据,并在用户选择单位时触发 `change` 事件通知父组件。
|
||||
|
||||
#### 二、组件使用的技术栈
|
||||
- **框架**:Vue 3(使用组合式 API)
|
||||
- **状态管理**:`@/store` (推测使用了 Vuex 或 Pinia 进行状态管理)
|
||||
- **UI 组件**:`uni-data-picker` (用于展示单位选择器)
|
||||
- **请求库**:自定义的 API 请求函数(如 `queryMyDeptTreeListApi`)
|
||||
|
||||
#### 三、组件输入(Props)
|
||||
|
||||
| 属性名 | 类型 | 是否必填 | 默认值 | 说明 |
|
||||
| ---- | ---- | ---- | ---- | ---- |
|
||||
| `returnCodeOrID` | String | 否 | "orgCode" | 决定返回单位的 `orgCode` 还是 `id` |
|
||||
|
||||
#### 四、组件输出(Emits)
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| ---- | ---- | ---- |
|
||||
| `change` | 当用户选择单位时触发,用于通知父组件选择结果 | 选中单位的 `orgCode` 或 `id`,以及单位的全部信息对象 |
|
||||
|
||||
#### 五、组件内部数据
|
||||
|
||||
| 变量名 | 类型 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| `departList` | Ref<Array> | 存储从后端获取的单位列表数据 |
|
||||
| `selectDepartID` | Ref<String> | 当前选中的单位 ID |
|
||||
| `selectDepartIDS` | Ref<Array> | 选中的级联单位 ID 数组 |
|
||||
| `tempSelectDepartID` | Ref<String> | 临时选择的单位 ID |
|
||||
| `depart` | Ref<Object> | 对 `uni-data-picker` 组件的引用 |
|
||||
| `departInfo` | Ref<Object> | 选中单位的全部信息 |
|
||||
|
||||
#### 六、组件方法
|
||||
|
||||
##### 1. `getDepartList`
|
||||
- **功能**:异步从后端获取单位列表数据,并将其赋值给 `departList`。
|
||||
- **实现逻辑**:
|
||||
- 调用 `queryMyDeptTreeListApi` 函数进行数据请求。
|
||||
- 若请求成功,将响应数据的 `result` 字段赋值给 `departList.value`。
|
||||
- 若请求失败,在控制台输出错误信息。
|
||||
|
||||
##### 2. `onnodeclick`
|
||||
- **功能**:处理用户点击单位节点的事件,更新 `departInfo` 和 `tempSelectDepartID`。
|
||||
- **实现逻辑**:
|
||||
- 将点击的单位信息赋值给 `departInfo.value`。
|
||||
- 根据 `props.returnCodeOrID` 的值,将点击单位的 `orgCode` 或 `value` 赋值给 `tempSelectDepartID.value`。
|
||||
|
||||
##### 3. `onchange`
|
||||
- **功能**:处理 `uni-data-picker` 的 `change` 事件,更新 `selectDepartID`。
|
||||
- **实现逻辑**:将选择的单位 ID 赋值给 `selectDepartID.value`。
|
||||
|
||||
##### 4. `onpopupclosed`
|
||||
- **功能**:处理 `uni-data-picker` 弹出框关闭的事件,将 `tempSelectDepartID` 的值赋给 `selectDepartID`。
|
||||
- **实现逻辑**:将 `tempSelectDepartID.value` 赋值给 `selectDepartID.value`。
|
||||
|
||||
##### 5. 监听 `tempSelectDepartID` 的变化
|
||||
- **功能**:当 `tempSelectDepartID` 发生变化时,触发 `change` 事件通知父组件。
|
||||
- **实现逻辑**:
|
||||
- 使用 `watch` 函数监听 `tempSelectDepartID` 的变化。
|
||||
- 当变化发生时,触发 `change` 事件,将 `tempSelectDepartID` 的新值和 `departInfo` 作为参数传递。
|
||||
- `immediate: true` 表示在组件初始化时就会执行一次监听回调。
|
||||
- `deep: true` 表示深度监听,确保 `tempSelectDepartID` 内部属性变化也能被捕获。
|
||||
|
||||
#### 七、组件生命周期钩子
|
||||
|
||||
##### `onLoad`
|
||||
- **功能**:在页面加载时调用 `getDepartList` 函数,从后端获取单位列表数据。
|
||||
|
||||
#### 八、样式说明
|
||||
```css
|
||||
.no-wrap-picker::v-deep .uni-data-picker-item {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
```
|
||||
增加选择到最后层级触发的change时间,返回数据到父组件的功能
|
||||
- 此样式规则用于强制 `uni-data-picker` 的选项不换行,超出部分隐藏并显示省略号。
|
||||
|
||||
```javascript
|
||||
const onchange = ((e) => {
|
||||
$emit('change', e.detail.value[e.detail.value.length - 1].value, {})
|
||||
})
|
||||
#### 九、使用示例
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<UnitSelector
|
||||
:returnCodeOrID="'id'"
|
||||
@change="handleUnitChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import UnitSelector from './UnitSelector.vue';
|
||||
|
||||
const handleUnitChange = (selectedId, unitInfo) => {
|
||||
console.log('选中的单位 ID:', selectedId);
|
||||
console.log('选中单位的信息:', unitInfo);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 1.0
|
||||
|
||||
属性 returnCodeOrID 默认值 orgCode, 组件返回单位的orgCode, 不设置属性或设置为其他,组件返回 单位ID
|
||||
|
||||
事件:change 选择内容发生变化时发生,通过emit 返回给父组件 选择单位的ID或code
|
||||
|
||||
由于uni-data-picker 无法选择任意节点数据,通过关闭组件事件返回当前选择的节点数据。
|
||||
```javascript
|
||||
const onpopupclosed = ((e) => {
|
||||
selectDepartID.value = tempSelectDepartID.value
|
||||
$emit('change', selectDepartID.value)
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
#### 十、注意事项
|
||||
- 确保 `queryMyDeptTreeListApi` 函数能正确获取后端数据,且响应数据的格式符合 `{ success: true, result: [...] }`。
|
||||
- 由于使用了 `watch` 的 `deep: true` 选项,在 `tempSelectDepartID` 内部元素发生变化时也会触发监听回调,可能会影响性能,需注意。
|
||||
- 代码中注释掉的部分(如 `onnodeclick` 和 `onpopupclosed` 中触发 `change` 事件的代码)可能是之前的实现逻辑,若需要可根据实际情况恢复使用。
|
Loading…
Reference in New Issue
Block a user