添加页面

This commit is contained in:
wangxiaowei
2025-09-22 18:48:19 +08:00
parent 8ce688c697
commit 9bfdf0b03e
31 changed files with 4121 additions and 155 deletions

View File

@ -99,6 +99,7 @@
"abortcontroller-polyfill": "^1.7.8",
"alova": "^3.3.3",
"dayjs": "1.11.10",
"echarts": "^6.0.0",
"js-cookie": "^3.0.5",
"pinia": "2.0.36",
"pinia-plugin-persistedstate": "3.2.1",

49
pnpm-lock.yaml generated
View File

@ -85,6 +85,9 @@ importers:
dayjs:
specifier: 1.11.10
version: 1.11.10
echarts:
specifier: ^6.0.0
version: 6.0.0
js-cookie:
specifier: ^3.0.5
version: 3.0.5
@ -237,12 +240,23 @@ importers:
specifier: ^2.2.10
version: 2.2.12(typescript@5.9.2)
src/uni_modules/lime-echart:
dependencies:
echarts:
specifier: ^5.4.1
version: 5.6.0
zrender:
specifier: ^5.4.3
version: 5.6.1
src/uni_modules/mescroll-uni: {}
src/uni_modules/uni-icons: {}
src/uni_modules/uni-scss: {}
src/uni_modules/xiaohe-echarts: {}
packages:
'@alova/adapter-uniapp@2.0.14':
@ -3428,6 +3442,12 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
echarts@5.6.0:
resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
echarts@6.0.0:
resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@ -5979,6 +5999,9 @@ packages:
ts-morph@25.0.1:
resolution: {integrity: sha512-QJEiTdnz1YjrB3JFhd626gX4rKHDLSjSVMvGGG4v7ONc3RBwa0Eei98G9AT9uNFDMtV54JyuXsFeC+OH0n6bXQ==}
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@ -6436,6 +6459,12 @@ packages:
resolution: {integrity: sha512-RcDeKFoCQB51dmrrTb1PMIazjTqGuAbFmjPS0/N5hdUNTCRvxGOOBTBFolvIxUcsWhrocI9C0mYDgUwXT6Dwcg==}
engines: {HBuilderX: ^3.0.7}
zrender@5.6.1:
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
zrender@6.0.0:
resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@ -10556,6 +10585,16 @@ snapshots:
eastasianwidth@0.2.0: {}
echarts@5.6.0:
dependencies:
tslib: 2.3.0
zrender: 5.6.1
echarts@6.0.0:
dependencies:
tslib: 2.3.0
zrender: 6.0.0
ee-first@1.1.1: {}
electron-to-chromium@1.5.194: {}
@ -13673,6 +13712,8 @@ snapshots:
'@ts-morph/common': 0.26.1
code-block-writer: 13.0.3
tslib@2.3.0: {}
tslib@2.8.1: {}
tsx@4.20.3:
@ -14134,4 +14175,12 @@ snapshots:
z-paging@2.8.7: {}
zrender@5.6.1:
dependencies:
tslib: 2.3.0
zrender@6.0.0:
dependencies:
tslib: 2.3.0
zwitch@2.0.4: {}

View File

@ -9,132 +9,6 @@
<template>
<view class="pb-254rpx">
<!-- 平台团购直营店 -->
<view>
<navbar :title="title" custom-class='!bg-[#F6F7F8]'></navbar>
</view>
<view class="text-[#909399] text-26rpx leading-36rpx mx-102rpx mb-40rpx">
<view v-if="orderStatus === OrderSourceOrderStatus.AfterSaleApply || orderStatus === OrderSourceOrderStatus.AfterSaleProcessing">请耐心等待,我们会尽快处理您的请求</view>
<view v-if="orderStatus === OrderSourceOrderStatus.Refunded" class="text-center mt-14rpx">
<view class="text-40rpx text-[#303133] leading-56rpx">
<view v-if="orderType !== OrderSource.TeaSpecialist">退款成功¥128.00</view>
<!-- 茶艺师退款需要有退款详情 -->
<view v-if="orderType === OrderSource.TeaSpecialist" @click="showRefundDetailsPopup = true">
退款成功¥128.00
<wd-icon name="arrow-right" size="40rpx" color="#666666" custom-class="!bg-[#F8F9FA]"></wd-icon>
</view>
</view>
<view class="text-28rpx text-[#606266] leading-54rpx mt-20rpx">谢谢您的信任,我们一定会做的更好</view>
<view class="text-24rpx text-[#606266] leading-34rpx mt-12rpx">2025年4月13日 18:22</view>
</view>
</view>
<!-- 订单 -->
<view class="mx-30rpx">
<view class="bg-white rounded-16rpx p-30rpx">
<view class="flex items-center">
<view class="mr-30rpx">
<wd-img width="190rpx" height="190rpx" :src="`${OSS}images/home/home_image5.png`" mode="scaleToFill"></wd-img>
</view>
<view class="flex-1">
<!-- 非茶艺师退款 -->
<view class="flex justify-between items-center" @click="afterSales.handleToCombo" v-if="orderType !== OrderSource.TeaSpecialist">
<view class="font-bold text-30rpx leading-42rpx text-[#303133] mr-10rpx w-362rpx line-1">这是团购套餐的可以点击进 </view>
<wd-icon name="chevron-right" size="32rpx"></wd-icon>
</view>
<!-- 茶艺师退款 -->
<view class="flex justify-between items-center" @click="afterSales.handleToCombo" v-if="orderType === OrderSource.TeaSpecialist">
<view class="font-bold text-30rpx leading-42rpx text-[#303133] mr-10rpx">这是茶艺师名称</view>
<view class="text-[#909399] text-26rpx leading-36rpx">¥324.22</view>
</view>
<view class="flex justify-between items-center text-26rpx leading-36rpx text-[#909399] mt-18rpx">
<view>3小时</view>
<view>x1</view>
</view>
<!-- 茶艺师退显示车马费-->
<view class="flex justify-between items-center text-26rpx leading-36rpx text-[#909399] mt-18rpx" v-if="orderType === OrderSource.TeaSpecialist">
<view>车马费¥3.00元/公里)</view>
<view>¥30.90</view>
</view>
<view class="text-[#606266] text-right mt-26rpx">
<text class="text-24rpx leading-34rpx mr-12rpx">实付</text>
<text class="tetx-32rpx leading-36rpx">¥29.32</text>
<wd-icon name="chevron-right" size="32rpx"></wd-icon>
</view>
</view>
</view>
</view>
</view>
<!-- 售后原因 -->
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-20rpx" @click="showResonPopup = true" v-if="orderStatus === OrderSourceOrderStatus.AfterSaleApply">
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between">
<view class="text-32rpx leading-44rpx text-[#303133]">售后原因</view>
<view>
<text class='text-[#909399] mr-10rpx'>{{ formData.reason ? reasonText : '请至少选择一项' }}</text>
<wd-icon name="arrow-right" size="24rpx" color="#666666" custom-class="!bg-[#F8F9FA]"></wd-icon>
</view>
</view>
</view>
<!-- 订单信息(选填) -->
<view class="bg-white rounded-16rpx px-30rpx pb-32rpx mx-30rpx mt-20rpx" v-if="orderStatus === OrderSourceOrderStatus.AfterSaleApply">
<view class="pt-32rpx text-[#303133] text-32rpx leading-44rpx">
<text>订单信息</text>
<text class="text-26rpx text-[#909399] leading-36rpx ml-20rpx">(选填)</text>
</view>
<view class="mt-28rpx">
<wd-textarea placeholder="有想说的可以在这里写哦!" v-model="formData.info" custom-class='!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA]' custom-textarea-class='!bg-[#F8F9FA]' />
</view>
</view>
<!-- 售后已完成 -->
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-20rpx" v-if="orderStatus === OrderSourceOrderStatus.Refunded">
<view class="text-[#303133] text-32rpx leading-44rpx">售后订单</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx" v-if="orderType !== OrderSource.TeaSpecialist">
<view>售后原因</view>
<view>买多了/买错了</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx" v-if="orderType !== OrderSource.TeaSpecialist">
<view>退款金额</view>
<view>¥159.22</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
<view>申请时间</view>
<view>2019-05-16 13:20:26</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
<view>退款编号</view>
<view>
<text>7327328627526903</text>
<wd-divider vertical />
<text class="text-[#4C9F44]">复制</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="w-full fixed bottom-0 left-0 right-0 h-152rpx"
:class="orderStatus === OrderSourceOrderStatus.AfterSaleApply ? '' : 'bg-white'"
v-if="orderType !== OrderSource.TeaSpecialist">
<view class="mt-34rpx">
<!-- 待使用 -->
<view class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center text-[#303133]"
v-if="orderStatus !== OrderSourceOrderStatus.AfterSaleApply">
<view class="w-330rpx h-90rpx bg-[#F6F7F8] rounded-8rpx mr-30rpx">联系商家</view>
<view class="w-330rpx h-90rpx bg-[#F6F7F8] rounded-8rpx">联系平台</view>
</view>
<!-- 申请售后 -->
<view class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center"
v-if="orderStatus === OrderSourceOrderStatus.AfterSaleApply" @click="afterSales.handleSubmitRefund">
<view class="w-630rpx h-90rpx bg-[#4C9F44] rounded-8rpx text-[#fff]">提交</view>
</view>
</view>
</view>
<!-- 售后原因弹出框 -->
<wd-popup v-model="showResonPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
<view class="relative pb-78rpx">
@ -160,7 +34,7 @@
</view>
</wd-radio-group>
</view>
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff] mx-auto mt-160rpx" @click="afterSales.handleSelectReason">确定</view>
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff] mx-auto mt-160rpx" @click="AfterSales.handleSelectReason">确定</view>
</view>
</wd-popup>
@ -294,11 +168,137 @@
</view>
</view>
</wd-popup>
<!-- 平台团购直营店 -->
<view>
<navbar :title="title" custom-class='!bg-[#F6F7F8]'></navbar>
</view>
<view class="text-[#909399] text-26rpx leading-36rpx mx-102rpx mb-40rpx">
<view v-if="orderStatus === OrderStatus.AfterSaleApply || orderStatus === OrderStatus.AfterSaleProcessing">请耐心等待我们会尽快处理您的请求</view>
<view v-if="orderStatus === OrderStatus.Refunded" class="text-center mt-14rpx">
<view class="text-40rpx text-[#303133] leading-56rpx">
<view v-if="orderType !== OrderSource.TeaSpecialist">退款成功128.00</view>
<!-- 茶艺师退款需要有退款详情 -->
<view v-if="orderType === OrderSource.TeaSpecialist" @click="showRefundDetailsPopup = true">
退款成功128.00
<wd-icon name="arrow-right" size="40rpx" color="#666666" custom-class="!bg-[#F8F9FA]"></wd-icon>
</view>
</view>
<view class="text-28rpx text-[#606266] leading-54rpx mt-20rpx">谢谢您的信任我们一定会做的更好</view>
<view class="text-24rpx text-[#606266] leading-34rpx mt-12rpx">2025年4月13日 18:22</view>
</view>
</view>
<!-- 订单 -->
<view class="mx-30rpx">
<view class="bg-white rounded-16rpx p-30rpx">
<view class="flex items-center">
<view class="mr-30rpx">
<wd-img width="190rpx" height="190rpx" :src="`${OSS}images/home/home_image5.png`" mode="scaleToFill"></wd-img>
</view>
<view class="flex-1">
<!-- 非茶艺师退款 -->
<view class="flex justify-between items-center" @click="AfterSales.handleToCombo" v-if="orderType !== OrderSource.TeaSpecialist">
<view class="font-bold text-30rpx leading-42rpx text-[#303133] mr-10rpx w-362rpx line-1">这是团购套餐的可以点击进 </view>
<wd-icon name="chevron-right" size="32rpx"></wd-icon>
</view>
<!-- 茶艺师退款 -->
<view class="flex justify-between items-center" @click="AfterSales.handleToCombo" v-if="orderType === OrderSource.TeaSpecialist">
<view class="font-bold text-30rpx leading-42rpx text-[#303133] mr-10rpx">这是茶艺师名称</view>
<view class="text-[#909399] text-26rpx leading-36rpx">324.22</view>
</view>
<view class="flex justify-between items-center text-26rpx leading-36rpx text-[#909399] mt-18rpx">
<view>3小时</view>
<view>x1</view>
</view>
<!-- 茶艺师退显示车马费-->
<view class="flex justify-between items-center text-26rpx leading-36rpx text-[#909399] mt-18rpx" v-if="orderType === OrderSource.TeaSpecialist">
<view>车马费3.00/公里</view>
<view>30.90</view>
</view>
<view class="text-[#606266] text-right mt-26rpx">
<text class="text-24rpx leading-34rpx mr-12rpx">实付</text>
<text class="tetx-32rpx leading-36rpx">29.32</text>
<wd-icon name="chevron-right" size="32rpx"></wd-icon>
</view>
</view>
</view>
</view>
</view>
<!-- 售后原因 -->
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-20rpx" @click="showResonPopup = true" v-if="orderStatus === OrderStatus.AfterSaleApply">
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between">
<view class="text-32rpx leading-44rpx text-[#303133]">售后原因</view>
<view>
<text class='text-[#909399] mr-10rpx'>{{ formData.reason ? reasonText : '请至少选择一项' }}</text>
<wd-icon name="arrow-right" size="24rpx" color="#666666" custom-class="!bg-[#F8F9FA]"></wd-icon>
</view>
</view>
</view>
<!-- 订单信息(选填) -->
<view class="bg-white rounded-16rpx px-30rpx pb-32rpx mx-30rpx mt-20rpx" v-if="orderStatus === OrderStatus.AfterSaleApply">
<view class="pt-32rpx text-[#303133] text-32rpx leading-44rpx">
<text>订单信息</text>
<text class="text-26rpx text-[#909399] leading-36rpx ml-20rpx">(选填)</text>
</view>
<view class="mt-28rpx">
<wd-textarea placeholder="有想说的可以在这里写哦!" v-model="formData.info" custom-class='!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA]' custom-textarea-class='!bg-[#F8F9FA]' />
</view>
</view>
<!-- 售后已完成 -->
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-20rpx" v-if="orderStatus === OrderStatus.Refunded">
<view class="text-[#303133] text-32rpx leading-44rpx">售后订单</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx" v-if="orderType !== OrderSource.TeaSpecialist">
<view>售后原因</view>
<view>买多了/买错了</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx" v-if="orderType !== OrderSource.TeaSpecialist">
<view>退款金额</view>
<view>159.22</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
<view>申请时间</view>
<view>2019-05-16 13:20:26</view>
</view>
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
<view>退款编号</view>
<view>
<text>7327328627526903</text>
<wd-divider vertical />
<text class="text-[#4C9F44]">复制</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="w-full fixed bottom-0 left-0 right-0 h-152rpx"
:class="orderStatus === OrderStatus.AfterSaleApply ? '' : 'bg-white'"
v-if="orderType !== OrderSource.TeaSpecialist">
<view class="mt-34rpx">
<!-- 待使用 -->
<view class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center text-[#303133]"
v-if="orderStatus !== OrderStatus.AfterSaleApply">
<view class="w-330rpx h-90rpx bg-[#F6F7F8] rounded-8rpx mr-30rpx">联系商家</view>
<view class="w-330rpx h-90rpx bg-[#F6F7F8] rounded-8rpx">联系平台</view>
</view>
<!-- 申请售后 -->
<view class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center"
v-if="orderStatus === OrderStatus.AfterSaleApply" @click="AfterSales.handleSubmitRefund">
<view class="w-630rpx h-90rpx bg-[#4C9F44] rounded-8rpx text-[#fff]">提交</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { OrderStatusTitle, PersonalReasonMap, MerchantReasonMap, ReasonMap, OrderSource } from '@/utils/order'
import { OrderStatusTitle, PersonalReasonMap, MerchantReasonMap, ReasonMap, OrderSource, OrderStatus } from '@/utils/order'
import {toast} from '@/utils/toast'
/** 表单相关 **/
@ -340,7 +340,7 @@
orderStatus.value = args.orderStatus
})
const afterSales = {
const AfterSales = {
handleToCombo: () => {
toast.info('跳转到套餐详情')
},

View File

@ -0,0 +1,162 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "添加新银行卡"
}
}
</route>
<template>
<view>
<view>
<view class="flex justify-between items-center mx-38rpx">
<view class="text-30rpx leading-42rpx text-#303133">银行</view>
<view class="flex items-center">
<view>
<wd-input inputmode="decimal" size="large" placeholder="请选择银行" required no-border custom-input-class="!text-right" placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;text-align: right;"></wd-input>
</view>
<view>
<wd-icon name='chevron-right' size="32rpx" color="#909399"></wd-icon>
</view>
</view>
</view>
<view>
<wd-gap height="2rpx" bg-color="#F2F2F2"></wd-gap>
</view>
</view>
<view>
<view class="flex justify-between items-center mx-38rpx">
<view class="text-30rpx leading-42rpx text-#303133">银行卡号</view>
<view class="flex items-center">
<view >
<wd-input inputmode="decimal" size="large" placeholder="请输入银行卡号" required no-border custom-input-class="!text-right" placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;text-align: right;"></wd-input>
</view>
</view>
</view>
<view>
<wd-gap height="2rpx" bg-color="#F2F2F2"></wd-gap>
</view>
</view>
<view>
<view class="flex justify-between items-center mx-38rpx">
<view class="text-30rpx leading-42rpx text-#303133">绑定手机号</view>
<view class="flex items-center">
<view >
<wd-input inputmode="decimal" size="large" placeholder="请输入银行卡号" no-border custom-input-class="!text-right" placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;text-align: right;"></wd-input>
</view>
</view>
</view>
<view>
<wd-gap height="2rpx" bg-color="#F2F2F2"></wd-gap>
</view>
</view>
<view>
<view class="flex justify-between items-center mx-38rpx">
<view class="text-30rpx leading-42rpx text-#303133 w-100rpx">验证码</view>
<view class="flex items-center">
<view>
<wd-input inputmode="decimal" size="large" placeholder="请输入银行卡号" no-border custom-input-class="!text-right" placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;text-align: right;">
<template #suffix>
<view class="flex items-center">
<view class="flex items-center">
<view>
<wd-divider color="#C9C9C9" vertical />
</view>
<view class="text-[#4C9F44] text-28rpx font-400 leading-40rpx" v-if="!startCountDown" @click="Add.handleCountDown">发送</view>
<view class="!text-[#C9C9C9] text-28rpx font-400 leading-40rpx flex items-center" v-if="startCountDown">
<wd-count-down ref="countDown" :time="countDownTime" millisecond :auto-start="false" format="ss" custom-class="!text-[#C9C9C9] !text-32rpx" @finish="Add.handleFinishCountDown"></wd-count-down>
<view> S后重发</view>
</view>
</view>
</view>
</template>
</wd-input>
</view>
</view>
</view>
<view>
<wd-gap height="2rpx" bg-color="#F2F2F2"></wd-gap>
</view>
</view>
<view class="font-bold text-30rpx leading-42rpx bg-[#4C9F44] text-#fff rounded-8rpx mx-60rpx h-90rpx leading-90rpx text-center mt-196rpx" @click="Add.handleBindBankCard">
绑定银行卡
</view>
</view>
</template>
<script lang="ts" setup>
import {toast} from '@/utils/toast'
const OSS = inject('OSS')
// 选择银行卡
const showBankCardPopup = ref<boolean>(false)
const selectedBankCardIndex = ref<number>(0)
const bankList = ref<Array<{ id: number, bankName: string, bankCard: string }>>([
{ id: 1, bankName: '招商银行', bankCard: '3265' },
{ id: 2, bankName: '建设银行', bankCard: '1234' },
{ id: 3, bankName: '农业银行', bankCard: '5678' },
])
// 提现金额
const withdrawMoney = ref<number>(0)
// 验证码倒计时
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
// 表单
const form = reactive<{
settleIn: number,
code: string,
message: string,
area: string[]
}>({
settleIn: 1,
code: '',
message: '',
area: []
})
const Add = {
// 确定提现
handleConfirmwithdrawMoney: () => {
},
// 发送验证码
handleCountDown: () => {
startCountDown.value = true
nextTick(() => {
countDown.value?.start()
// 发送验证码请求
})
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
// 绑定银行卡
handleBindBankCard: () => {
toast.info('绑定成功')
}
}
</script>
<style lang="scss" scoped>
page {
background: #fff;
}
</style>

View File

@ -0,0 +1,99 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#F6F7F9",
"navigationBarTitleText": "银行卡"
}
}
</route>
<template>
<view>
<!-- 删除确认框 -->
<wd-message-box selector="wd-message-box-slot"></wd-message-box>
<view class="flex items-center mx-58rpx mb-28rpx">
<view class="font-bold text-28rpx leading-40rpx text-#303133">我的银行卡9</view>
<view class="flex items-center">
<view class="flex items-center mr-12rpx">
<wd-img :src="`${OSS}icon/icon_safe.png`" width="32rpx" height="32rpx"></wd-img>
</view>
<view class="font-400 text-24rpx leading-34rpx text-#B6843C">保障中</view>
</view>
</view>
<view class="mx-30rpx">
<view class="bg-white rounded-16rpx px-30rpx py-26rpx mb-20rpx flex items-center justify-between" v-for="(item, index) in bankList" :key="index" >
<view class="flex items-center">
<view class="flex items-center mr-22rpx">
<wd-img :src="`${OSS}icon/icon_bank_card4.png`" width="48rpx" height="48rpx"></wd-img>
</view>
<view class="font-400 text-30rpx leading-42rpx text-#303133">招商银行 储蓄卡3265</view>
</view>
<view @click="List.handleDeleteBankCard(index)">
<wd-icon name="delete-thin" size="42rpx" color="#BFC2CC"></wd-icon>
</view>
</view>
</view>
<view class="font-bold text-30rpx leading-42rpx bg-[#4C9F44] text-#fff rounded-8rpx mx-60rpx h-90rpx leading-90rpx text-center mt-82rpx" @click="List.handleAddBankCard">
添加其他银行卡
</view>
</view>
</template>
<script lang="ts" setup>
import { useMessage } from 'wot-design-uni'
import {toast} from '@/utils/toast'
const OSS = inject('OSS')
const message = useMessage('wd-message-box-slot')
// 选择银行卡
const showBankCardPopup = ref<boolean>(false)
const selectedBankCardIndex = ref<number>(0)
const bankList = ref<Array<{ id: number, bankName: string, bankCard: string }>>([
{ id: 1, bankName: '招商银行', bankCard: '3265' },
{ id: 2, bankName: '建设银行', bankCard: '1234' },
{ id: 3, bankName: '农业银行', bankCard: '5678' },
])
const List = {
handleDeleteBankCard: (index: number) => {
message.confirm({
title: '是否删除银行卡',
msg: '删除银行卡后无法恢复,确定删除银行卡吗?',
confirmButtonText: '去预定',
cancelButtonText: '取消',
cancelButtonProps: {
customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
},
confirmButtonProps: {
customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
}
}).then((res) => {
bankList.value.splice(index, 1)
toast.info('删除成功')
// 点击确认按钮回调事件
}).catch(() => {
// 点击取消按钮回调事件
})
},
// 跳转到添加银行卡页面
handleAddBankCard: () => {
uni.navigateTo({
url: '/bundle/parten/pages/bank-card/add'
})
}
}
</script>
<style lang="scss" scoped>
page {
background: $cz-page-background;
}
</style>

