生产模块

This commit is contained in:
yangzhq68909 2025-05-22 15:30:36 +08:00
parent d510a9a734
commit 2ee9a1b0d6
175 changed files with 24684 additions and 48 deletions

View File

@ -33,10 +33,10 @@ export default defineUniPages({
text: '首页',
},
{
iconPath: 'static/tabbar/tabbar-message-2.png',
selectedIconPath: 'static/tabbar/tabbar-message.png',
pagePath: 'pages/message/message',
text: '消息',
"iconPath": "static/tabbar/tabbar-produce.png",
"selectedIconPath": "static/tabbar/tabbar-produce-2.png",
"pagePath": "pages/produce/index",
"text": "生产"
},
{
iconPath: 'static/tabbar/tabbar-workHome-2.png',

View File

@ -0,0 +1,115 @@
import { http } from '@/utils/http';
/* 获取部门所有人员信息 */
export function listApi(config : object) {
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/list',
method: 'GET',
data: config
})
}
/* 获取工作经历 */
export function workExperienceListApi(ldhth : string) {
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/queryLl',
method: 'GET',
data: ldhth
})
}
export function queryGzjlByRyLdhth(ldhth) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/queryLl',
method: 'GET',
data: ldhth
})
}
export function queryQzqkByRyLdhth(ldhth) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/queryZjtz',
method: 'GET',
data: ldhth
})
}
export function queryJtzycyByRyLdhth(ldhth) { //
return http({
url: '/cxchrygxxtj/cxcHrYgxxtj/queryJtzycy',
method: 'GET',
data: ldhth
})
}
export function queryXlxxByRyLdhth(ldhth) { //
return http({
url: '/cxchrygxxtj/cxcHrYgxxtj/listCxcXlxwxxFbByMainId',
method: 'GET',
data: ldhth
})
}
export function queryGbxxByRyLdhth(ldhth) { //
return http({
url: '/cxc_rlzy.gbxx/cxcRlzyGbxx/queryGbxx',
method: 'GET',
data: ldhth
})
}
export function queryZyzgdjByRyLdhth(ldhth) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/queryZyzgdj',
method: 'GET',
data: ldhth
})
}
export function queryJxkhByRyLdhth(ldhth) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/listCxcRlzyJxkhByMainId',
method: 'GET',
data: ldhth
})
}
export function cxcHrYgxxtj(parm) { //
return http({
url: '/cxchrygxxtj/cxcHrYgxxtj/list',
method: 'GET',
data: parm
})
}
export function cxcRyDataTongji(url, parm) { //
return http({
url: url,
method: 'GET',
data: parm
})
}
export function cxcRyDatAstatistics(parm) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/statistics',
method: 'GET',
data: parm
})
}
export function cxcRyDatAstatisticsCertificate(parm) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/getStatisticsCertificate',
method: 'GET',
data: parm
})
}
export function cxcRyDatAstatisticsDetails(parm) { //
return http({
url: '/cxc_rlzy.zb/cxcRlzyZb/details',
method: 'GET',
data: parm
})
}

51
src/api/produce/index.ts Normal file
View File

@ -0,0 +1,51 @@
import { http } from '@/utils/http';
export function queryJinriShengchansj(params : object) { //
return http({
url: '/scdt.CxcScdtChart/cxcScdtChart/list',
method: 'GET',
data: params
})
}
export function queryJinriTrqShengchansj(params : object) { // day gas unit
return http({
url: '/scdt.CxcScdtChart/cxcScdtChart/getStatisticsData',
method: 'GET',
data: params
})
}
export function queryYearShengchansj(params : object) { //
return http({
url: '/scdt.CxcScdtChart/cxcScdtChart/getYearStatis',
method: 'GET',
data: params
})
}
export function queryJinriYuanyouShengchansj(params : object) { //
return http({
url: '/scdt.cxcscdtwyrb/cxcScdtWyRb/list',
method: 'GET',
data: params
})
}
// API
export function queryJldZcList(params : object) { //
return http({
url: '/sys/sysDepart/queryDepartsByzdjl',
method: 'GET',
data: params
})
}
export function queryJldDataByZc(params : object) { //
return http({
url: 'http://10.75.166.6:9999/Gyk/sssj/GetJlByZc',
method: 'GET',
data: params
})
}

View File

@ -24,4 +24,12 @@ export function queryDepsByUserIdApi() {
url: '/sys/user/getCurrentUserDeparts',
method: 'GET',
});
}
export function queryMyDeptTreeListApi(config : object) { //
return http({
url: '/sys/sysDepart/queryTreeList',
method: 'GET',
data: config
})
}

View File

