This commit is contained in:
巫翔 2025-01-17 13:55:44 +08:00
parent 0ea067daae
commit f9f5edd657
141 changed files with 16816 additions and 12 deletions

19
.gitignore vendored
View File

@ -1,11 +1,8 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
node_modules/
dist/**
.project
unpackage/
.DS_Store
wxcomponents/**/*.vue
wxcomponents/**/*.css
.hbuilderx/

45
App.vue Normal file
View File

@ -0,0 +1,45 @@
<script lang="ts">
import db from './service/db';
export default {
onLaunch() {
console.log('App Launch');
db.open().then(() => {
db.createTable(
`records (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
temperature REAL,
velocity REAL,
acceleration REAL,
displacement REAL,
create_time TEXT,
operate_time TEXT,
check_point TEXT,
device_name TEXT,
wave TEXT
)`
).then((info) => {
console.log(`records 表已创建:${JSON.stringify(info)}`);
},
(e) => {
console.error(`records 创建失败:${JSON.stringify(e)}`);
}
);
});
},
onShow() {
console.log('App Show')
},
onHide() {
console.log('App Hide')
}
}
</script>
<style lang='scss'>
@import '@/uni_modules/uni-scss/index.scss';
</style>
<style>
@import "@/static/iconfont.css";
/*每个页面公共css */
</style>

View File