View File

@ -1,24 +1,244 @@
<route lang="jsonc" type="page">
{
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationBarTitleText": ""
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "收益明细"
}
}
</route>
}</route>
<template>
<view>
<view class="mt-30rpx">
<view class="text-32rpx leading-62rpx text-#111827 mx-30rpx">本月运营数据</view>
<!-- 柱状图 -->
<view class="mx-30rpx mt-16rpx">
<uni-echarts custom-class="chart" :option="option"></uni-echarts>
</view>
<!-- 数据卡片 -->
<view class="flex items-center justify-between mx-60rpx mt-20rpx">
<view class="bg-#FAFAFA rounded-16rpx px-30rpx py-18rpx w-300rpx">
<view class="mb-16rpx">
<view class="text-22rpx leading-32rpx text-#718096">本月销售额()</view>
<view class="font-400 text-36rpx leading-50rpx text-#303133 mt-6rpx">38,456.00</view>
</view>
<view>
<view class="text-22rpx leading-32rpx text-#718096">上月销售额()</view>
<view class="font-400 text-36rpx leading-50rpx text-#303133 mt-6rpx">21,300.00</view>
</view>
</view>
<view class="bg-#FAFAFA rounded-16rpx px-30rpx py-18rpx w-300rpx">
<view class="mb-16rpx">
<view class="text-22rpx leading-32rpx text-#718096">本月订单数</view>
<view class="font-400 text-36rpx leading-50rpx text-#303133 mt-6rpx">1,286</view>
</view>
<view>
<view class="text-22rpx leading-32rpx text-#718096">失效订单数</view>
<view class="font-400 text-36rpx leading-50rpx text-#303133 mt-6rpx">63</view>
</view>
</view>
</view>
<!-- 分隔线 -->
<view class="h-20rpx bg-#F6F7F8 mt-50rpx mb-28rpx"></view>
<view class="">
<tabbar tab="bill"></tabbar>
<view class="flex justify-between items-center mx-30rpx">
<view class="text-32rpx leading-44rpx text-[#303133] mb-20rpx">收益明细</view>
<view class="border-2rpx border-solid border-[#E5E5E5] w-196rpx h-56rpx flex justify-center items-center rounded-8rpx">
<view class="text-24rpx leading-34rpx text-[#606266] wall-date">
<!-- 2019年5月 -->
<wd-datetime-picker v-model="value" :maxDate="Date.now()" type="year-month" @confirm="Bill.handleConfirmDate"></wd-datetime-picker>
</view>
<view>
<wd-icon name="fill-arrow-down" size="32rpx" color="#BFC2CC"></wd-icon>
</view>
</view>
</view>
<!-- 分隔线 -->
<view class="h-2rpx bg-#F6F7F8 mt-22rpx"></view>
<view class="mt-30rpx" >
<!-- 数据列表 最后一个元素不显示border-->
<mescroll-body @init="mescrollInit" @down="downCallback" @up="Bill.upCallback">
<view>
<view class="mx-30rpx">
<view class="flex items-center">
<view class="w-12rpx h-12rpx rounded-12rpx bg-#4C9F44 mr-14rpx"></view>
<view class="font-400 text-26rpx leading-36rpx text-#9CA3AF">下单时间2025-04-23 17:21:32</view>
</view>
<view class="flex items-center justify-between mt-28rpx">
<view class="mr-18rpx">
<wd-img width="60rpx" height="60rpx" :src="`${OSS}icon/icon_room.png`"></wd-img>
<!-- <wd-img width="60rpx" height="60rpx" :src="`${OSS}icon/icon_avatar2.png`"></wd-img> -->
</view>
<view class="flex-1 flex items-center justify-between">
<view>
<view class="flex items-center">
<view class="font-bold text-30rpx leading-42rpx text-#303133 mr-18rpx">包间预定</view>
<!-- <view class="font-bold text-30rpx leading-42rpx text-#303133 mr-18rpx">茶艺师预定</view> -->
<view>
<wd-tag color="#F55726" bg-color="#F55726" plain custom-class="!rounded-4rpx">申请中</wd-tag>
<!-- <wd-tag color="#40AE36" bg-color="#40AE36" plain custom-class="!rounded-4rpx">已结算</wd-tag>
<wd-tag color="#C9C9C9" bg-color="#C9C9C9" plain custom-class="!rounded-4rpx">订单取消</wd-tag> -->
</view>
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133 mt-14rpx">预定号15001901995</view>
</view>
<view class="text-right">
<view>
<price-format color="#FF5951" :first-size="40" :second-size="40" :subscript-size="28" :price="23.02"></price-format>
<!-- 不予结算的情况下价格显示的颜色不一样 -->
<!-- <price-format color="#909399" :first-size="40" :second-size="40" :subscript-size="28" :price="23.02"></price-format> -->
</view>
<view class="font-400 text-26rpx leading-36rpx text-#9CA3AF mt-14rpx">预估收益</view>
<!-- <view class="font-400 text-26rpx leading-36rpx text-#9CA3AF mt-14rpx">已收益</view>
<view class="font-400 text-26rpx leading-36rpx text-#9CA3AF mt-14rpx">不予结算</view> -->
</view>
</view>
</view>
</view>
<view class="h-2rpx bg-#F6F7F8 mt-28rpx mb-30rpx"></view>
</view>
</mescroll-body>
</view>
</view>
</view>
<tabbar tab="bill" />
</view>
</template>
<script lang="ts" setup>
import Tabbar from '@/bundle/parten/components/Tabbar.vue'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { BarChart } from "echarts/charts";
import { DatasetComponent, LegendComponent, TooltipComponent, GridComponent } from "echarts/components";
import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { ref } from "vue";
import type { EChartsOption } from 'echarts'
use([
LegendComponent,
TooltipComponent,
DatasetComponent,
BarChart,
CanvasRenderer,
GridComponent
]);
const OSS = inject('OSS')
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
// 柱状图
const option = ref<EChartsOption>({
tooltip: { trigger: 'axis' },
legend: { show: false },
grid: { left: 12, right: 12, top: 10, bottom: 10, containLabel: true },
xAxis: {
type: 'category',
data: ['一', '二', '三', '四', '五', '六', '日'],
axisTick: { alignWithLabel: true }
},
yAxis: { type: 'value', splitNumber: 4 },
series: [
{
name: '销售额',
type: 'bar',
barWidth: '40%',
itemStyle: { color: '#4C9F44', borderRadius: [4, 4, 0, 0] },
data: [120, 200, 150, 80, 70, 110, 130]
}
]
})
// 日期过滤
const value = ref<number>(Date.now())
const Bill = {
// 上拉加载的回调: 其中num:当前页 从1开始, size:每页数据条数,默认10
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
// list({
// page: mescroll.num,
// size: mescroll.size
// }).then((res: { list: Array<any>, totalPages: Number }) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// console.log("🚀 ~ goods:", goods)
// mescroll.endByPage(curPageData.length, res.totalPages); //必传参数(当前页的数据个数, 总页数)
// }).catch(() => {
// mescroll.endErr(); // 请求失败, 结束加载
// })
// apiGoods(mescroll.num, mescroll.size).then(res=>{
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// //联网成功的回调,隐藏下拉刷新和上拉加载的状态;
// //mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
// //方法一(推荐): 后台接口有返回列表的总页数 totalPage
// //mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
// //方法二(推荐): 后台接口有返回列表的总数据量 totalSize
// //mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
// //方法三(推荐): 您有其他方式知道是否有下一页 hasNext
// //mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
// //方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据.
// mescroll.endSuccess(curPageData.length); // 请求成功, 结束加载
// }).catch(()=>{
mescroll.endErr(); // 请求失败, 结束加载
// })
},
// 确认日期
handleConfirmDate: (date: {value: number}) => {
const d = new Date(date.value)
console.log("🚀 ~ d:", d)
const year = d.getFullYear()
const month = d.getMonth() + 1
console.log(`${year}${month}`);
},
}
</script>
<style lang="scss">
.chart {
height: 500rpx;
}
<style lang="scss" scoped>
.wall-date {
:deep() {
.wd-cell {
padding-left: 0 !important;
background-color: transparent !important;
}
.wd-cell__wrapper {
padding: 0 !important
}
.wd-datetime-picker__arrow {
display: none !important;
}
.wd-datetime-picker__action--cancel {
color: #606266 !important;
}
.wd-datetime-picker__action {
color: #4C9F44;
}
}
}
</style>

View File

@ -0,0 +1,122 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "资金记录"
}
}
</route>
<template>
<view>
<view class="h-20rpx bg-#F6F7F8"></view>
<view class="pt-30rpx">
<view class="flex items-center mx-30rpx">
<view
class="bg-#F6F7F8 w-130rpx h-56rpx leading-56rpx rounded-8rpx text-center font-400 text-28rpx text-#303133 mr-16rpx"
:class="currentTab === index ? '!bg-#4C9F44 text-#fff' : ''"
v-for="(item, index) in tab" :key="index"
@click="FundRecord.handleChangeTab(index)">
{{ item.name }}
</view>
</view>
<view class="mt-20rpx">
<mescroll-body @init="mescrollInit" @down="downCallback" @up="FundRecord.upCallback">
<view class="mx-30rpx border-b border-b-solid mb-26rpx pb-30rpx" v-for="(item, index) in 10" :key="index">
<view class="flex items-center justify-between">
<view class="flex items-center">
<view class="text-28rpx text-#303133 leading-40rpx mr-30rpx">提现</view>
<wd-tag color="#40AE36" bg-color="#40AE36" plain custom-class="!rounded-4rpx">申请中</wd-tag>
<!-- <view class="w-86rpx h-32rpx leading-24rpx text-center rounded-4rpx text-22rpx border-2rpx border-solid border-#40AE36 text-#40AE36">申请中</view> -->
</view>
<view class="font-bold text-36rpx text-#303133 leading-50rpx">-5000.00</view>
</view>
<view class="mt-10rpx flex items-center justify-between text-24rpx text-#909399 leading-34rpx">
<view>2024-05-20 12:30:20</view>
<view>余额1388.22</view>
</view>
</view>
</mescroll-body>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
const OSS = inject('OSS')
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom)
// tab 切换
const tab = ref<Array<{ id: number, name: string }>>([
{ id: 1, name: '全部' },
{ id: 2, name: '收入' },
{ id: 3, name: '支出' },
])
const currentTab = ref<number>(0)
const FundRecord = {
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
// list({
// page: mescroll.num,
// size: mescroll.size
// }).then((res: { list: Array<any>, totalPages: Number }) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// console.log("🚀 ~ goods:", goods)
// mescroll.endByPage(curPageData.length, res.totalPages); //必传参数(当前页的数据个数, 总页数)
// }).catch(() => {
// mescroll.endErr(); // 请求失败, 结束加载
// })
// apiGoods(mescroll.num, mescroll.size).then(res=>{
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// //联网成功的回调,隐藏下拉刷新和上拉加载的状态;
// //mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
// //方法一(推荐): 后台接口有返回列表的总页数 totalPage
// //mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
// //方法二(推荐): 后台接口有返回列表的总数据量 totalSize
// //mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
// //方法三(推荐): 您有其他方式知道是否有下一页 hasNext
// //mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
// //方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据.
// mescroll.endSuccess(curPageData.length); // 请求成功, 结束加载
// }).catch(()=>{
mescroll.endErr(); // 请求失败, 结束加载
// })
},
handleChangeTab: (index: number) => {
currentTab.value = index
getMescroll().resetUpScroll() // 重置上拉
}
}
</script>
<style lang="scss" scoped>
page {
background: #FFF;
}
</style>