@ -1,9 +1,9 @@
import { http } from '@/utils/http'; // @/ ./src/
// ts HTTP TypeScript HTTP "POST""post"
interface LoginParams {
username: string;
password: string;
captcha?: string; //
username : string;
password : string;
captcha ?: string; //
}
/**
@ -12,13 +12,13 @@ interface LoginParams {
* @returns 登录请求的 Promise
*/
export function loginApi(config : LoginParams) {
// captcha/sys/login,/sys/sinopecLogin
const url = config.captcha ? '/sys/login' : '/sys/sinopecLogin';
return http({
url,
method: 'POST',
data: config,
});
// captcha/sys/login,/sys/sinopecLogin
const url = config.captcha ? '/sys/login' : '/sys/sinopecLogin';
return http({
url,
method: 'POST',
data: config,
});
}
/**
@ -48,7 +48,7 @@ export function jurisdictionApi(id : string) {
});
}
export function getUserPermissionApi(config) { //
export function getUserPermissionApi(config : object) { //
return http({
url: '/sys/permission/getUserPermissionByToken',
method: 'GET',
@ -59,7 +59,7 @@ export function getUserPermissionApi(config) { // 获取权限
/**
* 获取首页轮播图
*/
export function queryCarouselApi(config) {
export function queryCarouselApi(config : object) {
return http({
url: '/CxcDaping/cxcDaping/list',
method: 'GET',
@ -70,7 +70,7 @@ export function queryCarouselApi(config) {
/**
* 获取分类字典
*/
export function getCategoryItemsApi(pid) { //
export function getCategoryItemsApi(pid : string) {
return http({
url: '/sys/category/findtree',
method: 'GET',
@ -78,4 +78,11 @@ export function getCategoryItemsApi(pid) { // 分类字典专用
pid
}
})
}
export function getDictItemsApi(dictCode : string) { //
return http({
url: `/sys/dict/getDictItems/${dictCode}`,
method: 'GET'
})
}

View File

@ -155,7 +155,7 @@
}
const handleSubmit = () => {
if(loading.value) return
if (loading.value) return
form.value.validate().then(({
valid,
errors

View File

@ -17,7 +17,7 @@
</wd-row>
<wd-row>
<wd-col :span="24">
<SelectDept label="" v-model="orgCode" @change="Search" rowKey="orgCode" :multiple="false">
<SelectDept label="" v-model="orgCode" @change="queryLeave" rowKey="orgCode" :multiple="false">
</SelectDept>
</wd-col>
</wd-row>
@ -27,10 +27,10 @@
</wd-row>
<wd-row>
<wd-col :span="12">
<uni-easyinput v-model="realname" placeholder="姓名模糊查询" @change="Search" @clear="Search" />
<uni-easyinput v-model="realname" placeholder="姓名模糊查询" @change="queryLeave" @clear="queryLeave" />
</wd-col>
<wd-col :span="12">
<uni-easyinput v-model="contractNumber" placeholder="劳动合同号查询" @change="Search" @clear="Search" />
<uni-easyinput v-model="contractNumber" placeholder="劳动合同号查询" @change="queryLeave" @clear="queryLeave" />
</wd-col>
</wd-row>
<wd-row>
@ -70,10 +70,10 @@
const list = ref([]) //
const realname = ref('') //
const contractNumber = ref('') //
const orgCode = ref('') //
const type = ref('') //
const range = ref([]) //
const timeout = ref(null)
const orgCode = ref('')
let pageNo = 1
let pageSize = 10
let loading = false
@ -147,15 +147,6 @@
}, 300);
};
function Search() {
if (timeout.value) {
clearTimeout(timeout.value);
}
timeout.value = setTimeout(() => {
queryLeave()
}, 300); // 300ms
}
function reset() {
orgCode.value = ''
range.value = []

View File

@ -0,0 +1,511 @@
<template>
<PageLayout :navbarShow="false">
<wd-card style="margin: 10px;" id="top1">
<wd-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<wd-col :span="24"><uni-title :title="'所选单位'" align="left" type="h4"></uni-title></wd-col>
</wd-row>
<wd-row style="margin-bottom: 20rpx; margin-left: 30rpx; margin-right: 30rpx">
<wd-col :span="24">
<SelectDept label="" v-model="orgCode" @change="departChange" rowKey="orgCode" :multiple="false">
</SelectDept>
</wd-col>
</wd-row>
<!-- 概览统计 -->
<view class="stats-box" v-if="summary.total">
<view class="stat-item">
<text class="label">总人数</text>
<text class="value">{{ summary.total }}</text>
</view>
<view class="stat-item">
<text class="label">平均年龄</text>
<text class="value">{{ summary.avgAge.toFixed(1) }}</text>
</view>
</view>
</wd-card>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
<!-- 数据表格 -->
<wd-row style="margin-top: 10px; margin-left: 30rpx; margin-right: 30rpx" v-if="tableData.length > 0">
<wd-col :span="3">
<view class="titleStyle">序号</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">姓名</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">性别</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">年龄</view>
</wd-col>
<wd-col :span="6">
<view class="titleStyle">操作</view>
</wd-col>
</wd-row>
<scroll-view scroll-y :style="{ height: bottomHeight + 'px' }">
<wd-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<view v-for="(item, index) in tableData">
<wd-col :span="3">
<view class="dataStyle">
{{ index + 1 }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle">
{{ item.xm }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle">
{{ item.xb_dictText }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle">
{{ item.nl }}
</view>
</wd-col>
<wd-col :span="6">
<view class="dataStyle">
<span @click="detail(item)" style="color: red">详情</span>
<!-- <button size="mini" type="primary" @click="detail(item)">详情</button> -->
</view>
</wd-col>
</view>
</wd-row>
</scroll-view>
</PageLayout>
</template>
<script setup>
import {
useMessage,
useToast
} from 'wot-design-uni'
import * as echarts from 'echarts';
import SelectDept from '@/components/SelectDept/SelectDept'
import {
listApi
} from '@/api/humanResource/personnel';
const message = useMessage()
const toast = useToast()
//
const bottomHeight = ref(0);
//
const orgCode = ref('');
const rawData = ref([]);
const tableData = ref([]);
const summary = reactive({
total: 0,
avgAge: 0
});
const chart = ref(null);
const chartOption = ref({});
const drillPopup = ref(null);
const drillList = ref([]);
const drillTitle = ref('');
function detail(record) {
uni.navigateTo({
url: '/pages/views/renliziyuan/renyuanxinxi/detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
//
const calculateAge = (birthDate) => {
const today = new Date();
const birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
};
//
const departChange = async (e, data) => {
tableData.value = [];
orgCode.value = e;
try {
//
if (orgCode.value.length <= 6) {
toast.warning('全厂数据较多,请选下一层级...')
return;
} else {
toast.loading('数据加载中...')
}
let params = {
pageSize: 3000,
fields: ['xm', 'nl', 'xb', 'xb_dictText', 'orgCode', 'jcdw', 'jcxd', 'jcxdCode']
};
if (orgCode.value.length <= 9) {
params.orgCode = orgCode.value + '*';
} else {
params.jcxd_code = orgCode.value;
}
listApi(params)
.then((res) => {
if (res.success) {
toast.close()
processData(res.result.records);
//
uni.hideLoading();
}
})
.catch((err) => {
console.log(err);
toast.error('数据加载失败')
});
} catch (error) {
toast.error('数据加载失败')
} finally {
//
uni.hideLoading();
}
};
//
const processData = (data) => {
//
const validData = data
.map((item) => ({
...item,
nl: calculateAge(item.cssj)
}))
.filter((item) => item.nl >= 21 && item.nl <= 64);
//
summary.total = validData.length;
summary.avgAge = validData.reduce((sum, cur) => sum + cur.nl, 0) / summary.total || 0;
//
// tableData.value = validData;
groupsData(validData);
//
generateChartData(validData);
};
// ...
const subOrgStaffs = ref({}); //
const ageGroupStaffs = ref({}); //
const groupsData = (data) => {
//
subOrgStaffs.value = {};
ageGroupStaffs.value = {};
data.reduce((acc, cur) => {
// console.log(cur)
let subOrg = '';
let ageRange = getAgeRange(cur.nl);
// console.log(cur.orgCode, cur.jcxdCode)
if (cur.orgCode <= 6) {
subOrg = cur.orgCode;
} else {
subOrg = cur.jcxdCode;
}
// subOrgStaffs
if (!subOrgStaffs.value[subOrg]) {
subOrgStaffs.value[subOrg] = [];
}
subOrgStaffs.value[subOrg].push(cur);
// ageGroupStaffs
if (!ageGroupStaffs.value[ageRange]) {
ageGroupStaffs.value[ageRange] = [];
}
ageGroupStaffs.value[ageRange].push(cur);
});
};
//
const getAgeRange = (age) => {
const ranges = ['21-30岁', '31-40岁', '41-50岁', '51-60岁', '61-64岁'];
const index = Math.floor((age - 21) / 10);
return ranges[index] || '其他';
};
//
const showStaffList = (subOrg, ageRange) => {
//
const targetStaffs = subOrgStaffs.value[subOrg].filter((staff) => getAgeRange(staff.nl) === ageRange);
staffList.value = targetStaffs;
popupTitle.value = `${subOrg} ${ageRange}人员列表(共${targetStaffs.length}人)`;
popup.value.open();
};
//
const getSubOrgStaffs = (subOrgCode) => {
return subOrgStaffs.value[subOrgCode] || [];
};
//
const getAgeGroupStaffs = (ageRange) => {
return ageGroupStaffs.value[ageRange] || [];
};
//
const generateChartData = (data) => {
//
const ageRanges = ['21-30岁', '31-40岁', '41-50岁', '51-60岁', '61-64岁'];
const jcdwGroups = data.reduce((acc, cur) => {
if (!acc[cur.jcdw]) {
acc[cur.jcdw] = {
ageGroups: [0, 0, 0, 0, 0] // 21-30,31-40,41-50,51-60,61-64
};
}
const ageGroup = Math.floor((cur.nl - 21) / 10);
// console.log(ageGroup, cur.jcdw)
if (ageGroup >= 0 && ageGroup <= 4) {
acc[cur.jcdw].ageGroups[ageGroup]++;
}
return acc;
}, {});
//
const xData = Object.keys(jcdwGroups);
const seriesData = ageRanges.map((range, index) => ({
name: range,
type: 'bar',
data: xData.map((jcdw) => jcdwGroups[jcdw].ageGroups[index] || 0),
itemStyle: {
color: ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'][index]
},
//
label: {
show: true,
position: 'top'
}
// 20
// barWidth: 20
}));
chartOption.value = {
title: {
text: '人员年龄分组统计',
padding: [0, 0, 0, 30]
},
toolbox: {
padding: [0, 30, 0, 0],
show: true,
feature: {
//
restore: {
show: true //
},
saveAsImage: {
show: true //
}
}
},
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'shadow',
// label: {
// show: false
// }
// }
// },
grid: {
top: '15%',
left: '4%',
right: '4%',
bottom: '10%',
containLabel: true
},
legend: {
data: ageRanges,
itemGap: 5,
padding: [0, 15, 0, 15],
y: 'bottom',
itemHeight: 8, //
itemWidth: 8, //
type: 'scroll'
},
xAxis: {
type: 'category',
data: xData,
axisLabel: {
color: '#7F84B5',
fontWeight: 300,
interval: 0,
rotate: 0
},
padding: [0, 10, 0, 10],
axisTick: {
show: false //线
},
axisLine: {
show: false //线
}
},
yAxis: [{
show: true,
boundaryGap: false, //线
type: 'value',
// name: 'Budget (million USD)',
// data: this.yList,
minInterval: 1,
axisLabel: {
interval: 0
},
splitLine: {
show: true,
lineStyle: {
//线
type: 'dashed'
}
},
axisTick: {
show: true //线
},
axisLine: {
show: false //线
}
}],
series: seriesData
};
//
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
myChart.on('click', (params) => {
console.log(params.seriesName);
tableData.value = getAgeGroupStaffs(params.seriesName);
});
}, 300);
// #ifdef APP
getHeight();
// #endif
};
onMounted(() => {
toast.warning('全厂数据较多,请选下一层级...')
// #ifdef APP
getHeight();
// #endif
});
// #ifdef APP
const getHeight = () => {
//
const systemInfo = uni.getSystemInfoSync();
const screenHeight = systemInfo.screenHeight;
//
const query = uni.createSelectorQuery();
//
query
.select('#top1')
.boundingClientRect((rect1) => {
//
const topComponentsHeight = rect1.height;
//
bottomHeight.value = screenHeight - topComponentsHeight - 415;
})
.exec();
};
// #endif
//
const initChart = () => {
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
}, 300);
};
</script>
<style scoped>
.input-group {
display: flex;
gap: 20rpx;
margin-bottom: 30rpx;
}
.input {
flex: 1;
border: 1rpx solid #ddd;
padding: 15rpx;
border-radius: 8rpx;
}
.query-btn {
background: #007aff;
color: white;
padding: 0 40rpx;
border-radius: 8rpx;
}
.stats-box {
display: flex;
justify-content: space-around;
margin: 30rpx 0;
padding: 20rpx;
background: #f8f8f8;
border-radius: 12rpx;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.label {
font-size: 24rpx;
color: #666;
}
.value {
font-size: 36rpx;
font-weight: bold;
color: #0000ff;
}
.chart-container {
height: 400rpx;
margin-top: 20rpx;
}
.titleStyle {
font-size: 12px;
color: #747474;
line-height: 30px;
height: 30px;
background: #f2f9fc;
text-align: center;
vertical-align: middle;
border-left: 1px solid #919191;
border-bottom: 1px solid #919191;
}
/* 内容样式 */
.dataStyle {
max-font-size: 14px;
/* 最大字体限制 */
min-font-size: 10px;
/* 最小字体限制 */
font-size: 12px;
color: #00007f;
line-height: 30px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,615 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '人员详情',
},
}
</route>
<template>
<PageLayout navTitle="人员详情">
<scroll-view :scroll-x="true" :scroll-y="true">
<view style="padding: 10px 10px 10px 10px">
<uni-title title="基本信息" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="18"><yjly-row-cell :cellData="cellData" :rowDataCount="2"></yjly-row-cell></wd-col>
<wd-col :span="6">
<view class="img">
<image mode="aspectFit"
:src="'https://36.112.48.190/jeecg-boot/sys/common/static/' + imgUrl"></image>
</view>
</wd-col>
</wd-row>
<yjly-row-cell :cellData="cellData1" :rowDataCount="3"></yjly-row-cell>
<view v-if="roleDetail">
<uni-title title="年度绩效考核" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="4">
<view class="titleStyle">序号</view>
</wd-col>
<wd-col :span="10">
<view class="titleStyle">绩效考核年份</view>
</wd-col>
<wd-col :span="10">
<view class="titleStyle">绩效考核成绩</view>
</wd-col>
</wd-row>
<wd-row>
<view v-for="(item, index) in jxkhxxList">
<wd-col :span="4">
<view class="dataStyle1">
{{ index + 1 }}
</view>
</wd-col>
<wd-col :span="10">
<view class="dataStyle1">
{{ item.nf }}
</view>
</wd-col>
<wd-col :span="10">
<view class="dataStyle1">
{{ item.khcj + '---' + item.khcj_dictText }}
</view>
</wd-col>
</view>
</wd-row>
</view>
<uni-title title="工作简历" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="4">
<view class="titleStyle">起始时间</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">终止时间</view>
</wd-col>
<wd-col :span="11">
<view class="titleStyle">工作职务</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">岗位职务</view>
</wd-col>
</wd-row>
<wd-row>
<view v-for="(item, index) in gzjlList">
<wd-col :span="4">
<view class="dataStyle2">
{{ item.kssj }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2">
{{ item.jssj }}
</view>
</wd-col>
<wd-col :span="11">
<view class="dataStyle2" ref="dataView">
{{ item.jlms }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle1">
{{ item.jlms2 }}
</view>
</wd-col>
</view>
</wd-row>
<!-- <uni-segmented-control></uni-segmented-control> -->
<view>
<uni-title title="学历信息" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="4">
<view class="titleStyle">类别</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">毕业院校</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">所学专业</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">学历</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">学位</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">毕业时间</view>
</wd-col>
</wd-row>
<wd-row>
<view v-for="(item, index) in xlxxList">
<wd-col :span="4">
<view class="dataStyle1">
{{ item.xllb }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2">
{{ item.byyx }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2" ref="dataView">
{{ item.sxzy }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2">
{{ item.qdxl }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2">
{{ item.qdxw }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle2">
{{ item.bytime }}
</view>
</wd-col>
</view>
</wd-row>
</view>
<uni-title title="取证信息" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="6">
<view class="titleStyle">证书名称</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">证书等级</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">取证时间</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">复审时间</view>
</wd-col>
<wd-col :span="3">
<view class="titleStyle">备注</view>
</wd-col>
</wd-row>
<wd-row>
<view v-for="(item, index) in zjtzList">
<wd-col :span="6">
<view class="dataStyle2" ref="dataView">
{{ item.zjmc }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle1">
{{ item.zsdj }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle1">
{{ item.fzrq }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle1">
{{ item.fssj }}
</view>
</wd-col>
<wd-col :span="3">
<view class="dataStyle1">
{{ item.bz }}
</view>
</wd-col>
</view>
</wd-row>
<view v-if="roleDetail">
<uni-title title="家庭信息" type="h1" color="blue"></uni-title>
<wd-row>
<wd-col :span="4">
<view class="titleStyle">称谓</view>
</wd-col>
<wd-col :span="3">
<view class="titleStyle">姓名</view>
</wd-col>
<wd-col :span="6">
<view class="titleStyle">出生年月</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">政治面貌</view>
</wd-col>
<wd-col :span="7">
<view class="titleStyle">工作单位及职务</view>
</wd-col>
</wd-row>
<wd-row>
<view v-for="(item, index) in zyjtcyList">
<wd-col :span="4">
<view class="dataStyle2">
{{ item.ybrgx }}
</view>
</wd-col>
<wd-col :span="3">
<view class="dataStyle1">
{{ item.gxname }}
</view>
</wd-col>
<wd-col :span="6">
<view class="dataStyle1">
{{ item.cstime }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle1">
{{ item.cyzzmm }}
</view>
</wd-col>
<wd-col :span="7">
<view class="dataStyle2" ref="dataView">
{{ item.cygzdw }}
</view>
</wd-col>
</view>
</wd-row>
</view>
</view>
</scroll-view>
</PageLayout>
</template>
<script setup>
import {
queryGzjlByRyLdhth,
queryQzqkByRyLdhth,
queryJtzycyByRyLdhth,
queryXlxxByRyLdhth,
queryGbxxByRyLdhth,
queryZyzgdjByRyLdhth,
queryJxkhByRyLdhth
} from '@/api/humanResource/personnel'
import {
useUserStore
} from '@/store/user'
const store = useUserStore();
const tempCellData = ref({
title: '',
value: '',
titleSpan: 4,
valueSpan: 4
});
const roleDetail = ref(store.userInfo.realname === '廖德云' || store.userInfo.realname === '马敬朝');
const ldhth = ref('');
const imgUrl = ref('');
const renyuanData = ref({});
const cellData = ref([]);
const cellData1 = ref([]);
const gzjlList = ref([]); // -
const zjtzList = ref([]); // -
const zyjtcyList = ref([]); // -
const xlxxList = ref([]); // -
const gbxxList = ref([]); // -
const zyzgdjList = ref([]); // -
const jxkhxxList = ref([]); // -
function getChildTable() {
console.log(ldhth.value);
queryJxkhByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
jxkhxxList.value = res.result.records;
// console.log(jxkhxxList.value)
})
.catch((err) => {
console.log(err);
});
queryGbxxByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
gbxxList.value = res;
// console.log(gbxxList.value)
})
.catch((err) => {
console.log(err);
});
queryZyzgdjByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
zyzgdjList.value = res;
// console.log(zyzgdjList.value)
})
.catch((err) => {
console.log(err);
});
queryGzjlByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
if (res.length > 0) {
gzjlList.value = res;
}
})
.catch((err) => {
console.log(err);
});
queryQzqkByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
if (res.length > 0) {
zjtzList.value = res;
}
})
.catch((err) => {
console.log(err);
});
queryJtzycyByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
// console.log(res);
if (res.length > 0) {
zyjtcyList.value = res;
}
})
.catch((err) => {
console.log(err);
});
queryXlxxByRyLdhth({
ldhth: ldhth.value
})
.then((res) => {
if (res.success) {
xlxxList.value = [];
if (res.result.records.length > 0) {
var rress = res.result.records;
// console.log(rress);
for (let i = 0; i < rress.length; i++) {
if ((rress[i].onexl == 1) & (rress[i].zgxl == 1)) {
rress[i].xllb = '第一学历';
xlxxList.value.push(JSON.parse(JSON.stringify(rress[i])));
// console.log(xlxxList.value)
rress[i].xllb = '最高学历';
xlxxList.value.push(JSON.parse(JSON.stringify(rress[i])));
// console.log(xlxxList.value)
}
if ((rress[i].onexl == 1) & (rress[i].zgxl != 1)) {
rress[i].xllb = '第一学历';
xlxxList.value.push(rress[i]);
}
if ((rress[i].onexl != 1) & (rress[i].zgxl == 1)) {
rress[i].xllb = '最高学历';
xlxxList.value.push(rress[i]);
}
}
}
}
// adjustFontSize()
})
.catch((err) => {
console.log(err);
});
}
function getJbxx() {
cellData.value.push({
title: '姓名',
value: renyuanData.value.xm,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '性别',
value: renyuanData.value.xb_dictText,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '出生年月',
value: renyuanData.value.cssj,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '民族',
value: renyuanData.value.mz,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '籍贯',
value: renyuanData.value.jg,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '出生地',
value: renyuanData.value.csd,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '工作时间',
value: renyuanData.value.cjgzsj,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle'
});
cellData.value.push({
title: '现专业',
value: '',
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle1'
});
cellData.value.push({
title: '政治面貌',
value: renyuanData.value.zzmm === '群众' ? renyuanData.value.zzmm : renyuanData.value.zzmm + renyuanData
.value.jrsj,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle1'
});
cellData.value.push({
title: '用工形式',
value: renyuanData.value.rylb1_dictText,
titleSpan: 6,
valueSpan: 6,
class: 'dataStyle1'
});
cellData1.value.push({
title: '所在单位',
value: renyuanData.value.orgCode_dictText,
titleSpan: 4,
valueSpan: 7,
class: 'dataStyle1'
});
cellData1.value.push({
title: '专业技术资格',
value: gbxxList.value.zc === 0 ? '/' : gbxxList.value.zc + gbxxList.value.zcsj,
titleSpan: 5,
valueSpan: 7,
class: 'dataStyle1'
});
cellData1.value.push({
title: '职业资格等级',
value: zyzgdjList.value.ztgz === '0' ? '/' : zyzgdjList.value.ztgz + zyzgdjList.value.ztgzdj,
titleSpan: 4,
valueSpan: 4,
class: 'dataStyle1'
});
cellData1.value.push({
title: '职务(岗位)',
value: gbxxList.value.zw,
titleSpan: 4,
valueSpan: 4,
class: 'dataStyle1'
});
cellData1.value.push({
title: '职位级别',
value: gbxxList.value.zwcj,
titleSpan: 4,
valueSpan: 4,
class: 'dataStyle'
});
}
onLoad((e) => {
console.log(store.userinfo);
renyuanData.value = JSON.parse(decodeURIComponent(e.data));
imgUrl.value = renyuanData.value.zplj;
console.log(imgUrl.value, renyuanData.value);
ldhth.value = renyuanData.value.ldhth;
getChildTable();
setTimeout(function() {
getJbxx();
}, 500);
});
</script>
<style scoped>
.renyuanInfo {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 0;
height: 98vh;
width: 900px;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
flex-direction: row;
font-size: 18px;
color: #fff;
}
.titleStyle {
font-size: 12px;
color: #747474;
line-height: 35px;
height: 35px;
background: #f2f9fc;
text-align: center;
vertical-align: middle;
border-left: 1px solid #919191;
border-bottom: 1px solid #919191;
}
/* 内容样式 */
.dataStyle {
font-size: 14px;
color: #00007f;
line-height: 25px;
height: 50px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-align: center;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* 内容样式 */
.dataStyle1 {
font-size: 14px;
color: #00007f;
line-height: 30px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* 内容样式 */
.dataStyle2 {
font-size: 12px;
color: #00007f;
line-height: 15px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-align: center;
word-wrap: break-word;
overflow-wrap: break-word;
}
.img {
height: 154px;
border: 1px solid #ccc;
/* 仅用于展示容器范围,可根据需要移除 */
display: flex;
justify-content: center;
/* 水平居中 */
align-items: center;
/* 垂直居中 */
overflow: hidden;
/* 防止图片溢出容器 */
}
</style>

View File

@ -0,0 +1,890 @@
<template>
<view class="container" id="top1">
<wd-row style="margin-bottom: 10px">
<wd-col :span="5"><uni-title :title="'选择单位'" align="left" type="h4"></uni-title></wd-col>
<wd-col :span="19">
<SelectDept label="" v-model="selectedOrgCode" @change="onOrgCodeChange" rowKey="orgCode"
:multiple="false">
</SelectDept>
</wd-col>
</wd-row>
<wd-row style="margin-bottom: 10px">
<wd-col :span="5"><uni-title :title="'选择字段'" align="left" type="h4"></uni-title></wd-col>
<wd-col :span="12">
<wd-picker :columns="fieldList" v-model="selectedField" @confirm="onFieldChange" />
</wd-col>
<wd-col style="margin-left:5px" :span="6" v-if="selectedField==='zjmc'"><button type="primary" size="mini"
@click="openZhengshu">筛选</button></wd-col>
</wd-row>
</view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
<!-- 翻页按钮 -->
<!-- <view style="display: flex; justify-content: center; margin-top: 10px">
<button @click="prevPage" :disabled="currentPage === 1" size="mini">上一页</button>
<button @click="nextPage" :disabled="currentPage * pageSize >= chartDataCount.value" size="mini">下一页</button>
</view> -->
<!-- 数据表格 -->
<wd-row style="margin-top: 10px; margin-left: 20px; margin-right: 20px" v-if="personnelList.length > 0">
<wd-col :span="2">
<view class="titleStyle">序号</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">姓名</view>
</wd-col>
<wd-col :span="5">
<view class="titleStyle">基层单位</view>
</wd-col>
<wd-col :span="5" v-if="selectedField !== 'zjmc'">
<view class="titleStyle">基层班组</view>
</wd-col>
<wd-col :span="5" v-else>
<view class="titleStyle">岗位</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">年龄</view>
</wd-col>
<wd-col :span="4">
<view class="titleStyle">操作</view>
</wd-col>
</wd-row>
<!-- <scroll-view scroll-y :style="{ height: bottomHeight + 'px' }"> -->
<scroll-view scroll-y >
<wd-row style="margin-bottom: 10px; margin-left: 20px; margin-right: 20px; font-size: 12px">
<view v-for="(item, index) in personnelList">
<wd-col :span="2">
<view class="dataStyle">
{{ index + 1 }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle">
{{ item.xm }}
</view>
</wd-col>
<wd-col :span="5">
<view class="dataStyle1">
{{ item.jcdw }}
</view>
</wd-col>
<wd-col :span="5" v-if="selectedField != 'zjmc'">
<view class="dataStyle1">
{{ item.jcxd }}
</view>
</wd-col>
<wd-col :span="4" v-else>
<view class="dataStyle">
{{ item.sdgw }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle">
{{ item.nl }}
</view>
</wd-col>
<wd-col :span="4">
<view class="dataStyle">
<span @click="detail(item)" style="color: red;">详情</span>
</view>
</wd-col>
</view>
</wd-row>
</scroll-view>
<wd-popup v-model="showPopup" position="bottom">
<wd-card>
<uni-title :title="'已选证书:'" align="left" type="h4"></uni-title>
<view style="color: red;font-weight: 300;margin-bottom: 10px;">{{dictData}}</view>
<scroll-view scroll-y style="height: 40vh;">
<cxc-szcx-dictSelect :dictCode="dictCode" @change="dictChange"></cxc-szcx-dictSelect>
</scroll-view>
</wd-card>
</wd-popup>
</template>
<script setup>
import * as echarts from 'echarts';
import {
cxcRyDatAstatistics,
cxcRyDatAstatisticsCertificate,
cxcRyDatAstatisticsDetails
} from '@/api/humanResource/personnel';
import SelectDept from '@/components/SelectDept/SelectDept'
// tableData
// const bottomHeight = ref(0);
//
const chart = ref(null);
const chartDataCount = ref(0);
const fieldList = ref([{
label: '取证情况',
value: 'zjmc',
isDict: false,
dictCode: 'gzrlzy'
},
{
label: '岗位类别',
value: 'gwlb',
isDict: false,
dictCode: ''
},
{
label: '性别',
value: 'xb',
isDict: true,
dictCode: 'sex'
},
{
label: '政治面貌',
value: 'zzmm',
isDict: true,
dictCode: 'zzmm'
},
{
label: '民族',
value: 'mz',
isDict: true,
dictCode: 'mz'
}
]); //
const dictCode = ref('');
const dictData = ref('');
const showPopup = ref(false);
const fieldisDict = ref(true);
const selectedOrgCode = ref(''); // orgCode
const selectedField = ref(''); //
const orgCodeGroupData = ref([]); //orgcode
const chartData = ref({}); //
const personnelList = ref([]); // initChart
const fieldValues = ref([]); //
const fieldTexts = ref([]); //
const chartOption = ref({});
const orgType = ref(''); // by
//
const pageSize = ref(3); //
const currentPage = ref(1); //
function detail(record) {
uni.navigateTo({
url: './detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
// onMounted(() => {
// getHeight();
// });
const dictChange = (e) => {
dictData.value = e
fetchStatisticsData()
}
// const getHeight = () => {
// //
// const systemInfo = uni.getSystemInfoSync();
// const screenHeight = systemInfo.screenHeight;
// //
// const query = uni.createSelectorQuery();
// //
// query
// .select('#top1')
// .boundingClientRect((rect1) => {
// //
// const topComponentsHeight = rect1.height;
// //
// bottomHeight.value = screenHeight - topComponentsHeight - 415;
// })
// .exec();
// };
//
const initChart = () => {
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
}, 300);
};
// const prevPage = () => {
// if (currentPage.value > 1) {
// currentPage.value--;
// }
// updateChart(chartData.value);
// };
// const nextPage = () => {
// if (currentPage.value * pageSize.value < chartDataCount.value) {
// currentPage.value++;
// //
// const startIndex = (currentPage.value - 1) * pageSize.value;
// const endIndex = startIndex + pageSize.value;
// updateChart(chartData.value);
// // const tempChartData = temp.slice(startIndex, endIndex);
// }
// };
// option
const getChartOption = (ChartData) => {
// let temp = JSON.parse(JSON.stringify(tempchartData[0].children));
// chartDataCount.value = temp.length;
//
const startIndex = (currentPage.value - 1) * pageSize.value;
const endIndex = startIndex + pageSize.value;
//
const tempChartData = ChartData.slice(startIndex, endIndex);
try {
let xData = [];
let seriesData = [];
// transformDataForEcharts
let legendData = [];
for (let i = 0; i < fieldValues.value.length; i++) {
legendData.push(fieldValues.value[i].fieldText);
}
tempChartData.forEach((item) => {
xData.push(item.name);
});
for (let i = 0; i < fieldValues.value.length; i++) {
let tempData = [];
tempChartData.forEach((item) => {
if (item.data[i]) {
tempData.push(item.data[i]);
} else {
tempData.push(0);
}
});
seriesData.push({
name: fieldValues.value[i].fieldText,
type: 'bar',
label: {
show: true, //
position: 'top' //
},
data: tempData
});
}
let tempOption = {
toolbox: {
padding: [0, 30, 0, 0],
show: true,
feature: {
//
restore: {
show: true //
},
saveAsImage: {
show: true //
}
}
},
xAxis: {
type: 'category',
data: xData,
axisLabel: {
color: '#7F84B5',
fontWeight: 300,
interval: 0,
rotate: 0
},
padding: [0, 10, 0, 10],
axisTick: {
show: false //线
},
axisLine: {
show: false //线
}
},
yAxis: [{
show: true,
boundaryGap: false, //线
type: 'value',
// name: 'Budget (million USD)',
// data: this.yList,
minInterval: 1,
axisLabel: {
interval: 0
},
splitLine: {
show: true,
lineStyle: {
//线
type: 'dashed'
}
},
axisTick: {
show: true //线
},
axisLine: {
show: false //线
}
}],
series: seriesData,
legend: {
data: legendData,
itemGap: 1,
padding: [0, 0, 0, 0],
y: 'bottom',
itemHeight: 8, //
itemWidth: 18, //
type: 'scroll'
}
};
return tempOption;
} catch (error) {
console.log(error);
}
};
//
const updateChart = (tempchartData) => {
//
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
let tempOption = getChartOption(tempchartData);
myChart.setOption(tempOption);
//
myChart.on('click', (params) => {
let ldhth = '';
//,ldhth
if (orgCodeGroupData.value.children.length > 0) {
let updateData = findRyByOrgCode(orgCodeGroupData.value.children, params.name);
ldhth = updateData.fieldValues[params.seriesIndex].ldhth;
} else {
ldhth = orgCodeGroupData.value.fieldValues[params.seriesIndex].ldhth.join(',');
}
if (ldhth && ldhth.length > 0) {
fetchPersonnelList(ldhth);
}
// updateChart(updateData);
});
}, 300);
};
function openZhengshu() {
showPopup.value = true
}
//
//
function findRyByOrgCode(treeData, targetOrgCode) {
for (const node of treeData) {
//
if (node.orgText === targetOrgCode) {
return node;
}
//
if (node.children && node.children.length > 0) {
const found = findNodeByOrgCode(node.children, targetOrgCode);
if (found) return found;
}
}
return null;
}
/**
* 从echart图标的树状数据中根据 orgCode 获取节点及其子节点数据
* @param {Array} treeData 树状数据
* @param {string} targetOrgCode 目标 orgCode
* @returns {Object|null} 匹配的节点及其子节点数据未找到返回 null
*/
function findNodeByOrgCode(treeData, targetOrgCode) {
for (const node of treeData) {
//
if (node.name === targetOrgCode) {
return node;
}
//
if (node.children && node.children.length > 0) {
const found = findNodeByOrgCode(node.children, targetOrgCode);
if (found) return found;
}
}
return null;
}
//fieldValue
function collectUniqueKeyFieldValues(tree) {
const uniqueMap = new Map();
tree.forEach((item) => {
const {
fieldValue,
fieldText
} = item;
const key = `${fieldValue}-${fieldText}`;
if (!uniqueMap.has(key)) {
uniqueMap.set(key, {
fieldValue,
fieldText
});
}
});
const result = Array.from(uniqueMap.values());
return result;
}
//
class OrgCodeMapper {
constructor(data) {
//
this.orgCodeToOrgTextMap = {};
//
for (let i = 0; i < data.length; i++) {
const item = data[i];
// orgCode orgText
this.orgCodeToOrgTextMap[item.orgCode] = item.orgText;
}
}
// orgCode orgText
findOrgText(orgCode) {
return this.orgCodeToOrgTextMap[orgCode];
}
}
//
/**
* 转换数据为支持钻取的ECharts格式
* @param {Array} data 原始数据
* @param {string} selectOrgCode 当前选择的组织编码
* @returns {Object} 包含当前层级数据和子节点信息的对象 符合echart的格式
*/
//-----------------------------------------------------------------------------------------
function transformData(selectOrgCode, data) {
const nodes = new Map();
//fieldValue data[] fieldValue
fieldValues.value = collectUniqueKeyFieldValues(data);
const orgMapper = new OrgCodeMapper(data); //orgtext
// orgCode
function getHierarchy(orgCode) {
const hierarchy = [];
for (let i = selectOrgCode.length; i <= orgCode.length; i += 3) {
hierarchy.push(orgCode.substring(0, i));
}
return hierarchy;
}
// orgCode
function getParentCode(code) {
if (code.length <= 3) return null;
return code.substring(0, code.length - 3);
}
// series
let tempArrayValue = new Array(fieldValues.value.length).fill(0);
//
data.forEach((entry) => {
const hierarchy = getHierarchy(entry.orgCode);
hierarchy.forEach((code) => {
let tempOrgText = orgMapper.findOrgText(code) === undefined ? '' : orgMapper.findOrgText(
code);
if (!nodes.has(code)) {
nodes.set(code, {
orgCode: code,
name: tempOrgText,
type: 'bar',
data: JSON.parse(JSON.stringify(tempArrayValue)), // data[0, 0]
children: []
});
}
});
// data
const node = nodes.get(entry.orgCode);
const fieldValue = entry.fieldValue;
for (let i = 0; i < fieldValues.value.length; i++) {
if (fieldValue === fieldValues.value[i].fieldValue) {
node.data[i] += entry.number;
}
}
//
for (let i = 0; i < hierarchy.length - 1; i++) {
const parentCode = hierarchy[i];
const childCode = hierarchy[i + 1];
const parentNode = nodes.get(parentCode);
const childNode = nodes.get(childCode);
if (!parentNode.children.some((c) => c.orgCode === childCode)) {
parentNode.children.push(childNode);
}
}
});
// data
function computeData(node) {
if (node.children.length === 0) return;
node.data = JSON.parse(JSON.stringify(tempArrayValue));
node.children.forEach((child) => {
computeData(child);
for (let i = 0; i < fieldValues.value.length; i++) {
node.data[i] += child.data[i];
}
});
}
//
const rootNodes = [];
nodes.forEach((node, code) => {
const parentCode = getParentCode(code);
if (!parentCode || !nodes.has(parentCode)) {
rootNodes.push(node);
}
});
// data
rootNodes.forEach((root) => computeData(root));
return rootNodes;
}
//-----------------------------------------------------------------------------------------
// orgCode children deepseek
const groupByOrgCode = (orgCode, data) => {
//
const filteredData = data.filter((item) => item.orgCode.startsWith(orgCode));
const orgMapper = new OrgCodeMapper(data);
// null
if (filteredData.length === 0) {
uni.showToast({
title: '过滤后数据为空',
duration: 1000
});
return null;
}
// fieldValue
const groupedByFieldValue = {};
try {
filteredData.forEach((item) => {
if (!groupedByFieldValue[item.fieldValue]) {
groupedByFieldValue[item.fieldValue] = {
orgCode: item.orgCode,
orgText: item.orgText,
fieldText: item.fieldText,
number: 0,
ldhth: []
};
}
groupedByFieldValue[item.fieldValue].number += item.number;
groupedByFieldValue[item.fieldValue].ldhth.push(...item.ldhth.split(','));
});
} catch (error) {
console.log(error);
}
//
const result = {
orgCode: orgCode,
orgText: orgMapper.findOrgText(orgCode),
fieldValues: Object.keys(groupedByFieldValue).map((fieldValue) => {
const {
fieldText,
orgCode,
orgText
} = groupedByFieldValue[fieldValue] || {};
return {
fieldValue: fieldValue,
fieldText: fieldText,
orgCode: orgCode,
orgText: orgText,
number: groupedByFieldValue[fieldValue].number,
ldhth: [...new Set(groupedByFieldValue[fieldValue].ldhth)] //
};
}),
children: []
};
// orgCode
const nextLevelOrgCodes = new Set();
try {
filteredData.forEach((item) => {
if (item.orgCode !== orgCode && item.orgCode.startsWith(orgCode)) {
const nextLevelOrgCode = item.orgCode.substring(0, orgCode.length + 3);
nextLevelOrgCodes.add(nextLevelOrgCode);
}
});
} catch (error) {
console.log(error);
}
//
try {
nextLevelOrgCodes.forEach((nextLevelOrgCode) => {
const child = groupByOrgCode(nextLevelOrgCode, data);
if (child) {
result.children.push(child);
}
});
} catch (error) {
console.log(error);
}
return result;
};
// then filter
const fetchStatisticsData = async () => {
if (!selectedOrgCode.value || !selectedField.value) return;
let res = [];
chartData.value = [];
try {
// A01A01A01 orgType by
var tempOrgType = '1';
if (selectedOrgCode.value.includes('A01A01A01')) {
tempOrgType = '1';
} else {
tempOrgType = orgType
}
if (selectedField.value === 'zjmc') {
res = await cxcRyDatAstatisticsCertificate({
orgCode: selectedOrgCode.value + '*',
field: selectedField.value,
dictCode: dictCode.value,
fieldisDict: fieldisDict.value,
typeOfWorkList: dictData.value,
orgType: tempOrgType.value
});
} else {
res = await cxcRyDatAstatistics({
orgCode: selectedOrgCode.value,
field: selectedField.value,
dictCode: dictCode.value,
fieldisDict: fieldisDict.value,
orgType: tempOrgType.value
});
}
if (res.success) {
if (res.result.length < 1) {
uni.showToast({
title: '查询数据为空'
});
return;
}
//
orgCodeGroupData.value = groupByOrgCode(selectedOrgCode.value, res.result);
//echart
const temp = transformData(selectedOrgCode.value, res.result);
if (temp[0].children.length > 0) {
chartData.value = JSON.parse(JSON.stringify(temp[0].children));
chartDataCount.value = chartData.value.length;
} else {
var tempArr = [];
tempArr.push(temp[0])
chartData.value = tempArr;
}
updateChart(chartData.value);
}
} catch (error) {
console.error('获取统计数据失败:', error);
}
};
// delimiter
const fetchPersonnelList = async (ldhthList) => {
personnelList.value = [];
try {
const res = await cxcRyDatAstatisticsDetails({
ldhth: ldhthList
});
if (res.success) {
personnelList.value = res.result;
}
} catch (error) {
console.error('获取人员列表失败:', error);
}
};
//
const onOrgCodeChange = (e, data) => {
personnelList.value = [];
// selectedOrgCode.value = e;
// orgType.value = data.value.orgType; // by
fetchStatisticsData();
};
const onFieldChange = (e) => {
personnelList.value = [];
try {
selectedField.value = e.value;
for (var index = 0; index < fieldList.value.length; index++) {
var element = fieldList.value[index];
if (element.value == e.value) {
dictCode.value = element.dictCode;
fieldisDict.value = element.isDict;
}
}
if (selectedField.value === 'zjmc') {
showPopup.value = true
} else {
fetchStatisticsData();
}
} catch (error) {
//TODO handle the exception
console.log(error);
}
};
const onChartClick = (e) => {
personnelList.value = [];
const {
ldhth
} = chartData.value;
if (ldhth && ldhth.length > 0) {
fetchPersonnelList(ldhth);
}
};
</script>
<style scoped>
/* 颜色变量 */
:root {
--primary-blue: #409eff;
--deep-blue: #2c7be5;
--light-blue: #ecf5ff;
--gradient-start: #6b8cff;
--gradient-end: #4364f7;
--hover-blue: #66b1ff;
}
/* 全局容器 */
.container {
margin: 10px 10px;
padding: 10px;
background: linear-gradient(145deg, #f5f9ff, var(--light-blue));
border-radius: 12px;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
border: 1px solid rgba(64, 158, 255, 0.1);
}
/* 图表容器 */
.chart-container {
height: 250px;
margin: 10px 0;
border-radius: 12px;
overflow: hidden;
background: #ffffff;
box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
border: 1px solid rgba(64, 158, 255, 0.08);
}
/* 表格标题行 */
.titleStyle {
font-size: 10px;
color: #747474;
line-height: 30px;
height: 30px;
background: #f2f9fc;
text-align: center;
vertical-align: middle;
border-left: 1px solid #919191;
border-bottom: 1px solid #919191;
}
/* 内容样式 */
.dataStyle {
font-size: 10px;
color: #00007f;
line-height: 30px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-overflow: ellipsis;
}
/* 内容样式 */
.dataStyle1 {
font-size: 8px;
color: #00007f;
line-height: 30px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-overflow: ellipsis;
}
/* 滚动区域 */
scroll-view {
background: #ffffff;
border-radius: 0 0 8px 8px;
box-shadow: 0 4px 12px rgba(0, 35, 111, 0.08);
}
/* 输入框聚焦效果 */
.trq-depart-select:focus-within {
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
border-color: var(--primary-blue);
}
/* 加载动画优化 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.container>* {
animation: fadeIn 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
/* 自定义滚动条美化 */
::-webkit-scrollbar {
width: 4px;
background: rgba(64, 158, 255, 0.05);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(45deg, var(--primary-blue), var(--deep-blue));
border-radius: 6px;
border: 1px solid white;
}
/* 筛选行间距优化 */
.filter-row {
margin: 15px 0;
padding: 10px 0;
border-radius: 8px;
}
/* 响应式调整优化 */
@media (max-width: 768px) {
.chart-container {
height: 250px;
border-radius: 10px;
}
}
/* 数据行高亮效果 */
.data-row:nth-child(even) {
background: rgba(236, 245, 255, 0.3);
}
.data-row:hover {
box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.1);
}
</style>

View File

@ -0,0 +1,30 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '人员信息',
},
}
</route>
<template>
<PageLayout navTitle="人员信息">
<wd-tabs v-model="tab" animated swipeable>
<block v-for="item,key in tabs" :key="item">
<wd-tab :title="`${item}`" :name="item">
<standingbook v-if="key == 0"></standingbook>
<ageStatistics v-if="key == 1"></ageStatistics>
<generalFieldStatistics v-if="key == 2"></generalFieldStatistics>
</wd-tab>
</block>
</wd-tabs>
</PageLayout>
</template>
<script setup>
import standingbook from './standingbook'
import ageStatistics from './ageStatistics'
import generalFieldStatistics from './generalFieldStatistics'
const tab = ref(0)
const tabs = ref(['人员台账', '年龄段统计', '通用字段统计'])
</script>

View File

@ -0,0 +1,136 @@
<template>
<PageLayout :navbarShow="false">
<wd-card style="margin: 10px;" id="top1">
<wd-row>
<wd-col :span="24"><uni-title title="所属单位" align="left" type="h5"></uni-title></wd-col>
</wd-row>
<wd-row>
<wd-col :span="24">
<SelectDept label="" v-model="orgCode" @change="getList" rowKey="orgCode" :multiple="false">
</SelectDept>
</wd-col>
</wd-row>
<wd-row>
<wd-col :span="12"><uni-title title="姓名" align="left" type="h5"></uni-title></wd-col>
<wd-col :span="12"><uni-title title="劳动合同号" align="left" type="h5"></uni-title></wd-col>
</wd-row>
<wd-row>
<wd-col :span="12">
<uni-easyinput v-model="realname" placeholder="姓名模糊查询" @change="getList" @clear="getList" />
</wd-col>
<wd-col :span="12">
<uni-easyinput v-model="contractNumber" placeholder="劳动合同号模糊查询" @change="getList"
@clear="getList" />
</wd-col>
</wd-row>
</wd-card>
<wd-table :data="list" :height="tableHeight">
<wd-table-col prop="xh" label="序号" align="center" :width="screenWidth / 5"></wd-table-col>
<wd-table-col prop="xm" label="姓名" align="center" :width="screenWidth / 5"></wd-table-col>
<wd-table-col prop="xb_dictText" label="性别" align="center" :width="screenWidth / 5"></wd-table-col>
<wd-table-col prop="nl" label="年龄" align="center" :width="screenWidth /5"></wd-table-col>
<wd-table-col prop="" label="操作" align="center" :width="screenWidth / 5">
<template #value="{row}">
<text style="color: red;" @click="detail(row)">详情</text>
</template>
</wd-table-col>
</wd-table>
<wd-pagination custom-style="border: 1px solid #ececec;border-top:none" v-model="pageNo" :total="total"
@change="handleChange"></wd-pagination>
</PageLayout>
</template>
<script setup>
import {
useMessage,
useToast
} from 'wot-design-uni'
import SelectDept from '@/components/SelectDept/SelectDept'
import {
listApi
} from '@/api/humanResource/personnel'
const message = useMessage()
const toast = useToast()
const realname = ref('') //
const contractNumber = ref('') //
const orgCode = ref('') //
const list = ref([])
let pageNo = 0
let pageSize = 10
const total = ref(0)
const screenHeight = ref(0)
const screenWidth = ref(0)
const tableHeight = ref(0)
const getList = (e) => {
if (e != 1) { //1
pageNo = 1
}
let params = {
xm: '*' + realname.value + '*',
ldhth: '*' + contractNumber.value + '*'
}
if (orgCode.value.length <= 9) {
params.orgCode = orgCode.value + '*';
} else {
params.jcxd_code = orgCode.value + '*';
}
listApi({
...params,
pageNo,
pageSize
}).then((res) => {
if (res.success) {
list.value = res.result.records.map((item, index) => ({
...item, //
xh: index + 1, // xh 1
}));
total.value = res.result.total
}
// getHeight();
})
.catch((err) => {
console.log(err);
});
}
/*切换页码*/
const handleChange = ({
value
}) => {
pageNo = value
getList(1)
}
const calculateTableHeight = () => {
const systemInfo = uni.getSystemInfoSync();
screenWidth.value = systemInfo.screenWidth;
screenHeight.value = systemInfo.screenHeight;
// +
const query = uni.createSelectorQuery();
query.select('#top1').boundingClientRect(data => {
const topHeight = data ? data.height : 0;
const navHeight = 88; //
const margin = 20; //
//
tableHeight.value = screenHeight.value - topHeight - navHeight;
}).exec();
}
function detail(record) {
uni.navigateTo({
url: './detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
onMounted(() => {
getList();
calculateTableHeight();
//
uni.onWindowResize(() => {
calculateTableHeight();
});
})
</script>
<style>
</style>

View File

@ -9,12 +9,13 @@
</route>
<template>
<PageLayout navTitle="干部值班">
<wd-datetime-picker type="year-month" v-model="dataValue" label="年月" @confirm="getList" />
<wd-table :data="dataSource">
<wd-table-col prop="date" label="日期" width="60" align="center"></wd-table-col>
<wd-table-col prop="dbld_dictText" label="带班领导" width="73" align="center"></wd-table-col>
<wd-table-col prop="zbld_dictText" label="值班领导" width="73" align="center"></wd-table-col>
<wd-table-col prop="zbgbrealname" label="值班干部" width="153" align="center"></wd-table-col>
<wd-datetime-picker type="year-month" v-model="dataValue" label="年月" @confirm="getList" id="top1" />
<wd-table :data="dataSource" :height="tableHeight">
<wd-table-col prop="date" label="日期" :width="screenWidth / 7" align="center"></wd-table-col>
<wd-table-col prop="dbld_dictText" label="带班领导" :width="screenWidth * 7 / 32" align="center"></wd-table-col>
<wd-table-col prop="zbld_dictText" label="值班领导" :width="screenWidth * 7 / 32" align="center"></wd-table-col>
<wd-table-col prop="zbgbrealname" label="值班干部" :width="screenWidth * (47 / 112)"
align="center"></wd-table-col>
</wd-table>
</PageLayout>
</template>
@ -23,8 +24,11 @@
import {
getListApi
} from '@/api/pages/duty'
const dataValue = ref(Date.now())
const dataSource = ref([])
const tableHeight = ref(0)
const screenWidth = ref(0)
const getList = () => {
const date = new Date(dataValue.value);
const year = date.getFullYear();
@ -33,11 +37,10 @@
year: year,
month: month
}).then(res => {
//
dataSource.value = res.result.records.map(item => {
return {
...item,
date: formatDate(item.date) //
date: formatDate(item.date)
}
})
})
@ -46,12 +49,33 @@
const formatDate = (dateStr) => {
if (!dateStr) return '';
const [year, month, day] = dateStr.split('-');
const formattedMonth = parseInt(month, 10); // 0
const formattedDay = parseInt(day, 10); // 0
const formattedMonth = parseInt(month, 10);
const formattedDay = parseInt(day, 10);
return `${formattedMonth}.${formattedDay}`;
};
onLoad(() => {
getList()
const calculateTableHeight = () => {
const systemInfo = uni.getSystemInfoSync();
screenWidth.value = systemInfo.screenWidth;
const screenHeight = systemInfo.screenHeight;
// +
const query = uni.createSelectorQuery();
query.select('#top1').boundingClientRect(data => {
const topHeight = data ? data.height : 0;
const navHeight = 44; //
const margin = 20; //
//
tableHeight.value = screenHeight - topHeight - navHeight - margin;
}).exec();
}
onMounted(() => {
getList();
calculateTableHeight();
//
uni.onWindowResize(() => {
calculateTableHeight();
});
})
</script>

View File

@ -30,10 +30,10 @@
"text": "首页"
},
{
"iconPath": "static/tabbar/tabbar-message-2.png",
"selectedIconPath": "static/tabbar/tabbar-message.png",
"pagePath": "pages/message/message",
"text": "消息"
"iconPath": "static/tabbar/tabbar-produce.png",
"selectedIconPath": "static/tabbar/tabbar-produce-2.png",
"pagePath": "pages/produce/index",
"text": "生产"
},
{
"iconPath": "static/tabbar/tabbar-workHome-2.png",
@ -130,6 +130,15 @@
"navigationBarTitleText": "H5在线预览"
}
},
{
"path": "pages/produce/index",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "生产数据"
}
},
{
"path": "pages/user/people",
"type": "page",
@ -147,6 +156,42 @@
"navigationBarTitleText": "工作台",
"navigationStyle": "custom"
}
},
{
"path": "pages/produce/ribaoshuju/rbsjLsxq",
"type": "page"
},
{
"path": "pages/produce/ribaoshuju/trqRbsj",
"type": "page"
},
{
"path": "pages/produce/ribaoshuju/yyRbsj",
"type": "page"
},
{
"path": "pages/produce/shishishuju/aqbjSssj",
"type": "page"
},
{
"path": "pages/produce/shishishuju/gycsSssj",
"type": "page"
},
{
"path": "pages/produce/shishishuju/index",
"type": "page"
},
{
"path": "pages/produce/shishishuju/nyxhSssj",
"type": "page"
},
{
"path": "pages/produce/shishishuju/trqSssj",
"type": "page"
},
{
"path": "pages/produce/shishishuju/ysjSssj",
"type": "page"
}
],
"subPackages": [
@ -353,6 +398,36 @@
"navigationStyle": "custom",
"navigationBarTitleText": "请假信息"
}
},
{
"path": "personnel/ageStatistics",
"type": "page"
},
{
"path": "personnel/detail",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "人员详情"
}
},
{
"path": "personnel/generalFieldStatistics",
"type": "page"
},
{
"path": "personnel/index",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "人员信息"
}
},
{
"path": "personnel/standingbook",
"type": "page"
}
]
},

195
src/pages/produce/index.vue Normal file
View File

@ -0,0 +1,195 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '生产数据',
},
}
</route>
<template>
<PageLayout :navbarShow="false">
<view>
<view class="placeholder"></view>
<view style="width: 100%; display: grid; place-items: center">
<uni-title :title="dateDate + ':生产经营情况'" type="h1" color="blue" />
</view>
<view style="margin: 0 10px;">
<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-show="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-show="current === 1">
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
<sssjForm></sssjForm>
</scroll-view>
</view>
<view v-show="current === 2">
<scroll-view scroll-y :style="{ height: scrollViewHeight + 'px' }">
选项卡2的内容
</scroll-view>
</view>
</view>
</view>
</PageLayout>
</template>
<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 {
ref,
onMounted,
computed,
nextTick,
watchEffect,
onUnmounted
} from 'vue';
const items = ref(['日报数据', '实时数据', '经营数据'])
const current = ref(0)
const res = wx.getSystemInfoSync();
const statusHeight = res.statusBarHeight; //
const cusnavbarheight = statusHeight + 44 + 'px';
const scrollViewHeight = ref(0); //
const timePercent = ref(0);
const dateDate = ref('');
function onClickItem(e) {
if (current.value != e.currentIndex) {
current.value = e.currentIndex;
}
}
const strDate = () => {
const now = new Date();
if (now.getHours() < 11) {
return formatDate(getDateAfterDays(now, -1)); //11
} else {
return formatDate(now);
}
};
onMounted(() => {
dateDate.value = strDate();
timePercent.value = getYearProgress();
calculateScrollViewHeight();
//
window.addEventListener('resize', calculateScrollViewHeight);
});
onUnmounted(() => {
//
window.removeEventListener('resize', calculateScrollViewHeight);
});
const calculateScrollViewHeight = () => {
//
const screenHeight = uni.getSystemInfoSync().windowHeight;
//
const query = uni.createSelectorQuery();
//
query
.select('.nav')
.boundingClientRect();
query
.select('.placeholder')
.boundingClientRect();
query
.select('.uni-title')
.boundingClientRect();
query
.select('.uni-segmented-control')
.boundingClientRect();
query
.select('.progress-bartime')
.boundingClientRect();
//
query.exec((res) => {
let totalHeight = 0;
res.forEach((element) => {
if (element) {
totalHeight += element.height;
}
});
// scroll-view
scrollViewHeight.value = screenHeight - totalHeight - 80;
});
};
</script>
<style lang="scss" scoped>
.nav {
width: calc(100% - 60rpx);
padding: 0 30rpx;
height: v-bind(cusnavbarheight);
font-size: 24rpx;
color: #ffffff;
position: fixed;
top: 0;
left: 0;
z-index: 99;
background-image: url('../../static/my/navbg.png');
background-repeat: no-repeat;
background-size: 750rpx 458rpx;
}
.placeholder {
height: v-bind(cusnavbarheight);
}
.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;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: red;
/* 保持红色 */
font-size: 16px;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
/* 提升可读性 */
}
</style>