@ -1,3 +1,6 @@
# EWG01PDemo
EWG01P 测温测振的示例App
恩普特 EWG01P 测温测振仪的Demo。是一个 UniApp 的项目Vue2的因为使用了一个 echarts 的插件,只好用 js没用 ts。
主要代码来自于[EWG01Demo](https://m.shuto.cn:8681/center/EWG01Demo),不过这个项目是在 HbuilderX 中创建的,不是命令行,所以结构上有所有同。
在原项目基础上注释了设备相关的信息,增加了测振时显示曲线以及历史数据中查看时域图、频谱图。

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

43
js/fft/complex.js Normal file
View File

@ -0,0 +1,43 @@
//-------------------------------------------------
// Add two complex numbers
//-------------------------------------------------
var complexAdd = function (a, b) {
return [a[0] + b[0], a[1] + b[1]];
};
//-------------------------------------------------
// Subtract two complex numbers
//-------------------------------------------------
var complexSubtract = function (a, b) {
return [a[0] - b[0], a[1] - b[1]];
};
//-------------------------------------------------
// Multiply two complex numbers
//
// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
//-------------------------------------------------
var complexMultiply = function (a, b) {
// console.log(`a: ${a}, b: ${b}`);
return [a[0] * b[0] - a[1] * b[1], a[0] * b[1] + a[1] * b[0]];
};
//-------------------------------------------------
// Calculate |a + bi|
//
// sqrt(a*a + b*b)
//-------------------------------------------------
var complexMagnitude = function (c) {
// console.log(`c[0]:${c[0]}, c[1]:${c[1]}`);
return Math.sqrt(c[0] * c[0] + c[1] * c[1]);
};
//-------------------------------------------------
// Exports
//-------------------------------------------------
module.exports = {
add: complexAdd,
subtract: complexSubtract,
multiply: complexMultiply,
magnitude: complexMagnitude,
};

69
js/fft/fft.js Normal file
View File

@ -0,0 +1,69 @@
/*===========================================================================*\
* Fast Fourier Transform (Cooley-Tukey Method)
*
* (c) Vail Systems. Joshua Jung and Ben Bryan. 2015
*
* This code is not designed to be highly optimized but as an educational
* tool to understand the Fast Fourier Transform.
\*===========================================================================*/
//------------------------------------------------
// Note: Some of this code is not optimized and is
// primarily designed as an educational and testing
// tool.
// To get high performace would require transforming
// the recursive calls into a loop and then loop
// unrolling. All of this is best accomplished
// in C or assembly.
//-------------------------------------------------
//-------------------------------------------------
// The following code assumes a complex number is
// an array: [real, imaginary]
//-------------------------------------------------
var complex = require("./complex"),
fftUtil = require("./fftutil");
//-------------------------------------------------
// Calculate FFT for vector where vector.length
// is assumed to be a power of 2.
//-------------------------------------------------
export function fft(vector) {
var X = [],
N = vector.length;
// Base case is X = x + 0i since our input is assumed to be real only.
if (N == 1) {
if (Array.isArray(vector[0]))
//If input vector contains complex numbers
return [[vector[0][0], vector[0][1]]];
else return [[vector[0], 0]];
}
// Recurse: all even samples
var X_evens = fft(vector.filter(even)),
// Recurse: all odd samples
X_odds = fft(vector.filter(odd));
// console.log(`vector.length:${vector.length}, evens:${X_evens.length}, odds:${X_odds.length}`);
// Now, perform N/2 operations!
for (var k = 0; k < N / 2; k++) {
// t is a complex number!
var t = X_evens[k];
// console.log(`k:${k}, N:${N}, t:${t}, X_odds[k]: ${X_odds[k]}`);
var e = complex.multiply(fftUtil.exponent(k, N), X_odds[k]);
X[k] = complex.add(t, e);
X[k + N / 2] = complex.subtract(t, e);
}
function even(__, ix) {
return ix % 2 == 0;
}
function odd(__, ix) {
return ix % 2 == 1;
}
return X;
}

66
js/fft/fftutil.js Normal file
View File

@ -0,0 +1,66 @@
/*===========================================================================*\
* Fast Fourier Transform Frequency/Magnitude passes
*
* (c) Vail Systems. Joshua Jung and Ben Bryan. 2015
*
* This code is not designed to be highly optimized but as an educational
* tool to understand the Fast Fourier Transform.
\*===========================================================================*/
//-------------------------------------------------
// The following code assumes a complex number is
// an array: [real, imaginary]
//-------------------------------------------------
var complex = require("./complex");
//-------------------------------------------------
// By Eulers Formula:
//
// e^(i*x) = cos(x) + i*sin(x)
//
// and in DFT:
//
// x = -2*PI*(k/N)
//-------------------------------------------------
var mapExponent = {},
exponent = function (k, N) {
var x = -2 * Math.PI * (k / N);
mapExponent[N] = mapExponent[N] || {};
mapExponent[N][k] = mapExponent[N][k] || [Math.cos(x), Math.sin(x)]; // [Real, Imaginary]
return mapExponent[N][k];
};
//-------------------------------------------------
// Calculate FFT Magnitude for complex numbers.
//-------------------------------------------------
var fftMag = function (fftBins) {
var ret = fftBins.map(complex.magnitude);
return ret.slice(0, ret.length / 2);
};
//-------------------------------------------------
// Calculate Frequency Bins
//
// Returns an array of the frequencies (in hertz) of
// each FFT bin provided, assuming the sampleRate is
// samples taken per second.
//-------------------------------------------------
var fftFreq = function (fftBins, sampleRate) {
var stepFreq = sampleRate / fftBins.length;
var ret = fftBins.slice(0, fftBins.length / 2);
return ret.map(function (__, ix) {
return ix * stepFreq;
});
};
//-------------------------------------------------
// Exports
//-------------------------------------------------
module.exports = {
fftMag: fftMag,
fftFreq: fftFreq,
exponent: exponent,
};

22
main.js Normal file
View File

@ -0,0 +1,22 @@
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

128
manifest.json Normal file
View File

@ -0,0 +1,128 @@
{
"name" : "EWG01PDemo",
"appid" : "__UNI__3095C70",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {
"Barcode" : {},
"Bluetooth" : {},
"SQLite" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion" : 22
},
/* ios */
"ios" : {
"dSYMs" : false,
"idfa" : false
},
/* SDK */
"sdkConfigs" : {},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
},
"nativePlugins" : {
"UniCollectionUtils" : {
"__plugin_info__" : {
"name" : "UniCollectionUtils",
"description" : "测试传感器交互aar",
"platforms" : "Android",
"url" : "",
"android_package_name" : "",
"ios_bundle_id" : "",
"isCloud" : false,
"bought" : -1,
"pid" : "",
"parameters" : {}
}
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

View File

@ -0,0 +1,30 @@
{
"name": "UniCollectionUtils",
"id": "UniCollectionUtils",
"version": "1.0",
"description": "测试传感器交互aar",
"_dp_type": "nativeplugin",
"_dp_nativeplugin": {
"android": {
"plugins": [{
"type": "module",
"name": "UniCollectionUtils",
"class": "com.example.expertcollect.uniApp.UniCollectionUtils"
}],
"hooksClass": "",
"integrateType": "aar",
"compileOptions": {
"sourceCompatibility": "1.8",
"targetCompatibility": "1.8"
},
"abis": [
"armeabi-v7a",
"arm64-v8a",
"x86"
],
"parameters": {
}
}
}
}

38
pages.json Normal file
View File

@ -0,0 +1,38 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
},
{
"path" : "pages/records/records",
"style" :
{
"navigationBarTitleText" : "测量记录"
}
},
{
"path" : "pages/records/chart",
"style" :
{
"navigationBarTitleText" : "趋势图"
}
},
{
"path" : "pages/records/wave_chart",
"style" :
{
"navigationBarTitleText" : "波形图"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
}

480
pages/index/index.vue Normal file
View File

@ -0,0 +1,480 @@
<template>
<view class="uni-wrap">
<uni-section class="mb-10" title="设备信息" type="line" titleFontSize="16">
<template v-slot:right>
<view @click="scanQR" :disabled="connected">
<uni-icons type="scan" size="16"></uni-icons>
<text style="padding-left: 5px;">扫码绑定</text>
</view>
</template>
<uni-row>
<uni-col :span="20">
<text class="title">{{deviceId ? `已绑定设备:${deviceId}` : '未绑定设备'}}</text>
<text class="title" v-if="power>0" style="padding-left: 5px;">电量:{{power}}</text>
</uni-col>
<uni-col :span="4">
<button @click="toggleConnect()" type="primary" :plain="connected" :disabled="deviceId == ''" size="mini">
{{connected ? '断开' : '连接'}}
</button>
</uni-col>
</uni-row>
</uni-section>
<uni-section class="mb-10" title="数据测量" type="line" titleFontSize="16">
<!-- <uni-data-select
label="选择设备"
v-model="r.device_name"
:localdata="devices"
:clear="false"
>
</uni-data-select>
<uni-data-select
label="选择测点"
v-model="r.check_point"
:localdata="checkPoints"
:clear="false"
>
</uni-data-select> -->
<uni-row>
<uni-col :span="12">
<button @click="collectVib()" type="primary" :disabled="!connected || processing">测振</button>
</uni-col>
<uni-col :span="12">
<view class="padding">
<button @click="collectTemp()" type="primary" :disabled="!connected || processing">测温</button>
</view>
</uni-col>
</uni-row>
<uni-row class="text">
<uni-col>
<text class="title">速度{{r.velocity}} mm/s</text>
</uni-col>
</uni-row>
<uni-row class="text">
<uni-col>
<text class="title">加速度{{r.acceleration}} m/s<sup>2</sup></text>
</uni-col>
</uni-row>
<uni-row class="text">
<uni-col>
<text class="title">位移{{r.displacement}} &micro;m</text>
</uni-col>
</uni-row>
<uni-row class="text">
<uni-col>
<text class="title">温度{{r.temperature}} &deg;C</text>
</uni-col>
</uni-row>
<uni-row>
<uni-col>
<view class="padding">
<button @click="save()" type="primary" :disabled="saved">保存</button>
</view>
</uni-col>
</uni-row>
</uni-section>
<uni-section class="mb-10" title="数据查看" type="line" titleFontSize="16">
<uni-row>
<uni-col :span="12">
<view class="padding">
<button @click="toPage('/pages/records/records')" type="primary">查看历史数据</button>
</view>
</uni-col>
<uni-col :span="12">
<view class="padding">
<button @click="toPage('/pages/records/chart')" type="primary">查看趋势图</button>
</view>
</uni-col>
</uni-row>
</uni-section>
<view class="content">
<text>{{msg}}</text>
</view>
<uni-popup ref="popup" :is-mask-click="false">
<view class="chart-pop">
<uni-card>
<uni-row>
<uni-col>
<l-echart ref="wChart" @finished="initChart" custom-style="width:100%;"></l-echart>
</uni-col>
</uni-row>
<uni-row>
<uni-col :span="8">
<view class="center">
<button @click="closePop()" size="mini">取消</button>
</view>
</uni-col>
<uni-col :span="8">
<view class="center">
<button @click="collectVib()" size="mini">重测</button>
</view>
</uni-col>
<uni-col :span="8">
<view class="center">
<button @click="saveCheck()" size="mini" type="primary">保存</button>
</view>
</uni-col>
</uni-row>
</uni-card>
</view>
</uni-popup>
</view>
</template>
<script>
import Vue from 'vue';
import db from '@/service/db';
import {devices, checkPoints} from '@/service/common';
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min';
const Ewg = uni.requireNativePlugin('UniCollectionUtils');
let vibData = {
velocity: null,
acceleration: null,
displacement: null,
wave: [],
power: -1,
};
function now() {
const _now = formatTime(new Date());
return _now;
}
function formatTime(d, fmt = 'yyyy-MM-dd hh:mm:ss') {
const opt = {
'y+': d.getFullYear().toString(), //
'M+': (d.getMonth() + 1).toString(), //
'd+': d.getDate().toString(), //
'h+': d.getHours().toString(), //
'm+': d.getMinutes().toString(), //
's+': d.getSeconds().toString() //
}
let dateString = fmt;
for (const k in opt) {
const ret = new RegExp('(' + k + ')').exec(dateString);
if (ret) {
if (/(y+)/.test(k)) {
dateString = dateString.replace(ret[1], opt[k].substring(4 - ret[1].length))
} else {
dateString = dateString.replace(ret[1], (ret[1].length === 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, '0')))
}
}
}
return dateString;
}
export default Vue.extend({
data() {
const r = {
temperature: null,
velocity: null,
acceleration: null,
displacement: null,
create_time: null,
operate_time: null,
wave: null,
};
return {
buttonTitle: '连接设备',
msg: '.',
deviceId: '',
power: -1,
connected: false,
processing: false,
saved: true,
r,
extraIcon: {
size: '22',
type: 'list'
},
devices,
checkPoints
};
},
onLoad() {
uni.setNavigationBarTitle({
title: '蓝牙测振Demo V' + uni.getAppBaseInfo().appVersion
});
uni.getStorage({
key: 'deviceId',
success: (res) => {
console.log(`get deivceId: ${res.data}`);
this.deviceId = res.data;
}
});
plus.android.requestPermissions(["android.permission.ACCESS_FINE_LOCATION"], (i) => {
console.log('permission granted: ' + i);
}, (e) => {
console.error('prmission grant error: ' + e);
});
// initBL();
},
onUnload() {
},
methods: {
toggleConnect() {
uni.showLoading({ title: '加载中' });
if (this.connected) {
Ewg.disConnect(this.deviceId, (res) => {
console.log("断开设备", JSON.stringify((res)));
this.msg = '设备已断开';
this.connected = false;
uni.hideLoading();
});
}
else {
Ewg.connect(this.deviceId, (res) => {
console.log("连接设备", JSON.stringify((res)))
this.msg = '设备已连接';
this.connected = true;
uni.hideLoading();
});
}
},
collectTemp() {
this.msg = '正在测温';
uni.showLoading({ title: this.msg });
this.processing = true;
this.r.temperature = null;
Ewg.collectTemp("0.95", (res) => {
console.log("测温", JSON.stringify((res)));
this.stop();
if (res.result == 0) {
this.r.temperature = res.temperature;
this.r.operate_time = now();
this.power = res.battery;
this.msg = `温度结果:${JSON.stringify((res))}`;
this.saved = false;
}
else {
this.r.temperature = null;
this.power = -1;
this.msg = `测温异常:${JSON.stringify((res))}`;
}
uni.hideLoading();
});
},
collectVib() {
this.msg = '正在测振';
this.openPop();
uni.showLoading({ title: this.msg });
// TODO:
this.processing = true;
this.r.velocity = null;
this.r.acceleration = null;
this.r.displacement = null;
this.r.wave = null;
console.log('开始测振');
// 0 -- 1-- 2--
Ewg.collectVib(1, true, (res) => { //
this.stop();
if (res.result == 0) {
vibData.velocity = res.speedValue;
vibData.acceleration = res.accValue;
vibData.displacement = res.disValue;
vibData.wave = res.waveData;
vibData.power = res.battery;
this.waveChart();
// this.r.velocity = res.speedValue;
// this.r.acceleration = res.accValue;
// this.r.displacement = res.disValue;
// this.r.wave = res.waveData!.join(',');
// this.r.operate_time = now();
// this.power = res.battery;
// this.msg = ``;
// this.saved = false;
}
else {
this.r.velocity = null;
this.r.acceleration = null;
this.r.displacement = null;
this.r.wave = null;
this.power = -1;
this.msg = `测振异常:${JSON.stringify((res))}`;
}
uni.hideLoading();
});
},
stop() {
Ewg.stopCollect();
this.msg = '停止测量';
this.processing = false;
},
scanQR() {
uni.scanCode({
onlyFromCamera: true,
scanType: ['qrCode'],
success: (res) => {
const did = res.result;
console.log(`条码内容:${did}`);
if (did?.startsWith('expert-')) {
this.deviceId = did.substring('expert-'.length);
uni.setStorageSync('deviceId', this.deviceId);
}
else {
this.msg = `error device: ${did}`;
}
}
});
},
save() {
console.log(JSON.stringify(this.r));
// if (!this.r.check_point || !this.r.device_name) {
// uni.showModal({
// title: '',
// showCancel: false,
// content: `''''`
// });
// return;
// }
db.insertTableData('records', this.r).then(
() => {
this.saved = true;
this.msg = '测量数据已保存';
uni.showToast({
title: '保存成功',
icon: 'success',
mask: true
});
},
(e) => {
this.saved = false;
console.log(`保存失败:${JSON.stringify(e)}`);
this.msg = '测量数据保存失败';
uni.showModal({
title: '保存失败',
showCancel: false,
content: JSON.stringify(e)
});
}
);
},
toPage(path) {
uni.navigateTo({
url: path
});
},
openPop() {
this.$refs.popup.open();
},
async initChart() {
this.$refs.wChart.init(echarts,
chart => chart.setOption({
// grid: {
// left: 40,
// right: 110
// },
// tooltip: {
// },
xAxis: {
type: 'category',
name: '时间(ms)',
splitLine: {
show: true
},
axisLabel: {
formatter: function (value, index) {
return Math.round(value * 10) / 10;
},
// showMaxLabel: true,
},
},
yAxis: [
// {
// type: 'value',
// boundaryGap: [0, '100%'],
// splitLine: {
// show: false
// }
{
position: 'left',
type: 'value',
name: '速度(mm/s)',
axisLine: {
show: true
},
splitLine: {
show: true
}
}
],
series: [{
type: 'line',
smooth: true,
name: '速度',
yAxisIndex: 0,
}]
}),
{ locale: "ZH" },
);
uni.onWindowResize(res => {
this.$refs.wChart?.resize();
});
},
closePop() {
this.$refs.popup.close();
},
saveCheck() {
this.r.acceleration = vibData.acceleration;
this.r.displacement = vibData.displacement;
this.r.velocity = vibData.velocity;
this.r.wave = vibData.wave.join(',');
this.r.operate_time = now();
this.power = vibData.power;
this.msg = `温振完成`;
this.saved = false;
this.closePop();
},
waveChart() {
const wave = vibData.wave;
const point = wave.length;
const samplingRate = 5120;
const times = [];
const duration = (1000 * point) / samplingRate;
console.log(`duration: ${duration}`);
const timespace = duration / (point - 1);
for (let i = 0; i < point; i++) {
times.push(i * timespace);
}
this.$refs.wChart.setOption({xAxis: {data: times}, series: [{data: wave}]});
}
}
});
</script>
<style>
.uni-wrap {
background-color: #efeff4;
}
.uni-row {
padding: 10px;
}
.uni-row.text {
padding-top: 2px;
padding-bottom: 2px;
}
.padding {
padding: 0 5px;
}
.mb-10 {
margin-bottom: 10px;
}
.content {
padding: 20px;
background-color: #fff;
}
.title {
font-size: 16px;
}
.chart-pop {
width: 100vw;
}
.center {
display:flex;
justify-content:center;
}
</style>

210
pages/records/chart.vue Normal file
View File

@ -0,0 +1,210 @@
<template>
<view>
<!-- <uni-section class="section" title="设备及测点" type="line" titleFontSize="16">
<uni-data-select
label="选择设备"
v-model="p.device_name"
:localdata="devices"
:clear="false"
@change="loadChartData"
></uni-data-select>
<uni-data-select
label="选择测点"
v-model="p.check_point"
:localdata="checkPoints"
:clear="false"
@change="loadChartData"
></uni-data-select>
</uni-section> -->
<uni-section class="section" title="振动趋势图" type="line" titleFontSize="16">
<l-echart ref="chart"></l-echart>
</uni-section>
<uni-section class="section" title="温度趋势图" type="line" titleFontSize="16">
<l-echart ref="chartt" @finished="initChart"></l-echart>
</uni-section>
</view>
</template>
<script>
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min';
import db from '@/service/db';
import {devices, checkPoints} from '@/service/common';
const chartData = {
ots: [],
vs: [],
ds: [],
as: [],
ts: []
}
const options = {
legend: {
// x: 'center',
// y: 'bottom',
data: ['位移', '速度', '加速度']
},
grid: {
left: 40,
right: 110
},
tooltip: {
},
xAxis: {
type: 'time',
//data: chartData.ots,
splitLine: {
show: true
}
},
yAxis: [
// {
// type: 'value',
// boundaryGap: [0, '100%'],
// splitLine: {
// show: false
// }
{
position: 'left',
type: 'value',
name: '速度(mm/s)',
axisLine: {
show: true
},
splitLine: {
show: true
}
},
{
position: 'right',
type: 'value',
name: '位移(µm)',
axisLine: {
show: true
},
axisLabel: {
show: true
},
splitLine: {
show: false
}
},
{
position: 'right',
offset: 65,
type: 'value',
name: '加速度(m/s2)',
// nameRotate: 270,
// nameGap: 50,
// nameLocation: 'middle',
axisLine: {
show: true
},
axisLabel: {
show: true
},
splitLine: {
show: false
}
}
],
series: [{
type: 'line',
smooth: true,
name: '速度',
yAxisIndex: 0,
}, {
type: 'line',
smooth: true,
name: '位移',
yAxisIndex: 1,
}, {
type: 'line',
smooth: true,
name: '加速度',
yAxisIndex: 2,
}]
};
const options_t = {
xAxis: {
type: 'time',
splitLine: {
show: true
}
},
yAxis: {
type: 'value',
name: '温度(°C)',
boundaryGap: [0, '100%'],
splitLine: {
show: false
}
},
series: [{
type: 'line',
smooth: true,
name: '温度',
yAxisIndex: 0,
}]
};
export default {
data() {
return {
devices,
checkPoints,
p: {
device_name: '',
check_point: ''
},
};
},
onLoad() {
// this.p.check_point = checkPoints[0].value;
// this.p.device_name = devices[0].value;
},
onUnload() {
},
methods: {
async loadChartData() {
chartData.ots = [];
chartData.vs = [];
chartData.ds = [];
chartData.as = [];
chartData.ts = [];
// const sql = `select * from records where device_name = '${this.p.device_name}' and check_point = '${this.p.check_point}' order by operate_time`;
const sql = `select * from records order by operate_time`;
const records = await db.select(sql);
records.forEach((r) => {
// chartData.ots.push(r.operate_time);
chartData.as.push([r.operate_time, r.acceleration]);
chartData.vs.push([r.operate_time, r.velocity]);
chartData.ds.push([r.operate_time, r.displacement]);
chartData.ts.push([r.operate_time, r.temperature]);
});
this.$refs.chart.setOption({series: [{data: chartData.vs}, {data: chartData.ds}, {data: chartData.as}]});
this.$refs.chartt.setOption({series: [{data: chartData.ts}]});
},
async initChart() {
await this.$refs.chart.init(echarts, chart => chart.setOption(options), { locale: "ZH" });
await this.$refs.chartt.init(echarts, chart => chart.setOption(options_t), { locale: "ZH" });
this.loadChartData();
uni.onWindowResize(res => {
this.$refs.chart?.resize();
this.$refs.chartt?.resize();
});
}
}
}
</script>
<style lang="scss">
.section {
margin: 10px 0;
}
</style>

188
pages/records/records.vue Normal file
View File

@ -0,0 +1,188 @@
<template>
<view>
<uni-list>
<uni-card :isFull="true" v-for="item in records" :key="item.id">
<!-- <uni-row>
<uni-col>
<view>{{item.device_name}} - {{item.check_point}}</view>
</uni-col>
</uni-row> -->
<uni-row>
<uni-col :span="22">
<view>速度: {{item.velocity}} mm/s</view>
</uni-col>
<uni-col :span="2">
<uni-icons fontFamily="iconfont" :size="20" @click="waveChart(item.id)">{{'\ue644'}}</uni-icons>
</uni-col>
</uni-row>
<uni-row>
<uni-col>
<view>加速度: {{item.acceleration}} m/s<sup>2</sup></view>
</uni-col>
</uni-row>
<uni-row>
<uni-col>
<view>位移: {{item.displacement}} &micro;m</view>
</uni-col>
</uni-row>
<uni-row>
<uni-col>
<view>温度: {{item.temperature}} &deg;C</view>
</uni-col>
</uni-row>
<uni-row>
<uni-col :span="22">
<view>测量时间: {{item.operate_time}}</view>
</uni-col>
<uni-col :span="2">
<uni-icons type="trash" size="18" :size="26" @click="remove(item.id)"></uni-icons>
</uni-col>
</uni-row>
</uni-card>
</uni-list>
</view>
</template>
<script lang="ts">
import Vue from 'vue';
import db from '@/service/db';
const select = 'select * from records order by operate_time desc';
let page = 1;
export default Vue.extend({
data() {
return {
records: <any>[]
};
},
async onLoad() {
this.load();
},
onPullDownRefresh() {
this.load();
uni.stopPullDownRefresh();
},
onReachBottom() {
this.loadNext();
},
methods: {
async load() {
page = 1;
this.records = await db.selectPage(select, page);
},
async loadNext() {
page++;
const nextPage = await db.selectPage(select, page);
this.records.push(...nextPage);
},
remove(id: number) {
uni.showModal({
title: '确认操作',
icon: 'info',
content: '确定要删除吗?',
success: (res) => {
if (res.confirm) {
db.deleteTableData('records', 'id', id).then(
() => {
this.load();
uni.showToast({
title: '删除成功',
icon: 'success',
mask: true
});
},
(e) => {
uni.showModal({
title: '删除失败',
icon: 'error',
showCancel: false,
content: JSON.stringify(e)
})
}
);
} else if (res.cancel) {
console.log('用户点击取消');
}
}
})
db.deleteTableData('records', 'id', id);
},
// toChart(){
// uni.navigateTo({
// url: './chart'
// });
// },
waveChart(id: number){
uni.navigateTo({
url: `./wave_chart?id=${id}`
});
},
exportData() {
// plus.io.resolveLocalFileSystemURL("_doc/shuto.db", function(entry) {
// console.log(`${entry.fullPath} : ${entry.isFile}`);
// plus.android.requestPermissions(["android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"], (i) => {
// console.log('permission granted: ' + i);
// plus.io.resolveLocalFileSystemURL("_downloads", function(destEntry) {
// console.log(`${destEntry.fullPath} : ${destEntry.isFile}`);
// entry.copyTo(destEntry, "shuto_backup.db", result => {
// console.log(`${result.fullPath} : ${result.isFile}`);
// }, e => {
// console.error(e);
// });
// })
// }, (e) => {
// console.error('prmission grant error: ' + e);
// });
// }
// );
plus.io.resolveLocalFileSystemURL("_doc/shuto.db", function(entry) {
uni.showLoading({ title: '上传中...' });
uni.uploadFile({
url: 'http://8.214.43.244:38899/upload', //
filePath: entry.fullPath,
name: 'file',
formData: {
'device': uni.getDeviceInfo().deviceId
},
success: (uploadFileRes) => {
console.log(uploadFileRes.data);
uni.hideLoading();
uni.showToast({
title: '上传成功',
icon: 'success',
mask: true
});
},
fail: (e) => {
console.log(JSON.stringify(e));
uni.hideLoading();
uni.showModal({
title: '错误信息',
showCancel: false,
content: '上传失败'
})
},
complete: () => {
uni.hideLoading();
}
});
// uploadTask.onProgressUpdate((res) => {
// console.log('' + res.progress);
// console.log('' + res.totalBytesSent);
// console.log('' + res.totalBytesExpectedToSend);
// uni.showLoading({ title: '... ' + res.progress });
// });
});
}
}
});
</script>
<style>
</style>

View File

@ -0,0 +1,171 @@
<template>
<view>
<uni-section class="section" title="振动波形图" type="line" titleFontSize="16">
<l-echart ref="chart"></l-echart>
</uni-section>
<uni-section class="section" title="频谱图" type="line" titleFontSize="16">
<l-echart ref="chartt" @finished="initChart"></l-echart>
</uni-section>
</view>
</template>
<script>
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min';
import db from '@/service/db';
import {devices, checkPoints} from '@/service/common';
import {fft} from '@/js/fft/fft';
import * as util from "@/js/fft/fftutil";
const chartData = {
wave: [],
vs: [],
}
const options = {
// grid: {
// left: 40,
// right: 110
// },
tooltip: {
},
xAxis: {
type: 'category',
// name: '(ms)',
splitLine: {
show: true
},
axisLabel: {
formatter: function (value, index) {
return Math.round(value * 10) / 10;
},
},
},
yAxis: [
// {
// type: 'value',
// boundaryGap: [0, '100%'],
// splitLine: {
// show: false
// }
{
position: 'left',
type: 'value',
name: '速度(mm/s)',
axisLine: {
show: true
},
splitLine: {
show: true
}
}
],
series: [{
type: 'line',
smooth: true,
name: '速度',
yAxisIndex: 0,
}]
};
const options_t = {
xAxis: {
type: 'category',
name: '频率(Hz)',
axisLabel: {
formatter: function (value, index) {
return Math.round(value * 10) / 10;
},
// showMaxLabel: true,
// interval:1
},
// splitLine: {
// show: true
// }
},
yAxis: {
type: 'value',
name: '振幅',
// splitLine: {
// show: false
// }
},
series: [{
name: '振幅',
type: 'line',
// smooth: true,
// yAxisIndex: 0,
}],
dataZoom: [{
type: "inside",
}],
};
export default {
data() {
return {
id: null,
};
},
onLoad(option) {
this.id = option.id;
chartData.wave = [];
},
methods: {
async loadChartData() {
const sql = `select wave from records where id = ${this.id}`;
const records = await db.select(sql);
if (records.length < 1) {
alert("no record found");
return;
}
const wave = records[0].wave.split(',');
const point = 2048;
const samplingRate = 5120;
const times = [];
const duration = (1000 * point) / samplingRate;
console.log(`duration: ${duration}`);
const timespace = duration / (point - 1);
for (let i = 0; i < point; i++) {
chartData.wave.push(parseFloat(wave[i]));
times.push(i * timespace);
}
// console.log(`times.length: ${times.length}, wave.length: ${chartData.wave.length}`);
this.$refs.chart.setOption({xAxis: {data: times}, series: [{data: chartData.wave}]});
const phasors = fft(chartData.wave);
const resultSize = phasors.length / 8;
let frequencies = util.fftFreq(phasors, samplingRate);
frequencies = frequencies.slice(0, resultSize);
let magnitudes = util.fftMag(phasors);
const magnitudeSize = magnitudes.length;
magnitudes = magnitudes.slice(0, resultSize);
magnitudes = magnitudes.map(function (v, ix) {
return v / magnitudeSize;
});
magnitudes[0] = 0; // 0Hz
// console.log(`frequencies:[${frequencies[0]},${frequencies[frequencies.length-1]}], magnitudes[0]:[${magnitudes[0]}]`);
// console.log(`magnitudes: ${magnitudes[0]}, ${magnitudes[1]}, ${magnitudes[2]}, ${magnitudes[3]}, ${magnitudes[4]}, ${magnitudes[5]}, ${magnitudes[6]}, ${magnitudes[7]}, ${magnitudes[8]}, ${magnitudes[9]}`);
// console.log("magnitudes: ", magnitudes);
this.$refs.chartt.setOption({xAxis: {data: frequencies}, series: [{data: magnitudes}]});
},
async initChart() {
await this.$refs.chart.init(echarts, chart => chart.setOption(options), { locale: "ZH" });
await this.$refs.chartt.init(echarts, chart => chart.setOption(options_t), { locale: "ZH" });
this.loadChartData().catch(reason => console.error(reason));
uni.onWindowResize(res => {
this.$refs.chart?.resize();
this.$refs.chartt?.resize();
})
}
}
}
</script>
<style lang="scss">
.section {
margin: 10px 0;
}
</style>

13
service/common.ts Normal file
View File

@ -0,0 +1,13 @@
export const devices = [
{value: '1#离心泵', text: '1#离心泵'},
{value: '2#离心泵', text: '2#离心泵'}
];
export const checkPoints = [
{value: '驱动端水平', text: '驱动端水平'},
{value: '驱动端垂直', text: '驱动端垂直'},
{value: '驱动端轴向', text: '驱动端轴向'},
{value: '非驱动端水平', text: '非驱动端水平'},
{value: '非驱动端垂直', text: '非驱动端垂直'},
{value: '非驱动轴向', text: '非驱动轴向'}
];

299
service/db.ts Normal file
View File

@ -0,0 +1,299 @@
export default {
dbName: 'shuto', // 数据库名称
dbPath: '_doc/shuto.db', // 数据库地址,推荐以下划线为开头
// 判断数据库是否打开
isOpen() {
// 数据库打开了就返回 true,否则返回 false
return plus.sqlite.isOpenDatabase({
name: this.dbName, // 数据库名称
path: this.dbPath // 数据库地址
});
},
// 创建数据库 或 有该数据库就打开
open() {
return new Promise((resolve, reject) => {
// 打开数据库
plus.sqlite.openDatabase({
name: this.dbName,
path: this.dbPath,
success: (e) => {
console.log(`${this.dbPath} 已打开`);
resolve(e); // 成功回调
},
fail(e) {
console.error(`打开数据库错误:${e}`);
reject(e); // 失败回调
}
})
})
},
// 关闭数据库
close() {
return new Promise((resolve, reject) => {
plus.sqlite.closeDatabase({
name: this.dbName,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
},
// 数据库建表 sql:'CREATE TABLE IF NOT EXISTS dbTable("id" varchar(50),"name" TEXT)
// 创建 CREATE TABLE IF NOT EXISTS 、 表名不能用数字开头
createTable(dbTable : string) {
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: this.dbName,
sql: [`CREATE TABLE IF NOT EXISTS ${dbTable}`],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
},
// 数据库删表 sql:'DROP TABLE dbTable'
dropTable(dbTable : string) {
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: this.dbName,
sql: [`DROP TABLE ${dbTable}`],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
},
// 向表格里添加数据 sql:'INSERT INTO dbTable VALUES('x','x','x')' 对应新增
// 或者 sql:'INSERT INTO dbTable ('x','x','x') VALUES('x','x','x')' 具体新增
// 插入 INSERT INTO 、 dbTable 是表名、根据表头列名插入列值
insertTableData(dbTable : string, data : any) {
// 判断有没有传参
if (dbTable !== undefined && data !== undefined) {
// 判断传的参是否有值
let bol = (JSON.stringify(data) == "{}");
if (!bol) {
let properties:string = '', values: string = '';
Object.keys(data).forEach(key => {
properties += `${key},`;
let v = data[key];
if (typeof v == 'string') {
values += `'${data[key]}',`;
}
else {
values += `${data[key]},`;
}
});
const plen = properties.length;
const vlen = values.length;
if (plen <= 0 || vlen <= 0) {
return Promise.reject(`数据有误:${properties} -- ${values}`);
}
properties = properties.substring(0, plen-1);
values = values.substring(0, vlen-1);
let sql = `INSERT INTO ${dbTable} (${properties}) VALUES(${values})`;
console.log(sql);
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: this.dbName,
sql: [sql],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
} else {
return new Promise((reject) => { reject("错误添加") })
}
} else {
return new Promise((reject) => { reject("错误添加") })
}
},
// 根据条件向表格里添加数据 有数据更新、无数据插入
// (建表时需要设置主键) 例如 --- "roomid" varchar(50) PRIMARY KEY
insertOrReplaceData(dbTable : string, data : any, condition : any) {
// 判断有没有传参
if (dbTable !== undefined && data !== undefined) {
let sql: string;
if (condition == undefined) {
sql = `INSERT OR REPLACE INTO ${dbTable} VALUES('${data}')`;
} else {
sql = `INSERT OR REPLACE INTO ${dbTable} (${condition}) VALUES(${data})`;
}
// console.log(sql);
return new Promise((resolve, reject) => {
// 表格添加数据
plus.sqlite.executeSql({
name: this.dbName,
sql: [sql],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
} else {
return new Promise((reject) => { reject("错误添加") })
}
},
// 查询获取数据库里的数据 sql:'SELECT * FROM dbTable WHERE lname = 'lvalue''
// 查询 SELECT * FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值
selectTableData(dbTable : string, uname : string, namevalue : any, upass : string, passvalue : any, urrn : string, rrnvalue : any) {
if (dbTable !== undefined) {
let sql: string;
// 第一个是表单名称,后两个参数是列表名,用来检索
if (uname !== undefined && upass !== undefined && urrn !== undefined) {
// 三个检索条件
sql = `SELECT * FROM ${dbTable} WHERE ${uname} = '${namevalue}' AND ${upass} = '${passvalue}' AND ${urrn}='${rrnvalue}'`;
}
if (uname !== undefined && upass !== undefined && urrn == undefined) {
// 两个检索条件
sql = `SELECT * FROM ${dbTable} WHERE ${uname} = '${namevalue}' AND ${upass} = '${passvalue}'`;
}
if (uname !== undefined && upass == undefined && urrn == undefined) {
// 一个检索条件
sql = `SELECT * FROM ${dbTable} WHERE ${uname} = '${namevalue}'`;
// console.log(sql);
}
if (uname == undefined) {
sql = `SELECT * FROM ${dbTable}`;
}
return new Promise((resolve, reject) => {
// 表格查询数据 执行查询的SQL语句
plus.sqlite.selectSql({
name: this.dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
} else {
return new Promise((reject) => { reject("错误查询") });
}
},
// 删除表里的数据 sql:'DELETE FROM dbTable WHERE lname = 'lvalue''
// 删除 DELETE FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值
deleteTableData(dbTable : string, lname : string, lvalue : any, ww? : string, ee? : any) {
if (dbTable !== undefined) {
let sql: string;
if (lname == undefined) {
sql = `DELETE FROM ${dbTable}`;
} else {
if (ww !== undefined) {
// 两个检索条件
sql = `DELETE FROM ${dbTable} WHERE ${lname} = '${lvalue}' AND ${ww} = '${ee}'`;
} else {
// 一个检索条件
sql = `DELETE FROM ${dbTable} WHERE ${lname} = '${lvalue}'`;
}
}
return new Promise((resolve, reject) => {
// 删除表数据
plus.sqlite.executeSql({
name: this.dbName,
sql: [sql],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
} else {
return new Promise((reject) => { reject("错误删除") });
}
},
// 修改数据表里的数据 sql:"UPDATE dbTable SET 列名 = '列值',列名 = '列值' WHERE lname = 'lvalue'"
// 修改 UPDATE 、 dbTable 是表名, data: 要修改的列名=修改后列值, lname,lvalue 是查询条件的列名和列值
updateTableData(dbTable : string, data : any, lname : string, lvalue : any) {
let sql: string;
if (lname == undefined) {
sql = `UPDATE ${dbTable} SET ${data}`;
} else {
sql = `UPDATE ${dbTable} SET ${data} WHERE ${lname} = '${lvalue}'`;
}
// WHERE 前面是要修改的列名、列值,后面是条件的列名、列值
return new Promise((resolve, reject) => {
// 修改表数据
plus.sqlite.executeSql({
name: this.dbName,
sql: [sql],
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
},
// 获取指定数据条数 sql:"SELECT * FROM dbTable ORDER BY 'id' DESC LIMIT 15 OFFSET 'num'"
// dbTable 表名, ORDER BY 代表排序默认正序, id 是排序的条件 DESC 代表倒序,从最后一条数据开始拿
// LIMIT 15 OFFSET '${num}',这句的意思是跳过 num 条拿 15 条数据, num 为跳过多少条数据是动态值
// 例 初始num设为0就从最后的数据开始拿15条下次不拿刚获取的数据所以可以让num为15这样就能一步一步的拿完所有的数据
pullSQL(dbTable : string, id : any, num : number) {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: this.dbName,
sql: `SELECT * FROM ${dbTable} ORDER BY '${id}' DESC LIMIT 15 OFFSET '${num}'`,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
},
select(sql: string): Promise<any> {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: this.dbName,
sql: sql,
success(data: any) {
console.log(`${sql} 执行成功`);
resolve(data);
},
fail(e) {
console.log(`${sql} 执行失败,${JSON.stringify(e)}`);
reject(e);
}
})
});
},
selectPage(sql: string, page: number = 1, size: number = 10): Promise<any> {
const _page = page < 1 ? 0 : page - 1;
return this.select(`${sql} limit ${size} offset ${_page*size}`);
}
}

41
static/iconfont.css Normal file
View File

@ -0,0 +1,41 @@
@font-face {
font-family: "iconfont";
src: url('/static/iconfont.ttf') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-rotate:before {
content: "\e66e";
}
.icon-rotate-screen:before {
content: "\e648";
}
.icon-rotate-big:before {
content: "\e624";
}
.icon-linechat:before {
content: "\e79a";
}
.icon-mixchat-r:before {
content: "\e6d8";
}
.icon-mixchat-s:before {
content: "\e61a";
}
.icon-diogram:before {
content: "\e644";
}

BIN
static/iconfont.ttf Normal file

Binary file not shown.

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

13
uni.promisify.adaptor.js Normal file
View File

@ -0,0 +1,13 @@
uni.addInterceptor({
returnValue (res) {
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
return res;
}
return new Promise((resolve, reject) => {
res.then((res) => {
if (!res) return resolve(res)
return res[0] ? reject(res[0]) : resolve(res[1])
});
});
},
});

76
uni.scss Normal file
View File

@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;

View File

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

View File

@ -0,0 +1,385 @@
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',
// 'font'
];
const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
styles.forEach(style => {
Object.defineProperty(ctx, style, {
set: value => {
// if (style === 'font' && fontSizeReg.test(value)) {
// const match = fontSizeReg.exec(value);
// ctx.setFontSize(match[1]);
// return;
// }
if (style === 'opacity') {
ctx.setGlobalAlpha(value)
return;
}
if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
// #ifdef H5 || APP-PLUS || MP-BAIDU
if(typeof value == 'object') {
if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
}
return
}
// #endif
// #ifdef MP-TOUTIAO
if(colorReg.test(value)) {
value = value.replace(colorReg, '#$1$1$2$2$3$3')
}
// #endif
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
}
}
});
});
if(!this.isNew && !child) {
ctx.uniDrawImage = ctx.drawImage
ctx.drawImage = (...a) => {
a[0] = a[0].src
ctx.uniDrawImage(...a)
}
}
if(!ctx.createRadialGradient) {
ctx.createRadialGradient = function() {
return ctx.createCircularGradient(...[...arguments].slice(-3))
};
}
// 字节不支持
if (!ctx.strokeText) {
ctx.strokeText = (...a) => {
ctx.fillText(...a)
}
}
// 钉钉不支持
if (!ctx.measureText) {
const strLen = (str) => {
let len = 0;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
len++;
} else {
len += 2;
}
}
return len;
}
ctx.measureText = (text, font) => {
let fontSize = 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;
return {
width: strLen(text) * fontSize * widthFactor
};
}
}
}
_initEvent(e) {
this.event = {};
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown'
}, {
wxName: 'touchMove',
ecName: 'mousemove'
}, {
wxName: 'touchEnd',
ecName: 'mouseup'
}, {
wxName: 'touchEnd',
ecName: 'click'
}];
eventNames.forEach(name => {
this.event[name.wxName] = e => {
const touch = e.touches[0];
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y
});
};
});
}
set width(w) {
this.canvasNode.width = w
}
set height(h) {
this.canvasNode.height = h
}
get width() {
return this.canvasNode.width || 0
}
get height() {
return this.canvasNode.height || 0
}
get ctx() {
return cacheChart[this.canvasId]['ctx'] || null
}
set chart(chart) {
cacheChart[this.canvasId]['chart'] = chart
}
get chart() {
return cacheChart[this.canvasId]['chart'] || null
}
}
export function dispatch(name, {x,y, wheelDelta}) {
this.dispatch(name, {
zrX: x,
zrY: y,
zrDelta: wheelDelta,
preventDefault: () => {},
stopPropagation: () =>{}
});
}
export function setCanvasCreator(echarts, {canvas, node}) {
// echarts.setCanvasCreator(() => canvas);
if(echarts && !echarts.registerPreprocessor) {
return console.warn('echarts 版本不对或未传入echartsvue3请使用esm格式')
}
echarts.registerPreprocessor(option => {
if (option && option.series) {
if (option.series.length > 0) {
option.series.forEach(series => {
series.progressive = 0;
});
} else if (typeof option.series === 'object') {
option.series.progressive = 0;
}
}
});
function loadImage(src, onload, onerror) {
let img = null
if(node && node.createImage) {
img = node.createImage()
img.onload = onload.bind(img);
img.onerror = onerror.bind(img);
img.src = src;
return img
} else {
img = new Image()
img.onload = onload.bind(img)
img.onerror = onerror.bind(img);
img.src = src
return img
}
}
if(echarts.setPlatformAPI) {
echarts.setPlatformAPI({
loadImage: canvas.setChart ? loadImage : null,
createCanvas(){
const key = 'createOffscreenCanvas'
return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
}
})
}
}

View File

@ -0,0 +1,141 @@
<template>
<web-view class="lime-echart" ref="limeEchart" :style="[customStyle]" :webview-styles="[webviewStyles]"
src="/uni_modules/lime-echart/static/uvue.html?v=1011">
</web-view>
</template>
<script lang="uts">
import { Echarts } from './uvue';
type EchartsResolve = (value : Echarts) => void
export default {
name: 'l-echart',
props: {
webviewStyles: {
type: Object
},
customStyle: {
type: Object
},
isDisableScroll: {
type: Boolean,
default: false
},
isClickable: {
type: Boolean,
default: true
},
enableHover: {
type: Boolean,
default: false
},
beforeDelay: {
type: Number,
default: 30
}
},
data() {
return {
finished: false,
map: [] as EchartsResolve[],
context: null as UniWebViewElement | null,
// el: null as UniWebViewElement | null,
chart: null as Echarts | null
};
},
computed: {
},
unmounted() {
},
created() {
},
mounted() {
this.createSelectorQuery().select('.lime-echart').boundingClientRect(_ => {
const context = this.$el as UniWebViewElement
this.context = context
// context.addEventListener('error', (_ : WebViewErrorEvent) => { })
// context.addEventListener('loading', (_ : WebViewLoadingEvent) => { })
context.addEventListener('loaded', (event : WebViewLoadedEvent) => {
this.finished = true
event.stopPropagation()
event.preventDefault()
this.trigger()
this.$emit('finished')
})
}).exec()
},
methods: {
_next() {
if (this.chart == null) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
},
setOption(option : UTSJSONObject) {
this._next()
this.chart?.setOption(option);
},
showLoading() {
this._next()
this.chart?.showLoading();
},
hideLoading() {
this._next()
this.chart?.hideLoading();
},
clear() {
this._next()
this.chart?.clear();
},
dispose() {
this._next()
this.chart?.dispose();
},
resize(size:UTSJSONObject) {
this._next()
this.chart?.resize(size);
},
canvasToTempFilePath(opt: UTSJSONObject) {
this._next()
this.chart?.canvasToTempFilePath(opt)
},
trigger() {
if (this.finished) {
if (this.chart == null) {
this.chart = new Echarts(this.context!)
}
while (this.map.length > 0) {
const resolve = this.map.pop() as EchartsResolve
resolve(this.chart!)
}
}
},
init() : Promise<Echarts> {
return new Promise((resolve) => {
this.map.push(resolve)
this.trigger()
})
},
init(callback : (chart : Echarts) => void) : Promise<Echarts> {
if (this.chart !== null) {
callback(this.chart!)
} else {
console.warn('echarts 未加载完成,您可以延时一下')
}
return new Promise((resolve) => {
this.map.push(resolve)
this.trigger()
})
}
}
};
</script>
<style lang="scss">
.lime-echart {
flex: 1;
}
</style>

View File

@ -0,0 +1,506 @@
<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-if="isPC"
:style="canvasStyle"
:id="canvasId"
:canvas-id="canvasId"
:disable-scroll="isDisableScroll"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
/> -->
<canvas
class="lime-echart__canvas"
v-else
:width="nodeWidth"
:height="nodeHeight"
:style="canvasStyle"
:canvas-id="canvasId"
:id="canvasId"
:disable-scroll="isDisableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
<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/nvue.html?v=1"
@pagefinish="finished = true"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<script>
// #ifdef VUE3
// #ifdef APP-PLUS
global = {}
// #endif
// #endif
// #ifndef APP-NVUE
import {Canvas, setCanvasCreator, dispatch} from './canvas';
import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from './utils';
import {Echarts} from './nvue'
// #endif
const charts = {}
const echartsObj = {}
export default {
name: 'lime-echart',
props: {
// #ifdef MP-WEIXIN || MP-TOUTIAO
type: {
type: String,
default: '2d'
},
// #endif
// #ifdef APP-NVUE
webviewStyles: Object,
// hybrid: Boolean,
// #endif
customStyle: String,
isDisableScroll: Boolean,
isClickable: {
type: Boolean,
default: true
},
enableHover: Boolean,
beforeDelay: {
type: Number,
default: 30
}
},
data() {
return {
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: true,
// #endif
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: false,
// #endif
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: {
canvasId() {
return `lime-echart${this._ && this._.uid || this._uid}`
},
offscreenCanvasId() {
return `${this.canvasId}_offscreen`
},
offscreenStyle() {
return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
},
canvasStyle() {
return this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : ''
}
},
// #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();
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 res = e?.detail?.data[0] || null;
if (res?.event) {
const data = JSON.parse(res.data)
if(res.event === 'inited') {
this.inited = true
} else if(res.event.startsWith('@')){
this.chart.dispatchAction(res.event.split('@')[1], data.options)
}
this.$emit(res.event, data);
} else if(res?.file){
this.file = res.data
} else if(!res[0] && JSON.stringify(res[0]) != '{}'){
console.error(res);
} else {
console.log(...res)
}
},
// #endif
setChart(callback) {
if(!this.chart) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
if(typeof callback === 'function' && this.chart) {
callback(this.chart);
}
// #ifdef APP-NVUE
if(typeof callback === 'function') {
this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.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(arguments && arguments.length < 1) {
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)
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
if(typeof callback === 'function') {
callback(this.chart)
} else {
return this.chart
}
// #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
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] || {}
return x ? touches[0] : this.getRelative(e, touches);
},
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)) || {};
const handler = this.chart.getZr().handler;
const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
dispatch.call(handler, 'mouseup', touch)
handler.processGesture(wrapTouch(e), 'end');
if(isClick) {
dispatch.call(handler, 'click', touch)
} else {
this.endTimer = setTimeout(() => {
dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
},50)
}
}
},
// #endif
// #ifdef H5
mousewheel(e){
if(this.chart) {
dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
}
}
// #endif
}
};
</script>
<style>
.lime-echart {
position: relative;
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
.lime-echart__canvas {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.lime-echart__mask {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 1;
}
/* #endif */
</style>

View File

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

View File

@ -0,0 +1,145 @@
// #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;
// #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) {
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
// #endif
// #ifdef APP-NVUE
export function base64ToPath(base64) {
return new Promise((resolve, reject) => {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
const time = new Date().getTime();
const filePath = `_doc/uniapp_temp/${time}.${format}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
}, (error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
})
}
// #endif
export function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true)
}, time)
})
}
export function getRect(selector, options = {}) {
const typeDefault = 'boundingClientRect'
const {
context,
type = typeDefault
} = options
return new Promise((resolve, reject) => {
const dom = uni.createSelectorQuery().in(context).select(selector);
const result = (rect) => {
if (rect) {
resolve(rect)
} else {
reject()
}
}
if (type == typeDefault) {
dom[type](result).exec()
} else {
dom[type]({
node: true,
size: true,
rect: true
}, result).exec()
}
});
};

View File

@ -0,0 +1,105 @@
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 : WebViewMessageEvent) => {
// event.stopPropagation()
// event.preventDefault()
const log = e.detail.data!.get('log')
const event = e.detail.data!.get('event')
const options = e.detail.data!.get('options')
const file = e.detail.data!.get('file')
if (log != null) {
console.log(log)
}
if (event != null && options != null) {
this.dispatchAction(event as string, options as UTSJSONObject)
}
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){
this.context.evalJS(`resize(${JSON.stringify(size)})`);
}
resize(){
this.context.evalJS(`resize()`);
}
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.temp.push(opt)
}
}

View File

@ -0,0 +1,95 @@
<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);
})
},
save() {
// this.$refs.chart.canvasToTempFilePath({
// success(res) {
// console.log('res::::', res)
// }
// })
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,96 @@
<template>
<view style="width: 100%; height: 408px;">
<l-echart ref="chartRef" @finished="init"></l-echart>
</view>
</template>
<script lang="uts">
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 uvue')
},
methods: {
init() {
const chartRef = this.$refs['chartRef'] as LEchartComponentPublicInstance
chartRef.init(chart => {
chart.setOption(this.option);
})
},
save() {
// this.$refs.chart.canvasToTempFilePath({
// success(res) {
// console.log('res::::', res)
// }
// })
}
}
}
</script>
<style>
</style>

View File

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

View File

@ -0,0 +1,89 @@
{
"id": "lime-echart",
"displayName": "echarts",
"version": "0.8.4",
"description": "echarts 全端兼容一款使echarts图表能跑在uniapp各端中的插件",
"keywords": [
"echarts",
"canvas",
"图表",
"可视化"
],
"repository": "https://gitee.com/liangei/lime-echart",
"engines": {
"HBuilderX": "^3.6.4"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
},
"dependencies": {
"echarts": "^5.4.1",
"zrender": "^5.4.3"
}
}

View File

@ -0,0 +1,413 @@
# echarts 图表 <span style="font-size:16px;">👑👑👑👑👑 <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
> 一个基于 JavaScript 的开源可视化图表库 [查看更多 站点1](https://limeui.qcoon.cn/#/echart) | [查看更多 站点2](http://liangei.gitee.io/limeui/#/echart) <br>
> 基于 echarts 做了兼容处理,更多示例请访问 [uni示例 站点1](https://limeui.qcoon.cn/#/echart-example) | [uni示例 站点2](http://liangei.gitee.io/limeui/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html) <br>
> Q群1046793420 <br>
## 平台兼容
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
| √ | √ | √ | √ | √ | √ | √ |
## 安装
- 第一步:在市场导入 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899)
- 第二步:选择插件依赖:
1、可以选插件内的`echarts`包或自定义包,自定义包[下载地址](https://echarts.apache.org/zh/builder.html)<br>
2、或者使用`npm`安装`echarts`
**注意**
* 🔔 echarts 5.3.0及以上
* 🔔 如果是 `cli` 项目请下载插件到`src`目录下的`uni_modules`,没有这个目录就创建一个
## 代码演示
### Vue2
```html
<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef" @finished="init"></l-echart></view>
```
#### 插件依赖 方式一:引入插件内的或自定义包
- 引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
```js
// 插件内的 二选一
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
// 自定义的 二选一 下载后放入项目的路径
import * as echarts from 'xxx/echarts.min'
```
#### 插件依赖 方式二npm包
- 自行安装 `echarts`
- 可根据自己的需要全量引入或按需引入
```cmd
pnpm add echarts
-or-
npm install echarts
```
```js
// 全量包 二选一
import * as echarts from 'echarts'
// 按需引入 二选一
import * as echarts from 'echarts/core';
import {LineChart, BarChart} from 'echarts/charts';
import {TitleComponent,TooltipComponent,GridComponent, DatasetComponent, TransformComponent, LegendComponent } from 'echarts/components';
// 标签自动布局,全局过渡动画等特性
import {LabelLayout,UniversalTransition} from 'echarts/features';
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 是必须的一步
import {CanvasRenderer} from 'echarts/renderers';
// 按需引入 注册必须的组件
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LineChart,
BarChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
```
```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
```html
<view style="width:750rpx; height:750rpx"><l-echart ref="chart" @finished="init"></l-echart></view>
```
#### 插件依赖 小程序引入插件内的包或自定义包
- 引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
- `require`仅支持相对路径,不支持路径别名
```js
// 插件内的 二选一
const echarts = require('../../uni_modules/lime-echart/static/echarts.min');
// 自定义的 二选一 下载后放入项目的路径
const echarts = require('xxx/xxx/echarts');
```
#### 非小程序端不支持引入插件内的包
- 由于vue3使用的是vite 不支持非`esm`格式,故不支持引入插件内的包
- 可使用`npm`
#### 插件依赖 方式二npm包
- 可自行安装 `echarts`
- 可根据自己的需要全量引入或按需引入
```cmd
pnpm add echarts
-or-
npm install echarts
```
```js
// 全量包 二选一
import * as echarts from 'echarts'
// 按需引入 二选一
import * as echarts from 'echarts/core';
import {LineChart, BarChart} from 'echarts/charts';
import {TitleComponent,TooltipComponent,GridComponent, DatasetComponent, TransformComponent, LegendComponent } from 'echarts/components';
// 标签自动布局,全局过渡动画等特性
import {LabelLayout,UniversalTransition} from 'echarts/features';
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 是必须的一步
import {CanvasRenderer} from 'echarts/renderers';
// 按需引入 注册必须的组件
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LineChart,
BarChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
```
```js
const chartRef = ref(null)
onMounted( ()=>{
// 组件能被调用必须是组件的节点已经被渲染到页面上
setTimeout(async()=>{
if(!chartRef.value) return
const myChart = await chartRef.value.init(echarts)
myChart.setOption(data)
},300)
})
```
### Uvue
- Uvue和Nvue不需要引入`echarts`,因为它们的实现方式是`webview`
```js
methods: {
async init() {
const chartRef = this.$refs['chartRef'] as LEchartComponentPublicInstance
const myChart = await chartRef.init()
myChart.setOption(this.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
var prefix = () => {
var UNDEFINED = 'undefined'
if(typeof wx !== UNDEFINED) return wx // 微信
if(typeof tt !== UNDEFINED) return tt // 字节 飞书
if(typeof swan !== UNDEFINED) return swan // 百度
if(typeof my !== UNDEFINED) return my // 支付宝
if(typeof dd !== UNDEFINED) return dd // 钉钉
if(typeof ks !== UNDEFINED) return ks // 快手
if(typeof jd !== UNDEFINED) return jd // 京东
if(typeof qa !== UNDEFINED) return qa // 快应用
if(typeof qq !== UNDEFINED) return qq // qq
if(typeof qh !== UNDEFINED) return qh // 360
if(typeof uni !== UNDEFINED) return uni
return null
}
//在 !function(t,e){"object"==typeof 下面加入 可能是第36行
var wx = prefix();
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
```
方式二:在`vite.config.js``define`设置环境
```js
// 或者在`vite.config.js``define`设置环境
import { defineConfig } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
const UNI_PLATFORM = {
"app": "uni",
"web": "uni",
"mp-weixin": "wx",
"mp-baidu": "swan",
"mp-alipay": "my",
"mp-toutiao": "tt",
"mp-lark": "tt",
"mp-qq": "qq",
"mp-kuaishou": "ks",
"mp-jd": "jd",
"mp-360": "qh",
"quickapp-webview-union": "qa",
"quickapp-webview-huawei": "qa",
"quickapp-webview": "qa",
}
export default defineConfig({
plugins: [uni()],
define: {
global: UNI_PLATFORM[process.env.UNI_PLATFORM],
wx: UNI_PLATFORM[process.env.UNI_PLATFORM]
}
});
```
## Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------------- | -------- | ------- | ------------ | ----- |
| custom-style | 自定义样式 | `string` | - | - |
| type | 指定 canvas 类型 | `string` | `2d` | |
| is-disable-scroll | 触摸图表时是否禁止页面滚动 | `boolean` | `false` | |
| beforeDelay | 延迟初始化 (毫秒) | `number` | `30` | |
| enableHover | PC端使用鼠标悬浮 | `boolean` | `false` | |
## 事件
| 参数 | 说明 |
| --------------- | --------------- |
| init(echarts, chart => {}) | 初始化调用函数,第一个参数是传入`echarts`,第二个参数是回调函数,回调函数的参数是 `chart` 实例 |
| setChart(chart => {}) | 已经初始化后,请使用这个方法,是个回调函数,参数是 `chart` 实例 |
| setOption(data) | [图表配置项](https://echarts.apache.org/zh/option.html#title),用于更新 ,传递是数据 `option` |
| clear() | 清空当前实例,会移除实例中所有的组件和图表。 |
| dispose() | 销毁实例 |
| showLoading() | 显示加载 |
| hideLoading() | 隐藏加载 |
| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(opt) | 用于生成图片,与官方使用方法一致,但不需要传`canvasId` |
## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,173 @@
<!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(...agrs) {
emit('log', {
log: agrs,
})
}
function emit(event, data) {
postMessage({
// event,
data
})
cache = []
}
function 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 = null) {
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, (options) => {
const obj = {}
Object.keys(options).forEach(key => {
if (key != 'event') {
obj[key] = options[key]
}
})
emit(key, {
event: key,
options: obj
})
})
} else {
chart.on(type, (options) => {
const obj = {}
Object.keys(options).forEach(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: file
})
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,33 @@
## 1.2.22023-01-28
- 修复 运行/打包 控制台警告问题
## 1.2.12022-09-05
- 修复 当 text 超过 max-num 时badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
## 1.1.72021-11-08
- 优化 升级ui
- 修改 size 属性默认值调整为 small
- 修改 type 属性,默认值调整为 errorinfo 替换 default
## 1.1.62021-09-22
- 修复 在字节小程序上样式不生效的 bug
## 1.1.52021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.42021-07-29
- 修复 去掉 nvue 不支持css 的 align-self 属性nvue 下不暂支持 absolute 属性
## 1.1.32021-06-24
- 优化 示例项目
## 1.1.12021-05-12
- 新增 组件示例地址
## 1.1.02021-05-12
- 新增 uni-badge 的 absolute 属性,支持定位
- 新增 uni-badge 的 offset 属性,支持定位偏移
- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
- 优化 uni-badge 属性 custom-style 支持以对象形式自定义样式
## 1.0.72021-05-07
- 修复 uni-badge 在 App 端数字小于10时不是圆形的bug
- 修复 uni-badge 在父元素不是 flex 布局时宽度缩小的bug
- 新增 uni-badge 属性 custom-style 支持自定义样式
## 1.0.62021-02-04
- 调整为uni_modules目录规范

View File

@ -0,0 +1,268 @@
<template>
<view class="uni-badge--x">
<slot />
<text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
class="uni-badge" @click="onClick()">{{displayValue}}</text>
</view>
</template>
<script>
/**
* Badge 数字角标
* @description 数字角标一般和其它控件列表9宫格等配合使用用于进行数量提示默认为实心灰色背景
* @tutorial https://ext.dcloud.net.cn/plugin?id=21
* @property {String} text 角标内容
* @property {String} size = [normal|small] 角标内容
* @property {String} type = [info|primary|success|warning|error] 颜色类型
* @value info 灰色
* @value primary 蓝色
* @value success 绿色
* @value warning 黄色
* @value error 红色
* @property {String} inverted = [true|false] 是否无需背景颜色
* @property {Number} maxNum 展示封顶的数字值超过 99 显示 99+
* @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
* @value rightTop 右上
* @value rightBottom 右下
* @value leftTop 左上
* @value leftBottom 左下
* @property {Array[number]} offset 距定位角中心点的偏移量只有存在 absolute 属性时有效例如[-10, -10] 表示向外偏移 10px[10, 10] 表示向 absolute 指定的内偏移 10px
* @property {String} isDot = [true|false] 是否显示为一个小点
* @event {Function} click 点击 Badge 触发事件
* @example <uni-badge text="1"></uni-badge>
*/
export default {
name: 'UniBadge',
emits: ['click'],
props: {
type: {
type: String,
default: 'error'
},
inverted: {
type: Boolean,
default: false
},
isDot: {
type: Boolean,
default: false
},
maxNum: {
type: Number,
default: 99
},
absolute: {
type: String,
default: ''
},
offset: {
type: Array,
default () {
return [0, 0]
}
},
text: {
type: [String, Number],
default: ''
},
size: {
type: String,
default: 'small'
},
customStyle: {
type: Object,
default () {
return {}
}
}
},
data() {
return {};
},
computed: {
width() {
return String(this.text).length * 8 + 12
},
classNames() {
const {
inverted,
type,
size,
absolute
} = this
return [
inverted ? 'uni-badge--' + type + '-inverted' : '',
'uni-badge--' + type,
'uni-badge--' + size,
absolute ? 'uni-badge--absolute' : ''
].join(' ')
},
positionStyle() {
if (!this.absolute) return {}
let w = this.width / 2,
h = 10
if (this.isDot) {
w = 5
h = 5
}
const x = `${- w + this.offset[0]}px`
const y = `${- h + this.offset[1]}px`
const whiteList = {
rightTop: {
right: x,
top: y
},
rightBottom: {
right: x,
bottom: y
},
leftBottom: {
left: x,
bottom: y
},
leftTop: {
left: x,
top: y
}
}
const match = whiteList[this.absolute]
return match ? match : whiteList['rightTop']
},
dotStyle() {
if (!this.isDot) return {}
return {
width: '10px',
minWidth: '0',
height: '10px',
padding: '0',
borderRadius: '10px'
}
},
displayValue() {
const {
isDot,
text,
maxNum
} = this
return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
}
},
methods: {
onClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
$uni-success: #4cd964 !default;
$uni-warning: #f0ad4e !default;
$uni-error: #dd524d !default;
$uni-info: #909399 !default;
$bage-size: 12px;
$bage-small: scale(0.8);
.uni-badge--x {
/* #ifdef APP-NVUE */
// align-self: flex-start;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-block;
/* #endif */
position: relative;
}
.uni-badge--absolute {
position: absolute;
}
.uni-badge--small {
transform: $bage-small;
transform-origin: center center;
}
.uni-badge {
/* #ifndef APP-NVUE */
display: flex;
overflow: hidden;
box-sizing: border-box;
font-feature-settings: "tnum";
min-width: 20px;
/* #endif */
justify-content: center;
flex-direction: row;
height: 20px;
padding: 0 4px;
line-height: 18px;
color: #fff;
border-radius: 100px;
background-color: $uni-info;
background-color: transparent;
border: 1px solid #fff;
text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-size: $bage-size;
/* #ifdef H5 */
z-index: 999;
cursor: pointer;
/* #endif */
&--info {
color: #fff;
background-color: $uni-info;
}
&--primary {
background-color: $uni-primary;
}
&--success {
background-color: $uni-success;
}
&--warning {
background-color: $uni-warning;
}
&--error {
background-color: $uni-error;
}
&--inverted {
padding: 0 5px 0 0;
color: $uni-info;
}
&--info-inverted {
color: $uni-info;
background-color: transparent;
}
&--primary-inverted {
color: $uni-primary;
background-color: transparent;
}
&--success-inverted {
color: $uni-success;
background-color: transparent;
}
&--warning-inverted {
color: $uni-warning;
background-color: transparent;
}
&--error-inverted {
color: $uni-error;
background-color: transparent;
}
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-badge",
"displayName": "uni-badge 数字角标",
"version": "1.2.2",
"description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
"keywords": [
"",
"badge",
"uni-ui",
"uniui",
"数字角标",
"徽章"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,10 @@
## Badge 数字角标
> **组件名uni-badge**
> 代码块: `uBadge`
数字角标一般和其它控件列表、9宫格等配合使用用于进行数量提示默认为实心灰色背景
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,26 @@
## 1.3.12021-12-20
- 修复 在vue页面下略缩图显示不正常的bug
## 1.3.02021-11-19
- 重构插槽的用法 header 替换为 title
- 新增 actions 插槽
- 新增 cover 封面图属性和插槽
- 新增 padding 内容默认内边距离
- 新增 margin 卡片默认外边距离
- 新增 spacing 卡片默认内边距
- 新增 shadow 卡片阴影属性
- 取消 mode 属性,可使用组合插槽代替
- 取消 note 属性 使用actions插槽代替
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card)
## 1.2.12021-07-30
- 优化 vue3下事件警告的问题
## 1.2.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.82021-07-01
- 优化 图文卡片无图片加载时,提供占位图标
- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持)
- 修复 thumbnail 不存在仍然占位的 bug
## 1.1.72021-05-12
- 新增 组件示例地址
## 1.1.62021-02-04
- 调整为uni_modules目录规范

View File

@ -0,0 +1,270 @@
<template>
<view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}"
:style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}">
<!-- 封面 -->
<slot name="cover">
<view v-if="cover" class="uni-card__cover">
<image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image>
</view>
</slot>
<slot name="title">
<view v-if="title || extra" class="uni-card__header">
<!-- 卡片标题 -->
<view class="uni-card__header-box" @click="onClick('title')">
<view v-if="thumbnail" class="uni-card__header-avatar">
<image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" />
</view>
<view class="uni-card__header-content">
<text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text>
<text v-if="title&&subTitle"
class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text>
</view>
</view>
<view class="uni-card__header-extra" @click="onClick('extra')">
<text class="uni-card__header-extra-text">{{ extra }}</text>
</view>
</view>
</slot>
<!-- 卡片内容 -->
<view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')">
<slot></slot>
</view>
<view class="uni-card__actions" @click="onClick('actions')">
<slot name="actions"></slot>
</view>
</view>
</template>
<script>
/**
* Card 卡片
* @description 卡片视图组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=22
* @property {String} title 标题文字
* @property {String} subTitle 副标题
* @property {Number} padding 内容内边距
* @property {Number} margin 卡片外边距
* @property {Number} spacing 卡片内边距
* @property {String} extra 标题额外信息
* @property {String} cover 封面图本地路径需要引入
* @property {String} thumbnail 标题左侧缩略图
* @property {Boolean} is-full = [true | false] 卡片内容是否通栏 true 时将去除padding值
* @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影
* @property {String} shadow 卡片阴影
* @property {Boolean} border 卡片边框
* @event {Function} click 点击 Card 触发事件
*/
export default {
name: 'UniCard',
emits: ['click'],
props: {
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
padding: {
type: String,
default: '10px'
},
margin: {
type: String,
default: '15px'
},
spacing: {
type: String,
default: '0 10px'
},
extra: {
type: String,
default: ''
},
cover: {
type: String,
default: ''
},
thumbnail: {
type: String,
default: ''
},
isFull: {
//
type: Boolean,
default: false
},
isShadow: {
//
type: Boolean,
default: true
},
shadow: {
type: String,
default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)'
},
border: {
type: Boolean,
default: true
}
},
methods: {
onClick(type) {
this.$emit('click', type)
}
}
}
</script>
<style lang="scss">
$uni-border-3: #EBEEF5 !default;
$uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
$uni-main-color: #3a3a3a !default;
$uni-base-color: #6a6a6a !default;
$uni-secondary-color: #909399 !default;
$uni-spacing-sm: 8px !default;
$uni-border-color:$uni-border-3;
$uni-shadow: $uni-shadow-base;
$uni-card-title: 15px;
$uni-cart-title-color:$uni-main-color;
$uni-card-subtitle: 12px;
$uni-cart-subtitle-color:$uni-secondary-color;
$uni-card-spacing: 10px;
$uni-card-content-color: $uni-base-color;
.uni-card {
margin: $uni-card-spacing;
padding: 0 $uni-spacing-sm;
border-radius: 4px;
overflow: hidden;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
background-color: #fff;
flex: 1;
.uni-card__cover {
position: relative;
margin-top: $uni-card-spacing;
flex-direction: row;
overflow: hidden;
border-radius: 4px;
.uni-card__cover-image {
flex: 1;
// width: 100%;
/* #ifndef APP-PLUS */
vertical-align: middle;
/* #endif */
}
}
.uni-card__header {
display: flex;
border-bottom: 1px $uni-border-color solid;
flex-direction: row;
align-items: center;
padding: $uni-card-spacing;
overflow: hidden;
.uni-card__header-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
overflow: hidden;
}
.uni-card__header-avatar {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 5px;
margin-right: $uni-card-spacing;
.uni-card__header-avatar-image {
flex: 1;
width: 40px;
height: 40px;
}
}
.uni-card__header-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
flex: 1;
// height: 40px;
overflow: hidden;
.uni-card__header-content-title {
font-size: $uni-card-title;
color: $uni-cart-title-color;
// line-height: 22px;
}
.uni-card__header-content-subtitle {
font-size: $uni-card-subtitle;
margin-top: 5px;
color: $uni-cart-subtitle-color;
}
}
.uni-card__header-extra {
line-height: 12px;
.uni-card__header-extra-text {
font-size: 12px;
color: $uni-cart-subtitle-color;
}
}
}
.uni-card__content {
padding: $uni-card-spacing;
font-size: 14px;
color: $uni-card-content-color;
line-height: 22px;
}
.uni-card__actions {
font-size: 12px;
}
}
.uni-card--border {
border: 1px solid $uni-border-color;
}
.uni-card--shadow {
position: relative;
/* #ifndef APP-NVUE */
box-shadow: $uni-shadow;
/* #endif */
}
.uni-card--full {
margin: 0;
border-left-width: 0;
border-left-width: 0;
border-radius: 0;
}
/* #ifndef APP-NVUE */
.uni-card--full:after {
border-radius: 0;
}
/* #endif */
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
</style>

View File

@ -0,0 +1,90 @@
{
"id": "uni-card",
"displayName": "uni-card 卡片",
"version": "1.3.1",
"description": "Card 组件,提供常见的卡片样式。",
"keywords": [
"uni-ui",
"uniui",
"card",
"",
"卡片"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,12 @@
## Card 卡片
> **组件名uni-card**
> 代码块: `uCard`
卡片视图组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,37 @@
## 1.0.72024-01-20
- 修复 长文本回显超过容器的bug超过容器部分显示省略号
## 1.0.62023-04-12
- 修复 微信小程序点击时会改变背景颜色的 bug
## 1.0.52023-02-03
- 修复 禁用时会显示清空按钮
## 1.0.42023-02-02
- 优化 查询条件短期内多次变更只查询最后一次变更后的结果
- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue
## 1.0.32023-01-16
- 修复 不关联服务空间报错的问题
## 1.0.22023-01-14
- 新增 属性 `format` 可用于格式化显示选项内容
## 1.0.12022-12-06
- 修复 当where变化时数据不会自动更新的问题
## 0.1.92022-09-05
- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
## 0.1.82022-08-29
- 修复 点击的位置不准确
## 0.1.72022-08-12
- 新增 支持 disabled 属性
## 0.1.62022-07-06
- 修复 pc端宽度异常的bug
## 0.1.5
- 修复 pc端宽度异常的bug
## 0.1.42022-07-05
- 优化 显示样式
## 0.1.32022-06-02
- 修复 localdata 赋值不生效的 bug
- 新增 支持 uni.scss 修改颜色
- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
## 0.1.22022-05-08
- 修复 当 value 为 0 时选择不生效的 bug
## 0.1.12022-05-07
- 新增 记住上次的选项(仅 collection 存在时有效)
## 0.1.02022-04-22
- 初始化

View File

@ -0,0 +1,527 @@
<template>
<view class="uni-stat__select">
<span v-if="label" class="uni-label-text hide-on-phone">{{label + ''}}</span>
<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
<view class="uni-select" :class="{'uni-select--disabled':disabled}">
<view class="uni-select__input-box" @click="toggleSelector">
<view v-if="current" class="uni-select__input-text">{{textShow}}</view>
<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
<view v-if="current && clear && !disabled" @click.stop="clearVal" >
<uni-icons type="clear" color="#c0c4cc" size="24"/>
</view>
<view v-else>
<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
</view>
</view>
<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
<view class="uni-select__selector" v-if="showSelector">
<view class="uni-popper__arrow"></view>
<scroll-view scroll-y="true" class="uni-select__selector-scroll">
<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0">
<text>{{emptyTips}}</text>
</view>
<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
@click="change(item)">
<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染的下拉框组件
* @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
* @property {String} value 默认值
* @property {Array} localdata 本地数据 格式 [{text:'',value:''}]
* @property {Boolean} clear 是否可以清空已选项
* @property {Boolean} emptyText 没有数据时显示的文字 本地数据无效
* @property {String} label 左侧标题
* @property {String} placeholder 输入框的提示文字
* @property {Boolean} disabled 是否禁用
* @event {Function} change 选中发生变化触发
*/
export default {
name: "uni-data-select",
mixins: [uniCloud.mixinDatacom || {}],
props: {
localdata: {
type: Array,
default () {
return []
}
},
value: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
label: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
emptyTips: {
type: String,
default: '无选项'
},
clear: {
type: Boolean,
default: true
},
defItem: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: false
},
// field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
format: {
type: String,
default: ''
},
},
data() {
return {
showSelector: false,
current: '',
mixinDatacomResData: [],
apps: [],
channels: [],
cacheKey: "uni-data-select-lastSelectedValue",
};
},
created() {
this.debounceGet = this.debounce(() => {
this.query();
}, 300);
if (this.collection && !this.localdata.length) {
this.debounceGet();
}
},
computed: {
typePlaceholder() {
const text = {
'opendb-stat-app-versions': '版本',
'opendb-app-channels': '渠道',
'opendb-app-list': '应用'
}
const common = this.placeholder
const placeholder = text[this.collection]
return placeholder ?
common + placeholder :
common
},
valueCom(){
// #ifdef VUE3
return this.modelValue;
// #endif
// #ifndef VUE3
return this.value;
// #endif
},
textShow(){
//
let text = this.current;
if (text.length > 10) {
return text.slice(0, 25) + '...';
}
return text;
}
},
watch: {
localdata: {
immediate: true,
handler(val, old) {
if (Array.isArray(val) && old !== val) {
this.mixinDatacomResData = val
}
}
},
valueCom(val, old) {
this.initDefVal()
},
mixinDatacomResData: {
immediate: true,
handler(val) {
if (val.length) {
this.initDefVal()
}
}
},
},
methods: {
debounce(fn, time = 100){
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
},
//
query(){
this.mixinDatacomEasyGet();
},
//
onMixinDatacomPropsChange(){
if (this.collection) {
this.debounceGet();
}
},
initDefVal() {
let defValue = ''
if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
defValue = this.valueCom
} else {
let strogeValue
if (this.collection) {
strogeValue = this.getCache()
}
if (strogeValue || strogeValue === 0) {
defValue = strogeValue
} else {
let defItem = ''
if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
defItem = this.mixinDatacomResData[this.defItem - 1].value
}
defValue = defItem
}
if (defValue || defValue === 0) {
this.emit(defValue)
}
}
const def = this.mixinDatacomResData.find(item => item.value === defValue)
this.current = def ? this.formatItemName(def) : ''
},
/**
* @param {[String, Number]} value
* 判断用户给的 value 是否同时为禁用状态
*/
isDisabled(value) {
let isDisabled = false;
this.mixinDatacomResData.forEach(item => {
if (item.value === value) {
isDisabled = item.disable
}
})
return isDisabled;
},
clearVal() {
this.emit('')
if (this.collection) {
this.removeCache()
}
},
change(item) {
if (!item.disable) {
this.showSelector = false
this.current = this.formatItemName(item)
this.emit(item.value)
}
},
emit(val) {
this.$emit('input', val)
this.$emit('update:modelValue', val)
this.$emit('change', val)
if (this.collection) {
this.setCache(val);
}
},
toggleSelector() {
if (this.disabled) {
return
}
this.showSelector = !this.showSelector
},
formatItemName(item) {
let {
text,
value,
channel_code
} = item
channel_code = channel_code ? `(${channel_code})` : ''
if (this.format) {
//
let str = "";
str = this.format;
for (let key in item) {
str = str.replace(new RegExp(`{${key}}`,"g"),item[key]);
}
return str;
} else {
return this.collection.indexOf('app-list') > 0 ?
`${text}(${value})` :
(
text ?
text :
`未命名${channel_code}`
)
}
},
//
getLoadData(){
return this.mixinDatacomResData;
},
// key
getCurrentCacheKey(){
return this.collection;
},
//
getCache(name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
return cacheData[name];
},
//
setCache(value, name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
cacheData[name] = value;
uni.setStorageSync(this.cacheKey, cacheData);
},
//
removeCache(name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
delete cacheData[name];
uni.setStorageSync(this.cacheKey, cacheData);
},
}
}
</script>
<style lang="scss">
$uni-base-color: #6a6a6a !default;
$uni-main-color: #333 !default;
$uni-secondary-color: #909399 !default;
$uni-border-3: #e5e5e5;
/* #ifndef APP-NVUE */
@media screen and (max-width: 500px) {
.hide-on-phone {
display: none;
}
}
/* #endif */
.uni-stat__select {
display: flex;
align-items: center;
// padding: 15px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
width: 100%;
flex: 1;
box-sizing: border-box;
}
.uni-stat-box {
width: 100%;
flex: 1;
}
.uni-stat__actived {
width: 100%;
flex: 1;
// outline: 1px solid #2979ff;
}
.uni-label-text {
font-size: 14px;
font-weight: bold;
color: $uni-base-color;
margin: auto 0;
margin-right: 5px;
}
.uni-select {
font-size: 14px;
border: 1px solid $uni-border-3;
box-sizing: border-box;
border-radius: 4px;
padding: 0 5px;
padding-left: 10px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
user-select: none;
/* #endif */
flex-direction: row;
align-items: center;
border-bottom: solid 1px $uni-border-3;
width: 100%;
flex: 1;
height: 35px;
&--disabled {
background-color: #f5f7fa;
cursor: not-allowed;
}
}
.uni-select__label {
font-size: 16px;
// line-height: 22px;
height: 35px;
padding-right: 10px;
color: $uni-secondary-color;
}
.uni-select__input-box {
height: 35px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-select__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-select__input-plac {
font-size: 14px;
color: $uni-secondary-color;
}
.uni-select__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: calc(100% + 12px);
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 3;
padding: 4px 0;
}
.uni-select__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
/* #ifdef H5 */
@media (min-width: 768px) {
.uni-select__selector-scroll {
max-height: 600px;
}
}
/* #endif */
.uni-select__selector-empty,
.uni-select__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 35px;
font-size: 14px;
text-align: center;
/* border-bottom: solid 1px $uni-border-3; */
padding: 0px 10px;
}
.uni-select__selector-item:hover {
background-color: #f9f9f9;
}
.uni-select__selector-empty:last-child,
.uni-select__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
.uni-select__selector__disabled {
opacity: 0.4;
cursor: default;
}
/* picker 弹出层通用的指示小三角 */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
.uni-select__input-text {
// width: 280px;
width: 100%;
color: $uni-main-color;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
.uni-select__input-placeholder {
color: $uni-base-color;
font-size: 12px;
}
.uni-select--mask {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 2;
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-data-select",
"displayName": "uni-data-select 下拉框选择器",
"version": "1.0.7",
"description": "通过数据驱动的下拉框选择器",
"keywords": [
"uni-ui",
"select",
"uni-data-select",
"下拉框",
"下拉选"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.1"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-load-more"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "u",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,8 @@
## DataSelect 下拉框选择器
> **组件名uni-data-select**
> 代码块: `uDataSelect`
当选项过多时,使用下拉菜单展示并选择内容
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,10 @@
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
## 0.0.52021-07-08
- 调整 默认时间不再是当前时间,而是显示'-'字符
## 0.0.42021-05-12
- 新增 组件示例地址
## 0.0.32021-02-04
- 调整为uni_modules目录规范
- 修复 iOS 平台日期格式化出错的问题

View File

@ -0,0 +1,200 @@
// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型
function pad(str, length = 2) {
str += ''
while (str.length < length) {
str = '0' + str
}
return str.slice(-length)
}
const parser = {
yyyy: (dateObj) => {
return pad(dateObj.year, 4)
},
yy: (dateObj) => {
return pad(dateObj.year)
},
MM: (dateObj) => {
return pad(dateObj.month)
},
M: (dateObj) => {
return dateObj.month
},
dd: (dateObj) => {
return pad(dateObj.day)
},
d: (dateObj) => {
return dateObj.day
},
hh: (dateObj) => {
return pad(dateObj.hour)
},
h: (dateObj) => {
return dateObj.hour
},
mm: (dateObj) => {
return pad(dateObj.minute)
},
m: (dateObj) => {
return dateObj.minute
},
ss: (dateObj) => {
return pad(dateObj.second)
},
s: (dateObj) => {
return dateObj.second
},
SSS: (dateObj) => {
return pad(dateObj.millisecond, 3)
},
S: (dateObj) => {
return dateObj.millisecond
},
}
// 这都n年了iOS依然不认识2020-12-12需要转换为2020/12/12
function getDate(time) {
if (time instanceof Date) {
return time
}
switch (typeof time) {
case 'string':
{
// 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000
if (time.indexOf('T') > -1) {
return new Date(time)
}
return new Date(time.replace(/-/g, '/'))
}
default:
return new Date(time)
}
}
export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') {
if (!date && date !== 0) {
return ''
}
date = getDate(date)
const dateObj = {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds(),
millisecond: date.getMilliseconds()
}
const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/
let flag = true
let result = format
while (flag) {
flag = false
result = result.replace(tokenRegExp, function(matched) {
flag = true
return parser[matched](dateObj)
})
}
return result
}
export function friendlyDate(time, {
locale = 'zh',
threshold = [60000, 3600000],
format = 'yyyy/MM/dd hh:mm:ss'
}) {
if (time === '-') {
return time
}
if (!time && time !== 0) {
return ''
}
const localeText = {
zh: {
year: '年',
month: '月',
day: '天',
hour: '小时',
minute: '分钟',
second: '秒',
ago: '前',
later: '后',
justNow: '刚刚',
soon: '马上',
template: '{num}{unit}{suffix}'
},
en: {
year: 'year',
month: 'month',
day: 'day',
hour: 'hour',
minute: 'minute',
second: 'second',
ago: 'ago',
later: 'later',
justNow: 'just now',
soon: 'soon',
template: '{num} {unit} {suffix}'
}
}
const text = localeText[locale] || localeText.zh
let date = getDate(time)
let ms = date.getTime() - Date.now()
let absMs = Math.abs(ms)
if (absMs < threshold[0]) {
return ms < 0 ? text.justNow : text.soon
}
if (absMs >= threshold[1]) {
return formatDate(date, format)
}
let num
let unit
let suffix = text.later
if (ms < 0) {
suffix = text.ago
ms = -ms
}
const seconds = Math.floor((ms) / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
const days = Math.floor(hours / 24)
const months = Math.floor(days / 30)
const years = Math.floor(months / 12)
switch (true) {
case years > 0:
num = years
unit = text.year
break
case months > 0:
num = months
unit = text.month
break
case days > 0:
num = days
unit = text.day
break
case hours > 0:
num = hours
unit = text.hour
break
case minutes > 0:
num = minutes
unit = text.minute
break
default:
num = seconds
unit = text.second
break
}
if (locale === 'en') {
if (num === 1) {
num = 'a'
} else {
unit += 's'
}
}
return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g,
suffix)
}

View File

@ -0,0 +1,88 @@
<template>
<text>{{dateShow}}</text>
</template>
<script>
import {friendlyDate} from './date-format.js'
/**
* Dateformat 日期格式化
* @description 日期格式化组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3279
* @property {Object|String|Number} date 日期对象/日期字符串/时间戳
* @property {String} locale 格式化使用的语言
* @value zh 中文
* @value en 英文
* @property {Array} threshold 应用不同类型格式化的阈值
* @property {String} format 输出日期字符串时的格式
*/
export default {
name: 'uniDateformat',
props: {
date: {
type: [Object, String, Number],
default () {
return '-'
}
},
locale: {
type: String,
default: 'zh',
},
threshold: {
type: Array,
default () {
return [0, 0]
}
},
format: {
type: String,
default: 'yyyy/MM/dd hh:mm:ss'
},
// refreshRate使使
refreshRate: {
type: [Number, String],
default: 0
}
},
data() {
return {
refreshMark: 0
}
},
computed: {
dateShow() {
this.refreshMark
return friendlyDate(this.date, {
locale: this.locale,
threshold: this.threshold,
format: this.format
})
}
},
watch: {
refreshRate: {
handler() {
this.setAutoRefresh()
},
immediate: true
}
},
methods: {
refresh() {
this.refreshMark++
},
setAutoRefresh() {
clearInterval(this.refreshInterval)
if (this.refreshRate) {
this.refreshInterval = setInterval(() => {
this.refresh()
}, parseInt(this.refreshRate))
}
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,88 @@
{
"id": "uni-dateformat",
"displayName": "uni-dateformat 日期格式化",
"version": "1.0.0",
"description": "日期格式化组件可以将日期格式化为1分钟前、刚刚等形式",
"keywords": [
"uni-ui",
"uniui",
"日期格式化",
"时间格式化",
"格式化时间",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,11 @@
### DateFormat 日期格式化
> **组件名uni-dateformat**
> 代码块: `uDateformat`
日期格式化组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,103 @@
## 1.1.122024-01-29
- 补充 adjust-position文档属性补充
## 1.1.112024-01-29
- 补充 adjust-position属性传递值Boolean当键盘弹起时是否自动上推页面
## 1.1.102024-01-22
- 去除 移除无用的log输出
## 1.1.92023-04-11
- 修复 vue3 下 keyboardheightchange 事件报错的bug
## 1.1.82023-03-29
- 优化 trim 属性默认值
## 1.1.72023-03-29
- 新增 cursor-spacing 属性
## 1.1.62023-01-28
- 新增 keyboardheightchange 事件,可监听键盘高度变化
## 1.1.52022-11-29
- 优化 主题样式
## 1.1.42022-10-27
- 修复 props 中背景颜色无默认值的bug
## 1.1.02022-06-30
- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
- 新增 clear 事件,点击右侧叉号图标触发
- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
## 1.0.52022-06-07
- 优化 clearable 显示策略
## 1.0.42022-06-07
- 优化 clearable 显示策略
## 1.0.32022-05-20
- 修复 关闭图标某些情况下无法取消的 bug
## 1.0.22022-04-12
- 修复 默认值不生效的 bug
## 1.0.12022-04-02
- 修复 value 不能为 0 的 bug
## 1.0.02021-11-19
- 优化 组件 UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
## 0.1.42021-08-20
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.32021-08-11
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.22021-07-30
- 优化 vue3 下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.02021-07-13
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.162021-06-29
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.152021-06-21
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.142021-06-18
- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.132021-06-04
- 修复 disabled 状态可清出内容的 bug
## 0.0.122021-05-12
- 新增 组件示例地址
## 0.0.112021-05-07
- 修复 input-border 属性不生效的问题
## 0.0.102021-04-30
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.92021-02-05
- 调整为 uni_modules 目录规范
- 优化 兼容 nvue 页面

View File

@ -0,0 +1,54 @@
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行 false - 延迟执行
*/
export const debounce = function(func, wait = 1000, immediate = true) {
let timer;
return function() {
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 使用表时间戳在时间段开始的时候触发 2 使用表定时器在时间段结束的时候触发
*/
export const throttle = (func, wait = 1000, type = 1) => {
let previous = 0;
let timeout;
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}

View File

@ -0,0 +1,664 @@
<template>
<view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle">
<view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')" size="22"></uni-icons>
<textarea
v-if="type === 'textarea'"
class="uni-easyinput__content-textarea"
:class="{ 'input-padding': inputBorder }"
:name="name"
:value="val"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength"
:focus="focused"
:autoHeight="autoHeight"
:cursor-spacing="cursorSpacing"
:adjust-position="adjustPosition"
@input="onInput"
@blur="_Blur"
@focus="_Focus"
@confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"
></textarea>
<input
v-else
:type="type === 'password' ? 'text' : type"
class="uni-easyinput__content-input"
:style="inputStyle"
:name="name"
:value="val"
:password="!showPassword && type === 'password'"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
placeholder-class="uni-easyinput__placeholder-class"
:disabled="disabled"
:maxlength="inputMaxlength"
:focus="focused"
:confirmType="confirmType"
:cursor-spacing="cursorSpacing"
:adjust-position="adjustPosition"
@focus="_Focus"
@blur="_Blur"
@input="onInput"
@confirm="onConfirm"
@keyboardheightchange="onkeyboardheightchange"
/>
<template v-if="type === 'password' && passwordIcon">
<!-- 开启密码时显示小眼睛 -->
<uni-icons
v-if="isVal"
class="content-clear-icon"
:class="{ 'is-textarea-icon': type === 'textarea' }"
:type="showPassword ? 'eye-slash-filled' : 'eye-filled'"
:size="22"
:color="focusShow ? primaryColor : '#c0c4cc'"
@click="onEyes"
></uni-icons>
</template>
<template v-else-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')" size="22"></uni-icons>
</template>
<template v-else>
<uni-icons
v-if="clearable && isVal && !disabled && type !== 'textarea'"
class="content-clear-icon"
:class="{ 'is-textarea-icon': type === 'textarea' }"
type="clear"
:size="clearSize"
:color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'"
@click="onClear"
></uni-icons>
</template>
<slot name="right"></slot>
</view>
</view>
</template>
<script>
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验包括 "text" "textarea" 类型
* @tutorial https://ext.dcloud.net.cn/plugin?id=3455
* @property {String} value 输入内容
* @property {String } type 输入框的类型默认text password/text/textarea/..
* @value text 文本输入键盘
* @value textarea 多行文本输入键盘
* @value password 密码输入键盘
* @value number 数字输入键盘注意iOS上app-vue弹出的数字键盘并非9宫格方式
* @value idcard 身份证输入键盘支付宝百度QQ小程序
* @value digit 带小数点的数字键盘 App的nvue页面微信支付宝百度头条QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件点击可清空输入框内容默认true
* @property {Boolean} autoHeight 是否自动增高输入区域type为textarea时有效默认true
* @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式字符串)"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点默认false
* @property {Boolean} disabled 是否禁用默认false
* @property {Number } maxlength 最大输入长度设置为 -1 的时候不限制最大长度默认140
* @property {String } confirmType 设置键盘右下角按钮的文字仅在type="text"时生效默认done
* @property {Number } clearSize 清除图标的大小单位px默认15
* @property {String} prefixIcon 输入框头部图标
* @property {String} suffixIcon 输入框尾部图标
* @property {String} primaryColor 设置主题色默认#2979ff
* @property {Boolean} trim 是否自动去除两端的空格
* @property {Boolean} cursorSpacing 指定光标与键盘的距离单位 px
* @property {Boolean} ajust-position 当键盘弹起时是否上推内容默认值true
* @value both 去除两端空格
* @value left 去除左侧空格
* @value right 去除右侧空格
* @value start 去除左侧空格
* @value end 去除右侧空格
* @value all 去除全部空格
* @value none 不去除空格
* @property {Boolean} inputBorder 是否显示input输入框的边框默认true
* @property {Boolean} passwordIcon type=password时是否显示小眼睛图标
* @property {Object} styles 自定义颜色
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/
function obj2strClass(obj) {
let classess = '';
for (let key in obj) {
const val = obj[key];
if (val) {
classess += `${key} `;
}
}
return classess;
}
function obj2strStyle(obj) {
let style = '';
for (let key in obj) {
const val = obj[key];
style += `${key}:${val};`;
}
return style;
}
export default {
name: 'uni-easyinput',
emits: ['click', 'iconClick', 'update:modelValue', 'input', 'focus', 'blur', 'confirm', 'clear', 'eyes', 'change', 'keyboardheightchange'],
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
options: {
virtualHost: true
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
}
},
props: {
name: String,
value: [Number, String],
modelValue: [Number, String],
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ' '
},
placeholderStyle: String,
focus: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
clearSize: {
type: [Number, String],
default: 24
},
inputBorder: {
type: Boolean,
default: true
},
prefixIcon: {
type: String,
default: ''
},
suffixIcon: {
type: String,
default: ''
},
trim: {
type: [Boolean, String],
default: false
},
cursorSpacing: {
type: Number,
default: 0
},
passwordIcon: {
type: Boolean,
default: true
},
adjustPosition:{
type: Boolean,
default: true
},
primaryColor: {
type: String,
default: '#2979ff'
},
styles: {
type: Object,
default() {
return {
color: '#333',
backgroundColor: '#fff',
disableColor: '#F7F6F6',
borderColor: '#e5e5e5'
};
}
},
errorMessage: {
type: [String, Boolean],
default: ''
}
},
data() {
return {
focused: false,
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false,
focusShow: false,
localMsg: '',
isEnter: false // 使
};
},
computed: {
//
isVal() {
const val = this.val;
// fixed by mehaotian 00
if (val || val === 0) {
return true;
}
return false;
},
msg() {
// console.log('computed', this.form, this.formItem);
// if (this.form) {
// return this.errorMessage || this.formItem.errMsg;
// }
// TODO formItem errMsg
return this.localMsg || this.errorMessage;
},
// uniappinputmaxlength
inputMaxlength() {
return Number(this.maxlength);
},
// style
boxStyle() {
return `color:${this.inputBorder && this.msg ? '#e43d33' : this.styles.color};`;
},
// input
inputContentClass() {
return obj2strClass({
'is-input-border': this.inputBorder,
'is-input-error-border': this.inputBorder && this.msg,
'is-textarea': this.type === 'textarea',
'is-disabled': this.disabled,
'is-focused': this.focusShow
});
},
inputContentStyle() {
const focusColor = this.focusShow ? this.primaryColor : this.styles.borderColor;
const borderColor = this.inputBorder && this.msg ? '#dd524d' : focusColor;
return obj2strStyle({
'border-color': borderColor || '#e5e5e5',
'background-color': this.disabled ? this.styles.disableColor : this.styles.backgroundColor
});
},
// input
inputStyle() {
const paddingRight = this.type === 'password' || this.clearable || this.prefixIcon ? '' : '10px';
return obj2strStyle({
'padding-right': paddingRight,
'padding-left': this.prefixIcon ? '' : '10px'
});
}
},
watch: {
value(newVal) {
this.val = newVal;
},
modelValue(newVal) {
this.val = newVal;
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
}
},
created() {
this.init();
// TODO vue3 computed inject formItem.errMsg
if (this.form && this.formItem) {
this.$watch('formItem.errMsg', newVal => {
this.localMsg = newVal;
});
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
},
methods: {
/**
* 初始化变量值
*/
init() {
if (this.value || this.value === 0) {
this.val = this.value;
} else if (this.modelValue || this.modelValue === 0 || this.modelValue === '') {
this.val = this.modelValue;
} else {
this.val = null;
}
},
/**
* 点击图标时触发
* @param {Object} type
*/
onClickIcon(type) {
this.$emit('iconClick', type);
},
/**
* 显示隐藏内容密码框时生效
*/
onEyes() {
this.showPassword = !this.showPassword;
this.$emit('eyes', this.showPassword);
},
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value;
//
if (this.trim) {
if (typeof this.trim === 'boolean' && this.trim) {
value = this.trimStr(value);
}
if (typeof this.trim === 'string') {
value = this.trimStr(value, this.trim);
}
}
if (this.errMsg) this.errMsg = '';
this.val = value;
// TODO vue2
this.$emit('input', value);
// TODO  vue3
this.$emit('update:modelValue', value);
},
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true;
});
this.$emit('focus', null);
},
_Focus(event) {
this.focusShow = true;
this.$emit('focus', event);
},
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false;
this.$emit('focus', null);
},
_Blur(event) {
let value = event.detail.value;
this.focusShow = false;
this.$emit('blur', event);
// eventstring
if (this.isEnter === false) {
this.$emit('change', this.val);
}
//
if (this.form && this.formItem) {
const { validateTrigger } = this.form;
if (validateTrigger === 'blur') {
this.formItem.onFieldChange();
}
}
},
/**
* 按下键盘的发送键
* @param {Object} e
*/
onConfirm(e) {
this.$emit('confirm', this.val);
this.isEnter = true;
this.$emit('change', this.val);
this.$nextTick(() => {
this.isEnter = false;
});
},
/**
* 清理内容
* @param {Object} event
*/
onClear(event) {
this.val = '';
// TODO vue2
this.$emit('input', '');
// TODO vue2
// TODO  vue3
this.$emit('update:modelValue', '');
//
this.$emit('clear');
},
/**
* 键盘高度发生变化的时候触发此事件
* 兼容性微信小程序2.7.0+App 3.1.0+
* @param {Object} event
*/
onkeyboardheightchange(event) {
this.$emit("keyboardheightchange",event);
},
/**
* 去除空格
*/
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
} else if (pos === 'left') {
return str.trimLeft();
} else if (pos === 'right') {
return str.trimRight();
} else if (pos === 'start') {
return str.trimStart();
} else if (pos === 'end') {
return str.trimEnd();
} else if (pos === 'all') {
return str.replace(/\s+/g, '');
} else if (pos === 'none') {
return str;
}
return str;
}
}
};
</script>
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #dcdfe6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
position: relative;
text-align: left;
color: #333;
font-size: 14px;
}
.uni-easyinput__content {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
// border
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
// min-height: 36px;
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
// font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
.is-textarea-icon {
margin-top: 5px;
}
.uni-easyinput__content-textarea {
position: relative;
overflow: hidden;
flex: 1;
line-height: 1.5;
font-size: 14px;
margin: 6px;
margin-left: 0;
height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
/* #endif */
}
.input-padding {
padding-left: 10px;
}
.content-clear-icon {
padding: 0 5px;
}
.label-icon {
margin-right: 5px;
margin-top: -1px;
}
//
.is-input-border {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
}
.uni-error-message {
position: absolute;
bottom: -17px;
left: 0;
line-height: 12px;
color: $uni-error;
font-size: 12px;
text-align: left;
}
.uni-error-msg--boeder {
position: relative;
bottom: 0;
line-height: 22px;
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);
}
}
.uni-easyinput--border {
margin-bottom: 0;
padding: 10px 15px;
// padding-bottom: 0;
border-top: 1px #eee solid;
}
.uni-easyinput-error {
padding-bottom: 0;
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
.is-disabled {
background-color: #f7f6f6;
color: #d5d5d5;
.uni-easyinput__placeholder-class {
color: #d5d5d5;
font-size: 12px;
}
}
</style>

View File

@ -0,0 +1,87 @@
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.1.12",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,11 @@
### Easyinput 增强输入框
> **组件名uni-easyinput**
> 代码块: `uEasyinput`
easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的easyinput 内置了边框,图标等,同时包含 input 所有功能
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,40 @@
## 2.0.92024-01-12
fix: 修复图标大小默认值错误的问题
## 2.0.82023-12-14
- 修复 项目未使用 ts 情况下打包报错的bug
## 2.0.72023-12-14
- 修复 size 属性为 string 时不加单位导致尺寸异常的bug
## 2.0.62023-12-11
- 优化 兼容老版本icon类型如 top bottom 等
## 2.0.52023-12-11
- 优化 兼容老版本icon类型如 top bottom 等
## 2.0.42023-12-06
- 优化 uni-app x 下示例项目图标排序
## 2.0.32023-12-06
- 修复 nvue下引入组件报错的bug
## 2.0.22023-12-05
-优化 size 属性支持单位
## 2.0.12023-12-05
- 新增 uni-app x 支持定义图标
## 1.3.52022-01-24
- 优化 size 属性可以传入不带单位的字符串数值
## 1.3.42022-01-24
- 优化 size 支持其他单位
## 1.3.32022-01-17
- 修复 nvue 有些图标不显示的bug兼容老版本图标
## 1.3.22021-12-01
- 优化 示例可复制图标名称
## 1.3.12021-11-23
- 优化 兼容旧组件 type 值
## 1.3.02021-11-19
- 新增 更多图标
- 优化 自定义图标使用方式
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
## 1.1.72021-11-08
## 1.2.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.52021-05-12
- 新增 组件示例地址
## 1.1.42021-02-05
- 调整为uni_modules目录规范

View File

@ -0,0 +1,91 @@
<template>
<text class="uni-icons" :style="styleObj">
<slot>{{unicode}}</slot>
</text>
</template>
<script>
import { fontData, IconsDataItem } from './uniicons_file'
/**
* Icons 图标
* @description 用于展示 icon 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: "uni-icons",
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: Object,
default: 16
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {};
},
computed: {
unicode() : string {
let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
if (codes !== null) {
return codes.unicode
}
return ''
},
iconSize() : string {
const size = this.size
if (typeof size == 'string') {
const reg = /^[0-9]*$/g
return reg.test(size as string) ? '' + size + 'px' : '' + size;
// return '' + this.size
}
return this.getFontSize(size as number)
},
styleObj() : UTSJSONObject {
if (this.fontFamily !== '') {
return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
}
return { color: this.color, fontSize: this.iconSize }
}
},
created() { },
methods: {
/**
* 字体大小
*/
getFontSize(size : number) : string {
return size + 'px';
},
},
}
</script>
<style scoped>
@font-face {
font-family: UniIconsFontFamily;
src: url('./uniicons.ttf');
}
.uni-icons {
font-family: UniIconsFontFamily;
font-size: 18px;
font-style: normal;
color: #333;
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<!-- #ifdef APP-NVUE -->
<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
<slot></slot>
</text>
<!-- #endif -->
</template>
<script>
import { fontData } from './uniicons_file_vue.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
}
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import iconUrl from './uniicons.ttf'
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('" + iconUrl + "')"
});
// #endif
/**
* Icons 图标
* @description 用于展示 icons 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits: ['click'],
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
customPrefix: {
type: String,
default: ''
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {
icons: fontData
}
},
computed: {
unicode() {
let code = this.icons.find(v => v.font_class === this.type)
if (code) {
return code.unicode
}
return ''
},
iconSize() {
return getVal(this.size)
},
styleObj() {
if (this.fontFamily !== '') {
return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
}
return `color: ${this.color}; font-size: ${this.iconSize};`
}
},
methods: {
_onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
@import './uniicons.css';
@font-face {
font-family: uniicons;
src: url('./uniicons.ttf');
}
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
</style>

View File

@ -0,0 +1,664 @@
.uniui-cart-filled:before {
content: "\e6d0";
}
.uniui-gift-filled:before {
content: "\e6c4";
}
.uniui-color:before {
content: "\e6cf";
}
.uniui-wallet:before {
content: "\e6b1";
}
.uniui-settings-filled:before {
content: "\e6ce";
}
.uniui-auth-filled:before {
content: "\e6cc";
}
.uniui-shop-filled:before {
content: "\e6cd";
}
.uniui-staff-filled:before {
content: "\e6cb";
}
.uniui-vip-filled:before {
content: "\e6c6";
}
.uniui-plus-filled:before {
content: "\e6c7";
}
.uniui-folder-add-filled:before {
content: "\e6c8";
}
.uniui-color-filled:before {
content: "\e6c9";
}
.uniui-tune-filled:before {
content: "\e6ca";
}
.uniui-calendar-filled:before {
content: "\e6c0";
}
.uniui-notification-filled:before {
content: "\e6c1";
}
.uniui-wallet-filled:before {
content: "\e6c2";
}
.uniui-medal-filled:before {
content: "\e6c3";
}
.uniui-fire-filled:before {
content: "\e6c5";
}
.uniui-refreshempty:before {
content: "\e6bf";
}
.uniui-location-filled:before {
content: "\e6af";
}
.uniui-person-filled:before {
content: "\e69d";
}
.uniui-personadd-filled:before {
content: "\e698";
}
.uniui-arrowthinleft:before {
content: "\e6d2";
}
.uniui-arrowthinup:before {
content: "\e6d3";
}
.uniui-arrowthindown:before {
content: "\e6d4";
}
.uniui-back:before {
content: "\e6b9";
}
.uniui-forward:before {
content: "\e6ba";
}
.uniui-arrow-right:before {
content: "\e6bb";
}
.uniui-arrow-left:before {
content: "\e6bc";
}
.uniui-arrow-up:before {
content: "\e6bd";
}
.uniui-arrow-down:before {
content: "\e6be";
}
.uniui-arrowthinright:before {
content: "\e6d1";
}
.uniui-down:before {
content: "\e6b8";
}
.uniui-bottom:before {
content: "\e6b8";
}
.uniui-arrowright:before {
content: "\e6d5";
}
.uniui-right:before {
content: "\e6b5";
}
.uniui-up:before {
content: "\e6b6";
}
.uniui-top:before {
content: "\e6b6";
}
.uniui-left:before {
content: "\e6b7";
}
.uniui-arrowup:before {
content: "\e6d6";
}
.uniui-eye:before {
content: "\e651";
}
.uniui-eye-filled:before {
content: "\e66a";
}
.uniui-eye-slash:before {
content: "\e6b3";
}
.uniui-eye-slash-filled:before {
content: "\e6b4";
}
.uniui-info-filled:before {
content: "\e649";
}
.uniui-reload:before {
content: "\e6b2";
}
.uniui-micoff-filled:before {
content: "\e6b0";
}
.uniui-map-pin-ellipse:before {
content: "\e6ac";
}
.uniui-map-pin:before {
content: "\e6ad";
}
.uniui-location:before {
content: "\e6ae";
}
.uniui-starhalf:before {
content: "\e683";
}
.uniui-star:before {
content: "\e688";
}
.uniui-star-filled:before {
content: "\e68f";
}
.uniui-calendar:before {
content: "\e6a0";
}
.uniui-fire:before {
content: "\e6a1";
}
.uniui-medal:before {
content: "\e6a2";
}
.uniui-font:before {
content: "\e6a3";
}
.uniui-gift:before {
content: "\e6a4";
}
.uniui-link:before {
content: "\e6a5";
}
.uniui-notification:before {
content: "\e6a6";
}
.uniui-staff:before {
content: "\e6a7";
}
.uniui-vip:before {
content: "\e6a8";
}
.uniui-folder-add:before {
content: "\e6a9";
}
.uniui-tune:before {
content: "\e6aa";
}
.uniui-auth:before {
content: "\e6ab";
}
.uniui-person:before {
content: "\e699";
}
.uniui-email-filled:before {
content: "\e69a";
}
.uniui-phone-filled:before {
content: "\e69b";
}
.uniui-phone:before {
content: "\e69c";
}
.uniui-email:before {
content: "\e69e";
}
.uniui-personadd:before {
content: "\e69f";
}
.uniui-chatboxes-filled:before {
content: "\e692";
}
.uniui-contact:before {
content: "\e693";
}
.uniui-chatbubble-filled:before {
content: "\e694";
}
.uniui-contact-filled:before {
content: "\e695";
}
.uniui-chatboxes:before {
content: "\e696";
}
.uniui-chatbubble:before {
content: "\e697";
}
.uniui-upload-filled:before {
content: "\e68e";
}
.uniui-upload:before {
content: "\e690";
}
.uniui-weixin:before {
content: "\e691";
}
.uniui-compose:before {
content: "\e67f";
}
.uniui-qq:before {
content: "\e680";
}
.uniui-download-filled:before {
content: "\e681";
}
.uniui-pyq:before {
content: "\e682";
}
.uniui-sound:before {
content: "\e684";
}
.uniui-trash-filled:before {
content: "\e685";
}
.uniui-sound-filled:before {
content: "\e686";
}
.uniui-trash:before {
content: "\e687";
}
.uniui-videocam-filled:before {
content: "\e689";
}
.uniui-spinner-cycle:before {
content: "\e68a";
}
.uniui-weibo:before {
content: "\e68b";
}
.uniui-videocam:before {
content: "\e68c";
}
.uniui-download:before {
content: "\e68d";
}
.uniui-help:before {
content: "\e679";
}
.uniui-navigate-filled:before {
content: "\e67a";
}
.uniui-plusempty:before {
content: "\e67b";
}
.uniui-smallcircle:before {
content: "\e67c";
}
.uniui-minus-filled:before {
content: "\e67d";
}
.uniui-micoff:before {
content: "\e67e";
}
.uniui-closeempty:before {
content: "\e66c";
}
.uniui-clear:before {
content: "\e66d";
}
.uniui-navigate:before {
content: "\e66e";
}
.uniui-minus:before {
content: "\e66f";
}
.uniui-image:before {
content: "\e670";
}
.uniui-mic:before {
content: "\e671";
}
.uniui-paperplane:before {
content: "\e672";
}
.uniui-close:before {
content: "\e673";
}
.uniui-help-filled:before {
content: "\e674";
}
.uniui-paperplane-filled:before {
content: "\e675";
}
.uniui-plus:before {
content: "\e676";
}
.uniui-mic-filled:before {
content: "\e677";
}
.uniui-image-filled:before {
content: "\e678";
}
.uniui-locked-filled:before {
content: "\e668";
}
.uniui-info:before {
content: "\e669";
}
.uniui-locked:before {
content: "\e66b";
}
.uniui-camera-filled:before {
content: "\e658";
}
.uniui-chat-filled:before {
content: "\e659";
}
.uniui-camera:before {
content: "\e65a";
}
.uniui-circle:before {
content: "\e65b";
}
.uniui-checkmarkempty:before {
content: "\e65c";
}
.uniui-chat:before {
content: "\e65d";
}
.uniui-circle-filled:before {
content: "\e65e";
}
.uniui-flag:before {
content: "\e65f";
}
.uniui-flag-filled:before {
content: "\e660";
}
.uniui-gear-filled:before {
content: "\e661";
}
.uniui-home:before {
content: "\e662";
}
.uniui-home-filled:before {
content: "\e663";
}
.uniui-gear:before {
content: "\e664";
}
.uniui-smallcircle-filled:before {
content: "\e665";
}
.uniui-map-filled:before {
content: "\e666";
}
.uniui-map:before {
content: "\e667";
}
.uniui-refresh-filled:before {
content: "\e656";
}
.uniui-refresh:before {
content: "\e657";
}
.uniui-cloud-upload:before {
content: "\e645";
}
.uniui-cloud-download-filled:before {
content: "\e646";
}
.uniui-cloud-download:before {
content: "\e647";
}
.uniui-cloud-upload-filled:before {
content: "\e648";
}
.uniui-redo:before {
content: "\e64a";
}
.uniui-images-filled:before {
content: "\e64b";
}
.uniui-undo-filled:before {
content: "\e64c";
}
.uniui-more:before {
content: "\e64d";
}
.uniui-more-filled:before {
content: "\e64e";
}
.uniui-undo:before {
content: "\e64f";
}
.uniui-images:before {
content: "\e650";
}
.uniui-paperclip:before {
content: "\e652";
}
.uniui-settings:before {
content: "\e653";
}
.uniui-search:before {
content: "\e654";
}
.uniui-redo-filled:before {
content: "\e655";
}
.uniui-list:before {
content: "\e644";
}
.uniui-mail-open-filled:before {
content: "\e63a";
}
.uniui-hand-down-filled:before {
content: "\e63c";
}
.uniui-hand-down:before {
content: "\e63d";
}
.uniui-hand-up-filled:before {
content: "\e63e";
}
.uniui-hand-up:before {
content: "\e63f";
}
.uniui-heart-filled:before {
content: "\e641";
}
.uniui-mail-open:before {
content: "\e643";
}
.uniui-heart:before {
content: "\e639";
}
.uniui-loop:before {
content: "\e633";
}
.uniui-pulldown:before {
content: "\e632";
}
.uniui-scan:before {
content: "\e62a";
}
.uniui-bars:before {
content: "\e627";
}
.uniui-checkbox:before {
content: "\e62b";
}
.uniui-checkbox-filled:before {
content: "\e62c";
}
.uniui-shop:before {
content: "\e62f";
}
.uniui-headphones:before {
content: "\e630";
}
.uniui-cart:before {
content: "\e631";
}

View File

@ -0,0 +1,664 @@
export type IconsData = {
id : string
name : string
font_family : string
css_prefix_text : string
description : string
glyphs : Array<IconsDataItem>
}
export type IconsDataItem = {
font_class : string
unicode : string
}
export const fontData = [
{
"font_class": "arrow-down",
"unicode": "\ue6be"
},
{
"font_class": "arrow-left",
"unicode": "\ue6bc"
},
{
"font_class": "arrow-right",
"unicode": "\ue6bb"
},
{
"font_class": "arrow-up",
"unicode": "\ue6bd"
},
{
"font_class": "auth",
"unicode": "\ue6ab"
},
{
"font_class": "auth-filled",
"unicode": "\ue6cc"
},
{
"font_class": "back",
"unicode": "\ue6b9"
},
{
"font_class": "bars",
"unicode": "\ue627"
},
{
"font_class": "calendar",
"unicode": "\ue6a0"
},
{
"font_class": "calendar-filled",
"unicode": "\ue6c0"
},
{
"font_class": "camera",
"unicode": "\ue65a"
},
{
"font_class": "camera-filled",
"unicode": "\ue658"
},
{
"font_class": "cart",
"unicode": "\ue631"
},
{
"font_class": "cart-filled",
"unicode": "\ue6d0"
},
{
"font_class": "chat",
"unicode": "\ue65d"
},
{
"font_class": "chat-filled",
"unicode": "\ue659"
},
{
"font_class": "chatboxes",
"unicode": "\ue696"
},
{
"font_class": "chatboxes-filled",
"unicode": "\ue692"
},
{
"font_class": "chatbubble",
"unicode": "\ue697"
},
{
"font_class": "chatbubble-filled",
"unicode": "\ue694"
},
{
"font_class": "checkbox",
"unicode": "\ue62b"
},
{
"font_class": "checkbox-filled",
"unicode": "\ue62c"
},
{
"font_class": "checkmarkempty",
"unicode": "\ue65c"
},
{
"font_class": "circle",
"unicode": "\ue65b"
},
{
"font_class": "circle-filled",
"unicode": "\ue65e"
},
{
"font_class": "clear",
"unicode": "\ue66d"
},
{
"font_class": "close",
"unicode": "\ue673"
},
{
"font_class": "closeempty",
"unicode": "\ue66c"
},
{
"font_class": "cloud-download",
"unicode": "\ue647"
},
{
"font_class": "cloud-download-filled",
"unicode": "\ue646"
},
{
"font_class": "cloud-upload",
"unicode": "\ue645"
},
{
"font_class": "cloud-upload-filled",
"unicode": "\ue648"
},
{
"font_class": "color",
"unicode": "\ue6cf"
},
{
"font_class": "color-filled",
"unicode": "\ue6c9"
},
{
"font_class": "compose",
"unicode": "\ue67f"
},
{
"font_class": "contact",
"unicode": "\ue693"
},
{
"font_class": "contact-filled",
"unicode": "\ue695"
},
{
"font_class": "down",
"unicode": "\ue6b8"
},
{
"font_class": "bottom",
"unicode": "\ue6b8"
},
{
"font_class": "download",
"unicode": "\ue68d"
},
{
"font_class": "download-filled",
"unicode": "\ue681"
},
{
"font_class": "email",
"unicode": "\ue69e"
},
{
"font_class": "email-filled",
"unicode": "\ue69a"
},
{
"font_class": "eye",
"unicode": "\ue651"
},
{
"font_class": "eye-filled",
"unicode": "\ue66a"
},
{
"font_class": "eye-slash",
"unicode": "\ue6b3"
},
{
"font_class": "eye-slash-filled",
"unicode": "\ue6b4"
},
{
"font_class": "fire",
"unicode": "\ue6a1"
},
{
"font_class": "fire-filled",
"unicode": "\ue6c5"
},
{
"font_class": "flag",
"unicode": "\ue65f"
},
{
"font_class": "flag-filled",
"unicode": "\ue660"
},
{
"font_class": "folder-add",
"unicode": "\ue6a9"
},
{
"font_class": "folder-add-filled",
"unicode": "\ue6c8"
},
{
"font_class": "font",
"unicode": "\ue6a3"
},
{
"font_class": "forward",
"unicode": "\ue6ba"
},
{
"font_class": "gear",
"unicode": "\ue664"
},
{
"font_class": "gear-filled",
"unicode": "\ue661"
},
{
"font_class": "gift",
"unicode": "\ue6a4"
},
{
"font_class": "gift-filled",
"unicode": "\ue6c4"
},
{
"font_class": "hand-down",
"unicode": "\ue63d"
},
{
"font_class": "hand-down-filled",
"unicode": "\ue63c"
},
{
"font_class": "hand-up",
"unicode": "\ue63f"
},
{
"font_class": "hand-up-filled",
"unicode": "\ue63e"
},
{
"font_class": "headphones",
"unicode": "\ue630"
},
{
"font_class": "heart",
"unicode": "\ue639"
},
{
"font_class": "heart-filled",
"unicode": "\ue641"
},
{
"font_class": "help",
"unicode": "\ue679"
},
{
"font_class": "help-filled",
"unicode": "\ue674"
},
{
"font_class": "home",
"unicode": "\ue662"
},
{
"font_class": "home-filled",
"unicode": "\ue663"
},
{
"font_class": "image",
"unicode": "\ue670"
},
{
"font_class": "image-filled",
"unicode": "\ue678"
},
{
"font_class": "images",
"unicode": "\ue650"
},
{
"font_class": "images-filled",
"unicode": "\ue64b"
},
{
"font_class": "info",
"unicode": "\ue669"
},
{
"font_class": "info-filled",
"unicode": "\ue649"
},
{
"font_class": "left",
"unicode": "\ue6b7"
},
{
"font_class": "link",
"unicode": "\ue6a5"
},
{
"font_class": "list",
"unicode": "\ue644"
},
{
"font_class": "location",
"unicode": "\ue6ae"
},
{
"font_class": "location-filled",
"unicode": "\ue6af"
},
{
"font_class": "locked",
"unicode": "\ue66b"
},
{
"font_class": "locked-filled",
"unicode": "\ue668"
},
{
"font_class": "loop",
"unicode": "\ue633"
},
{
"font_class": "mail-open",
"unicode": "\ue643"
},
{
"font_class": "mail-open-filled",
"unicode": "\ue63a"
},
{
"font_class": "map",
"unicode": "\ue667"
},
{
"font_class": "map-filled",
"unicode": "\ue666"
},
{
"font_class": "map-pin",
"unicode": "\ue6ad"
},
{
"font_class": "map-pin-ellipse",
"unicode": "\ue6ac"
},
{
"font_class": "medal",
"unicode": "\ue6a2"
},
{
"font_class": "medal-filled",
"unicode": "\ue6c3"
},
{
"font_class": "mic",
"unicode": "\ue671"
},
{
"font_class": "mic-filled",
"unicode": "\ue677"
},
{
"font_class": "micoff",
"unicode": "\ue67e"
},
{
"font_class": "micoff-filled",
"unicode": "\ue6b0"
},
{
"font_class": "minus",
"unicode": "\ue66f"
},
{
"font_class": "minus-filled",
"unicode": "\ue67d"
},
{
"font_class": "more",
"unicode": "\ue64d"
},
{
"font_class": "more-filled",
"unicode": "\ue64e"
},
{
"font_class": "navigate",
"unicode": "\ue66e"
},
{
"font_class": "navigate-filled",
"unicode": "\ue67a"
},
{
"font_class": "notification",
"unicode": "\ue6a6"
},
{
"font_class": "notification-filled",
"unicode": "\ue6c1"
},
{
"font_class": "paperclip",
"unicode": "\ue652"
},
{
"font_class": "paperplane",
"unicode": "\ue672"
},
{
"font_class": "paperplane-filled",
"unicode": "\ue675"
},
{
"font_class": "person",
"unicode": "\ue699"
},
{
"font_class": "person-filled",
"unicode": "\ue69d"
},
{
"font_class": "personadd",
"unicode": "\ue69f"
},
{
"font_class": "personadd-filled",
"unicode": "\ue698"
},
{
"font_class": "personadd-filled-copy",
"unicode": "\ue6d1"
},
{
"font_class": "phone",
"unicode": "\ue69c"
},
{
"font_class": "phone-filled",
"unicode": "\ue69b"
},
{
"font_class": "plus",
"unicode": "\ue676"
},
{
"font_class": "plus-filled",
"unicode": "\ue6c7"
},
{
"font_class": "plusempty",
"unicode": "\ue67b"
},
{
"font_class": "pulldown",
"unicode": "\ue632"
},
{
"font_class": "pyq",
"unicode": "\ue682"
},
{
"font_class": "qq",
"unicode": "\ue680"
},
{
"font_class": "redo",
"unicode": "\ue64a"
},
{
"font_class": "redo-filled",
"unicode": "\ue655"
},
{
"font_class": "refresh",
"unicode": "\ue657"
},
{
"font_class": "refresh-filled",
"unicode": "\ue656"
},
{
"font_class": "refreshempty",
"unicode": "\ue6bf"
},
{
"font_class": "reload",
"unicode": "\ue6b2"
},
{
"font_class": "right",
"unicode": "\ue6b5"
},
{
"font_class": "scan",
"unicode": "\ue62a"
},
{
"font_class": "search",
"unicode": "\ue654"
},
{
"font_class": "settings",
"unicode": "\ue653"
},
{
"font_class": "settings-filled",
"unicode": "\ue6ce"
},
{
"font_class": "shop",
"unicode": "\ue62f"
},
{
"font_class": "shop-filled",
"unicode": "\ue6cd"
},
{
"font_class": "smallcircle",
"unicode": "\ue67c"
},
{
"font_class": "smallcircle-filled",
"unicode": "\ue665"
},
{
"font_class": "sound",
"unicode": "\ue684"
},
{
"font_class": "sound-filled",
"unicode": "\ue686"
},
{
"font_class": "spinner-cycle",
"unicode": "\ue68a"
},
{
"font_class": "staff",
"unicode": "\ue6a7"
},
{
"font_class": "staff-filled",
"unicode": "\ue6cb"
},
{
"font_class": "star",
"unicode": "\ue688"
},
{
"font_class": "star-filled",
"unicode": "\ue68f"
},
{
"font_class": "starhalf",
"unicode": "\ue683"
},
{
"font_class": "trash",
"unicode": "\ue687"
},
{
"font_class": "trash-filled",
"unicode": "\ue685"
},
{
"font_class": "tune",
"unicode": "\ue6aa"
},
{
"font_class": "tune-filled",
"unicode": "\ue6ca"
},
{
"font_class": "undo",
"unicode": "\ue64f"
},
{
"font_class": "undo-filled",
"unicode": "\ue64c"
},
{
"font_class": "up",
"unicode": "\ue6b6"
},
{
"font_class": "top",
"unicode": "\ue6b6"
},
{
"font_class": "upload",
"unicode": "\ue690"
},
{
"font_class": "upload-filled",
"unicode": "\ue68e"
},
{
"font_class": "videocam",
"unicode": "\ue68c"
},
{
"font_class": "videocam-filled",
"unicode": "\ue689"
},
{
"font_class": "vip",
"unicode": "\ue6a8"
},
{
"font_class": "vip-filled",
"unicode": "\ue6c6"
},
{
"font_class": "wallet",
"unicode": "\ue6b1"
},
{
"font_class": "wallet-filled",
"unicode": "\ue6c2"
},
{
"font_class": "weibo",
"unicode": "\ue68b"
},
{
"font_class": "weixin",
"unicode": "\ue691"
}
] as IconsDataItem[]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

View File

@ -0,0 +1,649 @@
export const fontData = [
{
"font_class": "arrow-down",
"unicode": "\ue6be"
},
{
"font_class": "arrow-left",
"unicode": "\ue6bc"
},
{
"font_class": "arrow-right",
"unicode": "\ue6bb"
},
{
"font_class": "arrow-up",
"unicode": "\ue6bd"
},
{
"font_class": "auth",
"unicode": "\ue6ab"
},
{
"font_class": "auth-filled",
"unicode": "\ue6cc"
},
{
"font_class": "back",
"unicode": "\ue6b9"
},
{
"font_class": "bars",
"unicode": "\ue627"
},
{
"font_class": "calendar",
"unicode": "\ue6a0"
},
{
"font_class": "calendar-filled",
"unicode": "\ue6c0"
},
{
"font_class": "camera",
"unicode": "\ue65a"
},
{
"font_class": "camera-filled",
"unicode": "\ue658"
},
{
"font_class": "cart",
"unicode": "\ue631"
},
{
"font_class": "cart-filled",
"unicode": "\ue6d0"
},
{
"font_class": "chat",
"unicode": "\ue65d"
},
{
"font_class": "chat-filled",
"unicode": "\ue659"
},
{
"font_class": "chatboxes",
"unicode": "\ue696"
},
{
"font_class": "chatboxes-filled",
"unicode": "\ue692"
},
{
"font_class": "chatbubble",
"unicode": "\ue697"
},
{
"font_class": "chatbubble-filled",
"unicode": "\ue694"
},
{
"font_class": "checkbox",
"unicode": "\ue62b"
},
{
"font_class": "checkbox-filled",
"unicode": "\ue62c"
},
{
"font_class": "checkmarkempty",
"unicode": "\ue65c"
},
{
"font_class": "circle",
"unicode": "\ue65b"
},
{
"font_class": "circle-filled",
"unicode": "\ue65e"
},
{
"font_class": "clear",
"unicode": "\ue66d"
},
{
"font_class": "close",
"unicode": "\ue673"
},
{
"font_class": "closeempty",
"unicode": "\ue66c"
},
{
"font_class": "cloud-download",
"unicode": "\ue647"
},
{
"font_class": "cloud-download-filled",
"unicode": "\ue646"
},
{
"font_class": "cloud-upload",
"unicode": "\ue645"
},
{
"font_class": "cloud-upload-filled",
"unicode": "\ue648"
},
{
"font_class": "color",
"unicode": "\ue6cf"
},
{
"font_class": "color-filled",
"unicode": "\ue6c9"
},
{
"font_class": "compose",
"unicode": "\ue67f"
},
{
"font_class": "contact",
"unicode": "\ue693"
},
{
"font_class": "contact-filled",
"unicode": "\ue695"
},
{
"font_class": "down",
"unicode": "\ue6b8"
},
{
"font_class": "bottom",
"unicode": "\ue6b8"
},
{
"font_class": "download",
"unicode": "\ue68d"
},
{
"font_class": "download-filled",
"unicode": "\ue681"
},
{
"font_class": "email",
"unicode": "\ue69e"
},
{
"font_class": "email-filled",
"unicode": "\ue69a"
},
{
"font_class": "eye",
"unicode": "\ue651"
},
{
"font_class": "eye-filled",
"unicode": "\ue66a"
},
{
"font_class": "eye-slash",
"unicode": "\ue6b3"
},
{
"font_class": "eye-slash-filled",
"unicode": "\ue6b4"
},
{
"font_class": "fire",
"unicode": "\ue6a1"
},
{
"font_class": "fire-filled",
"unicode": "\ue6c5"
},
{
"font_class": "flag",
"unicode": "\ue65f"
},
{
"font_class": "flag-filled",
"unicode": "\ue660"
},
{
"font_class": "folder-add",
"unicode": "\ue6a9"
},
{
"font_class": "folder-add-filled",
"unicode": "\ue6c8"
},
{
"font_class": "font",
"unicode": "\ue6a3"
},
{
"font_class": "forward",
"unicode": "\ue6ba"
},
{
"font_class": "gear",
"unicode": "\ue664"
},
{
"font_class": "gear-filled",
"unicode": "\ue661"
},
{
"font_class": "gift",
"unicode": "\ue6a4"
},
{
"font_class": "gift-filled",
"unicode": "\ue6c4"
},
{
"font_class": "hand-down",
"unicode": "\ue63d"
},
{
"font_class": "hand-down-filled",
"unicode": "\ue63c"
},
{
"font_class": "hand-up",
"unicode": "\ue63f"
},
{
"font_class": "hand-up-filled",
"unicode": "\ue63e"
},
{
"font_class": "headphones",
"unicode": "\ue630"
},
{
"font_class": "heart",
"unicode": "\ue639"
},
{
"font_class": "heart-filled",
"unicode": "\ue641"
},
{
"font_class": "help",
"unicode": "\ue679"
},
{
"font_class": "help-filled",
"unicode": "\ue674"
},
{
"font_class": "home",
"unicode": "\ue662"
},
{
"font_class": "home-filled",
"unicode": "\ue663"
},
{
"font_class": "image",
"unicode": "\ue670"
},
{
"font_class": "image-filled",
"unicode": "\ue678"
},
{
"font_class": "images",
"unicode": "\ue650"
},
{
"font_class": "images-filled",
"unicode": "\ue64b"
},
{
"font_class": "info",
"unicode": "\ue669"
},
{
"font_class": "info-filled",
"unicode": "\ue649"
},
{
"font_class": "left",
"unicode": "\ue6b7"
},
{
"font_class": "link",
"unicode": "\ue6a5"
},
{
"font_class": "list",
"unicode": "\ue644"
},
{
"font_class": "location",
"unicode": "\ue6ae"
},
{
"font_class": "location-filled",
"unicode": "\ue6af"
},
{
"font_class": "locked",
"unicode": "\ue66b"
},
{
"font_class": "locked-filled",
"unicode": "\ue668"
},
{
"font_class": "loop",
"unicode": "\ue633"
},
{
"font_class": "mail-open",
"unicode": "\ue643"
},
{
"font_class": "mail-open-filled",
"unicode": "\ue63a"
},
{
"font_class": "map",
"unicode": "\ue667"
},
{
"font_class": "map-filled",
"unicode": "\ue666"
},
{
"font_class": "map-pin",
"unicode": "\ue6ad"
},
{
"font_class": "map-pin-ellipse",
"unicode": "\ue6ac"
},
{
"font_class": "medal",
"unicode": "\ue6a2"
},
{
"font_class": "medal-filled",
"unicode": "\ue6c3"
},
{
"font_class": "mic",
"unicode": "\ue671"
},
{
"font_class": "mic-filled",
"unicode": "\ue677"
},
{
"font_class": "micoff",
"unicode": "\ue67e"
},
{
"font_class": "micoff-filled",
"unicode": "\ue6b0"
},
{
"font_class": "minus",
"unicode": "\ue66f"
},
{
"font_class": "minus-filled",
"unicode": "\ue67d"
},
{
"font_class": "more",
"unicode": "\ue64d"
},
{
"font_class": "more-filled",
"unicode": "\ue64e"
},
{
"font_class": "navigate",
"unicode": "\ue66e"
},
{
"font_class": "navigate-filled",
"unicode": "\ue67a"
},
{
"font_class": "notification",
"unicode": "\ue6a6"
},
{
"font_class": "notification-filled",
"unicode": "\ue6c1"
},
{
"font_class": "paperclip",
"unicode": "\ue652"
},
{
"font_class": "paperplane",
"unicode": "\ue672"
},
{
"font_class": "paperplane-filled",
"unicode": "\ue675"
},
{
"font_class": "person",
"unicode": "\ue699"
},
{
"font_class": "person-filled",
"unicode": "\ue69d"
},
{
"font_class": "personadd",
"unicode": "\ue69f"
},
{
"font_class": "personadd-filled",
"unicode": "\ue698"
},
{
"font_class": "personadd-filled-copy",
"unicode": "\ue6d1"
},
{
"font_class": "phone",
"unicode": "\ue69c"
},
{
"font_class": "phone-filled",
"unicode": "\ue69b"
},
{
"font_class": "plus",
"unicode": "\ue676"
},
{
"font_class": "plus-filled",
"unicode": "\ue6c7"
},
{
"font_class": "plusempty",
"unicode": "\ue67b"
},
{
"font_class": "pulldown",
"unicode": "\ue632"
},
{
"font_class": "pyq",
"unicode": "\ue682"
},
{
"font_class": "qq",
"unicode": "\ue680"
},
{
"font_class": "redo",
"unicode": "\ue64a"
},
{
"font_class": "redo-filled",
"unicode": "\ue655"
},
{
"font_class": "refresh",
"unicode": "\ue657"
},
{
"font_class": "refresh-filled",
"unicode": "\ue656"
},
{
"font_class": "refreshempty",
"unicode": "\ue6bf"
},
{
"font_class": "reload",
"unicode": "\ue6b2"
},
{
"font_class": "right",
"unicode": "\ue6b5"
},
{
"font_class": "scan",
"unicode": "\ue62a"
},
{
"font_class": "search",
"unicode": "\ue654"
},
{
"font_class": "settings",
"unicode": "\ue653"
},
{
"font_class": "settings-filled",
"unicode": "\ue6ce"
},
{
"font_class": "shop",
"unicode": "\ue62f"
},
{
"font_class": "shop-filled",
"unicode": "\ue6cd"
},
{
"font_class": "smallcircle",
"unicode": "\ue67c"
},
{
"font_class": "smallcircle-filled",
"unicode": "\ue665"
},
{
"font_class": "sound",
"unicode": "\ue684"
},
{
"font_class": "sound-filled",
"unicode": "\ue686"
},
{
"font_class": "spinner-cycle",
"unicode": "\ue68a"
},
{
"font_class": "staff",
"unicode": "\ue6a7"
},
{
"font_class": "staff-filled",
"unicode": "\ue6cb"
},
{
"font_class": "star",
"unicode": "\ue688"
},
{
"font_class": "star-filled",
"unicode": "\ue68f"
},
{
"font_class": "starhalf",
"unicode": "\ue683"
},
{
"font_class": "trash",
"unicode": "\ue687"
},
{
"font_class": "trash-filled",
"unicode": "\ue685"
},
{
"font_class": "tune",
"unicode": "\ue6aa"
},
{
"font_class": "tune-filled",
"unicode": "\ue6ca"
},
{
"font_class": "undo",
"unicode": "\ue64f"
},
{
"font_class": "undo-filled",
"unicode": "\ue64c"
},
{
"font_class": "up",
"unicode": "\ue6b6"
},
{
"font_class": "top",
"unicode": "\ue6b6"
},
{
"font_class": "upload",
"unicode": "\ue690"
},
{
"font_class": "upload-filled",
"unicode": "\ue68e"
},
{
"font_class": "videocam",
"unicode": "\ue68c"
},
{
"font_class": "videocam-filled",
"unicode": "\ue689"
},
{
"font_class": "vip",
"unicode": "\ue6a8"
},
{
"font_class": "vip-filled",
"unicode": "\ue6c6"
},
{
"font_class": "wallet",
"unicode": "\ue6b1"
},
{
"font_class": "wallet-filled",
"unicode": "\ue6c2"
},
{
"font_class": "weibo",
"unicode": "\ue68b"
},
{
"font_class": "weixin",
"unicode": "\ue691"
}
]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

View File

@ -0,0 +1,88 @@
{
"id": "uni-icons",
"displayName": "uni-icons 图标",
"version": "2.0.9",
"description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
"keywords": [
"uni-ui",
"uniui",
"icon",
"图标"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.2.14"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,8 @@
## Icons 图标
> **组件名uni-icons**
> 代码块: `uIcons`
用于展示 icons 图标 。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,46 @@
## 1.2.142023-04-14
- 优化 uni-list-chat 具名插槽`header` 非app端套一层元素方便使用时通过外层元素定位实现样式修改
## 1.2.132023-03-03
- uni-list-chat 新增 支持具名插槽`header`
## 1.2.122023-02-01
- 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props)
## 1.2.112023-01-31
- 修复 无反馈效果呈现的bug
## 1.2.92022-11-22
- 修复 uni-list-chat 在vue3下跳转报错的bug
## 1.2.82022-11-21
- 修复 uni-list-chat avatar属性 值为本地路径时错误的问题
## 1.2.72022-11-21
- 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题
## 1.2.62022-11-18
- 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题
## 1.2.52022-11-15
- 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug
## 1.2.42022-11-15
- 修复 uni-list-item 的 customStyle 属性 padding值在nvuevue2下无效的bug
## 1.2.32022-11-14
- uni-list-chat 新增 avatar 支持 fileId
## 1.2.22022-11-11
- uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html)
- uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im)
- uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor
## 1.2.12022-03-30
- 删除无用文件
## 1.2.02021-11-23
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-list](https://uniapp.dcloud.io/component/uniui/uni-list)
## 1.1.32021-08-30
- 修复 在vue3中to属性在发行应用的时候报错的bug
## 1.1.22021-07-30
- 优化 vue3下事件警告的问题
## 1.1.12021-07-21
- 修复 与其他组件嵌套使用时点击失效的Bug
## 1.1.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.172021-05-12
- 新增 组件示例地址
## 1.0.162021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.152021-02-05
- 调整为uni_modules目录规范
- 修复 uni-list-chat 角标显示不正常的问题

View File

@ -0,0 +1,107 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view class="uni-list-ad">
<view v-if="borderShow" :class="{'uni-list--border':border,'uni-list-item--first':isFirstChild}"></view>
<ad style="width: 200px;height: 300px;border-width: 1px;border-color: red;border-style: solid;" adpid="1111111111"
unit-id="" appid="" apid="" type="feed" @error="aderror" @close="closeAd"></ad>
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
// #ifdef APP-NVUE
const dom = uni.requireNativePlugin('dom');
// #endif
export default {
name: 'UniListAd',
props: {
title: {
type: String,
default: '',
}
},
// inject: ['list'],
data() {
return {
isFirstChild: false,
border: false,
borderShow: true,
}
},
mounted() {
this.list = this.getForm()
if (this.list) {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true
this.isFirstChild = true
}
this.border = this.list.border
}
},
methods: {
/**
* 获取父元素实例
*/
getForm(name = 'uniList') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
aderror(e) {
console.log("aderror: " + JSON.stringify(e.detail));
},
closeAd(e) {
this.borderShow = false
}
}
}
</script>
<style lang="scss" >
.uni-list-ad {
position: relative;
border: 1px red solid;
}
.uni-list--border {
position: relative;
padding-bottom: 1px;
/* #ifdef APP-PLUS */
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
margin-left: $uni-spacing-row-lg;
}
/* #ifndef APP-NVUE */
.uni-list--border:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: $uni-border-color;
}
.uni-list-item--first:after {
height: 0px;
}
/* #endif */
</style>

View File

@ -0,0 +1,58 @@
/**
* 这里是 uni-list 组件内置的常用样式变量
* 如果需要覆盖样式这里提供了基本的组件样式变量您可以尝试修改这里的变量去完成样式替换而不用去修改源码
*
*/
// 背景色
$background-color : #fff;
// 分割线颜色
$divide-line-color : #e5e5e5;
// 默认头像大小如需要修改此值注意同步修改 js 中的值 const avatarWidth = xx 目前只支持方形头像
// nvue 页面不支持修改头像大小
$avatar-width : 45px ;
// 头像边框
$avatar-border-radius: 5px;
$avatar-border-color: #eee;
$avatar-border-width: 1px;
// 标题文字样式
$title-size : 16px;
$title-color : #3b4144;
$title-weight : normal;
// 描述文字样式
$note-size : 12px;
$note-color : #999;
$note-weight : normal;
// 右侧额外内容默认样式
$right-text-size : 12px;
$right-text-color : #999;
$right-text-weight : normal;
// 角标样式
// nvue 页面不支持修改圆点位置以及大小
// 角标在左侧时角标的位置默认为 0 负数左/下移动正数右/上移动
$badge-left: 0px;
$badge-top: 0px;
// 显示圆点时圆点大小
$dot-width: 10px;
$dot-height: 10px;
// 显示角标时角标大小和字体大小
$badge-size : 18px;
$badge-font : 12px;
// 显示角标时角标前景色
$badge-color : #fff;
// 显示角标时角标背景色
$badge-background-color : #ff5a5f;
// 显示角标时角标左右间距
$badge-space : 6px;
// 状态样式
// 选中颜色
$hover : #f5f5f5;