View File

@ -29,7 +29,6 @@
<view class="flex items-center mr-12rpx">
<wd-img width="36rpx" height="36rpx" mode="aspectFill" :src="`${OSS}icon/icon_crown.png`" round></wd-img>
</view>
<!-- 这里要根据用户身份显示不同的文字 -->
<view class="text-24rpx text-[#675649] leading-34rpx flex items-center">茶址合伙人</view>
</view>
</view>
@ -48,7 +47,7 @@
</view>
<!-- 余额显示 -->
<view class="mt-30rpx relative">
<view class="mt-20rpx relative">
<wd-img width="750rpx" height="360rpx" :src="`${OSS}images/parten/parten_image2.png`" mode="aspectFill" />
<view class="absolute top-12rpx left-30rpx right-0 w-full">
@ -128,7 +127,7 @@
handleToMyBank: () => {
uni.navigateTo({
url: '/bundle/parten/pages/my-bank/my-bank'
url: '/bundle/parten/pages/bank-card/list'
})
},

View File

@ -0,0 +1,182 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
<template>
<view class="home-bg">
<view class="home-bg w-[100%] fixed top-0 left-0 z-100">
<view>
<navbar custom-class='!bg-[transparent]'></navbar>
</view>
</view>
<view class="pb-200rpx" :style="{ paddingTop: navbarHeight + 'px' }">
<!-- 账号昵称显示 -->
<view class="ml-60rpx flex items-center">
<view>
<wd-img width="120rpx" height="120rpx" :src="`${OSS}icon/icon_avatar.png`" mode="aspectFill" round />
</view>
<view class="flex-1 ml-22rpx flex justify-between items-center">
<view @click="Profile.handleToProfile">
<view class="text-[#303133] text-36rpx leading-50rpx ml-8rpx">昵称名称</view>
<view class="flex justify-center items-center mt-10rpx text-26rpx text-[#675649] font-400 leading-36rpx flex items-center">
13525626532
</view>
</view>
</view>
</view>
<view class="bg-white rounded-t-16rpx mt-46rpx px-30rpx">
<view class="font-bold text-34rpx leading-48rpx text-#303133 pt-36rpx">茶艺师的名字</view>
<view class="">
<wd-rate v-model="rate" readonly active-color="#FF5951" allow-half active-icon="star-filled" icon="star" space="4rpx"/>
</view>
<view class="flex items-center mt-20rpx">
<view class=" mr-20rpx">
<wd-tag color="#FF5951" bg-color="#FEF1F0" custom-class="!rounded-6rpx !px-16rpx !py-4rpx !h-40rpx">90后茶艺师</wd-tag>
</view>
<view class="w-160rpx h-40rpx relative mr-44rpx top-6rpx">
<view class="absolute left-0 top-0 h-36rpx flex items-start">
<wd-img :src="`${OSS}icon/icon_gold_medal.png`" width="36rpx" height="36rpx"></wd-img>
</view>
<view class="bg-[#F0F6EF] text-[#006C2D] font-400 text-22rpx leading-32rpx rounded-4rpx text-center w-150rpx ml-18rpx h-40rpx">金牌茶艺师</view>
</view>
</view>
</view>
<view class="h-2rpx bg-#F6F7F9 mx-30rpx mt-28rpx"></view>
<view class="flex justify-between items-center mx-88rpx text-center mt-30rpx">
<view>
<view class="font-400 text-28rpx leading-40rpx text-#606266">性别</view>
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx"></view>
</view>
<view class="w-4rpx h-66rpx bg-#F6F7F9"></view>
<view>
<view class="font-400 text-28rpx leading-40rpx text-#606266">年龄</view>
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx">21</view>
</view>
<view class="w-4rpx h-66rpx bg-#F6F7F9"></view>
<view>
<view class="font-400 text-28rpx leading-40rpx text-#606266">身高</view>
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx">165cm</view>
</view>
<view class="w-4rpx h-66rpx bg-#F6F7F9"></view>
<view>
<view class="font-400 text-28rpx leading-40rpx text-#606266">体重</view>
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx">53kg</view>
</view>
</view>
<view class="flex items-center justify-between mx-30rpx mt-40rpx">
<view>
<wd-img :src="`${OSS}images/reserve_room/reserve_room_image2.png`" width="220rpx" height="240rpx" radius="16rpx"></wd-img>
</view>
<view>
<wd-img :src="`${OSS}images/reserve_room/reserve_room_image2.png`" width="220rpx" height="240rpx" radius="16rpx"></wd-img>
</view>
<view>
<wd-img :src="`${OSS}images/reserve_room/reserve_room_image2.png`" width="220rpx" height="240rpx" radius="16rpx"></wd-img>
</view>
</view>
<!-- 兴趣爱好 -->
<view class="mx-30rpx mt-44rpx">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
兴趣爱好
</view>
<view class="bg-#FBFBFB rounded-8rpx px-28rpx py-20rpx mt-22rpx">
<view class="flex items-center">
<view class="mr-12rpx">
<wd-img :src="`${OSS}icon/icon_tea2.png`" width="32rpx" height="32rpx"></wd-img>
</view>
<view class="font-400 text-26rpx leading-36rpx text-#606266">兴趣爱好</view>
</view>
<view class="mt-20rpx font-400 text-28rpx leading-40rpx text-#303133">
爱好茶艺喜欢旅游把爱好当工作
</view>
</view>
</view>
<view class="h-20rpx bg-#F6F7F9 mt-42rpx mb-40rpx"></view>
<!-- 费用说明 -->
<view class="mx-30rpx mt-44rpx">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
费用说明
</view>
<view class="flex items-center mt-24rpx">
<view class="mr-12rpx flex items-center">
<wd-img :src="`${OSS}icon/icon_billing.png`" width="32rpx" height="32rpx"></wd-img>
</view>
<view class="font-400 text-26rpx leading-36rpx text-#606266">计费标准</view>
</view>
<view class=" mt-22rpx">
<view class="flex items-center justify-between">
<view class="w-8rpx h-8rpx rounded-8rpx bg-#6A6363 mr-14rpx"></view>
<view class="flex-1 flex items-center justify-between font-500 text-26rpx leading-48rpx text-#303133">
<view>服务费</view>
<view>160/小时</view>
</view>
</view>
<view class="flex items-center justify-between mt-20rpx">
<view class="w-8rpx h-8rpx rounded-8rpx bg-#6A6363 mr-14rpx"></view>
<view class="flex-1 flex items-center justify-between font-500 text-26rpx leading-48rpx text-#303133">
<view>车马费</view>
<view>3.00/公里</view>
</view>
</view>
</view>
</view>
</view>
<tabbar tab="my"></tabbar>
</view>
</template>
<script lang="ts" setup>
import Tabbar from '@/bundle/parten/components/Tabbar.vue'
const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
// 评分
const rate = ref(4.5)
const Profile = {
handleToProfile: () => {
uni.navigateTo({
url: '/pages/profile/profile'
})
},
handleToMyBank: () => {
uni.navigateTo({
url: '/bundle/parten/pages/my-bank/my-bank'
})
},
handleToFundRecord: () => {
uni.navigateTo({
url: '/bundle/parten/pages/fund-record/fund-record'
})
},
}
</script>
<style lang="scss">
page, home-bg{
background: #fff url(#{$OSS}images/parten/parten_image3.png) 0 0 no-repeat;
background-size: 100%;
}
</style>

View File

