温度接入数据库
parent
04e19f22df
commit
f5b9b4687f
|
|
@ -0,0 +1,12 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询DTU温湿度上报数据列表
|
||||
export function findDtuDataByInfo(query) {
|
||||
return request({
|
||||
url: '/system/data/findDtuDataByInfo',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<view class="container" :style="{padding: linePadding}">
|
||||
<!-- 带文字的分割线 -->
|
||||
<view class="divider" :style="{margin: lineMargin}">
|
||||
<view class="line" :style="{height: lineHeight,backgroundColor: lineColor}"></view>
|
||||
<text :style="{padding: textPadding,fontSize: fontSize,color: fontColor,fontWeight: fontWeight}">{{ lineText }}</text>
|
||||
<view class="line" :style="{height: lineHeight,backgroundColor: lineColor,}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DividerText",
|
||||
props: {
|
||||
|
||||
// 线边距
|
||||
linePadding: {
|
||||
type: String,
|
||||
default: '10rpx 0'
|
||||
},
|
||||
//线距离上面的距离
|
||||
lineMargin: {
|
||||
type: String,
|
||||
default: '30rpx 0'
|
||||
},
|
||||
// 分割线粗细
|
||||
lineHeight: {
|
||||
type: String,
|
||||
default: '1rpx'
|
||||
},
|
||||
// 分割线颜色
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#e5e5e5'
|
||||
},
|
||||
// 字体距离分割线中间的距离
|
||||
textPadding: {
|
||||
type: String,
|
||||
default: '0 20rpx'
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: '28rpx'
|
||||
},
|
||||
// 字体颜色
|
||||
fontColor: {
|
||||
type: String,
|
||||
default: '#999'
|
||||
},
|
||||
// 字体粗细
|
||||
fontWeight: {
|
||||
type: String,
|
||||
default: 'normal'
|
||||
},
|
||||
// 分割线文字
|
||||
lineText: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.line {
|
||||
flex: 1
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<view class="divider" :style="{ margin: margin }">
|
||||
<view
|
||||
:style="{
|
||||
backgroundColor: color,
|
||||
height: thickness + 'rpx',
|
||||
width: hasMargin ? 'calc(100% - ' + sideMargin + 'rpx)' : '100%'
|
||||
}"
|
||||
></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Divider",
|
||||
props: {
|
||||
// 分割线颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: "#e5e5e5"
|
||||
},
|
||||
// 分割线厚度
|
||||
thickness: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 上下外边距(如:"20rpx 0")
|
||||
margin: {
|
||||
type: String,
|
||||
default: "20rpx 0"
|
||||
},
|
||||
// 是否有左右边距
|
||||
hasMargin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 左右边距大小
|
||||
sideMargin: {
|
||||
type: Number,
|
||||
default: 40
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divider {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// 应用全局配置
|
||||
module.exports = {
|
||||
// baseUrl: 'https://vue.ruoyi.vip/prod-api',
|
||||
// baseUrl: 'http://localhost:8088',
|
||||
baseUrl: 'http://1.94.254.176:8088',
|
||||
baseUrl: 'http://172.14.24.109:8088',
|
||||
// baseUrl: 'http://1.94.254.176:8088',
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
|
|
|
|||
2
main.js
2
main.js
|
|
@ -5,6 +5,8 @@ import store from './store' // store
|
|||
import plugins from './plugins' // plugins
|
||||
import './permission' // permission
|
||||
import { getDicts } from "@/api/system/dict/data"
|
||||
import "./utils/uni.css";
|
||||
|
||||
|
||||
Vue.use(plugins)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,63 @@
|
|||
<view class="container">
|
||||
<!-- 控制设置标题 -->
|
||||
<view class="control-title">控制设置</view>
|
||||
<uni-section title="请选择大棚:" titleFontSize="16px" type="line">
|
||||
<uni-section title="请选择大棚:" titleFontSize="18px" type="line">
|
||||
<view class="uni-px-5 uni-pb-5">
|
||||
<uni-data-select v-model="value" :localdata="range" @change="change"></uni-data-select>
|
||||
</view>
|
||||
</uni-section>
|
||||
|
||||
<uni-section :title="title" titleFontSize="16px" type="line" v-if="value!== 1">
|
||||
|
||||
<uni-section title="实时温湿度" titleFontSize="16px" type="line" v-if="value!== 1">
|
||||
<template v-slot:right >
|
||||
{{ temp }}
|
||||
</template>
|
||||
|
||||
<view>
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp1 }}</text>
|
||||
<text class="data">温度1</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp2 }}</text>
|
||||
<text class="data">温度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp3 }}</text>
|
||||
<text class="data">温度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp4 }}</text>
|
||||
<text class="data">温度4</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi1 }}</text>
|
||||
<text class="data">湿度1</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi2 }}</text>
|
||||
<text class="data">湿度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi3 }}</text>
|
||||
<text class="data">湿度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi4 }}</text>
|
||||
<text class="data">湿度4</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</uni-section>
|
||||
|
||||
<uni-section title="设备控制" titleFontSize="16px" type="line" v-if="value!== 1">
|
||||
|
||||
<!-- 卷膜/卷被卡片容器(2列栅格布局) -->
|
||||
<view class="card-grid">
|
||||
<!-- 卷被开卡片 -->
|
||||
|
|
@ -107,8 +154,15 @@
|
|||
|
||||
<script>
|
||||
import mqtt from 'mqtt'
|
||||
import UniDatetimePicker
|
||||
from "../../uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue";
|
||||
import { findDtuDataByInfo } from "@/api/system/data";
|
||||
|
||||
export default {
|
||||
dicts: ['sys_data_map'],
|
||||
components: {
|
||||
UniDatetimePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
temp: "",
|
||||
|
|
@ -136,6 +190,16 @@ export default {
|
|||
title:'',
|
||||
message: {},
|
||||
connected:false,
|
||||
liveData: {
|
||||
temp1: '数据加载中...',
|
||||
temp2: '数据加载中...',
|
||||
temp3: '数据加载中...',
|
||||
temp4: '数据加载中...',
|
||||
humi1: '数据加载中...',
|
||||
humi2: '数据加载中...',
|
||||
humi3: '数据加载中...',
|
||||
humi4: '数据加载中...'
|
||||
},
|
||||
// 卡片状态(模拟后端返回数据)
|
||||
show: {
|
||||
jbk: "暂停",
|
||||
|
|
@ -157,7 +221,8 @@ export default {
|
|||
jm2g: 0,
|
||||
jm3k: 0,
|
||||
jm3g: 0
|
||||
}
|
||||
},
|
||||
fontStyle: ''
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
|
|
@ -203,12 +268,30 @@ export default {
|
|||
if (selectedItem) {
|
||||
this.selectedText = selectedItem.text; // 获取展示文本
|
||||
this.title= this.selectedText;
|
||||
var queryParams = {
|
||||
imei: this.imei
|
||||
}
|
||||
findDtuDataByInfo(queryParams).then(response => {
|
||||
this.liveData = {
|
||||
temp1: response.data.temp1 || '已离线...',
|
||||
temp2: response.data.temp2 || '已离线..',
|
||||
temp3: response.data.temp3 || '已离线..',
|
||||
temp4: response.data.temp4 || '已离线..',
|
||||
humi1: response.data.humi1 || '已离线..',
|
||||
humi2: response.data.humi2 || '已离线..',
|
||||
humi3: response.data.humi3 || '已离线..',
|
||||
humi4: response.data.humi4 || '已离线..'
|
||||
}
|
||||
this.temp = "最后更新时间:"+response.data.time;
|
||||
this.fontStyle = 'font-size:16px'
|
||||
})
|
||||
} else {
|
||||
this.selectedText = ''; // 无匹配项时清空
|
||||
this.title='';
|
||||
this.value=1;
|
||||
}
|
||||
this.reset();
|
||||
this.style="";
|
||||
// this.disconnectMqtt();
|
||||
},
|
||||
|
||||
|
|
@ -236,6 +319,16 @@ export default {
|
|||
};
|
||||
this.message = {};
|
||||
this.temp = '';
|
||||
this.liveData = {
|
||||
temp1: '数据加载中...',
|
||||
temp2: '数据加载中...',
|
||||
temp3: '数据加载中...',
|
||||
temp4: '数据加载中...',
|
||||
humi1: '数据加载中...',
|
||||
humi2: '数据加载中...',
|
||||
humi3: '数据加载中...',
|
||||
humi4: '数据加载中...'
|
||||
}
|
||||
},
|
||||
// 卡片点击事件(实际项目中调用接口修改状态) 功能标识
|
||||
handleCardClick(status, type) {
|
||||
|
|
@ -387,7 +480,7 @@ export default {
|
|||
// 👉 这是“指令回执”
|
||||
this.handleCommandAck(msgData, this.deviceType);
|
||||
} else {
|
||||
this.handleOtherContent(msgData)
|
||||
this.handleOtherContent(msgData,payload)
|
||||
}
|
||||
},
|
||||
addMessage(content) {
|
||||
|
|
@ -416,7 +509,7 @@ export default {
|
|||
},
|
||||
|
||||
// 处理其他内容的函数
|
||||
handleOtherContent(msgData) {
|
||||
handleOtherContent(msgData,payload) {
|
||||
// 业务逻辑:处理传感器数据、设备状态等
|
||||
// console.log("收到其他内容:", msgData);
|
||||
// 例如:更新温湿度显示、设备在线状态等
|
||||
|
|
@ -430,29 +523,51 @@ export default {
|
|||
this.status = {...msgData}
|
||||
// console.info("imei: "+this.publishTopic+"copy: ",this.status)
|
||||
}
|
||||
|
||||
|
||||
// 实时温度展示
|
||||
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
|
||||
if (Object.keys(msgData).length > 0 && allKeysNumeric2) {
|
||||
var avgResult = this.calculateAvg(msgData).averageFixed;
|
||||
this.temp = "当前棚内平均温度:"+avgResult+"℃"
|
||||
|
||||
// 你的键:温度101~104、湿度201~204(按你之前约定温度/10、湿度/10)
|
||||
const div10 = (v) => (v == null ? null : Math.round((Number(v)/10)*10)/10)
|
||||
|
||||
this.liveData = {
|
||||
temp1: msgData["201"]==null ? "已离线...":div10(msgData["201"])+"℃",
|
||||
humi1: msgData["101"]==null ? "已离线...":div10(msgData["101"])+"%RH",
|
||||
temp2: msgData["202"]==null ? "已离线...":div10(msgData["202"])+"℃",
|
||||
humi2: msgData["102"]==null ? "已离线...":div10(msgData["102"])+"%RH",
|
||||
temp3: msgData["203"]==null ? "已离线...":div10(msgData["203"])+"℃",
|
||||
humi3: msgData["103"]==null ? "已离线...":div10(msgData["103"])+"%RH",
|
||||
temp4: msgData["204"]==null ? "已离线...":div10(msgData["204"])+"℃",
|
||||
humi4: msgData["104"]==null ? "已离线...":div10(msgData["104"])+"%RH"
|
||||
}
|
||||
// 调用函数获取并输出格式化后的当前时间
|
||||
this.temp = "最新更新时间:" + this.getCurrentTime();
|
||||
this.fontStyle = 'font-size:16px'
|
||||
}
|
||||
|
||||
},
|
||||
calculateAvg(dataObj) {
|
||||
const targetKeys = ['201', '202', '203', '204'];
|
||||
const values = targetKeys.map(key => dataObj[key]).filter(val => typeof val === 'number' && !isNaN(val));
|
||||
if (values.length === 0) return { average: 0, averageFixed: '0.00' };
|
||||
const sum = values.reduce((total, num) => total + num, 0);
|
||||
const avg = sum/10 / values.length;
|
||||
return {
|
||||
average: avg,
|
||||
averageFixed: avg.toFixed(2)
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取格式化后的当前时间
|
||||
* @returns {string} 格式为 YYYY-MM-DD HH:mm:ss 的当前时间
|
||||
*/
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
|
||||
// 获取年、月、日(补零确保两位数)
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需 +1
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
|
||||
// 获取时、分、秒(补零确保两位数)
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
|
||||
// 拼接成标准格式
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
},
|
||||
},
|
||||
onHide() {
|
||||
this.disconnectMqtt();
|
||||
},
|
||||
|
|
@ -541,4 +656,45 @@ export default {
|
|||
.uni-pb-5 {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 50rpx;
|
||||
margin: 10rpx 10rpx 8rpx 0;
|
||||
padding: 0;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
text-align: center;
|
||||
background:#fff;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx #bfbec1
|
||||
}
|
||||
|
||||
.text:first-child {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.uni-view {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
height: 150rpx;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.data {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
line-height: 23px;
|
||||
color: #3a3a3a;
|
||||
}
|
||||
.data:nth-child(even) {
|
||||
font-size: 12px;
|
||||
margin-top: 3px;
|
||||
color: #9c9c9c;
|
||||
}
|
||||
/deep/ .uni-section-header__slot-right {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
const state = {
|
||||
dict: new Array()
|
||||
}
|
||||
const mutations = {
|
||||
SET_DICT: (state, { key, value }) => {
|
||||
if (key !== null && key !== "") {
|
||||
state.dict.push({
|
||||
key: key,
|
||||
value: value
|
||||
})
|
||||
}
|
||||
},
|
||||
REMOVE_DICT: (state, key) => {
|
||||
try {
|
||||
for (let i = 0; i < state.dict.length; i++) {
|
||||
if (state.dict[i].key == key) {
|
||||
state.dict.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
CLEAN_DICT: (state) => {
|
||||
state.dict = new Array()
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
// 设置字典
|
||||
setDict({ commit }, data) {
|
||||
commit('SET_DICT', data)
|
||||
},
|
||||
// 删除字典
|
||||
removeDict({ commit }, key) {
|
||||
commit('REMOVE_DICT', key)
|
||||
},
|
||||
// 清空字典
|
||||
cleanDict({ commit }) {
|
||||
commit('CLEAN_DICT')
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/**
|
||||
* 通用js方法封装处理
|
||||
* Copyright (c) 2019 agri
|
||||
*/
|
||||
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
} else if (typeof time === 'string') {
|
||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '')
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
export function resetForm(refName) {
|
||||
if (this.$refs[refName]) {
|
||||
this.$refs[refName].resetFields()
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期范围
|
||||
export function addDateRange(params, dateRange, propName) {
|
||||
let search = params
|
||||
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}
|
||||
dateRange = Array.isArray(dateRange) ? dateRange : []
|
||||
if (typeof (propName) === 'undefined') {
|
||||
search.params['beginTime'] = dateRange[0]
|
||||
search.params['endTime'] = dateRange[1]
|
||||
} else {
|
||||
search.params['begin' + propName] = dateRange[0]
|
||||
search.params['end' + propName] = dateRange[1]
|
||||
}
|
||||
return search
|
||||
}
|
||||
|
||||
// 回显数据字典
|
||||
export function selectDictLabel(datas, value) {
|
||||
if (value === undefined) {
|
||||
return ""
|
||||
}
|
||||
var actions = []
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + value)) {
|
||||
actions.push(datas[key].label)
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (actions.length === 0) {
|
||||
actions.push(value)
|
||||
}
|
||||
return actions.join('')
|
||||
}
|
||||
|
||||
// 回显数据字典(字符串、数组)
|
||||
export function selectDictLabels(datas, value, separator) {
|
||||
if (value === undefined || value.length ===0) {
|
||||
return ""
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(",")
|
||||
}
|
||||
var actions = []
|
||||
var currentSeparator = undefined === separator ? "," : separator
|
||||
var temp = value.split(currentSeparator)
|
||||
Object.keys(value.split(currentSeparator)).some((val) => {
|
||||
var match = false
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + temp[val])) {
|
||||
actions.push(datas[key].label + currentSeparator)
|
||||
match = true
|
||||
}
|
||||
})
|
||||
if (!match) {
|
||||
actions.push(temp[val] + currentSeparator)
|
||||
}
|
||||
})
|
||||
return actions.join('').substring(0, actions.join('').length - 1)
|
||||
}
|
||||
|
||||
// 字符串格式化(%s )
|
||||
export function sprintf(str) {
|
||||
var args = arguments, flag = true, i = 1
|
||||
str = str.replace(/%s/g, function () {
|
||||
var arg = args[i++]
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false
|
||||
return ''
|
||||
}
|
||||
return arg
|
||||
})
|
||||
return flag ? str : ''
|
||||
}
|
||||
|
||||
// 转换字符串,undefined,null等转化为""
|
||||
export function parseStrEmpty(str) {
|
||||
if (!str || str == "undefined" || str == "null") {
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// 数据合并
|
||||
export function mergeRecursive(source, target) {
|
||||
for (var p in target) {
|
||||
try {
|
||||
if (target[p].constructor == Object) {
|
||||
source[p] = mergeRecursive(source[p], target[p])
|
||||
} else {
|
||||
source[p] = target[p]
|
||||
}
|
||||
} catch (e) {
|
||||
source[p] = target[p]
|
||||
}
|
||||
}
|
||||
return source
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造树型结构数据
|
||||
* @param {*} data 数据源
|
||||
* @param {*} id id字段 默认 'id'
|
||||
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||
* @param {*} children 孩子节点字段 默认 'children'
|
||||
*/
|
||||
export function handleTree(data, id, parentId, children) {
|
||||
let config = {
|
||||
id: id || 'id',
|
||||
parentId: parentId || 'parentId',
|
||||
childrenList: children || 'children'
|
||||
}
|
||||
|
||||
var childrenListMap = {}
|
||||
var tree = []
|
||||
for (let d of data) {
|
||||
let id = d[config.id]
|
||||
childrenListMap[id] = d
|
||||
if (!d[config.childrenList]) {
|
||||
d[config.childrenList] = []
|
||||
}
|
||||
}
|
||||
|
||||
for (let d of data) {
|
||||
let parentId = d[config.parentId]
|
||||
let parentObj = childrenListMap[parentId]
|
||||
if (!parentObj) {
|
||||
tree.push(d)
|
||||
} else {
|
||||
parentObj[config.childrenList].push(d)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数处理
|
||||
* @param {*} params 参数
|
||||
*/
|
||||
export function tansParams(params) {
|
||||
let result = ''
|
||||
for (const propName of Object.keys(params)) {
|
||||
const value = params[propName]
|
||||
var part = encodeURIComponent(propName) + "="
|
||||
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||
if (typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||
let params = propName + '[' + key + ']'
|
||||
var subPart = encodeURIComponent(params) + "="
|
||||
result += subPart + encodeURIComponent(value[key]) + "&"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += part + encodeURIComponent(value) + "&"
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 返回项目路径
|
||||
export function getNormalPath(p) {
|
||||
if (p.length === 0 || !p || p == 'undefined') {
|
||||
return p
|
||||
}
|
||||
let res = p.replace('//', '/')
|
||||
if (res[res.length - 1] === '/') {
|
||||
return res.slice(0, res.length - 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 验证是否为blob格式
|
||||
export function blobValidate(data) {
|
||||
return data.type !== 'application/json'
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import Vue from 'vue'
|
||||
import { mergeRecursive } from "@/utils/agri"
|
||||
import DictMeta from './DictMeta'
|
||||
import DictData from './DictData'
|
||||
|
||||
const DEFAULT_DICT_OPTIONS = {
|
||||
types: [],
|
||||
}
|
||||
|
||||
/**
|
||||
* @classdesc 字典
|
||||
* @property {Object} label 标签对象,内部属性名为字典类型名称
|
||||
* @property {Object} dict 字段数组,内部属性名为字典类型名称
|
||||
* @property {Array.<DictMeta>} _dictMetas 字典元数据数组
|
||||
*/
|
||||
export default class Dict {
|
||||
constructor() {
|
||||
this.owner = null
|
||||
this.label = {}
|
||||
this.type = {}
|
||||
}
|
||||
|
||||
init(options) {
|
||||
if (options instanceof Array) {
|
||||
options = { types: options }
|
||||
}
|
||||
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
|
||||
if (opts.types === undefined) {
|
||||
throw new Error('need dict types')
|
||||
}
|
||||
const ps = []
|
||||
this._dictMetas = opts.types.map(t => DictMeta.parse(t))
|
||||
this._dictMetas.forEach(dictMeta => {
|
||||
const type = dictMeta.type
|
||||
Vue.set(this.label, type, {})
|
||||
Vue.set(this.type, type, [])
|
||||
if (dictMeta.lazy) {
|
||||
return
|
||||
}
|
||||
ps.push(loadDict(this, dictMeta))
|
||||
})
|
||||
return Promise.all(ps)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载字典
|
||||
* @param {String} type 字典类型
|
||||
*/
|
||||
reloadDict(type) {
|
||||
const dictMeta = this._dictMetas.find(e => e.type === type)
|
||||
if (dictMeta === undefined) {
|
||||
return Promise.reject(`the dict meta of ${type} was not found`)
|
||||
}
|
||||
return loadDict(this, dictMeta)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典
|
||||
* @param {Dict} dict 字典
|
||||
* @param {DictMeta} dictMeta 字典元数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function loadDict(dict, dictMeta) {
|
||||
return dictMeta.request(dictMeta)
|
||||
.then(response => {
|
||||
const type = dictMeta.type
|
||||
let dicts = dictMeta.responseConverter(response, dictMeta)
|
||||
if (!(dicts instanceof Array)) {
|
||||
console.error('the return of responseConverter must be Array.<DictData>')
|
||||
dicts = []
|
||||
} else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {
|
||||
console.error('the type of elements in dicts must be DictData')
|
||||
dicts = []
|
||||
}
|
||||
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
|
||||
dicts.forEach(d => {
|
||||
Vue.set(dict.label[type], d.value, d.label)
|
||||
})
|
||||
return dicts
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import DictOptions from './DictOptions'
|
||||
import DictData from './DictData'
|
||||
|
||||
export default function(dict, dictMeta) {
|
||||
const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)
|
||||
const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)
|
||||
return new DictData(dict[label], dict[value], dict)
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定字典字段
|
||||
* @param {DictData} dict
|
||||
* @param {...String} fields
|
||||
*/
|
||||
function determineDictField(dict, ...fields) {
|
||||
return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* @classdesc 字典数据
|
||||
* @property {String} label 标签
|
||||
* @property {*} value 标签
|
||||
* @property {Object} raw 原始数据
|
||||
*/
|
||||
export default class DictData {
|
||||
constructor(label, value, raw) {
|
||||
this.label = label
|
||||
this.value = value
|
||||
this.raw = raw
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { mergeRecursive } from "@/utils/agri"
|
||||
import DictOptions from './DictOptions'
|
||||
|
||||
/**
|
||||
* @classdesc 字典元数据
|
||||
* @property {String} type 类型
|
||||
* @property {Function} request 请求
|
||||
* @property {String} label 标签字段
|
||||
* @property {String} value 值字段
|
||||
*/
|
||||
export default class DictMeta {
|
||||
constructor(options) {
|
||||
this.type = options.type
|
||||
this.request = options.request
|
||||
this.responseConverter = options.responseConverter
|
||||
this.labelField = options.labelField
|
||||
this.valueField = options.valueField
|
||||
this.lazy = options.lazy === true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析字典元数据
|
||||
* @param {Object} options
|
||||
* @returns {DictMeta}
|
||||
*/
|
||||
DictMeta.parse= function(options) {
|
||||
let opts = null
|
||||
if (typeof options === 'string') {
|
||||
opts = DictOptions.metas[options] || {}
|
||||
opts.type = options
|
||||
} else if (typeof options === 'object') {
|
||||
opts = options
|
||||
}
|
||||
opts = mergeRecursive(DictOptions.metas['*'], opts)
|
||||
return new DictMeta(opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { mergeRecursive } from "@/utils/agri"
|
||||
import dictConverter from './DictConverter'
|
||||
|
||||
export const options = {
|
||||
metas: {
|
||||
'*': {
|
||||
/**
|
||||
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise
|
||||
*/
|
||||
request: (dictMeta) => {
|
||||
console.log(`load dict ${dictMeta.type}`)
|
||||
return Promise.resolve([])
|
||||
},
|
||||
/**
|
||||
* 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData
|
||||
*/
|
||||
responseConverter,
|
||||
labelField: 'label',
|
||||
valueField: 'value',
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 默认标签字段
|
||||
*/
|
||||
DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],
|
||||
/**
|
||||
* 默认值字段
|
||||
*/
|
||||
DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射字典
|
||||
* @param {Object} response 字典数据
|
||||
* @param {DictMeta} dictMeta 字典元数据
|
||||
* @returns {DictData}
|
||||
*/
|
||||
function responseConverter(response, dictMeta) {
|
||||
const dicts = response.content instanceof Array ? response.content : response
|
||||
if (dicts === undefined) {
|
||||
console.warn(`no dict data of "${dictMeta.type}" found in the response`)
|
||||
return []
|
||||
}
|
||||
return dicts.map(d => dictConverter(d, dictMeta))
|
||||
}
|
||||
|
||||
export function mergeOptions(src) {
|
||||
mergeRecursive(options, src)
|
||||
}
|
||||
|
||||
export default options
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import Dict from './Dict'
|
||||
import { mergeOptions } from './DictOptions'
|
||||
|
||||
export default function(Vue, options) {
|
||||
mergeOptions(options)
|
||||
Vue.mixin({
|
||||
data() {
|
||||
if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) {
|
||||
return {}
|
||||
}
|
||||
const dict = new Dict()
|
||||
dict.owner = this
|
||||
return {
|
||||
dict
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!(this.dict instanceof Dict)) {
|
||||
return
|
||||
}
|
||||
options.onCreated && options.onCreated(this.dict)
|
||||
this.dict.init(this.$options.dicts).then(() => {
|
||||
options.onReady && options.onReady(this.dict)
|
||||
this.$nextTick(() => {
|
||||
this.$emit('dictReady', this.dict)
|
||||
if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) {
|
||||
this.$options.methods.onDictReady.call(this, this.dict)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue