Compare commits

...

No commits in common. "d26302199da7fb6a05384ee770761bfec23c5e83" and "8e94f1d7f2fb3bb6796f84e616a64d99b3b82b0a" have entirely different histories.

40 changed files with 5127 additions and 422 deletions

1
.gitignore vendored
View File

@ -54,3 +54,4 @@
/unpackage/dist/dev/app-plus/manifest.json /unpackage/dist/dev/app-plus/manifest.json
/unpackage/dist/dev/app-plus/uni-app-view.umd.js /unpackage/dist/dev/app-plus/uni-app-view.umd.js
/node_modules /node_modules
/unpackage

View File

@ -1,26 +0,0 @@
{
// launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version" : "0.0",
"configurations" : [
{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
},
{
"playground" : "standard",
"type" : "uni-app:app-android"
}
]
}

View File

@ -1,13 +0,0 @@
{
"hash": "5610b5a1",
"browserHash": "3deef0d9",
"optimized": {
"base-64": {
"src": "../../node_modules/base-64/base64.js",
"file": "base-64.js",
"fileHash": "768aae23",
"needsInterop": true
}
},
"chunks": {}
}

View File

@ -1,117 +0,0 @@
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// ../../../../Documents/HBuilderProjects/B404219-tianranqi/node_modules/base-64/base64.js
var require_base64 = __commonJS({
"../../../../Documents/HBuilderProjects/B404219-tianranqi/node_modules/base-64/base64.js"(exports, module) {
(function(root) {
var freeExports = typeof exports == "object" && exports;
var freeModule = typeof module == "object" && module && module.exports == freeExports && module;
var freeGlobal = typeof global == "object" && global;
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
root = freeGlobal;
}
var InvalidCharacterError = function(message) {
this.message = message;
};
InvalidCharacterError.prototype = new Error();
InvalidCharacterError.prototype.name = "InvalidCharacterError";
var error = function(message) {
throw new InvalidCharacterError(message);
};
var TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
var decode = function(input) {
input = String(input).replace(REGEX_SPACE_CHARACTERS, "");
var length = input.length;
if (length % 4 == 0) {
input = input.replace(/==?$/, "");
length = input.length;
}
if (length % 4 == 1 || // http://whatwg.org/C#alphanumeric-ascii-characters
/[^+a-zA-Z0-9/]/.test(input)) {
error(
"Invalid character: the string to be decoded is not correctly encoded."
);
}
var bitCounter = 0;
var bitStorage;
var buffer;
var output = "";
var position = -1;
while (++position < length) {
buffer = TABLE.indexOf(input.charAt(position));
bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
if (bitCounter++ % 4) {
output += String.fromCharCode(
255 & bitStorage >> (-2 * bitCounter & 6)
);
}
}
return output;
};
var encode = function(input) {
input = String(input);
if (/[^\0-\xFF]/.test(input)) {
error(
"The string to be encoded contains characters outside of the Latin1 range."
);
}
var padding = input.length % 3;
var output = "";
var position = -1;
var a;
var b;
var c;
var buffer;
var length = input.length - padding;
while (++position < length) {
a = input.charCodeAt(position) << 16;
b = input.charCodeAt(++position) << 8;
c = input.charCodeAt(++position);
buffer = a + b + c;
output += TABLE.charAt(buffer >> 18 & 63) + TABLE.charAt(buffer >> 12 & 63) + TABLE.charAt(buffer >> 6 & 63) + TABLE.charAt(buffer & 63);
}
if (padding == 2) {
a = input.charCodeAt(position) << 8;
b = input.charCodeAt(++position);
buffer = a + b;
output += TABLE.charAt(buffer >> 10) + TABLE.charAt(buffer >> 4 & 63) + TABLE.charAt(buffer << 2 & 63) + "=";
} else if (padding == 1) {
buffer = input.charCodeAt(position);
output += TABLE.charAt(buffer >> 2) + TABLE.charAt(buffer << 4 & 63) + "==";
}
return output;
};
var base64 = {
"encode": encode,
"decode": decode,
"version": "1.0.0"
};
if (typeof define == "function" && typeof define.amd == "object" && define.amd) {
define(function() {
return base64;
});
} else if (freeExports && !freeExports.nodeType) {
if (freeModule) {
freeModule.exports = base64;
} else {
for (var key in base64) {
base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
}
}
} else {
root.base64 = base64;
}
})(exports);
}
});
export default require_base64();
/*! Bundled license information:
base-64/base64.js:
(*! https://mths.be/base64 v1.0.0 by @mathias | MIT license *)
*/
//# sourceMappingURL=base-64.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@ -55,4 +55,29 @@ export function queryZyzgdjByRyLdhth(ldhth) { // 获取人员职业资格等级
method: 'get', method: 'get',
data: ldhth data: ldhth
}) })
}
export function queryJxkhByRyLdhth(ldhth) { // 绩效考核信息
return https({
url: '/cxc_rlzy.zb/cxcRlzyZb/listCxcRlzyJxkhByMainId',
method: 'get',
data: ldhth
})
}
export function cxcHrYgxxtj(parm) { // 获取员工信息统计
return https({
url: '/cxchrygxxtj/cxcHrYgxxtj/list',
method: 'get',
data: parm
})
}
export function cxcRyDataTongji(url, parm) { // 员工信息统计
return https({
url: url,
method: 'get',
data: parm
})
} }

View File

@ -182,6 +182,14 @@
}).then((res) => { }).then((res) => {
if (res.success) { if (res.success) {
step.value = res.result.records step.value = res.result.records
step.value = step.value.map(item => {
if (item.name === 'start') {
item.name = '开始';
} else if (item.name === 'end') {
item.name = '结束';
}
return item;
});
} }
}) })
} }

View File

@ -1,123 +1,122 @@
{ {
"name" : "数智产销", "name": "数智产销",
"appid" : "__UNI__9F097F0", "appid": "__UNI__9F097F0",
"description" : "", "description": "",
"versionName" : "1.1.1", "versionName": "1.1.4.1",
"versionCode" : 20250106, "versionCode": 20250121,
"transformPx" : false, "transformPx": false,
/* 5+App */ /* 5+App */
"app-plus" : { "app-plus": {
"usingComponents" : true, "usingComponents": true,
"nvueStyleCompiler" : "uni-app", "nvueStyleCompiler": "uni-app",
"compilerVersion" : 3, "compilerVersion": 3,
"splashscreen" : { "splashscreen": {
"alwaysShowBeforeRender" : true, "alwaysShowBeforeRender": true,
"waiting" : true, "waiting": true,
"autoclose" : true, "autoclose": true,
"delay" : 0 "delay": 0
}, },
"compatible" : { "compatible": {
"ignoreVersion" : true "ignoreVersion": true
}, },
/* */ /* */
"modules" : { "modules": {
"Geolocation" : {}, "Geolocation": {},
"Fingerprint" : {}, "Fingerprint": {},
"Camera" : {}, "Camera": {},
"Barcode" : {} "Barcode": {}
}, },
/* */ /* */
"distribute" : { "distribute": {
/* android */ /* android */
"android" : { "android": {
"permissions" : [ "permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>", "<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
] ]
}, },
/* ios */ /* ios */
"ios" : { "ios": {
"dSYMs" : false "dSYMs": false
}, },
/* SDK */ /* SDK */
"sdkConfigs" : { "sdkConfigs": {
"ad" : {}, "ad": {},
"geolocation" : { "geolocation": {
"system" : { "system": {
"__platform__" : [ "android" ] "__platform__": ["android"]
} }
} }
}, },
"icons" : { "icons": {
"android" : { "android": {
"hdpi" : "unpackage/res/icons/72x72.png", "hdpi": "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png", "xhdpi": "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png", "xxhdpi": "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png" "xxxhdpi": "unpackage/res/icons/192x192.png"
}, },
"ios" : { "ios": {
"appstore" : "unpackage/res/icons/1024x1024.png", "appstore": "unpackage/res/icons/1024x1024.png",
"ipad" : { "ipad": {
"app" : "unpackage/res/icons/76x76.png", "app": "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png", "app@2x": "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png", "notification": "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png", "proapp@2x": "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png", "settings": "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png", "spotlight": "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png" "spotlight@2x": "unpackage/res/icons/80x80.png"
}, },
"iphone" : { "iphone": {
"app@2x" : "unpackage/res/icons/120x120.png", "app@2x": "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png", "app@3x": "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png", "notification@3x": "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png", "settings@3x": "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png", "spotlight@2x": "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png" "spotlight@3x": "unpackage/res/icons/120x120.png"
} }
} }
} }
} }
}, },
/* */ /* */
"quickapp" : {}, "quickapp": {},
/* */ /* */
"mp-weixin" : { "mp-weixin": {
"appid" : "", "appid": "",
"setting" : { "setting": {
"urlCheck" : false "urlCheck": false
}, },
"usingComponents" : true "usingComponents": true
}, },
"mp-alipay" : { "mp-alipay": {
"usingComponents" : true "usingComponents": true
}, },
"mp-baidu" : { "mp-baidu": {
"usingComponents" : true "usingComponents": true
}, },
"mp-toutiao" : { "mp-toutiao": {
"usingComponents" : true "usingComponents": true
}, },
"uniStatistics" : { "uniStatistics": {
"enable" : false "enable": false
}, },
"vueVersion" : "3" "vueVersion": "3"
} }
/* */ /* */

50
package-lock.json generated
View File

@ -7,7 +7,8 @@
"dependencies": { "dependencies": {
"@dcloudio/uni-ui": "^1.5.6", "@dcloudio/uni-ui": "^1.5.6",
"base-64": "^1.0.0", "base-64": "^1.0.0",
"dayjs": "^1.11.13" "dayjs": "^1.11.13",
"echarts": "^5.6.0"
} }
}, },
"node_modules/@dcloudio/uni-ui": { "node_modules/@dcloudio/uni-ui": {
@ -25,6 +26,31 @@
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
},
"node_modules/echarts": {
"version": "5.6.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.6.1"
}
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
"license": "0BSD"
},
"node_modules/zrender": {
"version": "5.6.1",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
}
} }
}, },
"dependencies": { "dependencies": {
@ -42,6 +68,28 @@
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
},
"echarts": {
"version": "5.6.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"requires": {
"tslib": "2.3.0",
"zrender": "5.6.1"
}
},
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
},
"zrender": {
"version": "5.6.1",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
"requires": {
"tslib": "2.3.0"
}
} }
} }
} }

View File

@ -2,6 +2,7 @@
"dependencies": { "dependencies": {
"@dcloudio/uni-ui": "^1.5.6", "@dcloudio/uni-ui": "^1.5.6",
"base-64": "^1.0.0", "base-64": "^1.0.0",
"dayjs": "^1.11.13" "dayjs": "^1.11.13",
"echarts": "^5.6.0"
} }
} }

View File

@ -216,7 +216,7 @@
"style": { "style": {
"navigationBarTitleText": "台账信息", "navigationBarTitleText": "台账信息",
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
}, },
{ {
@ -224,7 +224,23 @@
"style": { "style": {
"navigationBarTitleText": "人员详细信息", "navigationBarTitleText": "人员详细信息",
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
}
},
{
"path": "pages/views/renliziyuan/renyuanxinxi/tongji",
"style": {
"navigationBarTitleText": "人员年龄分组统计信息",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/views/renliziyuan/renyuanxinxi/xbtongji",
"style": {
"navigationBarTitleText": "人员性别分组统计信息",
"navigationBarTextStyle": "white"
} }
} }
], ],

View File

@ -64,7 +64,7 @@
const { const {
proxy proxy
} = getCurrentInstance() } = getCurrentInstance()
/** 系统信息 */ /** 系统信息 */
const systemInfo = uni.getSystemInfoSync() const systemInfo = uni.getSystemInfoSync()
/**是否明文显示密码*/ /**是否明文显示密码*/

212
pages/meeting/detail.vue Normal file
View File

@ -0,0 +1,212 @@
<template>
<view :class="['content',{'gray':store.isgray==1}]">
<view class="list_box">
<view class="list">
<view class="title f-row aic jcb">
<view class="">
年度部门讨论会议
</view>
<text>1分钟前</text>
</view>
<view class="info">
<view class="f-row aic jcb">
<view class="">
会议状态
</view>
<text>待开始/已开始/已结束</text>
</view>
<view class="f-row aic jcb">
<view class="">
发起人
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议日期
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议地点
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议内容
</view>
<text>周如意</text>
</view>
<view class="">
<view class="">
参与人员
</view>
<view class="person f-row aic">
<view class="item f-col aic" v-for="item,i in 7" :key="i">
<image src="" mode=""></image>
<view class="name">
周如意
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="btn f-row aic jcb">
<view class="refuse">
拒绝
</view>
<view class="agree" @click="openpop">
同意
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue';
import {
huiyiDetailApi
} from '@/api/api.js';
import {
onLoad
} from '@dcloudio/uni-app';
import {
useStore
} from '@/store'
const store = useStore()
onLoad(() => {
huiyiDetail()
})
const huiyiDetail = () => {
huiyiDetailApi({
mainid:1
}).then((res) => {
if (res.success) {
}
}).catch((err) => {
console.log(err);
})
}
</script>
<style lang="scss" scoped>
.content{
padding-bottom: 120rpx;
}
.btn {
position: fixed;
bottom: 0;
width: 690rpx;
height: 120rpx;
background: #FFFFFF;
padding: 0 30rpx;
border-top: 1px solid #EFEFEF;
view {
width: 330rpx;
height: 88rpx;
font-size: 28rpx;
border-radius: 16rpx;
text-align: center;
line-height: 88rpx;
}
.refuse {
box-sizing: border-box;
background: #FFFFFF;
border: 2rpx solid #01508B;
color: #01508B;
}
.agree {
background: #01508B;
color: #FFFFFF;
}
}
.list_box {
.list {
padding: 30rpx;
margin-bottom: 30rpx;
.title {
border-bottom: 1px solid #efefef;
padding-bottom: 24rpx;
margin-bottom: 8rpx;
view {
font-size: 28rpx;
color: #333333;
}
text {
font-size: 28rpx;
color: #999999;
}
}
.info {
font-size: 28rpx;
color: #666666;
view {
padding-top: 16rpx;
font-size: 28rpx;
color: #666666;
}
text{
font-size: 28rpx;
color: #333333;
}
.person{
flex-wrap: wrap;
.item{
width: 16.66%;
}
image{
width: 78rpx;
height: 78rpx;
border-radius: 38rpx;
background-color: #01508B;
}
}
}
.btn {
margin-top: 30rpx;
view {
width: 300rpx;
height: 64rpx;
border-radius: 8rpx;
font-size: 28rpx;
text-align: center;
line-height: 64rpx;
}
.entrust {
background: #FFFFFF;
border: 2rpx solid #01508B;
box-sizing: border-box;
color: #01508B;
}
.handle {
background: #01508B;
color: #FFFFFF;
}
}
}
}
</style>