@ -2,23 +2,157 @@
{
"layout": "default",
"style": {
"navigationBarTitleText": ""
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "下级用户"
}
}
</route>
<template>
<view class="">
<view>
<!-- 展示人员信息 -->
<wd-overlay :show="showProfilePopup" @click="showProfilePopup = false">
<view class="h-full relative pb-62rpx">
<view class="absolute-center bg-white rounded-30rpx w-660rpx px-88rpx py-48rpx">
<view>
<view class="text-center">
<wd-img width="100rpx" height="100rpx" :src="`${OSS}icon/icon_avatar.png`" mode="aspectFill" round />
</view>
<view class="font-bold text-28rpx leading-40rpx text-#303133 mt-16rpx text-center">用户昵称</view>
<view class="mt-10rpx text-center">13525626532</view>
<view class="flex items-center justify-between mt-62rpx">
<view class="text-center">
<view class="font-bold text-36rpx leading-50rpx text-#303133">1200</view>
<view class="font-400 text-26rpx leading-36rpx text-#606266 mt-20rpx">本月下级佣金</view>
</view>
<view class="text-center">
<view class="font-bold text-36rpx leading-50rpx text-#303133">1200</view>
<view class="font-400 text-26rpx leading-36rpx text-#606266 mt-20rpx">本月下级佣金</view>
</view>
</view>
<view class="mt-46rpx">
<text class="font-400 text-26rpx leading-36rpx text-#303133">加入时间</text>
<text class="text-26rpx leading-36rpx text-#909399">2025-03-08 11:20</text>
</view>
</view>
</view>
</view>
</wd-overlay>
<view class="mt-24rpx pb-60rpx">
<view class="mt-20rpx">
<mescroll-body @init="mescrollInit" @down="downCallback" @up="Sub.upCallback">
<view class="mx-30rpx border-b border-b-solid mb-26rpx pb-10rpx flex" v-for="(item, index) in 10" :key="index">
<view class="mr-20rpx" @click="Sub.handleShowProfilePopup">
<wd-img width="100rpx" height="100rpx" :src="`${OSS}icon/icon_avatar.png`" mode="aspectFill" round />
</view>
<view class="flex-1 flex justify-between items-center">
<view @click="Sub.handleToSubPorfile(item)">
<view class="font-bold text-28rpx leading-40rpx text-#303133">用户昵称</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133 mt-12rpx">13525626532</view>
<view class="text-24rpx leading-34rpx text-#909399 mt-10rpx">2025-03-08 11:20</view>
</view>
<view @click="Sub.handleCallMobile">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_phone.png`" mode="aspectFill" round />
</view>
</view>
</view>
</mescroll-body>
</view>
</view>
<tabbar tab="sub"></tabbar>
</view>
</template>
<script lang="ts" setup>
import Tabbar from '@/bundle/parten/components/Tabbar.vue'
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
const OSS = inject('OSS')
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom)
// 展示人员信息
const showProfilePopup = ref<boolean>(false)
// tab 切换
const tab = ref<Array<{ id: number, name: string }>>([
{ id: 1, name: '全部' },
{ id: 2, name: '收入' },
{ id: 3, name: '支出' },
])
const currentTab = ref<number>(0)
const Sub = {
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
// list({
// page: mescroll.num,
// size: mescroll.size
// }).then((res: { list: Array<any>, totalPages: Number }) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// console.log("🚀 ~ goods:", goods)
// mescroll.endByPage(curPageData.length, res.totalPages); //必传参数(当前页的数据个数, 总页数)
// }).catch(() => {
// mescroll.endErr(); // 请求失败, 结束加载
// })
// apiGoods(mescroll.num, mescroll.size).then(res=>{
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) goods.value = []; // 第一页需手动制空列表
// goods.value = goods.value.concat(curPageData); //追加新数据
// //联网成功的回调,隐藏下拉刷新和上拉加载的状态;
// //mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
// //方法一(推荐): 后台接口有返回列表的总页数 totalPage
// //mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
// //方法二(推荐): 后台接口有返回列表的总数据量 totalSize
// //mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
// //方法三(推荐): 您有其他方式知道是否有下一页 hasNext
// //mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
// //方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据.
// mescroll.endSuccess(curPageData.length); // 请求成功, 结束加载
// }).catch(()=>{
mescroll.endErr(); // 请求失败, 结束加载
// })
},
// 打电话
handleCallMobile: (mobile: number) => {
uni.makePhoneCall({
phoneNumber: '13525626532'
})
},
// 展示人员信息
handleShowProfilePopup: () => {
showProfilePopup.value = true
},
// 去下级用户详情
handleToSubPorfile: (item: any) => {
uni.navigateTo({
url: '/bundle/parten/pages/sub/profile'
})
}
}
</script>
<style lang="scss" scoped>
page {
background: #FFF;
}
</style>

View File

@ -0,0 +1,89 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "余额提现"
}
}
</route>
<template>
<view>
<view class="mt-76rpx">
<!-- 进度1-平台审核中 -->
<view class="mx-98rpx flex">
<view class="mr-44rpx">
<wd-img width="40rpx" height="260rpx" :src="`${OSS}images/withdraw/withdraw_image1.png`"></wd-img>
</view>
<view>
<view class="font-400 text-#606266">
<view class="text-26rpx leading-36rpx">发起提现申请</view>
<view class="mt-8rpx text-24rpx leading-34rpx ">2025-04-08 21:25:25</view>
</view>
<view class="font-400 text-#606266 mt-26rpx">
<view class="text-30rpx leading-42rpx">平台审核中</view>
<view class="mt-10rpx text-26rpx leading-36rpx ">2025-04-08 21:25:25</view>
</view>
<view class="mt-44rpx font-400 text-26rpx leading-36rpx text-#BFC2CC">
到账成功
</view>
</view>
</view>
<!-- 进度2-到账成功 -->
<!-- <view class="mx-98rpx flex">
<view class="mr-44rpx">
<wd-img width="22rpx" height="260rpx" :src="`${OSS}images/withdraw/withdraw_image2.png`"></wd-img>
</view>
<view>
<view class="font-400 text-#606266">
<view class="text-26rpx leading-36rpx">发起提现申请</view>
<view class="mt-8rpx text-24rpx leading-34rpx ">2025-04-08 21:25:25</view>
</view>
<view class="font-400 text-#606266 mt-26rpx">
<view class="text-30rpx leading-42rpx">平台审核中</view>
<view class="mt-10rpx text-26rpx leading-36rpx ">2025-04-08 21:25:25</view>
</view>
<view class="mt-44rpx font-400 text-30rpx leading-44rpx">
<view>到账成功</view>
<view class="mt-10rpx text-26rpx leading-36rpx">2025-04-08 21:25:25</view>
</view>
</view>
</view> -->
<view class="mt-82rpx mb-38rpx mx-30rpx">
<wd-gap height="2rpx" bg-color="#F6F7F9"></wd-gap>
</view>
<view class="mx-60rpx">
<view class="flex items-center justify-between font-400 text-28rpx leading-40rpx">
<view class="text-#606266">提现金额</view>
<view class="text-#303133">5000.00</view>
</view>
<view class="flex items-center justify-between font-400 text-28rpx leading-40rpx">
<view class="text-#606266">到账银行卡</view>
<view class="text-#303133">招商银行3265)</view>
</view>
</view>
<view class="mx-60rpx mt-90rpx bg-#F6F7F8 rounded-8rpx h-90rpx leading-90rpx text-center">
完成
</view>
</view>
</view>
</template>
<script lang="ts" setup>
const OSS = inject('OSS')
</script>
<style lang="scss" scoped>
page {
background: #FFF;
}
</style>

View File

@ -11,10 +11,54 @@
<template>
<!-- calc(100vh - var(--window-top) - var(--window-bottom)) 精准占满无滚动条 -->
<view class="flex flex-col bg-[#F6F7F9] overflow-hidden" :style="pageHeightStyle">
<!-- 售后原因弹出框 -->
<wd-popup v-model="showBankCardPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
<view class="relative pb-78rpx">
<view class="absolute top-18rpx right-30rpx" @click="showBankCardPopup = false">
<wd-img width="60rpx" height='60rpx' :src="`${OSS}icon/icon_close.png`"></wd-img>
</view>
<view class="text-36rpx text-[#121212] leading-50rpx text-center pt-50rpx pb-40rpx">选择到账</view>
<view>
<scroll-view class="h-500rpx" scroll-y :show-scrollbar="false">
<wd-radio-group v-model="selectedBankCardIndex">
<view v-for="(item, index) in bankList" :key="index">
<wd-radio :value="item.id" checked-color="#4C9F44" size='large' shape="dot" custom-class="!mr-60rpx">
<view class="flex items-center mx-60rpx ">
<view class="flex">
<view class="mr-40rpx">
<wd-img width="44rpx" height="44rpx" mode="aspectFill" :src="`${OSS}icon/icon_bank_card2.png`"></wd-img>
</view>
<view class="font-400 text-30rpx leading-42rpx text-#303133">{{ item.bankName }} 储蓄卡3265</view>
</view>
</view>
</wd-radio>
<view class="mx-60rpx mt-36rpx">
<wd-gap height="2rpx" bg-color="#F6F7F9"></wd-gap>
</view>
</view>
</wd-radio-group>
<view class="flex items-center justify-between mx-60rpx mt-26rpx" @click="Withdraw.handleAddNewBankCard">
<view class="flex items-center">
<view class="flex items-center mr-40rpx">
<wd-img width="44rpx" height="44rpx" mode="aspectFill" :src="`${OSS}icon/icon_bank_card3.png`"></wd-img>
</view>
<view class="font-400 text-30rpx leading-42rpx text-#303133">使用新卡提现</view>
</view>
<view class="flex items-center">
<wd-icon name='arrow-right' size="22rpx"></wd-icon>
</view>
</view>
</scroll-view>
</view>
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff] mx-auto mt-30rpx" @click="Withdraw.handleSelectReason">确定</view>
</view>
</wd-popup>
<!-- 顶部信息区 -->
<view class="flex justify-between items-center pl-60rpx pr-28rpx pt-64rpx pb-64rpx">
<view class="text-#303133 text-28rpx leading-40rpx">到账银行卡</view>
<view class="flex items-center">
<view class="flex items-center" @click="showBankCardPopup = true">
<view class="flex items-center mr-16rpx">
<wd-img width="44rpx" height="44rpx" mode="aspectFill" :src="`${OSS}icon/icon_bank_card2.png`"></wd-img>
</view>
@ -47,9 +91,18 @@
</template>
<script lang="ts" setup>
import { inject, computed } from 'vue'
const OSS = inject('OSS')
// 选择银行卡
const showBankCardPopup = ref<boolean>(false)
const selectedBankCardIndex = ref<number>(0)
const bankList = ref<Array<{ id: number, bankName: string, bankCard: string }>>([
{ id: 1, bankName: '招商银行', bankCard: '3265' },
{ id: 2, bankName: '建设银行', bankCard: '1234' },
{ id: 3, bankName: '农业银行', bankCard: '5678' },
])
// 提现金额
const withdrawMoney = ref<number>(0)
@ -62,7 +115,15 @@
// 确定提现
handleConfirmwithdrawMoney: () => {
}
},
// 添加银行卡
handleAddNewBankCard: () => {
showBankCardPopup.value = false
uni.navigateTo({
url: '/bundle/parten/pages/bank-card/add'
})
},
}
</script>

View File

@ -35,7 +35,7 @@
<view class="text-34rpx text-[#303133] leading-48rpx font-bold line-1">凝香茶业</view>
<view class="relative mt-18rpx h-34rpx">
<view class="absolute top-0 flex items-center">
<wd-rate v-model="rate" readonly active-color="#FF5951" color="#FF5951" active-icon="star-filled" icon="star" space="4rpx"></wd-rate>
<wd-rate v-model="rate" readonly active-color="#FF5951" allow-half active-icon="star-filled" icon="star" space="4rpx"/>
<view class="text-26rpx text-[#606266] leading-34rpx ml-8rpx">4.0 推荐</view>
</view>
</view>
@ -126,7 +126,10 @@
`${OSS}images/banner1.png`
])
const current = ref<number>(0)
// 评分
const rate = ref<number>(4)
// tab
const tab = ref<number>(0)

View File

@ -194,6 +194,14 @@
.wd-datetime-picker__arrow {
display: none !important;
}
.wd-datetime-picker__action--cancel {
color: #606266 !important;
}
.wd-datetime-picker__action {
color: #4C9F44;
}
}
}
</style>

1
src/hooks/echarts.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -396,12 +396,40 @@
"navigationStyle": "custom"
}
},
{
"path": "parten/pages/bank-card/add",
"type": "page",
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "添加新银行卡"
}
},
{
"path": "parten/pages/bank-card/list",
"type": "page",
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#F6F7F9",
"navigationBarTitleText": "银行卡"
}
},
{
"path": "parten/pages/bill/bill",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": ""
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "收益明细"
}
},
{
"path": "parten/pages/fund-record/fund-record",
"type": "page",
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "资金记录"
}
},
{
@ -412,12 +440,30 @@
"navigationStyle": "custom"
}
},
{
"path": "parten/pages/sub/profile",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "parten/pages/sub/sub",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": ""
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "下级用户"
}
},
{
"path": "parten/pages/withdraw/progress",
"type": "page",
"layout": "default",
"style": {
"navigationBarBackgroundColor": "#FFF",
"navigationBarTitleText": "余额提现"
}
},
{

View File

@ -0,0 +1,2 @@
## 1.1.22025-09-14
- 更新文档配置说明

View File

@ -0,0 +1 @@
export * from "./uni-echarts/types";

View File

@ -0,0 +1,95 @@
export const MOUSE_EVENTS = [
"click",
"dblclick",
"mouseout",
"mouseover",
"mouseup",
"mousedown",
"mousemove",
"contextmenu",
"globalout"
];
export const ELEMENT_EVENTS = [
...MOUSE_EVENTS,
"mousewheel",
"drag",
"dragstart",
"dragend",
"dragenter",
"dragleave",
"dragover",
"drop"
];
export const ZRENDER_EVENTS = ELEMENT_EVENTS.map((it) => `zr:${it}`);
export const OTHER_EVENTS = [
"highlight",
"downplay",
"selectchanged",
"legendselectchanged",
"legendselected",
"legendunselected",
"legendselectall",
"legendinverseselect",
"legendscroll",
"datazoom",
"datarangeselected",
"graphroam",
"georoam",
"treeroam",
"timelinechanged",
"timelineplaychanged",
"restore",
"dataviewchanged",
"magictypechanged",
"geoselectchanged",
"geoselected",
"geounselected",
"axisareaselected",
"brush",
"brushEnd",
"brushselected",
"globalcursortaken",
"showtip",
"hidetip"
];
export const ECHARTS_EVENTS = [
...MOUSE_EVENTS,
...OTHER_EVENTS,
"rendered",
"finished",
...ZRENDER_EVENTS
];
export const UNI_EVENTS = [
"touchstart",
"touchmove",
"touchcancel",
"touchend",
"tap",
"longpress",
"longtap",
"transitionend",
"animationstart",
"animationiteration",
"animationend",
"touchforcechange"
];
export const NATIVE_EVENTS = [
...ELEMENT_EVENTS,
...UNI_EVENTS
].map((it) => `native:${it}`);
export const COMPONENT_EVENTS = [
"inited"
];
export const EVENTS = [
...ECHARTS_EVENTS,
...NATIVE_EVENTS,
...COMPONENT_EVENTS
];

View File

@ -0,0 +1,183 @@
import type { ECElementEvent, ElementEvent } from "echarts/core";
import type { StyleValue } from "vue";
import type {
AutoResize,
ChartOption,
ChartTheme,
EChartsType,
InitOptions,
LoadingOptions,
NullableValue,
PublicApi,
UpdateOptions
} from "../../shared-core";
export interface UniEchartsProps {
/**
* Custom root el class.
*/
customClass?: any;
/**
* Custom root el style.
*/
customStyle?: StyleValue;
/**
* Same as the option of echarts.
*/
option?: ChartOption;
/**
* Option inject key.
*/
optionKey?: string;
/**
* Theme to be applied.
*/
theme?: ChartTheme;
/**
* Optional chart init configurations.
*/
initOptions?: InitOptions;
/**
* Options for updating chart option.
*/
updateOptions?: UpdateOptions;
/**
* Group name to be used in chart connection.
*/
group?: string;
/**
* For performance critical scenarios (having a large dataset) we'd better bypass Vue's reactivity system for `option` prop.
*/
manualUpdate?: boolean;
/**
* Whether the chart should be resized automatically whenever its root is resized.
*/
autoresize?: AutoResize;
/**
* Whether the chart is in loading state.
*/
loading?: boolean;
/**
* Configuration item of loading animation.
*/
loadingOptions?: LoadingOptions;
/**
* Canvas type.
*/
canvasType?: "2d" | "legacy";
/**
* Prevent screen scroll when touching the canvas.
*/
disableScroll?: boolean;
/**
* Support mouse hover behavior in PC.
*/
supportHover?: boolean;
/**
* Init delay time(ms).
*/
initDelay?: number;
}
type MouseEventName =
| "click"
| "dblclick"
| "mouseout"
| "mouseover"
| "mouseup"
| "mousedown"
| "mousemove"
| "contextmenu"
| "globalout";
type ElementEventName =
| MouseEventName
| "mousewheel"
| "drag"
| "dragstart"
| "dragend"
| "dragenter"
| "dragleave"
| "dragover"
| "drop";
type ZRenderEventName = `zr:${ElementEventName}`;
type OtherEventName =
| "highlight"
| "downplay"
| "selectchanged"
| "legendselectchanged"
| "legendselected"
| "legendunselected"
| "legendselectall"
| "legendinverseselect"
| "legendscroll"
| "datazoom"
| "datarangeselected"
| "graphroam"
| "georoam"
| "treeroam"
| "timelinechanged"
| "timelineplaychanged"
| "restore"
| "dataviewchanged"
| "magictypechanged"
| "geoselectchanged"
| "geoselected"
| "geounselected"
| "axisareaselected"
| "brush"
| "brushEnd"
| "brushselected"
| "globalcursortaken"
| "showtip"
| "hidetip";
type UniEventName =
| "touchstart"
| "touchmove"
| "touchcancel"
| "touchend"
| "tap"
| "longpress"
| "longtap"
| "transitionend"
| "animationstart"
| "animationiteration"
| "animationend"
| "touchforcechange";
type NativeEventName = `native:${ElementEventName}` | `native:${UniEventName}`;
type MouseEmits = {
[key in MouseEventName]: (params: ECElementEvent) => void;
};
type ZRenderEmits = {
[key in ZRenderEventName]: (params: ElementEvent) => void;
};
type OtherEmits = {
[key in OtherEventName]: (params: any) => void;
};
type NativeEmits = {
[key in NativeEventName]: (params: any) => void;
};
export type UniEchartsEmits = MouseEmits & ZRenderEmits & OtherEmits & NativeEmits & {
rendered: (params: { elapsedTime: number }) => void;
finished: () => void;
inited: () => void;
};
export interface UniEchartsInst extends PublicApi, Pick<
EChartsType,
"setOption" | "resize"
> {
root: NullableValue<HTMLElement>;
canvasId: string;
chart: NullableValue<EChartsType>;
toTempFilePath: (options?: Omit<UniApp.CanvasToTempFilePathOptions, "canvasId" | "canvas">) => Promise<UniApp.CanvasToTempFilePathRes>;
}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,505 @@
<template>
<view
ref="root"
class="uni-echarts"
:class="props.customClass"
:style="props.customStyle"
@click="emit('native:click', $event)"
@dblclick="emit('native:dblclick', $event)"
@mouseout="emit('native:mouseout', $event)"
@mouseover="emit('native:mouseover', $event)"
@mouseup="emit('native:mouseup', $event)"
@mousedown="emit('native:mousedown', $event)"
@mousemove="emit('native:mousemove', $event)"
@contextmenu="emit('native:contextmenu', $event)"
@mousewheel="emit('native:mousewheel', $event)"
@drag="emit('native:drag', $event)"
@dragstart="emit('native:dragstart', $event)"
@dragend="emit('native:dragend', $event)"
@dragenter="emit('native:dragenter', $event)"
@dragleave="emit('native:dragleave', $event)"
@dragover="emit('native:dragover', $event)"
@drop="emit('native:drop', $event)"
@touchstart="emit('native:touchstart', $event)"
@touchmove="emit('native:touchmove', $event)"
@touchcancel="emit('native:touchcancel', $event)"
@touchend="emit('native:touchend', $event)"
@tap="emit('native:tap', $event)"
@longpress="emit('native:longpress', $event)"
@longtap="emit('native:longtap', $event)"
@transitionend="emit('native:transitionend', $event)"
@animationstart="emit('native:animationstart', $event)"
@animationiteration="emit('native:animationiteration', $event)"
@animationend="emit('native:animationend', $event)"
@touchforcechange="emit('native:touchforcechange', $event)">
<canvas
v-if="useCanvas2D"
:id="canvasId"
class="uni-echarts__canvas"
type="2d"
:disable-scroll="props.disableScroll"
@touchstart="touch.onStart"
@touchmove="touch.onMove"
@touchend="touch.onEnd"></canvas>
<canvas
v-else
:id="canvasId"
class="uni-echarts__canvas"
:canvas-id="canvasId"
:disable-scroll="props.disableScroll"
@touchstart="touch.onStart"
@touchmove="touch.onMove"
@touchend="touch.onEnd"></canvas>
<view
v-if="isPc"
class="uni-echarts__mask"
@mousedown="touch.onStart"
@mousemove="touch.onMove"
@mouseup="touch.onEnd"
@touchstart="touch.onStart"
@touchmove="touch.onMove"
@touchend="touch.onEnd"></view>
<slot></slot>
</view>
</template>
<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, shallowRef, watch, watchEffect } from "vue";
import {
canIUseCanvas2d,
defaultTo,
getIsPc,
getWindowInfo,
isEmpty,
querySelect,
setupEchartsCanvas,
sleep,
UniCanvas,
useAutoresize,
useEcharts,
useEchartsInitOptions,
useEchartsMouseWheel,
useEchartsOption,
useEchartsTheme,
useEchartsTouch,
useEchartsUpdateOptions,
useLoading,
usePublicApi,
useUid,
useVueThis
} from "../../shared-core";
import { ECHARTS_EVENTS, EVENTS } from "./events";
defineOptions({
name: "UniEcharts",
options: {
// #ifdef MP-WEIXIN || MP-ALIPAY
virtualHost: true
// #endif
}
});
const props = defineProps({
customClass: [String, Object, Array],
customStyle: [String, Object],
option: Object,
optionKey: String,
theme: [Object, String],
initOptions: Object,
updateOptions: Object,
group: String,
manualUpdate: Boolean,
autoresize: [Boolean, Object],
loading: Boolean,
loadingOptions: Object,
canvasType: {
type: String,
default: "2d"
},
disableScroll: Boolean,
supportHover: Boolean,
initDelay: {
type: Number,
default: 30
}
});
const emit = defineEmits(EVENTS);
const vueThis = useVueThis();
const echarts = useEcharts();
const isPc = getIsPc();
const root = shallowRef(null);
const canvasId = `uni-chart${useUid()}`;
const chart = shallowRef(null);
const manualOption = shallowRef(null);
const {
innerOption,
injectOption
} = useEchartsOption(props.optionKey, () => defaultTo(manualOption.value, props.option));
const { innerTheme } = useEchartsTheme(() => props.theme);
const { innerInitOptions } = useEchartsInitOptions(() => props.initOptions);
const { innerUpdateOptions } = useEchartsUpdateOptions(() => props.updateOptions);
const useCanvas2D = computed(() => {
if (!canIUseCanvas2d()) {
return false;
}
return props.canvasType === "2d";
});
const canvasRect = reactive({
top: 0,
left: 0,
width: 0,
height: 0
});
function getRelativeTouch(event, touches) {
let { clientX, clientY } = event;
if (!(clientX && clientY) && touches && touches[0]) {
clientX = touches[0].clientX;
clientY = touches[0].clientY;
}
return {
x: clientX - canvasRect.left,
y: clientY - canvasRect.top,
wheelDelta: defaultTo(event.wheelDelta, 0)
};
}
function getTouch(event, touches) {
const { x } = defaultTo(touches && touches[0], {});
return x ? touches[0] : getRelativeTouch(event, touches);
}
const touch = useEchartsTouch({
vueThis,
supportHover: () => props.supportHover,
isPc,
canvasId,
chart,
canvasRect,
getTouch
});
// #ifdef WEB
useEchartsMouseWheel({
isPc,
chart,
getTouch
});
// #endif
async function getContext() {
const { top, left, width, height, node } = await querySelect(vueThis, `#${canvasId}`, {
rect: true,
size: true,
node: useCanvas2D.value
});
const devicePixelRatio = getWindowInfo().pixelRatio;
let dpr = devicePixelRatio;
let canvas;
if (node != null) {
canvas = new UniCanvas(canvasId, node.getContext("2d"), node);
} else {
// #ifdef WEB
dpr = 1;
// #endif
// #ifdef MP-ALIPAY || MP-LARK
dpr = devicePixelRatio;
// #endif
// #ifdef MP-TOUTIAO
dpr = isPc ? 1 : devicePixelRatio;
// #endif
// #ifndef WEB || MP-ALIPAY || MP-LARK || MP-TOUTIAO
dpr = isPc ? devicePixelRatio : 1;
// #endif
canvas = new UniCanvas(canvasId, uni.createCanvasContext(canvasId, vueThis), null);
}
return {
top,
left,
width,
height,
node,
canvas,
dpr
};
}
async function init(option) {
if (props.initDelay > 0) {
await sleep(props.initDelay);
}
const context = await getContext();
canvasRect.top = context.top;
canvasRect.left = context.left;
canvasRect.width = context.width;
canvasRect.height = context.height;
setupEchartsCanvas(echarts, {
canvas: context.canvas,
node: context.node
});
const instance = chart.value = echarts.init(context.canvas, innerTheme.value, {
devicePixelRatio: context.dpr,
renderer: "canvas",
width: context.width,
height: context.height,
...innerInitOptions.value
});
if (props.group) {
instance.group = props.group;
}
const zr = instance.getZr();
for (const evt of ECHARTS_EVENTS) {
if (evt.indexOf("zr:") === 0) {
zr.on(evt.substring(3), (...args) => {
emit(evt, ...args);
});
} else {
instance.on(evt, (...args) => {
emit(evt, ...args);
});
}
}
// #ifdef WEB
const _resize = () => {
if (instance == null || instance.isDisposed()) {
return;
}
const { offsetWidth, offsetHeight } = root.value.$el;
instance.resize({
width: offsetWidth,
height: offsetHeight
});
};
// #endif
const _commit = () => {
emit("inited");
const opt = defaultTo(option, innerOption.value);
if (isEmpty(opt)) {
return;
}
instance.setOption(opt, innerUpdateOptions.value);
};
// #ifdef WEB
if (props.autoresize) {
await nextTick();
_resize();
_commit();
} else {
_commit();
}
// #endif
// #ifndef WEB
_commit();
// #endif
}
function setOption(option, updateOptions) {
if (props.manualUpdate) {
manualOption.value = option;
}
if (chart.value == null) {
init(option);
} else {
chart.value.setOption(option, defaultTo(updateOptions, {}));
}
}
async function resize(options = {}) {
if (chart.value == null) {
return;
}
let { width, height } = options;
const updateWidth = isEmpty(width) || width === "auto";
const updateHeight = isEmpty(height) || height === "auto";
if (updateWidth || updateHeight) {
const el = await querySelect(vueThis, `#${canvasId}`, {
size: true
});
if (updateWidth) {
width = el.width;
}
if (updateHeight) {
height = el.height;
}
}
chart.value.resize({
...options,
width,
height
});
}
function cleanup() {
if (chart.value == null) {
return;
}
touch.cleanup();
chart.value.dispose();
chart.value = null;
}
let unwatchOption;
watch(() => props.manualUpdate, (manual) => {
if (typeof unwatchOption === "function") {
unwatchOption();
unwatchOption = null;
}
if (manual) {
return;
}
unwatchOption = watch(() => defaultTo(props.option, injectOption.value), (value, oldValue) => {
if (isEmpty(value)) {
return;
}
if (chart.value == null) {
init();
} else {
chart.value.setOption(value, {
notMerge: value !== oldValue,
...innerUpdateOptions.value
});
}
}, {
deep: true
});
}, {
immediate: true
});
watch([innerTheme, innerInitOptions], () => {
cleanup();
init();
}, {
deep: true
});
watchEffect(() => {
if (props.group && chart.value != null) {
chart.value.group = props.group;
}
});
const publicApi = usePublicApi(chart);
useLoading(chart, {
loading: () => props.loading,
loadingOptions: () => props.loadingOptions
});
// #ifdef WEB
useAutoresize(chart, {
echarts,
autoresize: () => props.autoresize,
root
});
// #endif
function toTempFilePath(options) {
if (chart.value == null || chart.value.isDisposed()) {
return;
}
const canvas = chart.value.getDom();
if (canvas == null) {
return;
}
return canvas.toTempFilePath(options);
}
onMounted(() => {
init();
});
onBeforeUnmount(() => {
cleanup();
});
defineExpose({
root,
canvasId,
chart,
setOption,
resize,
...publicApi,
toTempFilePath
});
</script>
<style scoped>
.uni-echarts {
position: relative;
}
.uni-echarts__canvas {
width: 100%;
height: 100%;
}
.uni-echarts__mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
}
</style>