View File

@ -0,0 +1,354 @@
<template>
<PageLayout :navbarShow="false">
<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>
<view v-if="type === 'trq'" 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.unit }}</view>
<view class="td">{{ item.rqDate }}</view>
<view class="td" :class="{ negative: item.rq < 0 }">
{{ item.rq }}
</view>
</view>
<!-- 空数据提示 -->
<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>
</PageLayout>
</template>
<script setup>
import {
queryJinriShengchansj,
queryJinriYuanyouShengchansj
} from '@/api/produce';
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 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);
});
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>
.table-container {
width: 100%;
height: 40vh;
overflow: hidden;
}
.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: 40px;
text-align: center;
color: #888;
font-size: 16px;
}
.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,742 @@
<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" background-color="#fff">
<view class="popup-content">
<view class="popup-header">
<text class="title">{{ selectedGas }}数据详情 单位(万立方米)</text>
<uni-icons type="closeempty" size="24" color="#666" @click="closePopup"></uni-icons>
</view>
<view class="table">
<!-- 表头 -->
<view class="tr header">
<view class="th1">序号</view>
<view class="th">名称</view>
<view class="th">日气量</view>
<view class="th">年累计</view>
<view class="th1">操作</view>
</view>
<scroll-view scroll-X="true" scroll-Y="true" class="table-container">
<!-- 表格内容 -->
<view class="tr" v-for="(item, index) in filteredData" :key="index"
:class="{ even: index % 2 === 0 }">
<view class="td1">{{ index + 1 }}</view>
<view class="td">{{ item.unit }}</view>
<view class="td" :class="{ negative: item.rq < 0 }">
{{ formatNumber(item.rq) }}
</view>
<view class="td" :class="{ negative: item.yearVolume < 0 }">
{{ formatNumber(item.yearVolume) }}
</view>
<view class="td1" style="color: red" @click="goHistory(item)">历史</view>
</view>
<!-- 空数据提示 -->
<view v-if="!filteredData.length" class="empty">暂无相关数据</view>
</scroll-view>
</view>
</view>
</wd-popup>
</template>
<script setup>
import {
queryJinriShengchansj,
queryYearShengchansj,
queryJinriTrqShengchansj
} from '@/api/produce';
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
const shishiArr = ref([]);
const shishiArrSelect = ref([]);
const shishiArrDisplay = ref(['气井气', '商品量', '站线综合输差']);
const dataJinriUnit = ref([]);
const selectedGas = 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 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 = 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) => {
selectedGas.value = gas;
filteredData.value = dataJinriSumUnit.value.filter((item) => item.gas === gas);
popup.value = true;
};
//
const closePopup = () => {
popup.value = false;
};
onMounted(() => {
getJinriTrqShengchansj();
getJinriShengchansj();
});
//
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) {
console.log(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;
// console.log(composite);
}
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;
// // console.log(queryParms);
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);
}
});
console.log(2, temp);
console.log(calcZhsc(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
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;
// 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.setId === itemYear.setId) {
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;
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;
}
.popup-content {
padding: 30rpx;
max-height: 40vh;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #eee;
.title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.table-container {
width: 100%;
height: 30vh;
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;
}
}
.empty {
padding: 40rpx;
text-align: center;
color: #888;
font-size: 16rpx;
}
.card-item {
flex: 1 1 200rpx; // 300rpx selectedGas 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-item {
margin-bottom: 20px;
}
.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,413 @@
<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" background-color="#fff">
<view class="popup-content">
<view class="popup-header">
<text class="title">原油数据详情 单位()</text>
<wd-icon name="closeempty" size="24" @click="closePopup"></wd-icon>
</view>
<scroll-view scroll-X="true" scroll-Y="true" class="table-container">
<view class="table">
<!-- 表头 -->
<view class="tr header">
<view class="th1">序号</view>
<view class="th">名称</view>
<view class="th">日油量</view>
<view class="th">月累计</view>
<view class="th">年累计</view>
<view class="th1">操作</view>
</view>
<!-- 表格内容 -->
<view class="tr" v-for="(item, index) in dataJinri" :key="index" :class="{ even: index % 2 === 0 }">
<view class="td1">{{ index }}</view>
<view class="td">{{ item.dw }}</view>
<view class="td">
{{ formatNumber(item.rcwy) }}
</view>
<view class="td">{{ formatNumber(item.yl) }}</view>
<view class="td">
{{ formatNumber(item.nl) }}
</view>
<view class="td1" style="color: red" @click="goHistory(item)">历史</view>
</view>
<!-- 空数据提示 -->
<view v-if="!dataJinri.length" class="empty">暂无相关数据</view>
</view>
</scroll-view>
</view>
</wd-popup>
</template>
<script setup>
import {
queryJinriYuanyouShengchansj
} from '@/api/produce';
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
const shishiArr = ref([{
gas: '原油产量',
rcwy: '',
yl: '',
nl: '',
yearPlan: '1500',
yearPerCent: ''
}]);
const dataJinri = ref([]);
const dataJinriSum = ref([]);
const popup = ref(false);
//
const handleCardClick = () => {
popup.value = true
};
//
const closePopup = () => {
popup.value = false
};
onMounted(() => {
getJinriYuanyouShengchansj();
// getYearShengchansj();
});
const strDate = ref('');
//
const formatNumber = (num) => {
if (typeof num !== 'number') return '-';
return num.toFixed(4).replace(/\.?0+$/, '');
};
function goHistory(val) {
uni.navigateTo({
url: '/pages/views/shengchan/ribaoshuju/rbsjLsxq?data=' + JSON.stringify(val) + '&type=yy'
});
}
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;
// // console.log(queryParms);
queryJinriYuanyouShengchansj(queryParms).then((res) => {
if (res.success) {
// // console.log(res);
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);
});
});
// 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;
}
.popup-content {
padding: 30rpx;
max-height: 70vh;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #eee;
.title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.table-container {
width: 100%;
height: 30vh;
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: 20rpx;
font-weight: 500;
color: #333;
text-align: center;
white-space: nowrap;
}
.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;
}
}
.empty {
padding: 40rpx;
text-align: center;
color: #888;
font-size: 16rpx;
}
.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-item {
margin-bottom: 20px;
}
.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,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,20 @@
<template>
<trqSssjVue></trqSssjVue>
</template>
<script setup>
import {
queryJinriShengchansj,
queryYearShengchansj,
queryJinriTrqShengchansj
} from '@/api/produce';
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
import trqSssjVue from './trqSssj';
</script>
<style scoped>
</style>

View File

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

View File

@ -0,0 +1,290 @@
<template>
<PageLayout :navbarShow="false">
<!-- 标题行 -->
<view class="header-row">
<view class="title">天然气实时数据</view>
<view style="min-width: 200px;">
<cxc-szcx-stationJl-select v-model="stationID" returnCodeOrID="id" @change="onChange">
</cxc-szcx-stationJl-select>
</view>
</view>
<button size="mini" @click="getData">连接WebSocket</button>
<view class="container">
<view v-for="(item, index) in jlData" :key="index" class="card">
<view class="field-item">
<text class="titlejl">{{ stationName }}--{{ 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-label">瞬时流量(/d)</text>
<text class="field-value">{{ formatNumber(item.ssll) || '-' }}</text>
</view>
<!-- 今日流量 -->
<view class="field-item">
<text class="field-label">今日流量()</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>
</PageLayout>
</template>
<script setup>
import {
queryJldZcList,
queryJldDataByZc
} from '@/api/produce'
import {
formatDate,
getDateAfterDays
} from '@/utils/dateTime';
const stationList = ref([])
//
const popupSelect = ref(null);
const stationID = ref("")
const stationName = ref("")
const jlData = ref([])
const sssjUrl = ref('wss://36.112.48.190/Gyk/websocket/')
const jlByzc = ref('https://36.112.48.190/Gyk/sssj/GetJlByZc')
//websocket线
// websocket
function connectSocketInit() {
console.log(11, )
let userID = '1412198011559055361'
// this.socketTasksocket
uni.connectSocket({
// ,使ws://127.0.0.1:9099
// url: 'wss://'+this.$api.sellerWebsocket+'/'+this.userinfo.id,
url: sssjUrl.value + userID,
success(data) {
console.log(data);
console.log('websocket连接成功');
}
});
// ,
uni.onSocketOpen(function(res) {
console.log('WebSocket连接已打开');
});
uni.onSocketMessage(function(res) {
console.log('收到服务器内容:' + res.data);
// start
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
innerAudioContext.src = 'https://wzs1.oss-cn-beijing.aliyuncs.com/music.mp3';
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onError(res => {
console.log(res.errMsg);
console.log(res.errCode);
});
// end
});
// socket
uni.onSocketClose(function(res) {
console.log('WebSocket 已关闭!');
});
}
function getData() {
connectSocketInit()
}
function onChange(e, data) {
console.log(2, e, data.value);
stationID.value = e
stationName.value = data.value.title
uni.request({
url: jlByzc.value + '?zhanc=' + stationID.value + '&jldLx=0',
method: 'GET',
success: (res) => {
jlData.value = JSON.parse(res.data.result).JlData;
}
})
}
const websock = ref(null);
const timer2 = ref(null);
//
const websocketheart = () => {
timer2.value = setInterval(() => {
if (websock.value && websock.value.readyState === 1) {
//
connectSocketInit()
}
}, 1000);
};
onMounted(() => {
queryJldZcList({
pId: '1267633406031953921'
}).then((res) => {
if (res.success) {
stationList.value = res.result
console.log(1, stationList.value)
}
})
websocketheart()
})
//
const formatNumber = (num) => {
let temp = 0;
try {
temp = parseFloat(num);
} catch (error) {
//TODO handle the exception
}
return temp.toFixed(4).replace(/\.?0+$/, '');
};
</script>
<style 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;
}
.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: 8px;
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;
}
</style>

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -13,8 +13,18 @@ interface NavigateToOptions {
"/pages/onlinePreview/detail" |
"/pages/onlinePreview/onlinePreview" |
"/pages/onlinePreview/onlinePreviewH5" |
"/pages/produce/index" |
"/pages/user/people" |
"/pages/workHome/index" |
"/pages/produce/ribaoshuju/rbsjLsxq" |
"/pages/produce/ribaoshuju/trqRbsj" |
"/pages/produce/ribaoshuju/yyRbsj" |
"/pages/produce/shishishuju/aqbjSssj" |
"/pages/produce/shishishuju/gycsSssj" |
"/pages/produce/shishishuju/index" |
"/pages/produce/shishishuju/nyxhSssj" |
"/pages/produce/shishishuju/trqSssj" |
"/pages/produce/shishishuju/ysjSssj" |
"/pages-home/home/home" |
"/pages-message/chat/chat" |
"/pages-message/contacts/contacts" |
@ -34,6 +44,11 @@ interface NavigateToOptions {
"/pages-humanResource/absence/add" |
"/pages-humanResource/absence/detail" |
"/pages-humanResource/absence/index" |
"/pages-humanResource/personnel/ageStatistics" |
"/pages-humanResource/personnel/detail" |
"/pages-humanResource/personnel/generalFieldStatistics" |
"/pages-humanResource/personnel/index" |
"/pages-humanResource/personnel/standingbook" |
"/pages-integrated/duty/index" |
"/pages-politics/health/add" |
"/pages-process/approvalTabbar" |
@ -42,7 +57,7 @@ interface NavigateToOptions {
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
url: "/pages/index/index" | "/pages/message/message" | "/pages/workHome/index" | "/pages/user/people"
url: "/pages/index/index" | "/pages/produce/index" | "/pages/workHome/index" | "/pages/user/people"
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;

View File

@ -0,0 +1,134 @@
<template>
<view class="compact-datetime-picker">
<!-- 输入框区域 -->
<view class="input-container">
<view class="compact-input" @click="openPicker">
{{ dateRange[0] || '开始日期' }}
</view>
<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="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="60" :rangeEndRepick="true"
:startDate="startDate" :endDate="endDate" :isSearch="isSearch"></wu-calendar>
</view>
</template>
<script>
import {
formatDate,
getDateAfterDays,
getDateAfterMonths
} from '@/utils/dateTime';
export default {
props: {
modelValue: {
type: Array,
default: () => [null, null]
},
//
mode: {
type: String,
default: 'single'
},
//
startDate: {
type: String,
default: ''
},
//
endDate: {
type: String,
default: ''
},
isSearch: {
type: Boolean,
default: true
}
},
data() {
return {
dateRange: null
};
},
watch: {
modelValue: {
immediate: true,
handler(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') {
if (newVal[1] == 'NaN-NaN-NaN') newVal[1] = newVal[0]
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);
}
}
};
</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;
min-width: 90px;
max-width: 120px;
white-space: nowrap;
}
.compact-input.active {
border-color: #409eff;
background-color: #f5f7ff;
}
</style>

View File

@ -0,0 +1,83 @@
{
"id": "cxc-szcx-dateRangeSelect",
"displayName": "cxc-szcx-dateRangeSelect",
"version": "1.0.0",
"description": "cxc-szcx-dateRangeSelect",
"keywords": [
"cxc-szcx-dateRangeSelect"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,168 @@
### 紧凑型日期时间选择器组件说明
#### 一、组件功能
该组件是基于 UniApp 开发的紧凑型日期选择器,支持以下功能:
1. **三种选择模式**
- 单选single
- 范围选择range
- 多选multiple
2. **弹出式日历选择**
- 支持月份滑动切换
- 显示农历日期
- 支持同天范围选择
3. **数据双向绑定**
- 通过 `modelValue` 实现父子组件数据同步
4. **自定义样式**
- 紧凑型输入框设计
- 支持自定义主题颜色
- 响应式布局适配
5. **支持自定义日期禁用范围**
- 限制用户选择的日期范围。只能选择在 startDate 和 endDate 之间的日期,超出该范围的日期将被禁用,无法选择。
- startDate设置可选日期的起始日期用户不能选择早于该日期的日期。
- endDate设置可选日期的结束日期用户不能选择晚于该日期的日期。
#### 二、组件Props
| 参数名 | 类型 | 默认值 | 说明 |
|--------------|------------|----------|-------------------------------- |
| `modelValue` | Array/Date | `[null]` | 双向绑定的值(根据模式变化) |
| `mode` | String | `single` | 选择模式single/range/multiple |
| `startDate` | String | ` ` | 设置可选日期的起始日期 |
| `endDate` | String | ` ` | 设置可选日期的结束日期 |
| `isSearch` | Boolean | `true` | 设置快速选择日期样式 |
#### 三、组件数据
| 名称 | 类型 | 说明 |
|------------|--------|--------------------------|
| `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="singleDate"
mode="single"
/>
<!-- 范围选择模式 -->
<compact-datetime-picker
v-model="rangeDate"
mode="range"
/>
<!-- 多选模式 -->
<compact-datetime-picker
v-model="multipleDate"
mode="multiple"
/>
</view>
</template>
<script>
export default {
data() {
return {
singleDate: null,
rangeDate: [null, null],
multipleDate: []
};
}
};
</script>
```
#### 八、注意事项
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. 增加国际化支持
3. 添加动画过渡效果
4. 支持预设常用日期范围
如果需要进一步定制化,可以修改以下部分:
1. 日历组件配置(`wu-calendar` 参数)
2. 日期格式化函数(`formatDate`
3. 输入框样式和交互逻辑
4. 异常处理机制

View File

@ -0,0 +1,69 @@
<template>
<view>
<uni-data-checkbox v-model="selectedValuesArray" :localdata="dictItems" :multiple="true" data-key="value"
data-value="label"></uni-data-checkbox>
</view>
</template>
<script setup>
import {
ref,
onMounted,
watch
} from 'vue';
import {
getDictItemsApi
} from '@/api/system';
// props emits
const props = defineProps({
dictCode: {
type: String,
required: true
},
modelValue: {
type: String,
default: ''
}
});
const emits = defineEmits(['update:modelValue', 'change']);
//
const dictItems = ref([]);
const selectedValuesArray = ref(props.modelValue ? props.modelValue.split(',') : []);
//
const loadDictItems = async () => {
try {
const response = await getDictItemsApi(props.dictCode);
dictItems.value = response.result; // { data: [...] }
} catch (error) {
console.error('加载字典数据失败:', error);
}
};
//
watch(selectedValuesArray, (newValue) => {
const selectedValuesString = newValue.join(',');
emits('update:modelValue', selectedValuesString);
emits('change', selectedValuesString);
}, {
deep: true
});
// props.modelValue
watch(() => props.modelValue, (newValue) => {
selectedValuesArray.value = newValue ? newValue.split(',') : [];
}, {
immediate: true
});
onMounted(() => {
loadDictItems();
});
</script>
<style scoped>
/* 可以添加组件的样式 */
</style>

View File

@ -0,0 +1,83 @@
{
"id": "cxc-szcx-dictSelect",
"displayName": "cxc-szcx-dictSelect",
"version": "1.0.0",
"description": "cxc-szcx-dictSelect",
"keywords": [
"cxc-szcx-dictSelect"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +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` 内部元素发生变化时也会触发监听回调,可能会影响性能,需注意。

View File

@ -0,0 +1,295 @@
<template>
<view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
</view>
</template>
<script setup>
import * as echarts from 'echarts';
import { formatDate, getDateAfterDays, getDateAfterMonths } from '@/utils/dateTime';
const props = defineProps({
dataList: {
type: Array,
default: () => [],
required: true
},
xField: {
type: String,
default: 'rqDate'
},
yField: {
type: String,
default: 'rq'
},
legendField: {
type: String,
default: 'unit'
},
referenceValue: {
type: Number,
default: 0
},
legendArrStr:{ // by
type: String,
default: '',
}
});
const chart = ref(null);
const chartOption = ref({});
const chartTitle = ref('');
//
const processSeriesData = () => {
const legendItems = [...new Set(props.dataList.map((item) => item[props.legendField]?.trim() || '未命名'))];
let legendArr = []
if(props.legendArrStr){
legendArr = props.legendArrStr.split(",")
}
//
return legendItems.map((legend,index) => {
const items = props.dataList
.filter((item) => item[props.legendField] === legend)
.sort((a, b) => new Date(a[props.xField]) - new Date(b[props.xField]))
.map((item) => ({
name: item[props.xField],
value: [
formatDate(item[props.xField]), // X
parseFloat(item[props.yField]) || 0 // Y
]
}));
if(legendArr.length>0){
return {
name: legendArr[index],
type: 'line',
showSymbol: true,
smooth: true,
data: items
};
}
return {
name: legend,
type: 'line',
showSymbol: true,
smooth: true,
data: items
};
});
};
//
watch(
() => props.dataList,
() => {
chartTitle.value = '历史趋势';
updateChart();
},
{ deep: true }
);
//
onMounted(() => {});
onUnmounted(() => {
if (chart.value) {
chart.value.dispose();
chart.value = null;
}
});
// formatDate
const updateChart = () => {
if (!chart.value) return;
chartOption.value = generateOptions();
chart.value.setOption(chartOption.value, true);
};
//
const generateOptions = () => ({
title: {
text: chartTitle.value,
padding: [0, 0, 0, 30]
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.96)',
borderColor: '#eee',
borderWidth: 1,
textStyle: {
color: '#333',
fontSize: 14
},
axisPointer: {
type: 'line',
lineStyle: {
color: '#FF6B6B',
width: 1,
type: 'dashed'
}
},
confine: true // tooltip
},
backgroundColor: 'rgba(255, 255, 255, 0.8)',
textStyle: {
color: '#666',
fontSize: 12
},
xAxis: {
type: 'time',
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisLabel: {
color: '#666',
formatter: '{MM}-{dd}',
rotate: 30
},
splitLine: {
show: true,
lineStyle: {
color: '#f5f5f5'
}
}
},
yAxis: {
type: 'value',
axisLine: {
show: true,
lineStyle: {
color: '#ddd'
}
},
splitLine: {
lineStyle: {
color: '#f5f5f5'
}
},
axisLabel: {
color: '#666',
formatter: (value) => `${value.toFixed(2)}`
}
},
series: [
...processSeriesData().map((series) => ({
...series,
lineStyle: {
width: 2,
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 6,
shadowOffsetY: 3
},
itemStyle: {
color: '#00007f', //
borderColor: '#fff',
borderWidth: 1
}
})),
{
type: 'line',
markLine: {
silent: true,
lineStyle: {
type: 'dashed',
color: '#FF4757',
width: 2
},
data: [{ yAxis: props.referenceValue }],
label: {
show: true,
position: 'end',
formatter: `参考值: ${props.referenceValue}`,
fontSize: 12
}
},
data: [] // 线
}
],
legend: {
type: 'scroll',
bottom: 5,
textStyle: {
color: '#666',
fontSize: 12
},
pageIconColor: '#FF6B6B',
pageTextStyle: {
color: '#999'
}
},
grid: {
top: 30,
right: 30,
bottom: 40,
left: 20,
containLabel: true
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
minValueSpan: 3600 * 24 * 1000 * 7 // 7
}
]
});
//
const initChart = () => {
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
}, 300);
};
</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;
}
/* 图表主体样式 */
.chart-wrapper {
width: 100%;
height: 100%;
transition: opacity 0.3s ease;
}
/* 移动端优化 */
@media screen and (max-width: 768px) {
.chart-container {
height: 30vh;
min-height: 200px;
padding: 10rpx;
border-radius: 12rpx;
}
}
</style>

View File

@ -0,0 +1,83 @@
{
"id": "cxc-szcx-lineChart",
"displayName": "cxc-szcx-lineChart",
"version": "1.0.0",
"description": "cxc-szcx-lineChart",
"keywords": [
"cxc-szcx-lineChart"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,179 @@
# cxc-szcx-lineChart
# # `line-chart.vue` 组件说明书
## 一、组件概述
`line-chart.vue` 是一个基于 ECharts 实现的折线图组件,用于在 UniApp 项目中展示数据的折线图。该组件接收一系列数据和配置参数,支持动态更新数据,并展示参考线。
## 二、组件依赖
- **Vue 3**:使用 Vue 3 的组合式 API 进行开发。
- **ECharts**:用于绘制折线图。
- **`lime-echart`**UniApp 插件,提供 ECharts 的渲染支持。
## 三、组件使用方法
### 1. 引入组件
在需要使用该组件的页面中引入 `line-chart.vue` 组件。
```vue
<template>
<view>
<line-chart
:dataList="dataList"
:xField="xField"
:yField="yField"
:legendField="legendField"
:referenceValue="referenceValue"
/>
</view>
</template>
<script setup>
import LineChart from '@/components/line-chart.vue';
const dataList = [
{ date: '2023-01-01', value: 10, category: 'A' },
// 更多数据...
];
const xField = 'date';
const yField = 'value';
const legendField = 'category';
const referenceValue = 15;
</script>
```
### 2. 组件属性
| 属性名 | 类型 | 默认值 | 描述 |
| ---- | ---- | ---- | ---- |
| `dataList` | `Object` | `[]` | 包含图表数据的数组,每个元素是一个对象,包含 `xField`、`yField` 和 `legendField` 对应的数据。 |
| `xField` | `String` | `''` | 数据中用于表示 x 轴的字段名。 |
| `yField` | `String` | `''` | 数据中用于表示 y 轴的字段名。 |
| `legendField` | `String` | `''` | 数据中用于表示图例的字段名。 |
| `referenceValue` | `Number` | `10` | 图表中参考线的数值。 |
## 四、组件内部实现
### 1. 模板部分
```vue
<template>
<view class="chart-container">
<l-echart ref="chart" @init="initChart"></l-echart>
</view>
</template>
```
- 使用 `l-echart` 组件渲染图表,通过 `ref` 绑定到 `chart`,并在初始化完成时调用 `initChart` 方法。
### 2. 脚本部分
#### 2.1 引入必要的模块
```javascript
import { ref, watch } from 'vue';
```
引入 Vue 3 的 `ref``watch` 函数。
#### 2.2 定义组件属性
```javascript
const props = defineProps({
dataList: {
type: Object,
default: () => []
},
// 其他属性...
});
```
定义组件接收的属性。
#### 2.3 初始化图表
```javascript
const chart = ref(null);
let echarts = null;
const initChart = async (canvas) => {
await initEcharts(canvas);
updateChart();
};
const initEcharts = async (canvas) => {
echarts = await import('echarts');
const { init } = await import('@/uni_modules/lime-echart/static/echarts.min');
echarts = init(canvas, echarts);
};
```
- `chart` 用于引用 `l-echart` 组件。
- `initChart` 方法在图表初始化时调用,先初始化 ECharts 实例,再更新图表。
- `initEcharts` 方法异步加载 ECharts 并初始化实例。
#### 2.4 处理数据
```javascript
const processData = () => {
const legendData = [...new Set(props.dataList.map((item) => item[props.legendField]))];
return legendData.map((legendItem) => {
const seriesData = props.dataList
.filter((item) => item[props.legendField] === legendItem)
.sort((a, b) => new Date(a[props.xField]) - new Date(b[props.xField]))
.map((item) => ({
name: item[props.xField],
value: [item[props.xField], item[props.yField]]
}));
return {
name: legendItem,
type: 'line',
showSymbol: true,
data: seriesData
};
});
};
```
处理传入的数据,根据 `legendField` 分组,对每组数据按 `xField` 排序,并转换为 ECharts 所需的格式。
#### 2.5 获取图表配置
```javascript
const getOption = () => ({
tooltip: {
trigger: 'axis',
formatter: (params) => {
return `${params[0].axisValue}<br/>` + params.map((item) => `${item.marker} ${item.seriesName}: ${item.value[1]}`).join('<br/>');
}
},
// 其他配置...
});
```
返回 ECharts 的配置对象,包括 tooltip、x 轴、y 轴、系列数据、图例和网格等配置。
#### 2.6 更新图表
```javascript
const updateChart = () => {
if (!chart.value) return;
const option = getOption();
chart.value.setOption(option);
};
```
如果 `chart` 实例存在,获取最新的图表配置并更新图表。
#### 2.7 监听数据变化
```javascript
watch(
() => props.dataList,
() => {
updateChart();
},
{ deep: true }
);
```
`props.dataList` 发生变化时,调用 `updateChart` 方法更新图表。
### 3. 样式部分
```css
.chart-container {
width: 100%;
height: 30vh;
}
```
设置图表容器的宽度为 100%,高度为 30vh。
## 五、注意事项
- 确保项目中已经正确安装并配置了 `lime-echart` 插件。
- 传入的 `dataList` 数据格式要符合要求,包含 `xField`、`yField` 和 `legendField` 对应的数据。
- 由于使用了异步加载 ECharts可能会有一定的延迟需要确保在合适的时机初始化图表。

View File

@ -0,0 +1,90 @@
<template>
<view v-if="returnCodeOrID === 'orgCode'">
<uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" :ellipsis="true" placeholder="请选择单位"
popup-title="请选择单位" :localdata="stationList" @nodeclick="onnodeclick" @popupclosed="onpopupclosed"
:map="{'text':'title','value':'orgCode'}" class="no-wrap-picker"></uni-data-picker>
</view>
<view v-else>
<uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" :ellipsis="true" placeholder="请选择单位"
popup-title="请选择单位" :localdata="stationList" @nodeclick="onnodeclick" @popupclosed="onpopupclosed"
:map="{'text':'title','value':'id'}" class="no-wrap-picker"></uni-data-picker>
</view>
</template>
<script setup>
import {
queryJldZcList
} from '@/api/produce'
import {
queryMyDeptTreeListApi
} from '@/api/system/department'
const props = defineProps({
returnCodeOrID: {
type: String,
default: "orgCode"
}
})
let stationList = ref([])
let selectDepartID = ref("") //ID
let selectDepartIDS = ref([]) //ID
let tempSelectDepartID = ref("") //ID
let depart = ref(null);
let departInfo = ref({}) //""
let $emit = defineEmits(['change']);
watch(
tempSelectDepartID,
(newVal, oldVal) => { //change by
$emit('change', newVal, departInfo)
}, {
immediate: true,
deep: true
}
)
const getDepartList = () => {
queryJldZcList({
pId: '1267633406031953921'
}).then((res) => {
if (res.success) {
stationList.value = res.result
}
})
}
const onnodeclick = ((e) => {
departInfo.value = e;
if (props.returnCodeOrID == "orgCode") {
tempSelectDepartID.value = e.orgCode
} else {
tempSelectDepartID.value = e.value
}
// if(!depart.value.isOpened){// change by
// $emit('change', selectDepartID.value, departInfo)
// }
})
const onchange = ((e) => {
selectDepartID.value = e.detail.value[e.detail.value.length - 1].value
// $emit('change', e.detail.value[e.detail.value.length - 1].value, {})
})
const onpopupclosed = ((e) => {
selectDepartID.value = tempSelectDepartID.value
// $emit('change', selectDepartID.value, departInfo)
})
onLoad((e) => {
getDepartList();
})
</script>
<style scoped>
/* 选择器根据实际情况调整 */
.no-wrap-picker::v-deep .uni-data-picker-item {
white-space: nowrap;
/* 强制不换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 超出部分显示省略号 */
}
</style>

View File

@ -0,0 +1,83 @@
{
"id": "cxc-szcx-stationJl-select",
"displayName": "cxc-szcx-stationJl-select",
"version": "1.0.0",
"description": "cxc-szcx-stationJl-select",
"keywords": [
"cxc-szcx-stationJl-select"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1 @@
# cxc-szcx-stationJl-select

View File

@ -0,0 +1,51 @@
## 1.0.62024-10-22
- 新增 当 multiple 为 false 且传递的 value 为 数组时,使用数组第一项用作反显
## 1.0.52024-03-20
- 修复 单选模式下选中样式不生效的bug
## 1.0.42024-01-27
- 修复 修复错别字chagne为change
## 1.0.32022-09-16
- 可以使用 uni-scss 控制主题色
## 1.0.22022-06-30
- 优化 在 uni-forms 中的依赖注入方式
## 1.0.12022-02-07
- 修复 multiple 为 true 时v-model 的值为 null 报错的 bug
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
## 0.2.52021-08-23
- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题
## 0.2.42021-08-17
- 修复 单选 list 模式下 icon 为 left 时,选中图标不显示的问题
## 0.2.32021-08-11
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.2.22021-07-30
- 优化 在uni-forms组件与label不对齐的问题
## 0.2.12021-07-27
- 修复 单选默认值为0不能选中的Bug
## 0.2.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.1.112021-07-06
- 优化 删除无用日志
## 0.1.102021-07-05
- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题
## 0.1.92021-07-05
- 修复 nvue 黑框样式问题
## 0.1.82021-06-28
- 修复 selectedTextColor 属性不生效的Bug
## 0.1.72021-06-02
- 新增 map 属性可以方便映射text/value属性
## 0.1.62021-05-26
- 修复 不关联服务空间的情况下组件报错的Bug
## 0.1.52021-05-12
- 新增 组件示例地址
## 0.1.42021-04-09
- 修复 nvue 下无法选中的问题
## 0.1.32021-03-22
- 新增 disabled属性
## 0.1.22021-02-24
- 优化 默认颜色显示
## 0.1.12021-02-24
- 新增 支持nvue
## 0.1.02021-02-18
- “暂无数据”显示居中

View File

@ -0,0 +1,316 @@
const events = {
load: 'load',
error: 'error'
}
const pageMode = {
add: 'add',
replace: 'replace'
}
const attrs = [
'pageCurrent',
'pageSize',
'collection',
'action',
'field',
'getcount',
'orderby',
'where'
]
export default {
data() {
return {
loading: false,
listData: this.getone ? {} : [],
paginationInternal: {
current: this.pageCurrent,
size: this.pageSize,
count: 0
},
errorMessage: ''
}
},
created() {
let db = null;
let dbCmd = null;
if(this.collection){
this.db = uniCloud.database();
this.dbCmd = this.db.command;
}
this._isEnded = false
this.$watch(() => {
let al = []
attrs.forEach(key => {
al.push(this[key])
})
return al
}, (newValue, oldValue) => {
this.paginationInternal.pageSize = this.pageSize
let needReset = false
for (let i = 2; i < newValue.length; i++) {
if (newValue[i] != oldValue[i]) {
needReset = true
break
}
}
if (needReset) {
this.clear()
this.reset()
}
if (newValue[0] != oldValue[0]) {
this.paginationInternal.current = this.pageCurrent
}
this._execLoadData()
})
// #ifdef H5
if (process.env.NODE_ENV === 'development') {
this._debugDataList = []
if (!window.unidev) {
window.unidev = {
clientDB: {
data: []
}
}
}
unidev.clientDB.data.push(this._debugDataList)
}
// #endif
// #ifdef MP-TOUTIAO
let changeName
let events = this.$scope.dataset.eventOpts
for (let i = 0; i < events.length; i++) {
let event = events[i]
if (event[0].includes('^load')) {
changeName = event[1][0][0]
}
}
if (changeName) {
let parent = this.$parent
let maxDepth = 16
this._changeDataFunction = null
while (parent && maxDepth > 0) {
let fun = parent[changeName]
if (fun && typeof fun === 'function') {
this._changeDataFunction = fun
maxDepth = 0
break
}
parent = parent.$parent
maxDepth--;
}
}
// #endif
// if (!this.manual) {
// this.loadData()
// }
},
// #ifdef H5
beforeDestroy() {
if (process.env.NODE_ENV === 'development' && window.unidev) {
let cd = this._debugDataList
let dl = unidev.clientDB.data
for (let i = dl.length - 1; i >= 0; i--) {
if (dl[i] === cd) {
dl.splice(i, 1)
break
}
}
}
},
// #endif
methods: {
loadData(args1, args2) {
let callback = null
if (typeof args1 === 'object') {
if (args1.clear) {
this.clear()
this.reset()
}
if (args1.current !== undefined) {
this.paginationInternal.current = args1.current
}
if (typeof args2 === 'function') {
callback = args2
}
} else if (typeof args1 === 'function') {
callback = args1
}
this._execLoadData(callback)
},
loadMore() {
if (this._isEnded) {
return
}
this._execLoadData()
},
refresh() {
this.clear()
this._execLoadData()
},
clear() {
this._isEnded = false
this.listData = []
},
reset() {
this.paginationInternal.current = 1
},
remove(id, {
action,
callback,
confirmTitle,
confirmContent
} = {}) {
if (!id || !id.length) {
return
}
uni.showModal({
title: confirmTitle || '提示',
content: confirmContent || '是否删除该数据',
showCancel: true,
success: (res) => {
if (!res.confirm) {
return
}
this._execRemove(id, action, callback)
}
})
},
_execLoadData(callback) {
if (this.loading) {
return
}
this.loading = true
this.errorMessage = ''
this._getExec().then((res) => {
this.loading = false
const {
data,
count
} = res.result
this._isEnded = data.length < this.pageSize
callback && callback(data, this._isEnded)
this._dispatchEvent(events.load, data)
if (this.getone) {
this.listData = data.length ? data[0] : undefined
} else if (this.pageData === pageMode.add) {
this.listData.push(...data)
if (this.listData.length) {
this.paginationInternal.current++
}
} else if (this.pageData === pageMode.replace) {
this.listData = data
this.paginationInternal.count = count
}
// #ifdef H5
if (process.env.NODE_ENV === 'development') {
this._debugDataList.length = 0
this._debugDataList.push(...JSON.parse(JSON.stringify(this.listData)))
}
// #endif
}).catch((err) => {
this.loading = false
this.errorMessage = err
callback && callback()
this.$emit(events.error, err)
})
},
_getExec() {
let exec = this.db
if (this.action) {
exec = exec.action(this.action)
}
exec = exec.collection(this.collection)
if (!(!this.where || !Object.keys(this.where).length)) {
exec = exec.where(this.where)
}
if (this.field) {
exec = exec.field(this.field)
}
if (this.orderby) {
exec = exec.orderBy(this.orderby)
}
const {
current,
size
} = this.paginationInternal
exec = exec.skip(size * (current - 1)).limit(size).get({
getCount: this.getcount
})
return exec
},
_execRemove(id, action, callback) {
if (!this.collection || !id) {
return
}
const ids = Array.isArray(id) ? id : [id]
if (!ids.length) {
return
}
uni.showLoading({
mask: true
})
let exec = this.db
if (action) {
exec = exec.action(action)
}
exec.collection(this.collection).where({
_id: dbCmd.in(ids)
}).remove().then((res) => {
callback && callback(res.result)
if (this.pageData === pageMode.replace) {
this.refresh()
} else {
this.removeData(ids)
}
}).catch((err) => {
uni.showModal({
content: err.message,
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
removeData(ids) {
let il = ids.slice(0)
let dl = this.listData
for (let i = dl.length - 1; i >= 0; i--) {
let index = il.indexOf(dl[i]._id)
if (index >= 0) {
dl.splice(i, 1)
il.splice(index, 1)
}
}
},
_dispatchEvent(type, data) {
if (this._changeDataFunction) {
this._changeDataFunction(data, this._isEnded)
} else {
this.$emit(type, data, this._isEnded)
}
}
}
}

View File

@ -0,0 +1,853 @@
<template>
<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
<template v-if="!isLocal">
<view class="uni-data-loading">
<uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18"
:content-text="contentText"></uni-load-more>
<text v-else>{{mixinDatacomErrorMessage}}</text>
</view>
</template>
<template v-else>
<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}"
@change="change">
<label class="checklist-box"
:class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''"
:checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')"
class="checkbox__inner" :style="item.styleIcon">
<view class="checkbox__inner-icon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
</view>
</label>
</checkbox-group>
<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="change">
<label class="checklist-box"
:class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''"
:checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner"
:style="item.styleBackgroud">
<view class="radio__inner-icon" :style="item.styleIcon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
</view>
</label>
</radio-group>
</template>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染 checkbox radio
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} mode = [default| list | button | tag] 显示模式
* @value default 默认横排模式
* @value list 列表模式
* @value button 按钮模式
* @value tag 标签模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {Array|String|Number} value 默认值
* @property {Array} localdata 本地数据 格式 [{text:'',value:''}]
* @property {Number|String} min 最小选择个数 multiple为true时生效
* @property {Number|String} max 最大选择个数 multiple为true时生效
* @property {Boolean} wrap 是否换行显示
* @property {String} icon = [left|right] list 列表模式下icon显示位置
* @property {Boolean} selectedColor 选中颜色
* @property {Boolean} emptyText 没有数据时显示的文字 本地数据无效
* @property {Boolean} selectedTextColor 选中文本颜色如不填写则自动显示
* @property {Object} map 字段映射 默认 map={text:'text',value:'value'}
* @value left 左侧显示
* @value right 右侧显示
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'uniDataChecklist',
mixins: [uniCloud.mixinDatacom || {}],
emits: ['input', 'update:modelValue', 'change'],
props: {
mode: {
type: String,
default: 'default'
},
multiple: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return ''
}
},
// TODO vue3
modelValue: {
type: [Array, String, Number],
default () {
return '';
}
},
localdata: {
type: Array,
default () {
return []
}
},
min: {
type: [Number, String],
default: ''
},
max: {
type: [Number, String],
default: ''
},
wrap: {
type: Boolean,
default: false
},
icon: {
type: String,
default: 'left'
},
selectedColor: {
type: String,
default: ''
},
selectedTextColor: {
type: String,
default: ''
},
emptyText: {
type: String,
default: '暂无数据'
},
disabled: {
type: Boolean,
default: false
},
map: {
type: Object,
default () {
return {
text: 'text',
value: 'value'
}
}
}
},
watch: {
localdata: {
handler(newVal) {
this.range = newVal
this.dataList = this.getDataList(this.getSelectedValue(newVal))
},
deep: true
},
mixinDatacomResData(newVal) {
this.range = newVal
this.dataList = this.getDataList(this.getSelectedValue(newVal))
},
value(newVal) {
this.dataList = this.getDataList(newVal)
// fix by mehaotian is_reset uni-forms
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
},
modelValue(newVal) {
this.dataList = this.getDataList(newVal);
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
}
},
data() {
return {
dataList: [],
range: [],
contentText: {
contentdown: '查看更多',
contentrefresh: '加载中',
contentnomore: '没有更多'
},
isLocal: true,
styles: {
selectedColor: '#2979ff',
selectedTextColor: '#666',
},
isTop: 0
};
},
computed: {
dataValue() {
if (this.value === '') return this.modelValue
if (this.modelValue === '') return this.value
return this.value
}
},
created() {
// this.form = this.getForm('uniForms')
// this.formItem = this.getForm('uniFormsItem')
// this.formItem && this.formItem.setValue(this.value)
// if (this.formItem) {
// this.isTop = 6
// if (this.formItem.name) {
// // name,formData
// if(!this.is_reset){
// this.is_reset = false
// this.formItem.setValue(this.dataValue)
// }
// this.rename = this.formItem.name
// this.form.inputChildrens.push(this)
// }
// }
if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true
this.range = this.localdata
this.dataList = this.getDataList(this.getSelectedValue(this.range))
} else {
if (this.collection) {
this.isLocal = false
this.loadData()
}
}
},
methods: {
loadData() {
this.mixinDatacomGet().then(res => {
this.mixinDatacomResData = res.result.data
if (this.mixinDatacomResData.length === 0) {
this.isLocal = false
this.mixinDatacomErrorMessage = this.emptyText
} else {
this.isLocal = true
}
}).catch(err => {
this.mixinDatacomErrorMessage = err.message
})
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
change(e) {
const values = e.detail.value
let detail = {
value: [],
data: []
}
if (this.multiple) {
this.range.forEach(item => {
if (values.includes(item[this.map.value] + '')) {
detail.value.push(item[this.map.value])
detail.data.push(item)
}
})
} else {
const range = this.range.find(item => (item[this.map.value] + '') === values)
if (range) {
detail = {
value: range[this.map.value],
data: range
}
}
}
// this.formItem && this.formItem.setValue(detail.value)
// TODO vue2
this.$emit('input', detail.value);
// // TOTO vue3
this.$emit('update:modelValue', detail.value);
this.$emit('change', {
detail
})
if (this.multiple) {
// v-model
// if (this.value.length === 0) {
this.dataList = this.getDataList(detail.value, true)
// }
} else {
this.dataList = this.getDataList(detail.value)
}
},
/**
* 获取渲染的新数组
* @param {Object} value 选中内容
*/
getDataList(value) {
//
let dataList = JSON.parse(JSON.stringify(this.range))
let list = []
if (this.multiple) {
if (!Array.isArray(value)) {
value = []
}
} else {
if (Array.isArray(value) && value.length) {
value = value[0]
}
}
dataList.forEach((item, index) => {
item.disabled = item.disable || item.disabled || false
if (this.multiple) {
if (value.length > 0) {
let have = value.find(val => val === item[this.map.value])
item.selected = have !== undefined
} else {
item.selected = false
}
} else {
item.selected = value === item[this.map.value]
}
list.push(item)
})
return this.setRange(list)
},
/**
* 处理最大最小值
* @param {Object} list
*/
setRange(list) {
let selectList = list.filter(item => item.selected)
let min = Number(this.min) || 0
let max = Number(this.max) || ''
list.forEach((item, index) => {
if (this.multiple) {
if (selectList.length <= min) {
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
if (have !== undefined) {
item.disabled = true
}
}
if (selectList.length >= max && max !== '') {
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
if (have === undefined) {
item.disabled = true
}
}
}
this.setStyles(item, index)
list[index] = item
})
return list
},
/**
* 设置 class
* @param {Object} item
* @param {Object} index
*/
setStyles(item, index) {
//
item.styleBackgroud = this.setStyleBackgroud(item)
item.styleIcon = this.setStyleIcon(item)
item.styleIconText = this.setStyleIconText(item)
item.styleRightIcon = this.setStyleRightIcon(item)
},
/**
* 获取选中值
* @param {Object} range
*/
getSelectedValue(range) {
if (!this.multiple) return this.dataValue
let selectedArr = []
range.forEach((item) => {
if (item.selected) {
selectedArr.push(item[this.map.value])
}
})
return this.dataValue.length > 0 ? this.dataValue : selectedArr
},
/**
* 设置背景样式
*/
setStyleBackgroud(item) {
let styles = {}
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
if (this.selectedColor) {
if (this.mode !== 'list') {
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
}
if (this.mode === 'tag') {
styles['background-color'] = item.selected ? selectedColor : '#f5f5f5'
}
}
let classles = ''
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIcon(item) {
let styles = {}
let classles = ''
if (this.selectedColor) {
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
styles['background-color'] = item.selected ? selectedColor : '#fff'
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
if (!item.selected && item.disabled) {
styles['background-color'] = '#F2F6FC'
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
}
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIconText(item) {
let styles = {}
let classles = ''
if (this.selectedColor) {
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
if (this.mode === 'tag') {
styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : '#fff') : '#666'
} else {
styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : selectedColor) : '#666'
}
if (!item.selected && item.disabled) {
styles.color = '#999'
}
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleRightIcon(item) {
let styles = {}
let classles = ''
if (this.mode === 'list') {
styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6'
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
}
}
}
</script>
<style lang="scss">
$uni-primary: #2979ff !default;
$border-color: #DCDFE6;
$disable: 0.4;
@mixin flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
}
.uni-data-loading {
@include flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 36px;
padding-left: 10px;
color: #999;
}
.uni-data-checklist {
position: relative;
z-index: 0;
flex: 1;
//
.checklist-group {
@include flex;
flex-direction: row;
flex-wrap: wrap;
&.is-list {
flex-direction: column;
}
.checklist-box {
@include flex;
flex-direction: row;
align-items: center;
position: relative;
margin: 5px 0;
margin-right: 25px;
.hidden {
position: absolute;
opacity: 0;
}
//
.checklist-content {
@include flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
.checklist-text {
font-size: 14px;
color: #666;
margin-left: 5px;
line-height: 14px;
}
.checkobx__list {
border-right-width: 1px;
border-right-color: #007aff;
border-right-style: solid;
border-bottom-width: 1px;
border-bottom-color: #007aff;
border-bottom-style: solid;
height: 12px;
width: 6px;
left: -5px;
transform-origin: center;
transform: rotate(45deg);
opacity: 0;
}
}
//
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 4px;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 2px;
/* #endif */
/* #ifndef APP-NVUE */
top: 1px;
/* #endif */
left: 5px;
height: 8px;
width: 4px;
border-right-width: 1px;
border-right-color: #fff;
border-right-style: solid;
border-bottom-width: 1px;
border-bottom-color: #fff;
border-bottom-style: solid;
opacity: 0;
transform-origin: center;
transform: rotate(40deg);
}
}
//
.radio__inner {
@include flex;
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
justify-content: center;
align-items: center;
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 16px;
background-color: #fff;
z-index: 1;
.radio__inner-icon {
width: 8px;
height: 8px;
border-radius: 10px;
opacity: 0;
}
}
//
&.is--default {
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
}
.checklist-text {
color: #999;
}
}
//
&.is-checked {
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $uni-primary;
.radio__inner-icon {
opacity: 1;
background-color: $uni-primary;
}
}
.checklist-text {
color: $uni-primary;
}
//
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
.radio__inner {
opacity: $disable;
}
}
}
}
//
&.is--button {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
transition: border-color 0.2s;
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
border: 1px #eee solid;
opacity: $disable;
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
border-color: $uni-primary;
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $uni-primary;
.radio__inner-icon {
opacity: 1;
background-color: $uni-primary;
}
}
.checklist-text {
color: $uni-primary;
}
//
&.is-disable {
opacity: $disable;
}
}
}
//
&.is--tag {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
background-color: #f5f5f5;
.checklist-text {
margin: 0;
color: #666;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
opacity: $disable;
}
&.is-checked {
background-color: $uni-primary;
border-color: $uni-primary;
.checklist-text {
color: #fff;
}
}
}
//
&.is--list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding: 10px 15px;
padding-left: 0;
margin: 0;
&.is-list-border {
border-top: 1px #eee solid;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
.checkbox__inner {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $uni-primary;
.radio__inner-icon {
opacity: 1;
background-color: $uni-primary;
}
}
.checklist-text {
color: $uni-primary;
}
.checklist-content {
.checkobx__list {
opacity: 1;
border-color: $uni-primary;
}
}
//
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,87 @@
{
"id": "uni-data-checkbox",
"displayName": "uni-data-checkbox 数据选择器",
"version": "1.0.6",
"description": "通过数据驱动的单选框和复选框",
"keywords": [
"uni-ui",
"checkbox",
"单选",
"多选",
"单选多选"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.1"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-load-more","uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-harmony": "u",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,18 @@
## DataCheckbox 数据驱动的单选复选框
> **组件名uni-data-checkbox**
> 代码块: `uDataCheckbox`
本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括
1. 数据绑定型组件给本组件绑定一个data会自动渲染一组候选内容。再以往开发者需要编写不少代码实现类似功能
2. 自动的表单校验组件绑定了data且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验
3. 本组件合并了单选多选
4. 本组件有若干风格选择如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件样式代码虽然不用自己写了却会牺牲一定的样式自定义性
在uniCloud开发中`DB Schema`中配置了enum枚举等类型后在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,79 @@
## 2.0.22025-04-14
- 修复 在readonly属性为true时选项匹配错误的问题
## 2.0.02023-12-14
- 新增 支持 uni-app-x
## 1.1.22023-04-11
- 修复 更改 modelValue 报错的 bug
- 修复 v-for 未使用 key 值控制台 warning
## 1.1.12023-02-21
- 修复代码合并时引发 value 属性为空时不渲染数据的问题
## 1.1.02023-02-15
- 修复 localdata 不支持动态更新的bug
## 1.0.92023-02-15
- 修复 localdata 不支持动态更新的bug
## 1.0.82022-09-16
- 可以使用 uni-scss 控制主题色
## 1.0.72022-07-06
- 优化 pc端图标位置不正确的问题
## 1.0.62022-07-05
- 优化 显示样式
## 1.0.52022-07-04
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
## 1.0.42022-04-19
- 修复 字节小程序 本地数据无法选择下一级的Bug
## 1.0.32022-02-25
- 修复 nvue 不支持的 v-show 的 bug
## 1.0.22022-02-25
- 修复 条件编译 nvue 不支持的 css 样式
## 1.0.12021-11-23
- 修复 由上个版本引发的map、v-model等属性不生效的bug
## 1.0.02021-11-19
- 优化 组件 UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
## 0.4.92021-10-28
- 修复 VUE2 v-model 概率无效的 bug
## 0.4.82021-10-27
- 修复 v-model 概率无效的 bug
## 0.4.72021-10-25
- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
## 0.4.62021-10-19
- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
## 0.4.52021-09-26
- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
- 修复 readonly 为 true 时报错的 bug
## 0.4.42021-09-26
- 修复 上一版本造成的 map 属性失效的 bug
- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
## 0.4.32021-09-24
- 修复 某些情况下级联未触发的 bug
## 0.4.22021-09-23
- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
- 新增 选项内容过长自动添加省略号
## 0.4.12021-09-15
- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
## 0.4.02021-07-13
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.3.52021-06-04
- 修复 无法加载云端数据的问题
## 0.3.42021-05-28
- 修复 v-model 无效问题
- 修复 loaddata 为空数据组时加载时间过长问题
- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
## 0.3.32021-05-12
- 新增 组件示例地址
## 0.3.22021-04-22
- 修复 非树形数据有 where 属性查询报错的问题
## 0.3.12021-04-15
- 修复 本地数据概率无法回显时问题
## 0.3.02021-04-07
- 新增 支持云端非树形表结构数据
- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
## 0.2.02021-03-15
- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
## 0.1.92021-03-09
- 修复 微信小程序某些情况下无法选择的问题
## 0.1.82021-02-05
- 优化 部分样式在 nvue 上的兼容表现
## 0.1.72021-02-05
- 调整为 uni_modules 目录规范

View File

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
//
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

View File

@ -0,0 +1,381 @@
<template>
<view class="uni-data-tree">
<view class="uni-data-tree-input" @click="handleInput">
<slot :data="selectedPaths" :error="error">
<view class="input-value" :class="{'input-value-border': border}">
<text v-if="error!=null" class="error-text">{{error!.errMsg}}</text>
<scroll-view v-if="selectedPaths.length" class="selected-path" scroll-x="true">
<view class="selected-list">
<template v-for="(item, index) in selectedPaths">
<text class="text-color">{{item[mappingTextName]}}</text>
<text v-if="index<selectedPaths.length-1" class="input-split-line">{{split}}</text>
</template>
</view>
</scroll-view>
<text v-else-if="error==null&&!loading" class="placeholder">{{placeholder}}</text>
<view v-if="!readonly" class="arrow-area">
<view class="input-arrow"></view>
</view>
</view>
</slot>
<view v-if="loading && !isOpened" class="selected-loading">
<slot name="picker-loading" :loading="loading"></slot>
</view>
</view>
<view class="uni-data-tree-cover" v-show="isOpened" @click="handleClose"></view>
<view class="uni-data-tree-dialog" v-show="isOpened">
<view class="uni-popper__arrow"></view>
<view class="dialog-caption">
<view class="dialog-title-view">
<text class="dialog-title">{{popupTitle}}</text>
</view>
<view class="dialog-close" @click="handleClose">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</view>
<view ref="pickerView" class="uni-data-pickerview">
<view v-if="error!=null" class="error">
<text class="error-text">{{error!.errMsg}}</text>
</view>
<scroll-view v-if="!isCloudDataList" :scroll-x="true">
<view class="selected-node-list">
<template v-for="(item, index) in selectedNodes">
<text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
@click="onTabSelect(index)">
{{item[mappingTextName]}}
</text>
</template>
</view>
</scroll-view>
<list-view class="list-view" :scroll-y="true">
<list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
<text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
<text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
</list-item>
</list-view>
<view class="loading-cover" v-if="loading">
<slot name="pickerview-loading" :loading="loading"></slot>
</view>
</view>
</view>
</view>
</template>
<script>
import { dataPicker } from "../uni-data-pickerview/uni-data-picker.uts"
/**
* DataPicker 级联选择
* @description 支持单列、和多列级联选择。列数没有限制如果屏幕显示不全顶部tab区域会左右滚动。
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {String} popup-title 弹出窗口标题
* @property {Array} localdata 本地数据,参考
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} readonly = [true|false] 是否仅读
* @property {Boolean} preload = [true|false] 是否预加载数据
* @value true 开启预加载数据,点击弹出窗口后显示已加载数据
* @value false 关闭预加载数据,点击弹出窗口后开始加载数据
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询,仅查询当前选中节点
* @value false 关闭分布查询,一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
*/
export default {
name: 'UniDataPicker',
emits: ['popupopened', 'popupclosed', 'nodeclick', 'change', 'input', 'update:modelValue', 'inputclick'],
mixins: [dataPicker],
props: {
popupTitle: {
type: String,
default: '请选择'
},
placeholder: {
type: String,
default: '请选择'
},
heightMobile: {
type: String,
default: ''
},
readonly: {
type: Boolean,
default: false
},
clearIcon: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
split: {
type: String,
default: '/'
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {
isOpened: false
}
},
computed: {
isShowClearIcon() : boolean {
if (this.readonly) {
return false
}
if (this.clearIcon && this.selectedPaths.length > 0) {
return true
}
return false
}
},
created() {
this.load()
},
methods: {
clear() {
},
load() {
if (this.isLocalData) {
this.loadLocalData()
} else if (this.isCloudDataList || this.isCloudDataTree) {
this.loadCloudDataPath()
}
},
show() {
this.isOpened = true
this.$emit('popupopened')
if (!this.hasCloudTreeData) {
this.loadData()
}
},
hide() {
this.isOpened = false
this.$emit('popupclosed')
},
handleInput() {
if (this.readonly) {
this.$emit('inputclick')
} else {
this.show()
}
},
handleClose() {
this.hide()
},
onFinish() {
this.selectedPaths = this.getChangeNodes()
this.$emit('update:modelValue', this.selectedPaths)
this.$emit('change', this.selectedPaths)
this.hide()
}
}
}
</script>
<style>
@import url("../uni-data-pickerview/uni-data-pickerview.css");
.uni-data-tree {
position: relative;
}
.uni-data-tree-input {
position: relative;
}
.selected-loading {
display: flex;
justify-content: center;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.error-text {
flex: 1;
font-size: 12px;
color: #DD524D;
}
.input-value {
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
padding: 5px 5px;
padding-right: 5px;
overflow: hidden;
min-height: 28px;
}
.input-value-border {
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.selected-path {
flex: 1;
flex-direction: row;
overflow: hidden;
}
.load-more {
width: 40px;
}
.selected-list {
flex-direction: row;
flex-wrap: nowrap;
}
.selected-item {
flex-direction: row;
flex-wrap: nowrap;
}
.text-color {
font-size: 14px;
color: #333;
}
.placeholder {
color: grey;
font-size: 14px;
}
.input-split-line {
opacity: .5;
margin-left: 1px;
margin-right: 1px;
}
.arrow-area {
position: relative;
padding: 0 12px;
margin-left: auto;
justify-content: center;
transform: rotate(-45deg);
transform-origin: center;
}
.input-arrow {
width: 8px;
height: 8px;
border-left: 2px solid #999;
border-bottom: 2px solid #999;
}
.uni-data-tree-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .4);
flex-direction: column;
z-index: 100;
}
.uni-data-tree-dialog {
position: fixed;
left: 0;
top: 20%;
right: 0;
bottom: 0;
background-color: #FFFFFF;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
flex-direction: column;
z-index: 102;
overflow: hidden;
}
.dialog-caption {
position: relative;
flex-direction: row;
}
.dialog-title-view {
flex: 1;
}
.dialog-title {
align-self: center;
padding: 0 10px;
line-height: 44px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
flex-direction: row;
align-items: center;
padding: 0 15px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #666;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.uni-data-pickerview {
flex: 1;
}
.icon-clear {
display: flex;
align-items: center;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-data-tree-cover {
background-color: transparent;
}
.uni-data-tree-dialog {
position: absolute;
top: 55px;
height: auto;
min-height: 400px;
max-height: 50vh;
background-color: #fff;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: unset;
}
.dialog-caption {
display: none;
}
}
/* #endif */
</style>

View File

@ -0,0 +1,545 @@
<template>
<view class="uni-data-tree">
<view class="uni-data-tree-input" @click="handleInput">
<slot :options="options" :data="inputSelected" :error="errorMessage">
<view class="input-value" :class="{'input-value-border': border}">
<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
<view v-else-if="loading && !isOpened" class="selected-area">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true">
<view class="selected-list">
<view class="selected-item" v-for="(item,index) in inputSelected" :key="index">
<text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1"
class="input-split-line">{{split}}</text>
</view>
</view>
</scroll-view>
<text v-else class="selected-area placeholder">{{placeholder}}</text>
<view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear">
<uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons>
</view>
<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
<view class="input-arrow"></view>
</view>
</view>
</slot>
</view>
<view class="uni-data-tree-cover" v-show="isOpened" @click="handleClose"></view>
<view class="uni-data-tree-dialog" v-show="isOpened">
<view class="uni-popper__arrow"></view>
<view class="dialog-caption">
<view class="title-area">
<text class="dialog-title">{{popupTitle}}</text>
</view>
<view class="dialog-close" @click="handleClose">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</view>
<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata"
:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where"
:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" :map="map"
:ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick">
</data-picker-view>
</view>
</view>
</template>
<script>
import dataPicker from "../uni-data-pickerview/uni-data-picker.js"
import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue"
/**
* DataPicker 级联选择
* @description 支持单列和多列级联选择列数没有限制如果屏幕显示不全顶部tab区域会左右滚动
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {String} popup-title 弹出窗口标题
* @property {Array} localdata 本地数据参考
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} readonly = [true|false] 是否仅读
* @property {Boolean} preload = [true|false] 是否预加载数据
* @value true 开启预加载数据点击弹出窗口后显示已加载数据
* @value false 关闭预加载数据点击弹出窗口后开始加载数据
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
*/
export default {
name: 'UniDataPicker',
emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue','inputclick'],
mixins: [dataPicker],
components: {
DataPickerView
},
props: {
options: {
type: [Object, Array],
default () {
return {}
}
},
popupTitle: {
type: String,
default: '请选择'
},
placeholder: {
type: String,
default: '请选择'
},
heightMobile: {
type: String,
default: ''
},
readonly: {
type: Boolean,
default: false
},
clearIcon: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
split: {
type: String,
default: '/'
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {
isOpened: false,
inputSelected: []
}
},
created() {
this.$nextTick(() => {
this.load();
})
},
watch: {
localdata: {
handler() {
this.load()
},
deep: true
},
},
methods: {
clear() {
this._dispatchEvent([]);
},
onPropsChange() {
this._treeData = [];
this.selectedIndex = 0;
this.load();
},
load() {
if (this.readonly) {
this._processReadonly(this.localdata, this.dataValue);
return;
}
//
if (this.isLocalData) {
this.loadData();
this.inputSelected = this.selected.slice(0);
} else if (this.isCloudDataList || this.isCloudDataTree) { // Cloud
this.loading = true;
this.getCloudDataValue().then((res) => {
this.loading = false;
this.inputSelected = res;
}).catch((err) => {
this.loading = false;
this.errorMessage = err;
})
}
},
show() {
this.isOpened = true
setTimeout(() => {
this.$refs.pickerView.updateData({
treeData: this._treeData,
selected: this.selected,
selectedIndex: this.selectedIndex
})
}, 200)
this.$emit('popupopened')
},
hide() {
this.isOpened = false
this.$emit('popupclosed')
},
handleInput() {
if (this.readonly) {
this.$emit('inputclick')
return
}
this.show()
},
handleClose(e) {
this.hide()
},
onnodeclick(e) {
this.$emit('nodeclick', e)
},
ondatachange(e) {
this._treeData = this.$refs.pickerView._treeData
},
onchange(e) {
this.hide()
this.$nextTick(() => {
this.inputSelected = e;
})
this._dispatchEvent(e)
},
_processReadonly(dataList, value) {
var isTree = dataList.findIndex((item) => {
return item.children
})
if (isTree > -1) {
let inputValue
if (Array.isArray(value)) {
inputValue = value[value.length - 1]
if (typeof inputValue === 'object' && inputValue.value) {
inputValue = inputValue.value
}
} else {
inputValue = value
}
this.inputSelected = this._findNodePath(inputValue, this.localdata)
return
}
if (!this.hasValue) {
this.inputSelected = []
return
}
let result = []
for (let i = 0; i < value.length; i++) {
var val = value[i]
var item = dataList.find((v) => {
return v.value == val
})
if (item) {
result.push(item)
}
}
if (result.length) {
this.inputSelected = result
}
},
_filterForArray(data, valueArray) {
var result = []
for (let i = 0; i < valueArray.length; i++) {
var value = valueArray[i]
var found = data.find((item) => {
return item.value == value
})
if (found) {
result.push(found)
}
}
return result
},
_dispatchEvent(selected) {
let item = {}
if (selected.length) {
var value = new Array(selected.length)
for (var i = 0; i < selected.length; i++) {
value[i] = selected[i].value
}
item = selected[selected.length - 1]
} else {
item.value = ''
}
if (this.formItem) {
this.formItem.setValue(item.value)
}
this.$emit('input', item.value)
this.$emit('update:modelValue', item.value)
this.$emit('change', {
detail: {
value: selected
}
})
}
}
}
</script>
<style>
.uni-data-tree {
flex: 1;
position: relative;
font-size: 14px;
}
.error-text {
color: #DD524D;
}
.input-value {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
font-size: 14px;
/* line-height: 35px; */
padding: 0 10px;
padding-right: 5px;
overflow: hidden;
height: 35px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.input-value-border {
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.selected-area {
flex: 1;
overflow: hidden;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.load-more {
/* #ifndef APP-NVUE */
margin-right: auto;
/* #endif */
/* #ifdef APP-NVUE */
width: 40px;
/* #endif */
}
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: nowrap;
/* padding: 0 5px; */
}
.selected-item {
flex-direction: row;
/* padding: 0 1px; */
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.text-color {
color: #333;
}
.placeholder {
color: grey;
font-size: 12px;
}
.input-split-line {
opacity: .5;
}
.arrow-area {
position: relative;
width: 20px;
/* #ifndef APP-NVUE */
margin-bottom: 5px;
margin-left: auto;
display: flex;
/* #endif */
justify-content: center;
transform: rotate(-45deg);
transform-origin: center;
}
.input-arrow {
width: 7px;
height: 7px;
border-left: 1px solid #999;
border-bottom: 1px solid #999;
}
.uni-data-tree-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .4);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 100;
}
.uni-data-tree-dialog {
position: fixed;
left: 0;
right: 0;
bottom: 5%;
background-color: #FFFFFF;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 102;
overflow: hidden;
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
}
.dialog-caption {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
/* border-bottom: 1px solid #f0f0f0; */
}
.title-area {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
padding: 0 10px;
}
.dialog-title {
/* font-weight: bold; */
line-height: 44px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 0 15px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #666;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.picker-view {
flex: 1;
overflow: hidden;
}
.icon-clear {
display: flex;
align-items: center;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-data-tree-cover {
background-color: transparent;
}
.uni-data-tree-dialog {
position: absolute;
top: 55px;
height: auto;
min-height: 400px;
max-height: 50vh;
background-color: #fff;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: unset;
}
.dialog-caption {
display: none;
}
.icon-clear {
/* margin-right: 5px; */
}
}
/* #endif */
/* picker 弹出层通用的指示小三角, todo扩展至上下左右方向定位 */
/* #ifndef APP-NVUE */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
/* #endif */
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,622 @@
export default {
props: {
localdata: {
type: [Array, Object],
default () {
return []
}
},
spaceInfo: {
type: Object,
default () {
return {}
}
},
collection: {
type: String,
default: ''
},
action: {
type: String,
default: ''
},
field: {
type: String,
default: ''
},
orderby: {
type: String,
default: ''
},
where: {
type: [String, Object],
default: ''
},
pageData: {
type: String,
default: 'add'
},
pageCurrent: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 500
},
getcount: {
type: [Boolean, String],
default: false
},
getone: {
type: [Boolean, String],
default: false
},
gettree: {
type: [Boolean, String],
default: false
},
manual: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return []
}
},
modelValue: {
type: [Array, String, Number],
default () {
return []
}
},
preload: {
type: Boolean,
default: false
},
stepSearh: {
type: Boolean,
default: true
},
selfField: {
type: String,
default: ''
},
parentField: {
type: String,
default: ''
},
multiple: {
type: Boolean,
default: false
},
map: {
type: Object,
default () {
return {
text: "text",
value: "value"
}
}
}
},
data() {
return {
loading: false,
errorMessage: '',
loadMore: {
contentdown: '',
contentrefresh: '',
contentnomore: ''
},
dataList: [],
selected: [],
selectedIndex: 0,
page: {
current: this.pageCurrent,
size: this.pageSize,
count: 0
}
}
},
computed: {
isLocalData() {
return !this.collection.length;
},
isCloudData() {
return this.collection.length > 0;
},
isCloudDataList() {
return (this.isCloudData && (!this.parentField && !this.selfField));
},
isCloudDataTree() {
return (this.isCloudData && this.parentField && this.selfField);
},
dataValue() {
let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null ||
this.modelValue !== undefined);
return isModelValue ? this.modelValue : this.value;
},
hasValue() {
if (typeof this.dataValue === 'number') {
return true
}
return (this.dataValue != null) && (this.dataValue.length > 0)
}
},
created() {
this.$watch(() => {
var al = [];
['pageCurrent',
'pageSize',
'spaceInfo',
'value',
'modelValue',
'localdata',
'collection',
'action',
'field',
'orderby',
'where',
'getont',
'getcount',
'gettree'
].forEach(key => {
al.push(this[key])
});
return al
}, (newValue, oldValue) => {
let needReset = false
for (let i = 2; i < newValue.length; i++) {
if (newValue[i] != oldValue[i]) {
needReset = true
break
}
}
if (newValue[0] != oldValue[0]) {
this.page.current = this.pageCurrent
}
this.page.size = this.pageSize
this.onPropsChange()
})
this._treeData = []
},
methods: {
onPropsChange() {
this._treeData = [];
},
// pickview
async loadData() {
if (this.isLocalData) {
this.loadLocalData();
} else if (this.isCloudDataList) {
this.loadCloudDataList();
} else if (this.isCloudDataTree) {
this.loadCloudDataTree();
}
},
//
async loadLocalData() {
this._treeData = [];
this._extractTree(this.localdata, this._treeData);
let inputValue = this.dataValue;
if (inputValue === undefined) {
return;
}
if (Array.isArray(inputValue)) {
inputValue = inputValue[inputValue.length - 1];
if (typeof inputValue === 'object' && inputValue[this.map.value]) {
inputValue = inputValue[this.map.value];
}
}
this.selected = this._findNodePath(inputValue, this.localdata);
},
// Cloud ()
async loadCloudDataList() {
if (this.loading) {
return;
}
this.loading = true;
try {
let response = await this.getCommand();
let responseData = response.result.data;
this._treeData = responseData;
this._updateBindData();
this._updateSelected();
this.onDataChange();
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// Cloud ()
async loadCloudDataTree() {
if (this.loading) {
return;
}
this.loading = true;
try {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataTreeWhere()
};
if (this.gettree) {
commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`;
}
let response = await this.getCommand(commandOptions);
let responseData = response.result.data;
this._treeData = responseData;
this._updateBindData();
this._updateSelected();
this.onDataChange();
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// Cloud ()
async loadCloudDataNode(callback) {
if (this.loading) {
return;
}
this.loading = true;
try {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataNodeWhere()
};
let response = await this.getCommand(commandOptions);
let responseData = response.result.data;
callback(responseData);
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// Cloud
getCloudDataValue() {
if (this.isCloudDataList) {
return this.getCloudDataListValue();
}
if (this.isCloudDataTree) {
return this.getCloudDataTreeValue();
}
},
// Cloud ()
getCloudDataListValue() {
// field's as value where
let where = [];
let whereField = this._getForeignKeyByField();
if (whereField) {
where.push(`${whereField} == '${this.dataValue}'`)
}
where = where.join(' || ');
if (this.where) {
where = `(${this.where}) && (${where})`
}
return this.getCommand({
field: this._cloudDataPostField(),
where
}).then((res) => {
this.selected = res.result.data;
return res.result.data;
});
},
// Cloud ()
getCloudDataTreeValue() {
return this.getCommand({
field: this._cloudDataPostField(),
getTreePath: {
startWith: `${this.selfField}=='${this.dataValue}'`
}
}).then((res) => {
let treePath = [];
this._extractTreePath(res.result.data, treePath);
this.selected = treePath;
return treePath;
});
},
getCommand(options = {}) {
/* eslint-disable no-undef */
let db = uniCloud.database(this.spaceInfo)
const action = options.action || this.action
if (action) {
db = db.action(action)
}
const collection = options.collection || this.collection
db = db.collection(collection)
const where = options.where || this.where
if (!(!where || !Object.keys(where).length)) {
db = db.where(where)
}
const field = options.field || this.field
if (field) {
db = db.field(field)
}
const orderby = options.orderby || this.orderby
if (orderby) {
db = db.orderBy(orderby)
}
const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
const size = options.pageSize !== undefined ? options.pageSize : this.page.size
const getCount = options.getcount !== undefined ? options.getcount : this.getcount
const getTree = options.gettree !== undefined ? options.gettree : this.gettree
const getOptions = {
getCount,
getTree
}
if (options.getTreePath) {
getOptions.getTreePath = options.getTreePath
}
db = db.skip(size * (current - 1)).limit(size).get(getOptions)
return db
},
_cloudDataPostField() {
let fields = [this.field];
if (this.parentField) {
fields.push(`${this.parentField} as parent_value`);
}
return fields.join(',');
},
_cloudDataTreeWhere() {
let result = []
let selected = this.selected
let parentField = this.parentField
if (parentField) {
result.push(`${parentField} == null || ${parentField} == ""`)
}
if (selected.length) {
for (var i = 0; i < selected.length - 1; i++) {
result.push(`${parentField} == '${selected[i].value}'`)
}
}
let where = []
if (this.where) {
where.push(`(${this.where})`)
}
if (result.length) {
where.push(`(${result.join(' || ')})`)
}
return where.join(' && ')
},
_cloudDataNodeWhere() {
let where = []
let selected = this.selected;
if (selected.length) {
where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`);
}
where = where.join(' || ');
if (this.where) {
return `(${this.where}) && (${where})`
}
return where
},
_getWhereByForeignKey() {
let result = []
let whereField = this._getForeignKeyByField();
if (whereField) {
result.push(`${whereField} == '${this.dataValue}'`)
}
if (this.where) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_getForeignKeyByField() {
let fields = this.field.split(',');
let whereField = null;
for (let i = 0; i < fields.length; i++) {
const items = fields[i].split('as');
if (items.length < 2) {
continue;
}
if (items[1].trim() === 'value') {
whereField = items[0].trim();
break;
}
}
return whereField;
},
_updateBindData(node) {
const {
dataList,
hasNodes
} = this._filterData(this._treeData, this.selected)
let isleaf = this._stepSearh === false && !hasNodes
if (node) {
node.isleaf = isleaf
}
this.dataList = dataList
this.selectedIndex = dataList.length - 1
if (!isleaf && this.selected.length < dataList.length) {
this.selected.push({
value: null,
text: "请选择"
})
}
return {
isleaf,
hasNodes
}
},
_updateSelected() {
let dl = this.dataList
let sl = this.selected
let textField = this.map.text
let valueField = this.map.value
for (let i = 0; i < sl.length; i++) {
let value = sl[i].value
let dl2 = dl[i]
for (let j = 0; j < dl2.length; j++) {
let item2 = dl2[j]
if (item2[valueField] === value) {
sl[i].text = item2[textField]
break
}
}
}
},
_filterData(data, paths) {
let dataList = []
let hasNodes = true
dataList.push(data.filter((item) => {
return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
}))
for (let i = 0; i < paths.length; i++) {
let value = paths[i].value
let nodes = data.filter((item) => {
return item.parent_value === value
})
if (nodes.length) {
dataList.push(nodes)
} else {
hasNodes = false
}
}
return {
dataList,
hasNodes
}
},
_extractTree(nodes, result, parent_value) {
let list = result || []
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
child.parent_value = parent_value
}
result.push(child)
let children = node.children
if (children) {
this._extractTree(children, result, node[valueField])
}
}
},
_extractTreePath(nodes, result) {
let list = result || []
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
result.push(child)
let children = node.children
if (children) {
this._extractTreePath(children, result)
}
}
},
_findNodePath(key, nodes, path = []) {
let textField = this.map.text
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let children = node.children
let text = node[textField]
let value = node[valueField]
path.push({
value,
text
})
if (value === key) {
return path
}
if (children) {
const p = this._findNodePath(key, children, path)
if (p.length) {
return p
}
}
path.pop()
}
return []
}
}
}

View File

@ -0,0 +1,692 @@
export type PaginationType = {
current : number,
size : number,
count : number
}
export type LoadMoreType = {
contentdown : string,
contentrefresh : string,
contentnomore : string
}
export type SelectedItemType = {
name : string,
value : string,
}
export type GetCommandOptions = {
collection ?: UTSJSONObject,
field ?: string,
orderby ?: string,
where ?: any,
pageData ?: string,
pageCurrent ?: number,
pageSize ?: number,
getCount ?: boolean,
getTree ?: any,
getTreePath ?: UTSJSONObject,
startwith ?: string,
limitlevel ?: number,
groupby ?: string,
groupField ?: string,
distinct ?: boolean,
pageIndistinct ?: boolean,
foreignKey ?: string,
loadtime ?: string,
manual ?: boolean
}
const DefaultSelectedNode = {
text: '请选择',
value: ''
}
export const dataPicker = defineMixin({
props: {
localdata: {
type: Array as PropType<Array<UTSJSONObject>>,
default: [] as Array<UTSJSONObject>
},
collection: {
type: Object,
default: ''
},
field: {
type: String,
default: ''
},
orderby: {
type: String,
default: ''
},
where: {
type: Object,
default: ''
},
pageData: {
type: String,
default: 'add'
},
pageCurrent: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 20
},
getcount: {
type: Boolean,
default: false
},
gettree: {
type: Object,
default: ''
},
gettreepath: {
type: Object,
default: ''
},
startwith: {
type: String,
default: ''
},
limitlevel: {
type: Number,
default: 10
},
groupby: {
type: String,
default: ''
},
groupField: {
type: String,
default: ''
},
distinct: {
type: Boolean,
default: false
},
pageIndistinct: {
type: Boolean,
default: false
},
foreignKey: {
type: String,
default: ''
},
loadtime: {
type: String,
default: 'auto'
},
manual: {
type: Boolean,
default: false
},
preload: {
type: Boolean,
default: false
},
stepSearh: {
type: Boolean,
default: true
},
selfField: {
type: String,
default: ''
},
parentField: {
type: String,
default: ''
},
multiple: {
type: Boolean,
default: false
},
value: {
type: Object,
default: ''
},
modelValue: {
type: Object,
default: ''
},
defaultProps: {
type: Object as PropType<UTSJSONObject>,
}
},
data() {
return {
loading: false,
error: null as UniCloudError | null,
treeData: [] as Array<UTSJSONObject>,
selectedIndex: 0,
selectedNodes: [] as Array<UTSJSONObject>,
selectedPages: [] as Array<UTSJSONObject>[],
selectedValue: '',
selectedPaths: [] as Array<UTSJSONObject>,
pagination: {
current: 1,
size: 20,
count: 0
} as PaginationType
}
},
computed: {
mappingTextName() : string {
// TODO
return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text'
},
mappingValueName() : string {
// TODO
return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value'
},
currentDataList() : Array<UTSJSONObject> {
if (this.selectedIndex > this.selectedPages.length - 1) {
return [] as Array<UTSJSONObject>
}
return this.selectedPages[this.selectedIndex]
},
isLocalData() : boolean {
return this.localdata.length > 0
},
isCloudData() : boolean {
return this._checkIsNotNull(this.collection)
},
isCloudDataList() : boolean {
return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0))
},
isCloudDataTree() : boolean {
return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0)
},
dataValue() : any {
return this.hasModelValue ? this.modelValue : this.value
},
hasCloudTreeData() : boolean {
return this.treeData.length > 0
},
hasModelValue() : boolean {
if (typeof this.modelValue == 'string') {
const valueString = this.modelValue as string
return (valueString.length > 0)
} else if (Array.isArray(this.modelValue)) {
const valueArray = this.modelValue as Array<string>
return (valueArray.length > 0)
}
return false
},
hasCloudDataValue() : boolean {
if (typeof this.dataValue == 'string') {
const valueString = this.dataValue as string
return (valueString.length > 0)
}
return false
}
},
created() {
this.pagination.current = this.pageCurrent
this.pagination.size = this.pageSize
this.$watch(
() : any => [
this.pageCurrent,
this.pageSize,
this.localdata,
this.value,
this.collection,
this.field,
this.getcount,
this.orderby,
this.where,
this.groupby,
this.groupField,
this.distinct
],
(newValue : Array<any>, oldValue : Array<any>) => {
this.pagination.size = this.pageSize
if (newValue[0] !== oldValue[0]) {
this.pagination.current = this.pageCurrent
}
this.onPropsChange()
}
)
},
methods: {
onPropsChange() {
this.selectedIndex = 0
this.selectedNodes.length = 0
this.selectedPages.length = 0
this.selectedPaths.length = 0
// 加载数据
this.$nextTick(() => {
this.loadData()
})
},
onTabSelect(index : number) {
this.selectedIndex = index
},
onNodeClick(nodeData : UTSJSONObject) {
if (nodeData.getBoolean('disable', false)) {
return
}
const isLeaf = this._checkIsLeafNode(nodeData)
this._trimSelectedNodes(nodeData)
this.$emit('nodeclick', nodeData)
if (this.isLocalData) {
if (isLeaf || !this._checkHasChildren(nodeData)) {
this.onFinish()
}
} else if (this.isCloudDataList) {
this.onFinish()
} else if (this.isCloudDataTree) {
if (isLeaf) {
this.onFinish()
} else if (!this._checkHasChildren(nodeData)) {
// 尝试请求一次,如果没有返回数据标记为叶子节点
this.loadCloudDataNode(nodeData)
}
}
},
getChangeNodes(): Array<UTSJSONObject> {
const nodes: Array<UTSJSONObject> = []
this.selectedNodes.forEach((node : UTSJSONObject) => {
const newNode: UTSJSONObject = {}
newNode[this.mappingTextName] = node.getString(this.mappingTextName)
newNode[this.mappingValueName] = node.getString(this.mappingValueName)
nodes.push(newNode)
})
return nodes
},
onFinish() { },
// 加载数据(自动判定环境)
loadData() {
if (this.isLocalData) {
this.loadLocalData()
} else if (this.isCloudDataList) {
this.loadCloudDataList()
} else if (this.isCloudDataTree) {
this.loadCloudDataTree()
}
},
// 加载本地数据
loadLocalData() {
this.treeData = this.localdata
if (Array.isArray(this.dataValue)) {
const value = this.dataValue as Array<UTSJSONObject>
this.selectedPaths = value.slice(0)
this._pushSelectedTreeNodes(value, this.localdata)
} else {
this._pushSelectedNodes(this.localdata)
}
},
// 加载 Cloud 数据 (单列)
loadCloudDataList() {
this._loadCloudData(null, (data : Array<UTSJSONObject>) => {
this.treeData = data
this._pushSelectedNodes(data)
})
},
// 加载 Cloud 数据 (树形)
loadCloudDataTree() {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataTreeWhere(),
getTree: true
} as GetCommandOptions
if (this._checkIsNotNull(this.gettree)) {
commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'`
}
this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
this.treeData = data
if (this.selectedPaths.length > 0) {
this._pushSelectedTreeNodes(this.selectedPaths, data)
} else {
this._pushSelectedNodes(data)
}
})
},
// 加载 Cloud 数据 (节点)
loadCloudDataNode(nodeData : UTSJSONObject) {
const commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataNodeWhere()
} as GetCommandOptions
this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
nodeData['children'] = data
if (data.length == 0) {
nodeData['isleaf'] = true
this.onFinish()
} else {
this._pushSelectedNodes(data)
}
})
},
// 回显 Cloud Tree Path
loadCloudDataPath() {
if (!this.hasCloudDataValue) {
return
}
const command : GetCommandOptions = {}
// 单列
if (this.isCloudDataList) {
// 根据 field's as value标识匹配 where 条件
let where : Array<string> = [];
let whereField = this._getForeignKeyByField();
if (whereField.length > 0) {
where.push(`${whereField} == '${this.dataValue as string}'`)
}
let whereString = where.join(' || ')
if (this._checkIsNotNull(this.where)) {
whereString = `(${this.where}) && (${whereString})`
}
command.field = this._cloudDataPostField()
command.where = whereString
}
// 树形
if (this.isCloudDataTree) {
command.field = this._cloudDataPostField()
command.getTreePath = {
startWith: `${this.selfField}=='${this.dataValue as string}'`
}
}
this._loadCloudData(command, (data : Array<UTSJSONObject>) => {
this._extractTreePath(data, this.selectedPaths)
})
},
_loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array<UTSJSONObject>) => void)) {
if (this.loading) {
return
}
this.loading = true
this.error = null
this._getCommand(options).then((response : UniCloudDBGetResult) => {
callback?.(response.data)
}).catch((err : any | null) => {
this.error = err as UniCloudError
}).finally(() => {
this.loading = false
})
},
_cloudDataPostField() : string {
let fields = [this.field];
if (this.parentField.length > 0) {
fields.push(`${this.parentField} as parent_value`)
}
return fields.join(',')
},
_cloudDataTreeWhere() : string {
let result : Array<string> = []
let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths
let parentField = this.parentField
if (parentField.length > 0) {
result.push(`${parentField} == null || ${parentField} == ""`)
}
if (selectedNodes.length > 0) {
for (var i = 0; i < selectedNodes.length - 1; i++) {
const parentFieldValue = selectedNodes[i].getString('value', '')
result.push(`${parentField} == '${parentFieldValue}'`)
}
}
let where : Array<string> = []
if (this._checkIsNotNull(this.where)) {
where.push(`(${this.where as string})`)
}
if (result.length > 0) {
where.push(`(${result.join(' || ')})`)
}
return where.join(' && ')
},
_cloudDataNodeWhere() : string {
const where : Array<string> = []
if (this.selectedNodes.length > 0) {
const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '')
where.push(`${this.parentField} == '${value}'`)
}
let whereString = where.join(' || ')
if (this._checkIsNotNull(this.where)) {
return `(${this.where as string}) && (${whereString})`
}
return whereString
},
_getWhereByForeignKey() : string {
let result : Array<string> = []
let whereField = this._getForeignKeyByField();
if (whereField.length > 0) {
result.push(`${whereField} == '${this.dataValue as string}'`)
}
if (this._checkIsNotNull(this.where)) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_getForeignKeyByField() : string {
const fields = this.field.split(',')
let whereField = ''
for (let i = 0; i < fields.length; i++) {
const items = fields[i].split('as')
if (items.length < 2) {
continue
}
if (items[1].trim() === 'value') {
whereField = items[0].trim()
break
}
}
return whereField
},
_getCommand(options ?: GetCommandOptions) : Promise<UniCloudDBGetResult> {
let db = uniCloud.databaseForJQL()
let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection)
let filter : UniCloudDBFilter | null = null
if (this.foreignKey.length > 0) {
filter = collection.foreignKey(this.foreignKey)
}
const where : any = options?.where ?? this.where
if (typeof where == 'string') {
const whereString = where as string
if (whereString.length > 0) {
filter = (filter != null) ? filter.where(where) : collection.where(where)
}
} else {
filter = (filter != null) ? filter.where(where) : collection.where(where)
}
let query : UniCloudDBQuery | null = null
if (this.field.length > 0) {
query = (filter != null) ? filter.field(this.field) : collection.field(this.field)
}
if (this.groupby.length > 0) {
if (query != null) {
query = query.groupBy(this.groupby)
} else if (filter != null) {
query = filter.groupBy(this.groupby)
}
}
if (this.groupField.length > 0) {
if (query != null) {
query = query.groupField(this.groupField)
} else if (filter != null) {
query = filter.groupField(this.groupField)
}
}
if (this.distinct == true) {
if (query != null) {
query = query.distinct(this.field)
} else if (filter != null) {
query = filter.distinct(this.field)
}
}
if (this.orderby.length > 0) {
if (query != null) {
query = query.orderBy(this.orderby)
} else if (filter != null) {
query = filter.orderBy(this.orderby)
}
}
const size = this.pagination.size
const current = this.pagination.current
if (query != null) {
query = query.skip(size * (current - 1)).limit(size)
} else if (filter != null) {
query = filter.skip(size * (current - 1)).limit(size)
} else {
query = collection.skip(size * (current - 1)).limit(size)
}
const getOptions = {}
const treeOptions = {
limitLevel: this.limitlevel,
startWith: this.startwith
}
if (this.getcount == true) {
getOptions['getCount'] = this.getcount
}
const getTree : any = options?.getTree ?? this.gettree
if (typeof getTree == 'string') {
const getTreeString = getTree as string
if (getTreeString.length > 0) {
getOptions['getTree'] = treeOptions
}
} else if (typeof getTree == 'object') {
getOptions['getTree'] = treeOptions
} else {
getOptions['getTree'] = getTree
}
const getTreePath = options?.getTreePath ?? this.gettreepath
if (typeof getTreePath == 'string') {
const getTreePathString = getTreePath as string
if (getTreePathString.length > 0) {
getOptions['getTreePath'] = getTreePath
}
} else {
getOptions['getTreePath'] = getTreePath
}
return query.get(getOptions)
},
_checkIsNotNull(value : any) : boolean {
if (typeof value == 'string') {
const valueString = value as string
return (valueString.length > 0)
} else if (value instanceof UTSJSONObject) {
return true
}
return false
},
_checkIsLeafNode(nodeData : UTSJSONObject) : boolean {
if (this.selectedIndex >= this.limitlevel) {
return true
}
if (nodeData.getBoolean('isleaf', false)) {
return true
}
return false
},
_checkHasChildren(nodeData : UTSJSONObject) : boolean {
const children = nodeData.getArray('children') ?? ([] as Array<any>)
return children.length > 0
},
_pushSelectedNodes(nodes : Array<UTSJSONObject>) {
this.selectedNodes.push(DefaultSelectedNode)
this.selectedPages.push(nodes)
this.selectedIndex = this.selectedPages.length - 1
},
_trimSelectedNodes(nodeData : UTSJSONObject) {
this.selectedNodes.splice(this.selectedIndex)
this.selectedNodes.push(nodeData)
if (this.selectedPages.length > 0) {
this.selectedPages.splice(this.selectedIndex + 1)
}
const children = nodeData.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
if (children.length > 0) {
this.selectedNodes.push(DefaultSelectedNode)
this.selectedPages.push(children)
}
this.selectedIndex = this.selectedPages.length - 1
},
_pushSelectedTreeNodes(paths : Array<UTSJSONObject>, nodes : Array<UTSJSONObject>) {
let children : Array<UTSJSONObject> = nodes
paths.forEach((node : UTSJSONObject) => {
const findNode = children.find((item : UTSJSONObject) : boolean => {
return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName))
})
if (findNode != null) {
this.selectedPages.push(children)
this.selectedNodes.push(node)
children = findNode.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
}
})
this.selectedIndex = this.selectedPages.length - 1
},
_extractTreePath(nodes : Array<UTSJSONObject>, result : Array<UTSJSONObject>) {
if (nodes.length == 0) {
return
}
const node = nodes[0]
result.push(node)
const children = node.getArray<UTSJSONObject>('children')
if (Array.isArray(children) && children!.length > 0) {
this._extractTreePath(children, result)
}
}
}
})

View File

@ -0,0 +1,76 @@
.uni-data-pickerview {
position: relative;
flex-direction: column;
overflow: hidden;
}
.loading-cover {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
align-items: center;
justify-content: center;
background-color: rgba(150, 150, 150, .1);
}
.error {
background-color: #fff;
padding: 15px;
}
.error-text {
color: #DD524D;
}
.selected-node-list {
flex-direction: row;
flex-wrap: nowrap;
}
.selected-node-item {
margin-left: 10px;
margin-right: 10px;
padding: 8px 10px 8px 10px;
border-bottom: 2px solid transparent;
}
.selected-node-item-active {
color: #007aff;
border-bottom-color: #007aff;
}
.list-view {
flex: 1;
}
.list-item {
flex-direction: row;
justify-content: space-between;
padding: 12px 15px;
border-bottom: 1px solid #f0f0f0;
}
.item-text {
color: #333333;
}
.item-text-disabled {
opacity: .5;
}
.item-text-overflow {
overflow: hidden;
}
.check {
margin-right: 5px;
border: 2px solid #007aff;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
transform: rotate(45deg);
}

View File

@ -0,0 +1,69 @@
<template>
<view class="uni-data-pickerview">
<view v-if="error!=null" class="error">
<text class="error-text">{{error!.errMsg}}</text>
</view>
<scroll-view v-if="!isCloudDataList" :scroll-x="true">
<view class="selected-node-list">
<template v-for="(item, index) in selectedNodes">
<text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
@click="onTabSelect(index)">
{{item[mappingTextName]}}
</text>
</template>
</view>
</scroll-view>
<list-view class="list-view" :scroll-y="true">
<list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
<text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
<text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
</list-item>
</list-view>
<view class="loading-cover" v-if="loading">
<slot name="pickerview-loading" :loading="loading"></slot>
</view>
</view>
</template>
<script>
import { dataPicker } from "./uni-data-picker.uts"
/**
* DataPickerview
* @description uni-data-pickerview
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {Array} localdata 本地数据,参考
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询,仅查询当前选中节点
* @value false 关闭分布查询,一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
*/
export default {
name: 'UniDataPickerView',
emits: ['nodeclick', 'change', 'update:modelValue'],
mixins: [dataPicker],
props: {
ellipsis: {
type: Boolean,
default: true
}
},
created() {
this.loadData()
},
methods: {
onFinish() {
this.$emit('change', this.getChangeNodes())
}
}
}
</script>
<style>
@import url("uni-data-pickerview.css");
</style>

