echarts插件更换

master
Lee 2023-02-03 11:15:54 +08:00
parent bdc3839df6
commit 87328bf217
28 changed files with 3683 additions and 2179 deletions

View File

@ -8,7 +8,7 @@ image{
}
.main{
width: 100vw;
min-height: 1920px;
min-height: 100vh;
background-image: url('/static/page-bg.jpg');
background-size: cover;
}

View File

@ -1,5 +1,5 @@
{
"name" : "碳中和",
"name" : "碳足迹计算器",
"appid" : "__UNI__DB95977",
"description" : "",
"versionName" : "1.0.0",

View File

@ -71,7 +71,7 @@
</view>
<!-- 饼图 -->
<view class="pie-chart">
<uni-chart :option="pie" />
<l-echart ref="chart" @finished="init"></l-echart>
</view>
</view>
<view class="assess-title">
@ -142,6 +142,7 @@
<script>
import swiperPull from '@/components/swiper/swiper-pull.vue';
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min.js';
export default {
components: {
swiperPull
@ -180,7 +181,17 @@
//
this.pieEv();
},
mounted() {
this.$refs.chart.init(echarts, chart => {
chart.setOption(this.pie);
});
},
methods: {
init() {
this.$refs.chart.init(echarts, chart => {
chart.setOption(this.pie);
});
},
//
pieEv() {
this.pie = {

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 xbmlz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,166 +0,0 @@
# 在 uni-app 中使用 Apache ECharts
本项目是 [Apache ECharts](https://github.com/apache/echarts) 的uni-app版本支持 [Vue.js](https://vuejs.org/) 2/3以及使用的示例。
开发者可以通过熟悉的 ECharts 配置方式,快速开发图表,满足各种可视化需求.
## 快速开始
### 方式一: 使用 uni_modules 安装(推荐)
使用 `uni_modules` 方式安装组件库,可以直接通过插件市场导入,通过右键菜单快速更新组件,不需要引用、注册,直接在页面中使用 `echarts-for-uniapp` 组件。[点击安装 echarts-for-uniapp 组件库](https://ext.dcloud.net.cn/plugin?name=echarts-for-uniapp)
### 方式二: 使用 npm 安装
`vue-cli` 项目中可以使用 `npm` 安装 `echarts-for-uniapp` 库 ,或者直接在 `HBuilderX` 项目中使用 `npm` 。(不推荐后一种方式)
```bash
npm i uniapp-echarts
```
### 创建图表
```vue
<template>
<view class="content">
<uni-chart :option="option" />
</view>
</template>
<script>
export default {
data() {
return {
option: {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}
]
}
}
}
}
<style>
.content {
width: 375px;
height: 375px;
}
</style>
```
这对于所有 ECharts 图表都是通用的,用户只需要修改上面 `option` 的内容,即可改变图表。`option` 的使用方法参见 [ECharts 配置项文档](https://echarts.apache.org/zh/option.html)。对于不熟悉 ECharts 的用户,可以参见 [5 分钟上手 ECharts](https://echarts.apache.org/zh/tutorial.html#5%20%E5%88%86%E9%92%9F%E4%B8%8A%E6%89%8B%20ECharts) 教程。
完整的例子请参见 [xbmlz/echarts-for-uniapp](https://github.com/xbmlz/echarts-for-uniapp) 项目。
### 支持平台
| Vue2 | Vue3 |
| --- | --- |
| ✔️ | ✔️ |
| APP-VUE | APP-NVUE | 小程序 | WEB/H5 |
| --- | --- | --- | --- |
| ✔️ | ❌ | ✔ | ✔ |
### 参数
| 参数名 | 类型 | 默认值 | 描述 |
| :--: | :--: | :--: | :-- |
| forceUseOldCanvas | Boolean | false | 强制使用旧版Canvas(不推荐) |
| option | Object | | [ECharts Option](https://echarts.apache.org/zh/option.html) |
| theme | String| | [Eharts Theme](https://echarts.apache.org/zh/download-theme.html) |
| map | Object| | 使用地图组件时,注册地图,详见:[registerMap](https://echarts.apache.org/en/api.html#echarts.registerMap) |
### 事件
| 事件名 | 参数 | 返回数据 | 描述 |
| --- | --- | --- | --- |
| inited | | chart | 初始化后返回 chart 对象 |
| click | | event | 点击 |
| touchstart | | event | 按下 |
| touchmove | | event | 移动 |
| touchend | | event | 松开 |
| mousedown | | event | 按下 |
| mousemove | | event | 移动 |
| mouseup | | event | 松开 |
## FAQ
### 如何获取图表实例?
```vue
<template>
<view class="content">
<uni-chart @inited="inited" />
</view>
</template>
<script>
export default {
data() {
return {}
},
methods: {
inited(chart) {
// do something
}
}
}
</script>
```
### 如何延迟加载图表?
参见 [pages/lazyLoad](./pages/lazyLoad/index.vue) 的例子,可以在获取数据后再初始化数据。
### 如何在一个页面中加载多个图表?
参见 [pages/multiCharts](./pages/multiCharts/index.vue) 的例子。
### 如何使用 Tooltip
具体使用方法和 ECharts 相同,例子参见 [pages/line](./pages/line/index.vue)
### 如何保存为图片?
参见 [pages/saveCanvas](./pages/saveCanvas/index.vue) 的例子。
### 多主题色
参见 [pages/themeColor](./pages/themeColor/index.vue) 的例子 。
### 完整示例
参见 [pages](./pages)
### 文件太大怎么办?
本项目默认提供的 ECharts 文件是最新版本的包含所有组件文件。可以下载不同版本的 [ECharts](https://github.com/apache/echarts/blob/master/dist/) 进行替换。建议调试时使用未压缩版本,发布时使用压缩版本,否则文件会太大无法发布。
发布时,如果对文件大小要求更高,可以在 [ECharts 在线定制](https://echarts.apache.org/zh/builder.html)网页下载仅包含必要组件的包,并且选择压缩。
下载的文件放在 `uni_modules/echarts-for-uniapp/static/echarts.min.js`**注意一定需要重命名为 `echarts.min.js`**。
此外,还可考虑使用微信小程序的[分包策略](https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/independent.html)
如有其它问题,也欢迎在 [issue](https://github.com/xbmlz/echarts-for-uniapp/issues) 中反馈,谢谢!

View File

@ -1,17 +0,0 @@
## 1.0.22022-11-20
- 新增 `pages` 文件夹下新增组件示例
- 新增 保存到相册方法
- 新增 地图注册方法
- 新增 多主题色功能
- 修复 修复点击事件报错
- 优化 优化部分细节
## 1.0.12022-11-20
- 新增 支持vue2.x, 旧版微信小程序
- 新增 h5点击事件
- 新增 小程序触摸事件
- 新增 forceUseOldCanvas参数强制使用旧版Canvas
- 移除 width、height属性用style代替
## 1.0.02022-11-18
- 在uni-app中可以以组件的形式使用 ECharts, 支持touch事件。

View File

@ -1,3 +0,0 @@
<template>
<view></view>
</template>

View File

@ -1,106 +0,0 @@
export class UniCanvas {
constructor(ctx, canvasNode) {
this.ctx = ctx
this.chart = {}
this.canvasNode = canvasNode
if (!canvasNode) {
this._initStyle(ctx)
}
this._initEvent()
}
getContext(contextType) {
if (contextType === '2d') {
return this.ctx
}
}
setChart(chart) {
this.chart = chart
}
attachEvent() {
// noop
}
detachEvent() {
// noop
}
addEventListener() {
// noop
}
removeEventListener() {
// noop
}
_initCanvas(zrender, ctx) {
zrender.util.getContext = () => {
return ctx
}
zrender.util.$override('measureText', (text, font) => {
ctx.font = font || '12px sans-serif'
return ctx.measureText(text)
})
}
_initStyle(ctx) {
ctx.createCircularGradient = (x, y, r) => {
return ctx.createCircularGradient(x, y, r)
}
}
_initEvent() {
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,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {},
})
}
})
}
get width() {
if (this.canvasNode) return this.canvasNode.width
return 0
}
set width(w) {
if (this.canvasNode) this.canvasNode.width = w
}
get height() {
if (this.canvasNode) return this.canvasNode.height
return 0
}
set height(h) {
if (this.canvasNode) this.canvasNode.height = h
}
}

View File

@ -1,351 +0,0 @@
<template>
<!-- #ifdef APP-VUE || H5 -->
<view v-if="systemInfo.deviceType === 'pc'" :id="canvasId" class="uni-canvas" @click="emits('click', $event)" />
<view
v-else
:id="canvasId"
class="uni-canvas"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
/>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || MP-QQ -->
<canvas
v-if="useNewCanvas"
type="2d"
:id="canvasId"
class="uni-canvas"
:canvas-id="canvasId"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
/>
<canvas
v-else
:id="canvasId"
class="uni-canvas"
:canvas-id="canvasId"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
/>
<!-- #endif -->
</template>
<script>
// #ifdef VUE3
import '../../static/echarts.min.js'
// #endif
// #ifdef VUE2 || MP-WEIXIN
const echarts = require('../../static/echarts.min.js')
// #endif
import { UniCanvas } from './uni-canvas.js'
let chart = null
export default {
name: 'uni-chart',
emits: ['inited', 'click', 'touchStart', 'touchMove', 'touchEnd'],
props: {
// 使 canvas
forceUseOldCanvas: {
type: Boolean,
default: false
},
//
theme: {
type: [String, Object],
},
//
option: {
type: Object,
require: true,
default() {
return {}
}
},
//
map: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
canvasId: `uni-canvas-${Date.now()}`,
systemInfo: uni.getSystemInfoSync(),
useNewCanvas: true
}
},
mounted() {
this.registerPreprocessor()
this.registerMap()
this.$nextTick(() => {
// #ifdef APP-VUE || H5
this.initH5()
// #endif
// #ifdef MP-WEIXIN || MP-QQ
this.initMiniProgram()
// #endif
})
},
beforeDestroy() {
this.dispose()
},
methods: {
// register echarts preprocessor
registerPreprocessor() {
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
}
}
})
},
// register geo json
registerMap() {
if (JSON.stringify(this.$props.map) === '{}') return
echarts.registerMap(this.$props.map.name, this.$props.map.opt)
},
// register theme color
registerTheme(name, opt) {
console.log('registerTheme', name, opt)
echarts.registerTheme(name, opt)
},
// init H5 app-vue
initH5() {
const canvasNode = document.getElementById(this.canvasId)
chart = echarts.init(canvasNode, this.$props.theme)
this.$emit('inited', chart)
chart.setOption(this.$props.option)
},
// init mini program
initMiniProgram() {
const version = this.systemInfo.SDKVersion
console.log(`当前基础库版本为: ${version}`)
const oldVersion = '1.9.91'
const baseVersion = '2.9.0'
let canUseNewCanvas = this.compareVersion(version, baseVersion) >= 0
if (this.$props.forceUseOldCanvas) {
if (canUseNewCanvas) console.warn('开发者强制使用旧canvas,建议关闭')
canUseNewCanvas = false
}
this.useNewCanvas = canUseNewCanvas && !this.forceUseOldCanvas
if (this.useNewCanvas) {
// 2.9.0 使 <canvas type="2d"></canvas>
this.initNewCanvas()
} else {
const isValid = this.compareVersion(version, oldVersion) >= 0
if (!isValid) {
console.error(`基础库版本过低,需大于等于 ${oldVersion}`)
return
} else {
console.warn(`建议将基础库调整大于等于${baseVersion}版本。升级后绘图将有更好性能`)
this.initOldCanvas()
}
}
},
// initNewCanvas
initNewCanvas() {
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.node(res => {
const canvasNode = res.node
const ctx = canvasNode?.getContext('2d')
canvasNode.width = canvasNode.width * this.systemInfo.pixelRatio
canvasNode.height = canvasNode.height * this.systemInfo.pixelRatio
ctx.scale(this.pixelRatio, this.pixelRatio)
const canvas = new UniCanvas(ctx, canvasNode)
this.initECharts(canvas, canvasNode.width, canvasNode.width, this.pixelRatio)
})
.exec()
},
// initOldCanvas
initOldCanvas() {
// 1.9.91 <= sdkVersion < 2.9.0
const ctx = uni.createCanvasContext(`#${this.canvasId}`, this)
const canvas = new UniCanvas(ctx)
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.boundingClientRect(res => {
// canvasdpr
this.initECharts(res.width, res.height, 1)
})
.exec()
},
// init
initECharts(canvas, width, height, dpr) {
echarts.setPlatformAPI({ createCanvas: () => canvas })
const theme = this.$props.theme
let themeName = ''
console.log('typeof theme', typeof theme)
if (typeof theme === 'object') {
this.registerTheme(theme.name, theme.opt)
themeName = theme.name
}
if (typeof theme === 'string') {
themeName = theme
}
chart = echarts.init(canvas, themeName, {
width: width,
height: height,
devicePixelRatio: dpr
})
this.$emit('inited', chart)
this.setOption(this.$props.option)
},
canvasToTempFilePath(opt) {
if (this.useNewCanvas) {
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.node(res => {
const canvasNode = res.node
opt.canvas = canvasNode
uni.canvasToTempFilePath(opt)
})
.exec()
} else {
if (!opt.canvasId) {
opt.canvasId = this.canvasId
}
// TODO
chart.ctx.draw(true, () => {
uni.canvasToTempFilePath(opt, this)
})
}
},
setOption(opt) {
chart.setOption(opt)
},
dispose() {
chart && chart.dispose()
},
// event
wrapTouch(event) {
for (let i = 0; i < event.touches.length; ++i) {
const touch = event.touches[i]
touch.offsetX = touch.x
touch.offsetY = touch.y
}
return event
},
click(e) {
this.$emit('click', e)
},
touchStart(e) {
if (chart && e.touches.length > 0) {
const touch = e.touches[0]
const handler = chart.getZr().handler
handler.dispatch('mousedown', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopPropagation: () => {}
})
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopPropagation: () => {}
})
handler.processGesture(this.wrapTouch(e), 'start')
this.$emit('touchStart', e)
}
},
touchMove(e) {
if (chart && e.touches.length > 0) {
let touch = e.touches[0]
const {
target: { offsetLeft, offsetTop }
} = e
touch.x = touch.pageX - offsetLeft
touch.y = touch.pageY - offsetTop
const handler = chart.getZr().handler
if (handler) {
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y
})
handler.processGesture(this.wrapTouch(e), 'change')
}
this.$emit('touchMove', e)
}
},
touchEnd(e) {
if (chart) {
const touch = e.changedTouches ? e.changedTouches[0] : {}
const {
target: { offsetLeft, offsetTop }
} = e
touch.x = touch.pageX - offsetLeft
touch.y = touch.pageY - offsetTop
var handler = chart.getZr().handler
if (handler) {
handler.dispatch('mouseup', {
zrX: touch.x,
zrY: touch.y
})
handler.dispatch('click', {
zrX: touch.x,
zrY: touch.y
})
handler.processGesture(this.wrapTouch(e), 'end')
}
this.$emit('touchEnd', e)
}
},
// utils
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])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
}
}
</script>
<style>
.uni-canvas {
width: 100%;
height: 100%;
display: block;
}
</style>