View File

@ -0,0 +1,19 @@
/* eslint-disable ts/no-empty-object-type */
import type { DefineComponent } from "vue";
import type { AllowedComponentProps } from "../../shared-core";
import type { UniEchartsEmits, UniEchartsProps } from "./types";
type UniEcharts = DefineComponent<
AllowedComponentProps & UniEchartsProps,
{},
{},
{},
{},
{},
{},
UniEchartsEmits
>;
declare const _default: UniEcharts;
export default _default;

View File

@ -0,0 +1,7 @@
declare module "vue" {
export interface GlobalComponents {
UniEcharts: typeof import("./components/uni-echarts/uni-echarts.vue")["default"];
}
}
export {};

View File

@ -0,0 +1,486 @@
import * as Echarts from 'echarts/core';
import { init, SetOptionOpts, ECElementEvent, ElementEvent } from 'echarts/core';
import { InjectionKey, MaybeRefOrGetter, ComponentPublicInstance, Ref, ComputedRef, Reactive, StyleValue } from 'vue';
type OptionalValue<T> = T | undefined;
type NullableValue<T> = T | null;
type ExtractValue<T> = T[keyof T];
interface AllowedComponentProps {
class?: any;
style?: StyleValue;
}
type Injection<T> = MaybeRefOrGetter<NullableValue<T>>;
type InitType = typeof init;
type InitParameters = Parameters<InitType>;
type ChartTheme = NonNullable<InitParameters[1]>;
type ChartThemeInjection = Injection<ChartTheme>;
type InitOptions = NonNullable<InitParameters[2]>;
type InitOptionsInjection = Injection<InitOptions>;
type UpdateOptions = SetOptionOpts;
type UpdateOptionsInjection = Injection<UpdateOptions>;
type EChartsType = ReturnType<InitType>;
type ZRenderType = ReturnType<EChartsType["getZr"]>;
type ZRenderHandler = ZRenderType["handler"];
type SetOptionType = EChartsType["setOption"];
type ChartOption = Parameters<SetOptionType>[0];
type ChartOptionInjection = Injection<ChartOption>;
interface LoadingOptions {
text?: string;
textColor?: string;
fontSize?: number | string;
fontWeight?: number | string;
fontStyle?: string;
fontFamily?: string;
maskColor?: string;
showSpinner?: boolean;
color?: string;
spinnerRadius?: number;
lineWidth?: number;
zlevel?: number;
}
type LoadingOptionsInjection = Injection<LoadingOptions>;
type MinifyEcharts = Pick<typeof Echarts, "init" | "registerPreprocessor" | "setPlatformAPI" | "throttle" | "use">;
declare const ECHARTS_KEY: InjectionKey<MinifyEcharts>;
declare function provideEcharts(echarts: MinifyEcharts): void;
declare function useEcharts(): MinifyEcharts;
type AutoResize = boolean | {
throttle?: number;
onResize?: () => void;
};
declare function useAutoresize(chart: Ref<NullableValue<EChartsType>>, { echarts, autoresize, root }: {
echarts: MinifyEcharts;
autoresize: MaybeRefOrGetter<AutoResize>;
root: Ref<NullableValue<ComponentPublicInstance>>;
}): void;
declare const INIT_OPTIONS_KEY: InjectionKey<InitOptionsInjection>;
declare function provideEchartsInitOptions(value: InitOptionsInjection): void;
declare function useEchartsInitOptions(value: MaybeRefOrGetter<OptionalValue<InitOptions>>): {
injectInitOptions: ComputedRef<NullableValue<InitOptions>>;
innerInitOptions: ComputedRef<InitOptions>;
};
type VueThis = ComponentPublicInstance;
declare function useVueThis(): VueThis;
interface CanvasRect {
top: number;
left: number;
width: number;
height: number;
}
interface NormalizedTouch {
x: number;
y: number;
wheelDelta: number;
}
interface GetTouchFuc {
(event: TouchEvent, touches: Touch[]): NormalizedTouch;
(event: MouseEvent): NormalizedTouch;
(event: TouchEvent | MouseEvent, touches?: Touch[]): NormalizedTouch;
}
declare function useEchartsTouch({ vueThis, supportHover, isPc, canvasId, chart, canvasRect, getTouch }: {
vueThis: VueThis;
supportHover: MaybeRefOrGetter<boolean>;
isPc: boolean;
canvasId: string;
chart: Ref<NullableValue<EChartsType>>;
canvasRect: Reactive<CanvasRect>;
getTouch: GetTouchFuc;
}): {
onStart: (event: TouchEvent) => void;
onMove: (event: TouchEvent) => void;
onEnd: (event: TouchEvent) => void;
cleanup: () => void;
};
declare function useEchartsMouseWheel({ isPc, chart, getTouch }: {
isPc: boolean;
chart: Ref<NullableValue<EChartsType>>;
getTouch: GetTouchFuc;
}): void;
declare const OPTION_KEY = "UniEcharts.option";
declare function getEchartsOptionKey(key?: string): string;
declare function provideEchartsOption(value: ChartOptionInjection): void;
declare function provideEchartsOption(key: string, value: ChartOptionInjection): void;
declare function useEchartsOption(key: OptionalValue<string>, value: MaybeRefOrGetter<OptionalValue<ChartOption>>): {
injectOption: ComputedRef<NullableValue<ChartOption>>;
innerOption: ComputedRef<NullableValue<ChartOption>>;
};
declare const THEME_KEY: InjectionKey<ChartThemeInjection>;
declare function provideEchartsTheme(value: ChartThemeInjection): void;
declare function useEchartsTheme(value: MaybeRefOrGetter<OptionalValue<ChartTheme>>): {
injectTheme: ComputedRef<NullableValue<ChartTheme>>;
innerTheme: ComputedRef<ChartTheme>;
};
declare const UPDATE_OPTIONS_KEY: InjectionKey<UpdateOptionsInjection>;
declare function provideEchartsUpdateOptions(value: UpdateOptionsInjection): void;
declare function useEchartsUpdateOptions(value: MaybeRefOrGetter<OptionalValue<UpdateOptions>>): {
injectUpdateOptions: ComputedRef<NullableValue<UpdateOptions>>;
innerUpdateOptions: ComputedRef<UpdateOptions>;
};
declare const LOADING_OPTIONS_KEY: InjectionKey<LoadingOptionsInjection>;
declare function provideEchartsLoadingOptions(value: LoadingOptionsInjection): void;
declare function useLoading(chart: Ref<NullableValue<EChartsType>>, { loading, loadingOptions }: {
loading: MaybeRefOrGetter<boolean>;
loadingOptions: MaybeRefOrGetter<OptionalValue<LoadingOptions>>;
}): void;
declare const ECHARTS_APIS: readonly ["getWidth", "getHeight", "getDom", "getOption", "dispatchAction", "convertToPixel", "convertFromPixel", "containPixel", "getDataURL", "getConnectedDataURL", "appendData", "clear", "isDisposed", "dispose"];
type EChartsApi = (typeof ECHARTS_APIS)[number];
type PublicApi = Pick<EChartsType, EChartsApi>;
declare function usePublicApi(chart: Ref<NullableValue<EChartsType>>): PublicApi;
declare function useUid(): number;
type CanvasNode = UniApp.NodeCallbackResult["node"];
type CanvasContext = UniApp.CanvasContext;
declare class UniCanvas {
tagName: "canvas";
attrs: Record<string, any>;
canvasId: string;
context: CanvasContext;
canvasNode: NullableValue<CanvasNode>;
private _emitter;
constructor(canvasId: string, context: CanvasContext, canvasNode: NullableValue<CanvasNode>);
private _setupContext;
get width(): number;
set width(value: number);
get height(): number;
set height(value: number);
getContext(type: "2d"): OptionalValue<CanvasContext>;
setAttribute(key: string, value: any): void;
getAttribute(key: string): any;
addEventListener(type: string, listener: (event: Event) => void): void;
removeEventListener(type: string, listener: (event: Event) => void): void;
dispatchEvent(type: string | Event, event?: Event): boolean;
attachEvent(): void;
detachEvent(): void;
requestAnimationFrame(callback: () => void): number;
cancelAnimationFrame(id: number): void;
toTempFilePath(options?: Omit<UniApp.CanvasToTempFilePathOptions, "canvasId" | "canvas">): Promise<UniApp.CanvasToTempFilePathRes>;
static parseFontSize(font: string): number;
static normalizeColor(context: CanvasContext, color: string): string;
static normalizeColor(context: CanvasContext, color: object): object;
static dispatch(handler: ZRenderHandler, event: Parameters<ZRenderHandler["dispatch"]>[0], touch: Parameters<ZRenderHandler["dispatch"]>[1]): void;
}
declare class UniImage {
tagName: "img";
width: number;
height: number;
onload?: (res: UniApp.GetImageInfoSuccessData) => void;
onerror?: (err: any) => void;
private _src;
constructor();
get src(): NullableValue<string>;
set src(value: string);
}
declare function setupEchartsCanvas(echarts: MinifyEcharts, { canvas, node }: {
canvas: UniCanvas;
node: NullableValue<CanvasNode>;
}): void;
declare function getIsPc(): boolean;
declare const Platform: {
readonly APP: "APP";
readonly APP_ANDROID: "APP-ANDROID";
readonly APP_IOS: "APP-IOS";
readonly APP_HARMONY: "APP-HARMONY";
readonly WEB: "WEB";
readonly MP: "MP";
readonly MP_WEIXIN: "MP-WEIXIN";
readonly MP_ALIPAY: "MP-ALIPAY";
readonly MP_BAIDU: "MP-BAIDU";
readonly MP_TOUTIAO: "MP-TOUTIAO";
readonly MP_LARK: "MP-LARK";
readonly MP_QQ: "MP-QQ";
readonly MP_KUAISHOU: "MP-KUAISHOU";
readonly MP_JD: "MP-JD";
readonly MP_360: "MP-360";
readonly MP_XHS: "MP-XHS";
readonly MP_HARMONY: "MP-HARMONY";
readonly QUICKAPP_WEBVIEW: "QUICKAPP-WEBVIEW";
readonly QUICKAPP_WEBVIEW_UNION: "QUICKAPP-WEBVIEW-UNION";
readonly QUICKAPP_WEBVIEW_HUAWEI: "QUICKAPP-WEBVIEW-HUAWEI";
readonly OTHER: "OTHER";
};
type PlatformType = ExtractValue<typeof Platform>;
declare function getPlatform(): PlatformType;
declare const platform: ExtractValue<{
readonly APP: "APP";
readonly APP_ANDROID: "APP-ANDROID";
readonly APP_IOS: "APP-IOS";
readonly APP_HARMONY: "APP-HARMONY";
readonly WEB: "WEB";
readonly MP: "MP";
readonly MP_WEIXIN: "MP-WEIXIN";
readonly MP_ALIPAY: "MP-ALIPAY";
readonly MP_BAIDU: "MP-BAIDU";
readonly MP_TOUTIAO: "MP-TOUTIAO";
readonly MP_LARK: "MP-LARK";
readonly MP_QQ: "MP-QQ";
readonly MP_KUAISHOU: "MP-KUAISHOU";
readonly MP_JD: "MP-JD";
readonly MP_360: "MP-360";
readonly MP_XHS: "MP-XHS";
readonly MP_HARMONY: "MP-HARMONY";
readonly QUICKAPP_WEBVIEW: "QUICKAPP-WEBVIEW";
readonly QUICKAPP_WEBVIEW_UNION: "QUICKAPP-WEBVIEW-UNION";
readonly QUICKAPP_WEBVIEW_HUAWEI: "QUICKAPP-WEBVIEW-HUAWEI";
readonly OTHER: "OTHER";
}>;
/** App */
declare const isApp: boolean;
/** App Android */
declare const isAppAndroid: boolean;
/** App iOS */
declare const isAppIos: boolean;
/** App HarmonyOS Next */
declare const isAppHarmony: boolean;
/** Web */
declare const isWeb: boolean;
/** 小程序 */
declare const isMp: boolean;
/** 微信小程序 */
declare const isMpWeixin: boolean;
/** 支付宝小程序 */
declare const isMpAlipay: boolean;
/** 百度小程序 */
declare const isMpBaidu: boolean;
/** 头条小程序 */
declare const isMpToutiao: boolean;
/** 飞书小程序 */
declare const isMpLark: boolean;
/** QQ小程序 */
declare const isMpQq: boolean;
/** 快手小程序 */
declare const isMpKuaishou: boolean;
/** 京东小程序 */
declare const isMpJd: boolean;
/** 360小程序 */
declare const isMp360: boolean;
/** 小红书小程序 */
declare const isMpXhs: boolean;
/** 鸿蒙元服务 */
declare const isMpHarmony: boolean;
/** 快应用 */
declare const isQuickappWebview: boolean;
/** 快应用联盟 */
declare const isQuickappWebviewUnion: boolean;
/** 快应用华为 */
declare const isQuickappWebviewHuawei: boolean;
/** 其他平台 */
declare const isOtherPlatform: boolean;
declare function isEmpty(value: unknown): boolean;
declare function defaultTo<T = any>(value: unknown, ...defaultValues: unknown[]): T;
declare function sleep(timeout?: number): Promise<void>;
declare function upperFirst(value: string): string;
declare function lowerFirst(value: string): string;
type EventType = string | symbol;
type Handler<T = unknown> = (event: T) => void;
type WildcardHandler<T = Record<string, unknown>> = (topic: keyof T, event: T[keyof T]) => void;
type EventHandlerList<T = unknown> = Array<Handler<T>>;
type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | "*", EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
interface Emitter<Events extends Record<EventType, unknown>> {
events: EventHandlerMap<Events>;
on<Topic extends keyof Events>(topic: Topic, handler: Handler<Events[Topic]>): void;
on(topic: "*", handler: WildcardHandler<Events>): void;
off<Topic extends keyof Events>(topic: Topic, handler?: Handler<Events[Topic]>): void;
off(topic: "*", handler: WildcardHandler<Events>): void;
emit<Topic extends keyof Events>(topic: Topic, event: Events[Topic]): void;
emit<Topic extends keyof Events>(topic: undefined extends Events[Topic] ? Topic : never): void;
}
declare function mitt<Events extends Record<EventType, unknown>>(events?: EventHandlerMap<Events>): Emitter<Events>;
declare function getDeviceInfo(): UniApp.GetDeviceInfoResult | UniApp.GetSystemInfoResult;
declare function getWindowInfo(): UniApp.GetWindowInfoResult | UniApp.GetSystemInfoResult;
declare function getAppBaseInfo(): UniApp.GetAppBaseInfoResult | UniApp.GetSystemInfoResult;
declare function getVersion(): string;
declare function compareVersion(v1: string, v2: string): 0 | 1 | -1;
declare function canIUseCanvas2d(): boolean;
declare function querySelect(component: VueThis, selector: string, fields: UniApp.NodeField): Promise<UniApp.NodeInfo>;
interface UniEchartsProps {
/**
* Custom root el class.
*/
customClass?: any;
/**
* Custom root el style.
*/
customStyle?: StyleValue;
/**
* Same as the option of echarts.
*/
option?: ChartOption;
/**
* Option inject key.
*/
optionKey?: string;
/**
* Theme to be applied.
*/
theme?: ChartTheme;
/**
* Optional chart init configurations.
*/
initOptions?: InitOptions;
/**
* Options for updating chart option.
*/
updateOptions?: UpdateOptions;
/**
* Group name to be used in chart connection.
*/
group?: string;
/**
* For performance critical scenarios (having a large dataset) we'd better bypass Vue's reactivity system for `option` prop.
*/
manualUpdate?: boolean;
/**
* Whether the chart should be resized automatically whenever its root is resized.
*/
autoresize?: AutoResize;
/**
* Whether the chart is in loading state.
*/
loading?: boolean;
/**
* Configuration item of loading animation.
*/
loadingOptions?: LoadingOptions;
/**
* Canvas type.
*/
canvasType?: "2d" | "legacy";
/**
* Prevent screen scroll when touching the canvas.
*/
disableScroll?: boolean;
/**
* Support mouse hover behavior in PC.
*/
supportHover?: boolean;
/**
* Init delay time(ms).
*/
initDelay?: number;
}
type MouseEventName =
| "click"
| "dblclick"
| "mouseout"
| "mouseover"
| "mouseup"
| "mousedown"
| "mousemove"
| "contextmenu"
| "globalout";
type ElementEventName =
| MouseEventName
| "mousewheel"
| "drag"
| "dragstart"
| "dragend"
| "dragenter"
| "dragleave"
| "dragover"
| "drop";
type ZRenderEventName = `zr:${ElementEventName}`;
type OtherEventName =
| "highlight"
| "downplay"
| "selectchanged"
| "legendselectchanged"
| "legendselected"
| "legendunselected"
| "legendselectall"
| "legendinverseselect"
| "legendscroll"
| "datazoom"
| "datarangeselected"
| "graphroam"
| "georoam"
| "treeroam"
| "timelinechanged"
| "timelineplaychanged"
| "restore"
| "dataviewchanged"
| "magictypechanged"
| "geoselectchanged"
| "geoselected"
| "geounselected"
| "axisareaselected"
| "brush"
| "brushEnd"
| "brushselected"
| "globalcursortaken"
| "showtip"
| "hidetip";
type UniEventName =
| "touchstart"
| "touchmove"
| "touchcancel"
| "touchend"
| "tap"
| "longpress"
| "longtap"
| "transitionend"
| "animationstart"
| "animationiteration"
| "animationend"
| "touchforcechange";
type NativeEventName = `native:${ElementEventName}` | `native:${UniEventName}`;
type MouseEmits = {
[key in MouseEventName]: (params: ECElementEvent) => void;
};
type ZRenderEmits = {
[key in ZRenderEventName]: (params: ElementEvent) => void;
};
type OtherEmits = {
[key in OtherEventName]: (params: any) => void;
};
type NativeEmits = {
[key in NativeEventName]: (params: any) => void;
};
type UniEchartsEmits = MouseEmits & ZRenderEmits & OtherEmits & NativeEmits & {
rendered: (params: { elapsedTime: number }) => void;
finished: () => void;
inited: () => void;
};
interface UniEchartsInst extends PublicApi, Pick<
EChartsType,
"setOption" | "resize"
> {
root: NullableValue<HTMLElement>;
canvasId: string;
chart: NullableValue<EChartsType>;
toTempFilePath: (options?: Omit<UniApp.CanvasToTempFilePathOptions, "canvasId" | "canvas">) => Promise<UniApp.CanvasToTempFilePathRes>;
}
export { ECHARTS_KEY, INIT_OPTIONS_KEY, LOADING_OPTIONS_KEY, OPTION_KEY, Platform, THEME_KEY, UPDATE_OPTIONS_KEY, UniCanvas, UniImage, canIUseCanvas2d, compareVersion, defaultTo, getAppBaseInfo, getDeviceInfo, getEchartsOptionKey, getIsPc, getPlatform, getVersion, getWindowInfo, isApp, isAppAndroid, isAppHarmony, isAppIos, isEmpty, isMp, isMp360, isMpAlipay, isMpBaidu, isMpHarmony, isMpJd, isMpKuaishou, isMpLark, isMpQq, isMpToutiao, isMpWeixin, isMpXhs, isOtherPlatform, isQuickappWebview, isQuickappWebviewHuawei, isQuickappWebviewUnion, isWeb, lowerFirst, mitt, platform, provideEcharts, provideEchartsInitOptions, provideEchartsLoadingOptions, provideEchartsOption, provideEchartsTheme, provideEchartsUpdateOptions, querySelect, setupEchartsCanvas, sleep, upperFirst, useAutoresize, useEcharts, useEchartsInitOptions, useEchartsMouseWheel, useEchartsOption, useEchartsTheme, useEchartsTouch, useEchartsUpdateOptions, useLoading, usePublicApi, useUid, useVueThis };
export type { AllowedComponentProps, AutoResize, CanvasContext, CanvasNode, CanvasRect, ChartOption, ChartOptionInjection, ChartTheme, ChartThemeInjection, EChartsType, ElementEventName, Emitter, EventHandlerList, EventHandlerMap, EventType, ExtractValue, GetTouchFuc, Handler, InitOptions, InitOptionsInjection, InitParameters, Injection, LoadingOptions, LoadingOptionsInjection, MinifyEcharts, MouseEmits, MouseEventName, NativeEmits, NativeEventName, NormalizedTouch, NullableValue, OptionalValue, OtherEmits, OtherEventName, PlatformType, PublicApi, UniEchartsEmits, UniEchartsInst, UniEchartsProps, UniEventName, UpdateOptions, UpdateOptionsInjection, VueThis, WildCardEventHandlerList, WildcardHandler, ZRenderEmits, ZRenderEventName, ZRenderHandler, ZRenderType };