View File

@ -0,0 +1,320 @@
<template>
<view class="uni-data-pickerview">
<scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true">
<view class="selected-list">
<view class="selected-item" v-for="(item,index) in selected" :key="index" :class="{
'selected-item-active':index == selectedIndex
}" @click="handleSelect(index)">
<text>{{item.text || ''}}</text>
</view>
</view>
</scroll-view>
<view class="tab-c">
<scroll-view class="list" :scroll-y="true">
<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in dataList[selectedIndex]"
:key="j" @click="handleNodeClick(item, selectedIndex, j)">
<text class="item-text">{{item[map.text]}}</text>
<view class="check"
v-if="selected.length > selectedIndex && item[map.value] == selected[selectedIndex].value">
</view>
</view>
</scroll-view>
<view class="loading-cover" v-if="loading">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<view class="error-message" v-if="errorMessage">
<text class="error-text">{{errorMessage}}</text>
</view>
</view>
</view>
</template>
<script>
import dataPicker from "./uni-data-picker.js"
/**
* DataPickerview
* @description uni-data-pickerview
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {Array} localdata 本地数据参考
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
*/
export default {
name: 'UniDataPickerView',
emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'],
mixins: [dataPicker],
props: {
managedMode: {
type: Boolean,
default: false
},
ellipsis: {
type: Boolean,
default: true
}
},
created() {
if (!this.managedMode) {
this.$nextTick(() => {
this.loadData();
})
}
},
methods: {
onPropsChange() {
this._treeData = [];
this.selectedIndex = 0;
this.$nextTick(() => {
this.loadData();
})
},
handleSelect(index) {
this.selectedIndex = index;
},
handleNodeClick(item, i, j) {
if (item.disable) {
return;
}
const node = this.dataList[i][j];
const text = node[this.map.text];
const value = node[this.map.value];
if (i < this.selected.length - 1) {
this.selected.splice(i, this.selected.length - i)
this.selected.push({
text,
value
})
} else if (i === this.selected.length - 1) {
this.selected.splice(i, 1, {
text,
value
})
}
if (node.isleaf) {
this.onSelectedChange(node, node.isleaf)
return
}
const {
isleaf,
hasNodes
} = this._updateBindData()
//
if (this.isLocalData) {
this.onSelectedChange(node, (!hasNodes || isleaf))
} else if (this.isCloudDataList) { // Cloud ()
this.onSelectedChange(node, true)
} else if (this.isCloudDataTree) { // Cloud ()
if (isleaf) {
this.onSelectedChange(node, node.isleaf)
} else if (!hasNodes) { //
this.loadCloudDataNode((data) => {
if (!data.length) {
node.isleaf = true
} else {
this._treeData.push(...data)
this._updateBindData(node)
}
this.onSelectedChange(node, node.isleaf)
})
}
}
},
updateData(data) {
this._treeData = data.treeData
this.selected = data.selected
if (!this._treeData.length) {
this.loadData()
} else {
//this.selected = data.selected
this._updateBindData()
}
},
onDataChange() {
this.$emit('datachange');
},
onSelectedChange(node, isleaf) {
if (isleaf) {
this._dispatchEvent()
}
if (node) {
this.$emit('nodeclick', node)
}
},
_dispatchEvent() {
this.$emit('change', this.selected.slice(0))
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-data-pickerview {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
overflow: hidden;
height: 100%;
}
.error-text {
color: #DD524D;
}
.loading-cover {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, .5);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
z-index: 1001;
}
.load-more {
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
}
.error-message {
background-color: #fff;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding: 15px;
opacity: .9;
z-index: 102;
}
/* #ifdef APP-NVUE */
.selected-area {
width: 750rpx;
}
/* #endif */
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
flex-wrap: nowrap;
/* #endif */
flex-direction: row;
padding: 0 5px;
border-bottom: 1px solid #f8f8f8;
}
.selected-item {
margin-left: 10px;
margin-right: 10px;
padding: 12px 0;
text-align: center;
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.selected-item-text-overflow {
width: 168px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 6em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.selected-item-active {
border-bottom: 2px solid $uni-primary;
}
.selected-item-text {
color: $uni-primary;
}
.tab-c {
position: relative;
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
overflow: hidden;
}
.list {
flex: 1;
}
.item {
padding: 12px 15px;
/* border-bottom: 1px solid #f0f0f0; */
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
}
.is-disabled {
opacity: .5;
}
.item-text {
/* flex: 1; */
color: #333333;
}
.item-text-overflow {
width: 280px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 20em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.check {
margin-right: 5px;
border: 2px solid $uni-primary;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
/* #ifndef APP-NVUE */
transition: all 0.3s;
/* #endif */
transform: rotate(45deg);
}
</style>

View File

@ -0,0 +1,93 @@
{
"id": "uni-data-picker",
"displayName": "uni-data-picker 数据驱动的picker选择器",
"version": "2.0.2",
"description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
"keywords": [
"uni-ui",
"uniui",
"picker",
"级联",
"省市区",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-load-more",
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "y",
"app-harmony": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,22 @@
## DataPicker 级联选择
> **组件名uni-data-picker**
> 代码块: `uDataPicker`
> 关联组件:`uni-data-pickerview`、`uni-load-more`。
`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
支持单列、和多列级联选择。列数没有限制如果屏幕显示不全顶部tab区域会左右滚动。
候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。
`<uni-data-picker>` 支持本地数据、云端静态数据(json)uniCloud云数据库数据。
`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema)可在schema2code中自动生成前端页面还支持服务器端校验。
在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面会自动生成地址管理的维护页面自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,25 @@
## 1.3.62024-10-15
- 修复 微信小程序中的getSystemInfo警告
## 1.3.52024-10-12
- 修复 微信小程序中的getSystemInfo警告
## 1.3.42024-10-12
- 修复 微信小程序中的getSystemInfo警告
## 1.3.32022-01-20
- 新增 showText属性 ,是否显示文本
## 1.3.22022-01-19
- 修复 nvue 平台下不显示文本的bug
## 1.3.12022-01-19
- 修复 微信小程序平台样式选择器报警告的问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more)
## 1.2.12021-08-24
- 新增 支持国际化
## 1.2.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.82021-05-12
- 新增 组件示例地址
## 1.1.72021-03-30
- 修复 uni-load-more 在首页使用时h5 平台报 'uni is not defined' 的 bug
## 1.1.62021-02-05
- 调整为uni_modules目录规范

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "Pull up to show more",
"uni-load-more.contentrefresh": "loading...",
"uni-load-more.contentnomore": "No more data"
}

View File

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉显示更多",
"uni-load-more.contentrefresh": "正在加载...",
"uni-load-more.contentnomore": "没有更多数据了"
}

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉顯示更多",
"uni-load-more.contentrefresh": "正在加載...",
"uni-load-more.contentnomore": "沒有更多數據了"
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
{
"id": "uni-load-more",
"displayName": "uni-load-more 加载更多",
"version": "1.3.6",
"description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
"keywords": [
"uni-ui",
"uniui",
"加载更多",
"load-more"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,14 @@
### LoadMore 加载更多
> **组件名uni-load-more**
> 代码块: `uLoadMore`
用于列表中,做滚动加载使用,展示 loading 的各种状态。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,15 @@
## 1.2.32024-04-02
- 修复 修复在微信小程序下inactiveColor失效bug
## 1.2.22024-03-28
- 修复 在vue2下:style动态绑定导致编译失败的bug
## 1.2.12024-03-20
- 新增 inActiveColor属性可供配置未激活时的颜色
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-segmented-control](https://uniapp.dcloud.io/component/uniui/uni-segmented-control)
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.52021-05-12
- 新增 项目示例地址
## 1.0.42021-02-05
- 调整为uni_modules目录规范

View File

@ -0,0 +1,146 @@
<template>
<view :class="[styleType === 'text'?'segmented-control--text' : 'segmented-control--button' ]"
:style="{ borderColor: styleType === 'text' ? '' : activeColor }" class="segmented-control">
<view v-for="(item, index) in values" :class="[styleType === 'text' ? '' : 'segmented-control__item--button',
index === 0 && styleType === 'button' ? 'segmented-control__item--button--first' : '',
index === values.length - 1 && styleType === 'button' ? 'segmented-control__item--button--last':'']" :key="index"
:style="{backgroundColor: index === currentIndex && styleType === 'button' ? activeColor : styleType === 'button' ?inActiveColor:'transparent', borderColor: index === currentIndex && styleType === 'text' || styleType === 'button' ? activeColor : inActiveColor}"
class="segmented-control__item" @click="_onClick(index)">
<view>
<text
:style="{color:index === currentIndex? styleType === 'text'? activeColor: '#fff': styleType === 'text'? '#000': activeColor}"
class="segmented-control__text"
:class="styleType === 'text' && index === currentIndex ? 'segmented-control__item--text': ''">{{ item }}</text>
</view>
</view>
</view>
</template>
<script>
/**
* SegmentedControl 分段器
* @description 用作不同视图的显示
* @tutorial https://ext.dcloud.net.cn/plugin?id=54
* @property {Number} current 当前选中的tab索引值从0计数
* @property {String} styleType = [button|text] 分段器样式类型
* @value button 按钮类型
* @value text 文字类型
* @property {String} activeColor 选中的标签背景色与边框颜色
* @property {String} inActiveColor 未选中的标签背景色与边框颜色
* @property {Array} values 选项数组
* @event {Function} clickItem 组件触发点击事件时触发e={currentIndex}
*/
export default {
name: 'UniSegmentedControl',
emits: ['clickItem'],
props: {
current: {
type: Number,
default: 0
},
values: {
type: Array,
default () {
return []
}
},
activeColor: {
type: String,
default: '#2979FF'
},
inActiveColor: {
type: String,
default: 'transparent'
},
styleType: {
type: String,
default: 'button'
}
},
data() {
return {
currentIndex: 0
}
},
watch: {
current(val) {
if (val !== this.currentIndex) {
this.currentIndex = val
}
}
},
computed: {},
created() {
this.currentIndex = this.current
},
methods: {
_onClick(index) {
if (this.currentIndex !== index) {
this.currentIndex = index
this.$emit('clickItem', {
currentIndex: index
})
}
}
}
}
</script>
<style lang="scss" scoped>
.segmented-control {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
height: 36px;
overflow: hidden;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.segmented-control__item {
/* #ifndef APP-NVUE */
display: inline-flex;
box-sizing: border-box;
/* #endif */
position: relative;
flex: 1;
justify-content: center;
align-items: center;
}
.segmented-control__item--button {
border-style: solid;
border-top-width: 1px;
border-bottom-width: 1px;
border-right-width: 1px;
border-left-width: 0;
}
.segmented-control__item--button--first {
border-left-width: 1px;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.segmented-control__item--button--last {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.segmented-control__item--text {
border-bottom-style: solid;
border-bottom-width: 2px;
padding: 6px 0;
}
.segmented-control__text {
font-size: 14px;
line-height: 20px;
text-align: center;
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-segmented-control",
"displayName": "uni-segmented-control 分段器",
"version": "1.2.3",
"description": "分段器由至少 2 个分段控件组成,用作不同视图的显示",
"keywords": [
"uni-ui",
"uniui",
"分段器",
"segement",
"顶部选择"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,13 @@
## SegmentedControl 分段器
> **组件名uni-segmented-control**
> 代码块: `uSegmentedControl`
用作不同视图的显示
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-segmented-control)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,145 @@
## 1.5.62024-11-25
1. 修复上版本日历高度错误的bug
2. 新增clearSelect方法用于清除日历选择
3. 新增todayDefaultStyle属性用于控制是否显示今日默认样式
## 1.5.52024-11-14
1. 通过减少view数量优化日历性能
2. 修复nvue错误日历样式以及滑动时无法切换日历的bug
3. 修复nvue警告
## 1.5.42024-10-12
修复忘记删除方法,导致运行错误
## 1.5.32024-10-12
更新nvue下可能会造成日历重叠的bug
## 1.5.22024-06-14
修复错误style语法
## 1.5.12024-06-11
修复弹窗确认与取消设置颜色不生效
## 1.5.02024-06-03
1. 修复monthSwitch事件初始化时被触发的问题
2. monthSwitch事件新增fullDate属性(用来方便直接获取字符串形式的完整日期)
3. 日历新增confirmFullDate属性用来指定在弹窗模式下的日期点击确认按钮时是否需要选择完整日期
## 1.4.92024-05-08
更新域名
## 1.4.82024-04-22
修复日历内外高度不一致的问题
## 1.4.72024-04-16
增加 operationPosition 属性 用来控制弹窗日历取消和确认按钮的显示位置
## 1.4.62024-04-16
新增属性 actBadgeColor, 当通过 `selected` 属性设置某个日期 `badgeColor`后,如果该日期被选择且主题色与 `badgeColor` 一致时,徽标会显示本颜色
## 1.4.52024-04-15
1. 新增reset方法来重置日历数据
2. 新增插槽-operation 用来自定义弹窗日历确认和取消按钮
3. selected 属性新增 badgeColor 用来指定 badge 颜色
4. close 事件改为弹窗日历点击mask关闭时触发新增cancel事件弹窗日历点击取消时触发。
## 1.4.42024-01-19
修复vue2初始化时使用同一引用类型造成的bug
## 1.4.32024-01-17
优化切换折叠状态时的记录方式
## 1.4.22024-01-12
优化 monthShowCurrentMonth 属性, 如果仅显示当月直接不展示该元素。
## 1.4.12024-01-12
优化 monthShowCurrentMonth 属性, 如果仅显示当月直接不展示该元素。
## 1.4.02024-01-11
修复type="week"时找不到
## 1.3.92024-01-10
优化: 将日历日期宽度固定改为跟随父级来决定宽度,避免出现父元素宽度变化带来的对不齐
## 1.3.82024-01-04
1. 增加节日
2. 日历增加自定义高度
3. vue页面日历折叠时增加动效
4. 增加头部插槽
5. 仅显示当月时自动扩大间距,不会再出现下面一排空白的尴尬情况了
## 1.3.72023-12-19
修复回到今日时没有正确记录折叠日期
修复selected变化时无法正常更新打点信息
## 1.3.62023-12-13
修复单选模式下选中时展示的错误的weeks
## 1.3.52023-12-13
修复动态修改date以及其他会触发更新init的函数, swiper不对及只会更新一个下一个数据的bug
## 1.3.42023-12-08
修复selectd没有启用深度监听导致直接添加数组带来的异常
## 1.3.32023-11-23
1. 修复weeks错误类型提示
2. 修复calendarChange返回month类型不对
## 1.3.22023-11-22
修复将今日日期打点禁用后造成的bug
## 1.3.12023-11-21
1. 修复wu-icon依赖缺失
2. 新增rangeHaveDisableTruncation属性, 用来指定范围选择遇到打点禁用日期是否进行截断
## 1.3.02023-11-19
1. 日历新增类型(周、月日历)
2. 日历新增折叠功能
3. 日历新增以周几开始的属性(周日、周一)
## 1.2.92023-11-08
1. 修复mode变化时不会正确重置的问题
2. 优化月份大文字显示方式
## 1.2.82023-10-22
新增maskClick用来控制是否点击遮罩层关闭
新增disabledChoice用来控制是否禁止点击日历
## 1.2.72023-09-22
修复传date值情况下不会默认选中的bug
## 1.2.62023-09-22
修改useToday描述
## 1.2.52023-09-22
新增useToday属性用来指定是否使用今天作为默认时间
## 1.2.42023-09-21
修复插入模式下顶部会显示弹窗关闭的内容
## 1.2.32023-09-20
修复恢复默认数据错误的bug
## 1.2.22023-09-19
新增rangeSameDay属性用来指定选日期范围选择时起始日期是否可以为同一天
## 1.2.12023-09-18
1.修复wu-calendar回到今日错误
2.优化wu-calendar picker日期与当前日历日期同步
3.wu-calendar新增mode属性用来控制单日期、多日期、日期选择范围模式
4.优化wu-calendar date属性来更好的指定多日期、范围日期的默认值
## 1.2.02023-09-17
修复date变化时未成功重置
## 1.1.92023-09-17
1. 新增mode属性用来指定日期选择模式
2. 增加多选
3. 增加范围选择模式、多选模式默认值
## 1.1.82023-09-12
修复回到今日错误
新增日历picker日期与日历同步
## 1.1.72023-09-11
自定义事件声明
## 1.1.62023-09-09
增加 `rangeEndRepick` 属性,用来指定选择完成后点击选择范围内的日期是否可以重选结束日期
## 1.1.52023-09-09
修复每月仅显示当月数据时上方星期错乱的问题
## 1.1.42023-09-05
1. 优化动态计算算法
2. 优化弹窗模式打开缓慢的问题
3. 优化Color方法
## 1.1.32023-09-03
新增插件预览图片
## 1.1.22023-09-02
1. 新增monthShowCurrentMonth 用来设置是否每月仅展示当月数据
2. 修复动态加载时下一月数据更新错误问题
3. 修复confirm和change事件选中的列表中有被禁止的日期的问题
## 1.1.12023-08-31
修复在插入模式中无法滑动的bug
## 1.1.02023-08-29
修复回到今日bug(来自群里464与A毛毛大佬的测试)
## 1.0.92023-08-29
修复依赖缺失
## 1.0.82023-08-22
修复v2初始化时使用不存在的属性导致不可使用的bug(来自wu-ui群内攸宁大佬的反馈)
## 1.0.72023-08-21
阻止默认滑动事件执行
## 1.0.62023-08-21
修复支付宝高度消失的问题
## 1.0.52023-08-21
修改说明
## 1.0.42023-08-20
去除误加打印
## 1.0.32023-08-20
1. 修复初始化时下个月份日期计算不对的问题
2. 增加打点禁用、设置打点徽标位置
## 1.0.22023-08-20
更新展示截图
## 1.0.12023-08-20
修改展示截图及说明
## 1.0.02023-08-20
动态滑动计算的日历插件,还可以设置滑动切换模式、自定义主题颜色、自定义文案、农历显示等功能,可以让您纵享丝滑的使用日历。

View File

@ -0,0 +1,14 @@
{
"wu-calender.ok": "ok",
"wu-calender.cancel": "cancel",
"wu-calender.year": "year",
"wu-calender.month": "month",
"wu-calender.today": "today",
"wu-calender.MON": "MON",
"wu-calender.TUE": "TUE",
"wu-calender.WED": "WED",
"wu-calender.THU": "THU",
"wu-calender.FRI": "FRI",
"wu-calender.SAT": "SAT",
"wu-calender.SUN": "SUN"
}

View File

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@ -0,0 +1,14 @@
{
"wu-calender.ok": "确定",
"wu-calender.cancel": "取消",
"wu-calender.year": "年",
"wu-calender.month": "月",
"wu-calender.today": "今日",
"wu-calender.SUN": "日",
"wu-calender.MON": "一",
"wu-calender.TUE": "二",
"wu-calender.WED": "三",
"wu-calender.THU": "四",
"wu-calender.FRI": "五",
"wu-calender.SAT": "六"
}

View File

@ -0,0 +1,14 @@
{
"wu-calender.ok": "確定",
"wu-calender.cancel": "取消",
"wu-calender.year": "年",
"wu-calender.month": "月",
"wu-calender.today": "今日",
"wu-calender.SUN": "日",
"wu-calender.MON": "一",
"wu-calender.TUE": "二",
"wu-calender.WED": "三",
"wu-calender.THU": "四",
"wu-calender.FRI": "五",
"wu-calender.SAT": "六"
}

View File

@ -0,0 +1,73 @@
export default {
props: {
showMonth: {
type: Boolean,
default: false
},
//
FoldStatus: {
type: String,
default: null
},
month: {
type: [Number, String],
default: null
},
color: {
type: String,
default: '#3c9cff'
},
startText: {
type: String,
default: '开始'
},
endText: {
type: String,
default: '结束'
},
weeks: {
type: [Object, Array],
default: ()=> {
return []
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
itemHeight: {
type: Number,
default: 64
},
monthShowCurrentMonth: {
type: Boolean,
default: false
},
actBadgeColor: {
type: String,
default: '#fff'
},
//
defaultMargin: {
type: Number,
default: 8
},
// (true)
todayDefaultStyle: {
type: Boolean,
default: true
},
}
}

View File

@ -0,0 +1,111 @@
<template>
<view class="wu-calendar-block">
<view v-if="showMonth && FoldShowMonth" class="wu-calendar__box-bg">
<text class="wu-calendar__box-bg-text">{{month}}</text>
</view>
<!-- 月或周日历 -->
<view class="wu-calendar__weeks" v-for="(item, index) in weeks" :key="index">
<template v-for="(weeks, weeksIndex) in item">
<wu-calendar-item :key="weeksIndex" v-if="!monthShowCurrentMonth || !weeks.empty" class="wu-calendar-item--hook" :weekItemStyle="weekItemStyle" :weeks="weeks" :calendar="calendar"
:selected="selected" :lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor"
:startText="startText" :endText="endText" :itemHeight="itemHeight - defaultMargin" :todayDefaultStyle="todayDefaultStyle"></wu-calendar-item>
<view v-else class="wu-calendar__weeks-item" :style="[weekItemStyle]"></view>
</template>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from '../i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
export default {
emits: ['change'],
mixins: [mpMixin, mixin, props],
data() {
return {
FoldShowMonth: false,
}
},
mounted() {
this.FoldShowMonth = this.FoldStatus == 'open';
},
computed: {
weekItemStyle() {
let weeksLength = Object.keys(this.weeks).length;
let calendarHeight = this.FoldStatus === 'open' ? this.itemHeight * 6 : this.itemHeight;
let margin = weeksLength && this.weeks[weeksLength - 1][0].empty ? this.itemHeight / (weeksLength - 1) + this.defaultMargin : this.defaultMargin
return {
marginTop: margin / 2 + 'px',
marginBottom: margin / 2 + 'px',
height: this.itemHeight - this.defaultMargin + 'px'
}
}
},
watch: {
FoldStatus(newVal) {
this.$nextTick(()=>{
this.FoldShowMonth = this.FoldStatus == 'open';
})
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$wu-text-color-grey: #999;
.wu-calendar-block {
.wu-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 0 8rpx;
}
.wu-calendar__weeks-item {
flex: 1;
}
.wu-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.wu-calendar__box-bg-text {
font-size: 100rpx;
transform: scale(4);
font-weight: bold;
color: $wu-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
}
</style>

View File

@ -0,0 +1,55 @@
export default {
props: {
color: {
type: String,
default: '#3c9cff'
},
startText: {
type: String,
default: '开始'
},
endText: {
type: String,
default: '结束'
},
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
itemHeight: {
type: Number,
default: 64
},
actBadgeColor: {
type: String,
default: '#fff',
},
weekItemStyle: {
type: Object,
default: {}
},
// (true)
todayDefaultStyle: {
type: Boolean,
default: true
},
}
}

View File

@ -0,0 +1,252 @@
<template>
<view ref="$weeksBox" class="wu-calendar-item__weeks-box" :style="[calendarItemStyle, weekItemStyle, {
borderTopLeftRadius: weeks.beforeRange ? '12rpx' : '',
borderBottomLeftRadius: weeks.beforeRange ? '12rpx' : '',
borderTopRightRadius: weeks.afterRange ? '12rpx' : '',
borderBottomRightRadius: weeks.afterRange ? '12rpx' : '',
}]" @click="choiceDate(weeks)">
<view class="wu-calendar-item__weeks-box-item" :style="[actMultipleStyle, {width: itemWidth, height: itemHeight + 'px'}]">
<!-- 自定义打点上方信息 -->
<text v-if="weeks.extraInfo && weeks.extraInfo.topInfo" class="wu-calendar-item__weeks-lunar-text" :style="[{color: weeks.extraInfo.topInfoColor || '#e43d33'}, calendarItemStyle, actMultipleStyle]">{{weeks.extraInfo.topInfo}}</text>
<!-- 徽标 -->
<text v-if="selected && weeks.extraInfo && weeks.extraInfo.badge" class="wu-calendar-item__weeks-box-circle" :style="[badgeStyle]"></text>
<!-- 日期文字 -->
<text class="wu-calendar-item__weeks-box-text" :style="[calendarItemStyle, actMultipleStyle]">{{weeks.date}}</text>
<!-- 今日的文字 -->
<text v-if="!lunar && !weeks.extraInfo && weeks.isDay && !weeks.beforeRange && !weeks.afterRange" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{todayText}}</text>
<!-- 农历文字 -->
<text v-if="lunar && !weeks.extraInfo && !weeks.beforeRange && !weeks.afterRange" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{dayText}}</text>
<!-- 选中的文字展示 -->
<text v-if="!weeks.extraInfo && (weeks.beforeRange || weeks.afterRange)" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{multipleText}}</text>
<!-- 自定义打点下方信息 -->
<text v-if="weeks.extraInfo && weeks.extraInfo.info" class="wu-calendar-item__weeks-lunar-text" :style="[{color: weeks.extraInfo.infoColor || '#e43d33'}, calendarItemStyle, actMultipleStyle]">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from '../i18n/index.js'
import { nextTick } from 'vue';
const {
t
} = initVueI18n(i18nMessages)
export default {
emits: ['change'],
mixins: [mpMixin, mixin, props],
computed: {
todayText() {
return t("wu-calender.today")
},
//
calendarItemStyle() {
let style = {};
let color = this.$w.Color.gradient(this.color, this.$w.Color.isLight(this.color) ? '#000' : '#fff', 100)[6]
//
//
if (this.weeks.rangeMultiple) {
style = {
backgroundColor: this.$w.Color.gradient(this.color, '#fff', 100)[80],
color
}
};
//
if (this.weeks.isDay && this.todayDefaultStyle) {
style.color = color;
}
//
if(this.weeks.disable) {
style = {
backgroundColor: 'rgba(249, 249, 249, 0.3)',
color: '#c0c0c0'
}
}
return style;
},
//
actMultipleStyle() {
if ((this.weeks.beforeRange || this.weeks.afterRange || this.weeks.multiples || (this.calendar.fullDate === this.weeks
.fullDate && this.weeks.mode === 'single')) && !this.weeks.disable) {
// #ifdef APP-NVUE
if(this.itemWidth == '100%') return {};
return {
backgroundColor: this.color,
color: '#fff',
borderRadius: '12rpx'
}
// #endif
// #ifndef APP-NVUE
return {
backgroundColor: this.color,
color: '#fff',
borderRadius: '12rpx'
}
// #endif
}
},
//
badgeStyle() {
let style = {
backgroundColor: this.weeks.disable ? '#c0c0c0' : '#e43d33',
width: '16rpx',
height: '16rpx'
};
if(this.weeks.extraInfo) {
if(this.weeks.extraInfo.badgeColor) {
// #fff
if ((this.weeks.beforeRange || this.weeks.afterRange || this.weeks.multiples || (this.calendar.fullDate === this.weeks
.fullDate && this.weeks.mode === 'single')) && !this.weeks.disable && this.$w.Color.convertFormat(this.weeks.extraInfo.badgeColor) == this.$w.Color.convertFormat(this.color)) {
style.backgroundColor = this.actBadgeColor;
} else {
style.backgroundColor = this.weeks.extraInfo.badgeColor
}
}
if(this.weeks.extraInfo.badgeSize) {
style.width = this.weeks.extraInfo.badgeSize
style.height = this.weeks.extraInfo.badgeSize
}
if(!this.weeks.extraInfo.badgePosition) {
style.right = '10rpx';
style.top = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'top-left'){
style.top = '10rpx';
style.left = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'top-center'){
style.top = '10rpx';
style.left = 'center';
} else if(this.weeks.extraInfo.badgePosition == 'top-right'){
style.top = '10rpx';
style.right = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-left'){
style.bottom = '10rpx';
style.left = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-center'){
style.bottom = '10rpx';
style.left = 'center';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-right'){
style.bottom = '10rpx';
style.right = '10rpx';
}
}
return style
},
//
dayText() {
let text = '';
if (this.weeks.isDay) {
text = this.todayText
} else if(this.weeks.lunar.festival) {
text = this.weeks.lunar.festival
} else if(this.weeks.lunar.isTerm) {
text = this.weeks.lunar.Term
} else if (this.weeks.lunar.IDayCn === '初一') {
text = this.weeks.lunar.IMonthCn
} else {
text = this.weeks.lunar.IDayCn
}
return text
},
//
multipleText() {
let text = '';
if (this.weeks.afterRange) {
text = this.endText
} else if (this.weeks.beforeRange) {
text = this.startText
}
return text;
}
},
data() {
return {
itemWidth: '100%'
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
},
mounted() {
// #ifdef APP-NVUE
setTimeout(()=>{
const dom = uni.requireNativePlugin('dom');
dom.getComponentRect(this.$refs.$weeksBox, res=> {
this.itemWidth = res.size.width + 'px';
})
}, 10)
// #endif
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/wu-ui-tools/theme.scss';
$wu-font-size-base: 28rpx;
$wu-text-color: #333;
$wu-font-size-sm: 24rpx;
$wu-color-error: #e43d33;
$wu-opacity-disabled: 0.3;
$wu-text-color-disable: #c0c0c0;
.wu-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 0.5px;
}
.wu-calendar-item__weeks-box-text {
font-size: $wu-font-size-base;
color: $wu-text-color;
}
.wu-calendar-item__weeks-lunar-text {
font-size: $wu-font-size-sm;
color: $wu-text-color;
}
.wu-calendar-item__weeks-box-item {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.wu-calendar-item__weeks-box-circle {
position: absolute;
border-radius: 16rpx;
background-color: $wu-color-error;
}
.wu-calendar-item--disable {
background-color: rgba(249, 249, 249, $wu-opacity-disabled);
color: $wu-text-color-disable;
}
.wu-calendar-item--extra {
color: $wu-color-error;
opacity: 0.8;
}
.wu-calendar-item--checked {
color: #fff;
}
</style>

View File

@ -0,0 +1,664 @@
/**
* @1900-2100区间内的公历农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hexAttribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0,
0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520
], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149',
'\u620c', '\u4ea5'
],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21',
'\u72d7', '\u732a'
],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206',
'\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3',
'\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206',
'\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'
],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: [
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'
],
festivals: {
'1-1': '元旦',
'2-14': '情人节',
'3-8': '妇女节',
'3-12': '植树节',
'4-1': '愚人节',
'5-1': '劳动节',
'5-4': '青年节',
'5-12': '护士节',
'6-1': '儿童节',
'8-1': '建军节',
'9-10': '教师节',
'10-1': '国庆',
'11-1': '万圣节',
'12-24': '圣诞节',
'正月初一': '春节',
'二月初二': '龙抬头',
'五月初五': '端午节',
'七月初七': '七夕节',
'七月十五': '中元节',
'八月十五': '中秋节',
'九月初九': '重阳节',
'腊月初八': '腊八节',
'腊月廿三': '小年',
'腊月三十': '除夕',
},
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d',
'\u5341'
],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341',
'\u51ac', '\u814a'
],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function(y) {
var i;
var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) {
sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0
}
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function(y) { // \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (02930)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function(y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-12930)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function(y, m) {
if (m > 12 || m < 1) {
return -1
} // 112-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-128293031)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function(y, m) {
if (m > 12 || m < 1) {
return -1
} // -1
var ms = m - 1
if (ms == 1) { // 22829
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function(lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10 // 0
if (zhiKey == 0) zhiKey = 12 // 0
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function(cMonth, cDay) {
var s =
'\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7' //
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function(offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;198724
*/
getTerm: function(y, n) {
if (y < 1900 || y > 2100) {
return -1
}
if (n < 1 || n > 24) {
return -1
}
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth=''
*/
toChinaMonth: function(m) { // => \u6708
if (m > 12 || m < 1) {
return -1
} // -1
var s = this.nStr3[m - 1]
s += '\u6708' //
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿'
*/
toChinaDay: function(d) { // => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341';
break
case 20:
s = '\u4e8c\u5341';
break
break
case 30:
s = '\u4e09\u5341';
break
break
default:
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是立春
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal=''
*/
getAnimal: function(y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function(y, m, d) { // 1900.1.31~2100.12.31
//
if (y < 1900 || y > 2100) {
return -1 // undefinedNaN
}
//
if (y == 1900 && m == 1 && d < 31) {
return -1
}
//
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i;
var leap = 0;
var temp = 0
// ymd
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0,
31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp;
i--
}
//
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
//
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
//
if (nWeek == 0) {
nWeek = 7
}
//
var year = i
var leap = this.leapMonth(i) //
var isLeap = false
//
for (i = 1; i < 13 && offset > 0; i++) {
//
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true;
temp = this.leapDays(year) //
} else {
temp = this.monthDays(year, i) //
}
//
if (isLeap == true && i == (leap + 1)) {
isLeap = false
}
offset -= temp
}
//
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true;
--i
}
}
if (offset < 0) {
offset += temp;
--i
}
//
var month = i
//
var day = offset + 1
//
var sm = m - 1
var gzY = this.toGanZhiYear(year)
//
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1)) //
var secondNode = this.getTerm(y, (m * 2)) //
// 12
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
//
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
//
const IMonthCn = (isLeap ? '\u95f0' : '') + this.toChinaMonth(month)
//
let IDayCn = this.toChinaDay(day)
//
let festival = '';
//
let lMDcn = IMonthCn + IDayCn;
//
let MD = m + '-' + d;
if (this.festivals.hasOwnProperty(lMDcn)) {
festival = this.festivals[lMDcn]
} else if(this.festivals.hasOwnProperty(MD)) {
festival = this.festivals[MD]
}
// 1900/1/1
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
//
var astro = this.toAstro(m, d)
return {
'lYear': year,
'lMonth': month,
'lDay': day,
'Animal': this.getAnimal(year),
'IMonthCn': IMonthCn,
'IDayCn': IDayCn,
'cYear': y,
'cMonth': m,
'cDay': d,
'gzYear': gzY,
'gzMonth': gzM,
'gzDay': gzD,
'isToday': isToday,
'isLeap': isLeap,
'nWeek': nWeek,
'ncWeek': '\u661f\u671f' + cWeek,
'isTerm': isTerm,
'Term': Term,
'astro': astro,
'festival': festival
}
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function(y, m, d, isLeapMonth) { // 1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) {
return -1
} //
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) {
return -1
} //
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) {
return -1
} //
//
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0;
var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { //
if (leap <= i && leap > 0) {
offset += this.leapDays(y);
isAdd = true
}
}
offset += this.monthDays(y, i)
}
//
if (isLeapMonth) {
offset += day
}
// 19001900130000()
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

View File

@ -0,0 +1,166 @@
export default {
props: {
//
date: {
type: [String, Array],
default: ''
},
// (month)
type: {
type: String,
default: 'month',
validator(value) {
return ['month', 'week'].includes(value)
}
},
//
mode: {
type: String,
default: 'single'
},
// 使(true)
useToday: {
type: Boolean,
default: true
},
// (true)
todayDefaultStyle: {
type: Boolean,
default: true
},
// 使
fold: {
type: Boolean,
default: null
},
//
color: {
type: String,
default: '#3c9cff'
},
// (70),px
itemHeight: {
type: Number,
default: 70
},
//
cancelColor: {
type: String,
default: '#333'
},
//
confirmColor: {
type: String,
default: '#333'
},
// mode=range
startText: {
type: String,
default: '开始'
},
// mode=range
endText: {
type: String,
default: '结束'
},
//
startWeek: {
type: String,
default: 'sun',
validator(value) {
return ['sun', 'mon'].includes(value)
}
},
// [{date: '2019-06-27', info: '', data: { custom: '', name: '',xxx:xxx... }}]
selected: {
type: Array,
default () {
return []
}
},
//
lunar: {
type: Boolean,
default: false
},
// -
startDate: {
type: String,
default: ''
},
// -
endDate: {
type: String,
default: ''
},
//
rangeEndRepick: {
type: Boolean,
default: false
},
//
rangeSameDay: {
type: Boolean,
default: false
},
//
rangeHaveDisableTruncation: {
type: Boolean,
default: false
},
//
monthShowCurrentMonth: {
type: Boolean,
default: false
},
// ,turefalse
insert: {
type: Boolean,
default: true
},
// horizontal: vertical none 使
slideSwitchMode: {
type: String,
default: 'horizontal'
},
//
showMonth: {
type: Boolean,
default: true
},
//
clearDate: {
type: Boolean,
default: true
},
//
maskClick: {
type: Boolean,
default: false
},
//
disabledChoice: {
type: Boolean,
default: false
},
//
operationPosition: {
type: String,
default: 'top',
validator(value) {
return ['top', 'bottom'].includes(value)
}
},
//
confirmFullDate: {
type: Boolean,
default: false
},
// `selected` `badgeColor` `badgeColor`
actBadgeColor: {
type: String,
default: '#fff'
},
...uni.$w?.props?.calendar
}
}

View File

@ -0,0 +1,552 @@
import calendar from './calendar.js';
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
mode,
monthShowCurrentMonth,
rangeEndRepick,
rangeSameDay,
rangeHaveDisableTruncation,
type,
foldStatus,
startWeek
} = {}) {
//
this.date = this.getDate(new Date()) //
//
this.selected = selected || [];
//
this.startDate = startDate
//
this.endDate = endDate
//
this.startWeek = startWeek
//
this.mode = mode
//
this.type = type
//
this.foldStatus = foldStatus
//
this.rangeEndRepick = rangeEndRepick
//
this.rangeSameDay = rangeSameDay
//
this.rangeHaveDisableTruncation = rangeHaveDisableTruncation
//
this.monthShowCurrentMonth = monthShowCurrentMonth
//
this.cleanRange()
//
this.weeks = {}
//
this.multiple = [];
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清除范围
*/
cleanRange() {
this.rangeStatus = {
before: '',
after: '',
data: []
}
}
/**
* 清除多选
*/
cleanMultiple() {
this.multiple = []
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
//
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
//
this.endDate = endDate
}
/**
* 重置是否每月仅显示当月数据
* @param {Boolean} show 是否仅显示当月数据
*/
resetMonthShowCurrentMonth(show) {
this.monthShowCurrentMonth = show
}
//
resetRangeEndRepick(val) {
this.rangeEndRepick = val
}
//
resetRangeSameDay(val) {
this.rangeSameDay = val
}
//
resetRangeHaveDisableTruncation(val) {
this.rangeHaveDisableTruncation = val
}
//
resetMode(val) {
this.mode = val
}
//
resetFoldStatus(val) {
this.foldStatus = val
}
//
resetStartWeek(val) {
this.startWeek = val
}
/**
* 创建本月某一天的信息
*/
_createCurrentDay(nowDate, full, date) {
//
let isDay = this.date.fullDate === nowDate
//
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
//
let disableBefore = true
let disableAfter = true
if (this.startDate) {
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
disableAfter = this.dateCompare(nowDate, this.endDate)
}
//
let ranges = this.rangeStatus.data
let checked = false
if (this.mode == 'range') {
checked = ranges.findIndex((item) => this.dateEqual(item, nowDate)) !== -1 ? true : false;
}
//
let multiples = this.multiple
let multiplesChecked = false
if (this.mode == 'multiple') {
multiplesChecked = multiples.findIndex(item => this.dateEqual(item, nowDate)) !== -1;
}
let data = {
fullDate: nowDate,
year: full.year,
date,
type: this.type,
mode: this.mode,
multiples: this.mode == 'multiple' ? multiplesChecked : false,
rangeMultiple: this.mode == 'range' ? checked : false,
beforeRange: this.dateEqual(this.rangeStatus.before, nowDate),
afterRange: this.dateEqual(this.rangeStatus.after, nowDate),
month: full.month,
lunar: this.getlunar(full.year, full.month, date),
disable: !(disableBefore && disableAfter),
isDay
}
if (info) {
data.extraInfo = info;
data.disable = info.disable || false;
}
return data
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // AddDayCount
break
case 'month':
if (dd.getDate() === 31 && AddDayCount > 0) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
const preMonth = dd.getMonth()
dd.setMonth(preMonth + AddDayCount) // AddDayCount
const nextMonth = dd.getMonth()
// pre 2(30 31)
if (AddDayCount < 0 && preMonth !== 0 && nextMonth - preMonth > AddDayCount) {
dd.setMonth(nextMonth + (nextMonth - preMonth + AddDayCount))
}
// next 2(30 31)
if (AddDayCount > 0 && nextMonth - preMonth > AddDayCount) {
dd.setMonth(nextMonth - (nextMonth - preMonth - AddDayCount))
}
}
break
case 'week':
dd.setDate(dd.getDate() + (AddDayCount * 7))
break;
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // AddDayCount
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 100
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 100
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
year: full.year,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDays(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
dateArr.push(this._createCurrentDay(nowDate, full, i))
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 获取任意日期的一周
*/
_getWeekDays(dateData) {
let dateArr = [];
let oneDayTime = 1000 * 60 * 60 * 24
let today = new Date(dateData);
//
let todayDay;
let startDate;
//
if(this.startWeek == 'mon') {
todayDay = today.getDay() || 7;
startDate = new Date(today.getTime() - oneDayTime * (todayDay - 1));
} else {
todayDay = today.getDay();
startDate = new Date(today.getTime() - oneDayTime * todayDay);
}
for (let i = 0; i < 7; i++) {
let temp = new Date(startDate.getTime() + i * oneDayTime)
let newDate = this.getDate(`${temp.getFullYear()}-${temp.getMonth() + 1}-${temp.getDate()}`)
dateArr.push(this._createCurrentDay(newDate.fullDate, newDate, Number(newDate.date)))
}
return dateArr;
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
//
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
//
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before = '', after = '') {
//
before = new Date(before.replace('-', '/').replace('-', '/'))
//
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
getDateAll(begin, end) {
//
let disableList = this.selected.filter(item => item.date && item.disable).map(item => item.date)
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var wuxDb = db.getTime() - 24 * 60 * 60 * 1000
var wuxDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = wuxDb; k <= wuxDe;) {
k = k + 24 * 60 * 60 * 1000
let fullDate = this.getDate(new Date(parseInt(k))).fullDate
//
if(this.rangeHaveDisableTruncation) {
//
if (disableList.includes(fullDate)) return arr;
arr.push(fullDate)
} else {
if (!disableList.includes(fullDate)) arr.push(fullDate);
}
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 设置范围
*/
setRange(fullDate) {
let {
before,
after
} = this.rangeStatus;
//
if (this.mode != 'range') return
// beforebefore true
let reset = this.dateCompare(fullDate, before);
//
if (this.rangeSameDay && before && reset) {
//
reset = !this.dateEqual(fullDate, before);
}
if ((before && after || reset) && (!this.rangeEndRepick || (this.rangeEndRepick && this.rangeStatus.data
.indexOf(fullDate) == -1))) {
this.rangeStatus.before = fullDate;
this.rangeStatus.after = '';
this.rangeStatus.data = [];
} else {
if (!before) {
this.rangeStatus.before = fullDate
} else {
if (this.dateCompare(this.rangeStatus.before, fullDate)) {
this.rangeStatus.data = this.getDateAll(this.rangeStatus.before, fullDate);
} else {
this.rangeStatus.data = this.getDateAll(fullDate, this.rangeStatus.before);
}
this.rangeStatus.after = this.rangeStatus.data[this.rangeStatus.data.length - 1]
}
}
this._getWeek(fullDate)
}
/**
* 设置多选
*/
setMultiple(fullDate) {
//
if (this.mode != 'multiple') return
//
let index = this.multiple.findIndex((item) => {
if (this.dateEqual(fullDate, item)) {
return item
}
});
if (index === -1) {
this.multiple.push(fullDate)
this.setDate(fullDate)
} else {
this.multiple = this.multiple.filter((item, i) => i != index)
}
this._getWeek(fullDate)
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData, useWeeks = true) {
const {
year,
month
} = this.getDate(dateData)
let weeks = {}
//
let canlender = [];
if (this.foldStatus === 'open') {
//
let firstDay = new Date(year, month - 1, 1).getDay();
//
if(this.startWeek === 'mon') {
firstDay = firstDay === 0 ? 6 : firstDay - 1;
}
let currentDay = new Date(year, month, 0).getDate()
//
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), //
currentMonthDys: this._currentMonthDays(currentDay, this.getDate(dateData)), //
weeks: []
}
//
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
//
if (this.monthShowCurrentMonth) {
//
canlender = canlender.concat(
dates.lastMonthDays.map(item => item = {
empty: true,
lunar: {},
}),
dates.currentMonthDys,
dates.nextMonthDays.map(item => item = {
empty: true,
lunar: {},
}),
);
} else {
// + +
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
}
} else {
canlender = this._getWeekDays(dateData)
}
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i] || {};
}
if (useWeeks) {
this.canlender = canlender
this.weeks = weeks
}
return weeks
}
//
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
{
"id": "wu-calendar",
"displayName": "wu-calendar 最全日历,动态滑动切换、多滑动模式、多日历类型、多日期选择模式等,全端兼容,无论平台,一致体验。",
"version": "1.5.6",
"description": "唯一支持动态滑动计算的日历插件,多滑动切换模式(纵、横、不滑动)、多日历类型(周、月)、多日期选择模式(单选、多选、范围)、日历周几(周日、周一)、自定义主题、农历显示等,可以让您纵享丝滑的使用日历",
"keywords": [
"wu-calendar",
"日历",
"多日历类型",
"动态滑动计算",
"wu-ui"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.5"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools",
"wu-icon",
"wu-safe-bottom"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@ -0,0 +1,16 @@
## wu-calendar 最全日历
> **组件名wu-calendar**
目前插件市场上唯一可以动态滑动计算的日历插件,多滑动切换模式(纵、横向滑动,不滑动)、多日历类型(周、月日历)、多日历选择模式(日期单选、多选、范围选择)、多日历起始周几设置(周日、周一)、自定义主题颜色(副色自动生成)、自定义文案、农历显示等功能,可以让您纵享丝滑的使用日历。
## [查看文档](https://wuui.cn/zh-CN/components/calendar.html)
## [更多组件, 请查看 `wu-ui` 组件库](https://ext.dcloud.net.cn/plugin?name=wu--ui)
(请勿下载插件zip)
<a href="https://ext.dcloud.net.cn/plugin?name=wu--ui">
<img src="https://wuui.cn/intr.png">
</a>
**如使用过程中有任何问题或者您对wu-ui有一些好的建议。<br>欢迎加入 [wu-ui 交流群](https://wuui.cn/zh-CN/components/qqFeedBack.html)**

View File

@ -0,0 +1,10 @@
## 1.0.42024-05-08
更新域名
## 1.0.32023-08-08
修复链接引入错误
## 1.0.22023-08-07
修复引入错误
## 1.0.12023-08-07
支持自定义(包括nvue)文字与图片图标
## 1.0.02023-08-03
基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。

View File

@ -0,0 +1,159 @@
export default {
'wuicon-level': 'e68f',
'wuicon-download': 'e670',
'wuicon-search': 'e632',
'wuicon-reload': 'e627',
'wuicon-scan': 'e631',
'wuicon-calendar': 'e65c',
'wuicon-bag': 'e647',
'wuicon-checkbox-mark': 'e659',
'wuicon-attach': 'e640',
'wuicon-wifi-off': 'e6cc',
'wuicon-woman': 'e626',
'wuicon-man': 'e675',
'wuicon-chat': 'e656',
'wuicon-chat-fill': 'e63f',
'wuicon-red-packet': 'e6c3',
'wuicon-folder': 'e694',
'wuicon-order': 'e695',
'wuicon-arrow-up-fill': 'e636',
'wuicon-arrow-down-fill': 'e638',
'wuicon-backspace': 'e64d',
'wuicon-photo': 'e60d',
'wuicon-photo-fill': 'e6b4',
'wuicon-lock': 'e69d',
'wuicon-lock-fill': 'e6a6',
'wuicon-lock-open': 'e68d',
'wuicon-lock-opened-fill': 'e6a1',
'wuicon-home': 'e67b',
'wuicon-home-fill': 'e68e',
'wuicon-star': 'e618',
'wuicon-star-fill': 'e61e',
'wuicon-share': 'e629',
'wuicon-share-fill': 'e6bb',
'wuicon-share-square': 'e6c4',
'wuicon-volume': 'e605',
'wuicon-volume-fill': 'e624',
'wuicon-volume-off': 'e6bd',
'wuicon-volume-off-fill': 'e6c8',
'wuicon-trash': 'e623',
'wuicon-trash-fill': 'e6ce',
'wuicon-shopping-cart': 'e6cb',
'wuicon-shopping-cart-fill': 'e630',
'wuicon-question-circle': 'e622',
'wuicon-question-circle-fill': 'e6bc',
'wuicon-plus': 'e625',
'wuicon-plus-circle': 'e603',
'wuicon-plus-circle-fill': 'e611',
'wuicon-tags': 'e621',
'wuicon-tags-fill': 'e613',
'wuicon-pause': 'e61c',
'wuicon-pause-circle': 'e696',
'wuicon-pause-circle-fill': 'e60c',
'wuicon-play-circle': 'e6af',
'wuicon-play-circle-fill': 'e62a',
'wuicon-map': 'e665',
'wuicon-map-fill': 'e6a8',
'wuicon-phone': 'e6ba',
'wuicon-phone-fill': 'e6ac',
'wuicon-list': 'e690',
'wuicon-list-dot': 'e6a9',
'wuicon-info-circle': 'e69f',
'wuicon-info-circle-fill': 'e6a7',
'wuicon-minus': 'e614',
'wuicon-minus-circle': 'e6a5',
'wuicon-mic': 'e66d',
'wuicon-mic-off': 'e691',
'wuicon-grid': 'e68c',
'wuicon-grid-fill': 'e698',
'wuicon-eye': 'e664',
'wuicon-eye-fill': 'e697',
'wuicon-eye-off': 'e69c',
'wuicon-eye-off-outline': 'e688',
'wuicon-file-text': 'e687',
'wuicon-file-text-fill': 'e67f',
'wuicon-edit-pen': 'e65d',
'wuicon-edit-pen-fill': 'e679',
'wuicon-email': 'e673',
'wuicon-email-fill': 'e683',
'wuicon-checkmark': 'e64a',
'wuicon-checkmark-circle': 'e643',
'wuicon-checkmark-circle-fill': 'e668',
'wuicon-clock': 'e66c',
'wuicon-clock-fill': 'e64b',
'wuicon-close': 'e65a',
'wuicon-close-circle': 'e64e',
'wuicon-close-circle-fill': 'e666',
'wuicon-car': 'e64f',
'wuicon-car-fill': 'e648',
'wuicon-bell': 'e651',
'wuicon-bell-fill': 'e604',
'wuicon-play-left': 'e6bf',
'wuicon-play-right': 'e6b3',
'wuicon-play-left-fill': 'e6ae',
'wuicon-play-right-fill': 'e6ad',
'wuicon-skip-back-left': 'e6c5',
'wuicon-skip-forward-right': 'e61f',
'wuicon-setting': 'e602',
'wuicon-setting-fill': 'e6d0',
'wuicon-more-dot-fill': 'e66f',
'wuicon-more-circle': 'e69e',
'wuicon-more-circle-fill': 'e684',
'wuicon-arrow-upward': 'e641',
'wuicon-arrow-downward': 'e634',
'wuicon-arrow-leftward': 'e63b',
'wuicon-arrow-rightward': 'e644',
'wuicon-arrow-up': 'e633',
'wuicon-arrow-down': 'e63e',
'wuicon-arrow-left': 'e646',
'wuicon-arrow-right': 'e63c',
'wuicon-thumb-up': 'e612',
'wuicon-thumb-up-fill': 'e62c',
'wuicon-thumb-down': 'e60a',
'wuicon-thumb-down-fill': 'e628',
'wuicon-coupon': 'e65f',
'wuicon-coupon-fill': 'e64c',
'wuicon-kefu-ermai': 'e660',
'wuicon-server-fill': 'e610',
'wuicon-server-man': 'e601',
'wuicon-warning': 'e6c1',
'wuicon-warning-fill': 'e6c7',
'wuicon-camera': 'e642',
'wuicon-camera-fill': 'e650',
'wuicon-pushpin': 'e6d1',
'wuicon-pushpin-fill': 'e6b6',
'wuicon-heart': 'e6a2',
'wuicon-heart-fill': 'e68b',
'wuicon-account': 'e63a',
'wuicon-account-fill': 'e653',
'wuicon-integral': 'e693',
'wuicon-integral-fill': 'e6b1',
'wuicon-gift': 'e680',
'wuicon-gift-fill': 'e6b0',
'wuicon-empty-data': 'e671',
'wuicon-empty-address': 'e68a',
'wuicon-empty-favor': 'e662',
'wuicon-empty-car': 'e656',
'wuicon-empty-order': 'e66b',
'wuicon-empty-list': 'e671',
'wuicon-empty-search': 'e677',
'wuicon-empty-permission': 'e67c',
'wuicon-empty-news': 'e67d',
'wuicon-empty-history': 'e684',
'wuicon-empty-coupon': 'e69b',
'wuicon-empty-page': 'e60e',
'wuicon-apple-fill': 'e635',
'wuicon-zhifubao-circle-fill': 'e617',
'wuicon-weixin-circle-fill': 'e6cd',
'wuicon-weixin-fill': 'e620',
'wuicon-qq-fill': 'e608',
'wuicon-qq-circle-fill': 'e6b9',
'wuicon-moments': 'e6a0',
'wuicon-moments-circel-fill': 'e6c2',
'wuicon-twitter': 'e607',
'wuicon-twitter-circle-fill': 'e6cf',
}

View File

@ -0,0 +1,90 @@
export default {
props: {
//
name: {
type: String,
default: ''
},
//
color: {
type: String,
default: '#606266'
},
// px
size: {
type: [String, Number],
default: '16px'
},
//
bold: {
type: Boolean,
default: false
},
// index
index: {
type: [String, Number],
default: null
},
//
hoverClass: {
type: String,
default: ''
},
// 便
customPrefix: {
type: String,
default: 'wuicon'
},
//
label: {
type: [String, Number],
default: ''
},
// label
labelPos: {
type: String,
default: 'right'
},
// label
labelSize: {
type: [String, Number],
default: '15px'
},
// label
labelColor: {
type: String,
default: '#606266'
},
// label
space: {
type: [String, Number],
default: '3px'
},
// mode
imgMode: {
type: String,
default: ''
},
//
width: {
type: [String, Number],
default: ''
},
//
height: {
type: [String, Number],
default: ''
},
//
top: {
type: [String, Number],
default: 0
},
//
stop: {
type: Boolean,
default: false
},
...uni.$w?.props?.icon
}
}

View File

@ -0,0 +1,225 @@
<template>
<view class="wu-icon" @tap="clickHandler" :class="['wu-icon--' + labelPos]">
<!-- 这里进行空字符串判断如果仅仅是v-if="label"可能会出现传递0的时候结果也无法显示 -->
<text v-if="label !== '' && (labelPos == 'left' || labelPos == 'top')" class="wu-icon__label" :style="labelStyle">{{ label }}</text>
<image class="wu-icon__img" v-if="isImg" :src="name" :mode="imgMode"
:style="[imgStyle, $w.addStyle(customStyle)]"></image>
<text v-else class="wu-icon__icon" :class="uClasses" :style="[iconStyle, $w.addStyle(customStyle)]"
:hover-class="hoverClass">{{icon}}</text>
<!-- 这里进行空字符串判断如果仅仅是v-if="label"可能会出现传递0的时候结果也无法显示 -->
<text v-if="label !== '' && (labelPos == 'right' || labelPos == 'bottom')" class="wu-icon__label" :style="labelStyle">{{ label }}</text>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
// nvueweexdom
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
import iconUrl from './wuicons.ttf';
const domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
'fontFamily': "wuicon-iconfont",
'src': "url('" + iconUrl + "')"
})
// #endif
// unicode
import icons from './icons';
import props from './props.js';
/**
* icon 图标
* @description 基于字体的图标集包含了大多数常见场景的图标
* @tutorial https://wuui.cn/zh-CN/components/icon.html
* @property {String} name 图标名称若带有 `/` 或遵循 `base64` 图片格式会被认为是图片图标则文字图标相关属性会失效
* @property {String} color 图标颜色,可接受主题色 默认 color: #606266
* @property {String | Number} size 图标字体大小单位px/rpx 默认 '16px'
* @property {Boolean} bold 是否显示粗体 默认 false
* @property {String | Number} index 点击图标的时候传递事件出去的index用于区分点击了哪一个
* @property {String} hoverClass 图标按下去的样式类用法同uni的view组件的hoverClass参数详情见官网
* @property {String} customPrefix 自定义扩展前缀方便用户扩展自己的图标库 默认 'wuicon'
* @property {String | Number} label 图标右侧的label文字
* @property {String} labelPos label相对于图标的位置默认 'right'
* @value top 上方
* @value bottom 下方
* @value left 左侧
* @value right 右侧
* @property {String | Number} labelSize label字体大小单位px 默认 '15px'
* @property {String} labelColor 图标右侧的label文字颜色 默认 color['wu-content-color']
* @property {String | Number} space label与图标的距离单位px 默认 '3px'
* @property {String} imgMode image组件的mode详见[image](https://uniapp.dcloud.net.cn/component/image.html#image)
* @property {String | Number} width 显示图片小图标时的宽度
* @property {String | Number} height 显示图片小图标时的高度
* @property {String | Number} top 图标在垂直方向上的定位 用于解决某些情况下让图标垂直居中的用途 默认 0
* @property {Boolean} stop 是否阻止事件传播 默认 false
* @property {Object} customStyle icon的样式对象形式
* @event {Function} click 点击图标时触发
* @event {Function} touchstart 事件触摸时触发
* @example <wu-icon name="photo" color="#2979ff" size="28"></wu-icon>
*/
export default {
name: 'wu-icon',
emits: ['click'],
mixins: [mpMixin, mixin, props],
data() {
return {
colorType: [
'primary',
'success',
'info',
'error',
'warning'
]
}
},
computed: {
uClasses() {
let classes = []
classes.push(this.customPrefix)
classes.push(this.customPrefix + '-' + this.name)
//
if (this.color && this.colorType.includes(this.color)) classes.push('wu-icon__icon--' + this.color)
// 使[a, b, c]
//
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
classes = classes.join(' ')
//#endif
return classes
},
iconStyle() {
let style = {}
style = {
fontSize: this.$w.addUnit(this.size),
lineHeight: this.$w.addUnit(this.size),
fontWeight: this.bold ? 'bold' : 'normal',
//
top: this.$w.addUnit(this.top)
}
//
if (this.color && !this.colorType.includes(this.color)) style.color = this.color
return style
},
// name"/"
isImg() {
const isBase64 = this.name.indexOf('data:') > -1 && this.name.indexOf('base64') > -1;
return this.name.indexOf('/') !== -1 || isBase64;
},
imgStyle() {
let style = {}
// widthheight使使size
style.width = this.width ? this.$w.addUnit(this.width) : this.$w.addUnit(this.size)
style.height = this.height ? this.$w.addUnit(this.height) : this.$w.addUnit(this.size)
return style
},
//
icon() {
// nameunicode
const code = icons['wuicon-' + this.name];
if (['wuicon'].indexOf(this.customPrefix) > -1) {
return code ? unescape(`%u${code}`) : this.name;
} else {
// #ifndef APP-NVUE
return ''
// #endif
// #ifdef APP-NVUE
return unescape(`%u${this.name}`)
// #endif
}
},
// label
labelStyle() {
let style = {
color: this.labelColor,
fontSize: this.$w.addUnit(this.labelSize),
marginLeft: this.labelPos == 'right' ? this.$w.addUnit(this.space) : 0,
marginTop: this.labelPos == 'bottom' ? this.$w.addUnit(this.space) : 0,
marginRight: this.labelPos == 'left' ? this.$w.addUnit(this.space) : 0,
marginBottom: this.labelPos == 'top' ? this.$w.addUnit(this.space) : 0
};
return style
}
},
methods: {
clickHandler(e) {
this.$emit('click', this.index)
//
this.stop && this.preventEvent(e)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/wu-ui-tools/libs/css/components.scss';
@import '@/uni_modules/wu-ui-tools/libs/css/color.scss';
//
$wu-icon-primary: $wu-primary !default;
$wu-icon-success: $wu-success !default;
$wu-icon-info: $wu-info !default;
$wu-icon-warning: $wu-warning !default;
$wu-icon-error: $wu-error !default;
$wu-icon-label-line-height: 1 !default;
/* #ifndef APP-NVUE */
// nvue
@font-face {
font-family: 'wuicon-iconfont';
src: url('./wuicons.ttf') format('truetype');
}
/* #endif */
.wu-icon {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
&--left, &--right {
flex-direction: row;
}
&--top, &--bottom {
flex-direction: column;
}
&__icon {
font-family: wuicon-iconfont;
position: relative;
@include flex;
align-items: center;
&--primary {
color: $wu-icon-primary;
}
&--success {
color: $wu-icon-success;
}
&--error {
color: $wu-icon-error;
}
&--warning {
color: $wu-icon-warning;
}
&--info {
color: $wu-icon-info;
}
}
&__img {
/* #ifndef APP-NVUE */
height: auto;
will-change: transform;
/* #endif */
}
&__label {
/* #ifndef APP-NVUE */
line-height: $wu-icon-label-line-height;
/* #endif */
}
}
</style>

Binary file not shown.

View File

@ -0,0 +1,87 @@
{
"id": "wu-icon",
"displayName": "wu-icon 图标 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.4",
"description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
"keywords": [
"wu-ui",
"图标",
"wu-icon",
"文字图标"
],
"repository": "",
"engines": {
"HBuilderX": "^3.4.15"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,10 @@
## wu-icon 图标库
> **组件名wu-icon**
基于字体的图标集,包含了大多数常见场景的图标,支持自定义(包括nvue)文字与图片图标等。
## <a href="https://wuui.cn/zh-CN/components/icon" target="_blank">查看文档</a>
## [更多插件请关注wu-ui组件库](https://ext.dcloud.net.cn/plugin?name=wuui) <small>(请不要 下载插件ZIP</small>
**如使用过程中有任何问题或者您对wu-ui有一些好的建议。<br>欢迎加入 [wu-ui 交流群](https://wuui.cn/zh-CN/components/qqFeedBack.html)**

View File

@ -0,0 +1,6 @@
## 1.0.22024-05-08
更新域名
## 1.0.12023-09-11
优化底部安全距离计算方法
## 1.0.02023-09-01
主要是针对IPhone X等一些底部带指示条的机型指示条的操作区域与页面底部存在重合容易导致用户误操作因此我们需要针对这些机型进行底部安全区适配。

View File

@ -0,0 +1,5 @@
export default {
props: {
...uni.$w?.props?.safeBottom
}
}

View File

@ -0,0 +1,61 @@
<template>
<view
class="wu-safe-bottom"
:style="[style]"
>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
/**
* SafeBottom 底部安全区
* @description 这个适配主要是针对IPhone X等一些底部带指示条的机型指示条的操作区域与页面底部存在重合容易导致用户误操作因此我们需要针对这些机型进行底部安全区适配
* @tutorial https://wuui.cn/zh-CN/components/safeAreaInset.html
* @property {type} prop_name
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function()}
* @example <wu-status-bar></wu-status-bar>
*/
export default {
name: "wu-safe-bottom",
mixins: [mpMixin, mixin, props],
data() {
return {
safeAreaBottomHeight: 0,
isNvue: false,
};
},
computed: {
style() {
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = this.$w.sys();
const style = {};
// #ifdef MP-WEIXIN
style.height = this.$w.addUnit(screenHeight - safeArea.bottom, 'px');
// #endif
// #ifndef MP-WEIXIN
style.height = this.$w.addUnit(safeAreaInsets.bottom, 'px');
// #endif
return this.$w.deepMerge(style, this.$w.addStyle(this.customStyle));
},
},
};
</script>
<style lang="scss" scoped>
.wu-safe-bottom {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
}
</style>

View File

@ -0,0 +1,88 @@
{
"id": "wu-safe-bottom",
"displayName": "wu-safe-bottom底部安全区 全端兼容 无论平台 一致体验",
"version": "1.0.2",
"description": "针对一些底部带指示条的机型,操作区域与页面底部重合,容易误操作,因此本插件对这些机型进行底部安全区适配",
"keywords": [
"wu-ui",
"wuui",
"wu-safe-bottom",
"safe-bottom",
"底部安全区"
],
"repository": "",
"engines": {
"HBuilderX": "^3.4.15"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More