View File

@ -1,89 +0,0 @@
{
"id": "echarts-for-uniapp",
"name": "uniapp-echarts",
"displayName": "echarts-for-uniapp",
"version": "1.0.2",
"description": "uni-app 版本 echarts, 支持 Vue2/3, 开发者可以通过熟悉的 ECharts 配置方式,快速开发图表,满足各种可视化需求.",
"keywords": [
"echarts-uniapp",
"echarts",
"echarts",
"图表",
"可视化"
],
"scripts": {
"release": "bumpp && npm publish"
},
"repository": "https://github.com/xbmlz/echarts-for-uniapp",
"engines": {
"HBuilderX": "^3.2.13"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "997909544"
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/echarts-for-uniapp"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,132 @@
## 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,372 @@
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;
}
}
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',
'font'
];
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 => {
if (style === 'font' && fontSizeReg.test(value)) {
const match = fontSizeReg.exec(value);
ctx.setFontSize(match[1]);
return;
}
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) {
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 = 12;
if (font) {
fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
}
fontSize /= 2;
return {
width: strLen(text) * fontSize
};
}
}
}
_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);
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(){
return canvas
}
})
}
}

View File

@ -0,0 +1,516 @@
<template>
<view class="lime-echart" :style="customStyle" v-if="canvasId" ref="limeEchart">
<!-- #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-if="isPc"
:style="canvasStyle"
:id="canvasId"
:canvas-id="canvasId"
:disable-scroll="isDisableScroll"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="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"
/>
<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/index.html"
@pagefinish="finished = true"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<script>
// #ifdef VUE3
// #ifdef APP-PLUS
global = {}
// #endif
// #endif
// #ifndef APP-NVUE
import {Canvas, setCanvasCreator, dispatch} from './canvas';
import { compareVersion, wrapTouch, devicePixelRatio ,sleep} from './utils';
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from './utils';
// #endif
const charts = {}
const echartsObj = {}
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
}
},
data() {
return {
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: true,
// #endif
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: false,
// #endif
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: {
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.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : ''
}
},
beforeDestroy() {
this.clear()
this.dispose()
// #ifdef H5
if(this.isPc) {
document.removeEventListener('mousewheel')
}
// #endif
},
created() {
// #ifdef H5
if(!('ontouchstart' in window)) {
this.isPc = true
document.addEventListener('mousewheel', (e) => {
if(this.chart) {
const touch = this.getTouch(e)
const handler = this.chart.getZr().handler;
dispatch.call(handler, 'mousewheel', touch)
}
})
}
// #endif
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
const { SDKVersion, version, platform, environment } = uni.getSystemInfoSync();
// #endif
// #ifdef MP-WEIXIN
this.isPC = /windows/i.test(platform)
this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0 && !((/ios/i.test(platform) && /7.0.20/.test(version)) || /wxwork/i.test(environment)) && !this.isPC;
// #endif
// #ifdef MP-TOUTIAO
this.isPC = /devtools/i.test(platform)
this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '1.78.0') >= 0;
// #endif
// #ifdef MP-ALIPAY
this.use2dCanvas = this.type === '2d' && compareVersion(my.SDKVersion, '2.7.0') >= 0;
// #endif
},
mounted() {
this.$nextTick(() => {
this.$emit('finished')
})
},
methods: {
// #ifdef APP-NVUE
onMessage(e) {
const res = e?.detail?.data[0] || null;
if (res?.event) {
if(res.event === 'inited') {
this.inited = true
}
this.$emit(res.event, JSON.parse(res.data));
} else if(res?.file){
this.file = res.data
} else if(!res[0] && JSON.stringify(res[0]) != '{}'){
console.error(res);
} else {
console.log(...res)
}
},
// #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.roptions)})`);
}
// #endif
},
setOption() {
if (!this.chart || !this.chart.setOption) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
// #ifndef APP-NVUE
this.chart.setOption(...arguments);
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
// #endif
},
showLoading() {
if(this.chart) {
// #ifndef APP-NVUE
this.chart.showLoading(...arguments)
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
// #endif
}
},
hideLoading() {
if(this.chart) {
// #ifndef APP-NVUE
this.chart.hideLoading()
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`hideLoading()`);
// #endif
}
},
clear() {
if(this.chart) {
// #ifndef APP-NVUE
this.chart.clear()
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`clear()`);
// #endif
}
},
dispose() {
if(this.chart) {
// #ifndef APP-NVUE
this.chart.dispose()
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`dispose()`);
// #endif
}
},
resize(size) {
if(size && size.width && size.height) {
this.height = size.height
this.width = size.width
if(this.chart) {this.chart.resize(size)}
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`resize(${size})`);
// #endif
} else {
this.$nextTick(() => {
// #ifndef APP-NVUE
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})
}
});
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJs(`resize()`);
// #endif
})
}
},
canvasToTempFilePath(args = {}) {
// #ifndef APP-NVUE
const { use2dCanvas, canvasId, canvasNode } = this;
return new Promise((resolve, reject) => {
const copyArgs = Object.assign({
canvasId,
success: resolve,
fail: reject
}, args);
if (use2dCanvas) {
delete copyArgs.canvasId;
copyArgs.canvas = 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) {
// #ifdef APP-NVUE
if(arguments && !arguments.length) {
console.error('缺少参数init(theme?:string, opts?: object, callback: function)')
return
}
// #endif
// #ifndef APP-NVUE
if(arguments && arguments.length < 2) {
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
if(typeof callback === 'function') {
setCanvasCreator(echarts, config)
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
callback(this.chart)
} else {
console.error('callback 非 function')
}
// #endif
// #ifdef APP-NVUE
if(callback) {
this.chart = {
setOption: (options) => {
this.roptions = options
}
}
callback(this.chart)
this.$refs.webview.evalJs(`init(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.roptions)}, ${JSON.stringify(opts)}, ${theme})`)
} else {
console.error('callback 非 function')
}
// #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
const { use2dCanvas} = this;
let dpr = devicePixelRatio
if (use2dCanvas) {
return new Promise(resolve => {
uni.createSelectorQuery()
.in(this)
.select(`#${this.canvasId}`)
.fields({
node: true,
size: true
})
.exec(res => {
let { node, width, height } = res[0];
this.width = width = width || 300;
this.height = height = height || 300;
const ctx = node.getContext('2d');
const canvas = new Canvas(ctx, this, true, node);
this.canvasNode = node
resolve({ canvas, width, height, devicePixelRatio: dpr, node });
});
});
}
return new Promise(resolve => {
uni.createSelectorQuery()
.in(this)
.select(`#${this.canvasId}`)
.boundingClientRect()
.exec(res => {
if (res) {
let { width, height } = res[0];
this.width = width = width || 300;
this.height = height = height || 300;
// #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
this.rect = res[0]
this.nodeWidth = width * dpr;
this.nodeHeight = height * dpr;
const ctx = uni.createCanvasContext(this.canvasId, this);
const canvas = new Canvas(ctx, this, false);
resolve({ canvas, width, height, devicePixelRatio: dpr });
}
});
});
// #endif
},
// #ifndef APP-NVUE
getRelative(e) {
return {x: e.pageX - this.rect.left, y: e.pageY - this.rect.top, wheelDelta: e.wheelDelta}
},
getTouch(e) {
return e.touches && e.touches[0] && e.touches[0].x ? e.touches[0] : this.getRelative(e);
},
touchStart(e) {
this.isDown = true
if (this.chart && ((e.touches.length > 0 || e.touches['0']) && e.type != 'mousemove' || e.type == 'mousedown')) {
const touch = this.getTouch(e)
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);
}
},
touchMove(e) {
if(this.isPc && this.enableHover && !this.isDown) {this.isDown = true}
if (this.chart && ((e.touches.length > 0 || e.touches['0']) && e.type != 'mousemove' || e.type == 'mousemove' && this.isDown)) {
const handler = this.chart.getZr().handler;
dispatch.call(handler, 'mousemove', this.getTouch(e))
handler.processGesture(wrapTouch(e), 'change');
}
},
touchEnd(e) {
this.isDown = false
if (this.chart) {
const {x} = e.changedTouches && e.changedTouches[0] || {}
const touch = (x ? e.changedTouches[0] : this.getRelative(e)) || {};
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
}
};
</script>
<style scoped>
.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 */
}
</style>

View File

@ -0,0 +1,74 @@
// #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
}
export function wrapTouch(event) {
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 = wx.getSystemInfoSync().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)
})
}