View File

@ -0,0 +1,2 @@
export * from "./components";
export * from "./shared-core";

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025-PRESENT xiaohe0601 <https://github.com/xiaohe0601>
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

@ -0,0 +1,105 @@
{
"id": "xiaohe-echarts",
"name": "uni-echarts",
"displayName": "xiaohe-echarts",
"type": "module",
"version": "1.1.2",
"description": "适用于 uni-app 的 Apache ECharts 组件仅支持Vue 3",
"author": "xiaohe0601 <xiaohe0601@outlook.com>",
"license": "MIT",
"homepage": "https://uni-echarts.xiaohe.ink",
"repository": "https://github.com/xiaohe0601/uni-echarts",
"bugs": "https://github.com/xiaohe0601/uni-echarts/issues",
"keywords": [
"echarts",
"vue-echarts",
"uni-echarts"
],
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/uni-echarts",
"darkmode": "√",
"i18n": "√",
"widescreen": "√"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"client": {
"uni-app": {
"vue": {
"vue2": "x",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "x",
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "√",
"baidu": "√",
"kuaishou": "√",
"jd": "√",
"harmony": "√",
"qq": "√",
"lark": "√"
},
"quickapp": {
"huawei": "√",
"union": "√"
}
},
"uni-app-x": {
"web": {
"safari": "x",
"chrome": "x"
},
"app": {
"android": "x",
"ios": "x",
"harmony": "x"
},
"mp": {
"weixin": "x"
}
}
},
"cloud": {
"aliyun": "√",
"tcb": "√",
"alipay": "√"
}
}
},
"engines": {
"HBuilderX": "^3.1.0",
"uni-app": "^3.6.9",
"uni-app-x": "^3.6.9"
}
}

View File

