优化生产展示逻辑,增加可选
增加自定义组件的说明书
This commit is contained in:
commit
3030c2a261
@ -13,7 +13,7 @@
|
||||
<!-- 标题行 -->
|
||||
<view class="header-row">
|
||||
<view class="title">天然气产量</view>
|
||||
<view class="more" @click="selectMore">更多 →</view>
|
||||
<view class="more" @click="selectMore">更多>></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="container">
|
||||
|
0
uni_modules/cxc-szcx-dateRangeSelect/changelog.md
Normal file
0
uni_modules/cxc-szcx-dateRangeSelect/changelog.md
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view class="compact-datetime-picker">
|
||||
<!-- 输入框区域 -->
|
||||
<view class="input-container">
|
||||
<view class="compact-input" @click="openPicker">
|
||||
{{ dateRange[0] || '开始日期' }}
|
||||
</view>
|
||||
<text v-if="mode==='range'" class="separator">至</text>
|
||||
<view v-if="mode==='range'" class="compact-input" @click="openPicker">
|
||||
{{ dateRange[1] || '结束日期' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wu-calendar ref="calendar" :mode="mode" :date="dateRange" @confirm="calendarConfirm"
|
||||
slideSwitchMode="horizontal" type="month" :fold="false" :insert="false" :rangeSameDay="true" :lunar="true"
|
||||
:monthShowCurrentMonth="false" :range-end-repick="true" :item-height="45"
|
||||
:rangeEndRepick="true"></wu-calendar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
formatDate,
|
||||
getDateAfterDays,
|
||||
getDateAfterMonths
|
||||
} from '@/utils/dateTime.js';
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [null, null]
|
||||
},
|
||||
// 日期选择模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
dateRange: null
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
console.log(44, JSON.stringify(newVal))
|
||||
let dateType = Object.prototype.toString.call(newVal);
|
||||
if (this.mode == 'single' && dateType != '[object String]') {
|
||||
return console.error(`类型错误,mode=${this.mode}时,date=String`);
|
||||
} else if (this.mode != 'single' && dateType != '[object Array]') {
|
||||
return console.error(`类型错误,mode=${this.mode}时,date=Array`);
|
||||
}
|
||||
if (this.mode == 'single') {
|
||||
this.dateRange = ''
|
||||
this.dateRange = newVal;
|
||||
}
|
||||
// 根据类型默认选中不同的值
|
||||
if (this.mode == 'multiple') {
|
||||
this.dateRange = []
|
||||
this.dateRange = newVal;
|
||||
} else if (this.mode == 'range') {
|
||||
this.dateRange = []
|
||||
this.dateRange.push(formatDate(new Date(newVal[0])));
|
||||
this.dateRange.push(formatDate(new Date(newVal[1])));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openPicker() {
|
||||
this.$refs.calendar.open();
|
||||
},
|
||||
calendarConfirm(e) {
|
||||
this.dateRange = [];
|
||||
try {
|
||||
this.dateRange.push(formatDate(new Date(e.range.before)));
|
||||
this.dateRange.push(formatDate(new Date(e.range.after)));
|
||||
} catch (error) {
|
||||
return;
|
||||
//TODO handle the exception
|
||||
}
|
||||
this.$emit('update:modelValue', this.dateRange);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 紧凑型输入框 */
|
||||
.compact-input {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 16px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.compact-input.active {
|
||||
border-color: #409eff;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
</style>
|
83
uni_modules/cxc-szcx-dateRangeSelect/package.json
Normal file
83
uni_modules/cxc-szcx-dateRangeSelect/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "cxc-szcx-dateRangeSelect",
|
||||
"displayName": "cxc-szcx-dateRangeSelect",
|
||||
"version": "1.0.0",
|
||||
"description": "cxc-szcx-dateRangeSelect",
|
||||
"keywords": [
|
||||
"cxc-szcx-dateRangeSelect"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "",
|
||||
"data": "",
|
||||
"permissions": ""
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "u",
|
||||
"aliyun": "u",
|
||||
"alipay": "u"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "u",
|
||||
"vue3": "u"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u",
|
||||
"app-uvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
162
uni_modules/cxc-szcx-dateRangeSelect/readme.md
Normal file
162
uni_modules/cxc-szcx-dateRangeSelect/readme.md
Normal file
@ -0,0 +1,162 @@
|
||||
|
||||
### 紧凑型日期时间选择器组件说明
|
||||
|
||||
|
||||
#### 一、组件功能
|
||||
该组件是基于 UniApp 开发的紧凑型日期选择器,支持以下功能:
|
||||
1. **三种选择模式**:
|
||||
- 单选(single)
|
||||
- 范围选择(range)
|
||||
- 多选(multiple)
|
||||
2. **弹出式日历选择**:
|
||||
- 支持月份滑动切换
|
||||
- 显示农历日期
|
||||
- 支持同天范围选择
|
||||
3. **数据双向绑定**:
|
||||
- 通过 `modelValue` 实现父子组件数据同步
|
||||
4. **自定义样式**:
|
||||
- 紧凑型输入框设计
|
||||
- 支持自定义主题颜色
|
||||
- 响应式布局适配
|
||||
|
||||
|
||||
#### 二、组件Props
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 说明 |
|
||||
|--------------|------------|----------|--------------------------|
|
||||
| `modelValue` | Array/Date | `[null]` | 双向绑定的值(根据模式变化) |
|
||||
| `mode` | String | `single` | 选择模式(single/range/multiple) |
|
||||
|
||||
|
||||
#### 三、组件数据
|
||||
|
||||
| 名称 | 类型 | 说明 |
|
||||
|------------|--------|--------------------------|
|
||||
| `dateRange` | Array | 当前选中的日期范围(内部状态) |
|
||||
|
||||
|
||||
#### 四、组件方法
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
|----------------|------------|--------------------------|
|
||||
| `openPicker` | 无 | 打开日历选择器 |
|
||||
| `calendarConfirm` | `e` | 日历确认回调(处理选中数据) |
|
||||
|
||||
|
||||
#### 五、事件说明
|
||||
|
||||
| 事件名 | 说明 | 返回值 |
|
||||
|--------------|--------------------------|----------------------|
|
||||
| `update:modelValue` | 数据更新时触发 | 当前选中的日期数组 |
|
||||
|
||||
|
||||
#### 六、样式说明
|
||||
```vue
|
||||
<style scoped>
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.compact-input {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 16px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.compact-input.active {
|
||||
border-color: #409eff;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**关键样式点**:
|
||||
1. 输入框宽度固定为 120px
|
||||
2. 紧凑型设计(height: 20px)
|
||||
3. 活动状态样式增强
|
||||
4. 水平排列布局
|
||||
|
||||
|
||||
#### 七、使用示例
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<!-- 单选模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="singleDate"
|
||||
mode="single"
|
||||
/>
|
||||
|
||||
<!-- 范围选择模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="rangeDate"
|
||||
mode="range"
|
||||
/>
|
||||
|
||||
<!-- 多选模式 -->
|
||||
<compact-datetime-picker
|
||||
v-model="multipleDate"
|
||||
mode="multiple"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
singleDate: null,
|
||||
rangeDate: [null, null],
|
||||
multipleDate: []
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
#### 八、注意事项
|
||||
1. **日期格式要求**:
|
||||
- 单选模式:`YYYY-MM-DD` 字符串格式
|
||||
- 范围模式:`[startDate, endDate]` 数组格式
|
||||
- 多选模式:`[date1, date2, ...]` 数组格式
|
||||
2. **依赖组件**:
|
||||
- 需安装 `wu-calendar` 组件(第三方库)
|
||||
3. **异常处理**:
|
||||
```javascript
|
||||
calendarConfirm(e) {
|
||||
this.dateRange = [];
|
||||
try {
|
||||
this.dateRange.push(formatDate(new Date(e.range.before)));
|
||||
this.dateRange.push(formatDate(new Date(e.range.after)));
|
||||
} catch (error) {
|
||||
return; // 异常处理
|
||||
}
|
||||
this.$emit('update:modelValue', this.dateRange);
|
||||
}
|
||||
```
|
||||
4. **性能优化**:
|
||||
- 建议使用 `:range-end-repick="true"` 开启范围重新选择
|
||||
- 通过 `:item-height="45"` 控制日历行高
|
||||
|
||||
|
||||
#### 九、扩展功能建议
|
||||
1. 添加时间选择功能
|
||||
2. 支持自定义日期禁用范围
|
||||
3. 增加国际化支持
|
||||
4. 添加动画过渡效果
|
||||
5. 支持预设常用日期范围
|
||||
|
||||
如果需要进一步定制化,可以修改以下部分:
|
||||
1. 日历组件配置(`wu-calendar` 参数)
|
||||
2. 日期格式化函数(`formatDate`)
|
||||
3. 输入框样式和交互逻辑
|
||||
4. 异常处理机制
|
0
uni_modules/cxc-szcx-dictSelect/changelog.md
Normal file
0
uni_modules/cxc-szcx-dictSelect/changelog.md
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-data-checkbox v-model="selectedValuesArray" :localdata="dictItems" :multiple="true" data-key="value"
|
||||
data-value="label"></uni-data-checkbox>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
watch
|
||||
} from 'vue';
|
||||
import {
|
||||
getDictItemsApi
|
||||
} from '@/api/api.js';
|
||||
|
||||
// 定义 props 和 emits
|
||||
const props = defineProps({
|
||||
dictCode: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
// 定义数据
|
||||
const dictItems = ref([]);
|
||||
const selectedValuesArray = ref(props.modelValue ? props.modelValue.split(',') : []);
|
||||
|
||||
// 加载字典数据
|
||||
const loadDictItems = async () => {
|
||||
try {
|
||||
const response = await getDictItemsApi(props.dictCode);
|
||||
dictItems.value = response.result; // 假设响应数据的格式为 { data: [...] }
|
||||
} catch (error) {
|
||||
console.error('加载字典数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 监听选中值的变化并通知父组件
|
||||
watch(selectedValuesArray, (newValue) => {
|
||||
console.log('selectedValuesArray 变化:', newValue);
|
||||
const selectedValuesString = newValue.join(',');
|
||||
emits('update:modelValue', selectedValuesString);
|
||||
emits('change', selectedValuesString);
|
||||
}, {
|
||||
deep: true
|
||||
});
|
||||
|
||||
// 监听 props.modelValue 的变化
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
console.log('props.modelValue 变化:', newValue);
|
||||
selectedValuesArray.value = newValue ? newValue.split(',') : [];
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadDictItems();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可以添加组件的样式 */
|
||||
</style>
|
83
uni_modules/cxc-szcx-dictSelect/package.json
Normal file
83
uni_modules/cxc-szcx-dictSelect/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "cxc-szcx-dictSelect",
|
||||
"displayName": "cxc-szcx-dictSelect",
|
||||
"version": "1.0.0",
|
||||
"description": "cxc-szcx-dictSelect",
|
||||
"keywords": [
|
||||
"cxc-szcx-dictSelect"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "",
|
||||
"data": "",
|
||||
"permissions": ""
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "u",
|
||||
"aliyun": "u",
|
||||
"alipay": "u"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "u",
|
||||
"vue3": "u"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u",
|
||||
"app-uvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
uni_modules/cxc-szcx-dictSelect/readme.md
Normal file
90
uni_modules/cxc-szcx-dictSelect/readme.md
Normal file
@ -0,0 +1,90 @@
|
||||
# cxc-szcx-dictSelect
|
||||
### 组件说明:`uni-data-checkbox` 多选字典项选择组件
|
||||
|
||||
#### 一、组件概述
|
||||
该组件基于 Vue 3 和 `uni-data-checkbox` 实现了一个多选的字典项选择器。它通过传入字典编码(`dictCode`)从后端接口获取字典项数据,并将其展示为多选框列表。组件支持双向数据绑定,可将选中的值同步给父组件,同时在选中值发生变化时触发 `change` 事件。
|
||||
|
||||
#### 二、组件使用的技术栈
|
||||
- **框架**:Vue 3(使用组合式 API)
|
||||
- **UI 组件**:`uni-data-checkbox`(用于展示多选框列表)
|
||||
- **请求库**:假设使用了自定义的 `getDictItemsApi` 函数进行后端数据请求
|
||||
|
||||
#### 三、组件输入(Props)
|
||||
|
||||
| 属性名 | 类型 | 是否必填 | 默认值 | 说明 |
|
||||
| ---- | ---- | ---- | ---- | ---- |
|
||||
| `dictCode` | String | 是 | 无 | 用于从后端获取字典项数据的字典编码 |
|
||||
| `modelValue` | String | 否 | '' | 双向绑定的选中值,多个值以逗号分隔 |
|
||||
|
||||
#### 四、组件输出(Emits)
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| ---- | ---- | ---- |
|
||||
| `update:modelValue` | 当选中值发生变化时,用于更新父组件的 `modelValue` | 选中值的字符串,多个值以逗号分隔 |
|
||||
| `change` | 当选中值发生变化时触发 | 选中值的字符串,多个值以逗号分隔 |
|
||||
|
||||
#### 五、组件内部数据
|
||||
|
||||
| 变量名 | 类型 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| `dictItems` | Ref<Array> | 存储从后端获取的字典项数据 |
|
||||
| `selectedValuesArray` | Ref<Array> | 存储当前选中的值,以数组形式存储 |
|
||||
|
||||
#### 六、组件方法
|
||||
|
||||
##### 1. `loadDictItems`
|
||||
- **功能**:异步从后端接口获取字典项数据,并将其赋值给 `dictItems`。
|
||||
- **实现逻辑**:
|
||||
- 调用 `getDictItemsApi` 函数,传入 `props.dictCode` 进行数据请求。
|
||||
- 若请求成功,将响应数据的 `result` 字段赋值给 `dictItems.value`。
|
||||
- 若请求失败,在控制台输出错误信息。
|
||||
|
||||
##### 2. 监听 `selectedValuesArray` 的变化
|
||||
- **功能**:当 `selectedValuesArray` 发生变化时,将选中的值转换为字符串,并触发 `update:modelValue` 和 `change` 事件通知父组件。
|
||||
- **实现逻辑**:
|
||||
- 使用 `watch` 函数监听 `selectedValuesArray` 的变化。
|
||||
- 当变化发生时,将新的选中值数组使用 `join(',')` 方法转换为字符串。
|
||||
- 触发 `update:modelValue` 和 `change` 事件,将转换后的字符串作为参数传递。
|
||||
|
||||
##### 3. 监听 `props.modelValue` 的变化
|
||||
- **功能**:当父组件传递的 `modelValue` 发生变化时,更新 `selectedValuesArray`。
|
||||
- **实现逻辑**:
|
||||
- 使用 `watch` 函数监听 `props.modelValue` 的变化。
|
||||
- 当变化发生时,将新的 `modelValue` 使用 `split(',')` 方法转换为数组,并赋值给 `selectedValuesArray.value`。
|
||||
- `immediate: true` 表示在组件初始化时就会执行一次监听回调,确保初始值能正确同步。
|
||||
|
||||
#### 七、组件生命周期钩子
|
||||
|
||||
##### `onMounted`
|
||||
- **功能**:在组件挂载后调用 `loadDictItems` 函数,从后端获取字典项数据。
|
||||
|
||||
#### 八、使用示例
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<MyDictCheckbox
|
||||
:dictCode="myDictCode"
|
||||
v-model="selectedValues"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<p>当前选中的值: {{ selectedValues }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import MyDictCheckbox from './MyDictCheckbox.vue';
|
||||
|
||||
const myDictCode = 'exampleDictCode';
|
||||
const selectedValues = ref('');
|
||||
|
||||
const handleChange = (newValue) => {
|
||||
console.log('选中值发生变化:', newValue);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 九、注意事项
|
||||
- 确保 `getDictItemsApi` 函数能正确获取后端数据,且响应数据的格式符合 `{ result: [...] }`。
|
||||
- 若 `modelValue` 初始值为空字符串,`selectedValuesArray` 会初始化为空数组。
|
||||
- 由于使用了 `watch` 的 `deep: true` 选项,在 `selectedValuesArray` 内部元素发生变化时也会触发监听回调,可能会影响性能,需注意。
|
0
uni_modules/cxc-szcx-lineChart/changelog.md
Normal file
0
uni_modules/cxc-szcx-lineChart/changelog.md
Normal file
@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- ECharts图表 -->
|
||||
<view class="chart-container">
|
||||
<l-echart ref="chart" @finished="initChart" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { formatDate, getDateAfterDays, getDateAfterMonths } from '@/utils/dateTime.js';
|
||||
const props = defineProps({
|
||||
dataList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true
|
||||
},
|
||||
xField: {
|
||||
type: String,
|
||||
default: 'rqDate'
|
||||
},
|
||||
yField: {
|
||||
type: String,
|
||||
default: 'rq'
|
||||
},
|
||||
legendField: {
|
||||
type: String,
|
||||
default: 'unit'
|
||||
},
|
||||
referenceValue: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
const chart = ref(null);
|
||||
const chartOption = ref({});
|
||||
const chartTitle = ref('');
|
||||
|
||||
//、数据处理
|
||||
const processSeriesData = () => {
|
||||
const legendItems = [...new Set(props.dataList.map((item) => item[props.legendField]?.trim() || '未命名'))];
|
||||
|
||||
return legendItems.map((legend) => {
|
||||
const items = props.dataList
|
||||
.filter((item) => item[props.legendField] === legend)
|
||||
.sort((a, b) => new Date(a[props.xField]) - new Date(b[props.xField]))
|
||||
.map((item) => ({
|
||||
name: item[props.xField],
|
||||
value: [
|
||||
formatDate(item[props.xField]), // X轴时间戳
|
||||
parseFloat(item[props.yField]) || 0 // Y轴数值
|
||||
]
|
||||
}));
|
||||
|
||||
return {
|
||||
name: legend,
|
||||
type: 'line',
|
||||
showSymbol: true,
|
||||
smooth: true,
|
||||
data: items
|
||||
};
|
||||
});
|
||||
};
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.dataList,
|
||||
() => {
|
||||
chartTitle.value = '历史趋势';
|
||||
updateChart();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 生命周期管理
|
||||
onMounted(() => {});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chart.value) {
|
||||
chart.value.dispose();
|
||||
chart.value = null;
|
||||
}
|
||||
});
|
||||
// 更新图表 formatDate
|
||||
const updateChart = () => {
|
||||
if (!chart.value) return;
|
||||
chartOption.value = generateOptions();
|
||||
chart.value.setOption(chartOption.value, true);
|
||||
};
|
||||
// 生成图表配置
|
||||
const generateOptions = () => ({
|
||||
title: {
|
||||
text: chartTitle.value,
|
||||
padding: [0, 0, 0, 30]
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.96)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 14
|
||||
},
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: '#FF6B6B',
|
||||
width: 1,
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
confine: true // 将 tooltip 限制在图表区域内
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
formatter: '{MM}-{dd}',
|
||||
rotate: 30
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f5f5f5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f5f5f5'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
formatter: (value) => `${value.toFixed(2)}`
|
||||
}
|
||||
},
|
||||
series: [
|
||||
...processSeriesData().map((series) => ({
|
||||
...series,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 6,
|
||||
shadowOffsetY: 3
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#00007f', // 建议改为动态颜色
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
}
|
||||
})),
|
||||
{
|
||||
type: 'line',
|
||||
markLine: {
|
||||
silent: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: '#FF4757',
|
||||
width: 2
|
||||
},
|
||||
data: [{ yAxis: props.referenceValue }],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'end',
|
||||
formatter: `参考值: ${props.referenceValue}`,
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
data: [] // 空数据系列仅用于显示参考线
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
bottom: 5,
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
},
|
||||
pageIconColor: '#FF6B6B',
|
||||
pageTextStyle: {
|
||||
color: '#999'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
right: 30,
|
||||
bottom: 40,
|
||||
left: 20,
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
minValueSpan: 3600 * 24 * 1000 * 7 // 最小缩放范围7天
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
setTimeout(async () => {
|
||||
if (!chart.value) return;
|
||||
const myChart = await chart.value.init(echarts);
|
||||
myChart.setOption(chartOption.value);
|
||||
}, 300);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 容器样式 */
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
min-height: 200px;
|
||||
padding: 20rpx;
|
||||
background: linear-gradient(145deg, #f8f9fa 0%, #ffffff 100%);
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 图表加载占位 */
|
||||
.chart-container::before {
|
||||
/* content: '数据加载中...'; */
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 图表主体样式 */
|
||||
.chart-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* 移动端优化 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 30vh;
|
||||
min-height: 200px;
|
||||
padding: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
83
uni_modules/cxc-szcx-lineChart/package.json
Normal file
83
uni_modules/cxc-szcx-lineChart/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "cxc-szcx-lineChart",
|
||||
"displayName": "cxc-szcx-lineChart",
|
||||
"version": "1.0.0",
|
||||
"description": "cxc-szcx-lineChart",
|
||||
"keywords": [
|
||||
"cxc-szcx-lineChart"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "",
|
||||
"data": "",
|
||||
"permissions": ""
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "u",
|
||||
"aliyun": "u",
|
||||
"alipay": "u"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "u",
|
||||
"vue3": "u"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u",
|
||||
"app-uvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
179
uni_modules/cxc-szcx-lineChart/readme.md
Normal file
179
uni_modules/cxc-szcx-lineChart/readme.md
Normal file
@ -0,0 +1,179 @@
|
||||
# cxc-szcx-lineChart
|
||||
# # `line-chart.vue` 组件说明书
|
||||
|
||||
## 一、组件概述
|
||||
`line-chart.vue` 是一个基于 ECharts 实现的折线图组件,用于在 UniApp 项目中展示数据的折线图。该组件接收一系列数据和配置参数,支持动态更新数据,并展示参考线。
|
||||
|
||||
## 二、组件依赖
|
||||
- **Vue 3**:使用 Vue 3 的组合式 API 进行开发。
|
||||
- **ECharts**:用于绘制折线图。
|
||||
- **`lime-echart`**:UniApp 插件,提供 ECharts 的渲染支持。
|
||||
|
||||
## 三、组件使用方法
|
||||
|
||||
### 1. 引入组件
|
||||
在需要使用该组件的页面中引入 `line-chart.vue` 组件。
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<line-chart
|
||||
:dataList="dataList"
|
||||
:xField="xField"
|
||||
:yField="yField"
|
||||
:legendField="legendField"
|
||||
:referenceValue="referenceValue"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LineChart from '@/components/line-chart.vue';
|
||||
|
||||
const dataList = [
|
||||
{ date: '2023-01-01', value: 10, category: 'A' },
|
||||
// 更多数据...
|
||||
];
|
||||
const xField = 'date';
|
||||
const yField = 'value';
|
||||
const legendField = 'category';
|
||||
const referenceValue = 15;
|
||||
</script>
|
||||
```
|
||||
|
||||
### 2. 组件属性
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| `dataList` | `Object` | `[]` | 包含图表数据的数组,每个元素是一个对象,包含 `xField`、`yField` 和 `legendField` 对应的数据。 |
|
||||
| `xField` | `String` | `''` | 数据中用于表示 x 轴的字段名。 |
|
||||
| `yField` | `String` | `''` | 数据中用于表示 y 轴的字段名。 |
|
||||
| `legendField` | `String` | `''` | 数据中用于表示图例的字段名。 |
|
||||
| `referenceValue` | `Number` | `10` | 图表中参考线的数值。 |
|
||||
|
||||
## 四、组件内部实现
|
||||
|
||||
### 1. 模板部分
|
||||
```vue
|
||||
<template>
|
||||
<view class="chart-container">
|
||||
<l-echart ref="chart" @init="initChart"></l-echart>
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
- 使用 `l-echart` 组件渲染图表,通过 `ref` 绑定到 `chart`,并在初始化完成时调用 `initChart` 方法。
|
||||
|
||||
### 2. 脚本部分
|
||||
|
||||
#### 2.1 引入必要的模块
|
||||
```javascript
|
||||
import { ref, watch } from 'vue';
|
||||
```
|
||||
引入 Vue 3 的 `ref` 和 `watch` 函数。
|
||||
|
||||
#### 2.2 定义组件属性
|
||||
```javascript
|
||||
const props = defineProps({
|
||||
dataList: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
},
|
||||
// 其他属性...
|
||||
});
|
||||
```
|
||||
定义组件接收的属性。
|
||||
|
||||
#### 2.3 初始化图表
|
||||
```javascript
|
||||
const chart = ref(null);
|
||||
let echarts = null;
|
||||
|
||||
const initChart = async (canvas) => {
|
||||
await initEcharts(canvas);
|
||||
updateChart();
|
||||
};
|
||||
|
||||
const initEcharts = async (canvas) => {
|
||||
echarts = await import('echarts');
|
||||
const { init } = await import('@/uni_modules/lime-echart/static/echarts.min');
|
||||
echarts = init(canvas, echarts);
|
||||
};
|
||||
```
|
||||
- `chart` 用于引用 `l-echart` 组件。
|
||||
- `initChart` 方法在图表初始化时调用,先初始化 ECharts 实例,再更新图表。
|
||||
- `initEcharts` 方法异步加载 ECharts 并初始化实例。
|
||||
|
||||
#### 2.4 处理数据
|
||||
```javascript
|
||||
const processData = () => {
|
||||
const legendData = [...new Set(props.dataList.map((item) => item[props.legendField]))];
|
||||
|
||||
return legendData.map((legendItem) => {
|
||||
const seriesData = props.dataList
|
||||
.filter((item) => item[props.legendField] === legendItem)
|
||||
.sort((a, b) => new Date(a[props.xField]) - new Date(b[props.xField]))
|
||||
.map((item) => ({
|
||||
name: item[props.xField],
|
||||
value: [item[props.xField], item[props.yField]]
|
||||
}));
|
||||
|
||||
return {
|
||||
name: legendItem,
|
||||
type: 'line',
|
||||
showSymbol: true,
|
||||
data: seriesData
|
||||
};
|
||||
});
|
||||
};
|
||||
```
|
||||
处理传入的数据,根据 `legendField` 分组,对每组数据按 `xField` 排序,并转换为 ECharts 所需的格式。
|
||||
|
||||
#### 2.5 获取图表配置
|
||||
```javascript
|
||||
const getOption = () => ({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
return `${params[0].axisValue}<br/>` + params.map((item) => `${item.marker} ${item.seriesName}: ${item.value[1]}`).join('<br/>');
|
||||
}
|
||||
},
|
||||
// 其他配置...
|
||||
});
|
||||
```
|
||||
返回 ECharts 的配置对象,包括 tooltip、x 轴、y 轴、系列数据、图例和网格等配置。
|
||||
|
||||
#### 2.6 更新图表
|
||||
```javascript
|
||||
const updateChart = () => {
|
||||
if (!chart.value) return;
|
||||
|
||||
const option = getOption();
|
||||
chart.value.setOption(option);
|
||||
};
|
||||
```
|
||||
如果 `chart` 实例存在,获取最新的图表配置并更新图表。
|
||||
|
||||
#### 2.7 监听数据变化
|
||||
```javascript
|
||||
watch(
|
||||
() => props.dataList,
|
||||
() => {
|
||||
updateChart();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
```
|
||||
当 `props.dataList` 发生变化时,调用 `updateChart` 方法更新图表。
|
||||
|
||||
### 3. 样式部分
|
||||
```css
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
}
|
||||
```
|
||||
设置图表容器的宽度为 100%,高度为 30vh。
|
||||
|
||||
## 五、注意事项
|
||||
- 确保项目中已经正确安装并配置了 `lime-echart` 插件。
|
||||
- 传入的 `dataList` 数据格式要符合要求,包含 `xField`、`yField` 和 `legendField` 对应的数据。
|
||||
- 由于使用了异步加载 ECharts,可能会有一定的延迟,需要确保在合适的时机初始化图表。
|
33
uni_modules/lime-drag/changelog.md
Normal file
33
uni_modules/lime-drag/changelog.md
Normal file
@ -0,0 +1,33 @@
|
||||
## 0.1.3(2023-08-19)
|
||||
- fix: 修复使用remove导致样式错乱
|
||||
## 0.1.2(2023-08-09)
|
||||
- fix: 修复nvue没有获取节点的问题
|
||||
- fix: 修复因延时导致卡在中途
|
||||
- fix: 修复change事件有时失效的问题
|
||||
## 0.1.1(2023-07-03)
|
||||
- chore: 更新文档
|
||||
## 0.1.0(2023-07-03)
|
||||
- fix: 外面的事件冒泡导致点击调动内部移动方法错乱
|
||||
## 0.0.9(2023-05-30)
|
||||
- fix: 修复因手机事件为`onLongpress`导致,在手机上无法长按
|
||||
- fix: 无法因css导致滚动
|
||||
## 0.0.8(2023-04-23)
|
||||
- feat: 更新文档
|
||||
## 0.0.7(2023-04-23)
|
||||
- feat: 由于删除是一个危险的动作,故把方法暴露出来,而不在内部处理。如果之前有使用删除的,需要注意
|
||||
- feat: 原来的`add`变更为`push`,增加`unshift`
|
||||
## 0.0.6(2023-04-12)
|
||||
- fix: 修复`handle`不生效问题
|
||||
- feat: 增加 `to`方法
|
||||
## 0.0.5(2023-04-11)
|
||||
- chore: `grid` 插槽增加 `nindex`、`oindex`
|
||||
## 0.0.4(2023-04-04)
|
||||
- chore: 去掉 script-setup 语法糖
|
||||
- chore: 文档增加 vue2 使用方法
|
||||
## 0.0.3(2023-03-30)
|
||||
- feat: 重要说明 更新 list 只会再次初始化
|
||||
- feat: 更新文档
|
||||
## 0.0.2(2023-03-29)
|
||||
- 修改文档
|
||||
## 0.0.1(2023-03-29)
|
||||
- 初次提交
|
93
uni_modules/lime-drag/components/l-drag/index.scss
Normal file
93
uni_modules/lime-drag/components/l-drag/index.scss
Normal file
@ -0,0 +1,93 @@
|
||||
$drag-handle-size: var(--l-drag-handle-size, 50rpx);
|
||||
$drag-delete-size: var(--l-drag-delete-size, 32rpx);
|
||||
.l-drag {
|
||||
// min-height: 100rpx;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
margin: 24rpx 30rpx 0 30rpx;
|
||||
// padding: 30rpx 0;
|
||||
|
||||
|
||||
/* #ifdef APP-NVUE */
|
||||
// flex: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
// width: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
.l-drag__inner {
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
min-height: 100rpx;
|
||||
}
|
||||
.l-drag__view {
|
||||
// touch-action: none;
|
||||
// user-select: none;
|
||||
// -webkit-user-select: auto;
|
||||
z-index: 2;
|
||||
transition: opacity 300ms ease;
|
||||
.mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: transparent;
|
||||
z-index: 9;
|
||||
}
|
||||
/* #ifndef APP-NVUE */
|
||||
> view {
|
||||
&:last-child {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
|
||||
}
|
||||
.l-drag-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
.l-drag__ghost {
|
||||
/* #ifndef APP-NVUE */
|
||||
> view {
|
||||
&:last-child {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
}
|
||||
.l-is-active {
|
||||
z-index: 3;
|
||||
}
|
||||
.l-is-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
.l-drag__delete {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: $drag-delete-size;
|
||||
height: $drag-delete-size;
|
||||
}
|
||||
.l-drag__handle {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: $drag-handle-size;
|
||||
height: $drag-handle-size;
|
||||
}
|
||||
/* #ifndef APP-NVUE */
|
||||
.l-drag__delete::before,.l-drag__handle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
/* #endif */
|
532
uni_modules/lime-drag/components/l-drag/l-drag.vue
Normal file
532
uni_modules/lime-drag/components/l-drag/l-drag.vue
Normal file
@ -0,0 +1,532 @@
|
||||
<template>
|
||||
<view class="l-drag l-class" :style="[areaStyles]" ref="dragRef" @touchstart="setDisabled">
|
||||
<movable-area class="l-drag__inner" v-if="isReset" :style="[innerStyles]">
|
||||
<slot></slot>
|
||||
<movable-view class="l-drag__ghost" v-if="isDrag && props.ghost" :animation="true" :style="[viewStyles]" direction="all" :x="ghostEl.x" :y="ghostEl.y" key="l-drag-clone">
|
||||
<slot name="ghost"></slot>
|
||||
</movable-view>
|
||||
<movable-view v-if="props.before" class="l-drag__before" disabled :animation="false" :style="[viewStyles]" :x="beforeEl.x" :y="beforeEl.y">
|
||||
<slot name="before"></slot>
|
||||
</movable-view>
|
||||
<movable-view
|
||||
v-for="(item, oindex) in cloneList" :key="item.id"
|
||||
direction="all"
|
||||
:data-oindex="oindex"
|
||||
:style="[viewStyles]"
|
||||
class="l-drag__view"
|
||||
:class="[{'l-is-active': oindex == active, 'l-is-hidden': !item.show}, item.class]"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:friction="friction"
|
||||
:damping="damping"
|
||||
:animation="animation"
|
||||
:disabled="isDisabled || props.disabled"
|
||||
@touchstart="touchStart"
|
||||
@change="touchMove"
|
||||
@touchend="touchEnd"
|
||||
@touchcancel="touchEnd"
|
||||
@longpress="setDisabled"
|
||||
>
|
||||
<!-- <view v-if="props.remove" class="l-drag__remove" :style="removeStyle" data-remove="true">
|
||||
<slot name="remove" :oindex="oindex" data-remove="true" />
|
||||
</view> -->
|
||||
<!-- <view v-if="props.handle" class="l-drag__handle" :style="handleStyle" data-handle="true">
|
||||
<slot name="handle" :oindex="oindex" :active="!isDisabled && !isDisabled && oindex == active" />
|
||||
</view> -->
|
||||
<slot name="grid" :oindex="oindex" :index="item.index" :oldindex="item.oldindex" :content="item.content" :active="!isDisabled && !isDisabled && oindex == active" />
|
||||
<view class="mask" v-if="!(isDisabled || props.disabled) && props.longpress"></view>
|
||||
</movable-view>
|
||||
|
||||
|
||||
<movable-view v-if="props.after" class="l-drag__after" disabled :animation="true" direction="all" :style="[viewStyles]" :x="afterEl.x" :y="afterEl.y">
|
||||
<slot name="after"></slot>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import { computed, onMounted, ref, getCurrentInstance, watch, nextTick, reactive , triggerRef, onUnmounted, defineComponent} from "./vue";
|
||||
import DragProps from './props';
|
||||
import type {GridRect, Grid, Position} from './type'
|
||||
// #ifdef APP-NVUE
|
||||
const dom = weex.requireModule('dom')
|
||||
// #endif
|
||||
|
||||
export default defineComponent({
|
||||
name: 'l-drag',
|
||||
externalClasses: ['l-class'],
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
},
|
||||
props: DragProps,
|
||||
emits: ['change'],
|
||||
setup(props, {emit, expose}) {
|
||||
const res = wx.getSystemInfoSync();
|
||||
const statusHeight = res.statusBarHeight; //状态栏高度
|
||||
const cusnavbarheight = (statusHeight + 44) + "px";
|
||||
// #ifdef APP-NVUE
|
||||
const dragRef = ref(null)
|
||||
// #endif
|
||||
const app = getCurrentInstance()
|
||||
const isDrag = ref(false)
|
||||
const isInit = ref(false)
|
||||
const isReset = ref(true)
|
||||
const colmunId = ref(-1)
|
||||
/** 选中项原始下标 */
|
||||
const active = ref(-1)
|
||||
const maxIndex = ref(-1)
|
||||
const animation = ref(true)
|
||||
const isDisabled = ref(props.handle || props.longpress ? true: false)
|
||||
|
||||
const dragEl = reactive({
|
||||
content: null,
|
||||
/** 当前视图下标*/
|
||||
index: 0,
|
||||
/** 旧视图下标 */
|
||||
oldindex: -1,
|
||||
/** 上次原始下标 */
|
||||
lastindex: -1
|
||||
})
|
||||
|
||||
const ghostEl = reactive({
|
||||
content: null,
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
const beforeEl = reactive({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
const afterEl = reactive({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
|
||||
let gridRects = [] //ref<GridRect[]>([])
|
||||
const areaWidth = ref(0)
|
||||
const cloneList = ref<Grid[]>([])
|
||||
// 删除项时可能会减少行数影响到删除过渡动画,故增加此值在删除时保持高度不变,等动画完成后再归零
|
||||
const leaveRow = ref(0)
|
||||
const extra = computed(() => (props.before ? 1 :0) + (props.after ? 1 : 0))
|
||||
const rows = computed(() => Math.ceil( ((isInit.value ? cloneList.value.length : props.list.length) + extra.value) / props.column ))
|
||||
const gridHeight = computed(() => props.aspectRatio ? girdWidth.value / props.aspectRatio : (/rpx$/.test(`${props.gridHeight}`) ? uni.upx2px(parseInt(`${props.gridHeight}`)) : parseInt(`${props.gridHeight}`)))
|
||||
const girdWidth = computed(() => areaWidth.value / props.column)
|
||||
const viewStyles = computed(() => ({width: girdWidth.value + 'px',height: gridHeight.value + 'px'}))
|
||||
const areaStyles = computed(() => ({height: (rows.value + leaveRow.value ) * gridHeight.value + 'px'}))
|
||||
const innerStyles = computed(() => ({
|
||||
// #ifdef APP-NVUE
|
||||
width: areaWidth.value + 'px',
|
||||
// #endif
|
||||
height: (rows.value + props.extraRow + leaveRow.value) * gridHeight.value + 'px'}))
|
||||
|
||||
const sleep = (cb: Function, time = 1000/60) => setTimeout(cb, time)
|
||||
const createGrid = (content: any, position?:Position|null): Grid => {
|
||||
colmunId.value++
|
||||
maxIndex.value++
|
||||
const index = maxIndex.value
|
||||
const colmun = gridRects[index]
|
||||
|
||||
let x = 0
|
||||
let y = 0
|
||||
if(colmun) {
|
||||
if(props.after) {
|
||||
let nxet = gridRects[index + 1]
|
||||
if(!nxet) {
|
||||
nxet = createGridRect(gridRects.length + (props.before ? 1 : 0))
|
||||
gridRects.push(nxet)
|
||||
}
|
||||
setReset(() => setAfter(nxet))
|
||||
} else {
|
||||
setReset()
|
||||
}
|
||||
x = colmun.x
|
||||
y = colmun.y
|
||||
} else {
|
||||
const nxet = createGridRect(gridRects.length + (props.before ? 1 : 0))
|
||||
gridRects.push(nxet)
|
||||
setReset()
|
||||
x = nxet.x
|
||||
y = nxet.y
|
||||
}
|
||||
if(position) {
|
||||
x = position.x
|
||||
y = position.y
|
||||
}
|
||||
return {id: `l-drag-item-${colmunId.value}`, index, oldindex: index, content, x, y, class: '', show: true}
|
||||
}
|
||||
const setReset = (cb?: any) => {
|
||||
// const newRow = (cloneList.value.length + extra.value) % (props.column)
|
||||
if(isInit.value) {
|
||||
cb&&sleep(cb)
|
||||
}
|
||||
}
|
||||
const setAfter = ({x, y} = {x: 0, y: 0}) => {
|
||||
if(props.after) {
|
||||
afterEl.x = x
|
||||
afterEl.y = y
|
||||
}
|
||||
}
|
||||
const setDisabled = (e: any, flag?: boolean= false) => {
|
||||
// e?.preventDefault()
|
||||
const type = `${e.type}`.toLowerCase()
|
||||
const {handle = props.touchHandle} = e.target.dataset
|
||||
if(props.handle && !handle) {
|
||||
isDisabled.value = true
|
||||
} else if(props.handle && handle && !props.longpress) {
|
||||
isDisabled.value = flag
|
||||
} else if(props.handle && handle && props.longpress && type.includes('longpress')) {
|
||||
isDisabled.value = false
|
||||
} else if(props.longpress && type.includes('longpress') && !props.handle) {
|
||||
isDisabled.value = false
|
||||
}
|
||||
if(type.includes('touchend') && props.longpress) {
|
||||
isDisabled.value = true
|
||||
}
|
||||
}
|
||||
const createGridRect = (i: number, last?: GridRect): GridRect => {
|
||||
let { row } = last || gridRects[gridRects.length - 1] || { row: 0 }
|
||||
const col = i % (props.column)
|
||||
const grid = (row: number, x: number, y: number):GridRect => {
|
||||
return {row, x, y, x1: x + girdWidth.value, y1: y + gridHeight.value}
|
||||
}
|
||||
if(col == 0 && i != 0) {row++}
|
||||
return grid(row, col * girdWidth.value, row * gridHeight.value)
|
||||
}
|
||||
const createGridRects = () => {
|
||||
let rects: GridRect[] = []
|
||||
const length = rows.value * props.column + extra.value
|
||||
gridRects = []
|
||||
for (var i = 0; i < length; i++) {
|
||||
const item = createGridRect(i, rects[rects.length - 1])
|
||||
rects.push(item)
|
||||
}
|
||||
if(props.before) {
|
||||
const {x, y} = rects.shift()
|
||||
beforeEl.x = x
|
||||
beforeEl.y = y
|
||||
}
|
||||
setAfter(rects[props.list.length])
|
||||
gridRects = rects as GridRect[]
|
||||
}
|
||||
const updateList = (v: any[]) => {
|
||||
cloneList.value = v.map((content) => createGrid(content))
|
||||
}
|
||||
|
||||
const touchStart = (e: any) => {
|
||||
if(e.target.dataset.remove) return
|
||||
// 选中项原始下标
|
||||
const {oindex} = e.currentTarget?.dataset || e.target?.dataset || {}
|
||||
if(typeof oindex !== 'number') return
|
||||
const target = cloneList.value[oindex]
|
||||
isDrag.value = true
|
||||
// 选中项原始下标
|
||||
active.value = oindex
|
||||
// 选中项的当前下标
|
||||
dragEl.index = dragEl.oldindex = target.index
|
||||
ghostEl.x = target.x||0
|
||||
ghostEl.y = target.y||0
|
||||
dragEl.content = ghostEl.content = target.content
|
||||
}
|
||||
|
||||
const touchEnd = (e: any) => {
|
||||
setTimeout(() => {
|
||||
if(e.target.dataset.remove || active.value==-1) return
|
||||
setDisabled(e, true)
|
||||
isDrag.value = false
|
||||
const isEmit = dragEl.index !== dragEl.oldindex && dragEl.oldindex > -1 // active.value !== dragEl.index
|
||||
dragEl.lastindex = active.value
|
||||
dragEl.oldindex = active.value = -1
|
||||
const last = cloneList.value[dragEl.lastindex]
|
||||
const position = gridRects[dragEl.index]
|
||||
nextTick(() => {
|
||||
last.x = position.x + 0.001
|
||||
last.y = position.y + 0.001
|
||||
sleep(() => {
|
||||
last.x = position.x
|
||||
last.y = position.y
|
||||
isEmit && emitting()
|
||||
})
|
||||
})
|
||||
},80)
|
||||
|
||||
}
|
||||
const emitting = () => {
|
||||
const clone = [...cloneList.value].sort((a, b) => a.index - b.index)//.map(item => ref(item.content))
|
||||
emit('change', clone)
|
||||
}
|
||||
|
||||
const touchMove = (e: any) => {
|
||||
if(!isDrag.value) return
|
||||
// #ifndef APP-NVUE
|
||||
let {oindex} = e.currentTarget.dataset
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
oindex = e.currentTarget.dataset['-oindex']
|
||||
// #endif
|
||||
if(oindex != active.value) return
|
||||
const {x, y} = e.detail
|
||||
const centerX = x + girdWidth.value / 2
|
||||
const centerY = y + gridHeight.value / 2
|
||||
for (let i = 0; i < cloneList.value.length; i++) {
|
||||
const item = gridRects[i]
|
||||
if(centerX > item.x && centerX < item.x1 && centerY > item.y && centerY < item.y1) {
|
||||
ghostEl.x = item.x
|
||||
ghostEl.y = item.y
|
||||
if(dragEl.index != i) {
|
||||
_move(active.value, i)
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const getDragEl = (oindex: number) => {
|
||||
if(isDrag.value) {return dragEl}
|
||||
return cloneList.value[oindex]
|
||||
}
|
||||
|
||||
/**
|
||||
* 把原始数据中排序为index的项 移动到 toIndex
|
||||
* @param oindex 原始数据的下标
|
||||
* @param toIndex 视图中的下标
|
||||
* @param position 指定坐标
|
||||
*/
|
||||
const _move = (oindex: number, toIndex: number, position?: Position|null, emit: boolean = true) => {
|
||||
const length = cloneList.value.length - 1
|
||||
if(toIndex > length || toIndex < 0) return
|
||||
// 获取oIdnex在视图中的项目
|
||||
const dragEl = getDragEl(oindex)
|
||||
let speed = 0
|
||||
let start = dragEl.index
|
||||
// 比较开始index和终点index,设置方向
|
||||
if(start < toIndex) {speed = 1}
|
||||
if(start > toIndex) {speed = -1}
|
||||
if(!speed) return
|
||||
// 距离
|
||||
let distance = start - toIndex
|
||||
// 找到区间所有的项
|
||||
while(distance) {
|
||||
distance += speed
|
||||
// 目标
|
||||
const target = isDrag.value ? (dragEl.index += speed) : (start += speed)
|
||||
let targetOindex = cloneList.value.findIndex(item => item.index == target && item.content != dragEl.content)
|
||||
if (targetOindex == oindex) return
|
||||
if (targetOindex < 0) {targetOindex = cloneList.value.length - 1}
|
||||
let targetEl = cloneList.value[targetOindex]
|
||||
if(!targetEl) return;
|
||||
// 上一个index
|
||||
const lastIndex = target - speed
|
||||
const activeEl = cloneList.value[oindex]
|
||||
const rect = gridRects[lastIndex]
|
||||
targetEl.x = rect.x
|
||||
targetEl.y = rect.y
|
||||
targetEl.oldindex = targetEl.index
|
||||
targetEl.index = lastIndex
|
||||
activeEl.oldindex = activeEl.index //oIndex
|
||||
activeEl.index = toIndex
|
||||
// 到达终点,如果是拖拽则不处理
|
||||
if(!distance && !isDrag.value) {
|
||||
const rect = gridRects[toIndex]
|
||||
const {x, y} = position||rect
|
||||
activeEl.x = dragEl.x = x
|
||||
activeEl.y = dragEl.y = y
|
||||
// triggerRef(cloneList)
|
||||
if(emit) {
|
||||
emitting()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 为区分是主动调用还是内部方法
|
||||
*/
|
||||
const move = (oindex: number, toIndex: number) => {
|
||||
active.value = -1
|
||||
isDrag.value = false
|
||||
_move(oindex, toIndex)
|
||||
}
|
||||
// 临时处理 待有空再完善
|
||||
const REMOVE_TIME = 400
|
||||
let removeTimer = null
|
||||
const remove = (oindex: number) => {
|
||||
active.value = -1
|
||||
isDrag.value = false
|
||||
|
||||
clearTimeout(removeTimer)
|
||||
const item = cloneList.value[oindex]
|
||||
if(props.disabled || !item) return
|
||||
item.show = false
|
||||
const after = cloneList.value.length - 1
|
||||
_move(oindex, after, item, false)
|
||||
setAfter(gridRects[after])
|
||||
maxIndex.value--
|
||||
const _remove = (_index = oindex) => {
|
||||
// 小程序 删除会闪一下 所以先关闭动画再开启
|
||||
// animation.value = false
|
||||
const row = Math.ceil((cloneList.value.length - 1 + extra.value) / props.column)
|
||||
if( row < rows.value) {
|
||||
leaveRow.value = (rows.value - row)
|
||||
}
|
||||
cloneList.value.splice(_index, 1)[0]
|
||||
emitting()
|
||||
removeTimer = setTimeout(() => {
|
||||
leaveRow.value = 0
|
||||
}, REMOVE_TIME)
|
||||
}
|
||||
_remove()
|
||||
}
|
||||
const push = (...args: any) => {
|
||||
if(props.disabled) return
|
||||
if(Array.isArray(args)) {
|
||||
Promise.all(args.map(async item => await add(item, true))).then(emitting)
|
||||
}
|
||||
}
|
||||
const add = (content: any, after: boolean) => {
|
||||
return new Promise((resolve) => {
|
||||
const item = createGrid(content, after ? null : {x: -100, y:0})
|
||||
item.class = 'l-drag-enter'
|
||||
cloneList.value.push(item)
|
||||
const length = cloneList.value.length - 1
|
||||
nextTick(() => {
|
||||
sleep(() => {
|
||||
item.class = 'l-drag-leave'
|
||||
_move(length, (after ? length : 0), null, false)
|
||||
triggerRef(cloneList)
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
const unshift = (...args: any) => {
|
||||
if(props.disabled) return
|
||||
if(Array.isArray(args)) {
|
||||
Promise.all(args.map(async (item) => await add(item))).then(emitting)
|
||||
}
|
||||
}
|
||||
|
||||
// 暂时先简单处理,待有空再完善
|
||||
const shift = () => {
|
||||
if(!cloneList.value.length) return
|
||||
remove(cloneList.value.findIndex(item => item.index == 0) || 0)
|
||||
}
|
||||
const pop = () => {
|
||||
const length = cloneList.value.length-1
|
||||
if(length < 0 ) return
|
||||
remove(cloneList.value.findIndex(item => item.index == length) || length)
|
||||
}
|
||||
// const splice = (start, count, ...context) => {
|
||||
// // 暂未实现
|
||||
// }
|
||||
const clear = () => {
|
||||
isInit.value = isDrag.value = false
|
||||
maxIndex.value = colmunId.value = active.value = -1
|
||||
cloneList.value = []
|
||||
gridRects = []
|
||||
}
|
||||
const init = () => {
|
||||
clear()
|
||||
createGridRects()
|
||||
nextTick(() => {
|
||||
updateList(props.list)
|
||||
isInit.value = true
|
||||
})
|
||||
}
|
||||
let count = 0
|
||||
const getRect = () => {
|
||||
count++
|
||||
// #ifndef APP-NVUE
|
||||
uni.createSelectorQuery().in(app.proxy).select('.l-drag').boundingClientRect((res: UniNamespace.NodeInfo) => {
|
||||
if(res) {
|
||||
areaWidth.value = res.width || 0
|
||||
// 小程序居然无法响应式?
|
||||
init()
|
||||
}
|
||||
}).exec()
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
sleep(() => {
|
||||
nextTick(() => {
|
||||
dom.getComponentRect(dragRef.value, (res) => {
|
||||
if(!res.size.width && count < 5) {
|
||||
getRect()
|
||||
} else {
|
||||
areaWidth.value = res.size.width || 0
|
||||
init()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
onMounted(getRect)
|
||||
onUnmounted(clear)
|
||||
watch(() => props.list, init)
|
||||
|
||||
// #ifdef VUE3
|
||||
expose({
|
||||
remove,
|
||||
// add,
|
||||
move,
|
||||
push,
|
||||
unshift,
|
||||
shift,
|
||||
pop
|
||||
})
|
||||
// #endif
|
||||
return {
|
||||
// #ifdef APP-NVUE
|
||||
dragRef,
|
||||
// #endif
|
||||
cloneList,
|
||||
|
||||
areaStyles,
|
||||
innerStyles,
|
||||
viewStyles,
|
||||
|
||||
setDisabled,
|
||||
isDisabled,
|
||||
isReset,
|
||||
isDrag,
|
||||
|
||||
active,
|
||||
animation,
|
||||
|
||||
afterEl,
|
||||
ghostEl,
|
||||
beforeEl,
|
||||
|
||||
touchStart,
|
||||
touchMove,
|
||||
touchEnd,
|
||||
|
||||
remove,
|
||||
// add,
|
||||
move,
|
||||
push,
|
||||
unshift,
|
||||
// shift,
|
||||
// pop,
|
||||
props
|
||||
// isDelete: props.delete,
|
||||
// ...toRefs(props)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.l-drag{
|
||||
margin-top: v-bind(cusnavbarheight);
|
||||
|
||||
}
|
||||
@import './index';
|
||||
|
||||
</style>
|
47
uni_modules/lime-drag/components/l-drag/props.ts
Normal file
47
uni_modules/lime-drag/components/l-drag/props.ts
Normal file
@ -0,0 +1,47 @@
|
||||
// @ts-nocheck
|
||||
export default {
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
/**宽高比 填写这项, gridHeight 失效*/
|
||||
aspectRatio: Number,
|
||||
gridHeight: {
|
||||
type: [Number, String],
|
||||
default: '120rpx'
|
||||
},
|
||||
// removeStyle: String,
|
||||
// handleStyle: String,
|
||||
damping: {
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
friction: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
/**
|
||||
* 由于 movable-area 无法动态设置高度,故增加额外的行数。用于增加动态项时,高度不够无法正确显示
|
||||
*/
|
||||
extraRow: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 由于 movable-area 无法动态设置高度,但vif 重染可以,另一种实现动态高度的方式, 这BUG uni官方好像修复了。
|
||||
*/
|
||||
// reset: Boolean,
|
||||
// sort: Boolean,
|
||||
// remove: Boolean,
|
||||
ghost: Boolean,
|
||||
handle: Boolean,
|
||||
touchHandle: Boolean,
|
||||
before: Boolean,
|
||||
after: Boolean,
|
||||
disabled: Boolean,
|
||||
longpress: Boolean,
|
||||
}
|
21
uni_modules/lime-drag/components/l-drag/type.ts
Normal file
21
uni_modules/lime-drag/components/l-drag/type.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export interface Position {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
export interface GridRect extends Position{
|
||||
row : number
|
||||
// x : number
|
||||
// y : number
|
||||
x1 : number
|
||||
y1 : number
|
||||
}
|
||||
export interface Grid extends Position{
|
||||
id : string
|
||||
index : number
|
||||
oldindex : number
|
||||
content : any
|
||||
// x : number
|
||||
// y : number
|
||||
class : string
|
||||
show: boolean
|
||||
}
|
9
uni_modules/lime-drag/components/l-drag/vue.ts
Normal file
9
uni_modules/lime-drag/components/l-drag/vue.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// export * from '@/uni_modules/lime-vue'
|
||||
|
||||
// #ifdef VUE3
|
||||
export * from 'vue';
|
||||
// #endif
|
||||
// #ifndef VUE3
|
||||
export * from '@vue/composition-api';
|
||||
// #endif
|
268
uni_modules/lime-drag/components/lime-drag/lime-drag.vue
Normal file
268
uni_modules/lime-drag/components/lime-drag/lime-drag.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<demo-block type="ultra" title="拖拽">
|
||||
<demo-block title="基础">
|
||||
<l-drag :list="list">
|
||||
<template #grid="{active, index}">
|
||||
<view class="inner" :class="{active}">
|
||||
<text class="text" :class="{'text-active': active}">{{index}}</text>
|
||||
</view>
|
||||
</template>
|
||||
</l-drag>
|
||||
</demo-block>
|
||||
<demo-block title="多列 长按">
|
||||
<!-- 列后 删除 幽灵 长按 -->
|
||||
<l-drag ref="dragRef2" :list="list" @change="change2" :aspectRatio="1" :column="4" after ghost longpress>
|
||||
<template #grid="{oindex, content, active}">
|
||||
<view class="grid">
|
||||
<view class="remove" @click="onRemove2(oindex)"></view>
|
||||
<view class="inner" :class="{active}">
|
||||
<text class="text" :class="{'text-active': active}">{{content}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #ghost>
|
||||
<view class="grid">
|
||||
<!-- 幽灵样式 -->
|
||||
<view class="inner ghost"></view>
|
||||
</view>
|
||||
</template>
|
||||
<!-- 示例未设置 before -->
|
||||
<template #before>
|
||||
<view class="grid">
|
||||
<view class="inner extra" @click="onAdd2">
|
||||
增加
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template #after>
|
||||
<view class="grid">
|
||||
<view class="inner extra" @click="onAdd2">
|
||||
增加
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</l-drag>
|
||||
<button @click="refresh2">更新列表</button>
|
||||
<button @click="onUnshift2">向前增加</button>
|
||||
<button @click="onPush2">向后增加</button>
|
||||
<button @click="onShift2">shift</button>
|
||||
<button @click="onPop2">pop</button>
|
||||
</demo-block>
|
||||
<demo-block title="单列 按手柄">
|
||||
<!-- 幽灵 手柄 -->
|
||||
<view class="inputs">
|
||||
<text>把原始下标为</text>
|
||||
<input type="text" v-model="move3.index">
|
||||
<text>的项移动到</text>
|
||||
<input type="text" v-model="move3.nindex">
|
||||
</view>
|
||||
<view style="height: 600rpx; overflow: auto;">
|
||||
<l-drag ref="dragRef3" :list="list3" @change="change3" grid-height="200rpx" :column="1" :touchHandle="touchHandle3" ghost handle>
|
||||
<template #grid="{active, content, index, oindex}">
|
||||
<view class="grid">
|
||||
<view class="mover" @touchstart="touchHandle3 = true" @touchend="touchHandle3 = false" style="left: 100rpx; top: 50%; transform: translateY(-50%); position: absolute; z-index: 1;"></view>
|
||||
<view class="inner" :class="{active}">
|
||||
<text class="text" :class="{'text-active': active}">{{content}}</text>
|
||||
<view @click.stop.prevent="to(oindex, index - 1)">上移</view>
|
||||
<view @click.stop.prevent="to(oindex, index + 1)">下移</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template #ghost>
|
||||
<view class="grid">
|
||||
<!-- 幽灵样式 -->
|
||||
<view class="inner ghost"></view>
|
||||
</view>
|
||||
</template>
|
||||
</l-drag>
|
||||
</view>
|
||||
</demo-block>
|
||||
</demo-block>
|
||||
</template>
|
||||
<script>
|
||||
import {ref, watch, defineComponent, reactive} from '../l-drag/vue'
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
// 示例1和2共用list
|
||||
const list = ref(new Array(7).fill(0).map((v,i) => i));
|
||||
// 示例2 多列 长按
|
||||
const dragRef2 = ref(null)
|
||||
const newList2 = ref([])
|
||||
const change2 = v => {
|
||||
console.log('示例2数据发生变化::', v)
|
||||
newList2.value = [...v]
|
||||
}
|
||||
const onRemove2 = (index) => {
|
||||
if(dragRef2.value && index >= 0) {
|
||||
dragRef2.value.remove(index)
|
||||
}
|
||||
}
|
||||
const onAdd2 = () => {
|
||||
dragRef2.value.push(Math.round(Math.random() * 1000))
|
||||
}
|
||||
const onShift2 = () => {
|
||||
dragRef2.value.shift()
|
||||
}
|
||||
const onPop2 = () => {
|
||||
dragRef2.value.pop()
|
||||
}
|
||||
const onUnshift2 = () => {
|
||||
dragRef2.value.unshift(Math.round(Math.random() * 1000), Math.round(Math.random() * 1000))
|
||||
}
|
||||
const onPush2 = () => {
|
||||
dragRef2.value.push(Math.round(Math.random() * 1000), Math.round(Math.random() * 1000))
|
||||
}
|
||||
const refresh2 = () => {
|
||||
list.value = new Array(10).fill(0).map((v,i) => i);
|
||||
}
|
||||
|
||||
|
||||
// 示例3
|
||||
const list3 = new Array(5).fill(0).map((v,i) => i);
|
||||
const dragRef3 = ref(null)
|
||||
const newList3 = ref([])
|
||||
const touchHandle3 = ref(false)
|
||||
|
||||
const change3 = v => {
|
||||
newList3.value = v
|
||||
}
|
||||
const move3 = reactive({
|
||||
index: 0,
|
||||
nindex: 0
|
||||
})
|
||||
watch(() => move3.nindex, (v, o) => {
|
||||
dragRef3.value.move(move3.index*1, move3.nindex*1)
|
||||
})
|
||||
|
||||
const to = (oindex, index) => {
|
||||
dragRef3.value.move(oindex, index)
|
||||
}
|
||||
|
||||
return {
|
||||
list,
|
||||
// 示例2
|
||||
dragRef2,
|
||||
change2,
|
||||
onRemove2,
|
||||
onAdd2,
|
||||
refresh2,
|
||||
onUnshift2,
|
||||
onPush2,
|
||||
onShift2,
|
||||
onPop2,
|
||||
// 示例3
|
||||
list3,
|
||||
dragRef3,
|
||||
change3,
|
||||
move3,
|
||||
touchHandle3,
|
||||
to
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
.grid {
|
||||
height: 100%;
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
padding: 16rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
.remove {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: red;
|
||||
border-radius: 50%;
|
||||
font-size: 16rpx;
|
||||
color: white;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
.inner {
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
|
||||
border: 1rpx solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
font-size: 50rpx;
|
||||
font-weight: bold;
|
||||
color: blue;
|
||||
transition: all 300ms ease;
|
||||
position: relative;
|
||||
}
|
||||
.extra {
|
||||
color: #ddd
|
||||
}
|
||||
.mover {
|
||||
position: relative;
|
||||
width: 50rpx;
|
||||
margin-top: 10rpx;
|
||||
height: 30rpx;
|
||||
border: 2px solid #ddd;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
/* background-color: #ddd; */
|
||||
}
|
||||
.mover::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 50rpx;
|
||||
top: 15rpx;
|
||||
border-top: 2px solid #ddd;
|
||||
}
|
||||
.active {
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
background-color: blue;
|
||||
color: white;
|
||||
border: 1rpx solid blue;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
/* #ifdef APP-NVUE */
|
||||
.text {
|
||||
font-size: 50rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-active {
|
||||
font-size: 50rpx;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
/* #endif */
|
||||
.ghost {
|
||||
background-color: rgba(0, 0, 255, 0.1);
|
||||
}
|
||||
.inputs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
input {
|
||||
background-color: #fff;
|
||||
width: 80rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
padding: 5rpx 10rpx;
|
||||
margin: 0 8rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
87
uni_modules/lime-drag/package.json
Normal file
87
uni_modules/lime-drag/package.json
Normal file
@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "lime-drag",
|
||||
"displayName": "拖拽排序-拖动排序-LimeUI",
|
||||
"version": "0.1.3",
|
||||
"description": "uniapp vue3 拖拽排序插件,用于图片或列表的拖动排序,可设置列数、增加删除等功能, vue2只要配置@vue/composition-api",
|
||||
"keywords": [
|
||||
"拖拽",
|
||||
"拖拽排序",
|
||||
"排序",
|
||||
"拖动",
|
||||
"拖动排序"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.7.12"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"lime-shared"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "n",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
170
uni_modules/lime-drag/readme.md
Normal file
170
uni_modules/lime-drag/readme.md
Normal file
@ -0,0 +1,170 @@
|
||||
# lime-drag 拖拽排序
|
||||
- 当前为初版 可能会有BUG
|
||||
- 基于uniapp vue3
|
||||
- Q群 1169785031
|
||||
|
||||
|
||||
### 安装
|
||||
- 在市场导入插件即可在任意页面使用,无须再`import`
|
||||
|
||||
|
||||
### 使用
|
||||
- 提供简单的使用示例,更多请查看下方的demo
|
||||
|
||||
```html
|
||||
<l-drag :list="list" @change="change">
|
||||
<!-- // 每一项的插槽 grid 的 content 您传入的数据 -->
|
||||
<template #grid="{active, content}">
|
||||
<!-- // grid.active 是否为当前拖拽项目 根据自己需要写样式 -->
|
||||
<view class="inner" :class="{active: active}">
|
||||
<text class="text" :class="{'text-active': active}">{{grid.content}}</text>
|
||||
</view>
|
||||
</template>
|
||||
</l-drag>
|
||||
```
|
||||
|
||||
```js
|
||||
const list = new Array(7).fill(0).map((v,i) => i);
|
||||
// 拖拽后新的数据
|
||||
const newList = ref([])
|
||||
const change = v => newList.value = v
|
||||
```
|
||||
#### 增删
|
||||
- 不要给list赋值,这样只会重新初始化
|
||||
- 增加数据 调用暴露的`push`
|
||||
- 删除某条数据调用暴露的`remove`方法,需要传入`oindex`
|
||||
|
||||
```html
|
||||
<l-drag :list="list" @change="change" ref="dragRef" after remove>
|
||||
<!-- 每一项插槽 grid 的 content 是您传入的数据 -->
|
||||
<template #grid="{active, index, oldindex, oindex}">
|
||||
<!-- active 是否为当前拖拽项目 根据自己需要写样式 -->
|
||||
<!-- index 排序后列表下标 -->
|
||||
<!-- oldindex 排序后列表旧下标 -->
|
||||
<!-- oindex 列表原始下标,输入的数据排位不会因为排序而改变 -->
|
||||
<view class="remove" @click="onRemove(oindex)"></view>
|
||||
<view class="inner" :class="{active}">
|
||||
<text class="text" :class="{'text-active': active}">{{content}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template #after>
|
||||
<view class="grid">
|
||||
<view class="inner extra" @click="onAdd">
|
||||
增加
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</l-drag>
|
||||
```
|
||||
```js
|
||||
const dragRef = ref(null)
|
||||
const list = new Array(7).fill(0).map((v,i) => i);
|
||||
const onAdd = () => {
|
||||
dragRef.value.push(Math.round(Math.random() * 1000))
|
||||
}
|
||||
const onRemove = (oindex) => {
|
||||
if(dragRef.value && oindex >= 0) {
|
||||
// 记得oindex为数组的原始index
|
||||
dragRef.value.remove(oindex)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### 插槽
|
||||
```html
|
||||
<l-drag :list="list">
|
||||
<!-- 每一项的插槽 -->
|
||||
<template #grid="{active, index, oldindex, oindex, content}"></template>
|
||||
<!-- 当前拖拽项幽灵插槽 设置`ghost`后使用 主要为实现拖拽时 有个影子跟着 -->
|
||||
<template #ghots></template>
|
||||
|
||||
<!-- 前后方插槽为固定在列表前方和后方,不能拖动 -->
|
||||
<!-- 列表前方的插槽 设置`before`后使用 -->
|
||||
<template #before></template>
|
||||
<!-- 列表后方的插槽 设置`after`后使用 -->
|
||||
<template #after></template>
|
||||
</l-drag>
|
||||
```
|
||||
|
||||
|
||||
### 查看示例
|
||||
- 导入后直接使用这个标签查看演示效果
|
||||
|
||||
```html
|
||||
<!-- // 代码位于 uni_modules/lime-drag/compoents/lime-drag -->
|
||||
<lime-drag />
|
||||
```
|
||||
|
||||
|
||||
### 插件标签
|
||||
- 默认 l-drag 为 component
|
||||
- 默认 lime-drag 为 demo
|
||||
|
||||
### 关于vue2的使用方式
|
||||
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
|
||||
- 关键代码是: 在main.js中 在vue2部分加上这一段即可,官方是把它单独成了一个文件.
|
||||
```js
|
||||
// vue2
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
|
||||
- 另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录
|
||||
```cmd
|
||||
// \HBuilderX\plugins\compile-typescript
|
||||
yarn add typescript -D
|
||||
- or -
|
||||
npm install typescript -D
|
||||
```
|
||||
|
||||
- 小程序需要在`manifest.json`启用`slotMultipleInstance`
|
||||
```json
|
||||
"mp-weixin" : {
|
||||
"slotMultipleInstance" : true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
|
||||
| list | 列表数组,不可变化,变化后会重新初始化 | <em>array</em> | `[]` |
|
||||
| column | 列数 | <em>number</em> | `2` |
|
||||
| gridHeight | 行高,宫格高度 | <em>string</em> | `120rpx` |
|
||||
| damping | 阻尼系数,用于控制x或y改变时的动画和过界回弹的动画,值越大移动越快 | <em>string</em> | `-` |
|
||||
| friction | 摩擦系数,用于控制惯性滑动的动画,值越大摩擦力越大,滑动越快停止;必须大于0,否则会被设置成默认值 | <em>number</em> | `2` |
|
||||
| extraRow | 额外行数 | <em>number</em> | `0` |
|
||||
| ghost | 开启幽灵插槽 | <em>boolean</em> | `false` |
|
||||
| before | 开启列前插槽 | <em>boolean</em> | `false` |
|
||||
| after | 开启列后插槽 | <em>boolean</em> | `false` |
|
||||
| disabled | 是否禁用 | <em>boolean</em> | `false` |
|
||||
| longpress | 是否长按 | <em>boolean</em> | `false` |
|
||||
|
||||
### Events
|
||||
| 参数 | 说明 | 参数 |
|
||||
| --------------------------| ------------------------------------------------------------ | ---------------- |
|
||||
| change | 返回新数据 | list |
|
||||
|
||||
### Expose
|
||||
| 参数 | 说明 | 参数 |
|
||||
| --------------------------| ------------------------------------------------------------ | ---------------- |
|
||||
| remove | 删除, 传入`oindex`,即数据列表原始的index | |
|
||||
| push | 向后增加,可以是数组或单数据 | |
|
||||
| unshift | 向前增加,可以是数组或单数据 | |
|
||||
| move | 移动, 传入(`oindex`, `toindex`),将数据列表原始的index项移到视图中的目标位置 | |
|
||||
|
||||
|
||||
### TODO
|
||||
将来实现的功能
|
||||
- splice
|
||||
|
||||
## 打赏
|
||||
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||

|
||||

|
205
uni_modules/lime-echart/changelog.md
Normal file
205
uni_modules/lime-echart/changelog.md
Normal file
@ -0,0 +1,205 @@
|
||||
## 0.9.8(2024-12-20)
|
||||
- fix: 修复 APP 无法放大问题
|
||||
## 0.9.7(2024-12-02)
|
||||
- feat: uniapp 增加`landscape`,当`landscape`为`true`时旋转90deg达到横屏效果。
|
||||
- feat: 支持uniapp x 微信小程序
|
||||
## 0.9.6(2024-07-23)
|
||||
- fix: 修复 uni is not defined
|
||||
## 0.9.5(2024-07-19)
|
||||
- chore: 鸿蒙`measureText`为异步,异步字体不正常,使用模拟方式。
|
||||
## 0.9.4(2024-07-18)
|
||||
- chore: 更新文档
|
||||
## 0.9.3(2024-07-16)
|
||||
- feat: 鸿蒙 canvas 事件缺失,待官方修复,如何在鸿蒙使用请看文档`常见问题 vue3`
|
||||
## 0.9.2(2024-07-12)
|
||||
- chore: 删除多余文件
|
||||
## 0.9.1(2024-07-12)
|
||||
- fix: 修复 安卓5不显示图表问题
|
||||
## 0.9.0(2024-06-13)
|
||||
- chore: 合并nvue和uvue
|
||||
## 0.8.9(2024-05-19)
|
||||
- chore: 更新文档
|
||||
## 0.8.8(2024-05-13)
|
||||
- chore: 更新文档和uvue示例
|
||||
## 0.8.7(2024-04-26)
|
||||
- fix: uniapp x需要HBX 4.13以上
|
||||
## 0.8.6(2024-04-10)
|
||||
- feat: 支持 uniapp x ios
|
||||
## 0.8.5(2024-04-03)
|
||||
- fix: 修复 nvue `reset`传值不生效问题
|
||||
- feat: 支持 uniapp x web
|
||||
## 0.8.4(2024-01-27)
|
||||
- chore: 更新文档
|
||||
## 0.8.3(2024-01-21)
|
||||
- chore: 更新文档
|
||||
## 0.8.2(2024-01-21)
|
||||
- feat: 支持 `uvue`
|
||||
## 0.8.1(2023-08-24)
|
||||
- fix: app 的`touch`事件为`object` 导致无法显示 `tooltip`
|
||||
## 0.8.0(2023-08-22)
|
||||
- fix: 离屏 报错问题
|
||||
- fix: 微信小程序PC无法使用事件
|
||||
- chore: 更新文档
|
||||
## 0.7.9(2023-07-29)
|
||||
- chore: 更新文档
|
||||
## 0.7.8(2023-07-29)
|
||||
- fix: 离屏 报错问题
|
||||
## 0.7.7(2023-07-27)
|
||||
- chore: 更新文档
|
||||
- chore: lime-echart 里的示例使用自定tooltips
|
||||
- feat: 对支持离屏的使用离屏创建(微信、字节、支付宝)
|
||||
## 0.7.6(2023-06-30)
|
||||
- fix: vue3 报`width`的错
|
||||
## 0.7.5(2023-05-25)
|
||||
- chore: 更新文档 和 demo, 使用`lime-echart`这个标签即可查看示例
|
||||
## 0.7.4(2023-05-22)
|
||||
- chore: 增加关于钉钉小程序上传时提示安全问题的说明及修改建议
|
||||
## 0.7.3(2023-05-16)
|
||||
- chore: 更新 vue3 非微信小程序平台可能缺少`wx`的说明
|
||||
## 0.7.2(2023-05-16)
|
||||
- chore: 更新 vue3 非微信小程序平台的可以缺少`wx`的说明
|
||||
## 0.7.1(2023-04-26)
|
||||
- chore: 更新demo,使用`lime-echart`这个标签即可查看示例
|
||||
- chore:微信小程序的`tooltip`文字有阴影,怀疑是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
|
||||
## 0.7.0(2023-04-24)
|
||||
- fix: 修复`setAttribute is not a function`
|
||||
## 0.6.9(2023-04-15)
|
||||
- chore: 更新文档,vue3请使用echarts esm的包
|
||||
## 0.6.8(2023-03-22)
|
||||
- feat: mac pc无法使用canvas 2d
|
||||
## 0.6.7(2023-03-17)
|
||||
- feat: 更新文档
|
||||
## 0.6.6(2023-03-17)
|
||||
- feat: 微信小程序PC已经支持canvas 2d,故去掉判断PC
|
||||
## 0.6.5(2022-11-03)
|
||||
- fix: 某些手机touches为对象,导致无法交互。
|
||||
## 0.6.4(2022-10-28)
|
||||
- fix: 优化点击事件的触发条件
|
||||
## 0.6.3(2022-10-26)
|
||||
- fix: 修复 dataZoom 拖动问题
|
||||
## 0.6.2(2022-10-23)
|
||||
- fix: 修复 飞书小程序 尺寸问题
|
||||
## 0.6.1(2022-10-19)
|
||||
- fix: 修复 PC mousewheel 事件 鼠标位置不准确的BUG,不兼容火狐!
|
||||
- feat: showLoading 增加传参
|
||||
## 0.6.0(2022-09-16)
|
||||
- feat: 增加PC的mousewheel事件
|
||||
## 0.5.4(2022-09-16)
|
||||
- fix: 修复 nvue 动态数据不显示问题
|
||||
## 0.5.3(2022-09-16)
|
||||
- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
|
||||
- chore: 更新文档
|
||||
## 0.5.2(2022-09-16)
|
||||
- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
|
||||
## 0.5.1(2022-09-16)
|
||||
- fix: 修复nvue报错
|
||||
## 0.5.0(2022-09-15)
|
||||
- feat: init(echarts, theme?:string, opts?:{}, callback: function(chart))
|
||||
## 0.4.8(2022-09-11)
|
||||
- feat: 增加 @finished
|
||||
## 0.4.7(2022-08-24)
|
||||
- chore: 去掉 stylus
|
||||
## 0.4.6(2022-08-24)
|
||||
- feat: 增加 beforeDelay
|
||||
## 0.4.5(2022-08-12)
|
||||
- chore: 更新文档
|
||||
## 0.4.4(2022-08-12)
|
||||
- fix: 修复 resize 无参数时报错
|
||||
## 0.4.3(2022-08-07)
|
||||
# 评论有说本插件对新手不友好,让我做不好就不要发出来。 还有的说跟官网一样,发出来做什么,给我整无语了。
|
||||
# 所以在此提醒一下准备要下载的你,如果你从未使用过 echarts 请不要下载 或 谨慎下载。
|
||||
# 如果你确认要下载,麻烦看完文档。还有请注意插件是让echarts在uniapp能运行,API 配置请自行去官网查阅!
|
||||
# 如果你不会echarts 但又需要图表,市场上有个很优秀的图表插件 uchart 你可以去使用这款插件,uchart的作者人很好,也热情。
|
||||
# 每个人都有自己的本职工作,如果你能力强可以自行兼容,如果使用了他人的插件也麻烦尊重他人的成果和劳动时间。谢谢。
|
||||
# 为了心情愉悦,本人已经使用插件屏蔽差评。
|
||||
- chore: 更新文档
|
||||
## 0.4.2(2022-07-20)
|
||||
- feat: 增加 resize
|
||||
## 0.4.1(2022-06-07)
|
||||
- fix: 修复 canvasToTempFilePath 不生效问题
|
||||
## 0.4.0(2022-06-04)
|
||||
- chore 为了词云 增加一个canvas 标签
|
||||
- 词云下载地址[echart-wordcloud](https://ext.dcloud.net.cn/plugin?id=8430)
|
||||
## 0.3.9(2022-06-02)
|
||||
- chore: 更新文档
|
||||
- tips: lines 不支持 `trailLength`
|
||||
## 0.3.8(2022-05-31)
|
||||
- fix: 修复 因mouse事件冲突tooltip跳动问题
|
||||
## 0.3.7(2022-05-26)
|
||||
- chore: 更新文档
|
||||
- chore: 设置默认宽高300px
|
||||
- fix: 修复 vue3 微信小程序 拖影BUG
|
||||
- chore: 支持PC
|
||||
## 0.3.5(2022-04-28)
|
||||
- chore: 更新使用方式
|
||||
- 🔔 必须使用hbuilderx 3.4.8-alpha以上
|
||||
## 0.3.4(2021-08-03)
|
||||
- chore: 增加 setOption的参数值
|
||||
## 0.3.3(2021-07-22)
|
||||
- fix: 修复 径向渐变报错的问题
|
||||
## 0.3.2(2021-07-09)
|
||||
- chore: 统一命名规范,无须主动引入组件
|
||||
## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
|
||||
## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.3.1(2021-06-21)
|
||||
- fix: 修复 app-nvue ios is-enable 无效的问题
|
||||
## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
|
||||
## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.3.0(2021-06-14)
|
||||
- fix: 修复 头条系小程序 2d 报 JSON.stringify 的问题
|
||||
- 目前 头条系小程序 2d 无法在开发工具上预览,划动图表页面无法滚动,axisLabel 字体颜色无法更改,建议使用非2d。
|
||||
## 0.2.9(2021-06-06)
|
||||
- fix: 修复 头条系小程序 2d 放大的BUG
|
||||
- 头条系小程序 2d 无法在开发工具上预览,也存在划动图表页面无法滚动的问题。
|
||||
## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.2.8(2021-05-19)
|
||||
- fix: 修复 微信小程序 PC 显示过大的问题
|
||||
## 0.2.7(2021-05-19)
|
||||
- fix: 修复 微信小程序 PC 不显示问题
|
||||
## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.2.6(2021-05-14)
|
||||
- feat: 支持 `image`
|
||||
- feat: props 增加 `ec.clear`,更新时是否先删除图表样式
|
||||
- feat: props 增加 `isDisableScroll` ,触摸图表时是否禁止页面滚动
|
||||
- feat: props 增加 `webviewStyles` ,webview 的样式, 仅nvue有效
|
||||
## 0.2.5(2021-05-13)
|
||||
- docs: 插件用到了css 预编译器 [stylus](https://ext.dcloud.net.cn/plugin?name=compile-stylus) 请安装它
|
||||
## 0.2.4(2021-05-12)
|
||||
- fix: 修复 百度平台 多个图表ctx 和 渐变色 bug
|
||||
- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.2.3(2021-05-10)
|
||||
- feat: 增加 `canvasToTempFilePath` 方法,用于生成图片
|
||||
```js
|
||||
this.$refs.chart.canvasToTempFilePath({success: (res) => {
|
||||
console.log('tempFilePath:', res.tempFilePath)
|
||||
}})
|
||||
```
|
||||
## 0.2.2(2021-05-10)
|
||||
- feat: 增加 `dispose` 方法,用于销毁实例
|
||||
- feat: 增加 `isClickable` 是否派发点击
|
||||
- feat: 实验性的支持 `nvue` 使用要慎重考虑
|
||||
- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.2.1(2021-05-06)
|
||||
- fix:修复 微信小程序 json 报错
|
||||
- chore: `reset` 更改为 `setChart`
|
||||
- feat: 增加 `isEnable` 开启初始化 启用这个后 无须再使用`init`方法
|
||||
```html
|
||||
<l-echart ref="chart" is-enable />
|
||||
```
|
||||
```js
|
||||
// 显示加载
|
||||
this.$refs.chart.showLoading()
|
||||
// 使用实例回调
|
||||
this.$refs.chart.setChart(chart => ...code)
|
||||
// 直接设置图表配置
|
||||
this.$refs.chart.setOption(data)
|
||||
```
|
||||
## 0.2.0(2021-05-05)
|
||||
- fix:修复 头条 百度 偏移的问题
|
||||
- docs: 更新文档
|
||||
## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
|
||||
## 0.1.0(2021-05-02)
|
||||
- chore: 第一次上传,基本全端兼容,使用方法与官网一致。
|
||||
- 已知BUG:非2d 无法使用背景色,已反馈官方
|
||||
- 已知BUG:头条 百度 有许些偏移
|
||||
- 后期计划:兼容nvue
|
395
uni_modules/lime-echart/components/l-echart/canvas.js
Normal file
395
uni_modules/lime-echart/components/l-echart/canvas.js
Normal file
@ -0,0 +1,395 @@
|
||||
import {getDeviceInfo} from './utils';
|
||||
|
||||
const cacheChart = {}
|
||||
const fontSizeReg = /([\d\.]+)px/;
|
||||
class EventEmit {
|
||||
constructor() {
|
||||
this.__events = {};
|
||||
}
|
||||
on(type, listener) {
|
||||
if (!type || !listener) {
|
||||
return;
|
||||
}
|
||||
const events = this.__events[type] || [];
|
||||
events.push(listener);
|
||||
this.__events[type] = events;
|
||||
}
|
||||
emit(type, e) {
|
||||
if (type.constructor === Object) {
|
||||
e = type;
|
||||
type = e && e.type;
|
||||
}
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const events = this.__events[type];
|
||||
if (!events || !events.length) {
|
||||
return;
|
||||
}
|
||||
events.forEach((listener) => {
|
||||
listener.call(this, e);
|
||||
});
|
||||
}
|
||||
off(type, listener) {
|
||||
const __events = this.__events;
|
||||
const events = __events[type];
|
||||
if (!events || !events.length) {
|
||||
return;
|
||||
}
|
||||
if (!listener) {
|
||||
delete __events[type];
|
||||
return;
|
||||
}
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
if (events[i] === listener) {
|
||||
events.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class Image {
|
||||
constructor() {
|
||||
this.currentSrc = null
|
||||
this.naturalHeight = 0
|
||||
this.naturalWidth = 0
|
||||
this.width = 0
|
||||
this.height = 0
|
||||
this.tagName = 'IMG'
|
||||
}
|
||||
set src(src) {
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
class OffscreenCanvas {
|
||||
constructor(ctx, com, canvasId) {
|
||||
this.tagName = 'canvas'
|
||||
this.com = com
|
||||
this.canvasId = canvasId
|
||||
this.ctx = ctx
|
||||
}
|
||||
set width(w) {
|
||||
this.com.offscreenWidth = w
|
||||
}
|
||||
set height(h) {
|
||||
this.com.offscreenHeight = h
|
||||
}
|
||||
get width() {
|
||||
return this.com.offscreenWidth || 0
|
||||
}
|
||||
get height() {
|
||||
return this.com.offscreenHeight || 0
|
||||
}
|
||||
getContext(type) {
|
||||
return this.ctx
|
||||
}
|
||||
getImageData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.com.$nextTick(() => {
|
||||
uni.canvasGetImageData({
|
||||
x:0,
|
||||
y:0,
|
||||
width: this.com.offscreenWidth,
|
||||
height: this.com.offscreenHeight,
|
||||
canvasId: this.canvasId,
|
||||
success: (res) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err)
|
||||
},
|
||||
}, this.com)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
export class Canvas {
|
||||
constructor(ctx, com, isNew, canvasNode={}) {
|
||||
cacheChart[com.canvasId] = {ctx}
|
||||
this.canvasId = com.canvasId;
|
||||
this.chart = null;
|
||||
this.isNew = isNew
|
||||
this.tagName = 'canvas'
|
||||
this.canvasNode = canvasNode;
|
||||
this.com = com;
|
||||
if (!isNew) {
|
||||
this._initStyle(ctx)
|
||||
}
|
||||
this._initEvent();
|
||||
this._ee = new EventEmit()
|
||||
}
|
||||
getContext(type) {
|
||||
if (type === '2d') {
|
||||
return this.ctx;
|
||||
}
|
||||
}
|
||||
setAttribute(key, value) {
|
||||
if(key === 'aria-label') {
|
||||
this.com['ariaLabel'] = value
|
||||
}
|
||||
}
|
||||
setChart(chart) {
|
||||
this.chart = chart;
|
||||
}
|
||||
createOffscreenCanvas(param){
|
||||
if(!this.children) {
|
||||
this.com.isOffscreenCanvas = true
|
||||
this.com.offscreenWidth = param.width||300
|
||||
this.com.offscreenHeight = param.height||300
|
||||
const com = this.com
|
||||
const canvasId = this.com.offscreenCanvasId
|
||||
const context = uni.createCanvasContext(canvasId, this.com)
|
||||
this._initStyle(context)
|
||||
this.children = new OffscreenCanvas(context, com, canvasId)
|
||||
}
|
||||
return this.children
|
||||
}
|
||||
appendChild(child) {
|
||||
console.log('child', child)
|
||||
}
|
||||
dispatchEvent(type, e) {
|
||||
if(typeof type == 'object') {
|
||||
this._ee.emit(type.type, type);
|
||||
} else {
|
||||
this._ee.emit(type, e);
|
||||
}
|
||||
return true
|
||||
}
|
||||
attachEvent() {
|
||||
}
|
||||
detachEvent() {
|
||||
}
|
||||
addEventListener(type, listener) {
|
||||
this._ee.on(type, listener)
|
||||
}
|
||||
removeEventListener(type, listener) {
|
||||
this._ee.off(type, listener)
|
||||
}
|
||||
_initCanvas(zrender, ctx) {
|
||||
// zrender.util.getContext = function() {
|
||||
// return ctx;
|
||||
// };
|
||||
// zrender.util.$override('measureText', function(text, font) {
|
||||
// ctx.font = font || '12px sans-serif';
|
||||
// return ctx.measureText(text, font);
|
||||
// });
|
||||
}
|
||||
_initStyle(ctx, child) {
|
||||
const styles = [
|
||||
'fillStyle',
|
||||
'strokeStyle',
|
||||
'fontSize',
|
||||
'globalAlpha',
|
||||
'opacity',
|
||||
'textAlign',
|
||||
'textBaseline',
|
||||
'shadow',
|
||||
'lineWidth',
|
||||
'lineCap',
|
||||
'lineJoin',
|
||||
'lineDash',
|
||||
'miterLimit',
|
||||
// #ifdef H5
|
||||
'font',
|
||||
// #endif
|
||||
];
|
||||
const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
|
||||
styles.forEach(style => {
|
||||
Object.defineProperty(ctx, style, {
|
||||
set: value => {
|
||||
// #ifdef H5
|
||||
if (style === 'font' && fontSizeReg.test(value)) {
|
||||
const match = fontSizeReg.exec(value);
|
||||
ctx.setFontSize(match[1]);
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (style === 'opacity') {
|
||||
ctx.setGlobalAlpha(value)
|
||||
return;
|
||||
}
|
||||
if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
|
||||
// #ifdef H5 || APP-PLUS || MP-BAIDU
|
||||
if(typeof value == 'object') {
|
||||
if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
|
||||
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
|
||||
}
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
if(colorReg.test(value)) {
|
||||
value = value.replace(colorReg, '#$1$1$2$2$3$3')
|
||||
}
|
||||
// #endif
|
||||
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if(!this.isNew && !child) {
|
||||
ctx.uniDrawImage = ctx.drawImage
|
||||
ctx.drawImage = (...a) => {
|
||||
a[0] = a[0].src
|
||||
ctx.uniDrawImage(...a)
|
||||
}
|
||||
}
|
||||
if(!ctx.createRadialGradient) {
|
||||
ctx.createRadialGradient = function() {
|
||||
return ctx.createCircularGradient(...[...arguments].slice(-3))
|
||||
};
|
||||
}
|
||||
// 字节不支持
|
||||
if (!ctx.strokeText) {
|
||||
ctx.strokeText = (...a) => {
|
||||
ctx.fillText(...a)
|
||||
}
|
||||
}
|
||||
|
||||
// 钉钉不支持 , 鸿蒙是异步
|
||||
if (!ctx.measureText || getDeviceInfo().osName == 'harmonyos') {
|
||||
ctx._measureText = ctx.measureText
|
||||
const strLen = (str) => {
|
||||
let len = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
|
||||
len++;
|
||||
} else {
|
||||
len += 2;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
ctx.measureText = (text, font) => {
|
||||
let fontSize = ctx?.state?.fontSize || 12;
|
||||
if (font) {
|
||||
fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
|
||||
}
|
||||
fontSize /= 2;
|
||||
let isBold = fontSize >= 16;
|
||||
const widthFactor = isBold ? 1.3 : 1;
|
||||
// ctx._measureText(text, (res) => {})
|
||||
return {
|
||||
width: strLen(text) * fontSize * widthFactor
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_initEvent(e) {
|
||||
this.event = {};
|
||||
const eventNames = [{
|
||||
wxName: 'touchStart',
|
||||
ecName: 'mousedown'
|
||||
}, {
|
||||
wxName: 'touchMove',
|
||||
ecName: 'mousemove'
|
||||
}, {
|
||||
wxName: 'touchEnd',
|
||||
ecName: 'mouseup'
|
||||
}, {
|
||||
wxName: 'touchEnd',
|
||||
ecName: 'click'
|
||||
}];
|
||||
|
||||
eventNames.forEach(name => {
|
||||
this.event[name.wxName] = e => {
|
||||
const touch = e.touches[0];
|
||||
this.chart.getZr().handler.dispatch(name.ecName, {
|
||||
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
|
||||
zrY: name.wxName === 'tap' ? touch.clientY : touch.y
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
set width(w) {
|
||||
this.canvasNode.width = w
|
||||
}
|
||||
set height(h) {
|
||||
this.canvasNode.height = h
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.canvasNode.width || 0
|
||||
}
|
||||
get height() {
|
||||
return this.canvasNode.height || 0
|
||||
}
|
||||
get ctx() {
|
||||
return cacheChart[this.canvasId]['ctx'] || null
|
||||
}
|
||||
set chart(chart) {
|
||||
cacheChart[this.canvasId]['chart'] = chart
|
||||
}
|
||||
get chart() {
|
||||
return cacheChart[this.canvasId]['chart'] || null
|
||||
}
|
||||
}
|
||||
|
||||
export function dispatch(name, {x,y, wheelDelta}) {
|
||||
this.dispatch(name, {
|
||||
zrX: x,
|
||||
zrY: y,
|
||||
zrDelta: wheelDelta,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () =>{}
|
||||
});
|
||||
}
|
||||
export function setCanvasCreator(echarts, {canvas, node}) {
|
||||
// echarts.setCanvasCreator(() => canvas);
|
||||
if(echarts && !echarts.registerPreprocessor) {
|
||||
return console.warn('echarts 版本不对或未传入echarts,vue3请使用esm格式')
|
||||
}
|
||||
echarts.registerPreprocessor(option => {
|
||||
if (option && option.series) {
|
||||
if (option.series.length > 0) {
|
||||
option.series.forEach(series => {
|
||||
series.progressive = 0;
|
||||
});
|
||||
} else if (typeof option.series === 'object') {
|
||||
option.series.progressive = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
function loadImage(src, onload, onerror) {
|
||||
let img = null
|
||||
if(node && node.createImage) {
|
||||
img = node.createImage()
|
||||
img.onload = onload.bind(img);
|
||||
img.onerror = onerror.bind(img);
|
||||
img.src = src;
|
||||
return img
|
||||
} else {
|
||||
img = new Image()
|
||||
img.onload = onload.bind(img)
|
||||
img.onerror = onerror.bind(img);
|
||||
img.src = src
|
||||
return img
|
||||
}
|
||||
}
|
||||
if(echarts.setPlatformAPI) {
|
||||
echarts.setPlatformAPI({
|
||||
loadImage: canvas.setChart ? loadImage : null,
|
||||
createCanvas(){
|
||||
const key = 'createOffscreenCanvas'
|
||||
return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
310
uni_modules/lime-echart/components/l-echart/l-echart.uvue
Normal file
310
uni_modules/lime-echart/components/l-echart/l-echart.uvue
Normal file
@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<!-- #ifdef APP -->
|
||||
<web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]"
|
||||
:webview-styles="[webviewStyles]" src="/uni_modules/lime-echart/static/uvue.html?v=10112">
|
||||
</web-view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<div class="lime-echart" ref="chartRef"></div>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 || APP-->
|
||||
<canvas class="lime-echart" :id="canvasid" :canvas-id="canvasid"
|
||||
@touchstart="touchstart"
|
||||
@touchmove="touchmove"
|
||||
@touchend="touchend">
|
||||
</canvas>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
// @ts-nocheck
|
||||
import { getCurrentInstance, nextTick } from "vue";
|
||||
import { Echarts } from './uvue';
|
||||
// #ifdef WEB
|
||||
import { dispatch } from './canvas';
|
||||
// #endif
|
||||
// #ifndef APP || WEB
|
||||
import {Canvas, setCanvasCreator, dispatch} from './canvas';
|
||||
import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
|
||||
// #endif
|
||||
type EchartsResolve = (value : Echarts) => void
|
||||
defineOptions({
|
||||
name: 'l-echart'
|
||||
})
|
||||
const emits = defineEmits(['finished'])
|
||||
const props = defineProps({
|
||||
// #ifdef APP
|
||||
webviewStyles: {
|
||||
type: Object
|
||||
},
|
||||
customStyle: {
|
||||
type: Object
|
||||
},
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
webviewStyles: {
|
||||
type: Object
|
||||
},
|
||||
customStyle: {
|
||||
type: [String, Object]
|
||||
},
|
||||
// #endif
|
||||
isDisableScroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isClickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
enableHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
beforeDelay: {
|
||||
type: Number,
|
||||
default: 30
|
||||
}
|
||||
})
|
||||
const instance = getCurrentInstance()!;
|
||||
const canvasid = `lime-echart-${instance.uid}`
|
||||
const finished = ref(false)
|
||||
const map = [] as EchartsResolve[]
|
||||
const callbackMap = [] as EchartsResolve[]
|
||||
// let context = null as UniWebViewElement | null
|
||||
let chart = null as Echarts | null
|
||||
let chartRef = ref<UniWebViewElement | null>(null)
|
||||
|
||||
const trigger = () => {
|
||||
// #ifdef APP
|
||||
if (finished.value) {
|
||||
if (chart == null) {
|
||||
chart = new Echarts(chartRef.value!)
|
||||
}
|
||||
while (map.length > 0) {
|
||||
const resolve = map.pop() as EchartsResolve
|
||||
resolve(chart!)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
while (map.length > 0) {
|
||||
if(chart != null){
|
||||
const resolve = map.pop() as EchartsResolve
|
||||
resolve(chart!)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
if(chart != null){
|
||||
while(callbackMap.length > 0){
|
||||
const callback = callbackMap.pop() as EchartsResolve
|
||||
callback(chart!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef APP
|
||||
const loaded = (event : UniWebViewLoadEvent) => {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
finished.value = true
|
||||
trigger()
|
||||
emits('finished')
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
const _next = () : boolean => {
|
||||
if (chart == null) {
|
||||
console.warn(`组件还未初始化,请先使用 init`)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
const setOption = (option : UTSJSONObject) => {
|
||||
if (_next()) return
|
||||
chart!.setOption(option);
|
||||
}
|
||||
const showLoading = () => {
|
||||
if (_next()) return
|
||||
chart!.showLoading();
|
||||
}
|
||||
const hideLoading = () => {
|
||||
if (_next()) return
|
||||
chart!.hideLoading();
|
||||
}
|
||||
const clear = () => {
|
||||
if (_next()) return
|
||||
chart!.clear();
|
||||
}
|
||||
const dispose = () => {
|
||||
if (_next()) return
|
||||
chart!.dispose();
|
||||
}
|
||||
const resize = (size : UTSJSONObject) => {
|
||||
if (_next()) return
|
||||
chart!.resize(size);
|
||||
}
|
||||
const canvasToTempFilePath = (opt : UTSJSONObject) => {
|
||||
if (_next()) return
|
||||
chart!.canvasToTempFilePath(opt);
|
||||
}
|
||||
|
||||
// #ifdef APP
|
||||
function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
|
||||
|
||||
if(callback!=null){
|
||||
callbackMap.push(callback)
|
||||
}
|
||||
return new Promise<Echarts>((resolve) => {
|
||||
map.push(resolve)
|
||||
trigger()
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
// #ifndef WEB
|
||||
let use2dCanvas = canIUseCanvas2d()
|
||||
const getContext = async () =>{
|
||||
return getRect(`#${canvasid}`, {context: instance.proxy!, type: use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
|
||||
if(res) {
|
||||
let dpr = uni.getWindowInfo().pixelRatio
|
||||
let {width, height, node} = res
|
||||
let canvas;
|
||||
if(node) {
|
||||
const ctx = node.getContext('2d');
|
||||
canvas = new Canvas(ctx, instance.proxy, true, node);
|
||||
} else {
|
||||
const ctx = uni.createCanvasContext(canvasid, instance.proxy);
|
||||
canvas = new Canvas(ctx, instance.proxy, false);
|
||||
}
|
||||
|
||||
return { canvas, width, height, devicePixelRatio: dpr, node };
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
const getTouch = (e) => {
|
||||
const touches = e.touches[0]
|
||||
// #ifdef WEB
|
||||
const rect = chart!.getZr().dom.getBoundingClientRect();
|
||||
const touch = {
|
||||
x: touches.clientX - rect.left,
|
||||
y: touches.clientY - rect.top
|
||||
}
|
||||
// #endif
|
||||
// #ifndef WEB
|
||||
const touch = {
|
||||
x: touches.x,
|
||||
y: touches.y
|
||||
}
|
||||
// #endif
|
||||
return touch
|
||||
}
|
||||
const touchstart = (e) => {
|
||||
if(chart == null) return
|
||||
const handler = chart.getZr().handler;
|
||||
const touch = getTouch(e)
|
||||
dispatch.call(handler, 'mousedown', touch)
|
||||
dispatch.call(handler, 'click', touch)
|
||||
}
|
||||
const touchmove = (e) => {
|
||||
if(chart == null) return
|
||||
const handler = chart.getZr().handler;
|
||||
const touch = getTouch(e)
|
||||
dispatch.call(handler, 'mousemove', touch)
|
||||
// const rect = chart.getZr().dom.getBoundingClientRect()
|
||||
// handler.dispatch('mousemove', {
|
||||
// zrX: e.touches[0].clientX - rect.left,
|
||||
// zrY: e.touches[0].clientY - rect.top
|
||||
// })
|
||||
}
|
||||
const touchend = (e) => {
|
||||
if(chart == null) return
|
||||
const handler = chart.getZr().handler;
|
||||
|
||||
const touch = {
|
||||
x: 999999999,
|
||||
y: 999999999
|
||||
}
|
||||
|
||||
dispatch.call(handler, 'mousemove', touch)
|
||||
dispatch.call(handler, 'touchend', touch)
|
||||
|
||||
}
|
||||
async function init(echarts: any, ...args: any[]): Promise<Echarts>{
|
||||
if(echarts == null){
|
||||
console.error('请确保已经引入了 ECharts 库');
|
||||
return Promise.reject('请确保已经引入了 ECharts 库');
|
||||
}
|
||||
let theme:string|null=null
|
||||
let opts={}
|
||||
let callback:Function|null=null;
|
||||
|
||||
args.forEach(item =>{
|
||||
if(typeof item === 'function') {
|
||||
callback = item
|
||||
} else if(['string'].includes(typeof item)){
|
||||
theme = item
|
||||
} else if(typeof item === 'object'){
|
||||
opts = item
|
||||
}
|
||||
})
|
||||
|
||||
// #ifdef WEB
|
||||
chart = echarts.init(chartRef.value, theme, opts)
|
||||
window.addEventListener('touchstart', touchstart)
|
||||
window.addEventListener('touchmove', touchmove)
|
||||
window.addEventListener('touchend', touchend)
|
||||
// #endif
|
||||
|
||||
// #ifndef WEB
|
||||
let config = await getContext();
|
||||
setCanvasCreator(echarts, config)
|
||||
chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
|
||||
// #endif
|
||||
console.log('chart', chart)
|
||||
if(callback!=null && typeof callback == 'function'){
|
||||
callbackMap.push(callback)
|
||||
}
|
||||
return new Promise<Echarts>((resolve) => {
|
||||
map.push(resolve)
|
||||
trigger()
|
||||
})
|
||||
}
|
||||
onMounted(()=>{
|
||||
nextTick(()=>{
|
||||
finished.value = true
|
||||
trigger()
|
||||
emits('finished')
|
||||
})
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
// #ifdef WEB
|
||||
window.removeEventListener('touchstart', touchstart)
|
||||
window.removeEventListener('touchmove', touchmove)
|
||||
window.removeEventListener('touchend', touchend)
|
||||
// #endif
|
||||
})
|
||||
// #endif
|
||||
|
||||
defineExpose({
|
||||
init,
|
||||
setOption,
|
||||
showLoading,
|
||||
hideLoading,
|
||||
clear,
|
||||
dispose,
|
||||
resize,
|
||||
canvasToTempFilePath
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.lime-echart {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
530
uni_modules/lime-echart/components/l-echart/l-echart.vue
Normal file
530
uni_modules/lime-echart/components/l-echart/l-echart.vue
Normal file
@ -0,0 +1,530 @@
|
||||
<template>
|
||||
<view class="lime-echart" :style="[customStyle]" v-if="canvasId" ref="limeEchart" :aria-label="ariaLabel">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<canvas
|
||||
class="lime-echart__canvas"
|
||||
v-if="use2dCanvas"
|
||||
type="2d"
|
||||
:id="canvasId"
|
||||
:style="canvasStyle"
|
||||
:disable-scroll="isDisableScroll"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="touchEnd"
|
||||
/>
|
||||
<canvas
|
||||
class="lime-echart__canvas"
|
||||
v-else
|
||||
:width="nodeWidth"
|
||||
:height="nodeHeight"
|
||||
:style="canvasStyle"
|
||||
:canvas-id="canvasId"
|
||||
:id="canvasId"
|
||||
:disable-scroll="isDisableScroll"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="touchEnd"
|
||||
/>
|
||||
<view class="lime-echart__mask"
|
||||
v-if="isPC"
|
||||
@mousedown="touchStart"
|
||||
@mousemove="touchMove"
|
||||
@mouseup="touchEnd"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="touchEnd">
|
||||
</view>
|
||||
<canvas v-if="isOffscreenCanvas" :style="offscreenStyle" :canvas-id="offscreenCanvasId"></canvas>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<web-view
|
||||
class="lime-echart__canvas"
|
||||
:id="canvasId"
|
||||
:style="canvasStyle"
|
||||
:webview-styles="webviewStyles"
|
||||
ref="webview"
|
||||
src="/uni_modules/lime-echart/static/uvue.html?v=1"
|
||||
@pagefinish="finished = true"
|
||||
@onPostMessage="onMessage"
|
||||
></web-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ts-nocheck
|
||||
// #ifndef APP-NVUE
|
||||
import {Canvas, setCanvasCreator, dispatch} from './canvas';
|
||||
import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect, getDeviceInfo} from './utils';
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
import { base64ToPath, sleep } from './utils';
|
||||
import {Echarts} from './nvue'
|
||||
// #endif
|
||||
const charts = {}
|
||||
const echartsObj = {}
|
||||
|
||||
|
||||
/**
|
||||
* LimeChart 图表
|
||||
* @description 全端兼容的eCharts
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=4899
|
||||
|
||||
* @property {String} customStyle 自定义样式
|
||||
* @property {String} type 指定 canvas 类型
|
||||
* @value 2d 使用canvas 2d,部分小程序支持
|
||||
* @value '' 使用原生canvas,会有层级问题
|
||||
* @value bottom right 不缩放图片,只显示图片的右下边区域
|
||||
* @property {Boolean} isDisableScroll
|
||||
* @property {number} beforeDelay = [30] 延迟初始化 (毫秒)
|
||||
* @property {Boolean} enableHover PC端使用鼠标悬浮
|
||||
|
||||
* @event {Function} finished 加载完成触发
|
||||
*/
|
||||
export default {
|
||||
name: 'lime-echart',
|
||||
props: {
|
||||
// #ifdef MP-WEIXIN || MP-TOUTIAO
|
||||
type: {
|
||||
type: String,
|
||||
default: '2d'
|
||||
},
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
webviewStyles: Object,
|
||||
// hybrid: Boolean,
|
||||
// #endif
|
||||
customStyle: String,
|
||||
isDisableScroll: Boolean,
|
||||
isClickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
enableHover: Boolean,
|
||||
beforeDelay: {
|
||||
type: Number,
|
||||
default: 30
|
||||
},
|
||||
landscape: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
|
||||
use2dCanvas: true,
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
|
||||
use2dCanvas: false,
|
||||
// #endif
|
||||
ariaLabel: '图表',
|
||||
width: null,
|
||||
height: null,
|
||||
nodeWidth: null,
|
||||
nodeHeight: null,
|
||||
// canvasNode: null,
|
||||
config: {},
|
||||
inited: false,
|
||||
finished: false,
|
||||
file: '',
|
||||
platform: '',
|
||||
isPC: false,
|
||||
isDown: false,
|
||||
isOffscreenCanvas: false,
|
||||
offscreenWidth: 0,
|
||||
offscreenHeight: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rootStyle() {
|
||||
if(this.landscape) {
|
||||
return `transform: translate(-50%,-50%) rotate(90deg); top:50%; left:50%;`
|
||||
}
|
||||
},
|
||||
canvasId() {
|
||||
return `lime-echart${this._ && this._.uid || this._uid}`
|
||||
},
|
||||
offscreenCanvasId() {
|
||||
return `${this.canvasId}_offscreen`
|
||||
},
|
||||
offscreenStyle() {
|
||||
return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
|
||||
},
|
||||
canvasStyle() {
|
||||
return this.rootStyle + (this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : '')
|
||||
}
|
||||
},
|
||||
// #ifndef VUE3
|
||||
beforeDestroy() {
|
||||
this.clear()
|
||||
this.dispose()
|
||||
// #ifdef H5
|
||||
if(this.isPC) {
|
||||
document.removeEventListener('mousewheel', this.mousewheel)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
this.clear()
|
||||
this.dispose()
|
||||
// #ifdef H5
|
||||
if(this.isPC) {
|
||||
document.removeEventListener('mousewheel', this.mousewheel)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// #endif
|
||||
created() {
|
||||
// #ifdef H5
|
||||
if(!('ontouchstart' in window)) {
|
||||
this.isPC = true
|
||||
document.addEventListener('mousewheel', this.mousewheel)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
|
||||
// const { platform } = uni.getSystemInfoSync();
|
||||
const { platform } = getDeviceInfo();
|
||||
this.isPC = /windows/i.test(platform)
|
||||
// #endif
|
||||
this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.$emit('finished')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// #ifdef APP-NVUE
|
||||
onMessage(e) {
|
||||
const detail = e?.detail?.data[0] || null;
|
||||
const data = detail?.data
|
||||
const key = detail?.event
|
||||
const options = data?.options
|
||||
const event = data?.event
|
||||
const file = detail?.file
|
||||
if (key == 'log' && data) {
|
||||
console.log(data)
|
||||
}
|
||||
if(event) {
|
||||
this.chart.dispatchAction(event.replace(/"/g,''), options)
|
||||
}
|
||||
if(file) {
|
||||
thie.file = file
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
setChart(callback) {
|
||||
if(!this.chart) {
|
||||
console.warn(`组件还未初始化,请先使用 init`)
|
||||
return
|
||||
}
|
||||
if(typeof callback === 'function' && this.chart) {
|
||||
callback(this.chart);
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
if(typeof callback === 'function') {
|
||||
this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
setOption() {
|
||||
if (!this.chart || !this.chart.setOption) {
|
||||
console.warn(`组件还未初始化,请先使用 init`)
|
||||
return
|
||||
}
|
||||
this.chart.setOption(...arguments);
|
||||
},
|
||||
showLoading() {
|
||||
if(this.chart) {
|
||||
this.chart.showLoading(...arguments)
|
||||
}
|
||||
},
|
||||
hideLoading() {
|
||||
if(this.chart) {
|
||||
this.chart.hideLoading()
|
||||
}
|
||||
},
|
||||
clear() {
|
||||
if(this.chart) {
|
||||
this.chart.clear()
|
||||
}
|
||||
},
|
||||
dispose() {
|
||||
if(this.chart) {
|
||||
this.chart.dispose()
|
||||
}
|
||||
},
|
||||
resize(size) {
|
||||
if(size && size.width && size.height) {
|
||||
this.height = size.height
|
||||
this.width = size.width
|
||||
if(this.chart) {this.chart.resize(size)}
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`.lime-echart`)
|
||||
.boundingClientRect()
|
||||
.exec(res => {
|
||||
if (res) {
|
||||
let { width, height } = res[0];
|
||||
this.width = width = width || 300;
|
||||
this.height = height = height || 300;
|
||||
this.chart.resize({width, height})
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
canvasToTempFilePath(args = {}) {
|
||||
// #ifndef APP-NVUE
|
||||
const { use2dCanvas, canvasId } = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
const copyArgs = Object.assign({
|
||||
canvasId,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
}, args);
|
||||
if (use2dCanvas) {
|
||||
delete copyArgs.canvasId;
|
||||
copyArgs.canvas = this.canvasNode;
|
||||
}
|
||||
uni.canvasToTempFilePath(copyArgs, this);
|
||||
});
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.file = ''
|
||||
this.$refs.webview.evalJs(`canvasToTempFilePath()`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.$watch('file', async (file) => {
|
||||
if(file) {
|
||||
const tempFilePath = await base64ToPath(file)
|
||||
resolve(args.success({tempFilePath}))
|
||||
} else {
|
||||
reject(args.fail({error: ``}))
|
||||
}
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
async init(echarts, ...args) {
|
||||
// #ifndef APP-NVUE
|
||||
if(args && args.length == 0 && !echarts) {
|
||||
console.error('缺少参数:init(echarts, theme?:string, opts?: object, callback?: function)')
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
let theme=null,opts={},callback;
|
||||
|
||||
Array.from(arguments).forEach(item => {
|
||||
if(typeof item === 'function') {
|
||||
callback = item
|
||||
}
|
||||
if(['string'].includes(typeof item)) {
|
||||
theme = item
|
||||
}
|
||||
if(typeof item === 'object') {
|
||||
opts = item
|
||||
}
|
||||
})
|
||||
|
||||
if(this.beforeDelay) {
|
||||
await sleep(this.beforeDelay)
|
||||
}
|
||||
let config = await this.getContext();
|
||||
// #ifndef APP-NVUE
|
||||
setCanvasCreator(echarts, config)
|
||||
try {
|
||||
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
|
||||
if(typeof callback === 'function') {
|
||||
callback(this.chart)
|
||||
} else {
|
||||
return this.chart
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(e.messges)
|
||||
return null
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.chart = new Echarts(this.$refs.webview)
|
||||
this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
|
||||
if(callback) {
|
||||
callback(this.chart)
|
||||
} else {
|
||||
return this.chart
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
getContext() {
|
||||
// #ifdef APP-NVUE
|
||||
if(this.finished) {
|
||||
return Promise.resolve(this.finished)
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this.$watch('finished', (val) => {
|
||||
if(val) {
|
||||
resolve(this.finished)
|
||||
}
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
return getRect(`#${this.canvasId}`, {context: this, type: this.use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
|
||||
if(res) {
|
||||
let dpr = devicePixelRatio
|
||||
let {width, height, node} = res
|
||||
let canvas;
|
||||
this.width = width = width || 300;
|
||||
this.height = height = height || 300;
|
||||
if(node) {
|
||||
const ctx = node.getContext('2d');
|
||||
canvas = new Canvas(ctx, this, true, node);
|
||||
this.canvasNode = node
|
||||
} else {
|
||||
// #ifdef MP-TOUTIAO
|
||||
dpr = !this.isPC ? devicePixelRatio : 1// 1.25
|
||||
// #endif
|
||||
// #ifndef MP-ALIPAY || MP-TOUTIAO
|
||||
dpr = this.isPC ? devicePixelRatio : 1
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY || MP-LARK
|
||||
dpr = devicePixelRatio
|
||||
// #endif
|
||||
// #ifdef WEB
|
||||
dpr = 1
|
||||
// #endif
|
||||
this.rect = res
|
||||
this.nodeWidth = width * dpr;
|
||||
this.nodeHeight = height * dpr;
|
||||
const ctx = uni.createCanvasContext(this.canvasId, this);
|
||||
canvas = new Canvas(ctx, this, false);
|
||||
}
|
||||
|
||||
return { canvas, width, height, devicePixelRatio: dpr, node };
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
// #ifndef APP-NVUE
|
||||
getRelative(e, touches) {
|
||||
let { clientX, clientY } = e
|
||||
if(!(clientX && clientY) && touches && touches[0]) {
|
||||
clientX = touches[0].clientX
|
||||
clientY = touches[0].clientY
|
||||
}
|
||||
return {x: clientX - this.rect.left, y: clientY - this.rect.top, wheelDelta: e.wheelDelta || 0}
|
||||
},
|
||||
getTouch(e, touches) {
|
||||
const {x} = touches && touches[0] || {}
|
||||
const touch = x ? touches[0] : this.getRelative(e, touches);
|
||||
if(this.landscape) {
|
||||
[touch.x, touch.y] = [touch.y, this.height - touch.x]
|
||||
}
|
||||
return touch;
|
||||
},
|
||||
touchStart(e) {
|
||||
this.isDown = true
|
||||
const next = () => {
|
||||
const touches = convertTouchesToArray(e.touches)
|
||||
if(this.chart) {
|
||||
const touch = this.getTouch(e, touches)
|
||||
this.startX = touch.x
|
||||
this.startY = touch.y
|
||||
this.startT = new Date()
|
||||
const handler = this.chart.getZr().handler;
|
||||
dispatch.call(handler, 'mousedown', touch)
|
||||
dispatch.call(handler, 'mousemove', touch)
|
||||
handler.processGesture(wrapTouch(e), 'start');
|
||||
clearTimeout(this.endTimer);
|
||||
}
|
||||
|
||||
}
|
||||
if(this.isPC) {
|
||||
getRect(`#${this.canvasId}`, {context: this}).then(res => {
|
||||
this.rect = res
|
||||
next()
|
||||
})
|
||||
return
|
||||
}
|
||||
next()
|
||||
},
|
||||
touchMove(e) {
|
||||
if(this.isPC && this.enableHover && !this.isDown) {this.isDown = true}
|
||||
const touches = convertTouchesToArray(e.touches)
|
||||
if (this.chart && this.isDown) {
|
||||
const handler = this.chart.getZr().handler;
|
||||
dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
|
||||
handler.processGesture(wrapTouch(e), 'change');
|
||||
}
|
||||
|
||||
},
|
||||
touchEnd(e) {
|
||||
this.isDown = false
|
||||
if (this.chart) {
|
||||
const touches = convertTouchesToArray(e.changedTouches)
|
||||
const {x} = touches && touches[0] || {}
|
||||
const touch = (x ? touches[0] : this.getRelative(e, touches)) || {};
|
||||
if(this.landscape) {
|
||||
[touch.x, touch.y] = [touch.y, this.height - touch.x]
|
||||
}
|
||||
const handler = this.chart.getZr().handler;
|
||||
const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
|
||||
dispatch.call(handler, 'mouseup', touch)
|
||||
handler.processGesture(wrapTouch(e), 'end');
|
||||
if(isClick) {
|
||||
dispatch.call(handler, 'click', touch)
|
||||
} else {
|
||||
this.endTimer = setTimeout(() => {
|
||||
dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
|
||||
dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
|
||||
},50)
|
||||
}
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
mousewheel(e){
|
||||
if(this.chart) {
|
||||
// dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.lime-echart {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
}
|
||||
.lime-echart__canvas {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
}
|
||||
/* #ifndef APP-NVUE */
|
||||
.lime-echart__mask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
51
uni_modules/lime-echart/components/l-echart/nvue.js
Normal file
51
uni_modules/lime-echart/components/l-echart/nvue.js
Normal file
@ -0,0 +1,51 @@
|
||||
export class Echarts {
|
||||
eventMap = new Map()
|
||||
constructor(webview) {
|
||||
this.webview = webview
|
||||
this.options = null
|
||||
}
|
||||
setOption() {
|
||||
this.options = arguments
|
||||
this.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
|
||||
}
|
||||
getOption() {
|
||||
return this.options
|
||||
}
|
||||
showLoading() {
|
||||
this.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
|
||||
}
|
||||
hideLoading() {
|
||||
this.webview.evalJs(`hideLoading()`);
|
||||
}
|
||||
clear() {
|
||||
this.webview.evalJs(`clear()`);
|
||||
}
|
||||
dispose() {
|
||||
this.webview.evalJs(`dispose()`);
|
||||
}
|
||||
resize(size) {
|
||||
if(size) {
|
||||
this.webview.evalJs(`resize(${JSON.stringify(size)})`);
|
||||
} else {
|
||||
this.webview.evalJs(`resize()`);
|
||||
}
|
||||
}
|
||||
on(type, ...args) {
|
||||
const query = args[0]
|
||||
const useQuery = query && typeof query != 'function'
|
||||
const param = useQuery ? [type, query] : [type]
|
||||
const key = `${type}${useQuery ? JSON.stringify(query): '' }`
|
||||
const callback = useQuery ? args[1]: args[0]
|
||||
if(typeof callback == 'function'){
|
||||
this.eventMap.set(key, callback)
|
||||
}
|
||||
this.webview.evalJs(`on(${JSON.stringify(param)})`);
|
||||
console.warn('nvue 暂不支持事件')
|
||||
}
|
||||
dispatchAction(type, options){
|
||||
const handler = this.eventMap.get(type)
|
||||
if(handler){
|
||||
handler(options)
|
||||
}
|
||||
}
|
||||
}
|
190
uni_modules/lime-echart/components/l-echart/utils.js
Normal file
190
uni_modules/lime-echart/components/l-echart/utils.js
Normal file
@ -0,0 +1,190 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 获取设备基础信息
|
||||
*
|
||||
* @see [uni.getDeviceInfo](https://uniapp.dcloud.net.cn/api/system/getDeviceInfo.html)
|
||||
*/
|
||||
export function getDeviceInfo() {
|
||||
if (uni.getDeviceInfo && uni.canIUse('getDeviceInfo')) {
|
||||
return uni.getDeviceInfo();
|
||||
} else {
|
||||
return uni.getSystemInfoSync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取窗口信息
|
||||
*
|
||||
* @see [uni.getWindowInfo](https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html)
|
||||
*/
|
||||
export function getWindowInfo() {
|
||||
if (uni.getWindowInfo && uni.canIUse('getWindowInfo')) {
|
||||
return uni.getWindowInfo();
|
||||
} else {
|
||||
return uni.getSystemInfoSync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取APP基础信息
|
||||
*
|
||||
* @see [uni.getAppBaseInfo](https://uniapp.dcloud.net.cn/api/system/getAppBaseInfo.html)
|
||||
*/
|
||||
export function getAppBaseInfo() {
|
||||
if (uni.getAppBaseInfo && uni.canIUse('getAppBaseInfo')) {
|
||||
return uni.getAppBaseInfo();
|
||||
} else {
|
||||
return uni.getSystemInfoSync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
// 计算版本
|
||||
export function compareVersion(v1, v2) {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// const systemInfo = uni.getSystemInfoSync();
|
||||
|
||||
function gte(version) {
|
||||
// 截止 2023-03-22 mac pc小程序不支持 canvas 2d
|
||||
// let {
|
||||
// SDKVersion,
|
||||
// platform
|
||||
// } = systemInfo;
|
||||
const { platform } = getDeviceInfo();
|
||||
let { SDKVersion } = getAppBaseInfo();
|
||||
// #ifdef MP-ALIPAY
|
||||
SDKVersion = my.SDKVersion
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
return platform !== 'mac' && compareVersion(SDKVersion, version) >= 0;
|
||||
// #endif
|
||||
return compareVersion(SDKVersion, version) >= 0;
|
||||
}
|
||||
|
||||
|
||||
export function canIUseCanvas2d() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return gte('2.9.0');
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return gte('2.7.0');
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
return gte('1.78.0');
|
||||
// #endif
|
||||
return false
|
||||
}
|
||||
|
||||
export function convertTouchesToArray(touches) {
|
||||
// 如果 touches 是一个数组,则直接返回它
|
||||
if (Array.isArray(touches)) {
|
||||
return touches;
|
||||
}
|
||||
// 如果touches是一个对象,则转换为数组
|
||||
if (typeof touches === 'object' && touches !== null) {
|
||||
return Object.values(touches);
|
||||
}
|
||||
// 对于其他类型,直接返回它
|
||||
return touches;
|
||||
}
|
||||
|
||||
export function wrapTouch(event) {
|
||||
event.touches = convertTouchesToArray(event.touches)
|
||||
for (let i = 0; i < event.touches.length; ++i) {
|
||||
const touch = event.touches[i];
|
||||
touch.offsetX = touch.x;
|
||||
touch.offsetY = touch.y;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
// export const devicePixelRatio = uni.getSystemInfoSync().pixelRatio
|
||||
export const devicePixelRatio = getWindowInfo().pixelRatio;
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
export function base64ToPath(base64) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
const time = new Date().getTime();
|
||||
const filePath = `_doc/uniapp_temp/${time}.${format}`
|
||||
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
console.error(`${JSON.stringify(error)}`)
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
console.error(`${JSON.stringify(error)}`)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
export function sleep(time) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true)
|
||||
}, time)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function getRect(selector, options = {}) {
|
||||
const typeDefault = 'boundingClientRect'
|
||||
const {
|
||||
context,
|
||||
type = typeDefault
|
||||
} = options
|
||||
return new Promise((resolve, reject) => {
|
||||
const dom = uni.createSelectorQuery().in(context).select(selector);
|
||||
const result = (rect) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
}
|
||||
if (type == typeDefault) {
|
||||
dom[type](result).exec()
|
||||
} else {
|
||||
dom[type]({
|
||||
node: true,
|
||||
size: true,
|
||||
rect: true
|
||||
}, result).exec()
|
||||
}
|
||||
});
|
||||
};
|
133
uni_modules/lime-echart/components/l-echart/uvue.uts
Normal file
133
uni_modules/lime-echart/components/l-echart/uvue.uts
Normal file
@ -0,0 +1,133 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef APP
|
||||
type EchartsEventHandler = (event: UTSJSONObject)=>void
|
||||
// type EchartsTempResolve = (obj : UTSJSONObject) => void
|
||||
// type EchartsTempOptions = UTSJSONObject
|
||||
export class Echarts {
|
||||
options: UTSJSONObject = {} as UTSJSONObject
|
||||
context: UniWebViewElement
|
||||
eventMap: Map<string, EchartsEventHandler> = new Map()
|
||||
private temp: UTSJSONObject[] = []
|
||||
constructor(context: UniWebViewElement){
|
||||
this.context = context
|
||||
this.init()
|
||||
}
|
||||
init(){
|
||||
this.context.evalJS(`init(null, null, ${JSON.stringify({})})`)
|
||||
|
||||
this.context.addEventListener('message', (e : UniWebViewMessageEvent) => {
|
||||
// event.stopPropagation()
|
||||
// event.preventDefault()
|
||||
|
||||
const detail = e.detail.data[0]
|
||||
const file = detail.getString('file')
|
||||
const data = detail.get('data')
|
||||
const key = detail.getString('event')
|
||||
const options = typeof data == 'object' ? (data as UTSJSONObject).getJSON('options'): null
|
||||
const event = typeof data == 'object' ? (data as UTSJSONObject).getString('event'): null
|
||||
if (key == 'log' && data != null) {
|
||||
console.log(data)
|
||||
}
|
||||
if (event != null && options != null) {
|
||||
this.dispatchAction(event.replace(/"/g,''), options)
|
||||
}
|
||||
if(file != null){
|
||||
while (this.temp.length > 0) {
|
||||
const opt = this.temp.pop()
|
||||
const success = opt?.get('success')
|
||||
if(typeof success == 'function'){
|
||||
success as (res: UTSJSONObject) => void
|
||||
success({tempFilePath: file})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
setOption(option: UTSJSONObject){
|
||||
this.options = option;
|
||||
this.context.evalJS(`setOption(${JSON.stringify([option])})`)
|
||||
}
|
||||
setOption(option: UTSJSONObject, notMerge: boolean = false, lazyUpdate: boolean = false){
|
||||
this.options = option;
|
||||
this.context.evalJS(`setOption(${JSON.stringify([option, notMerge, lazyUpdate])})`)
|
||||
}
|
||||
setOption(option: UTSJSONObject, notMerge: UTSJSONObject){
|
||||
this.options = option;
|
||||
this.context.evalJS(`setOption(${JSON.stringify([option, notMerge])})`)
|
||||
}
|
||||
getOption(): UTSJSONObject {
|
||||
return this.options
|
||||
}
|
||||
showLoading(){
|
||||
this.context.evalJS(`showLoading(${JSON.stringify([] as any[])})`);
|
||||
}
|
||||
showLoading(type: string, opts: UTSJSONObject){
|
||||
this.context.evalJS(`showLoading(${JSON.stringify([type, opts])})`);
|
||||
}
|
||||
hideLoading(){
|
||||
this.context.evalJS(`hideLoading()`);
|
||||
}
|
||||
clear(){
|
||||
this.context.evalJS(`clear()`);
|
||||
}
|
||||
dispose(){
|
||||
this.context.evalJS(`dispose()`);
|
||||
}
|
||||
resize(size:UTSJSONObject){
|
||||
setTimeout(()=>{
|
||||
this.context.evalJS(`resize(${JSON.stringify(size)})`);
|
||||
},0)
|
||||
}
|
||||
resize(){
|
||||
setTimeout(()=>{
|
||||
this.context.evalJS(`resize()`);
|
||||
},10)
|
||||
|
||||
}
|
||||
on(type:string, query: any, callback: EchartsEventHandler) {
|
||||
const key = `${type}${JSON.stringify(query)}`
|
||||
if(typeof callback == 'function'){
|
||||
this.eventMap.set(key, callback)
|
||||
}
|
||||
this.context.evalJS(`on(${JSON.stringify([type, query])})`);
|
||||
console.warn('uvue 暂不支持事件')
|
||||
}
|
||||
on(type:string, callback: EchartsEventHandler) {
|
||||
const key = `${type}`
|
||||
if(typeof callback == 'function'){
|
||||
this.eventMap.set(key, callback)
|
||||
}
|
||||
this.context.evalJS(`on(${JSON.stringify([type])})`);
|
||||
console.warn('uvue 暂不支持事件')
|
||||
}
|
||||
dispatchAction(type:string, options: UTSJSONObject){
|
||||
const handler = this.eventMap.get(type)
|
||||
if(handler!=null){
|
||||
handler(options)
|
||||
}
|
||||
}
|
||||
canvasToTempFilePath(opt: UTSJSONObject){
|
||||
// this.context.evalJS(`on(${JSON.stringify(opt)})`);
|
||||
this.context.evalJS(`canvasToTempFilePath(${JSON.stringify(opt)})`);
|
||||
this.temp.push(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
export class Echarts {
|
||||
constructor() {}
|
||||
setOption(option: UTSJSONObject): void
|
||||
isDisposed(): boolean;
|
||||
clear(): void;
|
||||
resize(size:UTSJSONObject): void;
|
||||
resize(): void;
|
||||
canvasToTempFilePath(opt : UTSJSONObject): void;
|
||||
dispose(): void;
|
||||
showLoading(cfg?: UTSJSONObject): void;
|
||||
showLoading(name?: string, cfg?: UTSJSONObject): void;
|
||||
hideLoading(): void;
|
||||
getZr(): any
|
||||
}
|
||||
// #endif
|
159
uni_modules/lime-echart/components/lime-echart/lime-echart.nvue
Normal file
159
uni_modules/lime-echart/components/lime-echart/lime-echart.nvue
Normal file
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<view style="width: 100%; height: 408px;">
|
||||
<l-echart ref="chartRef" @finished="init"></l-echart>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showTip: false,
|
||||
option: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// shadowBlur: 0,
|
||||
textStyle: {
|
||||
textShadowBlur: 0
|
||||
},
|
||||
renderMode: 'richText',
|
||||
},
|
||||
legend: {
|
||||
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [120, 132, 101, 134, 90, 230, 210]
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310]
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 232, 201, 154, 190, 330, 410]
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [320, 332, 301, 334, 390, 330, 320]
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('lime echarts nvue')
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const chartRef = this.$refs['chartRef']
|
||||
chartRef.init(chart => {
|
||||
chart.setOption(this.option);
|
||||
|
||||
|
||||
setTimeout(()=>{
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// shadowBlur: 0,
|
||||
textStyle: {
|
||||
textShadowBlur: 0
|
||||
},
|
||||
renderMode: 'richText',
|
||||
},
|
||||
legend: {
|
||||
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [120, 132, 101, 134, 90, 230, 210]
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310]
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 232, 201, 154, 190, 330, 410]
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [320, 332, 301, 334, 390, 330, 320]
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320]
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option);
|
||||
},1000)
|
||||
})
|
||||
},
|
||||
save() {
|
||||
// this.$refs.chart.canvasToTempFilePath({
|
||||
// success(res) {
|
||||
// console.log('res::::', res)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
160
uni_modules/lime-echart/components/lime-echart/lime-echart.uvue
Normal file
160
uni_modules/lime-echart/components/lime-echart/lime-echart.uvue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<view style="width: 100%; height: 408px;">
|
||||
<l-echart ref="chartRef" @finished="init"></l-echart>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
// @ts-nocheck
|
||||
// #ifndef APP
|
||||
import * as echarts from 'echarts/dist/echarts.esm.js'
|
||||
// #endif
|
||||
const chartRef = ref<LEchartComponentPublicInstance|null>(null)
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// shadowBlur: 0,
|
||||
textStyle: {
|
||||
textShadowBlur: 0
|
||||
},
|
||||
renderMode: 'richText',
|
||||
},
|
||||
// formatter: async (params: any) => {
|
||||
// console.log('params', params)
|
||||
// return 1
|
||||
// },
|
||||
legend: {
|
||||
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [120, 132, 101, 134, 90, 230, 210]
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310]
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 232, 201, 154, 190, 330, 410]
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [320, 332, 301, 334, 390, 330, 320]
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const init = async () =>{
|
||||
if(chartRef.value== null) return
|
||||
// #ifdef APP
|
||||
const chart = await chartRef.value!.init(null)
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
const chart = await chartRef.value!.init(echarts, null)
|
||||
// #endif
|
||||
chart.setOption(option)
|
||||
chart.on('mouseover', function (params) {
|
||||
console.log('params', params);
|
||||
});
|
||||
|
||||
|
||||
// setTimeout(()=> {
|
||||
// const option1 = {
|
||||
// tooltip: {
|
||||
// trigger: 'axis',
|
||||
// // shadowBlur: 0,
|
||||
// textStyle: {
|
||||
// textShadowBlur: 0
|
||||
// },
|
||||
// renderMode: 'richText',
|
||||
// },
|
||||
// legend: {
|
||||
// data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
|
||||
// },
|
||||
// grid: {
|
||||
// left: '3%',
|
||||
// right: '4%',
|
||||
// bottom: '3%',
|
||||
// containLabel: true
|
||||
// },
|
||||
// xAxis: {
|
||||
// type: 'category',
|
||||
// boundaryGap: false,
|
||||
// data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
// },
|
||||
// yAxis: {
|
||||
// type: 'value'
|
||||
// },
|
||||
// series: [
|
||||
// {
|
||||
// name: '邮件营销',
|
||||
// type: 'line',
|
||||
// stack: '总量',
|
||||
// data: [820, 132, 101, 134, 90, 230, 210]
|
||||
// },
|
||||
// {
|
||||
// name: '联盟广告',
|
||||
// type: 'line',
|
||||
// stack: '总量',
|
||||
// data: [220, 182, 191, 234, 290, 330, 310]
|
||||
// },
|
||||
// {
|
||||
// name: '视频广告',
|
||||
// type: 'line',
|
||||
// stack: '总量',
|
||||
// data: [950, 232, 201, 154, 190, 330, 410]
|
||||
// },
|
||||
// {
|
||||
// name: '直接访问',
|
||||
// type: 'line',
|
||||
// stack: '总量',
|
||||
// data: [320, 332, 301, 334, 390, 330, 320]
|
||||
// },
|
||||
// {
|
||||
// name: '搜索引擎',
|
||||
// type: 'line',
|
||||
// stack: '总量',
|
||||
// data: [820, 932, 901, 934, 1290, 1330, 1320]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// chart.setOption(option1)
|
||||
// },1000)
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
227
uni_modules/lime-echart/components/lime-echart/lime-echart.vue
Normal file
227
uni_modules/lime-echart/components/lime-echart/lime-echart.vue
Normal file
@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<view>
|
||||
<view style="height: 750rpx; position: relative">
|
||||
<l-echart ref="chart" @finished="init"></l-echart>
|
||||
<view
|
||||
class="customTooltips"
|
||||
:style="{ left: position[0] + 'px', top: position[1] + 'px' }"
|
||||
v-if="params.length && position.length && showTip"
|
||||
>
|
||||
<view>这是个自定的tooltips</view>
|
||||
<view>{{ params[0]['axisValue'] }}</view>
|
||||
<view v-for="item in params">
|
||||
<view>
|
||||
<text>{{ item.seriesName }}</text>
|
||||
<text>{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// nvue 不需要引入
|
||||
// #ifdef VUE2
|
||||
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
// #ifdef MP
|
||||
// 由于vue3 使用vite 不支持umd格式的包,小程序依然可以使用,但需要使用require
|
||||
const echarts = require('../../static/echarts.min')
|
||||
// #endif
|
||||
// #ifndef MP
|
||||
// 由于 vue3 使用vite 不支持umd格式的包,故引入npm的包
|
||||
import * as echarts from 'echarts/dist/echarts.esm'
|
||||
// #endif
|
||||
// #endif
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showTip: false,
|
||||
position: [],
|
||||
params: [],
|
||||
option: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// shadowBlur: 0,
|
||||
textStyle: {
|
||||
textShadowBlur: 0,
|
||||
},
|
||||
renderMode: 'richText',
|
||||
position: (point, params, dom, rect, size) => {
|
||||
// 假设自定义的tooltips尺寸
|
||||
const box = [170, 170]
|
||||
// 偏移
|
||||
const offsetX = point[0] < size.viewSize[0] / 2 ? 20 : -box[0] - 20
|
||||
const offsetY = point[1] < size.viewSize[1] / 2 ? 20 : -box[1] - 20
|
||||
const x = point[0] + offsetX
|
||||
const y = point[1] + offsetY
|
||||
|
||||
this.position = [x, y]
|
||||
this.params = params
|
||||
},
|
||||
formatter: (params, ticket, callback) => {},
|
||||
},
|
||||
legend: {
|
||||
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [120, 132, 101, 134, 90, 230, 210],
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310],
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 232, 201, 154, 190, 330, 410],
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [320, 332, 301, 334, 390, 330, 320],
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
// init(echarts, theme?:string, opts?:{}, chart => {})
|
||||
// echarts 必填, 非nvue必填,nvue不用填
|
||||
// theme 可选,应用的主题,目前只支持名称,如:'dark'
|
||||
// opts = { // 可选
|
||||
// locale?: string // 从 `5.0.0` 开始支持
|
||||
// }
|
||||
// chart => {} , callback 返回图表实例
|
||||
// setTimeout(()=>{
|
||||
// this.$refs.chart.init(echarts, chart => {
|
||||
// chart.setOption(this.option);
|
||||
// });
|
||||
// },300)
|
||||
this.$refs.chart.init(echarts, (chart) => {
|
||||
chart.setOption(this.option)
|
||||
|
||||
// 监听tooltip显示事件
|
||||
chart.on('showTip', (params) => {
|
||||
this.showTip = true
|
||||
console.log('showTip::')
|
||||
})
|
||||
chart.on('hideTip', (params) => {
|
||||
setTimeout(() => {
|
||||
this.showTip = false
|
||||
}, 300)
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// shadowBlur: 0,
|
||||
textStyle: {
|
||||
textShadowBlur: 0,
|
||||
},
|
||||
renderMode: 'richText',
|
||||
},
|
||||
legend: {
|
||||
data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [1120, 132, 101, 134, 90, 230, 210],
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310],
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 632, 201, 154, 190, 330, 410],
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 332, 301, 334, 390, 330, 320],
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
},
|
||||
],
|
||||
}
|
||||
chart.setOption(option)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
save() {
|
||||
this.$refs.chart.canvasToTempFilePath({
|
||||
success(res) {
|
||||
console.log('res::::', res)
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.customTooltips {
|
||||
position: absolute;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 20rpx;
|
||||
}
|
||||
</style>
|
91
uni_modules/lime-echart/package.json
Normal file
91
uni_modules/lime-echart/package.json
Normal file
@ -0,0 +1,91 @@
|
||||
{
|
||||
"id": "lime-echart",
|
||||
"displayName": "echarts",
|
||||
"version": "0.9.8",
|
||||
"description": "echarts 全端兼容,一款使echarts图表能跑在uniapp各端中的插件, 支持uniapp/uniappx(web,ios,安卓)",
|
||||
"keywords": [
|
||||
"echarts",
|
||||
"canvas",
|
||||
"图表",
|
||||
"可视化"
|
||||
],
|
||||
"repository": "https://gitee.com/liangei/lime-echart",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.4"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y",
|
||||
"app-uvue": "y",
|
||||
"app-harmony": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"echarts": "^5.4.1",
|
||||
"zrender": "^5.4.3"
|
||||
}
|
||||
}
|
407
uni_modules/lime-echart/readme.md
Normal file
407
uni_modules/lime-echart/readme.md
Normal file
@ -0,0 +1,407 @@
|
||||
# echarts 图表 <span style="font-size:16px;">👑👑👑👑👑 <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
|
||||
> 一个基于 JavaScript 的开源可视化图表库 [查看更多](https://limeui.qcoon.cn/#/echart) <br>
|
||||
> 基于 echarts 做了兼容处理,更多示例请访问 [uni示例](https://limeui.qcoon.cn/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html) <br>
|
||||
|
||||
|
||||
## 平台兼容
|
||||
|
||||
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
|
||||
| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
|
||||
| √ | √ | √ | √ | √ | √ | √ |
|
||||
|
||||
|
||||
## 安装
|
||||
- 第一步:在市场导入 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899)
|
||||
- 第二步:选择插件依赖:<br>
|
||||
1、可以选插件内的`echarts`包或自定义包,自定义包[下载地址](https://echarts.apache.org/zh/builder.html)<br>
|
||||
2、或者使用`npm`安装`echarts`
|
||||
|
||||
**注意**
|
||||
* 🔔 echarts 5.3.0及以上
|
||||
* 🔔 如果是 `cli` 项目请下载插件到`src`目录下的`uni_modules`,没有这个目录就创建一个
|
||||
|
||||
|
||||
## 代码演示
|
||||
|
||||
### Vue2
|
||||
- 引入依赖,可以是插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html),也可以是`npm`包
|
||||
|
||||
```html
|
||||
<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef" @finished="init"></l-echart></view>
|
||||
```
|
||||
|
||||
```js
|
||||
// 插件内的 三选一
|
||||
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
|
||||
// 自定义的 三选一 下载后放入项目的路径
|
||||
import * as echarts from 'xxx/echarts.min'
|
||||
// npm包 三选一 需要在控制台 输入命令:npm install echarts
|
||||
import * as echarts from 'echarts'
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
confine: true
|
||||
},
|
||||
legend: {
|
||||
data: ['热度', '正面', '负面']
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 15,
|
||||
top: 40,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#999999'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#999999'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '热度',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
}
|
||||
},
|
||||
data: [300, 270, 340, 344, 300, 320, 310],
|
||||
},
|
||||
{
|
||||
name: '正面',
|
||||
type: 'bar',
|
||||
stack: '总量',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [120, 102, 141, 174, 190, 250, 220]
|
||||
},
|
||||
{
|
||||
name: '负面',
|
||||
type: 'bar',
|
||||
stack: '总量',
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'left'
|
||||
}
|
||||
},
|
||||
data: [-20, -32, -21, -34, -90, -130, -110]
|
||||
}
|
||||
]
|
||||
},
|
||||
};
|
||||
},
|
||||
// 组件能被调用必须是组件的节点已经被渲染到页面上
|
||||
methods: {
|
||||
async init() {
|
||||
// chart 图表实例不能存在data里
|
||||
const chart = await this.$refs.chartRef.init(echarts);
|
||||
chart.setOption(this.option)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vue3
|
||||
- 小程序可以使用`require`引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
|
||||
- `require`仅支持相对路径,不支持路径别名
|
||||
- 非小程序使用 `npm` 包
|
||||
|
||||
|
||||
```html
|
||||
<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef"></l-echart></view>
|
||||
```
|
||||
|
||||
```js
|
||||
// 小程序 二选一
|
||||
// 插件内的 二选一
|
||||
const echarts = require('../../uni_modules/lime-echart/static/echarts.min');
|
||||
// 自定义的 二选一 下载后放入项目的路径
|
||||
const echarts = require('xxx/xxx/echarts');
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// 非小程序
|
||||
// 需要在控制台 输入命令:npm install echarts
|
||||
import * as echarts from 'echarts'
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
const chartRef = ref(null)
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
confine: true
|
||||
},
|
||||
legend: {
|
||||
data: ['热度', '正面', '负面']
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 15,
|
||||
top: 40,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#999999'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#999999'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666666'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '热度',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
}
|
||||
},
|
||||
data: [300, 270, 340, 344, 300, 320, 310],
|
||||
},
|
||||
{
|
||||
name: '正面',
|
||||
type: 'bar',
|
||||
stack: '总量',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [120, 102, 141, 174, 190, 250, 220]
|
||||
},
|
||||
{
|
||||
name: '负面',
|
||||
type: 'bar',
|
||||
stack: '总量',
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'left'
|
||||
}
|
||||
},
|
||||
data: [-20, -32, -21, -34, -90, -130, -110]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
onMounted( ()=>{
|
||||
// 组件能被调用必须是组件的节点已经被渲染到页面上
|
||||
setTimeout(async()=>{
|
||||
if(!chartRef.value) return
|
||||
const myChart = await chartRef.value.init(echarts)
|
||||
myChart.setOption(option)
|
||||
},300)
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Uvue
|
||||
- Uvue和Nvue不需要引入`echarts`,因为它们的实现方式是`webview`
|
||||
- uniapp x需要HBX 4.13以上
|
||||
|
||||
```html
|
||||
<view style="width: 100%; height: 408px;">
|
||||
<l-echart ref="chartRef" @finished="init"></l-echart>
|
||||
</view>
|
||||
```
|
||||
|
||||
```js
|
||||
// @ts-nocheck
|
||||
// #ifdef H5
|
||||
import * as echarts from 'echarts/dist/echarts.esm.js'
|
||||
// #endif
|
||||
const chartRef = ref<LEchartComponentPublicInstance|null>(null);
|
||||
const init = async () => {
|
||||
if(chartRef.value== null) return
|
||||
// #ifdef APP
|
||||
const chart = await chartRef.value!.init(null)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
const chart = await chartRef.value!.init(echarts, null)
|
||||
// #endif
|
||||
chart.setOption(option)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 数据更新
|
||||
- 1、使用 `ref` 可获取`setOption`设置更新
|
||||
- 2、也可以拿到图表实例`chart`设置`myChart.setOption(data)`
|
||||
|
||||
```js
|
||||
// ref
|
||||
this.$refs.chart.setOption(data)
|
||||
|
||||
// 图表实例
|
||||
myChart.setOption(data)
|
||||
```
|
||||
|
||||
## 图表大小
|
||||
- 在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
|
||||
|
||||
```js
|
||||
// 默认获取容器尺寸
|
||||
this.$refs.chart.resize()
|
||||
// 指定尺寸
|
||||
this.$refs.chart.resize({width: 375, height: 375})
|
||||
```
|
||||
|
||||
## 自定义Tooltips
|
||||
- uvue\nvue 不支持
|
||||
由于除H5之外都不存在dom,但又有tooltips个性化的需求,代码就不贴了,看示例吧
|
||||
```
|
||||
代码位于/uni_modules/lime-echart/component/lime-echart
|
||||
```
|
||||
|
||||
|
||||
## 插件标签
|
||||
- 默认 l-echart 为 component
|
||||
- 默认 lime-echart 为 demo
|
||||
```html
|
||||
// 在任意地方使用可查看domo, 代码位于/uni_modules/lime-echart/component/lime-echart
|
||||
<lime-echart></lime-echart>
|
||||
```
|
||||
|
||||
|
||||
## 常见问题
|
||||
- 钉钉小程序 由于没有`measureText`,模拟的`measureText`又无法得到当前字体的`fontWeight`,故可能存在估计不精细的问题
|
||||
- 微信小程序 `2d` 只支持 真机调试2.0
|
||||
- 微信开发工具会出现 `canvas` 不跟随页面的情况,真机不影响
|
||||
- 微信开发工具会出现 `canvas` 层级过高的问题,真机一般不受影响,可以先测只有两个元素的页面看是否会有层级问题。
|
||||
- toolbox 不支持 `saveImage`
|
||||
- echarts 5.3.0 的 lines 不支持 trailLength,故需设置为 `0`
|
||||
- dataZoom H5不要设置 `showDetail`
|
||||
- 如果微信小程序的`tooltip`文字有阴影,可能是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
|
||||
- 如果钉钉小程序上传时报安全问题`Uint8Clamped`,可以向钉钉反馈是安全代码扫描把Uint8Clamped数组错误识别了,也可以在 echarts 文件修改`Uint8Clamped`
|
||||
```js
|
||||
// 找到这段代码把代码中`Uint8Clamped`改成`Uint8_Clamped`,再把下划线去掉,不过直接去掉`Uint8Clamped`也是可行的
|
||||
// ["Int8","Uint8","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e+"Array]"]
|
||||
// 改成如下
|
||||
["Int8","Uint8","Uint8_Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e.replace('_','')+"Array]"]
|
||||
```
|
||||
|
||||
### vue3
|
||||
如果您是使用 **vite + vue3** 非微信小程序可能会遇到`echarts`文件缺少`wx`判断导致无法使用或缺少`tooltip`<br>
|
||||
|
||||
方式一:可以在`echarts.min.js`文件开头增加以下内容,参考插件内的echart.min.js的做法
|
||||
|
||||
```js
|
||||
let global = null
|
||||
let wx = uni
|
||||
```
|
||||
|
||||
方式二:在`vite.config.js`的`define`设置环境
|
||||
|
||||
```js
|
||||
// 或者在`vite.config.js`的`define`设置环境
|
||||
import { defineConfig } from 'vite';
|
||||
import uni from '@dcloudio/vite-plugin-uni';
|
||||
|
||||
const define = {}
|
||||
if(!["mp-weixin", "h5", "web"].includes(process.env.UNI_PLATFORM)) {
|
||||
define['global'] = null
|
||||
define['wx'] = 'uni'
|
||||
}
|
||||
export default defineConfig({
|
||||
plugins: [uni()],
|
||||
define
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --------------- | -------- | ------- | ------------ | ----- |
|
||||
| custom-style | 自定义样式 | `string` | - | - |
|
||||
| type | 指定 canvas 类型 | `string` | `2d` | |
|
||||
| is-disable-scroll | 触摸图表时是否禁止页面滚动 | `boolean` | `false` | |
|
||||
| beforeDelay | 延迟初始化 (毫秒) | `number` | `30` | |
|
||||
| enableHover | PC端使用鼠标悬浮 | `boolean` | `false` | |
|
||||
| landscape | 是否旋转90deg,模拟横屏效果 | `boolean` | `false` | |
|
||||
|
||||
## 事件
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --------------- | --------------- |
|
||||
| init(echarts, chart => {}) | 初始化调用函数,第一个参数是传入`echarts`,第二个参数是回调函数,回调函数的参数是 `chart` 实例 |
|
||||
| setChart(chart => {}) | 已经初始化后,请使用这个方法,是个回调函数,参数是 `chart` 实例 |
|
||||
| setOption(data) | [图表配置项](https://echarts.apache.org/zh/option.html#title),用于更新 ,传递是数据 `option` |
|
||||
| clear() | 清空当前实例,会移除实例中所有的组件和图表。 |
|
||||
| dispose() | 销毁实例 |
|
||||
| showLoading() | 显示加载 |
|
||||
| hideLoading() | 隐藏加载 |
|
||||
| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(opt) | 用于生成图片,与官方使用方法一致,但不需要传`canvasId` |
|
||||
|
||||
|
||||
## 打赏
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||

|
||||

|
1
uni_modules/lime-echart/static/ecStat.min.js
vendored
Normal file
1
uni_modules/lime-echart/static/ecStat.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
61
uni_modules/lime-echart/static/echarts.min.js
vendored
Normal file
61
uni_modules/lime-echart/static/echarts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
uni_modules/lime-echart/static/uni.webview.1.5.5.js
Normal file
1
uni_modules/lime-echart/static/uni.webview.1.5.5.js
Normal file
File diff suppressed because one or more lines are too long
177
uni_modules/lime-echart/static/uvue.html
Normal file
177
uni_modules/lime-echart/static/uvue.html
Normal file
@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
html,
|
||||
body,
|
||||
.canvas {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="canvas" id="limeChart"></div>
|
||||
<script type="text/javascript" src="./uni.webview.1.5.5.js"></script>
|
||||
<script type="text/javascript" src="./echarts.min.js"></script>
|
||||
<script type="text/javascript" src="./ecStat.min.js"></script>
|
||||
<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-liquidfill@latest/dist/echarts-liquidfill.min.js"></script> -->
|
||||
<script>
|
||||
let chart = null;
|
||||
let cache = [];
|
||||
console.log = function() {
|
||||
emit('log', {
|
||||
log: arguments,
|
||||
})
|
||||
}
|
||||
|
||||
function emit(event, data) {
|
||||
postMessage({
|
||||
event,
|
||||
data
|
||||
})
|
||||
cache = []
|
||||
}
|
||||
|
||||
function postMessage(data) {
|
||||
uni.webView.postMessage({
|
||||
data
|
||||
})
|
||||
// window.__uniapp_x_.postMessage(JSON.stringify(data))
|
||||
};
|
||||
|
||||
function stringify(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
return;
|
||||
}
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parse(name, callback, options) {
|
||||
const optionNameReg = /[\w]+\.setOption\(([\w]+\.)?([\w]+)\)/
|
||||
if (optionNameReg.test(callback)) {
|
||||
const optionNames = callback.match(optionNameReg)
|
||||
if (optionNames[1]) {
|
||||
const _this = optionNames[1].split('.')[0]
|
||||
window[_this] = {}
|
||||
window[_this][optionNames[2]] = options
|
||||
return optionNames[2]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function init(callback, options, opts, theme) {
|
||||
if (!chart) {
|
||||
chart = echarts.init(document.getElementById('limeChart'), theme, opts)
|
||||
|
||||
if (options) {
|
||||
chart.setOption(options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function on(data) {
|
||||
if (chart && data.length > 0) {
|
||||
const [type, query] = data
|
||||
const key = `${type}${JSON.stringify(query||'')}`
|
||||
if (query) {
|
||||
chart.on(type, query, function(options) {
|
||||
var obj = {};
|
||||
Object.keys(options).forEach(function(key) {
|
||||
if (key != 'event') {
|
||||
obj[key] = options[key];
|
||||
}
|
||||
});
|
||||
emit(key, {
|
||||
event: key,
|
||||
options: obj,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
chart.on(type, function(options) {
|
||||
var obj = {};
|
||||
Object.keys(options).forEach(function(key) {
|
||||
if (key != 'event') {
|
||||
obj[key] = options[key];
|
||||
}
|
||||
});
|
||||
emit(key, {
|
||||
event: key,
|
||||
options: obj,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setChart(callback, options) {
|
||||
if (!callback) return
|
||||
if (chart && callback && options) {
|
||||
var r = null
|
||||
const name = parse('r', callback, options)
|
||||
if (name) this[name] = options
|
||||
eval(`r = ${callback};`)
|
||||
if (r) {
|
||||
r(chart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setOption(data) {
|
||||
if (chart) chart.setOption(data[0], data[1])
|
||||
}
|
||||
|
||||
function showLoading(data) {
|
||||
if (chart) chart.showLoading(data[0], data[1])
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
if (chart) chart.hideLoading()
|
||||
}
|
||||
|
||||
function clear() {
|
||||
if (chart) chart.clear()
|
||||
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
if (chart) chart.dispose()
|
||||
}
|
||||
|
||||
function resize(size) {
|
||||
if (chart) chart.resize(size)
|
||||
}
|
||||
|
||||
function canvasToTempFilePath(opt) {
|
||||
if (chart) {
|
||||
delete opt.success
|
||||
const src = chart.getDataURL(opt)
|
||||
postMessage({
|
||||
// event: 'file',
|
||||
file: src
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('touchmove', () => {
|
||||
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
42
uni_modules/lime-shared/addUnit/index.ts
Normal file
42
uni_modules/lime-shared/addUnit/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// @ts-nocheck
|
||||
import {isNumeric} from '../isNumeric'
|
||||
import {isDef} from '../isDef'
|
||||
/**
|
||||
* 给一个值添加单位(像素 px)
|
||||
* @param value 要添加单位的值,可以是字符串或数字
|
||||
* @returns 添加了单位的值,如果值为 null 则返回 null
|
||||
*/
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export function addUnit(value?: string | number): string | null {
|
||||
if (!isDef(value)) {
|
||||
return null;
|
||||
}
|
||||
value = String(value); // 将值转换为字符串
|
||||
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
|
||||
return isNumeric(value) ? `${value}px` : value;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
function addUnit(value: string): string
|
||||
function addUnit(value: number): string
|
||||
function addUnit(value: any|null): string|null {
|
||||
if (!isDef(value)) {
|
||||
return null;
|
||||
}
|
||||
value = `${value}` //value.toString(); // 将值转换为字符串
|
||||
|
||||
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
|
||||
return isNumeric(value) ? `${value}px` : value;
|
||||
}
|
||||
export {addUnit}
|
||||
// #endif
|
||||
|
||||
|
||||
// console.log(addUnit(100)); // 输出: "100px"
|
||||
// console.log(addUnit("200")); // 输出: "200px"
|
||||
// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位)
|
||||
// console.log(addUnit()); // 输出: undefined(值为 undefined)
|
||||
// console.log(addUnit(null)); // 输出: undefined(值为 null)
|
82
uni_modules/lime-shared/animation/bezier.ts
Normal file
82
uni_modules/lime-shared/animation/bezier.ts
Normal file
@ -0,0 +1,82 @@
|
||||
export function cubicBezier(p1x : number, p1y : number, p2x : number, p2y : number):(x: number)=> number {
|
||||
const ZERO_LIMIT = 1e-6;
|
||||
// Calculate the polynomial coefficients,
|
||||
// implicit first and last control points are (0,0) and (1,1).
|
||||
const ax = 3 * p1x - 3 * p2x + 1;
|
||||
const bx = 3 * p2x - 6 * p1x;
|
||||
const cx = 3 * p1x;
|
||||
|
||||
const ay = 3 * p1y - 3 * p2y + 1;
|
||||
const by = 3 * p2y - 6 * p1y;
|
||||
const cy = 3 * p1y;
|
||||
|
||||
function sampleCurveDerivativeX(t : number) : number {
|
||||
// `ax t^3 + bx t^2 + cx t` expanded using Horner's rule
|
||||
return (3 * ax * t + 2 * bx) * t + cx;
|
||||
}
|
||||
|
||||
function sampleCurveX(t : number) : number {
|
||||
return ((ax * t + bx) * t + cx) * t;
|
||||
}
|
||||
|
||||
function sampleCurveY(t : number) : number {
|
||||
return ((ay * t + by) * t + cy) * t;
|
||||
}
|
||||
|
||||
// Given an x value, find a parametric value it came from.
|
||||
function solveCurveX(x : number) : number {
|
||||
let t2 = x;
|
||||
let derivative : number;
|
||||
let x2 : number;
|
||||
|
||||
// https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
|
||||
// first try a few iterations of Newton's method -- normally very fast.
|
||||
// http://en.wikipedia.org/wikiNewton's_method
|
||||
for (let i = 0; i < 8; i++) {
|
||||
// f(t) - x = 0
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
derivative = sampleCurveDerivativeX(t2);
|
||||
// == 0, failure
|
||||
/* istanbul ignore if */
|
||||
if (Math.abs(derivative) < ZERO_LIMIT) {
|
||||
break;
|
||||
}
|
||||
t2 -= x2 / derivative;
|
||||
}
|
||||
|
||||
// Fall back to the bisection method for reliability.
|
||||
// bisection
|
||||
// http://en.wikipedia.org/wiki/Bisection_method
|
||||
let t1 = 1;
|
||||
/* istanbul ignore next */
|
||||
let t0 = 0;
|
||||
|
||||
/* istanbul ignore next */
|
||||
t2 = x;
|
||||
/* istanbul ignore next */
|
||||
while (t1 > t0) {
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
if (x2 > 0) {
|
||||
t1 = t2;
|
||||
} else {
|
||||
t0 = t2;
|
||||
}
|
||||
t2 = (t1 + t0) / 2;
|
||||
}
|
||||
|
||||
// Failure
|
||||
return t2;
|
||||
}
|
||||
|
||||
return function (x : number) : number {
|
||||
return sampleCurveY(solveCurveX(x));
|
||||
}
|
||||
|
||||
// return solve;
|
||||
}
|
3
uni_modules/lime-shared/animation/ease.ts
Normal file
3
uni_modules/lime-shared/animation/ease.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import {cubicBezier} from './bezier';
|
||||
export let ease = cubicBezier(0.25, 0.1, 0.25, 1);
|
||||
export let linear = cubicBezier(0,0,1,1);
|
10
uni_modules/lime-shared/animation/index.ts
Normal file
10
uni_modules/lime-shared/animation/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
100
uni_modules/lime-shared/animation/useTransition.ts
Normal file
100
uni_modules/lime-shared/animation/useTransition.ts
Normal file
@ -0,0 +1,100 @@
|
||||
// @ts-nocheck
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import { ease, linear } from './ease';
|
||||
import { Timeline, Animation } from './';
|
||||
export type UseTransitionOptions = {
|
||||
duration ?: number
|
||||
immediate ?: boolean
|
||||
context ?: ComponentPublicInstance
|
||||
}
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import { ref, watch, type Ref } from '@/uni_modules/lime-shared/vue'
|
||||
|
||||
export function useTransition(percent : Ref<number>|(() => number), options : UseTransitionOptions) : Ref<number> {
|
||||
const current = ref(0)
|
||||
const { immediate, duration = 300 } = options
|
||||
let tl:Timeline|null = null;
|
||||
let timer = -1
|
||||
const isFunction = typeof percent === 'function'
|
||||
watch(isFunction ? percent : () => percent.value, (v) => {
|
||||
if(tl == null){
|
||||
tl = new Timeline()
|
||||
}
|
||||
tl.start();
|
||||
tl.add(
|
||||
new Animation(
|
||||
current.value,
|
||||
v,
|
||||
duration,
|
||||
0,
|
||||
ease,
|
||||
nowValue => {
|
||||
current.value = nowValue
|
||||
clearTimeout(timer)
|
||||
if(current.value == v){
|
||||
timer = setTimeout(()=>{
|
||||
tl?.pause();
|
||||
tl = null
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}, { immediate })
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
type UseTransitionReturnType = Ref<number>
|
||||
export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType {
|
||||
const outputRef : Ref<number> = ref(0)
|
||||
const immediate = options.immediate ?? false
|
||||
const duration = options.duration ?? 300
|
||||
const context = options.context //as ComponentPublicInstance | null
|
||||
let tl:Timeline|null = null;
|
||||
let timer = -1
|
||||
const watchFunc = (v : number) => {
|
||||
if(tl == null){
|
||||
tl = new Timeline()
|
||||
}
|
||||
tl!.start();
|
||||
tl!.add(
|
||||
new Animation(
|
||||
outputRef.value,
|
||||
v,
|
||||
duration,
|
||||
0,
|
||||
ease,
|
||||
nowValue => {
|
||||
outputRef.value = nowValue
|
||||
clearTimeout(timer)
|
||||
if(outputRef.value == v){
|
||||
timer = setTimeout(()=>{
|
||||
tl?.pause();
|
||||
tl = null
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (context != null && typeof source == 'string') {
|
||||
context.$watch(source, watchFunc, { immediate } as WatchOptions)
|
||||
} else if(typeof source == 'function'){
|
||||
watch(source, watchFunc, { immediate })
|
||||
} else if(source instanceof Ref<number>){
|
||||
watch(source as Ref<number>, watchFunc, { immediate })
|
||||
}
|
||||
|
||||
const stop = ()=>{
|
||||
|
||||
}
|
||||
return outputRef //as UseTransitionReturnType
|
||||
}
|
||||
|
||||
// #endif
|
119
uni_modules/lime-shared/animation/uvue.uts
Normal file
119
uni_modules/lime-shared/animation/uvue.uts
Normal file
@ -0,0 +1,119 @@
|
||||
import { raf, cancelRaf} from '../raf'
|
||||
// @ts-nocheck
|
||||
export class Timeline {
|
||||
state : string
|
||||
animations : Set<Animation> = new Set<Animation>()
|
||||
delAnimations : Animation[] = []
|
||||
startTimes : Map<Animation, number> = new Map<Animation, number>()
|
||||
pauseTime : number = 0
|
||||
pauseStart : number = Date.now()
|
||||
tickHandler : number = 0
|
||||
tickHandlers : number[] = []
|
||||
tick : (() => void) | null = null
|
||||
constructor() {
|
||||
this.state = 'Initiated';
|
||||
}
|
||||
start() {
|
||||
if (!(this.state == 'Initiated')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
let startTime = Date.now();
|
||||
this.pauseTime = 0;
|
||||
this.tick = () => {
|
||||
let now = Date.now();
|
||||
this.animations.forEach((animation : Animation) => {
|
||||
let t:number;
|
||||
const ani = this.startTimes.get(animation)
|
||||
if (ani == null) return
|
||||
if (ani < startTime) {
|
||||
t = now - startTime - animation.delay - this.pauseTime;
|
||||
} else {
|
||||
t = now - ani - animation.delay - this.pauseTime;
|
||||
}
|
||||
if (t > animation.duration) {
|
||||
this.delAnimations.push(animation)
|
||||
// 不能在 foreach 里面 对 集合进行删除操作
|
||||
// this.animations.delete(animation);
|
||||
t = animation.duration;
|
||||
}
|
||||
if (t > 0) animation.run(t);
|
||||
})
|
||||
// 不能在 foreach 里面 对 集合进行删除操作
|
||||
while (this.delAnimations.length > 0) {
|
||||
const animation = this.delAnimations.pop();
|
||||
if (animation == null) return
|
||||
this.animations.delete(animation);
|
||||
}
|
||||
// cancelAnimationFrame(this.tickHandler);
|
||||
if (this.state != 'Started') return
|
||||
|
||||
this.tickHandler = raf(()=>{
|
||||
this.tick!()
|
||||
})
|
||||
|
||||
this.tickHandlers.push(this.tickHandler)
|
||||
}
|
||||
if(this.tick != null) {
|
||||
this.tick!()
|
||||
}
|
||||
|
||||
}
|
||||
pause() {
|
||||
if (!(this.state === 'Started')) return;
|
||||
this.state = 'Paused';
|
||||
this.pauseStart = Date.now();
|
||||
cancelRaf(this.tickHandler);
|
||||
// cancelRaf(this.tickHandler);
|
||||
}
|
||||
resume() {
|
||||
if (!(this.state === 'Paused')) return;
|
||||
this.state = 'Started';
|
||||
this.pauseTime += Date.now() - this.pauseStart;
|
||||
this.tick!();
|
||||
}
|
||||
reset() {
|
||||
this.pause();
|
||||
this.state = 'Initiated';
|
||||
this.pauseTime = 0;
|
||||
this.pauseStart = 0;
|
||||
this.animations.clear()
|
||||
this.delAnimations.clear()
|
||||
this.startTimes.clear()
|
||||
this.tickHandler = 0;
|
||||
}
|
||||
add(animation : Animation, startTime ?: number | null) {
|
||||
if (startTime == null) startTime = Date.now();
|
||||
this.animations.add(animation);
|
||||
this.startTimes.set(animation, startTime);
|
||||
}
|
||||
}
|
||||
|
||||
export class Animation {
|
||||
startValue : number
|
||||
endValue : number
|
||||
duration : number
|
||||
timingFunction : (t : number) => number
|
||||
delay : number
|
||||
template : (t : number) => void
|
||||
constructor(
|
||||
startValue : number,
|
||||
endValue : number,
|
||||
duration : number,
|
||||
delay : number,
|
||||
timingFunction : (t : number) => number,
|
||||
template : (v : number) => void) {
|
||||
this.startValue = startValue;
|
||||
this.endValue = endValue;
|
||||
this.duration = duration;
|
||||
this.timingFunction = timingFunction;
|
||||
this.delay = delay;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
run(time : number) {
|
||||
let range = this.endValue - this.startValue;
|
||||
let progress = time / this.duration
|
||||
if(progress != 1) progress = this.timingFunction(progress)
|
||||
this.template(this.startValue + range * progress)
|
||||
}
|
||||
}
|
123
uni_modules/lime-shared/animation/vue.ts
Normal file
123
uni_modules/lime-shared/animation/vue.ts
Normal file
@ -0,0 +1,123 @@
|
||||
// @ts-nocheck
|
||||
const TICK = Symbol('tick');
|
||||
const TICK_HANDLER = Symbol('tick-handler');
|
||||
const ANIMATIONS = Symbol('animations');
|
||||
const START_TIMES = Symbol('start-times');
|
||||
const PAUSE_START = Symbol('pause-start');
|
||||
const PAUSE_TIME = Symbol('pause-time');
|
||||
const _raf = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(cb: Function) {return setTimeout(cb, 1000/60)}
|
||||
const _caf = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame: function(id: any) {clearTimeout(id)}
|
||||
|
||||
// const TICK = 'tick';
|
||||
// const TICK_HANDLER = 'tick-handler';
|
||||
// const ANIMATIONS = 'animations';
|
||||
// const START_TIMES = 'start-times';
|
||||
// const PAUSE_START = 'pause-start';
|
||||
// const PAUSE_TIME = 'pause-time';
|
||||
// const _raf = function(callback):number|null {return setTimeout(callback, 1000/60)}
|
||||
// const _caf = function(id: number):void {clearTimeout(id)}
|
||||
|
||||
export class Timeline {
|
||||
state: string
|
||||
constructor() {
|
||||
this.state = 'Initiated';
|
||||
this[ANIMATIONS] = new Set();
|
||||
this[START_TIMES] = new Map();
|
||||
}
|
||||
start() {
|
||||
if (!(this.state === 'Initiated')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
let startTime = Date.now();
|
||||
this[PAUSE_TIME] = 0;
|
||||
this[TICK] = () => {
|
||||
let now = Date.now();
|
||||
this[ANIMATIONS].forEach((animation) => {
|
||||
let t: number;
|
||||
if (this[START_TIMES].get(animation) < startTime) {
|
||||
t = now - startTime - animation.delay - this[PAUSE_TIME];
|
||||
} else {
|
||||
t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME];
|
||||
}
|
||||
|
||||
if (t > animation.duration) {
|
||||
this[ANIMATIONS].delete(animation);
|
||||
t = animation.duration;
|
||||
}
|
||||
if (t > 0) animation.run(t);
|
||||
})
|
||||
// for (let animation of this[ANIMATIONS]) {
|
||||
// let t: number;
|
||||
// console.log('animation', animation)
|
||||
// if (this[START_TIMES].get(animation) < startTime) {
|
||||
// t = now - startTime - animation.delay - this[PAUSE_TIME];
|
||||
// } else {
|
||||
// t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME];
|
||||
// }
|
||||
|
||||
// if (t > animation.duration) {
|
||||
// this[ANIMATIONS].delete(animation);
|
||||
// t = animation.duration;
|
||||
// }
|
||||
// if (t > 0) animation.run(t);
|
||||
// }
|
||||
this[TICK_HANDLER] = _raf(this[TICK]);
|
||||
};
|
||||
this[TICK]();
|
||||
}
|
||||
pause() {
|
||||
if (!(this.state === 'Started')) return;
|
||||
this.state = 'Paused';
|
||||
|
||||
this[PAUSE_START] = Date.now();
|
||||
_caf(this[TICK_HANDLER]);
|
||||
}
|
||||
resume() {
|
||||
if (!(this.state === 'Paused')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
this[PAUSE_TIME] += Date.now() - this[PAUSE_START];
|
||||
this[TICK]();
|
||||
}
|
||||
reset() {
|
||||
this.pause();
|
||||
this.state = 'Initiated';
|
||||
this[PAUSE_TIME] = 0;
|
||||
this[PAUSE_START] = 0;
|
||||
this[ANIMATIONS] = new Set();
|
||||
this[START_TIMES] = new Map();
|
||||
this[TICK_HANDLER] = null;
|
||||
}
|
||||
add(animation: any, startTime?: number) {
|
||||
if (arguments.length < 2) startTime = Date.now();
|
||||
this[ANIMATIONS].add(animation);
|
||||
this[START_TIMES].set(animation, startTime);
|
||||
}
|
||||
}
|
||||
|
||||
export class Animation {
|
||||
startValue: number
|
||||
endValue: number
|
||||
duration: number
|
||||
timingFunction: (t: number) => number
|
||||
delay: number
|
||||
template: (t: number) => void
|
||||
constructor(startValue: number, endValue: number, duration: number, delay: number, timingFunction: (t: number) => number, template: (v: number) => void) {
|
||||
timingFunction = timingFunction || (v => v);
|
||||
template = template || (v => v);
|
||||
|
||||
this.startValue = startValue;
|
||||
this.endValue = endValue;
|
||||
this.duration = duration;
|
||||
this.timingFunction = timingFunction;
|
||||
this.delay = delay;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
run(time: number) {
|
||||
let range = this.endValue - this.startValue;
|
||||
let progress = time / this.duration
|
||||
if(progress != 1) progress = this.timingFunction(progress)
|
||||
this.template(this.startValue + range * progress)
|
||||
}
|
||||
}
|
3888
uni_modules/lime-shared/areaData/city-china.json
Normal file
3888
uni_modules/lime-shared/areaData/city-china.json
Normal file
File diff suppressed because it is too large
Load Diff
71
uni_modules/lime-shared/areaData/index.ts
Normal file
71
uni_modules/lime-shared/areaData/index.ts
Normal file
@ -0,0 +1,71 @@
|
||||
// @ts-nocheck
|
||||
import _areaList from './city-china.json';
|
||||
export const areaList = _areaList
|
||||
// #ifndef UNI-APP-X
|
||||
type UTSJSONObject = Record<string, string>
|
||||
// #endif
|
||||
// #ifdef UNI-APP-X
|
||||
type Object = UTSJSONObject
|
||||
// #endif
|
||||
type AreaList = {
|
||||
province_list : Map<string, string>;
|
||||
city_list : Map<string, string>;
|
||||
county_list : Map<string, string>;
|
||||
}
|
||||
// type CascaderOption = {
|
||||
// text : string;
|
||||
// value : string;
|
||||
// children ?: CascaderOption[];
|
||||
// };
|
||||
|
||||
const makeOption = (
|
||||
label : string,
|
||||
value : string,
|
||||
children ?: UTSJSONObject[],
|
||||
) : UTSJSONObject => ({
|
||||
label,
|
||||
value,
|
||||
children,
|
||||
});
|
||||
|
||||
|
||||
|
||||
export function useCascaderAreaData() : UTSJSONObject[] {
|
||||
const city = areaList['city_list'] as UTSJSONObject
|
||||
const county = areaList['county_list'] as UTSJSONObject
|
||||
const province = areaList['province_list'] as UTSJSONObject
|
||||
const provinceMap = new Map<string, UTSJSONObject>();
|
||||
Object.keys(province).forEach((code) => {
|
||||
provinceMap.set(code.slice(0, 2), makeOption(`${province[code]}`, code, []));
|
||||
});
|
||||
|
||||
const cityMap = new Map<string, UTSJSONObject>();
|
||||
|
||||
Object.keys(city).forEach((code) => {
|
||||
const option = makeOption(`${city[code]}`, code, []);
|
||||
cityMap.set(code.slice(0, 4), option);
|
||||
|
||||
const _province = provinceMap.get(code.slice(0, 2));
|
||||
if (_province != null) {
|
||||
(_province['children'] as UTSJSONObject[]).push(option)
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(county).forEach((code) => {
|
||||
const _city = cityMap.get(code.slice(0, 4));
|
||||
if (_city != null) {
|
||||
(_city['children'] as UTSJSONObject[]).push(makeOption(`${county[code]}`, code, null));
|
||||
}
|
||||
});
|
||||
|
||||
// #ifndef APP-ANDROID || APP-IOS
|
||||
return Array.from(provinceMap.values());
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID || APP-IOS
|
||||
const obj : UTSJSONObject[] = []
|
||||
provinceMap.forEach((value, code) => {
|
||||
obj.push(value)
|
||||
})
|
||||
return obj
|
||||
// #endif
|
||||
}
|
8
uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
8
uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
10
uni_modules/lime-shared/arrayBufferToFile/uvue.uts
Normal file
10
uni_modules/lime-shared/arrayBufferToFile/uvue.uts
Normal file
@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
// import {platform} from '../platform'
|
||||
/**
|
||||
* buffer转路径
|
||||
* @param {Object} buffer
|
||||
*/
|
||||
// @ts-nocheck
|
||||
export function arrayBufferToFile(buffer: ArrayBuffer, name?: string, format?:string):Promise<(File|string)> {
|
||||
console.error('[arrayBufferToFile] 当前环境不支持')
|
||||
}
|
63
uni_modules/lime-shared/arrayBufferToFile/vue.ts
Normal file
63
uni_modules/lime-shared/arrayBufferToFile/vue.ts
Normal file
@ -0,0 +1,63 @@
|
||||
// @ts-nocheck
|
||||
import {platform} from '../platform'
|
||||
/**
|
||||
* buffer转路径
|
||||
* @param {Object} buffer
|
||||
*/
|
||||
// @ts-nocheck
|
||||
export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${fileName}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: buffer,
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
const file = new File([buffer], name, {
|
||||
type: format,
|
||||
});
|
||||
resolve(file)
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
const base64 = uni.arrayBufferToBase64(buffer)
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
const filePath = `_doc/uniapp_temp/${fileNmae}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
13
uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
13
uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
// 未完成
|
||||
export function base64ToArrayBuffer(base64 : string) {
|
||||
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
|
||||
if (!format) {
|
||||
new Error('ERROR_BASE64SRC_PARSE')
|
||||
}
|
||||
if(uni.base64ToArrayBuffer) {
|
||||
return uni.base64ToArrayBuffer(bodyData)
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
9
uni_modules/lime-shared/base64ToPath/index.ts
Normal file
9
uni_modules/lime-shared/base64ToPath/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
22
uni_modules/lime-shared/base64ToPath/uvue.uts
Normal file
22
uni_modules/lime-shared/base64ToPath/uvue.uts
Normal file
@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils'
|
||||
|
||||
/**
|
||||
* base64转路径
|
||||
* @param {Object} base64
|
||||
*/
|
||||
export function base64ToPath(base64: string, filename: string | null = null):Promise<string> {
|
||||
return new Promise((resolve,reject) => {
|
||||
processFile({
|
||||
type: 'toDataURL',
|
||||
path: base64,
|
||||
filename,
|
||||
success(res: string){
|
||||
resolve(res)
|
||||
},
|
||||
fail(err){
|
||||
reject(err)
|
||||
}
|
||||
} as ProcessFileOptions)
|
||||
})
|
||||
}
|
75
uni_modules/lime-shared/base64ToPath/vue.ts
Normal file
75
uni_modules/lime-shared/base64ToPath/vue.ts
Normal file
@ -0,0 +1,75 @@
|
||||
// @ts-nocheck
|
||||
import {platform} from '../platform'
|
||||
/**
|
||||
* base64转路径
|
||||
* @param {Object} base64
|
||||
*/
|
||||
export function base64ToPath(base64: string, filename?: string):Promise<string> {
|
||||
const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${name}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: base64.split(',')[1],
|
||||
encoding: 'base64',
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// mime类型
|
||||
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
|
||||
//base64 解码
|
||||
let byteString = atob(base64.split(',')[1]);
|
||||
//创建缓冲数组
|
||||
let arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
//创建视图
|
||||
let intArray = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
intArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
resolve(URL.createObjectURL(new Blob([intArray], {
|
||||
type: mimeString
|
||||
})))
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
const filePath = `_doc/uniapp_temp/${name}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
21
uni_modules/lime-shared/camelCase/index.ts
Normal file
21
uni_modules/lime-shared/camelCase/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 将字符串转换为 camelCase 或 PascalCase 风格的命名约定
|
||||
* @param str 要转换的字符串
|
||||
* @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function camelCase(str: string, isPascalCase: boolean = false): string {
|
||||
// 将字符串分割成单词数组
|
||||
let words: string[] = str.split(/[\s_-]+/);
|
||||
|
||||
// 将数组中的每个单词首字母大写(除了第一个单词)
|
||||
let camelCased: string[] = words.map((word, index):string => {
|
||||
if (index == 0 && !isPascalCase) {
|
||||
return word.toLowerCase(); // 第一个单词全小写
|
||||
}
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
});
|
||||
|
||||
// 将数组中的单词拼接成一个字符串
|
||||
return camelCased.join('');
|
||||
};
|
67
uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
67
uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
@ -0,0 +1,67 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
|
||||
// #ifdef MP-ALIPAY
|
||||
interface My {
|
||||
SDKVersion: string
|
||||
}
|
||||
declare var my: My
|
||||
// #endif
|
||||
|
||||
function compareVersion(v1:string, v2:string) {
|
||||
let a1 = v1.split('.');
|
||||
let a2 = v2.split('.');
|
||||
const len = Math.max(a1.length, a2.length);
|
||||
|
||||
while (a1.length < len) {
|
||||
a1.push('0');
|
||||
}
|
||||
while (a2.length < len) {
|
||||
a2.push('0');
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(a1[i], 10);
|
||||
const num2 = parseInt(a2[i], 10);
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1;
|
||||
}
|
||||
if (num1 < num2) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function gte(version: string) {
|
||||
let {SDKVersion} = uni.getSystemInfoSync();
|
||||
// #ifdef MP-ALIPAY
|
||||
SDKVersion = my.SDKVersion
|
||||
// #endif
|
||||
return compareVersion(SDKVersion, version) >= 0;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
/** 环境是否支持canvas 2d */
|
||||
export function canIUseCanvas2d(): boolean {
|
||||
// #ifdef MP-WEIXIN
|
||||
return gte('2.9.0');
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return gte('2.7.0');
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
return gte('1.78.0');
|
||||
// #endif
|
||||
// #ifdef UNI-APP-X && WEB || UNI-APP-X && APP
|
||||
return true;
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO
|
||||
return false
|
||||
// #endif
|
||||
|
||||
}
|
111
uni_modules/lime-shared/capitalizedAmount/index.ts
Normal file
111
uni_modules/lime-shared/capitalizedAmount/index.ts
Normal file
@ -0,0 +1,111 @@
|
||||
// @ts-nocheck
|
||||
import { isString } from "../isString";
|
||||
import { isNumber } from "../isNumber";
|
||||
/**
|
||||
* 将金额转换为中文大写形式
|
||||
* @param {number | string} amount - 需要转换的金额,可以是数字或字符串
|
||||
* @returns {string} 转换后的中文大写金额
|
||||
*/
|
||||
function capitalizedAmount(amount : number) : string
|
||||
function capitalizedAmount(amount : string) : string
|
||||
function capitalizedAmount(amount : any | null) : string {
|
||||
try {
|
||||
let _amountStr :string;
|
||||
let _amountNum :number = 0;
|
||||
// 如果输入是字符串,先将其转换为数字,并去除逗号
|
||||
if (typeof amount == 'string') {
|
||||
_amountNum = parseFloat((amount as string).replace(/,/g, ''));
|
||||
}
|
||||
if(isNumber(amount)) {
|
||||
_amountNum = amount as number
|
||||
}
|
||||
// 判断输入是否为有效的金额 || isNaN(amount)
|
||||
if (amount == null) throw new Error('不是有效的金额!');
|
||||
|
||||
let result = '';
|
||||
|
||||
// 处理负数情况
|
||||
if (_amountNum < 0) {
|
||||
result = '欠';
|
||||
_amountNum = Math.abs(_amountNum);
|
||||
}
|
||||
|
||||
// 金额不能超过千亿以上
|
||||
if (_amountNum >= 10e11) throw new Error('计算金额过大!');
|
||||
|
||||
// 保留两位小数并转换为字符串
|
||||
_amountStr = _amountNum.toFixed(2);
|
||||
|
||||
// 定义数字、单位和小数单位的映射
|
||||
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
const units = ['', '拾', '佰', '仟'];
|
||||
const bigUnits = ['', '万', '亿'];
|
||||
const decimalUnits = ['角', '分'];
|
||||
|
||||
// 分离整数部分和小数部分
|
||||
const amountArray = _amountStr.split('.');
|
||||
let integerPart = amountArray[0]; // string| number[]
|
||||
const decimalPart = amountArray[1];
|
||||
|
||||
// 处理整数部分
|
||||
if (integerPart != '0') {
|
||||
let _integerPart = integerPart.split('').map((item):number => parseInt(item));
|
||||
|
||||
// 将整数部分按四位一级进行分组
|
||||
const levels = _integerPart.reverse().reduce((prev:string[][], item, index):string[][] => {
|
||||
// const level = prev?.[0]?.length < 4 ? prev[0] : [];
|
||||
const level = prev.length > 0 && prev[0].length < 4 ? prev[0]: []
|
||||
|
||||
const value = item == 0 ? digits[item] : digits[item] + units[index % 4];
|
||||
|
||||
level.unshift(value);
|
||||
|
||||
if (level.length == 1) {
|
||||
prev.unshift(level);
|
||||
} else {
|
||||
prev[0] = level;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, [] as string[][]);
|
||||
// 将分组后的整数部分转换为中文大写形式
|
||||
result += levels.reduce((prev, item, index):string => {
|
||||
let _level = bigUnits[levels.length - index - 1];
|
||||
let _item = item.join('').replace(/(零)\1+/g, '$1');
|
||||
|
||||
if (_item == '零') {
|
||||
_level = '';
|
||||
_item = '';
|
||||
} else if (_item.endsWith('零')) {
|
||||
_item = _item.slice(0, _item.length - 1);
|
||||
}
|
||||
|
||||
return prev + _item + _level;
|
||||
}, '');
|
||||
} else {
|
||||
result += '零';
|
||||
}
|
||||
|
||||
// 添加元
|
||||
result += '元';
|
||||
|
||||
// 处理小数部分
|
||||
if (decimalPart != '00') {
|
||||
if (result == '零元') result = '';
|
||||
|
||||
for (let i = 0; i < decimalPart.length; i++) {
|
||||
const digit = parseInt(decimalPart.charAt(i));
|
||||
|
||||
if (digit != 0) {
|
||||
result += digits[digit] + decimalUnits[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += '整';
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error : Error) {
|
||||
return error.message;
|
||||
}
|
||||
};
|
59
uni_modules/lime-shared/changelog.md
Normal file
59
uni_modules/lime-shared/changelog.md
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
## 0.2.7(2025-01-17)
|
||||
- fix: 针对canvas 平台判断优化
|
||||
## 0.2.6(2025-01-09)
|
||||
- feat: 增加`areaData`中国省市区数据
|
||||
## 0.2.5(2025-01-07)
|
||||
- fix: animation在app上类型问题
|
||||
## 0.2.4(2025-01-04)
|
||||
- feat: getRect类型问题
|
||||
## 0.2.3(2025-01-01)
|
||||
- chore: unitConvert使用uni.rpx2px
|
||||
## 0.2.2(2024-12-11)
|
||||
- chore: 动画使用`requestAnimationFrame`
|
||||
## 0.2.1(2024-11-20)
|
||||
- feat: 增加`characterLimit`
|
||||
## 0.2.0(2024-11-14)
|
||||
- fix: vue2的类型问题
|
||||
## 0.1.9(2024-11-14)
|
||||
- feat: 增加`shuffle`
|
||||
## 0.1.8(2024-10-08)
|
||||
- fix: vue2 条件编译 // #ifdef APP-IOS || APP-ANDROID 会生效
|
||||
## 0.1.7(2024-09-23)
|
||||
- fix: raf 类型跟随版本变更
|
||||
## 0.1.6(2024-07-24)
|
||||
- fix: vue2 app ts需要明确的后缀,所有补全
|
||||
- chore: 减少依赖
|
||||
## 0.1.5(2024-07-21)
|
||||
- feat: 删除 Hooks
|
||||
- feat: 兼容uniappx
|
||||
## 0.1.4(2023-09-05)
|
||||
- feat: 增加 Hooks `useIntersectionObserver`
|
||||
- feat: 增加 `floatAdd`
|
||||
- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译
|
||||
## 0.1.3(2023-08-13)
|
||||
- feat: 增加 `camelCase`
|
||||
## 0.1.2(2023-07-17)
|
||||
- feat: 增加 `getClassStr`
|
||||
## 0.1.1(2023-07-06)
|
||||
- feat: 增加 `isNumeric`, 区别于 `isNumber`
|
||||
## 0.1.0(2023-06-30)
|
||||
- fix: `clamp`忘记导出了
|
||||
## 0.0.9(2023-06-27)
|
||||
- feat: 增加`arrayBufferToFile`
|
||||
## 0.0.8(2023-06-19)
|
||||
- feat: 增加`createAnimation`、`clamp`
|
||||
## 0.0.7(2023-06-08)
|
||||
- chore: 更新注释
|
||||
## 0.0.6(2023-06-08)
|
||||
- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖
|
||||
## 0.0.5(2023-06-03)
|
||||
- chore: 更新注释
|
||||
## 0.0.4(2023-05-22)
|
||||
- feat: 增加`range`,`exif`,`selectComponent`
|
||||
## 0.0.3(2023-05-08)
|
||||
- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
|
||||
## 0.0.2(2023-05-05)
|
||||
- chore: 更新文档
|
||||
## 0.0.1(2023-05-05)
|
||||
- 无
|
63
uni_modules/lime-shared/characterLimit/index.ts
Normal file
63
uni_modules/lime-shared/characterLimit/index.ts
Normal file
@ -0,0 +1,63 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 计算字符串字符的长度并可以截取字符串。
|
||||
* @param char 传入字符串(maxcharacter条件下,一个汉字表示两个字符)
|
||||
* @param max 规定最大字符串长度
|
||||
* @returns 当没有传入maxCharacter/maxLength 时返回字符串字符长度,当传入maxCharacter/maxLength时返回截取之后的字符串和长度。
|
||||
*/
|
||||
export type CharacterLengthResult = {
|
||||
length : number;
|
||||
characters : string;
|
||||
}
|
||||
// #ifdef APP-ANDROID
|
||||
type ChartType = any
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
type ChartType = string | number
|
||||
// #endif
|
||||
|
||||
export function characterLimit(type : string, char : ChartType, max : number) : CharacterLengthResult {
|
||||
const str = `${char}`;
|
||||
|
||||
if (str.length == 0) {
|
||||
return {
|
||||
length: 0,
|
||||
characters: '',
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
|
||||
if (type == 'maxcharacter') {
|
||||
let len = 0;
|
||||
for (let i = 0; i < str.length; i += 1) {
|
||||
let currentStringLength : number// = 0;
|
||||
const code = str.charCodeAt(i)!
|
||||
if (code > 127 || code == 94) {
|
||||
currentStringLength = 2;
|
||||
} else {
|
||||
currentStringLength = 1;
|
||||
}
|
||||
if (len + currentStringLength > max) {
|
||||
return {
|
||||
length: len,
|
||||
characters: str.slice(0, i),
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
len += currentStringLength;
|
||||
}
|
||||
return {
|
||||
length: len,
|
||||
characters: str,
|
||||
} as CharacterLengthResult
|
||||
} else if (type == 'maxlength') {
|
||||
const length = str.length > max ? max : str.length;
|
||||
return {
|
||||
length: length,
|
||||
characters: str.slice(0, length),
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
|
||||
return {
|
||||
length: str.length,
|
||||
characters: str,
|
||||
} as CharacterLengthResult
|
||||
};
|
16
uni_modules/lime-shared/clamp/index.ts
Normal file
16
uni_modules/lime-shared/clamp/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 将一个值限制在指定的范围内
|
||||
* @param val 要限制的值
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @returns 限制后的值
|
||||
*/
|
||||
export function clamp(val: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
|
||||
// console.log(clamp(5 ,0, 10)); // 输出: 5(在范围内,不做更改)
|
||||
// console.log(clamp(-5 ,0, 10)); // 输出: 0(小于最小值,被限制为最小值)
|
||||
// console.log(clamp(15 ,0, 10)); // 输出: 10(大于最大值,被限制为最大值)
|
10
uni_modules/lime-shared/cloneDeep/index.ts
Normal file
10
uni_modules/lime-shared/cloneDeep/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
17
uni_modules/lime-shared/cloneDeep/uvue.ts
Normal file
17
uni_modules/lime-shared/cloneDeep/uvue.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj: any): T {
|
||||
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
|
||||
// if(['number', 'string'].includes(typeof obj) || Array.isArray(obj)){
|
||||
// return obj as T
|
||||
// }
|
||||
if(typeof obj == 'object'){
|
||||
return JSON.parse(JSON.stringify(obj as T)) as T;
|
||||
}
|
||||
return obj as T
|
||||
}
|
||||
|
103
uni_modules/lime-shared/cloneDeep/vue.ts
Normal file
103
uni_modules/lime-shared/cloneDeep/vue.ts
Normal file
@ -0,0 +1,103 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj: any): T {
|
||||
// 如果传入的对象为空,返回空
|
||||
if (obj === null) {
|
||||
return null as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象
|
||||
if (obj instanceof Set) {
|
||||
return new Set([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象
|
||||
if (obj instanceof Map) {
|
||||
return new Map([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值
|
||||
if (obj instanceof WeakMap) {
|
||||
let weakMap = new WeakMap();
|
||||
weakMap = obj;
|
||||
return weakMap as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值
|
||||
if (obj instanceof WeakSet) {
|
||||
let weakSet = new WeakSet();
|
||||
weakSet = obj;
|
||||
return weakSet as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象
|
||||
if (obj instanceof RegExp) {
|
||||
return new RegExp(obj) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 undefined 类型,则返回 undefined
|
||||
if (typeof obj === 'undefined') {
|
||||
return undefined as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(cloneDeep) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆
|
||||
if (typeof obj === 'object') {
|
||||
const newObj: any = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
newObj[key] = cloneDeep(value);
|
||||
}
|
||||
const symbolKeys = Object.getOwnPropertySymbols(obj);
|
||||
for (const key of symbolKeys) {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 示例使用
|
||||
|
||||
// // 克隆一个对象
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
// const clonedObj = cloneDeep(obj);
|
||||
|
||||
// console.log(clonedObj); // 输出: { name: 'John', age: 30 }
|
||||
// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的)
|
||||
|
||||
// // 克隆一个数组
|
||||
// const arr = [1, 2, 3];
|
||||
// const clonedArr = cloneDeep(arr);
|
||||
|
||||
// console.log(clonedArr); // 输出: [1, 2, 3]
|
||||
// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的)
|
||||
|
||||
// // 克隆一个包含嵌套对象的对象
|
||||
// const person = {
|
||||
// name: 'Alice',
|
||||
// age: 25,
|
||||
// address: {
|
||||
// city: 'New York',
|
||||
// country: 'USA',
|
||||
// },
|
||||
// };
|
||||
// const clonedPerson = cloneDeep(person);
|
||||
|
||||
// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } }
|
||||
// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的)
|
||||
// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的)
|
22
uni_modules/lime-shared/closest/index.ts
Normal file
22
uni_modules/lime-shared/closest/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 在给定数组中找到最接近目标数字的元素。
|
||||
* @param arr 要搜索的数字数组。
|
||||
* @param target 目标数字。
|
||||
* @returns 最接近目标数字的数组元素。
|
||||
*/
|
||||
export function closest(arr: number[], target: number):number {
|
||||
return arr.reduce((pre: number, cur: number):number =>
|
||||
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
|
||||
);
|
||||
}
|
||||
|
||||
// 示例
|
||||
// // 定义一个数字数组
|
||||
// const numbers = [1, 3, 5, 7, 9];
|
||||
|
||||
// // 在数组中找到最接近目标数字 6 的元素
|
||||
// const closestNumber = closest(numbers, 6);
|
||||
|
||||
// console.log(closestNumber); // 输出结果: 5
|
156
uni_modules/lime-shared/components/lime-shared/lime-shared.vue
Normal file
156
uni_modules/lime-shared/components/lime-shared/lime-shared.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<view id="shared" style="height: 500px; width: 300px; background-color: aqua;">
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { getRect, getAllRect } from '@/uni_modules/lime-shared/getRect'
|
||||
|
||||
import { camelCase } from '@/uni_modules/lime-shared/camelCase'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
import { clamp } from '@/uni_modules/lime-shared/clamp'
|
||||
import { cloneDeep } from '@/uni_modules/lime-shared/cloneDeep'
|
||||
import { closest } from '@/uni_modules/lime-shared/closest'
|
||||
import { debounce } from '@/uni_modules/lime-shared/debounce'
|
||||
import { fillZero } from '@/uni_modules/lime-shared/fillZero'
|
||||
import { floatAdd } from '@/uni_modules/lime-shared/floatAdd'
|
||||
import { floatMul } from '@/uni_modules/lime-shared/floatMul'
|
||||
import { floatDiv } from '@/uni_modules/lime-shared/floatDiv'
|
||||
import { floatSub } from '@/uni_modules/lime-shared/floatSub'
|
||||
import { getClassStr } from '@/uni_modules/lime-shared/getClassStr'
|
||||
import { getCurrentPage } from '@/uni_modules/lime-shared/getCurrentPage'
|
||||
import { getStyleStr } from '@/uni_modules/lime-shared/getStyleStr'
|
||||
import { hasOwn } from '@/uni_modules/lime-shared/hasOwn'
|
||||
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
|
||||
import { isBrowser } from '@/uni_modules/lime-shared/isBrowser'
|
||||
import { isDef } from '@/uni_modules/lime-shared/isDef'
|
||||
import { isEmpty } from '@/uni_modules/lime-shared/isEmpty'
|
||||
import { isFunction } from '@/uni_modules/lime-shared/isFunction'
|
||||
import { isNumber } from '@/uni_modules/lime-shared/isNumber'
|
||||
import { isNumeric } from '@/uni_modules/lime-shared/isNumeric'
|
||||
import { isObject } from '@/uni_modules/lime-shared/isObject'
|
||||
import { isPromise } from '@/uni_modules/lime-shared/isPromise'
|
||||
import { isString } from '@/uni_modules/lime-shared/isString'
|
||||
import { kebabCase } from '@/uni_modules/lime-shared/kebabCase'
|
||||
import { raf, doubleRaf } from '@/uni_modules/lime-shared/raf'
|
||||
import { random } from '@/uni_modules/lime-shared/random'
|
||||
import { range } from '@/uni_modules/lime-shared/range'
|
||||
import { sleep } from '@/uni_modules/lime-shared/sleep'
|
||||
import { throttle } from '@/uni_modules/lime-shared/throttle'
|
||||
import { toArray } from '@/uni_modules/lime-shared/toArray'
|
||||
import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
|
||||
import { toNumber } from '@/uni_modules/lime-shared/toNumber'
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { getCurrentInstance } from '@/uni_modules/lime-shared/vue'
|
||||
import { capitalizedAmount } from '@/uni_modules/lime-shared/capitalizedAmount'
|
||||
|
||||
// #ifdef VUE2
|
||||
type UTSJSONObject = any
|
||||
// #endif
|
||||
|
||||
const context = getCurrentInstance()
|
||||
// getRect('#shared', context!).then(res =>{
|
||||
// console.log('res', res.bottom)
|
||||
// })
|
||||
// getAllRect('#shared', context).then(res =>{
|
||||
// console.log('res', res)
|
||||
// })
|
||||
|
||||
|
||||
// console.log('camelCase::', camelCase("hello world"));
|
||||
// console.log('camelCase::', camelCase("my_name_is_john", true));
|
||||
// console.log('canIUseCanvas2d::', canIUseCanvas2d());
|
||||
// console.log('clamp::', clamp(5 ,0, 10));
|
||||
// console.log('cloneDeep::', cloneDeep<UTSJSONObject>({a:5}));
|
||||
// console.log('closest::', closest([1, 3, 5, 7, 9], 6));
|
||||
|
||||
|
||||
|
||||
|
||||
// const saveData = (data: any) => {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
// debouncedSaveData('Data 1');
|
||||
// debouncedSaveData('Data 2');
|
||||
|
||||
// console.log('fillZero', fillZero(1))
|
||||
// console.log('floatAdd', floatAdd(0.1, 0.2), floatAdd(1.05, 0.05), floatAdd(0.1, 0.7), floatAdd(0.0001, 0.0002), floatAdd(123.456 , 789.012))
|
||||
// console.log('floatMul', floatMul(0.1, 0.02), floatMul(1.0255, 100))
|
||||
// console.log('floatDiv', floatDiv(10.44, 100), floatDiv(1.0255, 100), floatDiv(5.419909340994699, 0.2))
|
||||
// console.log('floatSub', floatSub(0.4, 0.1), floatSub(1.0255, 100))
|
||||
const now = () : number => System.nanoTime() / 1_000_000.0
|
||||
console.log('capitalizedAmount', capitalizedAmount(0.4))
|
||||
console.log('capitalizedAmount', capitalizedAmount(100))
|
||||
console.log('capitalizedAmount', capitalizedAmount(100000000))
|
||||
console.log('capitalizedAmount', capitalizedAmount('2023.04'))
|
||||
console.log('capitalizedAmount', capitalizedAmount(-1024))
|
||||
console.log('now', now(), Date.now())
|
||||
// console.log('getClassStr', getClassStr({hover: true}))
|
||||
// console.log('getStyleStr', getStyleStr({ color: 'red', fontSize: '16px', backgroundColor: '', border: null }))
|
||||
// console.log('hasOwn', hasOwn({a: true}, 'key'))
|
||||
// console.log('isBase64::', isBase64("SGVsbG8sIFdvcmxkIQ=="));
|
||||
// console.log('isBrowser::', isBrowser);
|
||||
// console.log('isDef::', isDef('6'));
|
||||
// console.log('isEmpty::', isEmpty({a: true}));
|
||||
|
||||
// const b = () =>{}
|
||||
// console.log('isFunction::', isFunction(b));
|
||||
// console.log('isNumber::', isNumber('6'));
|
||||
// console.log('isNumeric::', isNumeric('6'));
|
||||
// console.log('isObject::', isObject({}));
|
||||
|
||||
// const promise = ():Promise<boolean> => {
|
||||
// return new Promise((resolve) => {
|
||||
// resolve(true)
|
||||
// })
|
||||
// }
|
||||
// const a = promise()
|
||||
// console.log('isPromise::', isPromise(a));
|
||||
// console.log('isString::', isString('null'));
|
||||
// console.log('kebabCase::', kebabCase('my love'));
|
||||
// console.log('raf::', raf(()=>{
|
||||
// console.log('raf:::1')
|
||||
// }));
|
||||
// console.log('doubleRaf::', doubleRaf(()=>{
|
||||
// console.log('doubleRaf:::1')
|
||||
// }));
|
||||
// console.log('random', random(0, 10))
|
||||
// console.log('random', random(0, 1, 2))
|
||||
// console.log('range', range(0, 10, 2))
|
||||
// console.log('sleep', sleep(300).then(res => {
|
||||
// console.log('log')
|
||||
// }))
|
||||
|
||||
// const handleScroll = (a: string) => {
|
||||
// console.log("Scroll event handled!", a);
|
||||
// }
|
||||
|
||||
// // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒
|
||||
// const throttledScroll = throttle(handleScroll, 500);
|
||||
// throttledScroll('5');
|
||||
// const page = getCurrentPage()
|
||||
// console.log('getCurrentPage::', page)
|
||||
|
||||
// console.log('toArray', toArray<number>(5))
|
||||
// console.log('toBoolean', toBoolean(5))
|
||||
// console.log('toNumber', toNumber('5'))
|
||||
// console.log('unitConvert', unitConvert('5'))
|
||||
|
||||
// uni.getImageInfo({
|
||||
// src: '/static/logo.png',
|
||||
// success(res) {
|
||||
// console.log('res', res)
|
||||
// }
|
||||
// })
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
9
uni_modules/lime-shared/createAnimation/index.ts
Normal file
9
uni_modules/lime-shared/createAnimation/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
export * from './type.ts'
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
25
uni_modules/lime-shared/createAnimation/type.ts
Normal file
25
uni_modules/lime-shared/createAnimation/type.ts
Normal file
@ -0,0 +1,25 @@
|
||||
export type CreateAnimationOptions = {
|
||||
/**
|
||||
* 动画持续时间,单位ms
|
||||
*/
|
||||
duration ?: number;
|
||||
/**
|
||||
* 定义动画的效果
|
||||
* - linear: 动画从头到尾的速度是相同的
|
||||
* - ease: 动画以低速开始,然后加快,在结束前变慢
|
||||
* - ease-in: 动画以低速开始
|
||||
* - ease-in-out: 动画以低速开始和结束
|
||||
* - ease-out: 动画以低速结束
|
||||
* - step-start: 动画第一帧就跳至结束状态直到结束
|
||||
* - step-end: 动画一直保持开始状态,最后一帧跳到结束状态
|
||||
*/
|
||||
timingFunction ?: string //'linear' | 'ease' | 'ease-in' | 'ease-in-out' | 'ease-out' | 'step-start' | 'step-end';
|
||||
/**
|
||||
* 动画延迟时间,单位 ms
|
||||
*/
|
||||
delay ?: number;
|
||||
/**
|
||||
* 设置transform-origin
|
||||
*/
|
||||
transformOrigin ?: string;
|
||||
}
|
5
uni_modules/lime-shared/createAnimation/uvue.ts
Normal file
5
uni_modules/lime-shared/createAnimation/uvue.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// @ts-nocheck
|
||||
// export * from '@/uni_modules/lime-animateIt'
|
||||
export function createAnimation() {
|
||||
console.error('当前环境不支持,请使用:lime-animateIt')
|
||||
}
|
148
uni_modules/lime-shared/createAnimation/vue.ts
Normal file
148
uni_modules/lime-shared/createAnimation/vue.ts
Normal file
@ -0,0 +1,148 @@
|
||||
// @ts-nocheck
|
||||
// nvue 需要在节点上设置ref或在export里传入
|
||||
// const animation = createAnimation({
|
||||
// ref: this.$refs['xxx'],
|
||||
// duration: 0,
|
||||
// timingFunction: 'linear'
|
||||
// })
|
||||
// animation.opacity(1).translate(x, y).step({duration})
|
||||
// animation.export(ref)
|
||||
|
||||
// 抹平nvue 与 uni.createAnimation的使用差距
|
||||
// 但是nvue动画太慢
|
||||
|
||||
|
||||
|
||||
import { CreateAnimationOptions } from './type'
|
||||
// #ifdef APP-NVUE
|
||||
const nvueAnimation = uni.requireNativePlugin('animation')
|
||||
|
||||
type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ'
|
||||
| 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom'
|
||||
|
||||
interface Styles {
|
||||
[key : string] : any
|
||||
}
|
||||
|
||||
interface StepConfig {
|
||||
duration?: number
|
||||
timingFunction?: string
|
||||
delay?: number
|
||||
needLayout?: boolean
|
||||
transformOrigin?: string
|
||||
}
|
||||
interface StepAnimate {
|
||||
styles?: Styles
|
||||
config?: StepConfig
|
||||
}
|
||||
interface StepAnimates {
|
||||
[key: number]: StepAnimate
|
||||
}
|
||||
// export interface CreateAnimationOptions extends UniApp.CreateAnimationOptions {
|
||||
// ref?: string
|
||||
// }
|
||||
|
||||
type Callback = (time: number) => void
|
||||
const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
|
||||
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
|
||||
'translateZ'
|
||||
]
|
||||
const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor']
|
||||
const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom']
|
||||
|
||||
class LimeAnimation {
|
||||
ref : any
|
||||
context : any
|
||||
options : UniApp.CreateAnimationOptions
|
||||
// stack : any[] = []
|
||||
next : number = 0
|
||||
currentStepAnimates : StepAnimates = {}
|
||||
duration : number = 0
|
||||
constructor(options : CreateAnimationOptions) {
|
||||
const {ref} = options
|
||||
this.ref = ref
|
||||
this.options = options
|
||||
}
|
||||
addAnimate(type : AnimationTypes, args: (string | number)[]) {
|
||||
let aniObj = this.currentStepAnimates[this.next]
|
||||
let stepAnimate:StepAnimate = {}
|
||||
if (!aniObj) {
|
||||
stepAnimate = {styles: {}, config: {}}
|
||||
} else {
|
||||
stepAnimate = aniObj
|
||||
}
|
||||
|
||||
if (animateTypes1.includes(type)) {
|
||||
if (!stepAnimate.styles.transform) {
|
||||
stepAnimate.styles.transform = ''
|
||||
}
|
||||
let unit = ''
|
||||
if (type === 'rotate') {
|
||||
unit = 'deg'
|
||||
}
|
||||
stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) `
|
||||
} else {
|
||||
stepAnimate.styles[type] = `${args.join(',')}`
|
||||
}
|
||||
this.currentStepAnimates[this.next] = stepAnimate
|
||||
}
|
||||
animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) {
|
||||
const el = ref || this.ref
|
||||
if (!el) return
|
||||
return new Promise((resolve) => {
|
||||
const time = +new Date()
|
||||
nvueAnimation.transition(el, {
|
||||
styles,
|
||||
...config
|
||||
}, () => {
|
||||
resolve(+new Date() - time)
|
||||
})
|
||||
})
|
||||
}
|
||||
nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) {
|
||||
let obj = animates[step]
|
||||
if (obj) {
|
||||
let { styles, config } = obj
|
||||
// this.duration += config.duration
|
||||
this.animateRun(styles, config, ref).then((time: number) => {
|
||||
step += 1
|
||||
this.duration += time
|
||||
this.nextAnimate(animates, step, ref, cb)
|
||||
})
|
||||
} else {
|
||||
this.currentStepAnimates = {}
|
||||
cb && cb(this.duration)
|
||||
}
|
||||
}
|
||||
step(config:StepConfig = {}) {
|
||||
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
|
||||
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
|
||||
this.next++
|
||||
return this
|
||||
}
|
||||
export(ref: any, cb?: Callback) {
|
||||
ref = ref || this.ref
|
||||
if(!ref) return
|
||||
this.duration = 0
|
||||
this.next = 0
|
||||
this.nextAnimate(this.currentStepAnimates, 0, ref, cb)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
|
||||
LimeAnimation.prototype[type] = function(...args: (string | number)[]) {
|
||||
this.addAnimate(type, args)
|
||||
return this
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
export function createAnimation(options : CreateAnimationOptions) {
|
||||
// #ifndef APP-NVUE
|
||||
return uni.createAnimation({ ...options })
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
return new LimeAnimation(options)
|
||||
// #endif
|
||||
}
|
73
uni_modules/lime-shared/createCanvas/index.ts
Normal file
73
uni_modules/lime-shared/createCanvas/index.ts
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue'
|
||||
import { getRect } from '@/uni_modules/lime-shared/getRect'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
export const isCanvas2d = canIUseCanvas2d()
|
||||
// #endif
|
||||
|
||||
|
||||
export function createCanvas(canvasId : string, component : ComponentInternalInstance) {
|
||||
// #ifdef UNI-APP-X
|
||||
uni.createCanvasContextAsync({
|
||||
canvasId,
|
||||
component,
|
||||
success(context : CanvasContext) {
|
||||
|
||||
},
|
||||
fail(error : UniError) {
|
||||
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X
|
||||
const isCanvas2d = canIUseCanvas2d()
|
||||
getRect('#' + canvasId, context, isCanvas2d).then(res => {
|
||||
if (res.node) {
|
||||
res.node.width = res.width
|
||||
res.node.height = res.height
|
||||
return res.node
|
||||
} else {
|
||||
const ctx = uni.createCanvasContext(canvasId, context)
|
||||
if (!ctx._drawImage) {
|
||||
ctx._drawImage = ctx.drawImage
|
||||
ctx.drawImage = function (...args) {
|
||||
const { path } = args.shift()
|
||||
ctx._drawImage(path, ...args)
|
||||
}
|
||||
}
|
||||
if (!ctx.getImageData) {
|
||||
ctx.getImageData = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.canvasGetImageData({
|
||||
canvasId,
|
||||
x: parseInt(arguments[0]),
|
||||
y: parseInt(arguments[1]),
|
||||
width: parseInt(arguments[2]),
|
||||
height: parseInt(arguments[3]),
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail(err) {
|
||||
reject(err)
|
||||
}
|
||||
}, context)
|
||||
})
|
||||
|
||||
}
|
||||
return {
|
||||
getContext(type: string) {
|
||||
if(type == '2d') {
|
||||
return ctx
|
||||
}
|
||||
},
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
createImage
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
71
uni_modules/lime-shared/createImage/index.ts
Normal file
71
uni_modules/lime-shared/createImage/index.ts
Normal file
@ -0,0 +1,71 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import {isBrowser} from '../isBrowser'
|
||||
class Image {
|
||||
currentSrc: string | null = null
|
||||
naturalHeight: number = 0
|
||||
naturalWidth: number = 0
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
tagName: string = 'IMG'
|
||||
path: string = ''
|
||||
crossOrigin: string = ''
|
||||
referrerPolicy: string = ''
|
||||
onload: () => void = () => {}
|
||||
onerror: () => void = () => {}
|
||||
complete: boolean = false
|
||||
constructor() {}
|
||||
set src(src: string) {
|
||||
console.log('src', src)
|
||||
if(!src) {
|
||||
return this.onerror()
|
||||
}
|
||||
src = src.replace(/^@\//,'/')
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
const localReg = /^\.|^\/(?=[^\/])/;
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
|
||||
res.path = localReg.test(src) ? `/${res.path}` : res.path;
|
||||
// #endif
|
||||
this.complete = true
|
||||
this.path = res.path
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
interface UniImage extends WechatMiniprogram.Image {
|
||||
complete?: boolean
|
||||
naturalHeight?: number
|
||||
naturalWidth?: number
|
||||
}
|
||||
/** 创建用于 canvas 的 img */
|
||||
export function createImage(canvas?: any): HTMLImageElement | UniImage {
|
||||
if(canvas && canvas.createImage) {
|
||||
return (canvas as WechatMiniprogram.Canvas).createImage()
|
||||
} else if(this && this['tagName'] == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){
|
||||
return new Image()
|
||||
} else if(isBrowser) {
|
||||
return new window.Image()
|
||||
}
|
||||
return new Image()
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export function createImage():Image{
|
||||
// console.error('当前环境不支持')
|
||||
return new Image()
|
||||
}
|
||||
// #endif
|
10
uni_modules/lime-shared/debounce/index.ts
Normal file
10
uni_modules/lime-shared/debounce/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
36
uni_modules/lime-shared/debounce/uvue.ts
Normal file
36
uni_modules/lime-shared/debounce/uvue.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 防抖函数,通过延迟一定时间来限制函数的执行频率。
|
||||
* @param fn 要防抖的函数。
|
||||
* @param wait 触发防抖的等待时间,单位为毫秒。
|
||||
* @returns 防抖函数。
|
||||
*/
|
||||
export function debounce<A extends any>(fn : (args: A)=> void, wait = 300): (args: A)=> void {
|
||||
let timer = -1
|
||||
|
||||
return (args: A) => {
|
||||
if (timer >-1) {clearTimeout(timer)};
|
||||
|
||||
timer = setTimeout(()=>{
|
||||
fn(args)
|
||||
}, wait)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 示例
|
||||
// 定义一个函数
|
||||
// function saveData(data: string) {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
|
||||
// // 连续调用防抖函数
|
||||
// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数
|
||||
// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数
|
||||
|
||||
// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2"
|
40
uni_modules/lime-shared/debounce/vue.ts
Normal file
40
uni_modules/lime-shared/debounce/vue.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// @ts-nocheck
|
||||
type Timeout = ReturnType<typeof setTimeout> | null;
|
||||
/**
|
||||
* 防抖函数,通过延迟一定时间来限制函数的执行频率。
|
||||
* @param fn 要防抖的函数。
|
||||
* @param wait 触发防抖的等待时间,单位为毫秒。
|
||||
* @returns 防抖函数。
|
||||
*/
|
||||
export function debounce<A extends any, R>(
|
||||
fn : (...args : A) => R,
|
||||
wait : number = 300) : (...args : A) => void {
|
||||
let timer : Timeout = null;
|
||||
|
||||
return function (...args : A) {
|
||||
if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它
|
||||
|
||||
// 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args); // 使用提供的参数调用原始函数
|
||||
}, wait);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 示例
|
||||
// 定义一个函数
|
||||
// function saveData(data: string) {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
|
||||
// // 连续调用防抖函数
|
||||
// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数
|
||||
// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数
|
||||
|
||||
// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2"
|
9
uni_modules/lime-shared/exif/index.ts
Normal file
9
uni_modules/lime-shared/exif/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
7
uni_modules/lime-shared/exif/uvue.ts
Normal file
7
uni_modules/lime-shared/exif/uvue.ts
Normal file
@ -0,0 +1,7 @@
|
||||
class EXIF {
|
||||
constructor(){
|
||||
console.error('当前环境不支持')
|
||||
}
|
||||
}
|
||||
|
||||
export const exif = new EXIF()
|
1057
uni_modules/lime-shared/exif/vue.ts
Normal file
1057
uni_modules/lime-shared/exif/vue.ts
Normal file
File diff suppressed because it is too large
Load Diff
11
uni_modules/lime-shared/fillZero/index.ts
Normal file
11
uni_modules/lime-shared/fillZero/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 在数字前填充零,返回字符串形式的结果
|
||||
* @param number 要填充零的数字
|
||||
* @param length 填充零后的字符串长度,默认为2
|
||||
* @returns 填充零后的字符串
|
||||
*/
|
||||
export function fillZero(number: number, length: number = 2): string {
|
||||
// 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度
|
||||
return `${number}`.padStart(length, '0');
|
||||
}
|
36
uni_modules/lime-shared/floatAdd/index.ts
Normal file
36
uni_modules/lime-shared/floatAdd/index.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { isNumber } from '../isNumber'
|
||||
/**
|
||||
* 返回两个浮点数相加的结果
|
||||
* @param num1 第一个浮点数
|
||||
* @param num2 第二个浮点数
|
||||
* @returns 两个浮点数的相加结果
|
||||
*/
|
||||
export function floatAdd(num1 : number, num2 : number) : number {
|
||||
// 检查 num1 和 num2 是否为数字类型
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
|
||||
let r1 : number, r2 : number, m : number;
|
||||
|
||||
try {
|
||||
// 获取 num1 小数点后的位数
|
||||
r1 = num1.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r1 = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取 num2 小数点后的位数
|
||||
r2 = num2.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r2 = 0;
|
||||
}
|
||||
|
||||
// 计算需要扩大的倍数
|
||||
m = Math.pow(10, Math.max(r1, r2));
|
||||
|
||||
// 返回相加结果
|
||||
return (num1 * m + num2 * m) / m;
|
||||
}
|
45
uni_modules/lime-shared/floatDiv/index.ts
Normal file
45
uni_modules/lime-shared/floatDiv/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { floatMul } from '../floatMul';
|
||||
import { isNumber } from '../isNumber';
|
||||
|
||||
/**
|
||||
* 除法函数,用于处理浮点数除法并保持精度。
|
||||
* @param {number} num1 - 被除数。
|
||||
* @param {number} num2 - 除数。
|
||||
* @returns {number} 除法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatDiv(num1:number, num2:number):number {
|
||||
// 如果传入的不是数字类型,则打印警告并返回NaN
|
||||
if (!isNumber(num1) || !isNumber(num2)) {
|
||||
console.warn('请传入数字类型');
|
||||
return NaN;
|
||||
}
|
||||
|
||||
let m1 = 0, // 被除数小数点后的位数
|
||||
m2 = 0, // 除数小数点后的位数
|
||||
s1 = num1.toString(), // 将被除数转换为字符串
|
||||
s2 = num2.toString(); // 将除数转换为字符串
|
||||
|
||||
// 计算被除数小数点后的位数
|
||||
try {
|
||||
m1 += s1.split('.')[1].length;
|
||||
} catch (error) {}
|
||||
|
||||
// 计算除数小数点后的位数
|
||||
try {
|
||||
m2 += s2.split('.')[1].length;
|
||||
} catch (error) {}
|
||||
|
||||
// 进行除法运算并处理小数点后的位数,使用之前定义的乘法函数保持精度
|
||||
// #ifdef APP-ANDROID
|
||||
return floatMul(
|
||||
parseFloat(s1.replace('.', '')) / parseFloat(s2.replace('.', '')),
|
||||
Math.pow(10, m2 - m1),
|
||||
);
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
return floatMul(
|
||||
Number(s1.replace('.', '')) / Number(s2.replace('.', '')),
|
||||
Math.pow(10, m2 - m1),
|
||||
);
|
||||
// #endif
|
||||
}
|
44
uni_modules/lime-shared/floatMul/index.ts
Normal file
44
uni_modules/lime-shared/floatMul/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// @ts-nocheck
|
||||
import {isNumber} from '../isNumber';
|
||||
// #ifdef APP-ANDROID
|
||||
import BigDecimal from 'java.math.BigDecimal'
|
||||
// import BigDecimal from 'java.math.BigDecimal'
|
||||
// import StringBuilder from 'java.lang.StringBuilder'
|
||||
// import java.math.BigDecimal;
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 乘法函数,用于处理浮点数乘法并保持精度。
|
||||
* @param {number} num1 - 第一个乘数。
|
||||
* @param {number} num2 - 第二个乘数。
|
||||
* @returns {number} 乘法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatMul(num1 : number, num2 : number) : number {
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
let m = 0;
|
||||
// #ifdef APP-ANDROID
|
||||
let s1 = BigDecimal.valueOf(num1.toDouble()).toPlainString(); //new UTSNumber(num1).toString() // //`${num1.toFloat()}`// num1.toString(),
|
||||
let s2 = BigDecimal.valueOf(num2.toDouble()).toPlainString(); //new UTSNumber(num2).toString() //`${num2.toFloat()}`//.toString();
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
let s1:string = `${num1}`// num1.toString(),
|
||||
let s2:string = `${num2}`//.toString();
|
||||
// #endif
|
||||
|
||||
try {
|
||||
m += s1.split('.')[1].length;
|
||||
} catch (error) { }
|
||||
try {
|
||||
m += s2.split('.')[1].length;
|
||||
} catch (error) { }
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
return parseFloat(s1.replace('.', '')) * parseFloat(s2.replace('.', '')) / Math.pow(10, m);
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
|
||||
// #endif
|
||||
}
|
32
uni_modules/lime-shared/floatSub/index.ts
Normal file
32
uni_modules/lime-shared/floatSub/index.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { isNumber } from '../isNumber';
|
||||
/**
|
||||
* 减法函数,用于处理浮点数减法并保持精度。
|
||||
* @param {number} num1 - 被减数。
|
||||
* @param {number} num2 - 减数。
|
||||
* @returns {number} 减法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatSub(num1 : number, num2 : number) : number {
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
let r1:number, r2:number, m:number, n:number;
|
||||
try {
|
||||
r1 = num1.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r1 = 0;
|
||||
}
|
||||
try {
|
||||
r2 = num2.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r2 = 0;
|
||||
}
|
||||
m = Math.pow(10, Math.max(r1, r2));
|
||||
n = r1 >= r2 ? r1 : r2;
|
||||
// #ifndef APP-ANDROID
|
||||
return Number(((num1 * m - num2 * m) / m).toFixed(n));
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
return parseFloat(((num1 * m - num2 * m) / m).toFixed(n));
|
||||
// #endif
|
||||
}
|
53
uni_modules/lime-shared/getClassStr/index.ts
Normal file
53
uni_modules/lime-shared/getClassStr/index.ts
Normal file
@ -0,0 +1,53 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
import { isNumber } from '../isNumber'
|
||||
import { isString } from '../isString'
|
||||
import { isDef } from '../isDef'
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取对象的类名字符串
|
||||
* @param obj - 需要处理的对象
|
||||
* @returns 由对象属性作为类名组成的字符串
|
||||
*/
|
||||
export function getClassStr<T>(obj : T) : string {
|
||||
let classNames : string[] = [];
|
||||
// #ifdef UNI-APP-X && APP
|
||||
if (obj instanceof UTSJSONObject) {
|
||||
(obj as UTSJSONObject).toMap().forEach((value, key) => {
|
||||
if (isDef(value)) {
|
||||
if (isNumber(value)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (isString(value) && value !== '') {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (typeof value == 'boolean' && (value as boolean)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// 遍历对象的属性
|
||||
for (let key in obj) {
|
||||
// 检查属性确实属于对象自身且其值为true
|
||||
if ((obj as any).hasOwnProperty(key) && obj[key]) {
|
||||
// 将属性名添加到类名数组中
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// 将类名数组用空格连接成字符串并返回
|
||||
return classNames.join(' ');
|
||||
}
|
||||
|
||||
|
||||
// 示例
|
||||
// const obj = { foo: true, bar: false, baz: true };
|
||||
// const classNameStr = getClassStr(obj);
|
||||
// console.log(classNameStr); // 输出: "foo baz"
|
9
uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
9
uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
5
uni_modules/lime-shared/getCurrentPage/uvue.uts
Normal file
5
uni_modules/lime-shared/getCurrentPage/uvue.uts
Normal file
@ -0,0 +1,5 @@
|
||||
// @ts-nocheck
|
||||
export const getCurrentPage = ():Page => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1]
|
||||
};
|
6
uni_modules/lime-shared/getCurrentPage/vue.ts
Normal file
6
uni_modules/lime-shared/getCurrentPage/vue.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// @ts-nocheck
|
||||
/** 获取当前页 */
|
||||
export const getCurrentPage = () => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
|
||||
};
|
62
uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
62
uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
@ -0,0 +1,62 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef APP-NVUE || APP-VUE
|
||||
export const getLocalFilePath = (path : string) => {
|
||||
if (typeof plus == 'undefined') return path
|
||||
if (/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path
|
||||
if (/^\//.test(path)) {
|
||||
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.slice(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export { getResourcePath as getLocalFilePath } from '@/uni_modules/lime-file-utils'
|
||||
// export const getLocalFilePath = (path : string) : string => {
|
||||
// let uri = path
|
||||
// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) {
|
||||
// return uri
|
||||
// }
|
||||
// if (uri.startsWith("file://")) {
|
||||
// uri = uri.substring("file://".length)
|
||||
// } else if (uri.startsWith("unifile://")) {
|
||||
// uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
// } else {
|
||||
// uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
// if (uri.startsWith("/android_asset/")) {
|
||||
// uri = uri.replace("/android_asset/", "")
|
||||
// }
|
||||
// }
|
||||
// if (new File(uri).exists()) {
|
||||
// return uri
|
||||
// } else {
|
||||
// return null
|
||||
// }
|
||||
// // return UTSAndroid.convert2AbsFullPath(path)
|
||||
// }
|
||||
// #endif
|
||||
// #ifdef APP-IOS
|
||||
// export const getLocalFilePath = (path : string) : string => {
|
||||
// try {
|
||||
// let uri = path
|
||||
// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) {
|
||||
// return uri
|
||||
// }
|
||||
// if (uri.startsWith("file://")) {
|
||||
// return uri.substring("file://".length)
|
||||
// } else if (path.startsWith("/var/")) {
|
||||
// return path
|
||||
// }
|
||||
// return UTSiOS.getResourcePath(path)
|
||||
// } catch (e) {
|
||||
// return null
|
||||
// }
|
||||
// // return UTSiOS.getResourcePath(path)
|
||||
// }
|
||||
// #endif
|
9
uni_modules/lime-shared/getRect/index.ts
Normal file
9
uni_modules/lime-shared/getRect/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
16
uni_modules/lime-shared/getRect/uvue.uts
Normal file
16
uni_modules/lime-shared/getRect/uvue.uts
Normal file
@ -0,0 +1,16 @@
|
||||
// @ts-nocheck
|
||||
export function getRect(selector : string, context: ComponentPublicInstance):Promise<NodeInfo> {
|
||||
return new Promise((resolve)=>{
|
||||
uni.createSelectorQuery().in(context).select(selector).boundingClientRect(res =>{
|
||||
resolve(res as NodeInfo)
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
|
||||
export function getAllRect(selector : string, context: ComponentPublicInstance):Promise<NodeInfo[]> {
|
||||
return new Promise((resolve)=>{
|
||||
uni.createSelectorQuery().in(context).selectAll(selector).boundingClientRect(res =>{
|
||||
resolve(res as NodeInfo[])
|
||||
}).exec();
|
||||
})
|
||||
}
|
117
uni_modules/lime-shared/getRect/vue.ts
Normal file
117
uni_modules/lime-shared/getRect/vue.ts
Normal file
@ -0,0 +1,117 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取节点信息
|
||||
* @param selector 选择器字符串
|
||||
* @param context ComponentInternalInstance 对象
|
||||
* @param node 是否获取node
|
||||
* @returns 包含节点信息的 Promise 对象
|
||||
*/
|
||||
export function getRect(selector : string, context : ComponentInternalInstance|ComponentPublicInstance, node: boolean = false) {
|
||||
// 之前是个对象,现在改成实例,防止旧版会报错
|
||||
if(context== null) {
|
||||
return Promise.reject('context is null')
|
||||
}
|
||||
if(context.context){
|
||||
context = context.context
|
||||
}
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
|
||||
// #ifndef APP-NVUE
|
||||
const dom = uni.createSelectorQuery().in(context).select(selector);
|
||||
const result = (rect: UniNamespace.NodeInfo) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
dom.boundingClientRect(result).exec()
|
||||
} else {
|
||||
dom.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
rect: true
|
||||
}, result).exec()
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
let { context } = options
|
||||
if (/#|\./.test(selector) && context.refs) {
|
||||
selector = selector.replace(/#|\./, '')
|
||||
if (context.refs[selector]) {
|
||||
selector = context.refs[selector]
|
||||
if(Array.isArray(selector)) {
|
||||
selector = selector[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
dom.getComponentRect(selector, (res) => {
|
||||
if (res.size) {
|
||||
resolve(res.size)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export function getAllRect(selector : string, context: ComponentInternalInstance|ComponentPublicInstance, node:boolean = false) {
|
||||
if(context== null) {
|
||||
return Promise.reject('context is null')
|
||||
}
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
|
||||
// #ifndef APP-NVUE
|
||||
const dom = uni.createSelectorQuery().in(context).selectAll(selector);
|
||||
const result = (rect: UniNamespace.NodeInfo[]) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
dom.boundingClientRect(result).exec()
|
||||
} else {
|
||||
dom.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
rect: true
|
||||
}, result).exec()
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
let { context } = options
|
||||
if (/#|\./.test(selector) && context.refs) {
|
||||
selector = selector.replace(/#|\./, '')
|
||||
if (context.refs[selector]) {
|
||||
selector = context.refs[selector]
|
||||
if(Array.isArray(selector)) {
|
||||
selector = selector[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
dom.getComponentRect(selector, (res) => {
|
||||
if (res.size) {
|
||||
resolve([res.size])
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
});
|
||||
};
|
54
uni_modules/lime-shared/getStyleStr/index.ts
Normal file
54
uni_modules/lime-shared/getStyleStr/index.ts
Normal file
@ -0,0 +1,54 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
interface CSSProperties {
|
||||
[key : string] : string | number | null
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
// #ifdef UNI-APP-X && APP
|
||||
type CSSProperties = UTSJSONObject
|
||||
// #endif
|
||||
// #endif
|
||||
/**
|
||||
* 将字符串转换为带有连字符分隔的小写形式
|
||||
* @param key - 要转换的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function toLowercaseSeparator(key : string):string {
|
||||
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式对象对应的样式字符串
|
||||
* @param style - CSS样式对象
|
||||
* @returns 由非空有效样式属性键值对组成的字符串
|
||||
*/
|
||||
export function getStyleStr(style : CSSProperties) : string {
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
let styleStr = '';
|
||||
style.toMap().forEach((value, key) => {
|
||||
if(value !== null && value != '') {
|
||||
styleStr += `${toLowercaseSeparator(key as string)}: ${value};`
|
||||
}
|
||||
})
|
||||
return styleStr
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
return Object.keys(style)
|
||||
.filter(
|
||||
(key) =>
|
||||
style[key] !== undefined &&
|
||||
style[key] !== null &&
|
||||
style[key] !== '')
|
||||
.map((key : string) => `${toLowercaseSeparator(key)}: ${style[key]};`)
|
||||
.join(' ');
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
|
||||
// const styleStr = getStyleStr(style);
|
||||
// console.log(styleStr);
|
||||
// 输出: "color: red; font-size: 16px;"
|
39
uni_modules/lime-shared/getStyleStr/index_.uts
Normal file
39
uni_modules/lime-shared/getStyleStr/index_.uts
Normal file
@ -0,0 +1,39 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
interface CSSProperties {
|
||||
[key : string] : string | number
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
type CSSProperties = UTSJSONObject
|
||||
// #endif
|
||||
/**
|
||||
* 将字符串转换为带有连字符分隔的小写形式
|
||||
* @param key - 要转换的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function toLowercaseSeparator(key : string) : string {
|
||||
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式对象对应的样式字符串
|
||||
* @param style - CSS样式对象
|
||||
* @returns 由非空有效样式属性键值对组成的字符串
|
||||
*/
|
||||
export function getStyleStr(style : CSSProperties) : string {
|
||||
let styleStr = '';
|
||||
style.toMap().forEach((value, key) => {
|
||||
if(value !== null && value != '') {
|
||||
styleStr += `${toLowercaseSeparator(key as string)}: ${value};`
|
||||
}
|
||||
})
|
||||
return styleStr
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
|
||||
// const styleStr = getStyleStr(style);
|
||||
// console.log(styleStr);
|
||||
// 输出: "color: red; font-size: 16px;"
|
9
uni_modules/lime-shared/hasOwn/index.ts
Normal file
9
uni_modules/lime-shared/hasOwn/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
39
uni_modules/lime-shared/hasOwn/uvue.ts
Normal file
39
uni_modules/lime-shared/hasOwn/uvue.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查对象或数组是否具有指定的属性或键
|
||||
* @param obj 要检查的对象或数组
|
||||
* @param key 指定的属性或键
|
||||
* @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false
|
||||
*/
|
||||
function hasOwn(obj: UTSJSONObject, key: string): boolean
|
||||
function hasOwn(obj: Map<string, unknown>, key: string): boolean
|
||||
function hasOwn(obj: any, key: string): boolean {
|
||||
if(obj instanceof UTSJSONObject){
|
||||
return obj[key] != null
|
||||
}
|
||||
if(obj instanceof Map<string, unknown>){
|
||||
return (obj as Map<string, unknown>).has(key)
|
||||
}
|
||||
return false
|
||||
}
|
||||
export {
|
||||
hasOwn
|
||||
}
|
||||
// 示例
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
|
||||
// if (hasOwn(obj, 'name')) {
|
||||
// console.log("对象具有 'name' 属性");
|
||||
// } else {
|
||||
// console.log("对象不具有 'name' 属性");
|
||||
// }
|
||||
// // 输出: 对象具有 'name' 属性
|
||||
|
||||
// const arr = [1, 2, 3];
|
||||
|
||||
// if (hasOwn(arr, 'length')) {
|
||||
// console.log("数组具有 'length' 属性");
|
||||
// } else {
|
||||
// console.log("数组不具有 'length' 属性");
|
||||
// }
|
||||
// 输出: 数组具有 'length' 属性
|
30
uni_modules/lime-shared/hasOwn/vue.ts
Normal file
30
uni_modules/lime-shared/hasOwn/vue.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// @ts-nocheck
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
/**
|
||||
* 检查对象或数组是否具有指定的属性或键
|
||||
* @param obj 要检查的对象或数组
|
||||
* @param key 指定的属性或键
|
||||
* @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false
|
||||
*/
|
||||
export function hasOwn(obj: Object | Array<any>, key: string): boolean {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
|
||||
// if (hasOwn(obj, 'name')) {
|
||||
// console.log("对象具有 'name' 属性");
|
||||
// } else {
|
||||
// console.log("对象不具有 'name' 属性");
|
||||
// }
|
||||
// // 输出: 对象具有 'name' 属性
|
||||
|
||||
// const arr = [1, 2, 3];
|
||||
|
||||
// if (hasOwn(arr, 'length')) {
|
||||
// console.log("数组具有 'length' 属性");
|
||||
// } else {
|
||||
// console.log("数组不具有 'length' 属性");
|
||||
// }
|
||||
// 输出: 数组具有 'length' 属性
|
43
uni_modules/lime-shared/index.ts
Normal file
43
uni_modules/lime-shared/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// @ts-nocheck
|
||||
// validator
|
||||
// export * from './isString'
|
||||
// export * from './isNumber'
|
||||
// export * from './isNumeric'
|
||||
// export * from './isDef'
|
||||
// export * from './isFunction'
|
||||
// export * from './isObject'
|
||||
// export * from './isPromise'
|
||||
// export * from './isBase64'
|
||||
|
||||
// export * from './hasOwn'
|
||||
|
||||
// // 单位转换
|
||||
// export * from './addUnit'
|
||||
// export * from './unitConvert'
|
||||
// export * from './toNumber'
|
||||
|
||||
// export * from './random'
|
||||
// export * from './range'
|
||||
// export * from './fillZero'
|
||||
|
||||
// // image
|
||||
// export * from './base64ToPath'
|
||||
// export * from './pathToBase64'
|
||||
// export * from './exif'
|
||||
|
||||
// // canvas
|
||||
// export * from './canIUseCanvas2d'
|
||||
|
||||
// // page
|
||||
// export * from './getCurrentPage'
|
||||
|
||||
// // dom
|
||||
// export * from './getRect'
|
||||
// export * from './selectComponent'
|
||||
// export * from './createAnimation'
|
||||
|
||||
// // delay
|
||||
// export * from './sleep'
|
||||
// export * from './debounce'
|
||||
// export * from './throttle'
|
||||
|
23
uni_modules/lime-shared/isBase64/index.ts
Normal file
23
uni_modules/lime-shared/isBase64/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 判断一个字符串是否为Base64编码。
|
||||
* Base64编码的字符串只包含A-Z、a-z、0-9、+、/ 和 = 这些字符。
|
||||
* @param {string} str - 要检查的字符串。
|
||||
* @returns {boolean} 如果字符串是Base64编码,返回true,否则返回false。
|
||||
*/
|
||||
export function isBase64(str: string): boolean {
|
||||
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
||||
return base64Regex.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个字符串是否为Base64编码的data URI。
|
||||
* Base64编码的data URI通常以"data:"开头,后面跟着MIME类型和编码信息,然后是Base64编码的数据。
|
||||
* @param {string} str - 要检查的字符串。
|
||||
* @returns {boolean} 如果字符串是Base64编码的data URI,返回true,否则返回false。
|
||||
*/
|
||||
export function isBase64DataUri(str: string): boolean {
|
||||
const dataUriRegex = /^data:([a-zA-Z]+\/[a-zA-Z0-9-+.]+)(;base64)?,([a-zA-Z0-9+/]+={0,2})$/;
|
||||
return dataUriRegex.test(str);
|
||||
}
|
8
uni_modules/lime-shared/isBrowser/index.ts
Normal file
8
uni_modules/lime-shared/isBrowser/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef WEB
|
||||
export const isBrowser = typeof window !== 'undefined';
|
||||
// #endif
|
||||
|
||||
// #ifndef WEB
|
||||
export const isBrowser = false;
|
||||
// #endif
|
23
uni_modules/lime-shared/isDef/index.ts
Normal file
23
uni_modules/lime-shared/isDef/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否已定义(不为 undefined)且不为 null
|
||||
* @param value 要检查的值
|
||||
* @returns 如果值已定义且不为 null,则返回 true;否则返回 false
|
||||
*/
|
||||
// #ifndef UNI-APP-X
|
||||
export function isDef(value: unknown): boolean {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
export function isDef(value : any|null) : boolean {
|
||||
// #ifdef UNI-APP-X && APP
|
||||
return value != null;
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
return value != null && value != undefined;
|
||||
// #endif
|
||||
}
|
||||
// #endif
|
83
uni_modules/lime-shared/isEmpty/index.ts
Normal file
83
uni_modules/lime-shared/isEmpty/index.ts
Normal file
@ -0,0 +1,83 @@
|
||||
// @ts-nocheck
|
||||
import {isDef} from '../isDef'
|
||||
import {isString} from '../isString'
|
||||
import {isNumber} from '../isNumber'
|
||||
/**
|
||||
* 判断一个值是否为空。
|
||||
*
|
||||
* 对于字符串,去除首尾空格后判断长度是否为0。
|
||||
* 对于数组,判断长度是否为0。
|
||||
* 对于对象,判断键的数量是否为0。
|
||||
* 对于null或undefined,直接返回true。
|
||||
* 其他类型(如数字、布尔值等)默认不为空。
|
||||
*
|
||||
* @param {any} value - 要检查的值。
|
||||
* @returns {boolean} 如果值为空,返回true,否则返回false。
|
||||
*/
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export function isEmpty(value : any | null) : boolean {
|
||||
// 为null
|
||||
if(!isDef(value)){
|
||||
return true
|
||||
}
|
||||
// 为空字符
|
||||
if(isString(value)){
|
||||
return value.toString().trim().length == 0
|
||||
}
|
||||
// 为数值
|
||||
if(isNumber(value)){
|
||||
return false
|
||||
}
|
||||
|
||||
if(typeof value == 'object'){
|
||||
// 数组
|
||||
if(Array.isArray(value)){
|
||||
return (value as Array<unknown>).length == 0
|
||||
}
|
||||
// Map
|
||||
if(value instanceof Map<unknown, unknown>) {
|
||||
return value.size == 0
|
||||
}
|
||||
// Set
|
||||
if(value instanceof Set<unknown>) {
|
||||
return value.size == 0
|
||||
}
|
||||
if(value instanceof UTSJSONObject) {
|
||||
return value.toMap().size == 0
|
||||
}
|
||||
return JSON.stringify(value) == '{}'
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export function isEmpty(value: any): boolean {
|
||||
// 检查是否为null或undefined
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查字符串是否为空
|
||||
if (typeof value === 'string') {
|
||||
return value.trim().length === 0;
|
||||
}
|
||||
|
||||
// 检查数组是否为空
|
||||
if (Array.isArray(value)) {
|
||||
return value.length === 0;
|
||||
}
|
||||
|
||||
// 检查对象是否为空
|
||||
if (typeof value === 'object') {
|
||||
return Object.keys(value).length === 0;
|
||||
}
|
||||
|
||||
// 其他类型(如数字、布尔值等)不为空
|
||||
return false;
|
||||
}
|
||||
// #endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user