247
pages/meeting/index.vue Normal file
View File

@ -0,0 +1,247 @@
<template>
<view :class="{'gray':store.isgray==1}">
<customNav>
<view class="nav_box f-row aic jcb">
<view class="back f-row aic" @click="back">
<uni-icons type="left" size="20" color="#fff"></uni-icons>
</view>
<view class="search f-row aic">
<input type="text" v-model="searchKey" @confirm="search" @blur="showicon=true&&!searchKey"
@focus="showicon=false" />
<view class="f-row aic" v-if="showicon">
<image src="../../static/search.png" mode=""></image>
<text>搜索</text>
</view>
</view>
</view>
</customNav>
<view class="list_box">
<view class="list" v-for="item,i in 3" :key="i" @click="jump(`/pages/meeting/detail?id=1`)">
<view class="title f-row aic jcb">
<view class="">
年度部门讨论会议
</view>
<text>1分钟前</text>
</view>
<view class="info">
<view class="f-row aic jcb">
<view class="">
发起人
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议日期
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议地点
</view>
<text>周如意</text>
</view>
<view class="f-row aic jcb">
<view class="">
会议内容
</view>
<text>周如意</text>
</view>
</view>
<!-- <view class="btn f-row aic jcb">
<view class="entrust">
拒绝
</view>
<view class="handle">
同意
</view>
</view> -->
<view class="handled f-row">
<view class="refused">
已拒绝
</view>
<!-- <view class="agreed">
已同意
</view> -->
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue';
import {
useStore
} from '@/store'
const store = useStore()
import {
huiyilistApi
} from '@/api/api.js';
import customNav from '../../bpm/customNav.vue';
import {
onLoad
} from '@dcloudio/uni-app';
import {
beforeJump
} from '@/utils/index.js';
const showicon = ref(true)
const searchKey = ref('')
onLoad(() => {
// huiyilist()
})
const huiyilist = () => {
huiyilistApi().then((res) => {
if (res.success) {
}
}).catch((err) => {
console.log(err);
})
}
const jump = (url) => {
beforeJump(url, () => {
uni.navigateTo({
url
})
})
}
const back = () => {
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
.nav_box {
position: absolute;
bottom: 14rpx;
width: 100%;
left: 0;
}
.back {
padding: 0 30rpx;
}
.search {
position: relative;
padding-right: 30rpx;
flex: 1;
view {
position: absolute;
left: 28rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999999;
}
input {
flex: 1;
height: 72rpx;
background: #F8F8F8;
border-radius: 44rpx;
padding: 0 28rpx;
}
image {
width: 34rpx;
height: 34rpx;
margin-right: 16rpx;
}
}
.list_box {
padding: 14rpx 30rpx 0 30rpx;
margin-top: 24rpx;
.list {
background: #FFFFFF;
box-shadow: 0rpx 2rpx 4rpx 0rpx rgba(0, 0, 0, 0.5);
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
.title {
border-bottom: 1px solid #efefef;
padding-bottom: 24rpx;
margin-bottom: 8rpx;
view {
font-size: 28rpx;
color: #333333;
}
text {
font-size: 28rpx;
color: #999999;
}
}
.info {
font-size: 28rpx;
color: #666666;
view {
padding-top: 16rpx;
}
}
.btn {
margin-top: 30rpx;
view {
width: 300rpx;
height: 64rpx;
border-radius: 8rpx;
font-size: 28rpx;
text-align: center;
line-height: 64rpx;
}
.entrust {
background: #FFFFFF;
border: 2rpx solid #01508B;
box-sizing: border-box;
color: #01508B;
}
.handle {
background: #01508B;
color: #FFFFFF;
}
}
}
}
.refused {
color: #333333;
}
.agreed {
color: #01508B;
}
.handled {
justify-content: flex-end;
margin-top: 30rpx;
view {
width: 150rpx;
height: 64rpx;
background: #EFEFEF;
border-radius: 8rpx;
text-align: center;
line-height: 64rpx;
font-size: 28rpx;
}
}
</style>

View File

@ -37,7 +37,7 @@
<view class="onduty"> <view class="onduty">
<view class="title f-row aic jcb"> <view class="title f-row aic jcb">
值班信息 值班信息
<view class="more" @click="jump(`/pages/zhiban/index`)"> <view class="more" @click="jump(`/pages/views/zhongheguanli/zhiban/index`)">
查看更多 查看更多
<image src="../../static/index/back.png" mode=""></image> <image src="../../static/index/back.png" mode=""></image>
</view> </view>

View File

@ -4,6 +4,45 @@
<view style="padding: 10px 10px 10px 10px;"> <view style="padding: 10px 10px 10px 10px;">
<uni-title title="基本信息" type="h1" color="blue"></uni-title> <uni-title title="基本信息" type="h1" color="blue"></uni-title>
<yjly-row-cell :cellData="cellData" :rowDataCount="3"></yjly-row-cell> <yjly-row-cell :cellData="cellData" :rowDataCount="3"></yjly-row-cell>
<uni-title title="年度绩效考核" type="h1" color="blue"></uni-title>
<uni-row>
<uni-col :span="4">
<view class="titleStyle">
序号
</view>
</uni-col>
<uni-col :span="10">
<view class="titleStyle">
绩效考核年份
</view>
</uni-col>
<uni-col :span="10">
<view class="titleStyle">
绩效考核成绩
</view>
</uni-col>
</uni-row>
<uni-row>
<view v-for="(item,index) in jxkhxxList">
<uni-col :span="4">
<view class="dataStyle">
{{index+1}}
</view>
</uni-col>
<uni-col :span="10">
<view class="dataStyle">
{{item.nf}}
</view>
</uni-col><uni-col :span="10">
<view class="dataStyle">
{{item.khcj+"---"+item.khcj_dictText}}
</view>
</uni-col>
</view>
</uni-row>
<uni-title title="工作简历" type="h1" color="blue"></uni-title> <uni-title title="工作简历" type="h1" color="blue"></uni-title>
<uni-row> <uni-row>
<uni-col :span="4"> <uni-col :span="4">
@ -248,7 +287,8 @@
queryJtzycyByRyLdhth, queryJtzycyByRyLdhth,
queryXlxxByRyLdhth, queryXlxxByRyLdhth,
queryGbxxByRyLdhth, queryGbxxByRyLdhth,
queryZyzgdjByRyLdhth queryZyzgdjByRyLdhth,
queryJxkhByRyLdhth
} from '@/api/renyuan.js' } from '@/api/renyuan.js'
import { import {
useStore useStore
@ -266,15 +306,46 @@
const zjtzList = ref([]) // - const zjtzList = ref([]) // -
const zyjtcyList = ref([]) // - const zyjtcyList = ref([]) // -
const xlxxList = ref([]) // - const xlxxList = ref([]) // -
const gbxxList = ref([]) // -
const zyzgdjList = ref([]) // -
const jxkhxxList = ref([]) // -
function getChildTable() { function getChildTable() {
console.log(ldhth.value) 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({ queryGzjlByRyLdhth({
"ldhth": ldhth.value "ldhth": ldhth.value
}).then((res) => { }).then((res) => {
console.log(res); // console.log(res);
if (res.length > 0) { if (res.length > 0) {
gzjlList.value = res gzjlList.value = res
} }
@ -284,7 +355,7 @@
queryQzqkByRyLdhth({ queryQzqkByRyLdhth({
"ldhth": ldhth.value "ldhth": ldhth.value
}).then((res) => { }).then((res) => {
console.log(res); // console.log(res);
if (res.length > 0) { if (res.length > 0) {
zjtzList.value = res zjtzList.value = res
} }
@ -294,7 +365,7 @@
queryJtzycyByRyLdhth({ queryJtzycyByRyLdhth({
"ldhth": ldhth.value "ldhth": ldhth.value
}).then((res) => { }).then((res) => {
console.log(res); // console.log(res);
if (res.length > 0) { if (res.length > 0) {
zyjtcyList.value = res zyjtcyList.value = res
} }
@ -308,34 +379,36 @@
xlxxList.value = [] xlxxList.value = []
if (res.result.records.length > 0) { if (res.result.records.length > 0) {
var rress = res.result.records var rress = res.result.records
// console.log(rress);
for (let i = 0; i < rress.length; i++) { for (let i = 0; i < rress.length; i++) {
if (rress[i].onexl == 1) { 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 = "第一学历" rress[i].xllb = "第一学历"
xlxxList.value.push(rress[i]) xlxxList.value.push(rress[i])
} }
if (rress[i].zgxl == 1) { if (rress[i].onexl != 1 & rress[i].zgxl == 1) {
rress[i].xllb = "最高学历" rress[i].xllb = "最高学历"
xlxxList.value.push(rress[i]) xlxxList.value.push(rress[i])
} }
} }
}
console.log(xlxxList.value);
}
} }
}).catch((err) => { }).catch((err) => {
console.log(err); console.log(err);
}) })
console.log(xlxxList.value)
console.log(gzjlList.value)
console.log(zjtzList.value)
console.log(zyjtcyList.value)
} }
onLoad((e) => { function getJbxx() {
renyuanData.value = JSON.parse(decodeURIComponent(e.data));
ldhth.value = renyuanData.value.ldhth
getChildTable()
cellData.value.push({ cellData.value.push({
"title": "姓名", "title": "姓名",
"value": renyuanData.value.xm, "value": renyuanData.value.xm,
@ -412,14 +485,13 @@
}) })
cellData.value.push({ cellData.value.push({
"title": "专业技术资格", "title": "专业技术资格",
"value": renyuanData.value.zc + renyuanData.value.zcsj, "value": gbxxList.value.zc + gbxxList.value.zcsj,
"titleSpan": 5, "titleSpan": 5,
"valueSpan": 7 "valueSpan": 7
}) })
cellData.value.push({ cellData.value.push({
"title": "职业资格等级", "title": "职业资格等级",
"value": renyuanData.value.zc + renyuanData.value.zcsj, "value": zyzgdjList.value.ztgz + zyzgdjList.value.ztgzdj,
"titleSpan": 5, "titleSpan": 5,
"valueSpan": 7 "valueSpan": 7
}) })
@ -432,14 +504,14 @@
cellData.value.push({ cellData.value.push({
"title": "职务(岗位)", "title": "职务(岗位)",
"value": renyuanData.value.zc + renyuanData.value.zcsj, "value": gbxxList.value.zw,
"titleSpan": 5, "titleSpan": 5,
"valueSpan": 7 "valueSpan": 7
}) })
cellData.value.push({ cellData.value.push({
"title": "职位级别", "title": "职位级别",
"value": "", "value": gbxxList.value.zwcj,
"titleSpan": 5, "titleSpan": 5,
"valueSpan": 7 "valueSpan": 7
}) })
@ -449,6 +521,16 @@
"titleSpan": 0, "titleSpan": 0,
"valueSpan": 0 "valueSpan": 0
}) })
}
onLoad((e) => {
renyuanData.value = JSON.parse(decodeURIComponent(e.data));
ldhth.value = renyuanData.value.ldhth
getChildTable()
setTimeout(function() {
getJbxx()
}, 500);
}) })
</script> </script>
@ -496,7 +578,9 @@
vertical-align: middle; vertical-align: middle;
border-bottom: 1px solid #919191; border-bottom: 1px solid #919191;
border-left: 1px solid #919191; border-left: 1px solid #919191;
text-overflow: ellipsis; text-align: center;
word-wrap: break-word;
overflow-wrap: break-word;
} }
</style> </style>

View File

@ -3,39 +3,36 @@
<uni-card :is-shadow="false"> <uni-card :is-shadow="false">
<uni-section title="台账信息" type="line"> <uni-section title="台账信息" type="line">
<uni-card :is-shadow="false"> <uni-card :is-shadow="false">
<button type="primary" @click="getTaizhang">人员台账</button> <button type="primary" @click="toTaizhang">人员台账</button>
</uni-card> </uni-card>
</uni-section> </uni-section>
<uni-section title="统计信息" type="line"> <uni-section title="统计信息" type="line">
<uni-card :is-shadow="false"> <uni-card :is-shadow="false">
<text class="uni-body">这是一个基础卡片示例内容较少此示例展示了一个没有任何属性不带阴影的卡片</text> <button type="primary" @click="toTongji">年龄分组统计</button>
<button type="primary" @click="toXbTongji">性别分组统计</button>
</uni-card> </uni-card>
</uni-section> </uni-section>
</uni-card> </uni-card>
</view> </view>
</template> </template>
<script> <script setup>
export default { function toTaizhang() {
components: { uni.navigateTo({
url: '/pages/views/renliziyuan/renyuanxinxi/taizhang'
});
}
}, function toTongji() {
data() { uni.navigateTo({
return { url: '/pages/views/renliziyuan/renyuanxinxi/tongji'
});
} }
}, function toXbTongji() {
methods: { uni.navigateTo({
getTaizhang() { url: '/pages/views/renliziyuan/renyuanxinxi/xbtongji'
uni.navigateTo({ });
url:"/pages/views/renliziyuan/renyuanxinxi/taizhang" }
})
}
},
}
</script> </script>
<style> <style></style>
</style>

View File

@ -1,8 +1,7 @@
<template> <template>
<view> <view>
<scroll-view :scroll-y="true"> <scroll-view :scroll-y="true" style="height: 100vh;">
<uni-card> <uni-card>
<uni-title title="" type="h1" align="center"></uni-title>
<view> <view>
<uni-row> <uni-row>
<uni-col :span="11"><uni-title title="姓名 " align="left" type="h5"></uni-title></uni-col> <uni-col :span="11"><uni-title title="姓名 " align="left" type="h5"></uni-title></uni-col>
@ -10,10 +9,10 @@
type="h5"></uni-title></uni-col> type="h5"></uni-title></uni-col>
</uni-row> </uni-row>
<uni-row> <uni-row>
<uni-col :span="11"><uni-easyinput v-model="xm" focus suffixIcon="search" clearable <uni-col :span="11"><uni-easyinput v-model="xm" suffixIcon="search" clearable
placeholder="姓名模糊查询" @change="Search" @iconClick="Search" /></uni-col> placeholder="姓名模糊查询" @change="Search" @iconClick="Search" /></uni-col>
<uni-col :span="11" :push="2"> <uni-col :span="11" :push="2">
<uni-easyinput v-model="ldhth" focus suffixIcon="search" clearable placeholder="劳动合同号模糊查询" <uni-easyinput v-model="ldhth" suffixIcon="search" clearable placeholder="劳动合同号模糊查询"
@change="Search" @iconClick="Search" /> @change="Search" @iconClick="Search" />
</uni-col> </uni-col>
</uni-row> </uni-row>
@ -24,7 +23,8 @@
</uni-row> </uni-row>
<uni-row> <uni-row>
<uni-col :span="24"> <uni-col :span="24">
<trq-depart-select returnCodeOrID="orgCode" @change="departChange"></trq-depart-select> <trq-depart-select v-model="departID" returnCodeOrID="orgCode"
@change="departChange"></trq-depart-select>
</uni-col> </uni-col>
</uni-row> </uni-row>
</view> </view>
@ -34,6 +34,9 @@
:border="true" :data="ryDataList" @detail="detail"></zb-table> :border="true" :data="ryDataList" @detail="detail"></zb-table>
<uni-pagination :current="current" :pagerCount="pages" :total="total" prev-text="前一页" next-text="后一页" <uni-pagination :current="current" :pagerCount="pages" :total="total" prev-text="前一页" next-text="后一页"
:show-icon="false" @change="pagechange" /> :show-icon="false" @change="pagechange" />
<view>
<text class="example-info">当前页{{ current }}数据总量{{ total }}每页数据{{ pageSize }}</text>
</view>
</uni-card> </uni-card>
</scroll-view> </scroll-view>
@ -97,12 +100,6 @@
width: 70, width: 70,
align: 'center' align: 'center'
}, },
// {
// name: 'gzdw',
// label: '',
// align: 'center',
// width: 150
// },
{ {
name: 'xb_dictText', name: 'xb_dictText',
label: '性别', label: '性别',
@ -118,6 +115,7 @@
{ {
name: 'operation', name: 'operation',
type: 'operation', type: 'operation',
width: 60,
fixed: true, fixed: true,
label: '操作', label: '操作',
align: 'center', align: 'center',
@ -133,13 +131,12 @@
}) })
onMounted((e) => { onMounted((e) => {
departID.value = "A01A01A19" departID.value = ""
getRenyuanByDepID() getRenyuanByDepID()
}) })
function detail(record) { function detail(record) {
// console.log(record) // console.log(record)
uni.navigateTo({ uni.navigateTo({
url: "/pages/views/renliziyuan/renyuanxinxi/detail?data=" + encodeURIComponent(JSON.stringify(record)) url: "/pages/views/renliziyuan/renyuanxinxi/detail?data=" + encodeURIComponent(JSON.stringify(record))
}) })
@ -148,29 +145,47 @@
function pagechange(e) { function pagechange(e) {
current.value = e.current current.value = e.current
pageNo.value = e.current pageNo.value = e.current
getRenyuanByDepID() let params = {
}
function departChange(e) {
departID.value = e
getRenyuanByDepID()
}
function getRenyuanByDepID() {
ryDataList.value = []
let queryParm = {
pageNo: pageNo.value, pageNo: pageNo.value,
pageSize: pageSize.value, pageSize: pageSize.value,
current: current.value current: current.value
}; };
queryParm.orgCode = departID.value if (departID.value.length <= 9) {
params.orgCode = departID.value
} else {
params.jcxd_code = departID.value
}
getRenyuanByDepID(params)
}
function departChange(e, data) {
departID.value = e
let params = {
pageNo: pageNo.value,
pageSize: pageSize.value,
current: current.value
};
console.log(e)
if (e.length <= 9) {
params.orgCode = departID.value
} else {
params.jcxd_code = departID.value
}
getRenyuanByDepID(params)
}
function getRenyuanByDepID(queryParm) {
ryDataList.value = []
console.log(queryParm)
queryRenyuanByDepartID(queryParm).then((res) => { queryRenyuanByDepartID(queryParm).then((res) => {
console.log(res)
if (res.success) { if (res.success) {
ryDataList.value = res.result.records ryDataList.value = res.result.records
total.value = res.result.total total.value = res.result.total
pages.value = res.result.pages pages.value = res.result.pages
current.value = res.result.current
} }
}).catch((err) => { }).catch((err) => {
console.log(err); console.log(err);
@ -192,6 +207,7 @@
if (ldhth.value !== '') { if (ldhth.value !== '') {
queryParm.ldhth = '*' + ldhth.value + '*'; queryParm.ldhth = '*' + ldhth.value + '*';
} }
console.log(queryParm)
queryRenyuanByDepartID(queryParm).then((res) => { queryRenyuanByDepartID(queryParm).then((res) => {
if (res.success) { if (res.success) {
ryDataList.value = res.result.records ryDataList.value = res.result.records

View File

@ -0,0 +1,528 @@
<template>
<view>
<view class="container" id="top1">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24"><uni-title :title="'所选单位ID:' + orgCode" align="left" type="h4"></uni-title></uni-col>
</uni-row>
<uni-row style="margin-bottom: 20rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24">
<trq-depart-select v-model="orgCode" returnCodeOrID="orgCode" @change="departChange"></trq-depart-select>
</uni-col>
</uni-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>
</view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
<!-- 数据表格 -->
<uni-row style="margin-top: 10px; margin-left: 30rpx; margin-right: 30rpx" v-if="tableData.length > 0">
<uni-col :span="3">
<view class="titleStyle">序号</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">姓名</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">性别</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">年龄</view>
</uni-col>
<uni-col :span="6">
<view class="titleStyle">操作</view>
</uni-col>
</uni-row>
<scroll-view scroll-y :style="{ height: bottomHeight + 'px' }">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<view v-for="(item, index) in tableData">
<uni-col :span="3">
<view class="dataStyle">
{{ index + 1 }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xm }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xb_dictText }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.nl }}
</view>
</uni-col>
<uni-col :span="6">
<view class="dataStyle">
<button size="mini" type="primary" @click="detail(item)">详情</button>
</view>
</uni-col>
</view>
</uni-row>
</scroll-view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import * as echarts from 'echarts';
import { queryRenyuanByDepartID } from '@/api/renyuan.js';
//
const bottomHeight = ref(0);
//
const isLoading = ref(false);
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) {
// console.log(record)
uni.navigateTo({
url: '/pages/views/renliziyuan/renyuanxinxi/detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
// initChart
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 = [];
console.log(e);
orgCode.value = e;
try {
//
isLoading.value = true;
if (orgCode.value.length <= 6) {
console.log(123242353);
uni.showLoading({
title: '全厂数据较多,耐心等待数据加载中...',
mask: true
});
} else {
uni.showLoading({
title: '数据加载中...',
mask: true
});
}
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;
}
queryRenyuanByDepartID(params)
.then((res) => {
if (res.success) {
processData(res.result.records);
//
isLoading.value = false;
uni.hideLoading();
}
})
.catch((err) => {
console.log(err);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
});
} catch (error) {
console.log(error);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
} finally {
//
isLoading.value = false;
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(() => {
// #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>
.container {
margin: 20, 20, 20, 20rpx;
}
.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,517 @@
<template>
<view>
<view class="container" id="top1">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24"><uni-title :title="'所选单位ID:' + orgCode" align="left" type="h4"></uni-title></uni-col>
</uni-row>
<uni-row style="margin-bottom: 20rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24">
<trq-depart-select v-model="orgCode" returnCodeOrID="orgCode" @change="departChange"></trq-depart-select>
</uni-col>
</uni-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>
</view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
<!-- 数据表格 -->
<uni-row style="margin-top: 10px; margin-left: 30rpx; margin-right: 30rpx" v-if="tableData.length > 0">
<uni-col :span="3">
<view class="titleStyle">序号</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">姓名</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">性别</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">年龄</view>
</uni-col>
<uni-col :span="6">
<view class="titleStyle">操作</view>
</uni-col>
</uni-row>
<scroll-view scroll-y :style="{ height: bottomHeight + 'px' }">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<view v-for="(item, index) in tableData">
<uni-col :span="3">
<view class="dataStyle">
{{ index + 1 }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xm }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xb_dictText }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.nl }}
</view>
</uni-col>
<uni-col :span="6">
<view class="dataStyle">
<button size="mini" type="primary" @click="detail(item)">详情</button>
</view>
</uni-col>
</view>
</uni-row>
</scroll-view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import * as echarts from 'echarts';
import { cxcRyDataTongji, queryRenyuanByDepartID } from '@/api/renyuan.js';
//
const bottomHeight = ref(0);
//
const isLoading = ref(false);
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) {
// console.log(record)
uni.navigateTo({
url: '/pages/views/renliziyuan/renyuanxinxi/detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
// initChart
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 = [];
console.log(e);
orgCode.value = e;
try {
//
isLoading.value = true;
uni.showLoading({
title: '数据加载中...',
mask: true
});
let params = {
orgCode: orgCode.value
};
if (orgCode.value.length <= 9) {
params.orgCode = orgCode.value;
} else {
params.jcxd_code = orgCode.value;
}
cxcRyDataTongji(params)
.then((res) => {
console.log(res);
if (res.success) {
processData(res.result.records);
//
isLoading.value = false;
uni.hideLoading();
}
})
.catch((err) => {
console.log(err);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
});
} catch (error) {
console.log(error);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
} finally {
//
isLoading.value = false;
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(() => {
// #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>
.container {
margin: 20, 20, 20, 20rpx;
}
.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

@ -1,40 +1,54 @@
<template> <template>
<view :class="['f-col','aic',{'gray':store.isgray==1}]"> <view :class="[{'gray':store.isgray==1}]">
<picker fields="month" mode="date" @change="bindPickerChange" :value="index"> <view style="margin-top: 10px;margin-bottom: 10px;">
<view class="date">{{index}} 点击选择月份</view> <picker fields="month" mode="date" @change="bindPickerChange" :value="index">
</picker> <view class="date">{{index}} 点击选择月份</view>
<view class="info"> </picker>
<view class="info_title f-row aic"> </view>
<view class=""> <uni-row>
<uni-col :span="7">
<view class="titleStyle">
日期 日期
</view> </view>
<view class=""> </uni-col>
<uni-col :span="4">
<view class="titleStyle">
带班领导 带班领导
</view> </view>
<view class=""> </uni-col>
<uni-col :span="4">
<view class="titleStyle">
值班领导 值班领导
</view> </view>
<view class=""> </uni-col>
<uni-col :span="9">
<view class="titleStyle">
值班干部 值班干部
</view> </view>
</view> </uni-col>
<view class="data_box"> </uni-row>
<view class="data f-row aic" v-for="item,i in zhibanArr"> <uni-row>
<view class=""> <view v-for="(item,index) in zhibanArr">
<uni-col :span="7">
<view class="dataStyle">
{{item.date}} {{item.date}}
</view> </view>
<view class=""> </uni-col>
<uni-col :span="4">
<view class="dataStyle">
{{item.dbld_dictText}} {{item.dbld_dictText}}
</view> </view>
<view class=""> </uni-col><uni-col :span="4">
<view class="dataStyle">
{{item.zbld_dictText}} {{item.zbld_dictText}}
</view> </view>
<view class=""> </uni-col><uni-col :span="9">
<view class="dataStyle">
{{item.zbgbrealname}} {{item.zbgbrealname}}
</view> </view>
</view> </uni-col>
</view> </view>
</view> </uni-row>
</view> </view>
</template> </template>
@ -53,6 +67,36 @@
} from '@/store'; } from '@/store';
const store = useStore() const store = useStore()
import dayjs from 'dayjs'; import dayjs from 'dayjs';
let column = ref([{
name: 'date',
label: '日期',
width: 70,
align: 'center'
},
{
name: 'dbld_dictText',
label: '带班领导',
align: 'center',
width: 60
},
{
name: 'zbld_dictText',
label: '值班领导',
align: 'center',
width: 60
},
{
name: 'zbgbrealname',
label: '值班干部',
align: 'center',
width: 150
}
])
const zhibanArr = ref([]) const zhibanArr = ref([])
onLoad(() => { onLoad(() => {
zhibanQuery() zhibanQuery()
@ -83,36 +127,36 @@
color: #333333; color: #333333;
} }
.info {
background: #F8F8F8; .titleStyle {
border-radius: 8rpx; font-size: 12px;
color: #747474;
line-height: 25px;
height: 25px;
background: #F2F9FC;
text-align: center; text-align: center;
width: 690rpx; vertical-align: middle;
margin-top: 23rpx; border-left: 1px solid #919191;
border-bottom: 1px solid #919191;
;
}
.info_title { /* 内容样式 */
font-size: 24rpx; .dataStyle {
color: #333333; max-font-size: 14px;
padding: 24rpx 0; /* 最大字体限制 */
border-bottom: 1px solid #EFEFEF; min-font-size: 10px;
/* 最小字体限制 */
font-size: 12px;
color: #00007f;
line-height: 25px;
height: 25px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-overflow: ellipsis;
view {
flex: 1;
}
}
.data_box {
font-size: 24rpx;
padding-bottom: 24rpx;
color: #888888;
.data {
margin-top: 23rpx;
view {
flex: 1;
}
}
}
} }
</style> </style>

View File

@ -0,0 +1,205 @@
## 0.9.82024-12-20
- fix: 修复 APP 无法放大问题
## 0.9.72024-12-02
- feat: uniapp 增加`landscape`,当`landscape`为`true`时旋转90deg达到横屏效果。
- feat: 支持uniapp x 微信小程序
## 0.9.62024-07-23
- fix: 修复 uni is not defined
## 0.9.52024-07-19
- chore: 鸿蒙`measureText`为异步,异步字体不正常,使用模拟方式。
## 0.9.42024-07-18
- chore: 更新文档
## 0.9.32024-07-16
- feat: 鸿蒙 canvas 事件缺失,待官方修复,如何在鸿蒙使用请看文档`常见问题 vue3`
## 0.9.22024-07-12
- chore: 删除多余文件
## 0.9.12024-07-12
- fix: 修复 安卓5不显示图表问题
## 0.9.02024-06-13
- chore: 合并nvue和uvue
## 0.8.92024-05-19
- chore: 更新文档
## 0.8.82024-05-13
- chore: 更新文档和uvue示例
## 0.8.72024-04-26
- fix: uniapp x需要HBX 4.13以上
## 0.8.62024-04-10
- feat: 支持 uniapp x ios
## 0.8.52024-04-03
- fix: 修复 nvue `reset`传值不生效问题
- feat: 支持 uniapp x web
## 0.8.42024-01-27
- chore: 更新文档
## 0.8.32024-01-21
- chore: 更新文档
## 0.8.22024-01-21
- feat: 支持 `uvue`
## 0.8.12023-08-24
- fix: app 的`touch`事件为`object` 导致无法显示 `tooltip`
## 0.8.02023-08-22
- fix: 离屏 报错问题
- fix: 微信小程序PC无法使用事件
- chore: 更新文档
## 0.7.92023-07-29
- chore: 更新文档
## 0.7.82023-07-29
- fix: 离屏 报错问题
## 0.7.72023-07-27
- chore: 更新文档
- chore: lime-echart 里的示例使用自定tooltips
- feat: 对支持离屏的使用离屏创建(微信、字节、支付宝)
## 0.7.62023-06-30
- fix: vue3 报`width`的错
## 0.7.52023-05-25
- chore: 更新文档 和 demo, 使用`lime-echart`这个标签即可查看示例
## 0.7.42023-05-22
- chore: 增加关于钉钉小程序上传时提示安全问题的说明及修改建议
## 0.7.32023-05-16
- chore: 更新 vue3 非微信小程序平台可能缺少`wx`的说明
## 0.7.22023-05-16
- chore: 更新 vue3 非微信小程序平台的可以缺少`wx`的说明
## 0.7.12023-04-26
- chore: 更新demo使用`lime-echart`这个标签即可查看示例
- chore微信小程序的`tooltip`文字有阴影,怀疑是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
## 0.7.02023-04-24
- fix: 修复`setAttribute is not a function`
## 0.6.92023-04-15
- chore: 更新文档vue3请使用echarts esm的包
## 0.6.82023-03-22
- feat: mac pc无法使用canvas 2d
## 0.6.72023-03-17
- feat: 更新文档
## 0.6.62023-03-17
- feat: 微信小程序PC已经支持canvas 2d故去掉判断PC
## 0.6.52022-11-03
- fix: 某些手机touches为对象导致无法交互。
## 0.6.42022-10-28
- fix: 优化点击事件的触发条件
## 0.6.32022-10-26
- fix: 修复 dataZoom 拖动问题
## 0.6.22022-10-23
- fix: 修复 飞书小程序 尺寸问题
## 0.6.12022-10-19
- fix: 修复 PC mousewheel 事件 鼠标位置不准确的BUG不兼容火狐
- feat: showLoading 增加传参
## 0.6.02022-09-16
- feat: 增加PC的mousewheel事件
## 0.5.42022-09-16
- fix: 修复 nvue 动态数据不显示问题
## 0.5.32022-09-16
- feat: 增加enableHover属性 在PC端时当鼠标进入显示tooltip不必按下。
- chore: 更新文档
## 0.5.22022-09-16
- feat: 增加enableHover属性 在PC端时当鼠标进入显示tooltip不必按下。
## 0.5.12022-09-16
- fix: 修复nvue报错
## 0.5.02022-09-15
- feat: init(echarts, theme?:string, opts?:{}, callback: function(chart))
## 0.4.82022-09-11
- feat: 增加 @finished
## 0.4.72022-08-24
- chore: 去掉 stylus
## 0.4.62022-08-24
- feat: 增加 beforeDelay
## 0.4.52022-08-12
- chore: 更新文档
## 0.4.42022-08-12
- fix: 修复 resize 无参数时报错
## 0.4.32022-08-07
# 评论有说本插件对新手不友好,让我做不好就不要发出来。 还有的说跟官网一样,发出来做什么,给我整无语了。
# 所以在此提醒一下准备要下载的你,如果你从未使用过 echarts 请不要下载 或 谨慎下载。
# 如果你确认要下载麻烦看完文档。还有请注意插件是让echarts在uniapp能运行API 配置请自行去官网查阅!
# 如果你不会echarts 但又需要图表,市场上有个很优秀的图表插件 uchart 你可以去使用这款插件uchart的作者人很好也热情。
# 每个人都有自己的本职工作,如果你能力强可以自行兼容,如果使用了他人的插件也麻烦尊重他人的成果和劳动时间。谢谢。
# 为了心情愉悦,本人已经使用插件屏蔽差评。
- chore: 更新文档
## 0.4.22022-07-20
- feat: 增加 resize
## 0.4.12022-06-07
- fix: 修复 canvasToTempFilePath 不生效问题
## 0.4.02022-06-04
- chore 为了词云 增加一个canvas 标签
- 词云下载地址[echart-wordcloud](https://ext.dcloud.net.cn/plugin?id=8430)
## 0.3.92022-06-02
- chore: 更新文档
- tips: lines 不支持 `trailLength`
## 0.3.82022-05-31
- fix: 修复 因mouse事件冲突tooltip跳动问题
## 0.3.72022-05-26
- chore: 更新文档
- chore: 设置默认宽高300px
- fix: 修复 vue3 微信小程序 拖影BUG
- chore: 支持PC
## 0.3.52022-04-28
- chore: 更新使用方式
- 🔔 必须使用hbuilderx 3.4.8-alpha以上
## 0.3.42021-08-03
- chore: 增加 setOption的参数值
## 0.3.32021-07-22
- fix: 修复 径向渐变报错的问题
## 0.3.22021-07-09
- chore: 统一命名规范,无须主动引入组件
## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
## 0.3.12021-06-21
- fix: 修复 app-nvue ios is-enable 无效的问题
## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
## 0.3.02021-06-14
- fix: 修复 头条系小程序 2d 报 JSON.stringify 的问题
- 目前 头条系小程序 2d 无法在开发工具上预览划动图表页面无法滚动axisLabel 字体颜色无法更改建议使用非2d。
## 0.2.92021-06-06
- fix: 修复 头条系小程序 2d 放大的BUG
- 头条系小程序 2d 无法在开发工具上预览,也存在划动图表页面无法滚动的问题。
## [代码示例http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
## 0.2.82021-05-19
- fix: 修复 微信小程序 PC 显示过大的问题
## 0.2.72021-05-19
- fix: 修复 微信小程序 PC 不显示问题
## [代码示例http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
## 0.2.62021-05-14
- feat: 支持 `image`
- feat: props 增加 `ec.clear`,更新时是否先删除图表样式
- feat: props 增加 `isDisableScroll` ,触摸图表时是否禁止页面滚动
- feat: props 增加 `webviewStyles` webview 的样式, 仅nvue有效
## 0.2.52021-05-13
- docs: 插件用到了css 预编译器 [stylus](https://ext.dcloud.net.cn/plugin?name=compile-stylus) 请安装它
## 0.2.42021-05-12
- fix: 修复 百度平台 多个图表ctx 和 渐变色 bug
- ## [代码示例http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
## 0.2.32021-05-10
- feat: 增加 `canvasToTempFilePath` 方法,用于生成图片
```js
this.$refs.chart.canvasToTempFilePath({success: (res) => {
console.log('tempFilePath:', res.tempFilePath)
}})
```
## 0.2.22021-05-10
- feat: 增加 `dispose` 方法,用于销毁实例
- feat: 增加 `isClickable` 是否派发点击
- feat: 实验性的支持 `nvue` 使用要慎重考虑
- ## [代码示例http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
## 0.2.12021-05-06
- fix修复 微信小程序 json 报错
- chore: `reset` 更改为 `setChart`
- feat: 增加 `isEnable` 开启初始化 启用这个后 无须再使用`init`方法
```html
<l-echart ref="chart" is-enable />
```
```js
// 显示加载
this.$refs.chart.showLoading()
// 使用实例回调
this.$refs.chart.setChart(chart => ...code)
// 直接设置图表配置
this.$refs.chart.setOption(data)
```
## 0.2.02021-05-05
- fix修复 头条 百度 偏移的问题
- docs: 更新文档
## [代码示例http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
## 0.1.02021-05-02
- chore: 第一次上传,基本全端兼容,使用方法与官网一致。
- 已知BUG非2d 无法使用背景色,已反馈官方
- 已知BUG头条 百度 有许些偏移
- 后期计划兼容nvue

View File

@ -0,0 +1,395 @@
import {getDeviceInfo} from './utils';
const cacheChart = {}
const fontSizeReg = /([\d\.]+)px/;
class EventEmit {
constructor() {
this.__events = {};
}
on(type, listener) {
if (!type || !listener) {
return;
}
const events = this.__events[type] || [];
events.push(listener);
this.__events[type] = events;
}
emit(type, e) {
if (type.constructor === Object) {
e = type;
type = e && e.type;
}
if (!type) {
return;
}
const events = this.__events[type];
if (!events || !events.length) {
return;
}
events.forEach((listener) => {
listener.call(this, e);
});
}
off(type, listener) {
const __events = this.__events;
const events = __events[type];
if (!events || !events.length) {
return;
}
if (!listener) {
delete __events[type];
return;
}
for (let i = 0, len = events.length; i < len; i++) {
if (events[i] === listener) {
events.splice(i, 1);
i--;
}
}
}
}
class Image {
constructor() {
this.currentSrc = null
this.naturalHeight = 0
this.naturalWidth = 0
this.width = 0
this.height = 0
this.tagName = 'IMG'
}
set src(src) {
this.currentSrc = src
uni.getImageInfo({
src,
success: (res) => {
this.naturalWidth = this.width = res.width
this.naturalHeight = this.height = res.height
this.onload()
},
fail: () => {
this.onerror()
}
})
}
get src() {
return this.currentSrc
}
}
class OffscreenCanvas {
constructor(ctx, com, canvasId) {
this.tagName = 'canvas'
this.com = com
this.canvasId = canvasId
this.ctx = ctx
}
set width(w) {
this.com.offscreenWidth = w
}
set height(h) {
this.com.offscreenHeight = h
}
get width() {
return this.com.offscreenWidth || 0
}
get height() {
return this.com.offscreenHeight || 0
}
getContext(type) {
return this.ctx
}
getImageData() {
return new Promise((resolve, reject) => {
this.com.$nextTick(() => {
uni.canvasGetImageData({
x:0,
y:0,
width: this.com.offscreenWidth,
height: this.com.offscreenHeight,
canvasId: this.canvasId,
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
},
}, this.com)
})
})
}
}
export class Canvas {
constructor(ctx, com, isNew, canvasNode={}) {
cacheChart[com.canvasId] = {ctx}
this.canvasId = com.canvasId;
this.chart = null;
this.isNew = isNew
this.tagName = 'canvas'
this.canvasNode = canvasNode;
this.com = com;
if (!isNew) {
this._initStyle(ctx)
}
this._initEvent();
this._ee = new EventEmit()
}
getContext(type) {
if (type === '2d') {
return this.ctx;
}
}
setAttribute(key, value) {
if(key === 'aria-label') {
this.com['ariaLabel'] = value
}
}
setChart(chart) {
this.chart = chart;
}
createOffscreenCanvas(param){
if(!this.children) {
this.com.isOffscreenCanvas = true
this.com.offscreenWidth = param.width||300
this.com.offscreenHeight = param.height||300
const com = this.com
const canvasId = this.com.offscreenCanvasId
const context = uni.createCanvasContext(canvasId, this.com)
this._initStyle(context)
this.children = new OffscreenCanvas(context, com, canvasId)
}
return this.children
}
appendChild(child) {
console.log('child', child)
}
dispatchEvent(type, e) {
if(typeof type == 'object') {
this._ee.emit(type.type, type);
} else {
this._ee.emit(type, e);
}
return true
}
attachEvent() {
}
detachEvent() {
}
addEventListener(type, listener) {
this._ee.on(type, listener)
}
removeEventListener(type, listener) {
this._ee.off(type, listener)
}
_initCanvas(zrender, ctx) {
// zrender.util.getContext = function() {
// return ctx;
// };
// zrender.util.$override('measureText', function(text, font) {
// ctx.font = font || '12px sans-serif';
// return ctx.measureText(text, font);
// });
}
_initStyle(ctx, child) {
const styles = [
'fillStyle',
'strokeStyle',
'fontSize',
'globalAlpha',
'opacity',
'textAlign',
'textBaseline',
'shadow',
'lineWidth',
'lineCap',
'lineJoin',
'lineDash',
'miterLimit',
// #ifdef H5
'font',
// #endif
];
const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
styles.forEach(style => {
Object.defineProperty(ctx, style, {
set: value => {
// #ifdef H5
if (style === 'font' && fontSizeReg.test(value)) {
const match = fontSizeReg.exec(value);
ctx.setFontSize(match[1]);
return;
}
// #endif
if (style === 'opacity') {
ctx.setGlobalAlpha(value)
return;
}
if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
// #ifdef H5 || APP-PLUS || MP-BAIDU
if(typeof value == 'object') {
if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
}
return
}
// #endif
// #ifdef MP-TOUTIAO
if(colorReg.test(value)) {
value = value.replace(colorReg, '#$1$1$2$2$3$3')
}
// #endif
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
}
}
});
});
if(!this.isNew && !child) {
ctx.uniDrawImage = ctx.drawImage
ctx.drawImage = (...a) => {
a[0] = a[0].src
ctx.uniDrawImage(...a)
}
}
if(!ctx.createRadialGradient) {
ctx.createRadialGradient = function() {
return ctx.createCircularGradient(...[...arguments].slice(-3))
};
}
// 字节不支持
if (!ctx.strokeText) {
ctx.strokeText = (...a) => {
ctx.fillText(...a)
}
}
// 钉钉不支持 , 鸿蒙是异步
if (!ctx.measureText || getDeviceInfo().osName == 'harmonyos') {
ctx._measureText = ctx.measureText
const strLen = (str) => {
let len = 0;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
len++;
} else {
len += 2;
}
}
return len;
}
ctx.measureText = (text, font) => {
let fontSize = ctx?.state?.fontSize || 12;
if (font) {
fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
}
fontSize /= 2;
let isBold = fontSize >= 16;
const widthFactor = isBold ? 1.3 : 1;
// ctx._measureText(text, (res) => {})
return {
width: strLen(text) * fontSize * widthFactor
};
}
}
}
_initEvent(e) {
this.event = {};
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown'
}, {
wxName: 'touchMove',
ecName: 'mousemove'
}, {
wxName: 'touchEnd',
ecName: 'mouseup'
}, {
wxName: 'touchEnd',
ecName: 'click'
}];
eventNames.forEach(name => {
this.event[name.wxName] = e => {
const touch = e.touches[0];
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y
});
};
});
}
set width(w) {
this.canvasNode.width = w
}
set height(h) {
this.canvasNode.height = h
}
get width() {
return this.canvasNode.width || 0
}
get height() {
return this.canvasNode.height || 0
}
get ctx() {
return cacheChart[this.canvasId]['ctx'] || null
}
set chart(chart) {
cacheChart[this.canvasId]['chart'] = chart
}
get chart() {
return cacheChart[this.canvasId]['chart'] || null
}
}
export function dispatch(name, {x,y, wheelDelta}) {
this.dispatch(name, {
zrX: x,
zrY: y,
zrDelta: wheelDelta,
preventDefault: () => {},
stopPropagation: () =>{}
});
}
export function setCanvasCreator(echarts, {canvas, node}) {
// echarts.setCanvasCreator(() => canvas);
if(echarts && !echarts.registerPreprocessor) {
return console.warn('echarts 版本不对或未传入echartsvue3请使用esm格式')
}
echarts.registerPreprocessor(option => {
if (option && option.series) {
if (option.series.length > 0) {
option.series.forEach(series => {
series.progressive = 0;
});
} else if (typeof option.series === 'object') {
option.series.progressive = 0;
}
}
});
function loadImage(src, onload, onerror) {
let img = null
if(node && node.createImage) {
img = node.createImage()
img.onload = onload.bind(img);
img.onerror = onerror.bind(img);
img.src = src;
return img
} else {
img = new Image()
img.onload = onload.bind(img)
img.onerror = onerror.bind(img);
img.src = src
return img
}
}
if(echarts.setPlatformAPI) {
echarts.setPlatformAPI({
loadImage: canvas.setChart ? loadImage : null,
createCanvas(){
const key = 'createOffscreenCanvas'
return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
}
})
}
}

View File

@ -0,0 +1,310 @@
<template>
<!-- #ifdef APP -->
<web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]"
:webview-styles="[webviewStyles]" src="/uni_modules/lime-echart/static/uvue.html?v=10112">
</web-view>
<!-- #endif -->
<!-- #ifdef H5 -->
<div class="lime-echart" ref="chartRef"></div>
<!-- #endif -->
<!-- #ifndef H5 || APP-->
<canvas class="lime-echart" :id="canvasid" :canvas-id="canvasid"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend">
</canvas>
<!-- #endif -->
</template>
<script lang="uts" setup>
// @ts-nocheck
import { getCurrentInstance, nextTick } from "vue";
import { Echarts } from './uvue';
// #ifdef WEB
import { dispatch } from './canvas';
// #endif
// #ifndef APP || WEB
import {Canvas, setCanvasCreator, dispatch} from './canvas';
import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
// #endif
type EchartsResolve = (value : Echarts) => void
defineOptions({
name: 'l-echart'
})
const emits = defineEmits(['finished'])
const props = defineProps({
// #ifdef APP
webviewStyles: {
type: Object
},
customStyle: {
type: Object
},
// #endif
// #ifndef APP
webviewStyles: {
type: Object
},
customStyle: {
type: [String, Object]
},
// #endif
isDisableScroll: {
type: Boolean,
default: false
},
isClickable: {
type: Boolean,
default: true
},
enableHover: {
type: Boolean,
default: false
},
beforeDelay: {
type: Number,
default: 30
}
})
const instance = getCurrentInstance()!;
const canvasid = `lime-echart-${instance.uid}`
const finished = ref(false)
const map = [] as EchartsResolve[]
const callbackMap = [] as EchartsResolve[]
// let context = null as UniWebViewElement | null
let chart = null as Echarts | null
let chartRef = ref<UniWebViewElement | null>(null)
const trigger = () => {
// #ifdef APP
if (finished.value) {
if (chart == null) {
chart = new Echarts(chartRef.value!)
}
while (map.length > 0) {
const resolve = map.pop() as EchartsResolve
resolve(chart!)
}
}
// #endif
// #ifndef APP
while (map.length > 0) {
if(chart != null){
const resolve = map.pop() as EchartsResolve
resolve(chart!)
}
}
// #endif
if(chart != null){
while(callbackMap.length > 0){
const callback = callbackMap.pop() as EchartsResolve
callback(chart!)
}
}
}
// #ifdef APP
const loaded = (event : UniWebViewLoadEvent) => {
event.stopPropagation()
event.preventDefault()
finished.value = true
trigger()
emits('finished')
}
// #endif
const _next = () : boolean => {
if (chart == null) {
console.warn(`组件还未初始化,请先使用 init`)
return true
}
return false
}
const setOption = (option : UTSJSONObject) => {
if (_next()) return
chart!.setOption(option);
}
const showLoading = () => {
if (_next()) return
chart!.showLoading();
}
const hideLoading = () => {
if (_next()) return
chart!.hideLoading();
}
const clear = () => {
if (_next()) return
chart!.clear();
}
const dispose = () => {
if (_next()) return
chart!.dispose();
}
const resize = (size : UTSJSONObject) => {
if (_next()) return
chart!.resize(size);
}
const canvasToTempFilePath = (opt : UTSJSONObject) => {
if (_next()) return
chart!.canvasToTempFilePath(opt);
}
// #ifdef APP
function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
if(callback!=null){
callbackMap.push(callback)
}
return new Promise<Echarts>((resolve) => {
map.push(resolve)
trigger()
})
}
// #endif
// #ifndef APP
// #ifndef WEB
let use2dCanvas = canIUseCanvas2d()
const getContext = async () =>{
return getRect(`#${canvasid}`, {context: instance.proxy!, type: use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
if(res) {
let dpr = uni.getWindowInfo().pixelRatio
let {width, height, node} = res
let canvas;
if(node) {
const ctx = node.getContext('2d');
canvas = new Canvas(ctx, instance.proxy, true, node);
} else {
const ctx = uni.createCanvasContext(canvasid, instance.proxy);
canvas = new Canvas(ctx, instance.proxy, false);
}
return { canvas, width, height, devicePixelRatio: dpr, node };
} else {
return {}
}
})
}
// #endif
const getTouch = (e) => {
const touches = e.touches[0]
// #ifdef WEB
const rect = chart!.getZr().dom.getBoundingClientRect();
const touch = {
x: touches.clientX - rect.left,
y: touches.clientY - rect.top
}
// #endif
// #ifndef WEB
const touch = {
x: touches.x,
y: touches.y
}
// #endif
return touch
}
const touchstart = (e) => {
if(chart == null) return
const handler = chart.getZr().handler;
const touch = getTouch(e)
dispatch.call(handler, 'mousedown', touch)
dispatch.call(handler, 'click', touch)
}
const touchmove = (e) => {
if(chart == null) return
const handler = chart.getZr().handler;
const touch = getTouch(e)
dispatch.call(handler, 'mousemove', touch)
// const rect = chart.getZr().dom.getBoundingClientRect()
// handler.dispatch('mousemove', {
// zrX: e.touches[0].clientX - rect.left,
// zrY: e.touches[0].clientY - rect.top
// })
}
const touchend = (e) => {
if(chart == null) return
const handler = chart.getZr().handler;
const touch = {
x: 999999999,
y: 999999999
}
dispatch.call(handler, 'mousemove', touch)
dispatch.call(handler, 'touchend', touch)
}
async function init(echarts: any, ...args: any[]): Promise<Echarts>{
if(echarts == null){
console.error('请确保已经引入了 ECharts 库');
return Promise.reject('请确保已经引入了 ECharts 库');
}
let theme:string|null=null
let opts={}
let callback:Function|null=null;
args.forEach(item =>{
if(typeof item === 'function') {
callback = item
} else if(['string'].includes(typeof item)){
theme = item
} else if(typeof item === 'object'){
opts = item
}
})
// #ifdef WEB
chart = echarts.init(chartRef.value, theme, opts)
window.addEventListener('touchstart', touchstart)
window.addEventListener('touchmove', touchmove)
window.addEventListener('touchend', touchend)
// #endif
// #ifndef WEB
let config = await getContext();
setCanvasCreator(echarts, config)
chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
// #endif
console.log('chart', chart)
if(callback!=null && typeof callback == 'function'){
callbackMap.push(callback)
}
return new Promise<Echarts>((resolve) => {
map.push(resolve)
trigger()
})
}
onMounted(()=>{
nextTick(()=>{
finished.value = true
trigger()
emits('finished')
})
})
onUnmounted(()=>{
// #ifdef WEB
window.removeEventListener('touchstart', touchstart)
window.removeEventListener('touchmove', touchmove)
window.removeEventListener('touchend', touchend)
// #endif
})
// #endif
defineExpose({
init,
setOption,
showLoading,
hideLoading,
clear,
dispose,
resize,
canvasToTempFilePath
})
</script>
<style lang="scss">
.lime-echart {
flex: 1;
width: 100%;
}
</style>

View File

@ -0,0 +1,530 @@
<template>
<view class="lime-echart" :style="[customStyle]" v-if="canvasId" ref="limeEchart" :aria-label="ariaLabel">
<!-- #ifndef APP-NVUE -->
<canvas
class="lime-echart__canvas"
v-if="use2dCanvas"
type="2d"
:id="canvasId"
:style="canvasStyle"
:disable-scroll="isDisableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
<canvas
class="lime-echart__canvas"
v-else
:width="nodeWidth"
:height="nodeHeight"
:style="canvasStyle"
:canvas-id="canvasId"
:id="canvasId"
:disable-scroll="isDisableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
<view class="lime-echart__mask"
v-if="isPC"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd">
</view>
<canvas v-if="isOffscreenCanvas" :style="offscreenStyle" :canvas-id="offscreenCanvasId"></canvas>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<web-view
class="lime-echart__canvas"
:id="canvasId"
:style="canvasStyle"
:webview-styles="webviewStyles"
ref="webview"
src="/uni_modules/lime-echart/static/uvue.html?v=1"
@pagefinish="finished = true"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<script>
// @ts-nocheck
// #ifndef APP-NVUE
import {Canvas, setCanvasCreator, dispatch} from './canvas';
import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect, getDeviceInfo} from './utils';
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from './utils';
import {Echarts} from './nvue'
// #endif
const charts = {}
const echartsObj = {}
/**
* LimeChart 图表
* @description 全端兼容的eCharts
* @tutorial https://ext.dcloud.net.cn/plugin?id=4899
* @property {String} customStyle 自定义样式
* @property {String} type 指定 canvas 类型
* @value 2d 使用canvas 2d部分小程序支持
* @value '' 使用原生canvas会有层级问题
* @value bottom right 不缩放图片只显示图片的右下边区域
* @property {Boolean} isDisableScroll
* @property {number} beforeDelay = [30] 延迟初始化 (毫秒)
* @property {Boolean} enableHover PC端使用鼠标悬浮
* @event {Function} finished 加载完成触发
*/
export default {
name: 'lime-echart',
props: {
// #ifdef MP-WEIXIN || MP-TOUTIAO
type: {
type: String,
default: '2d'
},
// #endif
// #ifdef APP-NVUE
webviewStyles: Object,
// hybrid: Boolean,
// #endif
customStyle: String,
isDisableScroll: Boolean,
isClickable: {
type: Boolean,
default: true
},
enableHover: Boolean,
beforeDelay: {
type: Number,
default: 30
},
landscape: Boolean
},
data() {
return {
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: true,
// #endif
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: false,
// #endif
ariaLabel: '图表',
width: null,
height: null,
nodeWidth: null,
nodeHeight: null,
// canvasNode: null,
config: {},
inited: false,
finished: false,
file: '',
platform: '',
isPC: false,
isDown: false,
isOffscreenCanvas: false,
offscreenWidth: 0,
offscreenHeight: 0,
};
},
computed: {
rootStyle() {
if(this.landscape) {
return `transform: translate(-50%,-50%) rotate(90deg); top:50%; left:50%;`
}
},
canvasId() {
return `lime-echart${this._ && this._.uid || this._uid}`
},
offscreenCanvasId() {
return `${this.canvasId}_offscreen`
},
offscreenStyle() {
return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
},
canvasStyle() {
return this.rootStyle + (this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : '')
}
},
// #ifndef VUE3
beforeDestroy() {
this.clear()
this.dispose()
// #ifdef H5
if(this.isPC) {
document.removeEventListener('mousewheel', this.mousewheel)
}
// #endif
},
// #endif
// #ifdef VUE3
beforeUnmount() {
this.clear()
this.dispose()
// #ifdef H5
if(this.isPC) {
document.removeEventListener('mousewheel', this.mousewheel)
}
// #endif
},
// #endif
created() {
// #ifdef H5
if(!('ontouchstart' in window)) {
this.isPC = true
document.addEventListener('mousewheel', this.mousewheel)
}
// #endif
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
// const { platform } = uni.getSystemInfoSync();
const { platform } = getDeviceInfo();
this.isPC = /windows/i.test(platform)
// #endif
this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
},
mounted() {
this.$nextTick(() => {
this.$emit('finished')
})
},
methods: {
// #ifdef APP-NVUE
onMessage(e) {
const detail = e?.detail?.data[0] || null;
const data = detail?.data
const key = detail?.event
const options = data?.options
const event = data?.event
const file = detail?.file
if (key == 'log' && data) {
console.log(data)
}
if(event) {
this.chart.dispatchAction(event.replace(/"/g,''), options)
}
if(file) {
thie.file = file
}
},
// #endif
setChart(callback) {
if(!this.chart) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
if(typeof callback === 'function' && this.chart) {
callback(this.chart);
}
// #ifdef APP-NVUE
if(typeof callback === 'function') {
this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`);
}
// #endif
},
setOption() {
if (!this.chart || !this.chart.setOption) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
this.chart.setOption(...arguments);
},
showLoading() {
if(this.chart) {
this.chart.showLoading(...arguments)
}
},
hideLoading() {
if(this.chart) {
this.chart.hideLoading()
}
},
clear() {
if(this.chart) {
this.chart.clear()
}
},
dispose() {
if(this.chart) {
this.chart.dispose()
}
},
resize(size) {
if(size && size.width && size.height) {
this.height = size.height
this.width = size.width
if(this.chart) {this.chart.resize(size)}
} else {
this.$nextTick(() => {
uni.createSelectorQuery()
.in(this)
.select(`.lime-echart`)
.boundingClientRect()
.exec(res => {
if (res) {
let { width, height } = res[0];
this.width = width = width || 300;
this.height = height = height || 300;
this.chart.resize({width, height})
}
});
})
}
},
canvasToTempFilePath(args = {}) {
// #ifndef APP-NVUE
const { use2dCanvas, canvasId } = this;
return new Promise((resolve, reject) => {
const copyArgs = Object.assign({
canvasId,
success: resolve,
fail: reject
}, args);
if (use2dCanvas) {
delete copyArgs.canvasId;
copyArgs.canvas = this.canvasNode;
}
uni.canvasToTempFilePath(copyArgs, this);
});
// #endif
// #ifdef APP-NVUE
this.file = ''
this.$refs.webview.evalJs(`canvasToTempFilePath()`);
return new Promise((resolve, reject) => {
this.$watch('file', async (file) => {
if(file) {
const tempFilePath = await base64ToPath(file)
resolve(args.success({tempFilePath}))
} else {
reject(args.fail({error: ``}))
}
})
})
// #endif
},
async init(echarts, ...args) {
// #ifndef APP-NVUE
if(args && args.length == 0 && !echarts) {
console.error('缺少参数init(echarts, theme?:string, opts?: object, callback?: function)')
return
}
// #endif
let theme=null,opts={},callback;
Array.from(arguments).forEach(item => {
if(typeof item === 'function') {
callback = item
}
if(['string'].includes(typeof item)) {
theme = item
}
if(typeof item === 'object') {
opts = item
}
})
if(this.beforeDelay) {
await sleep(this.beforeDelay)
}
let config = await this.getContext();
// #ifndef APP-NVUE
setCanvasCreator(echarts, config)
try {
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
if(typeof callback === 'function') {
callback(this.chart)
} else {
return this.chart
}
} catch(e) {
console.error(e.messges)
return null
}
// #endif
// #ifdef APP-NVUE
this.chart = new Echarts(this.$refs.webview)
this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
if(callback) {
callback(this.chart)
} else {
return this.chart
}
// #endif
},
getContext() {
// #ifdef APP-NVUE
if(this.finished) {
return Promise.resolve(this.finished)
}
return new Promise(resolve => {
this.$watch('finished', (val) => {
if(val) {
resolve(this.finished)
}
})
})
// #endif
// #ifndef APP-NVUE
return getRect(`#${this.canvasId}`, {context: this, type: this.use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
if(res) {
let dpr = devicePixelRatio
let {width, height, node} = res
let canvas;
this.width = width = width || 300;
this.height = height = height || 300;
if(node) {
const ctx = node.getContext('2d');
canvas = new Canvas(ctx, this, true, node);
this.canvasNode = node
} else {
// #ifdef MP-TOUTIAO
dpr = !this.isPC ? devicePixelRatio : 1// 1.25
// #endif
// #ifndef MP-ALIPAY || MP-TOUTIAO
dpr = this.isPC ? devicePixelRatio : 1
// #endif
// #ifdef MP-ALIPAY || MP-LARK
dpr = devicePixelRatio
// #endif
// #ifdef WEB
dpr = 1
// #endif
this.rect = res
this.nodeWidth = width * dpr;
this.nodeHeight = height * dpr;
const ctx = uni.createCanvasContext(this.canvasId, this);
canvas = new Canvas(ctx, this, false);
}
return { canvas, width, height, devicePixelRatio: dpr, node };
} else {
return {}
}
})
// #endif
},
// #ifndef APP-NVUE
getRelative(e, touches) {
let { clientX, clientY } = e
if(!(clientX && clientY) && touches && touches[0]) {
clientX = touches[0].clientX
clientY = touches[0].clientY
}
return {x: clientX - this.rect.left, y: clientY - this.rect.top, wheelDelta: e.wheelDelta || 0}
},
getTouch(e, touches) {
const {x} = touches && touches[0] || {}
const touch = x ? touches[0] : this.getRelative(e, touches);
if(this.landscape) {
[touch.x, touch.y] = [touch.y, this.height - touch.x]
}
return touch;
},
touchStart(e) {
this.isDown = true
const next = () => {
const touches = convertTouchesToArray(e.touches)
if(this.chart) {
const touch = this.getTouch(e, touches)
this.startX = touch.x
this.startY = touch.y
this.startT = new Date()
const handler = this.chart.getZr().handler;
dispatch.call(handler, 'mousedown', touch)
dispatch.call(handler, 'mousemove', touch)
handler.processGesture(wrapTouch(e), 'start');
clearTimeout(this.endTimer);
}
}
if(this.isPC) {
getRect(`#${this.canvasId}`, {context: this}).then(res => {
this.rect = res
next()
})
return
}
next()
},
touchMove(e) {
if(this.isPC && this.enableHover && !this.isDown) {this.isDown = true}
const touches = convertTouchesToArray(e.touches)
if (this.chart && this.isDown) {
const handler = this.chart.getZr().handler;
dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
handler.processGesture(wrapTouch(e), 'change');
}
},
touchEnd(e) {
this.isDown = false
if (this.chart) {
const touches = convertTouchesToArray(e.changedTouches)
const {x} = touches && touches[0] || {}
const touch = (x ? touches[0] : this.getRelative(e, touches)) || {};
if(this.landscape) {
[touch.x, touch.y] = [touch.y, this.height - touch.x]
}
const handler = this.chart.getZr().handler;
const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
dispatch.call(handler, 'mouseup', touch)
handler.processGesture(wrapTouch(e), 'end');
if(isClick) {
dispatch.call(handler, 'click', touch)
} else {
this.endTimer = setTimeout(() => {
dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
},50)
}
}
},
// #endif
// #ifdef H5
mousewheel(e){
if(this.chart) {
// dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
}
}
// #endif
}
};
</script>
<style>
.lime-echart {
position: relative;
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
.lime-echart__canvas {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.lime-echart__mask {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 1;
}
/* #endif */
</style>

View File

@ -0,0 +1,51 @@
export class Echarts {
eventMap = new Map()
constructor(webview) {
this.webview = webview
this.options = null
}
setOption() {
this.options = arguments
this.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
}
getOption() {
return this.options
}
showLoading() {
this.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
}
hideLoading() {
this.webview.evalJs(`hideLoading()`);
}
clear() {
this.webview.evalJs(`clear()`);
}
dispose() {
this.webview.evalJs(`dispose()`);
}
resize(size) {
if(size) {
this.webview.evalJs(`resize(${JSON.stringify(size)})`);
} else {
this.webview.evalJs(`resize()`);
}
}
on(type, ...args) {
const query = args[0]
const useQuery = query && typeof query != 'function'
const param = useQuery ? [type, query] : [type]
const key = `${type}${useQuery ? JSON.stringify(query): '' }`
const callback = useQuery ? args[1]: args[0]
if(typeof callback == 'function'){
this.eventMap.set(key, callback)
}
this.webview.evalJs(`on(${JSON.stringify(param)})`);
console.warn('nvue 暂不支持事件')
}
dispatchAction(type, options){
const handler = this.eventMap.get(type)
if(handler){
handler(options)
}
}
}

View File

@ -0,0 +1,190 @@
// @ts-nocheck
/**
* 获取设备基础信息
*
* @see [uni.getDeviceInfo](https://uniapp.dcloud.net.cn/api/system/getDeviceInfo.html)
*/
export function getDeviceInfo() {
if (uni.getDeviceInfo && uni.canIUse('getDeviceInfo')) {
return uni.getDeviceInfo();
} else {
return uni.getSystemInfoSync();
}
}
/**
* 获取窗口信息
*
* @see [uni.getWindowInfo](https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html)
*/
export function getWindowInfo() {
if (uni.getWindowInfo && uni.canIUse('getWindowInfo')) {
return uni.getWindowInfo();
} else {
return uni.getSystemInfoSync();
}
}
/**
* 获取APP基础信息
*
* @see [uni.getAppBaseInfo](https://uniapp.dcloud.net.cn/api/system/getAppBaseInfo.html)
*/
export function getAppBaseInfo() {
if (uni.getAppBaseInfo && uni.canIUse('getAppBaseInfo')) {
return uni.getAppBaseInfo();
} else {
return uni.getSystemInfoSync();
}
}
// #ifndef APP-NVUE
// 计算版本
export function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i], 10)
const num2 = parseInt(v2[i], 10)
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
// const systemInfo = uni.getSystemInfoSync();
function gte(version) {
// 截止 2023-03-22 mac pc小程序不支持 canvas 2d
// let {
// SDKVersion,
// platform
// } = systemInfo;
const { platform } = getDeviceInfo();
let { SDKVersion } = getAppBaseInfo();
// #ifdef MP-ALIPAY
SDKVersion = my.SDKVersion
// #endif
// #ifdef MP-WEIXIN
return platform !== 'mac' && compareVersion(SDKVersion, version) >= 0;
// #endif
return compareVersion(SDKVersion, version) >= 0;
}
export function canIUseCanvas2d() {
// #ifdef MP-WEIXIN
return gte('2.9.0');
// #endif
// #ifdef MP-ALIPAY
return gte('2.7.0');
// #endif
// #ifdef MP-TOUTIAO
return gte('1.78.0');
// #endif
return false
}
export function convertTouchesToArray(touches) {
// 如果 touches 是一个数组,则直接返回它
if (Array.isArray(touches)) {
return touches;
}
// 如果touches是一个对象则转换为数组
if (typeof touches === 'object' && touches !== null) {
return Object.values(touches);
}
// 对于其他类型,直接返回它
return touches;
}
export function wrapTouch(event) {
event.touches = convertTouchesToArray(event.touches)
for (let i = 0; i < event.touches.length; ++i) {
const touch = event.touches[i];
touch.offsetX = touch.x;
touch.offsetY = touch.y;
}
return event;
}
// export const devicePixelRatio = uni.getSystemInfoSync().pixelRatio
export const devicePixelRatio = getWindowInfo().pixelRatio;
// #endif
// #ifdef APP-NVUE
export function base64ToPath(base64) {
return new Promise((resolve, reject) => {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
const time = new Date().getTime();
const filePath = `_doc/uniapp_temp/${time}.${format}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
}, (error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
})
}
// #endif
export function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true)
}, time)
})
}
export function getRect(selector, options = {}) {
const typeDefault = 'boundingClientRect'
const {
context,
type = typeDefault
} = options
return new Promise((resolve, reject) => {
const dom = uni.createSelectorQuery().in(context).select(selector);
const result = (rect) => {
if (rect) {
resolve(rect)
} else {
reject()
}
}
if (type == typeDefault) {
dom[type](result).exec()
} else {
dom[type]({
node: true,
size: true,
rect: true
}, result).exec()
}
});
};

View File

@ -0,0 +1,133 @@
// @ts-nocheck
// #ifdef APP
type EchartsEventHandler = (event: UTSJSONObject)=>void
// type EchartsTempResolve = (obj : UTSJSONObject) => void
// type EchartsTempOptions = UTSJSONObject
export class Echarts {
options: UTSJSONObject = {} as UTSJSONObject
context: UniWebViewElement
eventMap: Map<string, EchartsEventHandler> = new Map()
private temp: UTSJSONObject[] = []
constructor(context: UniWebViewElement){
this.context = context
this.init()
}
init(){
this.context.evalJS(`init(null, null, ${JSON.stringify({})})`)
this.context.addEventListener('message', (e : UniWebViewMessageEvent) => {
// event.stopPropagation()
// event.preventDefault()
const detail = e.detail.data[0]
const file = detail.getString('file')
const data = detail.get('data')
const key = detail.getString('event')
const options = typeof data == 'object' ? (data as UTSJSONObject).getJSON('options'): null
const event = typeof data == 'object' ? (data as UTSJSONObject).getString('event'): null
if (key == 'log' && data != null) {
console.log(data)
}
if (event != null && options != null) {
this.dispatchAction(event.replace(/"/g,''), options)
}
if(file != null){
while (this.temp.length > 0) {
const opt = this.temp.pop()
const success = opt?.get('success')
if(typeof success == 'function'){
success as (res: UTSJSONObject) => void
success({tempFilePath: file})
}
}
}
})
}
setOption(option: UTSJSONObject){
this.options = option;
this.context.evalJS(`setOption(${JSON.stringify([option])})`)
}
setOption(option: UTSJSONObject, notMerge: boolean = false, lazyUpdate: boolean = false){
this.options = option;
this.context.evalJS(`setOption(${JSON.stringify([option, notMerge, lazyUpdate])})`)
}
setOption(option: UTSJSONObject, notMerge: UTSJSONObject){
this.options = option;
this.context.evalJS(`setOption(${JSON.stringify([option, notMerge])})`)
}
getOption(): UTSJSONObject {
return this.options
}
showLoading(){
this.context.evalJS(`showLoading(${JSON.stringify([] as any[])})`);
}
showLoading(type: string, opts: UTSJSONObject){
this.context.evalJS(`showLoading(${JSON.stringify([type, opts])})`);
}
hideLoading(){
this.context.evalJS(`hideLoading()`);
}
clear(){
this.context.evalJS(`clear()`);
}
dispose(){
this.context.evalJS(`dispose()`);
}
resize(size:UTSJSONObject){
setTimeout(()=>{
this.context.evalJS(`resize(${JSON.stringify(size)})`);
},0)
}
resize(){
setTimeout(()=>{
this.context.evalJS(`resize()`);
},10)
}
on(type:string, query: any, callback: EchartsEventHandler) {
const key = `${type}${JSON.stringify(query)}`
if(typeof callback == 'function'){
this.eventMap.set(key, callback)
}
this.context.evalJS(`on(${JSON.stringify([type, query])})`);
console.warn('uvue 暂不支持事件')
}
on(type:string, callback: EchartsEventHandler) {
const key = `${type}`
if(typeof callback == 'function'){
this.eventMap.set(key, callback)
}
this.context.evalJS(`on(${JSON.stringify([type])})`);
console.warn('uvue 暂不支持事件')
}
dispatchAction(type:string, options: UTSJSONObject){
const handler = this.eventMap.get(type)
if(handler!=null){
handler(options)
}
}
canvasToTempFilePath(opt: UTSJSONObject){
// this.context.evalJS(`on(${JSON.stringify(opt)})`);
this.context.evalJS(`canvasToTempFilePath(${JSON.stringify(opt)})`);
this.temp.push(opt)
}
}
// #endif
// #ifndef APP
export class Echarts {
constructor() {}
setOption(option: UTSJSONObject): void
isDisposed(): boolean;
clear(): void;
resize(size:UTSJSONObject): void;
resize(): void;
canvasToTempFilePath(opt : UTSJSONObject): void;
dispose(): void;
showLoading(cfg?: UTSJSONObject): void;
showLoading(name?: string, cfg?: UTSJSONObject): void;
hideLoading(): void;
getZr(): any
}
// #endif

View File

@ -0,0 +1,159 @@
<template>
<view style="width: 100%; height: 408px;">
<l-echart ref="chartRef" @finished="init"></l-echart>
</view>
</template>
<script>
export default {
data() {
return {
showTip: false,
option: {
tooltip: {
trigger: 'axis',
// shadowBlur: 0,
textStyle: {
textShadowBlur: 0
},
renderMode: 'richText',
},
legend: {
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis: {
type: 'value'
},
series: [
{
name: '邮件营销',
type: 'line',
stack: '总量',
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '联盟广告',
type: 'line',
stack: '总量',
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: '视频广告',
type: 'line',
stack: '总量',
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: '直接访问',
type: 'line',
stack: '总量',
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: '搜索引擎',
type: 'line',
stack: '总量',
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
}
}
},
mounted() {
console.log('lime echarts nvue')
},
methods: {
init() {
const chartRef = this.$refs['chartRef']
chartRef.init(chart => {
chart.setOption(this.option);
setTimeout(()=>{
const option = {
tooltip: {
trigger: 'axis',
// shadowBlur: 0,
textStyle: {
textShadowBlur: 0
},
renderMode: 'richText',
},
legend: {
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis: {
type: 'value'
},
series: [
{
name: '邮件营销',
type: 'line',
stack: '总量',
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '联盟广告',
type: 'line',
stack: '总量',
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: '视频广告',
type: 'line',
stack: '总量',
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: '直接访问',
type: 'line',
stack: '总量',
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: '搜索引擎',
type: 'line',
stack: '总量',
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
}
chart.setOption(option);
},1000)
})
},
save() {
// this.$refs.chart.canvasToTempFilePath({
// success(res) {
// console.log('res::::', res)
// }
// })
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,160 @@
<template>
<view style="width: 100%; height: 408px;">
<l-echart ref="chartRef" @finished="init"></l-echart>
</view>
</template>
<script lang="uts" setup>
// @ts-nocheck
// #ifndef APP
import * as echarts from 'echarts/dist/echarts.esm.js'
// #endif
const chartRef = ref<LEchartComponentPublicInstance|null>(null)
const option = {
tooltip: {
trigger: 'axis',
// shadowBlur: 0,
textStyle: {
textShadowBlur: 0
},
renderMode: 'richText',
},
// formatter: async (params: any) => {
// console.log('params', params)
// return 1
// },
legend: {
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis: {
type: 'value'
},
series: [
{
name: '邮件营销',
type: 'line',
stack: '总量',
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '联盟广告',
type: 'line',
stack: '总量',
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: '视频广告',
type: 'line',
stack: '总量',
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: '直接访问',
type: 'line',
stack: '总量',
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: '搜索引擎',
type: 'line',
stack: '总量',
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
}
const init = async () =>{
if(chartRef.value== null) return
// #ifdef APP
const chart = await chartRef.value!.init(null)
// #endif
// #ifndef APP
const chart = await chartRef.value!.init(echarts, null)
// #endif
chart.setOption(option)
chart.on('mouseover', function (params) {
console.log('params', params);
});
// setTimeout(()=> {
// const option1 = {
// tooltip: {
// trigger: 'axis',
// // shadowBlur: 0,
// textStyle: {
// textShadowBlur: 0
// },
// renderMode: 'richText',
// },
// legend: {
// data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
// },
// grid: {
// left: '3%',
// right: '4%',
// bottom: '3%',
// containLabel: true
// },
// xAxis: {
// type: 'category',
// boundaryGap: false,
// data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
// },
// yAxis: {
// type: 'value'
// },
// series: [
// {
// name: '邮件营销',
// type: 'line',
// stack: '总量',
// data: [820, 132, 101, 134, 90, 230, 210]
// },
// {
// name: '联盟广告',
// type: 'line',
// stack: '总量',
// data: [220, 182, 191, 234, 290, 330, 310]
// },
// {
// name: '视频广告',
// type: 'line',
// stack: '总量',
// data: [950, 232, 201, 154, 190, 330, 410]
// },
// {
// name: '直接访问',
// type: 'line',
// stack: '总量',
// data: [320, 332, 301, 334, 390, 330, 320]
// },
// {
// name: '搜索引擎',
// type: 'line',
// stack: '总量',
// data: [820, 932, 901, 934, 1290, 1330, 1320]
// }
// ]
// }
// chart.setOption(option1)
// },1000)
}
</script>
<style>
</style>

View File

@ -0,0 +1,227 @@
<template>
<view>
<view style="height: 750rpx; position: relative">
<l-echart ref="chart" @finished="init"></l-echart>
<view
class="customTooltips"
:style="{ left: position[0] + 'px', top: position[1] + 'px' }"
v-if="params.length && position.length && showTip"
>
<view>这是个自定的tooltips</view>
<view>{{ params[0]['axisValue'] }}</view>
<view v-for="item in params">
<view>
<text>{{ item.seriesName }}</text>
<text>{{ item.value }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
// nvue
// #ifdef VUE2
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
// #endif
// #ifdef VUE3
// #ifdef MP
// vue3 使vite umd使使require
const echarts = require('../../static/echarts.min')
// #endif
// #ifndef MP
// vue3 使vite umdnpm
import * as echarts from 'echarts/dist/echarts.esm'
// #endif
// #endif
export default {
data() {
return {
showTip: false,
position: [],
params: [],
option: {
tooltip: {
trigger: 'axis',
// shadowBlur: 0,
textStyle: {
textShadowBlur: 0,
},
renderMode: 'richText',
position: (point, params, dom, rect, size) => {
// tooltips
const box = [170, 170]
//
const offsetX = point[0] < size.viewSize[0] / 2 ? 20 : -box[0] - 20
const offsetY = point[1] < size.viewSize[1] / 2 ? 20 : -box[1] - 20
const x = point[0] + offsetX
const y = point[1] + offsetY
this.position = [x, y]
this.params = params
},
formatter: (params, ticket, callback) => {},
},
legend: {
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
},
yAxis: {
type: 'value',
},
series: [
{
name: '邮件营销',
type: 'line',
stack: '总量',
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: '联盟广告',
type: 'line',
stack: '总量',
data: [220, 182, 191, 234, 290, 330, 310],
},
{
name: '视频广告',
type: 'line',
stack: '总量',
data: [150, 232, 201, 154, 190, 330, 410],
},
{
name: '直接访问',
type: 'line',
stack: '总量',
data: [320, 332, 301, 334, 390, 330, 320],
},
{
name: '搜索引擎',
type: 'line',
stack: '总量',
data: [820, 932, 901, 934, 1290, 1330, 1320],
},
],
},
}
},
methods: {
init() {
// init(echarts, theme?:string, opts?:{}, chart => {})
// echarts nvuenvue
// theme 'dark'
// opts = { //
// locale?: string // `5.0.0`
// }
// chart => {} callback
// setTimeout(()=>{
// this.$refs.chart.init(echarts, chart => {
// chart.setOption(this.option);
// });
// },300)
this.$refs.chart.init(echarts, (chart) => {
chart.setOption(this.option)
// tooltip
chart.on('showTip', (params) => {
this.showTip = true
console.log('showTip::')
})
chart.on('hideTip', (params) => {
setTimeout(() => {
this.showTip = false
}, 300)
})
setTimeout(() => {
const option = {
tooltip: {
trigger: 'axis',
// shadowBlur: 0,
textStyle: {
textShadowBlur: 0,
},
renderMode: 'richText',
},
legend: {
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
},
yAxis: {
type: 'value',
},
series: [
{
name: '邮件营销',
type: 'line',
stack: '总量',
data: [1120, 132, 101, 134, 90, 230, 210],
},
{
name: '联盟广告',
type: 'line',
stack: '总量',
data: [220, 182, 191, 234, 290, 330, 310],
},
{
name: '视频广告',
type: 'line',
stack: '总量',
data: [150, 632, 201, 154, 190, 330, 410],
},
{
name: '直接访问',
type: 'line',
stack: '总量',
data: [820, 332, 301, 334, 390, 330, 320],
},
{
name: '搜索引擎',
type: 'line',
stack: '总量',
data: [820, 932, 901, 934, 1290, 1330, 1320],
},
],
}
chart.setOption(option)
}, 1000)
})
},
save() {
this.$refs.chart.canvasToTempFilePath({
success(res) {
console.log('res::::', res)
},
})
},
},
}
</script>
<style>
.customTooltips {
position: absolute;
background-color: rgba(255, 255, 255, 0.8);
padding: 20rpx;
}
</style>

View File

@ -0,0 +1,91 @@
{
"id": "lime-echart",
"displayName": "echarts",
"version": "0.9.8",
"description": "echarts 全端兼容一款使echarts图表能跑在uniapp各端中的插件, 支持uniapp/uniappx(web,ios,安卓)",
"keywords": [
"echarts",
"canvas",
"图表",
"可视化"
],
"repository": "https://gitee.com/liangei/lime-echart",
"engines": {
"HBuilderX": "^3.6.4"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"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": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
},
"dependencies": {
"echarts": "^5.4.1",
"zrender": "^5.4.3"
}
}

View File

@ -0,0 +1,407 @@
# echarts 图表 <span style="font-size:16px;">👑👑👑👑👑 <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
> 一个基于 JavaScript 的开源可视化图表库 [查看更多](https://limeui.qcoon.cn/#/echart) <br>
> 基于 echarts 做了兼容处理,更多示例请访问 [uni示例](https://limeui.qcoon.cn/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html) <br>
## 平台兼容
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
| √ | √ | √ | √ | √ | √ | √ |
## 安装
- 第一步:在市场导入 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899)
- 第二步:选择插件依赖:<br>
1、可以选插件内的`echarts`包或自定义包,自定义包[下载地址](https://echarts.apache.org/zh/builder.html)<br>
2、或者使用`npm`安装`echarts`
**注意**
* 🔔 echarts 5.3.0及以上
* 🔔 如果是 `cli` 项目请下载插件到`src`目录下的`uni_modules`,没有这个目录就创建一个
## 代码演示
### Vue2
- 引入依赖,可以是插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html),也可以是`npm`包
```html
<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef" @finished="init"></l-echart></view>
```
```js
// 插件内的 三选一
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
// 自定义的 三选一 下载后放入项目的路径
import * as echarts from 'xxx/echarts.min'
// npm包 三选一 需要在控制台 输入命令npm install echarts
import * as echarts from 'echarts'
```
```js
export default {
data() {
return {
option: {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
confine: true
},
legend: {
data: ['热度', '正面', '负面']
},
grid: {
left: 20,
right: 20,
bottom: 15,
top: 40,
containLabel: true
},
xAxis: [
{
type: 'value',
axisLine: {
lineStyle: {
color: '#999999'
}
},
axisLabel: {
color: '#666666'
}
}
],
yAxis: [
{
type: 'category',
axisTick: { show: false },
data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
axisLine: {
lineStyle: {
color: '#999999'
}
},
axisLabel: {
color: '#666666'
}
}
],
series: [
{
name: '热度',
type: 'bar',
label: {
normal: {
show: true,
position: 'inside'
}
},
data: [300, 270, 340, 344, 300, 320, 310],
},
{
name: '正面',
type: 'bar',
stack: '总量',
label: {
normal: {
show: true
}
},
data: [120, 102, 141, 174, 190, 250, 220]
},
{
name: '负面',
type: 'bar',
stack: '总量',
label: {
normal: {
show: true,
position: 'left'
}
},
data: [-20, -32, -21, -34, -90, -130, -110]
}
]
},
};
},
// 组件能被调用必须是组件的节点已经被渲染到页面上
methods: {
async init() {
// chart 图表实例不能存在data里
const chart = await this.$refs.chartRef.init(echarts);
chart.setOption(this.option)
}
}
}
```
### Vue3
- 小程序可以使用`require`引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
- `require`仅支持相对路径,不支持路径别名
- 非小程序使用 `npm`
```html
<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef"></l-echart></view>
```
```js
// 小程序 二选一
// 插件内的 二选一
const echarts = require('../../uni_modules/lime-echart/static/echarts.min');
// 自定义的 二选一 下载后放入项目的路径
const echarts = require('xxx/xxx/echarts');
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 非小程序
// 需要在控制台 输入命令npm install echarts
import * as echarts from 'echarts'
```
```js
const chartRef = ref(null)
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
confine: true
},
legend: {
data: ['热度', '正面', '负面']
},
grid: {
left: 20,
right: 20,
bottom: 15,
top: 40,
containLabel: true
},
xAxis: [
{
type: 'value',
axisLine: {
lineStyle: {
color: '#999999'
}
},
axisLabel: {
color: '#666666'
}
}
],
yAxis: [
{
type: 'category',
axisTick: { show: false },
data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
axisLine: {
lineStyle: {
color: '#999999'
}
},
axisLabel: {
color: '#666666'
}
}
],
series: [
{
name: '热度',
type: 'bar',
label: {
normal: {
show: true,
position: 'inside'
}
},
data: [300, 270, 340, 344, 300, 320, 310],
},
{
name: '正面',
type: 'bar',
stack: '总量',
label: {
normal: {
show: true
}
},
data: [120, 102, 141, 174, 190, 250, 220]
},
{
name: '负面',
type: 'bar',
stack: '总量',
label: {
normal: {
show: true,
position: 'left'
}
},
data: [-20, -32, -21, -34, -90, -130, -110]
}
]
};
onMounted( ()=>{
// 组件能被调用必须是组件的节点已经被渲染到页面上
setTimeout(async()=>{
if(!chartRef.value) return
const myChart = await chartRef.value.init(echarts)
myChart.setOption(option)
},300)
})
```
### Uvue
- Uvue和Nvue不需要引入`echarts`,因为它们的实现方式是`webview`
- uniapp x需要HBX 4.13以上
```html
<view style="width: 100%; height: 408px;">
<l-echart ref="chartRef" @finished="init"></l-echart>
</view>
```
```js
// @ts-nocheck
// #ifdef H5
import * as echarts from 'echarts/dist/echarts.esm.js'
// #endif
const chartRef = ref<LEchartComponentPublicInstance|null>(null);
const init = async () => {
if(chartRef.value== null) return
// #ifdef APP
const chart = await chartRef.value!.init(null)
// #endif
// #ifdef H5
const chart = await chartRef.value!.init(echarts, null)
// #endif
chart.setOption(option)
}
```
## 数据更新
- 1、使用 `ref` 可获取`setOption`设置更新
- 2、也可以拿到图表实例`chart`设置`myChart.setOption(data)`
```js
// ref
this.$refs.chart.setOption(data)
// 图表实例
myChart.setOption(data)
```
## 图表大小
- 在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
```js
// 默认获取容器尺寸
this.$refs.chart.resize()
// 指定尺寸
this.$refs.chart.resize({width: 375, height: 375})
```
## 自定义Tooltips
- uvue\nvue 不支持
由于除H5之外都不存在dom但又有tooltips个性化的需求代码就不贴了看示例吧
```
代码位于/uni_modules/lime-echart/component/lime-echart
```
## 插件标签
- 默认 l-echart 为 component
- 默认 lime-echart 为 demo
```html
// 在任意地方使用可查看domo, 代码位于/uni_modules/lime-echart/component/lime-echart
<lime-echart></lime-echart>
```
## 常见问题
- 钉钉小程序 由于没有`measureText`,模拟的`measureText`又无法得到当前字体的`fontWeight`,故可能存在估计不精细的问题
- 微信小程序 `2d` 只支持 真机调试2.0
- 微信开发工具会出现 `canvas` 不跟随页面的情况,真机不影响
- 微信开发工具会出现 `canvas` 层级过高的问题,真机一般不受影响,可以先测只有两个元素的页面看是否会有层级问题。
- toolbox 不支持 `saveImage`
- echarts 5.3.0 的 lines 不支持 trailLength故需设置为 `0`
- dataZoom H5不要设置 `showDetail`
- 如果微信小程序的`tooltip`文字有阴影,可能是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
- 如果钉钉小程序上传时报安全问题`Uint8Clamped`,可以向钉钉反馈是安全代码扫描把Uint8Clamped数组错误识别了也可以在 echarts 文件修改`Uint8Clamped`
```js
// 找到这段代码把代码中`Uint8Clamped`改成`Uint8_Clamped`,再把下划线去掉,不过直接去掉`Uint8Clamped`也是可行的
// ["Int8","Uint8","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e+"Array]"]
// 改成如下
["Int8","Uint8","Uint8_Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e.replace('_','')+"Array]"]
```
### vue3
如果您是使用 **vite + vue3** 非微信小程序可能会遇到`echarts`文件缺少`wx`判断导致无法使用或缺少`tooltip`<br>
方式一:可以在`echarts.min.js`文件开头增加以下内容参考插件内的echart.min.js的做法
```js
let global = null
let wx = uni
```
方式二:在`vite.config.js`的`define`设置环境
```js
// 或者在`vite.config.js`的`define`设置环境
import { defineConfig } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
const define = {}
if(!["mp-weixin", "h5", "web"].includes(process.env.UNI_PLATFORM)) {
define['global'] = null
define['wx'] = 'uni'
}
export default defineConfig({
plugins: [uni()],
define
});
```
## Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------------- | -------- | ------- | ------------ | ----- |
| custom-style | 自定义样式 | `string` | - | - |
| type | 指定 canvas 类型 | `string` | `2d` | |
| is-disable-scroll | 触摸图表时是否禁止页面滚动 | `boolean` | `false` | |
| beforeDelay | 延迟初始化 (毫秒) | `number` | `30` | |
| enableHover | PC端使用鼠标悬浮 | `boolean` | `false` | |
| landscape | 是否旋转90deg,模拟横屏效果 | `boolean` | `false` | |
## 事件
| 参数 | 说明 |
| --------------- | --------------- |
| init(echarts, chart => {}) | 初始化调用函数,第一个参数是传入`echarts`,第二个参数是回调函数,回调函数的参数是 `chart` 实例 |
| setChart(chart => {}) | 已经初始化后,请使用这个方法,是个回调函数,参数是 `chart` 实例 |
| setOption(data) | [图表配置项](https://echarts.apache.org/zh/option.html#title),用于更新 ,传递是数据 `option` |
| clear() | 清空当前实例,会移除实例中所有的组件和图表。 |
| dispose() | 销毁实例 |
| showLoading() | 显示加载 |
| hideLoading() | 隐藏加载 |
| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(opt) | 用于生成图片,与官方使用方法一致,但不需要传`canvasId` |
## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style type="text/css">
html,
body,
.canvas {
padding: 0;
margin: 0;
overflow-y: hidden;
background-color: transparent;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="canvas" id="limeChart"></div>
<script type="text/javascript" src="./uni.webview.1.5.5.js"></script>
<script type="text/javascript" src="./echarts.min.js"></script>
<script type="text/javascript" src="./ecStat.min.js"></script>
<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-liquidfill@latest/dist/echarts-liquidfill.min.js"></script> -->
<script>
let chart = null;
let cache = [];
console.log = function() {
emit('log', {
log: arguments,
})
}
function emit(event, data) {
postMessage({
event,
data
})
cache = []
}
function postMessage(data) {
uni.webView.postMessage({
data
})
// window.__uniapp_x_.postMessage(JSON.stringify(data))
};
function stringify(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
return;
}
cache.push(value);
}
return value;
}
function parse(name, callback, options) {
const optionNameReg = /[\w]+\.setOption\(([\w]+\.)?([\w]+)\)/
if (optionNameReg.test(callback)) {
const optionNames = callback.match(optionNameReg)
if (optionNames[1]) {
const _this = optionNames[1].split('.')[0]
window[_this] = {}
window[_this][optionNames[2]] = options
return optionNames[2]
} else {
return null
}
}
return null
}
function init(callback, options, opts, theme) {
if (!chart) {
chart = echarts.init(document.getElementById('limeChart'), theme, opts)
if (options) {
chart.setOption(options)
}
}
}
function on(data) {
if (chart && data.length > 0) {
const [type, query] = data
const key = `${type}${JSON.stringify(query||'')}`
if (query) {
chart.on(type, query, function(options) {
var obj = {};
Object.keys(options).forEach(function(key) {
if (key != 'event') {
obj[key] = options[key];
}
});
emit(key, {
event: key,
options: obj,
});
});
} else {
chart.on(type, function(options) {
var obj = {};
Object.keys(options).forEach(function(key) {
if (key != 'event') {
obj[key] = options[key];
}
});
emit(key, {
event: key,
options: obj,
});
});
}
}
}
function setChart(callback, options) {
if (!callback) return
if (chart && callback && options) {
var r = null
const name = parse('r', callback, options)
if (name) this[name] = options
eval(`r = ${callback};`)
if (r) {
r(chart)
}
}
}
function setOption(data) {
if (chart) chart.setOption(data[0], data[1])
}
function showLoading(data) {
if (chart) chart.showLoading(data[0], data[1])
}
function hideLoading() {
if (chart) chart.hideLoading()
}
function clear() {
if (chart) chart.clear()
}
function dispose() {
if (chart) chart.dispose()
}
function resize(size) {
if (chart) chart.resize(size)
}
function canvasToTempFilePath(opt) {
if (chart) {
delete opt.success
const src = chart.getDataURL(opt)
postMessage({
// event: 'file',
file: src
})
}
}
document.addEventListener('touchmove', () => {
})
</script>
</body>
</html>

View File

@ -1,13 +1,13 @@
<template> <template>
<view v-if="returnCodeOrID === 'orgCode'"> <view v-if="returnCodeOrID === 'orgCode'">
<uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" placeholder="请选择单位code" <uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" :ellipsis="true"
popup-title="请选择单位" :localdata="departList" @nodeclick="onnodeclick" @popupclosed="onpopupclosed" placeholder="请选择单位code" popup-title="请选择单位" :localdata="departList" @nodeclick="onnodeclick"
:map="{'text':'title','value':'orgCode'}"></uni-data-picker> @popupclosed="onpopupclosed" @change="onchange" :map="{'text':'title','value':'orgCode'}"></uni-data-picker>
</view> </view>
<view v-else> <view v-else>
<uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" placeholder="请选择单位id" <uni-data-picker v-model="selectDepartID" ref="depart" :openSearch="true" :ellipsis="true" placeholder="请选择单位id"
popup-title="请选择单位" :localdata="departList" @nodeclick="onnodeclick" @popupclosed="onpopupclosed" popup-title="请选择单位" :localdata="departList" @nodeclick="onnodeclick" @popupclosed="onpopupclosed"
:map="{'text':'title','value':'id'}"></uni-data-picker> @change="onchange" :map="{'text':'title','value':'id'}"></uni-data-picker>
</view> </view>
</template> </template>
@ -35,7 +35,10 @@
const store = useStore() const store = useStore()
const props = defineProps({ const props = defineProps({
returnCodeOrID: {type:String,default:"orgCode"} returnCodeOrID: {
type: String,
default: "orgCode"
}
}) })
let departList = ref([]) let departList = ref([])
@ -43,6 +46,8 @@
let selectDepartIDS = ref([]) //ID let selectDepartIDS = ref([]) //ID
let tempSelectDepartID = ref("") //ID let tempSelectDepartID = ref("") //ID
let departInfo = ref({}) //""
let $emit = defineEmits(['change']); let $emit = defineEmits(['change']);
const getDepartList = () => { const getDepartList = () => {
@ -56,17 +61,22 @@
}) })
} }
const onnodeclick = ((e) => { const onnodeclick = ((e) => {
// console.log(e) departInfo.value = e;
if (props.returnCodeOrID = "orgCode") { if (props.returnCodeOrID = "orgCode") {
tempSelectDepartID.value = e.orgCode tempSelectDepartID.value = e.orgCode
} else { } else {
tempSelectDepartID.value = e.value tempSelectDepartID.value = e.value
} }
})
const onchange = ((e) => {
$emit('change', e.detail.value[e.detail.value.length - 1].value, {})
}) })
const onpopupclosed = ((e) => { const onpopupclosed = ((e) => {
selectDepartID.value = tempSelectDepartID.value selectDepartID.value = tempSelectDepartID.value
$emit('change', selectDepartID.value) $emit('change', selectDepartID.value, departInfo)
}) })
onLoad((e) => { onLoad((e) => {
getDepartList(); getDepartList();

View File

@ -1,5 +1,25 @@
# trq-depart-select # trq-depart-select
# 1.0.1
更新,添加返回值,将整个机构对象返回父组件
```javascript
const onpopupclosed = ((e) => {
selectDepartID.value = tempSelectDepartID.value
$emit('change', selectDepartID.value, departInfo)
})
```
增加选择到最后层级触发的change时间返回数据到父组件的功能
```javascript
const onchange = ((e) => {
$emit('change', e.detail.value[e.detail.value.length - 1].value, {})
})
```
# 1.0
属性 returnCodeOrID 默认值 orgCode 组件返回单位的orgCode 不设置属性或设置为其他,组件返回 单位ID 属性 returnCodeOrID 默认值 orgCode 组件返回单位的orgCode 不设置属性或设置为其他,组件返回 单位ID
事件change 选择内容发生变化时发生通过emit 返回给父组件 选择单位的ID或code 事件change 选择内容发生变化时发生通过emit 返回给父组件 选择单位的ID或code