@ -0,0 +1,70 @@
<div align="center">
<img src="https://oss.xiaohe.ink/images/uni-echarts.png" width="160" alt="logo"/>
<h1>Uni ECharts</h1>
<span>🪀 适用于 uni-app 的 Apache ECharts 组件仅支持Vue 3</span>
</div>
<br>
[![github stars][github-stars-src]][github-stars-href]
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
xiaohe0601 / [github@xiaohe0601](https://github.com/xiaohe0601) / [gitee@xiaohe0601](https://gitee.com/xiaohe0601)
## 🎉 特性
- 🚀 快速上手,与 [Vue ECharts](https://github.com/ecomfe/vue-echarts) 近乎一致的使用体验
- 📱 多端兼容,支持 Web、小程序、APP
- 📦 支持 easycom
- ☕ 支持 TypeScript
- 🍳 支持免费商用
## 🔗 链接
- [文档首页](https://uni-echarts.xiaohe.ink)
- [快速开始](https://uni-echarts.xiaohe.ink/guide/getting-started)
- [API 参考](https://uni-echarts.xiaohe.ink/apis/component)
- [常见问题](https://uni-echarts.xiaohe.ink/guide/faq)
- [更新日志](https://uni-echarts.xiaohe.ink/guide/changelog)
- [支持我们](https://uni-echarts.xiaohe.ink/sponsor)
- [在线演示](https://uni-echarts.xiaohe.ink/ui)
## 🍬 鸣谢
得益于以下项目对开源的付出,让 Uni ECharts 能够站在巨人的肩膀上。
- [echarts](https://github.com/apache/echarts)
- [vue-echarts](https://github.com/ecomfe/vue-echarts)
- [lime-echart](https://gitee.com/liangei/lime-echart)
- [echarts-for-weixin](https://github.com/ecomfe/echarts-for-weixin)
- [mitt](https://github.com/developit/mitt)
## 🐶 讨论交流
- ❓:若有疑问或 BUG 反馈,可提交 [issues](https://github.com/xiaohe0601/uni-echarts/issues),也欢迎 PR请参考 [贡献指南](./CONTRIBUTING.md)
- 📫:[xiaohe0601@outlook.com](mailto:xiaohe0601@outlook.com)
- 🐧:暂未开通
## 🏆 开源协议
Uni ECharts 基于 [MIT](https://github.com/xiaohe0601/uni-echarts/blob/main/LICENSE) 许可发布,请自由地享受和参与开源。
## 🚓 声明
The Apache Software Foundation [Apache ECharts, ECharts](https://echarts.apache.org/), Apache, the Apache feather,
and the Apache ECharts project logo are either registered trademarks or trademarks of the
[Apache Software Foundation](https://www.apache.org/).
[github-stars-src]: https://img.shields.io/github/stars/xiaohe0601/uni-echarts?style=flat&colorA=080f12&colorB=1fa669&logo=GitHub
[github-stars-href]: https://github.com/xiaohe0601/uni-echarts
[npm-version-src]: https://img.shields.io/npm/v/uni-echarts?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/uni-echarts
[npm-downloads-src]: https://img.shields.io/npm/dm/uni-echarts?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/uni-echarts
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/uni-echarts
[license-src]: https://img.shields.io/github/license/xiaohe0601/uni-echarts.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/xiaohe0601/uni-echarts/blob/main/LICENSE

View File

@ -0,0 +1,316 @@
import { MaybeRefOrGetter, StyleValue, InjectionKey, Ref, ComponentPublicInstance, ComputedRef, Reactive } from 'vue';
import * as Echarts from 'echarts/core';
import { init, SetOptionOpts } from 'echarts/core';
type OptionalValue<T> = T | undefined;
type NullableValue<T> = T | null;
type ExtractValue<T> = T[keyof T];
interface AllowedComponentProps {
class?: any;
style?: StyleValue;
}
type Injection<T> = MaybeRefOrGetter<NullableValue<T>>;
type InitType = typeof init;
type InitParameters = Parameters<InitType>;
type ChartTheme = NonNullable<InitParameters[1]>;
type ChartThemeInjection = Injection<ChartTheme>;
type InitOptions = NonNullable<InitParameters[2]>;
type InitOptionsInjection = Injection<InitOptions>;
type UpdateOptions = SetOptionOpts;
type UpdateOptionsInjection = Injection<UpdateOptions>;
type EChartsType = ReturnType<InitType>;
type ZRenderType = ReturnType<EChartsType["getZr"]>;
type ZRenderHandler = ZRenderType["handler"];
type SetOptionType = EChartsType["setOption"];
type ChartOption = Parameters<SetOptionType>[0];
type ChartOptionInjection = Injection<ChartOption>;
interface LoadingOptions {
text?: string;
textColor?: string;
fontSize?: number | string;
fontWeight?: number | string;
fontStyle?: string;
fontFamily?: string;
maskColor?: string;
showSpinner?: boolean;
color?: string;
spinnerRadius?: number;
lineWidth?: number;
zlevel?: number;
}
type LoadingOptionsInjection = Injection<LoadingOptions>;
type MinifyEcharts = Pick<typeof Echarts, "init" | "registerPreprocessor" | "setPlatformAPI" | "throttle" | "use">;
declare const ECHARTS_KEY: InjectionKey<MinifyEcharts>;
declare function provideEcharts(echarts: MinifyEcharts): void;
declare function useEcharts(): MinifyEcharts;
type AutoResize = boolean | {
throttle?: number;
onResize?: () => void;
};
declare function useAutoresize(chart: Ref<NullableValue<EChartsType>>, { echarts, autoresize, root }: {
echarts: MinifyEcharts;
autoresize: MaybeRefOrGetter<AutoResize>;
root: Ref<NullableValue<ComponentPublicInstance>>;
}): void;
declare const INIT_OPTIONS_KEY: InjectionKey<InitOptionsInjection>;
declare function provideEchartsInitOptions(value: InitOptionsInjection): void;
declare function useEchartsInitOptions(value: MaybeRefOrGetter<OptionalValue<InitOptions>>): {
injectInitOptions: ComputedRef<NullableValue<InitOptions>>;
innerInitOptions: ComputedRef<InitOptions>;
};
type VueThis = ComponentPublicInstance;
declare function useVueThis(): VueThis;
interface CanvasRect {
top: number;
left: number;
width: number;
height: number;
}
interface NormalizedTouch {
x: number;
y: number;
wheelDelta: number;
}
interface GetTouchFuc {
(event: TouchEvent, touches: Touch[]): NormalizedTouch;
(event: MouseEvent): NormalizedTouch;
(event: TouchEvent | MouseEvent, touches?: Touch[]): NormalizedTouch;
}
declare function useEchartsTouch({ vueThis, supportHover, isPc, canvasId, chart, canvasRect, getTouch }: {
vueThis: VueThis;
supportHover: MaybeRefOrGetter<boolean>;
isPc: boolean;
canvasId: string;
chart: Ref<NullableValue<EChartsType>>;
canvasRect: Reactive<CanvasRect>;
getTouch: GetTouchFuc;
}): {
onStart: (event: TouchEvent) => void;
onMove: (event: TouchEvent) => void;
onEnd: (event: TouchEvent) => void;
cleanup: () => void;
};
declare function useEchartsMouseWheel({ isPc, chart, getTouch }: {
isPc: boolean;
chart: Ref<NullableValue<EChartsType>>;
getTouch: GetTouchFuc;
}): void;
declare const OPTION_KEY = "UniEcharts.option";
declare function getEchartsOptionKey(key?: string): string;
declare function provideEchartsOption(value: ChartOptionInjection): void;
declare function provideEchartsOption(key: string, value: ChartOptionInjection): void;
declare function useEchartsOption(key: OptionalValue<string>, value: MaybeRefOrGetter<OptionalValue<ChartOption>>): {
injectOption: ComputedRef<NullableValue<ChartOption>>;
innerOption: ComputedRef<NullableValue<ChartOption>>;
};
declare const THEME_KEY: InjectionKey<ChartThemeInjection>;
declare function provideEchartsTheme(value: ChartThemeInjection): void;
declare function useEchartsTheme(value: MaybeRefOrGetter<OptionalValue<ChartTheme>>): {
injectTheme: ComputedRef<NullableValue<ChartTheme>>;
innerTheme: ComputedRef<ChartTheme>;
};
declare const UPDATE_OPTIONS_KEY: InjectionKey<UpdateOptionsInjection>;
declare function provideEchartsUpdateOptions(value: UpdateOptionsInjection): void;
declare function useEchartsUpdateOptions(value: MaybeRefOrGetter<OptionalValue<UpdateOptions>>): {
injectUpdateOptions: ComputedRef<NullableValue<UpdateOptions>>;
innerUpdateOptions: ComputedRef<UpdateOptions>;
};
declare const LOADING_OPTIONS_KEY: InjectionKey<LoadingOptionsInjection>;
declare function provideEchartsLoadingOptions(value: LoadingOptionsInjection): void;
declare function useLoading(chart: Ref<NullableValue<EChartsType>>, { loading, loadingOptions }: {
loading: MaybeRefOrGetter<boolean>;
loadingOptions: MaybeRefOrGetter<OptionalValue<LoadingOptions>>;
}): void;
declare const ECHARTS_APIS: readonly ["getWidth", "getHeight", "getDom", "getOption", "dispatchAction", "convertToPixel", "convertFromPixel", "containPixel", "getDataURL", "getConnectedDataURL", "appendData", "clear", "isDisposed", "dispose"];
type EChartsApi = (typeof ECHARTS_APIS)[number];
type PublicApi = Pick<EChartsType, EChartsApi>;
declare function usePublicApi(chart: Ref<NullableValue<EChartsType>>): PublicApi;
declare function useUid(): number;
type CanvasNode = UniApp.NodeCallbackResult["node"];
type CanvasContext = UniApp.CanvasContext;
declare class UniCanvas {
tagName: "canvas";
attrs: Record<string, any>;
canvasId: string;
context: CanvasContext;
canvasNode: NullableValue<CanvasNode>;
private _emitter;
constructor(canvasId: string, context: CanvasContext, canvasNode: NullableValue<CanvasNode>);
private _setupContext;
get width(): number;
set width(value: number);
get height(): number;
set height(value: number);
getContext(type: "2d"): OptionalValue<CanvasContext>;
setAttribute(key: string, value: any): void;
getAttribute(key: string): any;
addEventListener(type: string, listener: (event: Event) => void): void;
removeEventListener(type: string, listener: (event: Event) => void): void;
dispatchEvent(type: string | Event, event?: Event): boolean;
attachEvent(): void;
detachEvent(): void;
requestAnimationFrame(callback: () => void): number;
cancelAnimationFrame(id: number): void;
toTempFilePath(options?: Omit<UniApp.CanvasToTempFilePathOptions, "canvasId" | "canvas">): Promise<UniApp.CanvasToTempFilePathRes>;
static parseFontSize(font: string): number;
static normalizeColor(context: CanvasContext, color: string): string;
static normalizeColor(context: CanvasContext, color: object): object;
static dispatch(handler: ZRenderHandler, event: Parameters<ZRenderHandler["dispatch"]>[0], touch: Parameters<ZRenderHandler["dispatch"]>[1]): void;
}
declare class UniImage {
tagName: "img";
width: number;
height: number;
onload?: (res: UniApp.GetImageInfoSuccessData) => void;
onerror?: (err: any) => void;
private _src;
constructor();
get src(): NullableValue<string>;
set src(value: string);
}
declare function setupEchartsCanvas(echarts: MinifyEcharts, { canvas, node }: {
canvas: UniCanvas;
node: NullableValue<CanvasNode>;
}): void;
declare function getIsPc(): boolean;
declare const Platform: {
readonly APP: "APP";
readonly APP_ANDROID: "APP-ANDROID";
readonly APP_IOS: "APP-IOS";
readonly APP_HARMONY: "APP-HARMONY";
readonly WEB: "WEB";
readonly MP: "MP";
readonly MP_WEIXIN: "MP-WEIXIN";
readonly MP_ALIPAY: "MP-ALIPAY";
readonly MP_BAIDU: "MP-BAIDU";
readonly MP_TOUTIAO: "MP-TOUTIAO";
readonly MP_LARK: "MP-LARK";
readonly MP_QQ: "MP-QQ";
readonly MP_KUAISHOU: "MP-KUAISHOU";
readonly MP_JD: "MP-JD";
readonly MP_360: "MP-360";
readonly MP_XHS: "MP-XHS";
readonly MP_HARMONY: "MP-HARMONY";
readonly QUICKAPP_WEBVIEW: "QUICKAPP-WEBVIEW";
readonly QUICKAPP_WEBVIEW_UNION: "QUICKAPP-WEBVIEW-UNION";
readonly QUICKAPP_WEBVIEW_HUAWEI: "QUICKAPP-WEBVIEW-HUAWEI";
readonly OTHER: "OTHER";
};
type PlatformType = ExtractValue<typeof Platform>;
declare function getPlatform(): PlatformType;
declare const platform: ExtractValue<{
readonly APP: "APP";
readonly APP_ANDROID: "APP-ANDROID";
readonly APP_IOS: "APP-IOS";
readonly APP_HARMONY: "APP-HARMONY";
readonly WEB: "WEB";
readonly MP: "MP";
readonly MP_WEIXIN: "MP-WEIXIN";
readonly MP_ALIPAY: "MP-ALIPAY";
readonly MP_BAIDU: "MP-BAIDU";
readonly MP_TOUTIAO: "MP-TOUTIAO";
readonly MP_LARK: "MP-LARK";
readonly MP_QQ: "MP-QQ";
readonly MP_KUAISHOU: "MP-KUAISHOU";
readonly MP_JD: "MP-JD";
readonly MP_360: "MP-360";
readonly MP_XHS: "MP-XHS";
readonly MP_HARMONY: "MP-HARMONY";
readonly QUICKAPP_WEBVIEW: "QUICKAPP-WEBVIEW";
readonly QUICKAPP_WEBVIEW_UNION: "QUICKAPP-WEBVIEW-UNION";
readonly QUICKAPP_WEBVIEW_HUAWEI: "QUICKAPP-WEBVIEW-HUAWEI";
readonly OTHER: "OTHER";
}>;
/** App */
declare const isApp: boolean;
/** App Android */
declare const isAppAndroid: boolean;
/** App iOS */
declare const isAppIos: boolean;
/** App HarmonyOS Next */
declare const isAppHarmony: boolean;
/** Web */
declare const isWeb: boolean;
/** 小程序 */
declare const isMp: boolean;
/** 微信小程序 */
declare const isMpWeixin: boolean;
/** 支付宝小程序 */
declare const isMpAlipay: boolean;
/** 百度小程序 */
declare const isMpBaidu: boolean;
/** 头条小程序 */
declare const isMpToutiao: boolean;
/** 飞书小程序 */
declare const isMpLark: boolean;
/** QQ小程序 */
declare const isMpQq: boolean;
/** 快手小程序 */
declare const isMpKuaishou: boolean;
/** 京东小程序 */
declare const isMpJd: boolean;
/** 360小程序 */
declare const isMp360: boolean;
/** 小红书小程序 */
declare const isMpXhs: boolean;
/** 鸿蒙元服务 */
declare const isMpHarmony: boolean;
/** 快应用 */
declare const isQuickappWebview: boolean;
/** 快应用联盟 */
declare const isQuickappWebviewUnion: boolean;
/** 快应用华为 */
declare const isQuickappWebviewHuawei: boolean;
/** 其他平台 */
declare const isOtherPlatform: boolean;
declare function isEmpty(value: unknown): boolean;
declare function defaultTo<T = any>(value: unknown, ...defaultValues: unknown[]): T;
declare function sleep(timeout?: number): Promise<void>;
declare function upperFirst(value: string): string;
declare function lowerFirst(value: string): string;
type EventType = string | symbol;
type Handler<T = unknown> = (event: T) => void;
type WildcardHandler<T = Record<string, unknown>> = (topic: keyof T, event: T[keyof T]) => void;
type EventHandlerList<T = unknown> = Array<Handler<T>>;
type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | "*", EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
interface Emitter<Events extends Record<EventType, unknown>> {
events: EventHandlerMap<Events>;
on<Topic extends keyof Events>(topic: Topic, handler: Handler<Events[Topic]>): void;
on(topic: "*", handler: WildcardHandler<Events>): void;
off<Topic extends keyof Events>(topic: Topic, handler?: Handler<Events[Topic]>): void;
off(topic: "*", handler: WildcardHandler<Events>): void;
emit<Topic extends keyof Events>(topic: Topic, event: Events[Topic]): void;
emit<Topic extends keyof Events>(topic: undefined extends Events[Topic] ? Topic : never): void;
}
declare function mitt<Events extends Record<EventType, unknown>>(events?: EventHandlerMap<Events>): Emitter<Events>;
declare function getDeviceInfo(): UniApp.GetDeviceInfoResult | UniApp.GetSystemInfoResult;
declare function getWindowInfo(): UniApp.GetWindowInfoResult | UniApp.GetSystemInfoResult;
declare function getAppBaseInfo(): UniApp.GetAppBaseInfoResult | UniApp.GetSystemInfoResult;
declare function getVersion(): string;
declare function compareVersion(v1: string, v2: string): 0 | 1 | -1;
declare function canIUseCanvas2d(): boolean;
declare function querySelect(component: VueThis, selector: string, fields: UniApp.NodeField): Promise<UniApp.NodeInfo>;
export { ECHARTS_KEY, INIT_OPTIONS_KEY, LOADING_OPTIONS_KEY, OPTION_KEY, Platform, THEME_KEY, UPDATE_OPTIONS_KEY, UniCanvas, UniImage, canIUseCanvas2d, compareVersion, defaultTo, getAppBaseInfo, getDeviceInfo, getEchartsOptionKey, getIsPc, getPlatform, getVersion, getWindowInfo, isApp, isAppAndroid, isAppHarmony, isAppIos, isEmpty, isMp, isMp360, isMpAlipay, isMpBaidu, isMpHarmony, isMpJd, isMpKuaishou, isMpLark, isMpQq, isMpToutiao, isMpWeixin, isMpXhs, isOtherPlatform, isQuickappWebview, isQuickappWebviewHuawei, isQuickappWebviewUnion, isWeb, lowerFirst, mitt, platform, provideEcharts, provideEchartsInitOptions, provideEchartsLoadingOptions, provideEchartsOption, provideEchartsTheme, provideEchartsUpdateOptions, querySelect, setupEchartsCanvas, sleep, upperFirst, useAutoresize, useEcharts, useEchartsInitOptions, useEchartsMouseWheel, useEchartsOption, useEchartsTheme, useEchartsTouch, useEchartsUpdateOptions, useLoading, usePublicApi, useUid, useVueThis };
export type { AllowedComponentProps, AutoResize, CanvasContext, CanvasNode, CanvasRect, ChartOption, ChartOptionInjection, ChartTheme, ChartThemeInjection, EChartsType, Emitter, EventHandlerList, EventHandlerMap, EventType, ExtractValue, GetTouchFuc, Handler, InitOptions, InitOptionsInjection, InitParameters, Injection, LoadingOptions, LoadingOptionsInjection, MinifyEcharts, NormalizedTouch, NullableValue, OptionalValue, PlatformType, PublicApi, UpdateOptions, UpdateOptionsInjection, VueThis, WildCardEventHandlerList, WildcardHandler, ZRenderHandler, ZRenderType };

View File

@ -0,0 +1,977 @@
import { watch, provide, inject, computed, toValue, onMounted, onBeforeUnmount, shallowRef, watchEffect, getCurrentInstance } from 'vue';
import { throttle, use, setPlatformAPI, registerPreprocessor, init } from 'echarts/core';
function useAutoresize(chart, {
echarts,
autoresize,
root
}) {
watch(
[chart, autoresize, root],
(values, _, onCleanup) => {
const _chart = values[0];
const _autoresize = values[1];
const _root = values[2];
let observer = null;
if (_chart != null && _root != null && _autoresize) {
const { offsetWidth, offsetHeight } = _root.$el;
const autoresizeOptions = _autoresize === true ? {} : _autoresize;
const { throttle: wait = 100, onResize } = autoresizeOptions;
let triggered = false;
const callback = () => {
_chart.resize({
width: _root.$el.offsetWidth,
height: _root.$el.offsetHeight
});
if (onResize != null) {
onResize();
}
};
const resizeCallback = wait ? echarts.throttle(callback, wait) : callback;
observer = new ResizeObserver(() => {
if (!triggered) {
triggered = true;
if (_root.$el.offsetWidth === offsetWidth && _root.$el.offsetHeight === offsetHeight) {
return;
}
}
if (_root.$el.offsetWidth === 0 || _root.$el.offsetHeight === 0) {
return;
}
resizeCallback();
});
observer.observe(_root.$el);
}
onCleanup(() => {
if (observer == null) {
return;
}
observer.disconnect();
observer = null;
});
}
);
}
const ECHARTS_KEY = Symbol("UniEcharts.echarts");
function provideEcharts(echarts) {
provide(ECHARTS_KEY, echarts);
}
function useEcharts() {
return inject(ECHARTS_KEY, {
init,
registerPreprocessor,
setPlatformAPI,
use,
throttle
});
}
function isEmpty(value) {
if (value == null) {
return true;
}
if (typeof value === "string" || Array.isArray(value)) {
return value.length === 0;
}
if (typeof value === "object") {
return Object.keys(value).length === 0;
}
return false;
}
function defaultTo(value, ...defaultValues) {
if (defaultValues.length === 0) {
return value;
}
if (value == null || value !== value) {
return defaultTo(defaultValues[0], ...defaultValues.slice(1));
}
return value;
}
function sleep(timeout) {
return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
}
function upperFirst(value) {
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
}
function lowerFirst(value) {
return `${value.charAt(0).toLowerCase()}${value.slice(1)}`;
}
function mitt(events) {
const _events = defaultTo(events, /* @__PURE__ */ new Map());
return {
events: _events,
on(topic, handler) {
const handlers = _events.get(topic);
if (handlers) {
handlers.push(handler);
} else {
_events.set(topic, [handler]);
}
},
off(topic, handler) {
const handlers = _events.get(topic);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
} else {
_events.set(topic, []);
}
}
},
emit(topic, event) {
const handlers = _events.get(topic);
if (handlers) {
for (const handler of handlers.slice()) {
handler(event);
}
}
const hdlrs = _events.get("*");
if (hdlrs) {
for (const handler of hdlrs.slice()) {
handler(topic, event);
}
}
}
};
}
const Platform = {
APP: "APP",
APP_ANDROID: "APP-ANDROID",
APP_IOS: "APP-IOS",
APP_HARMONY: "APP-HARMONY",
WEB: "WEB",
MP: "MP",
MP_WEIXIN: "MP-WEIXIN",
MP_ALIPAY: "MP-ALIPAY",
MP_BAIDU: "MP-BAIDU",
MP_TOUTIAO: "MP-TOUTIAO",
MP_LARK: "MP-LARK",
MP_QQ: "MP-QQ",
MP_KUAISHOU: "MP-KUAISHOU",
MP_JD: "MP-JD",
MP_360: "MP-360",
MP_XHS: "MP-XHS",
MP_HARMONY: "MP-HARMONY",
QUICKAPP_WEBVIEW: "QUICKAPP-WEBVIEW",
QUICKAPP_WEBVIEW_UNION: "QUICKAPP-WEBVIEW-UNION",
QUICKAPP_WEBVIEW_HUAWEI: "QUICKAPP-WEBVIEW-HUAWEI",
OTHER: "OTHER"
};
function getPlatform() {
let platform2 = Platform.OTHER;
// #ifdef APP
platform2 = Platform.APP;
// #endif
// #ifdef APP-ANDROID
platform2 = Platform.APP_ANDROID;
// #endif
// #ifdef APP-IOS
platform2 = Platform.APP_IOS;
// #endif
// #ifdef APP-HARMONY
platform2 = Platform.APP_HARMONY;
// #endif
// #ifdef WEB
platform2 = Platform.WEB;
// #endif
// #ifdef MP
platform2 = Platform.MP;
// #endif
// #ifdef MP-WEIXIN
platform2 = Platform.MP_WEIXIN;
// #endif
// #ifdef MP-ALIPAY
platform2 = Platform.MP_ALIPAY;
// #endif
// #ifdef MP-BAIDU
platform2 = Platform.MP_BAIDU;
// #endif
// #ifdef MP-TOUTIAO
platform2 = Platform.MP_TOUTIAO;
// #endif
// #ifdef MP-LARK
platform2 = Platform.MP_LARK;
// #endif
// #ifdef MP-QQ
platform2 = Platform.MP_QQ;
// #endif
// #ifdef MP-KUAISHOU
platform2 = Platform.MP_KUAISHOU;
// #endif
// #ifdef MP-JD
platform2 = Platform.MP_JD;
// #endif
// #ifdef MP-360
platform2 = Platform.MP_360;
// #endif
// #ifdef MP-XHS
platform2 = Platform.MP_XHS;
// #endif
// #ifdef MP-HARMONY
platform2 = Platform.MP_HARMONY;
// #endif
// #ifdef QUICKAPP-WEBVIEW
platform2 = Platform.QUICKAPP_WEBVIEW;
// #endif
// #ifdef QUICKAPP-WEBVIEW-UNION
platform2 = Platform.QUICKAPP_WEBVIEW_UNION;
// #endif
// #ifdef QUICKAPP-WEBVIEW-HUAWEI
platform2 = Platform.QUICKAPP_WEBVIEW_HUAWEI;
// #endif
return platform2;
}
const platform = getPlatform();
const isApp = platform === Platform.APP;
const isAppAndroid = platform === Platform.APP_ANDROID;
const isAppIos = platform === Platform.APP_IOS;
const isAppHarmony = platform === Platform.APP_HARMONY;
const isWeb = platform === Platform.WEB;
const isMp = platform === Platform.MP;
const isMpWeixin = platform === Platform.MP_WEIXIN;
const isMpAlipay = platform === Platform.MP_ALIPAY;
const isMpBaidu = platform === Platform.MP_BAIDU;
const isMpToutiao = platform === Platform.MP_TOUTIAO;
const isMpLark = platform === Platform.MP_LARK;
const isMpQq = platform === Platform.MP_QQ;
const isMpKuaishou = platform === Platform.MP_KUAISHOU;
const isMpJd = platform === Platform.MP_JD;
const isMp360 = platform === Platform.MP_360;
const isMpXhs = platform === Platform.MP_XHS;
const isMpHarmony = platform === Platform.MP_HARMONY;
const isQuickappWebview = platform === Platform.QUICKAPP_WEBVIEW;
const isQuickappWebviewUnion = platform === Platform.QUICKAPP_WEBVIEW_UNION;
const isQuickappWebviewHuawei = platform === Platform.QUICKAPP_WEBVIEW_HUAWEI;
const isOtherPlatform = platform === Platform.OTHER;
function getDeviceInfo() {
if (uni.canIUse("getDeviceInfo") || uni.getDeviceInfo) {
return uni.getDeviceInfo();
} else {
return uni.getSystemInfoSync();
}
}
function getWindowInfo() {
if (uni.canIUse("getWindowInfo") || uni.getWindowInfo) {
return uni.getWindowInfo();
} else {
return uni.getSystemInfoSync();
}
}
function getAppBaseInfo() {
if (uni.canIUse("getAppBaseInfo") || uni.getAppBaseInfo) {
return uni.getAppBaseInfo();
} else {
return uni.getSystemInfoSync();
}
}
function getVersion() {
if (isMpAlipay) {
return my.SDKVersion;
}
return getAppBaseInfo().SDKVersion;
}
function compareVersion(v1, v2) {
const s1 = v1.split(".");
const s2 = v2.split(".");
for (let i = 0; i < Math.max(s1.length, s2.length); i += 1) {
const num1 = Number.parseInt(defaultTo(s1[i], "0"));
const num2 = Number.parseInt(defaultTo(s2[i], "0"));
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
}
function gte(version) {
return compareVersion(getVersion(), version) >= 0;
}
function canIUseCanvas2d() {
if (isMpWeixin) {
if (getDeviceInfo().platform === "mac") {
return false;
}
return gte("2.9.0");
}
if (isMpAlipay) {
return gte("2.7.0");
}
if (isMpToutiao) {
return gte("1.78.0");
}
return false;
}
function querySelect(component, selector, fields) {
return new Promise((resolve, reject) => {
uni.createSelectorQuery().in(component).select(selector).fields(fields, () => {
}).exec(([node]) => {
if (node) {
resolve(node);
} else {
reject();
}
});
});
}
const SHORT_HEX_REGEX = /#([0-9a-f])([0-9a-f])([0-9a-f])\b/gi;
class UniCanvas {
tagName = "canvas";
attrs = {};
canvasId;
context;
canvasNode;
_emitter;
constructor(canvasId, context, canvasNode) {
this.canvasId = canvasId;
this.context = context;
this.canvasNode = canvasNode;
if (canvasNode == null) {
this._setupContext(context);
}
this._emitter = mitt();
}
_setupContext(context) {
const styles = [
"fillStyle",
"fontSize",
"globalAlpha",
"lineCap",
"lineDash",
"lineJoin",
"lineWidth",
"miterLimit",
"strokeStyle",
"textAlign",
"textBaseline",
"opacity",
"shadowOffsetX",
"shadowOffsetY",
"shadowBlur",
"shadowColor",
"font"
];
const shadow = {
offsetX: 0,
offsetY: 0,
blur: 0,
color: "#000000"
};
for (const key of styles) {
Object.defineProperty(context, key, {
set(value) {
if (key === "opacity") {
context.setGlobalAlpha(value);
return;
}
if (key === "font") {
context.setFontSize(UniCanvas.parseFontSize(value));
return;
}
if (key.indexOf("shadow") === 0) {
if (key !== "shadowColor") {
shadow[lowerFirst(key.slice(6))] = value;
} else {
shadow.color = UniCanvas.normalizeColor(context, value);
context.setShadow(
shadow.offsetX,
shadow.offsetY,
shadow.blur,
shadow.color
);
}
return;
}
if (key === "fillStyle" || key === "strokeStyle") {
value = UniCanvas.normalizeColor(context, value);
}
context[`set${upperFirst(key)}`](value);
}
});
}
const _drawImage = context.drawImage;
context.drawImage = (...args) => {
_drawImage(args[0].src, ...args.slice(1));
};
if (context.strokeText == null) {
context.strokeText = (...args) => {
context.fillText(...args);
};
}
if (context.createRadialGradient == null) {
context.createRadialGradient = (...args) => {
return context.createCircularGradient(...args.slice(-3));
};
}
if (context.measureText == null || getDeviceInfo().osName === "harmonyos") {
const strlen = (str) => {
let len = 0;
for (let i = 0; i < str.length; i += 1) {
const unicode = str.charCodeAt(i);
if (unicode > 0 && unicode < 128) {
len += 1;
} else {
len += 2;
}
}
return len;
};
context.measureText = (text, font) => {
const fontSize = defaultTo(
// @ts-expect-error whatever
context.state && context.state.fontSize,
UniCanvas.parseFontSize(font),
12
) / 2;
const factor = fontSize >= 16 ? 1.3 : 1;
return {
width: strlen(text) * fontSize * factor
};
};
}
}
get width() {
if (this.canvasNode == null) {
return this.getAttribute("width");
}
return this.canvasNode.width;
}
set width(value) {
if (this.canvasNode == null) {
this.setAttribute("width", value);
return;
}
this.canvasNode.width = value;
}
get height() {
if (this.canvasNode == null) {
return this.getAttribute("height");
}
return this.canvasNode.height;
}
set height(value) {
if (this.canvasNode == null) {
this.setAttribute("height", value);
return;
}
this.canvasNode.height = value;
}
getContext(type) {
if (type === "2d") {
return this.context;
}
}
setAttribute(key, value) {
this.attrs[key] = value;
}
getAttribute(key) {
return this.attrs[key];
}
addEventListener(type, listener) {
this._emitter.on(type, listener);
}
removeEventListener(type, listener) {
this._emitter.off(type, listener);
}
dispatchEvent(type, event) {
if (typeof type === "object") {
this._emitter.emit(type.type, type);
} else {
this._emitter.emit(type, event);
}
return true;
}
attachEvent() {
}
detachEvent() {
}
requestAnimationFrame(callback) {
if (this.canvasNode != null && typeof this.canvasNode.requestAnimationFrame === "function") {
return this.canvasNode.requestAnimationFrame(callback);
}
if (typeof requestAnimationFrame === "function") {
return requestAnimationFrame(callback);
}
return setTimeout(callback, 1e3 / 60);
}
cancelAnimationFrame(id) {
if (this.canvasNode != null && typeof this.canvasNode.cancelAnimationFrame === "function") {
this.canvasNode.cancelAnimationFrame(id);
return;
}
if (typeof cancelAnimationFrame === "function") {
cancelAnimationFrame(id);
return;
}
clearTimeout(id);
}
toTempFilePath(options = {}) {
const opts = {};
if (this.canvasNode != null) {
opts.canvas = this.canvasNode;
} else {
opts.canvasId = this.canvasId;
}
return uni.canvasToTempFilePath({
...opts,
...options
});
}
static parseFontSize(font) {
return Number.parseFloat(defaultTo(font, "").match(/([\d.]+)px/)[1]);
}
static normalizeColor(context, color) {
if (typeof color === "string") {
// #ifdef MP-TOUTIAO
SHORT_HEX_REGEX.lastIndex = 0;
if (SHORT_HEX_REGEX.test(color)) {
return color.replace(SHORT_HEX_REGEX, "#$1$1$2$2$3$3");
}
// #endif
}
return color;
}
static dispatch(handler, event, touch) {
handler.dispatch(event, {
zrX: touch.x,
zrY: touch.y,
zrDelta: touch.wheelDelta,
preventDefault: () => {
},
stopImmediatePropagation: () => {
},
stopPropagation: () => {
}
});
}
}
class UniImage {
tagName = "img";
width;
height;
onload;
onerror;
_src;
constructor() {
this._src = null;
this.width = 0;
this.height = 0;
}
get src() {
return this._src;
}
set src(value) {
this._src = value;
uni.getImageInfo({
src: value,
success: (res) => {
this.width = res.width;
this.height = res.height;
if (this.onload) {
this.onload(res);
}
},
fail: (err) => {
if (this.onerror) {
this.onerror(err);
}
}
});
}
}
function setupEchartsCanvas(echarts, {
canvas,
node
}) {
echarts.registerPreprocessor((option) => {
if (option == null) {
return;
}
if (option.series != null) {
if (Array.isArray(option.series)) {
if (!isEmpty(option.series)) {
for (const item of option.series) {
item.progressive = 0;
}
}
} else if (typeof option.series === "object") {
option.series.progressive = 0;
}
}
});
const loadImage = (src, onload, onerror) => {
if (node != null && node.createImage) {
const image2 = node.createImage();
image2.onload = onload;
image2.onerror = onerror;
image2.src = src;
return image2;
}
const image = new UniImage();
image.onload = onload;
image.onerror = onerror;
image.src = src;
return image;
};
echarts.setPlatformAPI({
loadImage: node != null ? loadImage : void 0,
createCanvas() {
return canvas;
}
});
}
function getIsPc() {
// #ifdef WEB
if (!("ontouchstart" in window)) {
return true;
}
// #endif
if (isMpWeixin || isMpToutiao || isMpAlipay) {
return /windows/i.test(getDeviceInfo().platform);
}
return false;
}
const INIT_OPTIONS_KEY = Symbol("UniEcharts.initOptions");
function provideEchartsInitOptions(value) {
provide(INIT_OPTIONS_KEY, value);
}
function useEchartsInitOptions(value) {
const injectInitOptions = inject(INIT_OPTIONS_KEY, null);
const unwrapInjectInitOptions = computed(() => {
return toValue(injectInitOptions);
});
const innerInitOptions = computed(() => {
return defaultTo(toValue(value), unwrapInjectInitOptions.value, {});
});
return {
injectInitOptions: unwrapInjectInitOptions,
innerInitOptions
};
}
function useEchartsMouseWheel({
isPc,
chart,
getTouch
}) {
function mouseWheelHandler(event) {
if (chart.value == null) {
return;
}
const { handler } = chart.value.getZr();
UniCanvas.dispatch(handler, "mousewheel", getTouch(event));
}
onMounted(() => {
if (isPc) {
document.addEventListener("wheel", mouseWheelHandler);
}
});
onBeforeUnmount(() => {
if (isPc) {
document.removeEventListener("wheel", mouseWheelHandler);
}
});
}
const OPTION_KEY = "UniEcharts.option";
function getEchartsOptionKey(key) {
if (isEmpty(key)) {
return OPTION_KEY;
}
return `${OPTION_KEY}_${key}`;
}
function provideEchartsOption(keyOrValue, value) {
if (typeof keyOrValue === "string") {
provide(getEchartsOptionKey(keyOrValue), value);
return;
}
provide(getEchartsOptionKey(), keyOrValue);
}
function useEchartsOption(key, value) {
const injectOption = inject(getEchartsOptionKey(key), null);
const unwrapInjectOption = computed(() => {
return toValue(injectOption);
});
const innerOption = computed(() => {
return defaultTo(toValue(value), unwrapInjectOption.value);
});
return {
injectOption: unwrapInjectOption,
innerOption
};
}
const THEME_KEY = Symbol("UniEcharts.theme");
function provideEchartsTheme(value) {
provide(THEME_KEY, value);
}
function useEchartsTheme(value) {
const injectTheme = inject(THEME_KEY, null);
const unwrapInjectTheme = computed(() => {
return toValue(injectTheme);
});
const innerTheme = computed(() => {
return defaultTo(toValue(value), unwrapInjectTheme.value, {});
});
return {
injectTheme: unwrapInjectTheme,
innerTheme
};
}
function useEchartsTouch({
vueThis,
supportHover,
isPc,
canvasId,
chart,
canvasRect,
getTouch
}) {
const touching = shallowRef(false);
const state = {
x: 0,
y: 0,
t: 0
};
let timer = 0;
let ticking = false;
let rafId = 0;
let lastMoveEvent = null;
function destroyTimer() {
if (timer === 0) {
return;
}
clearTimeout(timer);
timer = 0;
}
function getCanvas() {
if (chart.value == null) {
return null;
}
return chart.value.getDom();
}
function normalizeTouches(touches) {
if (Array.isArray(touches)) {
return touches;
}
if (typeof touches === "object" && touches != null) {
return Object.values(touches);
}
return touches;
}
function transformTouchesEvent(event) {
for (let i = 0; i < event.touches.length; i += 1) {
const item = event.touches[i];
item.offsetX = item.x;
item.offsetY = item.y;
}
return event;
}
function onStart(event) {
touching.value = true;
const next = () => {
if (chart.value == null) {
return;
}
const touch = getTouch(event, normalizeTouches(event.touches));
state.x = touch.x;
state.y = touch.y;
state.t = Date.now();
const { handler } = chart.value.getZr();
UniCanvas.dispatch(handler, "mousedown", touch);
UniCanvas.dispatch(handler, "mousemove", touch);
handler.processGesture(transformTouchesEvent(event), "start");
destroyTimer();
};
if (isPc) {
querySelect(vueThis, `#${canvasId}`, {
rect: true
}).then(({ top, left }) => {
canvasRect.top = top;
canvasRect.left = left;
next();
});
return;
}
next();
}
function _onMove(event) {
if (isPc && toValue(supportHover) && !touching.value) {
touching.value = true;
}
if (chart.value == null || !touching.value) {
return;
}
const { handler } = chart.value.getZr();
UniCanvas.dispatch(handler, "mousemove", getTouch(event, normalizeTouches(event.touches)));
handler.processGesture(transformTouchesEvent(event), "change");
}
function onMove(event) {
const canvas = getCanvas();
if (canvas == null) {
return;
}
lastMoveEvent = event;
if (ticking) {
return;
}
ticking = true;
rafId = canvas.requestAnimationFrame(() => {
try {
if (lastMoveEvent != null) {
_onMove(lastMoveEvent);
}
} finally {
lastMoveEvent = null;
ticking = false;
}
});
}
function onEnd(event) {
touching.value = false;
if (chart.value == null) {
return;
}
const touch = getTouch(event, normalizeTouches(event.changedTouches));
const { handler } = chart.value.getZr();
UniCanvas.dispatch(handler, "mouseup", touch);
handler.processGesture(transformTouchesEvent(event), "end");
const isClick = Math.abs(touch.x - state.x) < 10 && Date.now() - state.t < 200;
if (isClick) {
UniCanvas.dispatch(handler, "click", touch);
} else {
if (timer > 0) {
destroyTimer();
}
timer = setTimeout(() => {
UniCanvas.dispatch(handler, "mousemove", { x: 999999999, y: 999999999 });
UniCanvas.dispatch(handler, "mouseup", { x: 999999999, y: 999999999 });
}, 50);
}
}
function cleanup() {
destroyTimer();
if (rafId !== 0) {
const canvas = getCanvas();
if (canvas != null) {
canvas.cancelAnimationFrame(rafId);
}
rafId = 0;
}
lastMoveEvent = null;
ticking = false;
}
return {
onStart,
onMove,
onEnd,
cleanup
};
}
const UPDATE_OPTIONS_KEY = Symbol("UniEcharts.updateOptions");
function provideEchartsUpdateOptions(value) {
provide(UPDATE_OPTIONS_KEY, value);
}
function useEchartsUpdateOptions(value) {
const injectUpdateOptions = inject(UPDATE_OPTIONS_KEY, null);
const unwrapInjectUpdateOptions = computed(() => {
return toValue(injectUpdateOptions);
});
const innerUpdateOptions = computed(() => {
return defaultTo(toValue(value), unwrapInjectUpdateOptions.value, {});
});
return {
injectUpdateOptions: unwrapInjectUpdateOptions,
innerUpdateOptions
};
}
const LOADING_OPTIONS_KEY = Symbol("UniEcharts.loadingOptions");
function provideEchartsLoadingOptions(value) {
provide(LOADING_OPTIONS_KEY, value);
}
function useLoading(chart, {
loading,
loadingOptions
}) {
const injectLoadingOptions = inject(LOADING_OPTIONS_KEY, null);
const unwrapInjectLoadingOptions = computed(() => {
return toValue(injectLoadingOptions);
});
const innerLoadingOptions = computed(() => {
return {
...defaultTo(unwrapInjectLoadingOptions.value, {}),
...defaultTo(toValue(loadingOptions), {})
};
});
watchEffect(() => {
const instance = chart.value;
if (instance == null) {
return;
}
if (toValue(loading)) {
instance.showLoading(innerLoadingOptions.value);
} else {
instance.hideLoading();
}
});
}
const ECHARTS_APIS = [
"getWidth",
"getHeight",
"getDom",
"getOption",
"dispatchAction",
"convertToPixel",
"convertFromPixel",
"containPixel",
"getDataURL",
"getConnectedDataURL",
"appendData",
"clear",
"isDisposed",
"dispose"
];
function usePublicApi(chart) {
function makePublicMethod(name) {
return (...args) => {
if (chart.value == null) {
throw new Error("ECharts is not initialized yet.");
}
return chart.value[name].apply(chart.value, args);
};
}
function makePublicMethods() {
const methods = /* @__PURE__ */ Object.create(null);
for (const name of ECHARTS_APIS) {
methods[name] = makePublicMethod(name);
}
return methods;
}
return makePublicMethods();
}
let uid = 0;
function useUid() {
uid += 1;
return uid;
}
function useVueThis() {
const vm = getCurrentInstance();
return vm.proxy;
}
export { ECHARTS_KEY, INIT_OPTIONS_KEY, LOADING_OPTIONS_KEY, OPTION_KEY, Platform, THEME_KEY, UPDATE_OPTIONS_KEY, UniCanvas, UniImage, canIUseCanvas2d, compareVersion, defaultTo, getAppBaseInfo, getDeviceInfo, getEchartsOptionKey, getIsPc, getPlatform, getVersion, getWindowInfo, isApp, isAppAndroid, isAppHarmony, isAppIos, isEmpty, isMp, isMp360, isMpAlipay, isMpBaidu, isMpHarmony, isMpJd, isMpKuaishou, isMpLark, isMpQq, isMpToutiao, isMpWeixin, isMpXhs, isOtherPlatform, isQuickappWebview, isQuickappWebviewHuawei, isQuickappWebviewUnion, isWeb, lowerFirst, mitt, platform, provideEcharts, provideEchartsInitOptions, provideEchartsLoadingOptions, provideEchartsOption, provideEchartsTheme, provideEchartsUpdateOptions, querySelect, setupEchartsCanvas, sleep, upperFirst, useAutoresize, useEcharts, useEchartsInitOptions, useEchartsMouseWheel, useEchartsOption, useEchartsTheme, useEchartsTouch, useEchartsUpdateOptions, useLoading, usePublicApi, useUid, useVueThis };