View File

@ -0,0 +1,593 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view :hover-class="!clickable && !link ? '' : 'uni-list-chat--hover'" class="uni-list-chat" @click.stop="onClick">
<view :class="{ 'uni-list--border': border, 'uni-list-chat--first': isFirstChild }"></view>
<view class="uni-list-chat__container">
<view class="uni-list-chat__header-warp">
<view v-if="avatarCircle || avatarList.length === 0" class="uni-list-chat__header" :class="{ 'header--circle': avatarCircle }">
<image class="uni-list-chat__header-image" :class="{ 'header--circle': avatarCircle }" :src="avatarUrl" mode="aspectFill"></image>
</view>
<!-- 头像组 -->
<view v-else class="uni-list-chat__header">
<view v-for="(item, index) in avatarList" :key="index" class="uni-list-chat__header-box" :class="computedAvatar"
:style="{ width: imageWidth + 'px', height: imageWidth + 'px' }">
<image class="uni-list-chat__header-image" :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }" :src="item.url"
mode="aspectFill"></image>
</view>
</view>
</view>
<!-- #ifndef APP -->
<view class="slot-header">
<!-- #endif -->
<slot name="header"></slot>
<!-- #ifndef APP -->
</view>
<!-- #endif -->
<view v-if="badgeText && badgePositon === 'left'" class="uni-list-chat__badge uni-list-chat__badge-pos" :class="[isSingle]">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
<view class="uni-list-chat__content">
<view class="uni-list-chat__content-main">
<text class="uni-list-chat__content-title uni-ellipsis">{{ title }}</text>
<view style="flex-direction: row;">
<text class="draft" v-if="isDraft">[草稿]</text>
<text class="uni-list-chat__content-note uni-ellipsis">{{isDraft?note.slice(14):note}}</text>
</view>
</view>
<view class="uni-list-chat__content-extra">
<slot>
<text class="uni-list-chat__content-extra-text">{{ time }}</text>
<view v-if="badgeText && badgePositon === 'right'" class="uni-list-chat__badge" :class="[isSingle, badgePositon === 'right' ? 'uni-list-chat--right' : '']">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
</slot>
</view>
</view>
</view>
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
//
const avatarWidth = 45;
/**
* ListChat 聊天列表
* @description 聊天列表,用于创建聊天类列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} title 标题
* @property {String} note 描述
* @property {Boolean} clickable = [true|false] 是否开启点击反馈默认为false
* @property {String} badgeText 数字角标内容
* @property {String} badgePositon = [left|right] 角标位置默认为 right
* @property {String} link = [falsenavigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈默认为false
* @value false 不开启
* @value navigateTo uni.navigateTo()
* @value redirectTo uni.redirectTo()
* @value reLaunch uni.reLaunch()
* @value switchTab uni.switchTab()
* @property {String | PageURIString} to 跳转目标页面
* @property {String} time 右侧时间显示
* @property {Boolean} avatarCircle = [true|false] 是否显示圆形头像默认为false
* @property {String} avatar 头像地址avatarCircle 不填时生效
* @property {Array} avatarList 头像组格式为 [{url:''}]
* @event {Function} click 点击 uniListChat 触发事件
*/
export default {
name: 'UniListChat',
emits:['click'],
props: {
title: {
type: String,
default: ''
},
note: {
type: String,
default: ''
},
clickable: {
type: Boolean,
default: false
},
link: {
type: [Boolean, String],
default: false
},
to: {
type: String,
default: ''
},
badgeText: {
type: [String, Number],
default: ''
},
badgePositon: {
type: String,
default: 'right'
},
time: {
type: String,
default: ''
},
avatarCircle: {
type: Boolean,
default: false
},
avatar: {
type: String,
default: ''
},
avatarList: {
type: Array,
default () {
return [];
}
}
},
// inject: ['list'],
computed: {
isDraft(){
return this.note.slice(0,14) == '[uni-im-draft]'
},
isSingle() {
if (this.badgeText === 'dot') {
return 'uni-badge--dot';
} else {
const badgeText = this.badgeText.toString();
if (badgeText.length > 1) {
return 'uni-badge--complex';
} else {
return 'uni-badge--single';
}
}
},
computedAvatar() {
if (this.avatarList.length > 4) {
this.imageWidth = avatarWidth * 0.31;
return 'avatarItem--3';
} else if (this.avatarList.length > 1) {
this.imageWidth = avatarWidth * 0.47;
return 'avatarItem--2';
} else {
this.imageWidth = avatarWidth;
return 'avatarItem--1';
}
}
},
watch: {
avatar:{
handler(avatar) {
if(avatar.substr(0,8) == 'cloud://'){
uniCloud.getTempFileURL({
fileList: [avatar]
}).then(res=>{
// console.log(res);
// uniCloud
let fileList = res.fileList || res.result.fileList
this.avatarUrl = fileList[0].tempFileURL
})
}else{
this.avatarUrl = avatar
}
},
immediate: true
}
},
data() {
return {
isFirstChild: false,
border: true,
// avatarList: 3,
imageWidth: 50,
avatarUrl:''
};
},
mounted() {
this.list = this.getForm()
if (this.list) {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true;
this.isFirstChild = true;
}
this.border = this.list.border;
}
},
methods: {
/**
* 获取父元素实例
*/
getForm(name = 'uniList') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
onClick() {
if (this.to !== '') {
this.openPage();
return;
}
if (this.clickable || this.link) {
this.$emit('click', {
data: {}
});
}
},
openPage() {
if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
this.pageApi(this.link);
} else {
this.pageApi('navigateTo');
}
},
pageApi(api) {
let callback = {
url: this.to,
success: res => {
this.$emit('click', {
data: res
});
},
fail: err => {
this.$emit('click', {
data: err
});
}
}
switch (api) {
case 'navigateTo':
uni.navigateTo(callback)
break
case 'redirectTo':
uni.redirectTo(callback)
break
case 'reLaunch':
uni.reLaunch(callback)
break
case 'switchTab':
uni.switchTab(callback)
break
default:
uni.navigateTo(callback)
}
}
}
};
</script>
<style lang="scss" >
$uni-font-size-lg:16px;
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
$background-color: #fff;
$divide-line-color: #e5e5e5;
$avatar-width: 45px;
$avatar-border-radius: 5px;
$avatar-border-color: #eee;
$avatar-border-width: 1px;
$title-size: 16px;
$title-color: #3b4144;
$title-weight: normal;
$note-size: 12px;
$note-color: #999;
$note-weight: normal;
$right-text-size: 12px;
$right-text-color: #999;
$right-text-weight: normal;
$badge-left: 0px;
$badge-top: 0px;
$dot-width: 10px;
$dot-height: 10px;
$badge-size: 18px;
$badge-font: 12px;
$badge-color: #fff;
$badge-background-color: #ff5a5f;
$badge-space: 6px;
$hover: #f5f5f5;
.uni-list-chat {
font-size: $uni-font-size-lg;
position: relative;
flex-direction: column;
justify-content: space-between;
background-color: $background-color;
}
// .uni-list-chat--disabled {
// opacity: 0.3;
// }
.uni-list-chat--hover {
background-color: $hover;
}
.uni-list--border {
position: relative;
margin-left: $uni-spacing-row-lg;
/* #ifdef APP-PLUS */
border-top-color: $divide-line-color;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list--border:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $divide-line-color;
}
.uni-list-item--first:after {
height: 0px;
}
/* #endif */
.uni-list-chat--first {
border-top-width: 0px;
}
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
.uni-ellipsis-2 {
/* #ifndef APP-NVUE */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* #endif */
/* #ifdef APP-NVUE */
lines: 2;
/* #endif */
}
.uni-list-chat__container {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
padding: $uni-spacing-row-base $uni-spacing-row-lg;
position: relative;
overflow: hidden;
}
.uni-list-chat__header-warp {
position: relative;
}
.uni-list-chat__header {
/* #ifndef APP-NVUE */
display: flex;
align-content: center;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap-reverse;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
border-radius: $avatar-border-radius;
border-color: $avatar-border-color;
border-width: $avatar-border-width;
border-style: solid;
overflow: hidden;
}
.uni-list-chat__header-box {
/* #ifndef APP-PLUS */
box-sizing: border-box;
display: flex;
width: $avatar-width;
height: $avatar-width;
/* #endif */
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
overflow: hidden;
border-radius: 2px;
}
.uni-list-chat__header-image {
margin: 1px;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list-chat__header-image {
display: block;
width: 100%;
height: 100%;
}
.avatarItem--1 {
width: 100%;
height: 100%;
}
.avatarItem--2 {
width: 47%;
height: 47%;
}
.avatarItem--3 {
width: 32%;
height: 32%;
}
/* #endif */
.header--circle {
border-radius: 50%;
}
.uni-list-chat__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
overflow: hidden;
padding: 2px 0;
}
.uni-list-chat__content-main {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
padding-left: $uni-spacing-row-base;
flex: 1;
overflow: hidden;
}
.uni-list-chat__content-title {
font-size: $title-size;
color: $title-color;
font-weight: $title-weight;
overflow: hidden;
}
.draft ,.uni-list-chat__content-note {
margin-top: 3px;
color: $note-color;
font-size: $note-size;
font-weight: $title-weight;
overflow: hidden;
}
.draft{
color: #eb3a41;
/* #ifndef APP-NVUE */
flex-shrink: 0;
/* #endif */
padding-right: 3px;
}
.uni-list-chat__content-extra {
/* #ifndef APP-NVUE */
flex-shrink: 0;
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
margin-left: 5px;
}
.uni-list-chat__content-extra-text {
color: $right-text-color;
font-size: $right-text-size;
font-weight: $right-text-weight;
overflow: hidden;
}
.uni-list-chat__badge-pos {
position: absolute;
/* #ifdef APP-NVUE */
left: 55px;
top: 3px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 10px - #{$badge-space} + #{$badge-left});
top: calc(#{$uni-spacing-row-base}/ 2 + 1px + #{$badge-top});
/* #endif */
}
.uni-list-chat__badge {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
border-radius: 100px;
background-color: $badge-background-color;
}
.uni-list-chat__badge-text {
color: $badge-color;
font-size: $badge-font;
}
.uni-badge--single {
/* #ifndef APP-NVUE */
// left: calc(#{$avatar-width} + 7px + #{$badge-left});
/* #endif */
width: $badge-size;
height: $badge-size;
}
.uni-badge--complex {
/* #ifdef APP-NVUE */
left: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
height: $badge-size;
padding: 0 $badge-space;
}
.uni-badge--dot {
/* #ifdef APP-NVUE */
left: 60px;
top: 6px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 15px - #{$dot-width}/ 2 + 1px + #{$badge-left});
/* #endif */
width: $dot-width;
height: $dot-height;
padding: 0;
}
.uni-list-chat--right {
/* #ifdef APP-NVUE */
left: 0;
/* #endif */
}
</style>

View File

@ -0,0 +1,534 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell :keep-scroll-position="keepScrollPosition">
<!-- #endif -->
<view :class="{ 'uni-list-item--disabled': disabled }" :style="{'background-color':customStyle.backgroundColor}"
:hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uni-list-item--hover'"
class="uni-list-item" @click="onClick">
<view v-if="!isFirstChild" class="border--left" :class="{ 'uni-list--border': border }"></view>
<view class="uni-list-item__container"
:class="{ 'container--right': showArrow || link, 'flex--direction': direction === 'column'}"
:style="{paddingTop:padding.top,paddingLeft:padding.left,paddingRight:padding.right,paddingBottom:padding.bottom}">
<slot name="header">
<view class="uni-list-item__header">
<view v-if="thumb" class="uni-list-item__icon">
<image :src="thumb" class="uni-list-item__icon-img" :class="['uni-list--' + thumbSize]" />
</view>
<view v-else-if="showExtraIcon" class="uni-list-item__icon">
<uni-icons :customPrefix="extraIcon.customPrefix" :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type" />
</view>
</view>
</slot>
<slot name="body">
<view class="uni-list-item__content"
:class="{ 'uni-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }">
<text v-if="title" class="uni-list-item__content-title"
:class="[ellipsis !== 0 && ellipsis <= 2 ? 'uni-ellipsis-' + ellipsis : '']">{{ title }}</text>
<text v-if="note" class="uni-list-item__content-note">{{ note }}</text>
</view>
</slot>
<slot name="footer">
<view v-if="rightText || showBadge || showSwitch" class="uni-list-item__extra"
:class="{ 'flex--justify': direction === 'column' }">
<text v-if="rightText" class="uni-list-item__extra-text">{{ rightText }}</text>
<uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" :custom-style="badgeStyle" />
<switch v-if="showSwitch" :disabled="disabled" :checked="switchChecked"
@change="onSwitchChange" />
</view>
</slot>
</view>
<uni-icons v-if="showArrow || link" :size="16" class="uni-icon-wrapper" color="#bbb" type="arrowright" />
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
/**
* ListItem 列表子组件
* @description 列表子组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} title 标题
* @property {String} note 描述
* @property {String} thumb 左侧缩略图若thumb有值则不会显示扩展图标
* @property {String} thumbSize = [lg|base|sm] 略缩图大小
* @value lg 大图
* @value base 一般
* @value sm 小图
* @property {String} badgeText 数字角标内容
* @property {String} badgeType 数字角标类型参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21)
* @property {Object} badgeStyle 数字角标样式
* @property {String} rightText 右侧文字内容
* @property {Boolean} disabled = [true|false] 是否禁用
* @property {Boolean} clickable = [true|false] 是否开启点击反馈
* @property {String} link = [navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈
* @value navigateTo uni.navigateTo()
* @value redirectTo uni.redirectTo()
* @value reLaunch uni.reLaunch()
* @value switchTab uni.switchTab()
* @property {String | PageURIString} to 跳转目标页面
* @property {Boolean} showBadge = [true|false] 是否显示数字角标
* @property {Boolean} showSwitch = [true|false] 是否显示Switch
* @property {Boolean} switchChecked = [true|false] Switch是否被选中
* @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标
* @property {Object} extraIcon 扩展图标参数格式为 {color: '#4cd964',size: '22',type: 'spinner'}
* @property {String} direction = [row|column] 排版方向
* @value row 水平排列
* @value column 垂直排列
* @event {Function} click 点击 uniListItem 触发事件
* @event {Function} switchChange 点击切换 Switch 时触发
*/
export default {
name: 'UniListItem',
emits: ['click', 'switchChange'],
props: {
direction: {
type: String,
default: 'row'
},
title: {
type: String,
default: ''
},
note: {
type: String,
default: ''
},
ellipsis: {
type: [Number, String],
default: 0
},
disabled: {
type: [Boolean, String],
default: false
},
clickable: {
type: Boolean,
default: false
},
showArrow: {
type: [Boolean, String],
default: false
},
link: {
type: [Boolean, String],
default: false
},
to: {
type: String,
default: ''
},
showBadge: {
type: [Boolean, String],
default: false
},
showSwitch: {
type: [Boolean, String],
default: false
},
switchChecked: {
type: [Boolean, String],
default: false
},
badgeText: {
type: String,
default: ''
},
badgeType: {
type: String,
default: 'success'
},
badgeStyle: {
type: Object,
default () {
return {}
}
},
rightText: {
type: String,
default: ''
},
thumb: {
type: String,
default: ''
},
thumbSize: {
type: String,
default: 'base'
},
showExtraIcon: {
type: [Boolean, String],
default: false
},
extraIcon: {
type: Object,
default () {
return {
type: '',
color: '#000000',
size: 20,
customPrefix: ''
};
}
},
border: {
type: Boolean,
default: true
},
customStyle: {
type: Object,
default () {
return {
padding: '',
backgroundColor: '#FFFFFF'
}
}
},
keepScrollPosition: {
type: Boolean,
default: false
}
},
watch: {
'customStyle.padding': {
handler(padding) {
if(typeof padding == 'number'){
padding += ''
}
let paddingArr = padding.split(' ')
if (paddingArr.length === 1) {
const allPadding = paddingArr[0]
this.padding = {
"top": allPadding,
"right": allPadding,
"bottom": allPadding,
"left": allPadding
}
} else if (paddingArr.length === 2) {
const [verticalPadding, horizontalPadding] = paddingArr;
this.padding = {
"top": verticalPadding,
"right": horizontalPadding,
"bottom": verticalPadding,
"left": horizontalPadding
}
} else if (paddingArr.length === 4) {
const [topPadding, rightPadding, bottomPadding, leftPadding] = paddingArr;
this.padding = {
"top": topPadding,
"right": rightPadding,
"bottom": bottomPadding,
"left": leftPadding
}
}
},
immediate: true
}
},
// inject: ['list'],
data() {
return {
isFirstChild: false,
padding: {
top: "",
right: "",
bottom: "",
left: ""
}
};
},
mounted() {
this.list = this.getForm()
// uni-list
if (this.list) {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true;
this.isFirstChild = true;
}
}
},
methods: {
/**
* 获取父元素实例
*/
getForm(name = 'uniList') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
onClick() {
if (this.to !== '') {
this.openPage();
return;
}
if (this.clickable || this.link) {
this.$emit('click', {
data: {}
});
}
},
onSwitchChange(e) {
this.$emit('switchChange', e.detail);
},
openPage() {
if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
this.pageApi(this.link);
} else {
this.pageApi('navigateTo');
}
},
pageApi(api) {
let callback = {
url: this.to,
success: res => {
this.$emit('click', {
data: res
});
},
fail: err => {
this.$emit('click', {
data: err
});
}
}
switch (api) {
case 'navigateTo':
uni.navigateTo(callback)
break
case 'redirectTo':
uni.redirectTo(callback)
break
case 'reLaunch':
uni.reLaunch(callback)
break
case 'switchTab':
uni.switchTab(callback)
break
default:
uni.navigateTo(callback)
}
}
}
};
</script>
<style lang="scss">
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
$uni-spacing-col-lg: 12px;
$uni-spacing-row-lg: 15px;
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
$uni-border-color:#e5e5e5;
$uni-bg-color-hover:#f1f1f1;
$uni-text-color-grey:#999;
$list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
.uni-list-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
font-size: $uni-font-size-lg;
position: relative;
justify-content: space-between;
align-items: center;
background-color: #fff;
flex-direction: row;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-list-item--disabled {
opacity: 0.3;
}
.uni-list-item--hover {
background-color: $uni-bg-color-hover !important;
}
.uni-list-item__container {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: $list-item-pd;
padding-left: $uni-spacing-row-lg;
flex: 1;
overflow: hidden;
// align-items: center;
}
.container--right {
padding-right: 0;
}
// .border--left {
// margin-left: $uni-spacing-row-lg;
// }
.uni-list--border {
position: absolute;
top: 0;
right: 0;
left: 0;
/* #ifdef APP-NVUE */
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list--border:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
}
/* #endif */
.uni-list-item__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding-right: 8px;
flex: 1;
color: #3b4144;
// overflow: hidden;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
}
.uni-list-item__content--center {
justify-content: center;
}
.uni-list-item__content-title {
font-size: $uni-font-size-base;
color: #3b4144;
overflow: hidden;
}
.uni-list-item__content-note {
margin-top: 6rpx;
color: $uni-text-color-grey;
font-size: $uni-font-size-sm;
overflow: hidden;
}
.uni-list-item__extra {
// width: 25%;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.uni-list-item__header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
}
.uni-list-item__icon {
margin-right: 18rpx;
flex-direction: row;
justify-content: center;
align-items: center;
}
.uni-list-item__icon-img {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
height: $uni-img-size-base;
width: $uni-img-size-base;
margin-right: 10px;
}
.uni-icon-wrapper {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
padding: 0 10px;
}
.flex--direction {
flex-direction: column;
/* #ifndef APP-NVUE */
align-items: initial;
/* #endif */
}
.flex--justify {
/* #ifndef APP-NVUE */
justify-content: initial;
/* #endif */
}
.uni-list--lg {
height: $uni-img-size-lg;
width: $uni-img-size-lg;
}
.uni-list--base {
height: $uni-img-size-base;
width: $uni-img-size-base;
}
.uni-list--sm {
height: $uni-img-size-sm;
width: $uni-img-size-sm;
}
.uni-list-item__extra-text {
color: $uni-text-color-grey;
font-size: $uni-font-size-sm;
}
.uni-ellipsis-1 {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
text-overflow: ellipsis;
/* #endif */
}
.uni-ellipsis-2 {
/* #ifndef APP-NVUE */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* #endif */
/* #ifdef APP-NVUE */
lines: 2;
text-overflow: ellipsis;
/* #endif */
}
</style>

View File

@ -0,0 +1,123 @@
<template>
<!-- #ifndef APP-NVUE -->
<view class="uni-list uni-border-top-bottom">
<view v-if="border" class="uni-list--border-top"></view>
<slot />
<view v-if="border" class="uni-list--border-bottom"></view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<list :bounce="false" :scrollable="true" show-scrollbar :render-reverse="renderReverse" @scroll="scroll" class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop"
loadmoreoffset="15">
<slot />
</list>
<!-- #endif -->
</template>
<script>
/**
* List 列表
* @description 列表组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} border = [true|false] 标题
*/
export default {
name: 'uniList',
'mp-weixin': {
options: {
multipleSlots: false
}
},
props: {
stackFromEnd:{
type: Boolean,
default:false
},
enableBackToTop: {
type: [Boolean, String],
default: false
},
scrollY: {
type: [Boolean, String],
default: false
},
border: {
type: Boolean,
default: true
},
renderReverse:{
type: Boolean,
default: false
}
},
// provide() {
// return {
// list: this
// };
// },
created() {
this.firstChildAppend = false;
},
methods: {
loadMore(e) {
this.$emit('scrolltolower');
},
scroll(e) {
this.$emit('scroll', e);
}
}
};
</script>
<style lang="scss">
$uni-bg-color:#ffffff;
$uni-border-color:#e5e5e5;
.uni-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
background-color: $uni-bg-color;
position: relative;
flex-direction: column;
}
.uni-list--border {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 0.5px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 0.5px;
/* #endif */
z-index: -1;
}
/* #ifndef APP-NVUE */
.uni-list--border-top {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
z-index: 1;
}
.uni-list--border-bottom {
position: absolute;
bottom: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
}
/* #endif */
</style>

View File

@ -0,0 +1,65 @@
<template>
<!-- #ifdef APP-NVUE -->
<refresh :display="display" @refresh="onrefresh" @pullingdown="onpullingdown">
<slot />
</refresh>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view ref="uni-refresh" class="uni-refresh" v-show="isShow">
<slot />
</view>
<!-- #endif -->
</template>
<script>
export default {
name: 'UniRefresh',
props: {
display: {
type: [String],
default: "hide"
}
},
data() {
return {
pulling: false
}
},
computed: {
isShow() {
if (this.display === "show" || this.pulling === true) {
return true;
}
return false;
}
},
created() {},
methods: {
onchange(value) {
this.pulling = value;
},
onrefresh(e) {
this.$emit("refresh", e);
},
onpullingdown(e) {
// #ifdef APP-NVUE
this.$emit("pullingdown", e);
// #endif
// #ifndef APP-NVUE
var detail = {
viewHeight: 90,
pullingDistance: e.height
}
this.$emit("pullingdown", detail);
// #endif
}
}
}
</script>
<style>
.uni-refresh {
height: 0;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,87 @@
var pullDown = {
threshold: 95,
maxHeight: 200,
callRefresh: 'onrefresh',
callPullingDown: 'onpullingdown',
refreshSelector: '.uni-refresh'
};
function ready(newValue, oldValue, ownerInstance, instance) {
var state = instance.getState()
state.canPullDown = newValue;
// console.log(newValue);
}
function touchStart(e, instance) {
var state = instance.getState();
state.refreshInstance = instance.selectComponent(pullDown.refreshSelector);
state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined);
if (!state.canPullDown) {
return
}
// console.log("touchStart");
state.height = 0;
state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY;
state.refreshInstance.setStyle({
'height': 0
});
state.refreshInstance.callMethod("onchange", true);
}
function touchMove(e, ownerInstance) {
var instance = e.instance;
var state = instance.getState();
if (!state.canPullDown) {
return
}
var oldHeight = state.height;
var endY = e.touches[0].pageY || e.changedTouches[0].pageY;
var height = endY - state.touchStartY;
if (height > pullDown.maxHeight) {
return;
}
var refreshInstance = state.refreshInstance;
refreshInstance.setStyle({
'height': height + 'px'
});
height = height < pullDown.maxHeight ? height : pullDown.maxHeight;
state.height = height;
refreshInstance.callMethod(pullDown.callPullingDown, {
height: height
});
}
function touchEnd(e, ownerInstance) {
var state = e.instance.getState();
if (!state.canPullDown) {
return
}
state.refreshInstance.callMethod("onchange", false);
var refreshInstance = state.refreshInstance;
if (state.height > pullDown.threshold) {
refreshInstance.callMethod(pullDown.callRefresh);
return;
}
refreshInstance.setStyle({
'height': 0
});
}
function propObserver(newValue, oldValue, instance) {
pullDown = newValue;
}
module.exports = {
touchmove: touchMove,
touchstart: touchStart,
touchend: touchEnd,
propObserver: propObserver
}

View File

@ -0,0 +1,88 @@
{
"id": "uni-list",
"displayName": "uni-list 列表",
"version": "1.2.14",
"description": "List 组件 ,帮助使用者快速构建列表。",
"keywords": [
"",
"uni-ui",
"uniui",
"列表",
"",
"list"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-badge",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,346 @@
## List 列表
> **组件名uni-list**
> 代码块: `uList``uListItem`
> 关联组件:`uni-list-item``uni-badge``uni-icons``uni-list-chat``uni-list-ad`
List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。
在vue页面里它默认使用页面级滚动。在app-nvue页面里它默认使用原生list组件滚动。这样的长列表在滚动出屏幕外后系统会回收不可见区域的渲染内存资源不会造成滚动越长手机越卡的问题。
uni-list组件是父容器里面的核心是uni-list-item子组件它代表列表中的一个可重复行子组件可以无限循环。
uni-list-item有很多风格uni-list-item组件通过内置的属性满足一些常用的场景。当内置属性不满足需求时可以通过扩展插槽来自定义列表内容。
内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表、通信录列表、聊天记录列表。
涉及很多大图或丰富内容的列表,比如类今日头条的新闻列表、类淘宝的电商列表,需要通过扩展插槽实现。
下文均有样例给出。
uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29)
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 组件需要依赖 `sass` 插件 ,请自行手动安装
> - 组件内部依赖 `'uni-icons'``uni-badge` 组件
> - `uni-list``uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item`
> - 只有开启点击反馈后,会有点击选中效果
> - 使用插槽时,可以完全自定义内容
> - note 、rightText 属性暂时没做限制,不支持文字溢出隐藏,使用时应该控制长度显示或通过默认插槽自行扩展
> - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: 详情 --> 项目配置 --> 启用 component2 编译
> - 如果需要修改 `switch``badge` 样式,请使用插槽自定义
> - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli`
> - 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839
### 基本用法
- 设置 `title` 属性,可以显示列表标题
- 设置 `disabled` 属性,可以禁用当前项
```html
<uni-list>
<uni-list-item title="列表文字" ></uni-list-item>
<uni-list-item :disabled="true" title="列表禁用状态" ></uni-list-item>
</uni-list>
```
### 多行内容显示
- 设置 `note` 属性 ,可以在第二行显示描述文本信息
```html
<uni-list>
<uni-list-item title="列表文字" note="列表描述信息"></uni-list-item>
<uni-list-item :disabled="true" title="列表文字" note="列表禁用状态"></uni-list-item>
</uni-list>
```
### 右侧显示角标、switch
- 设置 `show-badge` 属性 ,可以显示角标内容
- 设置 `show-switch` 属性,可以显示 switch 开关
```html
<uni-list>
<uni-list-item title="列表右侧显示角标" :show-badge="true" badge-text="12" ></uni-list-item>
<uni-list-item title="列表右侧显示 switch" :show-switch="true" @switchChange="switchChange" ></uni-list-item>
</uni-list>
```
### 左侧显示略缩图、图标
- 设置 `thumb` 属性 ,可以在列表左侧显示略缩图
- 设置 `show-extra-icon` 属性,并指定 `extra-icon` 可以在左侧显示图标
```html
<uni-list>
<uni-list-item title="列表左侧带略缩图" note="列表描述信息" thumb="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png"
thumb-size="lg" rightText="右侧文字"></uni-list-item>
<uni-list-item :show-extra-icon="true" :extra-icon="extraIcon1" title="列表左侧带扩展图标" ></uni-list-item>
</uni-list>
```
### 开启点击反馈和右侧箭头
- 设置 `clickable``true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件
- 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头
- 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo`
```html
<uni-list>
<uni-list-item title="开启点击反馈" clickable @click="onClick" ></uni-list-item>
<uni-list-item title="默认 navigateTo 方式跳转页面" link to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
<uni-list-item title="reLaunch 方式跳转页面" link="reLaunch" to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
</uni-list>
```
### 聊天列表示例
- 设置 `clickable``true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件
- 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo`
- 设置 `to` 属性,可以跳转页面
- `time` 属性,通常会设置成时间显示,但是这个属性不仅仅可以设置时间,你可以传入任何文本,注意文本长度可能会影响显示
- `avatar``avatarList` 属性同时只会有一个生效,同时设置的话,`avatarList` 属性的长度大于1 `avatar` 属性将失效
- 可以通过默认插槽自定义列表右侧内容
```html
<uni-list>
<uni-list :border="true">
<!-- 显示圆形头像 -->
<uni-list-chat :avatar-circle="true" title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" ></uni-list-chat>
<!-- 右侧带角标 -->
<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-text="12" :badge-style="{backgroundColor:'#FF80AB'}"></uni-list-chat>
<!-- 头像显示圆点 -->
<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
<!-- 头像显示角标 -->
<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="99"></uni-list-chat>
<!-- 显示多头像 -->
<uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
<!-- 自定义右侧内容 -->
<uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot">
<view class="chat-custom-right">
<text class="chat-custom-text">刚刚</text>
<!-- 需要使用 uni-icons 请自行引入 -->
<uni-icons type="star-filled" color="#999" size="18"></uni-icons>
</view>
</uni-list-chat>
</uni-list>
</uni-list>
```
```javascript
export default {
components: {},
data() {
return {
avatarList: [{
url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
}, {
url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
}, {
url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
}]
}
}
}
```
```css
.chat-custom-right {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
}
.chat-custom-text {
font-size: 12px;
color: #999;
}
```
## API
### List Props
属性名 |类型 |默认值 | 说明
:-: |:-: |:-: | :-:
border |Boolean |true | 是否显示边框
### ListItem Props
属性名 |类型 |默认值 | 说明
:-: |:-: |:-: | :-:
title |String |- | 标题
note |String |- | 描述
ellipsis |Number |0 | title 是否溢出隐藏可选值0:默认; 1:显示一行; 2:显示两行;【nvue 暂不支持】
thumb |String |- | 左侧缩略图若thumb有值则不会显示扩展图标
thumbSize |String |medium | 略缩图尺寸可选值lg:大图; medium:一般; sm:小图;
showBadge |Boolean |false | 是否显示数字角标
badgeText |String |- | 数字角标内容
badgeType |String |- | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21)
badgeStyle |Object |- | 数字角标样式使用uni-badge的custom-style参数
rightText |String |- | 右侧文字内容
disabled |Boolean |false | 是否禁用
showArrow |Boolean |true | 是否显示箭头图标
link |String |navigateTo | 新页面跳转方式,可选值见下表
to |String |- | 新页面跳转地址如填写此属性click 会返回页面是否跳转成功
clickable |Boolean |false | 是否开启点击反馈
showSwitch |Boolean |false | 是否显示Switch
switchChecked |Boolean |false | Switch是否被选中
showExtraIcon |Boolean |false | 左侧是否显示扩展图标
extraIcon |Object |- | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28)
direction | String |row | 排版方向可选值row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排也受此属性控制
#### Link Options
属性名 | 说明
:-: | :-:
navigateTo | 同 uni.navigateTo()
redirectTo | 同 uni.reLaunch()
reLaunch | 同 uni.reLaunch()
switchTab | 同 uni.switchTab()
### ListItem Events
事件称名 |说明 |返回参数
:-: |:-: |:-:
click |点击 uniListItem 触发事件,需开启点击反馈 |-
switchChange |点击切换 Switch 时触发,需显示 switch |e={value:checked}
### ListItem Slots
名称 | 说明
:-: | :-:
header | 左/上内容插槽,可完全自定义默认显示
body | 中间内容插槽,可完全自定义中间内容
footer | 右/下内容插槽,可完全自定义右侧内容
> **通过插槽扩展**
> 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现
> 如果 `uni-list-item` 组件内置属性样式无法满足需求可以使用插槽来自定义uni-list-item里的内容。
> uni-list-item提供了3个可扩展的插槽`header``body``footer`
> - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分
> - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分
> 开发者可以只用1个插槽也可以3个一起使用。在插槽中可自主编写view标签实现自己所需的效果。
**示例**
```html
<uni-list>
<uni-list-item title="自定义右侧插槽" note="列表描述信息" link>
<template slot="header">
<image class="slot-image" src="/static/logo.png" mode="widthFix"></image>
</template>
</uni-list-item>
<uni-list-item>
<!-- 自定义 header -->
<view slot="header" class="slot-box"><image class="slot-image" src="/static/logo.png" mode="widthFix"></image></view>
<!-- 自定义 body -->
<text slot="body" class="slot-box slot-text">自定义插槽</text>
<!-- 自定义 footer-->
<template slot="footer">
<image class="slot-image" src="/static/logo.png" mode="widthFix"></image>
</template>
</uni-list-item>
</uni-list>
```
### ListItemChat Props
属性名 |类型 |默认值 | 说明
:-: |:-: |:-: | :-:
title |String |- | 标题
note |String |- | 描述
clickable |Boolean |false | 是否开启点击反馈
badgeText |String |- | 数字角标内容,设置为 `dot` 将显示圆点
badgePositon |String |right | 角标位置
link |String |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表
clickable |Boolean |false | 是否开启点击反馈
to |String |- | 跳转页面地址如填写此属性click 会返回页面是否跳转成功
time |String |- | 右侧时间显示
avatarCircle |Boolean |false | 是否显示圆形头像
avatar |String |- | 头像地址avatarCircle 不填时生效
avatarList |Array |- | 头像组,格式为 [{url:''}]
#### Link Options
属性名 | 说明
:-: | :-:
navigateTo | 同 uni.navigateTo()
redirectTo | 同 uni.reLaunch()
reLaunch | 同 uni.reLaunch()
switchTab | 同 uni.switchTab()
### ListItemChat Slots
名称 | 说明
:- | :-
default | 自定义列表右侧内容(包括时间和角标显示)
### ListItemChat Events
事件称名 | 说明 | 返回参数
:-: | :-: | :-:
@click | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息
## 基于uni-list扩展的页面模板
通过扩展插槽,可实现多种常见样式的列表
**新闻列表类**
1. 云端一体混合布局:[https://ext.dcloud.net.cn/plugin?id=2546](https://ext.dcloud.net.cn/plugin?id=2546)
2. 云端一体垂直布局,大图模式:[https://ext.dcloud.net.cn/plugin?id=2583](https://ext.dcloud.net.cn/plugin?id=2583)
3. 云端一体垂直布局,多行图文混排:[https://ext.dcloud.net.cn/plugin?id=2584](https://ext.dcloud.net.cn/plugin?id=2584)
4. 云端一体垂直布局,多图模式:[https://ext.dcloud.net.cn/plugin?id=2585](https://ext.dcloud.net.cn/plugin?id=2585)
5. 云端一体水平布局,左图右文:[https://ext.dcloud.net.cn/plugin?id=2586](https://ext.dcloud.net.cn/plugin?id=2586)
6. 云端一体水平布局,左文右图:[https://ext.dcloud.net.cn/plugin?id=2587](https://ext.dcloud.net.cn/plugin?id=2587)
7. 云端一体垂直布局,无图模式,主标题+副标题:[https://ext.dcloud.net.cn/plugin?id=2588](https://ext.dcloud.net.cn/plugin?id=2588)
**商品列表类**
1. 云端一体列表/宫格视图互切:[https://ext.dcloud.net.cn/plugin?id=2651](https://ext.dcloud.net.cn/plugin?id=2651)
2. 云端一体列表(宫格模式):[https://ext.dcloud.net.cn/plugin?id=2671](https://ext.dcloud.net.cn/plugin?id=2671)
3. 云端一体列表(列表模式):[https://ext.dcloud.net.cn/plugin?id=2672](https://ext.dcloud.net.cn/plugin?id=2672)
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list)

View File

@ -0,0 +1,19 @@
## 1.3.32022-01-20
- 新增 showText属性 ,是否显示文本
## 1.3.22022-01-19
- 修复 nvue 平台下不显示文本的bug
## 1.3.12022-01-19
- 修复 微信小程序平台样式选择器报警告的问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more)
## 1.2.12021-08-24
- 新增 支持国际化
## 1.2.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.82021-05-12
- 新增 组件示例地址
## 1.1.72021-03-30
- 修复 uni-load-more 在首页使用时h5 平台报 'uni is not defined' 的 bug
## 1.1.62021-02-05
- 调整为uni_modules目录规范

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "Pull up to show more",
"uni-load-more.contentrefresh": "loading...",
"uni-load-more.contentnomore": "No more data"
}

View File

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉显示更多",
"uni-load-more.contentrefresh": "正在加载...",
"uni-load-more.contentnomore": "没有更多数据了"
}

View File

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉顯示更多",
"uni-load-more.contentrefresh": "正在加載...",
"uni-load-more.contentnomore": "沒有更多數據了"
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,86 @@
{
"id": "uni-load-more",
"displayName": "uni-load-more 加载更多",
"version": "1.3.3",
"description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
"keywords": [
"uni-ui",
"uniui",
"加载更多",
"load-more"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,14 @@
### LoadMore 加载更多
> **组件名uni-load-more**
> 代码块: `uLoadMore`
用于列表中,做滚动加载使用,展示 loading 的各种状态。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,51 @@
## 1.3.112023-03-29
- 修复 自定义状态栏高度闪动BUG
## 1.3.102023-03-29
- 修复 暗黑模式下边线颜色错误的bug
## 1.3.92022-10-13
- 修复 条件编译错误的bug
## 1.3.82022-10-12
- 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug
## 1.3.72022-08-11
- 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug
## 1.3.62022-06-30
- 修复 组件示例中插槽用法无法显示内容的bug
## 1.3.52022-05-24
- 新增 stat 属性 可开启统计title 上报 仅使用了title 属性且项目开启了uni统计生效
## 1.3.42022-01-24
- 更新 组件示例
## 1.3.32022-01-24
- 新增 left-width/right-width属性 ,可修改左右两侧的宽度
## 1.3.22022-01-18
- 修复 在vue下标题不垂直居中的bug
## 1.3.12022-01-18
- 修复 height 属性类型错误
## 1.3.02022-01-18
- 新增 height 属性,可修改组件高度
- 新增 dark 属性可可开启暗黑模式
- 优化 标题字数过多显示省略号
- 优化 插槽,插入内容可完全覆盖
## 1.2.12022-01-10
- 修复 color 属性不生效的bug
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-nav-bar](https://uniapp.dcloud.io/component/uniui/uni-nav-bar)
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.112021-05-12
- 新增 组件示例地址
## 1.0.102021-04-30
- 修复 在nvue下fixed为true宽度不能撑满的Bug
## 1.0.92021-04-21
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.82021-04-14
- uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug
## 1.0.72021-02-25
- 修复 easycom 下,找不到 uni-status-bar 的bug
## 1.0.62021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.52021-02-05
- 调整为uni_modules目录规范

View File

@ -0,0 +1,357 @@
<template>
<view class="uni-navbar" :class="{'uni-dark':dark, 'uni-nvue-fixed': fixed}">
<view class="uni-navbar__content" :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }"
:style="{ 'background-color': themeBgColor, 'border-bottom-color':themeColor }" >
<status-bar v-if="statusBar" />
<view :style="{ color: themeColor,backgroundColor: themeBgColor ,height:navbarHeight}"
class="uni-navbar__header">
<view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left"
:style="{width:leftIconWidth}">
<slot name="left">
<view class="uni-navbar__content_view" v-if="leftIcon.length > 0">
<uni-icons :color="themeColor" :type="leftIcon" size="20" />
</view>
<view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length > 0 }" class="uni-navbar-btn-text"
v-if="leftText.length">
<text :style="{ color: themeColor, fontSize: '12px' }">{{ leftText }}</text>
</view>
</slot>
</view>
<view class="uni-navbar__header-container " @tap="onClickTitle">
<slot>
<view class="uni-navbar__header-container-inner" v-if="title.length>0">
<text class="uni-nav-bar-text uni-ellipsis-1"
:style="{color: themeColor }">{{ title }}</text>
</view>
</slot>
</view>
<view @click="onClickRight" class="uni-navbar__header-btns uni-navbar__header-btns-right"
:style="{width:rightIconWidth}">
<slot name="right">
<view v-if="rightIcon.length">
<uni-icons :color="themeColor" :type="rightIcon" size="22" />
</view>
<view class="uni-navbar-btn-text" v-if="rightText.length && !rightIcon.length">
<text class="uni-nav-bar-right-text" :style="{ color: themeColor}">{{ rightText }}</text>
</view>
</slot>
</view>
</view>
</view>
<!-- #ifndef APP-NVUE -->
<view class="uni-navbar__placeholder" v-if="fixed">
<status-bar v-if="statusBar" />
<view class="uni-navbar__placeholder-view" :style="{ height:navbarHeight}" />
</view>
<!-- #endif -->
</view>
</template>
<script>
import statusBar from "./uni-status-bar.vue";
const getVal = (val) => typeof val === 'number' ? val + 'px' : val;
/**
*
*
* NavBar 自定义导航栏
* @description 导航栏组件主要用于头部导航
* @tutorial https://ext.dcloud.net.cn/plugin?id=52
* @property {Boolean} dark 开启黑暗模式
* @property {String} title 标题文字
* @property {String} leftText 左侧按钮文本
* @property {String} rightText 右侧按钮文本
* @property {String} leftIcon 左侧按钮图标图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type
* @property {String} rightIcon 右侧按钮图标图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type
* @property {String} color 图标和文字颜色
* @property {String} backgroundColor 导航栏背景颜色
* @property {Boolean} fixed = [true|false] 是否固定顶部
* @property {Boolean} statusBar = [true|false] 是否包含状态栏
* @property {Boolean} shadow = [true|false] 导航栏下是否有阴影
* @property {Boolean} stat 是否开启统计标题上报
* @event {Function} clickLeft 左侧按钮点击时触发
* @event {Function} clickRight 右侧按钮点击时触发
* @event {Function} clickTitle 中间标题点击时触发
*/
export default {
name: "UniNavBar",
components: {
statusBar
},
emits: ['clickLeft', 'clickRight', 'clickTitle'],
props: {
dark: {
type: Boolean,
default: false
},
title: {
type: String,
default: ""
},
leftText: {
type: String,
default: ""
},
rightText: {
type: String,
default: ""
},
leftIcon: {
type: String,
default: ""
},
rightIcon: {
type: String,
default: ""
},
fixed: {
type: [Boolean, String],
default: false
},
color: {
type: String,
default: ""
},
backgroundColor: {
type: String,
default: ""
},
statusBar: {
type: [Boolean, String],
default: false
},
shadow: {
type: [Boolean, String],
default: false
},
border: {
type: [Boolean, String],
default: true
},
height: {
type: [Number, String],
default: 44
},
leftWidth: {
type: [Number, String],
default: 60
},
rightWidth: {
type: [Number, String],
default: 60
},
stat: {
type: [Boolean, String],
default: ''
}
},
computed: {
themeBgColor() {
if (this.dark) {
//
if (this.backgroundColor) {
return this.backgroundColor
} else {
return this.dark ? '#333' : '#FFF'
}
}
return this.backgroundColor || '#FFF'
},
themeColor() {
if (this.dark) {
//
if (this.color) {
return this.color
} else {
return this.dark ? '#fff' : '#333'
}
}
return this.color || '#333'
},
navbarHeight() {
return getVal(this.height)
},
leftIconWidth() {
return getVal(this.leftWidth)
},
rightIconWidth() {
return getVal(this.rightWidth)
}
},
mounted() {
if (uni.report && this.stat && this.title !== '') {
uni.report('title', this.title)
}
},
methods: {
onClickLeft() {
this.$emit("clickLeft");
},
onClickRight() {
this.$emit("clickRight");
},
onClickTitle() {
this.$emit("clickTitle");
}
}
};
</script>
<style lang="scss" scoped>
$nav-height: 44px;
.uni-nvue-fixed {
/* #ifdef APP-NVUE */
position: sticky;
/* #endif */
}
.uni-navbar {
// box-sizing: border-box;
}
.uni-nav-bar-text {
/* #ifdef APP-PLUS */
font-size: 34rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 14px;
/* #endif */
}
.uni-nav-bar-right-text {
font-size: 12px;
}
.uni-navbar__content {
position: relative;
// background-color: #fff;
// box-sizing: border-box;
background-color: transparent;
}
.uni-navbar__content_view {
// box-sizing: border-box;
}
.uni-navbar-btn-text {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: flex-start;
align-items: center;
line-height: 12px;
}
.uni-navbar__header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding: 0 10px;
flex-direction: row;
height: $nav-height;
font-size: 12px;
}
.uni-navbar__header-btns {
/* #ifndef APP-NVUE */
overflow: hidden;
display: flex;
/* #endif */
flex-wrap: nowrap;
flex-direction: row;
width: 120rpx;
// padding: 0 6px;
justify-content: center;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-navbar__header-btns-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
width: 120rpx;
justify-content: flex-start;
align-items: center;
}
.uni-navbar__header-btns-right {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
// width: 150rpx;
// padding-right: 30rpx;
justify-content: flex-end;
align-items: center;
}
.uni-navbar__header-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
padding: 0 10px;
overflow: hidden;
}
.uni-navbar__header-container-inner {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 12px;
overflow: hidden;
// box-sizing: border-box;
}
.uni-navbar__placeholder-view {
height: $nav-height;
}
.uni-navbar--fixed {
position: fixed;
z-index: 998;
/* #ifdef H5 */
left: var(--window-left);
right: var(--window-right);
/* #endif */
/* #ifndef H5 */
left: 0;
right: 0;
/* #endif */
}
.uni-navbar--shadow {
box-shadow: 0 1px 6px #ccc;
}
.uni-navbar--border {
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #eee;
}
.uni-ellipsis-1 {
overflow: hidden;
/* #ifndef APP-NVUE */
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
text-overflow: ellipsis;
/* #endif */
}
//
.uni-dark {}
</style>

View File

@ -0,0 +1,24 @@
<template>
<view :style="{ height: statusBarHeight }" class="uni-status-bar">
<slot />
</view>
</template>
<script>
export default {
name: 'UniStatusBar',
data() {
return {
statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px'
}
}
}
</script>
<style lang="scss" >
.uni-status-bar {
// width: 750rpx;
height: 20px;
// height: var(--status-bar-height);
}
</style>

View File

@ -0,0 +1,86 @@
{
"id": "uni-nav-bar",
"displayName": "uni-nav-bar 自定义导航栏",
"version": "1.3.11",
"description": "自定义导航栏组件,主要用于头部导航。",
"keywords": [
"uni-ui",
"导航",
"导航栏",
"自定义导航栏"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,15 @@
## NavBar 导航栏
> **组件名uni-nav-bar**
> 代码块: `uNavBar`
导航栏组件,主要用于头部导航。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-nav-bar)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,90 @@
## 1.9.62025-01-08
- 修复 示例中过期图片地址
## 1.9.52024-10-15
- 修复 微信小程序中的getSystemInfo警告
## 1.9.22024-09-21
- 修复 uni-popup在android上的重复点击弹出位置不正确的bug
## 1.9.12024-04-02
- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
## 1.9.02024-03-28
- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
## 1.8.92024-03-20
- 修复 uni-popup-dialog 数据输入时修正为双向绑定
## 1.8.82024-02-20
- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
## 1.8.72024-02-02
- 新增 uni-popup-dialog 新增属性focusinput模式下是否自动自动聚焦
## 1.8.62024-01-30
- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
## 1.8.52024-01-26
- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
## 1.8.42023-11-15
- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
## 1.8.32023-04-17
- 修复 uni-popup 重复打开时的 bug
## 1.8.22023-02-02
- uni-popup-dialog 组件新增 inputType 属性
## 1.8.12022-12-01
- 修复 nvue 下 v-show 报错
## 1.8.02022-11-29
- 优化 主题样式
## 1.7.92022-04-02
- 修复 弹出层内部无法滚动的bug
## 1.7.82022-03-28
- 修复 小程序中高度错误的bug
## 1.7.72022-03-17
- 修复 快速调用open出现问题的Bug
## 1.7.62022-02-14
- 修复 safeArea 属性不能设置为false的bug
## 1.7.52022-01-19
- 修复 isMaskClick 失效的bug
## 1.7.42022-01-19
- 新增 cancelText \ confirmText 属性 ,可自定义文本
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
## 1.7.32022-01-13
- 修复 设置 safeArea 属性不生效的bug
## 1.7.22021-11-26
- 优化 组件示例
## 1.7.12021-11-26
- 修复 vuedoc 文字错误
## 1.7.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
## 1.6.22021-08-24
- 新增 支持国际化
## 1.6.12021-07-30
- 优化 vue3下事件警告的问题
## 1.6.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.5.02021-06-23
- 新增 mask-click 遮罩层点击事件
## 1.4.52021-06-22
- 修复 nvue 平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.42021-06-18
- 修复 H5平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.32021-06-08
- 修复 错误的 watch 字段
- 修复 safeArea 属性不生效的问题
- 修复 点击内容再点击遮罩无法关闭的Bug
## 1.4.22021-05-12
- 新增 组件示例地址
## 1.4.12021-04-29
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
## 1.4.0 2021-04-29
- 新增 type 属性的 left\right 值,支持左右弹出
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
- 新增 safeArea 属性,是否适配底部安全区
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
- 修复 App 端弹出等待的Bug
- 优化 提升低配设备性能,优化动画卡顿问题
- 优化 更简单的组件自定义方式
## 1.2.92021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.2.82021-02-05
- 调整为uni_modules目录规范
## 1.2.72021-02-05
- 调整为uni_modules目录规范
- 新增 支持 PC 端
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

View File

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

View File

@ -0,0 +1,316 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
</view>
<view v-if="mode === 'base'" class="uni-dialog-content">
<slot>
<text class="uni-dialog-content-text">{{content}}</text>
</slot>
</view>
<view v-else class="uni-dialog-content">
<slot>
<input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
:placeholder="placeholderText" :focus="focus">
</slot>
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
<text class="uni-dialog-button-text">{{closeText}}</text>
</view>
<view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {Boolean} focus input模式下是否自动聚焦默认为true
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式
* @value base 基础对话框
* @value input 可输入对话框
* @showClose {Boolean} 是否显示关闭按钮
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @property {Number} maxlength 输入
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: "uniPopupDialog",
mixins: [popup],
emits: ['confirm', 'close', 'update:modelValue', 'input'],
props: {
inputType: {
type: String,
default: 'text'
},
showClose: {
type: Boolean,
default: true
},
// #ifdef VUE2
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [Number, String],
default: ''
},
// #endif
placeholder: {
type: [String, Number],
default: ''
},
type: {
type: String,
default: 'error'
},
mode: {
type: String,
default: 'base'
},
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
},
cancelText: {
type: String,
default: ''
},
confirmText: {
type: String,
default: ''
},
maxlength: {
type: Number,
default: -1,
},
focus: {
type: Boolean,
default: true,
}
},
data() {
return {
dialogType: 'error',
val: ""
}
},
computed: {
okText() {
return this.confirmText || t("uni-popup.ok")
},
closeText() {
return this.cancelText || t("uni-popup.cancel")
},
placeholderText() {
return this.placeholder || t("uni-popup.placeholder")
},
titleText() {
return this.title || t("uni-popup.title")
}
},
watch: {
type(val) {
this.dialogType = val
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info'
}
},
value(val) {
if (this.maxlength != -1 && this.mode === 'input') {
this.val = val.slice(0, this.maxlength);
} else {
this.val = val
}
},
val(val) {
// #ifdef VUE2
// TODO vue2
this.$emit('input', val);
// #endif
// #ifdef VUE3
// TODO  vue3
this.$emit('update:modelValue', val);
// #endif
}
},
created() {
//
this.popup.disableMask()
// this.popup.closeMask()
if (this.mode === 'input') {
this.dialogType = 'info'
this.val = this.value;
// #ifdef VUE3
this.val = this.modelValue;
// #endif
} else {
this.dialogType = this.type
}
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
if (this.mode === 'input') {
this.$emit('confirm', this.val)
} else {
this.$emit('confirm')
}
if (this.beforeClose) return
this.popup.close()
},
/**
* 点击取消按钮
*/
closeDialog() {
this.$emit('close')
if (this.beforeClose) return
this.popup.close()
},
close() {
this.popup.close()
}
}
}
</script>
<style lang="scss">
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 25px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 20px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6C6C6C;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 16px;
color: #333;
}
.uni-button-color {
color: #007aff;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
border: 1px #eee solid;
height: 40px;
padding: 0 10px;
border-radius: 5px;
color: #555;
}
.uni-popup__success {
color: #4cd964;
}
.uni-popup__warn {
color: #f0ad4e;
}
.uni-popup__error {
color: #dd524d;
}
.uni-popup__info {
color: #909399;
}
</style>

Some files were not shown because too many files have changed in this diff Show More