View File

@ -0,0 +1,84 @@
{
"id": "lime-echart",
"displayName": "百度图表 echarts",
"version": "0.6.5",
"description": "echarts 全端兼容一款使echarts图表能跑在uniapp各端中的插件",
"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"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"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"
}
}
}
}
}

View File

@ -0,0 +1,245 @@
# echarts 图表 <span style="font-size:16px;">👑👑👑👑👑 <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
> 一个基于 JavaScript 的开源可视化图表库 [查看更多 站点1](https://limeui.qcoon.cn/#/echart) | [查看更多 站点2](http://liangei.gitee.io/limeui/#/echart) <br>
> 基于 echarts 做了兼容处理,更多示例请访问 [uni示例 站点1](https://limeui.qcoon.cn/#/echart-example) | [uni示例 站点2](http://liangei.gitee.io/limeui/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html) <br>
> Q群1046793420 <br>
## 平台兼容
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
| √ | √ | √ | √ | √ | √ | √ |
## 安装
- 第一步、在uniapp 插件市场 找到 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899) 导入
- 第二步、安装 echarts 或者直接使用插件内的echarts.min文件
```cmd
pnpm add echarts
-or-
npm install echarts
```
**注意**
* 🔔 必须使用hbuilderx 3.4.8-alpha及以上
* 🔔 echarts 5.3.0及以上
* 🔔 如果是 `cli` 项目需要主动 `import` 插件
```js
import LEchart from '@/uni_modules/lime-echart/components/l-echart/l-echart.vue';
export default {
components: {LEchart}
}
```
## 代码演示
### 基础用法
```html
<view><l-echart ref="chart" @finished="init"></l-echart></view>
```
```js
// 如果你使用插件内提供的echarts.min
// 也可以自行去官网下载自定义覆盖
// 这种方式仅限于vue2
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
//---or----------------------------------
// 如果你使用 npm 安装了 echarts --------- 使用以下方式
// 引入全量包
import * as echarts from 'echarts'
//---or----------------------------------
// 按需引入 开始
import * as echarts from 'echarts/core';
import {LineChart, BarChart} from 'echarts/charts';
import {TitleComponent,TooltipComponent,GridComponent, DatasetComponent, TransformComponent, LegendComponent } from 'echarts/components';
// 标签自动布局,全局过渡动画等特性
import {LabelLayout,UniversalTransition} from 'echarts/features';
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 是必须的一步
import {CanvasRenderer} from 'echarts/renderers';
// 注册必须的组件
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LineChart,
BarChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
//-------------按需引入结束------------------------
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]
}
]
},
};
},
// 组件能被调用必须是组件的节点已经被渲染到页面上
// 1、在页面mounted里调用有时候mounted 组件也未必渲染完成
mounted() {
// init(echarts, theme?:string, opts?:{}, chart => {})
// echarts 必填, 非nvue必填nvue不用填
// theme 可选,应用的主题,目前只支持名称,如:'dark'
// opts = { // 可选
// locale?: string // 从 `5.0.0` 开始支持
// }
// chart => {} callback 必填,返回图表实例
this.$refs.chart.init(echarts, chart => {
chart.setOption(this.option);
});
},
// 2、或者使用组件的finished事件里调用
methods: {
init() {
this.$refs.chart.init(echarts, chart => {
chart.setOption(this.option);
});
}
}
}
```
## 数据更新
- 使用 `ref` 可获取`setOption`设置更新
```js
this.$refs.chart.setOption(data)
```
## 图表大小
- 在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
```js
// 默认获取容器尺寸
this.$refs.chart.resize()
// 指定尺寸
this.$refs.chart.resize({width: 375, height: 375})
```
## 常见问题
- 微信小程序 `2d` 只支持 真机调试2.0
- 微信开发工具会出现canvas不跟随页面的情况真机不影响
- toolbox 不支持 `saveImage`
- echarts 5.3.0 的 lines 不支持 trailLength故需设置为 `0`
- dataZoom H5不要设置 `showDetail`
## Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------------- | -------- | ------- | ------------ | ----- |
| custom-style | 自定义样式 | `string` | - | - |
| type | 指定 canvas 类型 | `string` | `2d` | |
| is-disable-scroll | 触摸图表时是否禁止页面滚动 | `boolean` | `false` | |
| beforeDelay | 延迟初始化 (毫秒) | `number` | `30` | |
| enableHover | PC端使用鼠标悬浮 | `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://static-6d65bd90-8508-4d6c-abbc-a4ef5c8e49e7.bspapp.com/image/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg")
![输入图片说明](https://static-6d65bd90-8508-4d6c-abbc-a4ef5c8e49e7.bspapp.com/image/wxplay.jpg "wxplay.jpg")

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,129 @@
<!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.3.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(...agrs) {
postMessage(agrs)
}
function emit(event, data) {
let dataStr = JSON.stringify(data, stringify)
postMessage({
event,
data: dataStr
})
cache = []
}
function postMessage(data) {
uni.postMessage({
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 = null) {
if(!chart) {
chart = echarts.init(document.getElementById('limeChart'), theme, opts)
if(options) {
chart.setOption(options)
}
// const name = parse('a', callback, options)
// console.log('options::', callback)
// if(name) this[name] = options
// eval(`a = ${callback};`)
// if(a) {a(chart)}
}
}
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) {
const src = chart.getDataURL(opt)
postMessage({
file: true,
data: src
})
}
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
var isReady=false;var onReadyCallbacks=[];
var isServiceReady=false;var onServiceReadyCallbacks=[];
var __uniConfig = {"pages":["pages/index/index","pages/count/count","pages/assess/assess","pages/eliminate/eliminate","pages/contribution/contribution"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"碳足迹计算器","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8","titleNView":false,"bounce":"none","scrollIndicator":"none"},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"碳中和","compilerVersion":"3.6.18","entryPagePath":"pages/index/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniConfig = {"pages":["pages/index/index","pages/count/count","pages/assess/assess","pages/eliminate/eliminate","pages/contribution/contribution"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"碳足迹计算器","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8","titleNView":false,"bounce":"none","scrollIndicator":"none"},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"碳足迹计算器","compilerVersion":"3.6.18","entryPagePath":"pages/index/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniRoutes = [{"path":"/pages/index/index","meta":{"isQuit":true},"window":{"navigationBarTitleText":"设备判断"}},{"path":"/pages/count/count","meta":{},"window":{"navigationBarTitleText":"计算碳足迹"}},{"path":"/pages/assess/assess","meta":{},"window":{"navigationBarTitleText":"评估碳足迹"}},{"path":"/pages/eliminate/eliminate","meta":{},"window":{"navigationBarTitleText":"消除碳足迹"}},{"path":"/pages/contribution/contribution","meta":{},"window":{"navigationBarTitleText":"在线捐款"}}];
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__DB95977","name":"碳中和","version":{"name":"1.0.0","code":"100"},"description":"","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"icons":{"android":{"hdpi":"unpackage/res/icons/72x72.png","xhdpi":"unpackage/res/icons/96x96.png","xxhdpi":"unpackage/res/icons/144x144.png","xxxhdpi":"unpackage/res/icons/192x192.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"}}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"]},"apple":{"dSYMs":false},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}}},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"3.6.18","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html"}}
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__DB95977","name":"碳足迹计算器","version":{"name":"1.0.0","code":"100"},"description":"","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"icons":{"android":{"hdpi":"unpackage/res/icons/72x72.png","xhdpi":"unpackage/res/icons/96x96.png","xxhdpi":"unpackage/res/icons/144x144.png","xxxhdpi":"unpackage/res/icons/192x192.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"}}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"]},"apple":{"dSYMs":false},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}}},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"3.6.18","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html"}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long