
自荐,uniapp开发新选择!丰富、强大、优雅的全新组件库FirstUI!
一、产品介绍
给大家推荐下我们做的FirstUI(https://www.firstui.cn/),它是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。我们前后花了大概6个月的时间,目前组件、布局部分基本完成了,还做了几款简单的模板,后续会着力做模板这一块,多提供一些成熟常用的场景模板。可以实现快速上手,更好的节约使用者的时间与成本。
FirstUI我们倾注了大量的精力,很期待大家的关注试用,谢谢!
1、产品特征:
- 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
- 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
- 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
- 专属社区。我们用flarum(ps:这个大家也可以试下,设计与功能方面好用,感觉用来做社区非常棒)打造了FirstU专属社区(https://forum.firstui.cn/),用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。
2、源码及文档:
- github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
- gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
- 文档地址: https://doc.firstui.cn
3、扫码体验:
大部分组件是操作性的,大家扫码操作可以有更真切的感受。目前发布了6个平台版本,大家可以扫码体验下。考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。
4、部分效果:
5、进展与已上线功能清单:
进展情况:
已上线功能清单:
- 框架
- FirstUI组件库(uni-app版)
- 模板
- 登录(A)模板
- 登录(B)模板
- 登录(C)模板
- 登录(D)模板
- 门户模板
- 组件
- 基础组件
- Color 色彩
- Icon 图标
- Button 按钮
- Footer 页脚
- Animation 动画
- 导航组件
- NavBar 导航栏
- Pagination 分页器
- SegmentedControl 分段器
- Tabbar 标签栏
- Tabs 标签页
- IndexList 索引列表
- NoticeBar 通告栏
- Sticky 吸顶容器
- Steps 步骤条
- Fab 浮动按钮
- Drawer 抽屉
- BottomPopup 底部弹出层
- TopPopup 顶部弹出层
- TimeAxis 时间轴
- 布局组件
- Layout 栅格布局
- Panel 面板
- Preview 表单预览
- List 列表
- Card 卡片
- 扩展组件
- ShareSheet 分享面板
- Result 结果页
- FilterBar 筛选栏
- DigitalRoller 数字滚轮
- DigitalKeyboard 数字键盘
- CountdownVerify 验证码倒计时
- SwiperDot 轮播图指示点
- LicensePlate 车牌号键盘
- SingleInput 单输入框
- 操作反馈
- ActionSheet 上拉菜单
- Toast 轻提示
- BackDrop 遮罩层
- Dialog 对话框
- DropdownMenu 下拉菜单
- Modal 模态框
- Landscape 压屏窗
- Loading 加载
- Message 消息提示
- SwipeAction 滑动菜单
- DropdownList 下拉菜单
- LoadAni 加载动画
- 数据组件
- Badge 徽章
- Alert 警告框
- Avatar 头像
- Tag 标签
- Progress 进度条
- Collapse 折叠面板
- Divider 分割线
- LoadMore 加载更多
- Empty 暂无数据
- 画布
- Poster 绘制分享海报
- 表单组件
- Picker 选择器
- DatePicker 日期时间选择器
- Form 表单校验
- Input 输入框
- InputNumber 数字输入框
- Radio 单选框
- Checkbox 复选框
- Switch 开关
- Textarea 多行输入框
- SearchBar 搜索栏
- JS
- Clipboard 复制文本
- Request 网络请求
- Utils 工具类
- Validator 表单验证
二、商业版与开源版
FirstUI分为开源版与商业版,部分组件为商业版可用,目前商业版的费用是¥150元,一次性费用,支持后续更新。
VIP会员权益:
- 完整版源码
- 全部组件
- 物料商城享VIP折扣
- 专属会员群指导、答疑
- 新特性优先体验
- VIP专属文档
会员权益详情: https://www.firstui.cn/right
三、使用体验与建议
FirstUI刚刚开始成长,非常期待大家的体验使用和宝贵的建议!大家可以在文后留言,让我们有新的认识,谢谢!
一、产品介绍
给大家推荐下我们做的FirstUI(https://www.firstui.cn/),它是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。我们前后花了大概6个月的时间,目前组件、布局部分基本完成了,还做了几款简单的模板,后续会着力做模板这一块,多提供一些成熟常用的场景模板。可以实现快速上手,更好的节约使用者的时间与成本。
FirstUI我们倾注了大量的精力,很期待大家的关注试用,谢谢!
1、产品特征:
- 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
- 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
- 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
- 专属社区。我们用flarum(ps:这个大家也可以试下,设计与功能方面好用,感觉用来做社区非常棒)打造了FirstU专属社区(https://forum.firstui.cn/),用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。
2、源码及文档:
- github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
- gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
- 文档地址: https://doc.firstui.cn
3、扫码体验:
大部分组件是操作性的,大家扫码操作可以有更真切的感受。目前发布了6个平台版本,大家可以扫码体验下。考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。
4、部分效果:
5、进展与已上线功能清单:
进展情况:
已上线功能清单:
- 框架
- FirstUI组件库(uni-app版)
- 模板
- 登录(A)模板
- 登录(B)模板
- 登录(C)模板
- 登录(D)模板
- 门户模板
- 组件
- 基础组件
- Color 色彩
- Icon 图标
- Button 按钮
- Footer 页脚
- Animation 动画
- 导航组件
- NavBar 导航栏
- Pagination 分页器
- SegmentedControl 分段器
- Tabbar 标签栏
- Tabs 标签页
- IndexList 索引列表
- NoticeBar 通告栏
- Sticky 吸顶容器
- Steps 步骤条
- Fab 浮动按钮
- Drawer 抽屉
- BottomPopup 底部弹出层
- TopPopup 顶部弹出层
- TimeAxis 时间轴
- 布局组件
- Layout 栅格布局
- Panel 面板
- Preview 表单预览
- List 列表
- Card 卡片
- 扩展组件
- ShareSheet 分享面板
- Result 结果页
- FilterBar 筛选栏
- DigitalRoller 数字滚轮
- DigitalKeyboard 数字键盘
- CountdownVerify 验证码倒计时
- SwiperDot 轮播图指示点
- LicensePlate 车牌号键盘
- SingleInput 单输入框
- 操作反馈
- ActionSheet 上拉菜单
- Toast 轻提示
- BackDrop 遮罩层
- Dialog 对话框
- DropdownMenu 下拉菜单
- Modal 模态框
- Landscape 压屏窗
- Loading 加载
- Message 消息提示
- SwipeAction 滑动菜单
- DropdownList 下拉菜单
- LoadAni 加载动画
- 数据组件
- Badge 徽章
- Alert 警告框
- Avatar 头像
- Tag 标签
- Progress 进度条
- Collapse 折叠面板
- Divider 分割线
- LoadMore 加载更多
- Empty 暂无数据
- 画布
- Poster 绘制分享海报
- 表单组件
- Picker 选择器
- DatePicker 日期时间选择器
- Form 表单校验
- Input 输入框
- InputNumber 数字输入框
- Radio 单选框
- Checkbox 复选框
- Switch 开关
- Textarea 多行输入框
- SearchBar 搜索栏
- JS
- Clipboard 复制文本
- Request 网络请求
- Utils 工具类
- Validator 表单验证
二、商业版与开源版
FirstUI分为开源版与商业版,部分组件为商业版可用,目前商业版的费用是¥150元,一次性费用,支持后续更新。
VIP会员权益:
- 完整版源码
- 全部组件
- 物料商城享VIP折扣
- 专属会员群指导、答疑
- 新特性优先体验
- VIP专属文档
会员权益详情: https://www.firstui.cn/right
三、使用体验与建议
FirstUI刚刚开始成长,非常期待大家的体验使用和宝贵的建议!大家可以在文后留言,让我们有新的认识,谢谢!
收起阅读 »
应用外唤起地图并标注地点
map.js
/*
* 功能:应用外唤起地图并标注地点
* 特点:uni.openLocation()的简易替代品,唤起指定地图的某个标点。
* 优点:不依赖uniapi,无须集成sdk,无须声明位置权限,兼容多个地图。
* 缺点:非应用内置地图,依赖文档等等等。
* 日期:2021年12月15日
* 支付宝:\u004e\u0054\u0041\u0079\u004d\u0054\u0067\u0077\u004e\u007a\u0051\u0035\u0051\u0048\u0046\u0078\u004c\u006d\u004e\u0076\u0062\u0051\u003d\u003d
*/
/*
# 用法
import mapTool from 'xxx.js'
mapTool.navTo(point, map)
# 提示
1.未加入Google Map,未使用WGS84坐标系,需要唤起后直接导航 或 地址逆解析,请自行解决。
2.(重要)使用腾讯地图scheme需要开发者Key,请自行注册。
3.(重要)使用查询地图是否安装的方法mapsExist(),iOS9以后需要添加白名单才可查询。即在manifest.json文件plus->distribute->apple->urlschemewhitelist节点下添加(如urlschemewhitelist:["xxx"]),名称请参考下方MapsInfo对象各属性的i_wlname。
4.Document:
<https://lbsyun.baidu.com/index.php?title=uri>
<https://lbs.amap.com/api/amap-mobile/summary>
<https://lbs.qq.com/webApi/uriV1/uriGuide/uriOverview>
<https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html>
<https://developers.google.com/maps/documentation/ios/urlscheme>
*/
// 腾讯地图开发者Key
const QQMapKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
const Platform = plus.os.name // Android;iOS;...
const MapsInfo = {
baidu: {
url: 'http://map.baidu.com',
CN: '百度地图',
EN: 'bdmap',
w_uri: 'http://api.map.baidu.com',
a_intent: 'baidumap://',
a_pname: 'com.baidu.BaiduMap', //package name
i_scheme: 'baidumap://', // action
i_wlname: 'baidumap' // white list name
},
gaode: {
url: 'https://m.amap.com',
CN: '高德地图',
EN: 'amap',
w_uri: 'https://uri.amap.com',
a_intent: 'androidamap://',
a_pname: 'com.autonavi.minimap', //package name
i_scheme: 'iosamap://', // action
i_wlname: 'iosamap' // white list name
},
tengxun: {
url: 'https://map.qq.com',
CN: '腾讯地图',
EN: 'qqmap',
w_uri: 'https://apis.map.qq.com',
a_intent: 'qqmap://',
a_pname: 'com.tencent.map', //package name
i_scheme: 'qqmap://', // action
i_wlname: 'qqmap' // white list name
},
pingguo: { // 白名单需要增加maps,才可检测到系统地图
url: 'http://maps.apple.com',
CN: '系统地图',
EN: 'maps',
w_uri: 'http://maps.apple.com',
a_intent: 'androidamap://', // 安卓高德地图,理论上用不到
a_pname: 'com.autonavi.minimap', // 安卓高德地图,理论上用不到
i_scheme: 'maps://', // action
i_wlname: 'maps' // white list name
}
}
const DefaultMap = MapsInfo.tengxun // 据自身业务采用【腾讯地图】作为默认地图,腾讯地图scheme需要开发者Key
const DefaultPoint = { // 默认标注,【找不到所在位置】意为lat、lng缺失;【未知位置】意为lbl缺失;
lat: 22.517007, // gcj02
lng: 113.392532, // gcj02
lbl: '找不到所在位置', // label
dtl: '未知地址' // detail
}
const DefaultService = { // 来源信息(可设为包名),百度地图、高德地图必填,不可空
web: 'web.xxx.app',
ios: 'ios.xxx.app',
android: 'android.xxx.app'
}
// GCJ-02(腾讯、高德等火星坐标系) To BD-09(百度坐标系)
const gcj2bd = function(lat, lng) {
var point = new Object();
var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
var x = new Number(lng);
var y = new Number(lat);
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
var bd_lng = z * Math.cos(theta) + 0.0065;
var bd_lat = z * Math.sin(theta) + 0.006;
point.lng = bd_lng;
point.lat = bd_lat;
return point;
}
// BD-09(百度坐标系) To GCJ-02(腾讯、高德等火星坐标系)
const bd2gcj = function(lat, lng) {
var point = new Object();
var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
var x = new Number(lng - 0.0065);
var y = new Number(lat - 0.006);
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
var gcj_lng = z * Math.cos(theta);
var gcj_lat = z * Math.sin(theta);
point.lng = gcj_lng;
point.lat = gcj_lat;
return point;
}
// URL参数 To JSON对象
const query2json = function(query) {
let param = {}; // 存储最终JSON结果对象
query.replace(/([^?&]+)=([^?&]+)/g, function(s, v, k) {
param[v] = decodeURIComponent(k); //解析字符为中文(据业务处理)
return k + '=' + v;
});
return param;
}
// JSON对象 To URL参数
const json2query = function(json) {
let param = []
Object.keys(json).forEach(e => {
param.push(e + '=' + encodeURIComponent(json[e]))
})
return param.join('&')
}
// 深拷贝
function _isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function _deepClone(obj) {
// 对常见的“非”值,直接返回原来值
if([null, undefined, NaN, false].includes(obj)) return obj;
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = _isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? _deepClone(obj[i]) : obj[i];
}
}
return o;
}
// 判断是否存在这四个应用(百度地图、高德地图、腾讯地图、苹果地图),返回对象数组
const mapsExist = function() {
let res = []
if (plus.runtime.isApplicationExist({
pname: MapsInfo.baidu.a_pname,
action: MapsInfo.baidu.i_scheme
})) {
console.log("[已安装百度地图]");
res.push(MapsInfo.baidu)
}
if (plus.runtime.isApplicationExist({
pname: MapsInfo.gaode.a_pname,
action: MapsInfo.gaode.i_scheme
})) {
console.log("[已安装高德地图]");
res.push(MapsInfo.gaode)
}
if (plus.runtime.isApplicationExist({
pname: MapsInfo.tengxun.a_pname,
action: MapsInfo.tengxun.i_scheme
})) {
console.log("[已安装腾讯地图]");
res.push(MapsInfo.tengxun)
}
if (Platform == 'iOS' && plus.runtime.isApplicationExist({
pname: MapsInfo.pingguo.a_pname,
action: MapsInfo.pingguo.i_scheme
})) {
console.log("[已安装系统地图]");
res.push(MapsInfo.pingguo)
}
return res
}
// 仅仅是打开地图应用(不荐)
const openApp = function(map) {
if (Platform == "Android") {
plus.runtime.launchApplication({
pname: map.a_pname,
}, function(e) {
alert("Open system default browser failed: " + e.message);
});
} else if (Platform == "iOS") {
plus.runtime.launchApplication({
action: map.i_scheme
}, function(e) {
alert("Open system default browser failed: " + e.message);
});
} else {
alert("Operating system is not supported");
}
}
// 坐标参数处理
const argHandle = function(point) {
if (!point) {
return DefaultPoint
}
let pot = _deepClone(point)
if (!pot.lat || !pot.lng) {
pot = DefaultPoint
}
if (!pot.lbl) {
pot['lbl'] = '未知位置'
}
if (!pot.dtl) {
pot['dtl'] = '未知地址'
}
return pot
}
// 应用内唤起地图应用,并标注位置,失败则使用浏览器唤起 C2C 对应导航path请查阅api文档
const openByApp = function(map, point) {
return new Promise((resolve) => {
let pot = argHandle(point)
let url = ''
if (map.EN == 'bdmap') {
// 腾讯坐标系转换为百度坐标
let p = gcj2bd(pot.lat, pot.lng)
pot = {
...pot,
...p
}
if (Platform == 'Android') {
url =
`bdapp://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.android}`
} else if (Platform == 'iOS') {
url =
`baidumap://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.ios}`
}
} else if (map.EN == 'amap') {
if (Platform == 'Android') {
url =
`androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`
} else if (Platform == 'iOS') {
url =
`iosamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.ios}&dev=0`
}
} else if (map.EN == 'qqmap') {
if (Platform == 'Android') {
url =
`qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`
} else if (Platform == 'iOS') {
url =
`qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`
}
} else if (map.EN == 'maps') {
if (Platform == 'Android') {
url =
`androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`
} else if (Platform == 'iOS') {
// Unlike some schemes, map URLs do not start with a “maps” scheme identifier. Instead, map links are specified as regular http links and are opened either in Safari or the Maps app on the target platform.
url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`
}
}
if (url != '') {
plus.runtime.openURL(encodeURI(url), async function(e) {
console.error(e)
let result = await openByBrowser(map, point)
resolve(result)
});
resolve(true)
} else {
console.error('应用内唤起失败')
resolve(false)
}
})
}
// 浏览器唤起地图应用,并标注位置 C2B2C 对应导航path请查阅api文档
const openByBrowser = function(map, point) {
return new Promise((resolve) => {
let pot = argHandle(point)
let url = ''
if (map.EN == 'bdmap') {
// 腾讯坐标系转换为百度坐标
let p = gcj2bd(pot.lat, pot.lng)
pot = {
...pot,
...p
}
url =
`http://api.map.baidu.com/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.web}&output=html`
} else if (map.EN == 'amap') {
url =
`https://uri.amap.com/marker?position=${pot.lng},${pot.lat}&name=${pot.lbl}&src=${DefaultService.web}&callnative=1`
} else if (map.EN == 'qqmap') {
url =
`https://apis.map.qq.com/uri/v1/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${DefaultService.web}`
} else if (map.EN == 'maps') {
url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`
}
if (url != '') {
plus.runtime.openURL(encodeURI(url), function(e) {
console.error(e)
resolve(false)
});
resolve(true)
} else {
console.error('浏览器唤起失败')
resolve(false)
}
})
}
// 地图标注,map指定唤起地图(string,['qqmap','amap','bdmap','maps']之一),point坐标对象(object,DefaultPoint)
const navTo = async function(point, map) {
// test
// let result = openByBrowser(MapsInfo.pingguo, point)
// let result = await openByApp(MapsInfo.pingguo, point)
// console.log('打开结果', result)
// return false
let result = false
let res = mapsExist()
if (res.length > 0) { // 无地图应用
let index = res.findIndex(e => { // 参数有指定地图应用
return e.EN == map
})
if (index != -1) {
result = await openByApp(res[index], point)
} else if (res.findIndex(e => { // 如果有安装默认地图应用,优先使用默认地图Api
return e.EN == DefaultMap.EN
}) != -1) {
result = await openByApp(DefaultMap, point)
} else { // 默认用第一个地图导航
result = await openByApp(res[0], point)
}
} else { // 无地图应用,浏览器打开
let arr = Object.values(MapsInfo)
let index = Object.values(MapsInfo).findIndex(e => { // 参数有指定地图应用
return e.EN == map
})
if (index != -1) {
result = await openByBrowser(arr[index], point)
} else {
result = await openByBrowser(DefaultMap, point)
}
}
return result
}
module.exports = {
navTo
}
map.js
/*
* 功能:应用外唤起地图并标注地点
* 特点:uni.openLocation()的简易替代品,唤起指定地图的某个标点。
* 优点:不依赖uniapi,无须集成sdk,无须声明位置权限,兼容多个地图。
* 缺点:非应用内置地图,依赖文档等等等。
* 日期:2021年12月15日
* 支付宝:\u004e\u0054\u0041\u0079\u004d\u0054\u0067\u0077\u004e\u007a\u0051\u0035\u0051\u0048\u0046\u0078\u004c\u006d\u004e\u0076\u0062\u0051\u003d\u003d
*/
/*
# 用法
import mapTool from 'xxx.js'
mapTool.navTo(point, map)
# 提示
1.未加入Google Map,未使用WGS84坐标系,需要唤起后直接导航 或 地址逆解析,请自行解决。
2.(重要)使用腾讯地图scheme需要开发者Key,请自行注册。
3.(重要)使用查询地图是否安装的方法mapsExist(),iOS9以后需要添加白名单才可查询。即在manifest.json文件plus->distribute->apple->urlschemewhitelist节点下添加(如urlschemewhitelist:["xxx"]),名称请参考下方MapsInfo对象各属性的i_wlname。
4.Document:
<https://lbsyun.baidu.com/index.php?title=uri>
<https://lbs.amap.com/api/amap-mobile/summary>
<https://lbs.qq.com/webApi/uriV1/uriGuide/uriOverview>
<https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html>
<https://developers.google.com/maps/documentation/ios/urlscheme>
*/
// 腾讯地图开发者Key
const QQMapKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
const Platform = plus.os.name // Android;iOS;...
const MapsInfo = {
baidu: {
url: 'http://map.baidu.com',
CN: '百度地图',
EN: 'bdmap',
w_uri: 'http://api.map.baidu.com',
a_intent: 'baidumap://',
a_pname: 'com.baidu.BaiduMap', //package name
i_scheme: 'baidumap://', // action
i_wlname: 'baidumap' // white list name
},
gaode: {
url: 'https://m.amap.com',
CN: '高德地图',
EN: 'amap',
w_uri: 'https://uri.amap.com',
a_intent: 'androidamap://',
a_pname: 'com.autonavi.minimap', //package name
i_scheme: 'iosamap://', // action
i_wlname: 'iosamap' // white list name
},
tengxun: {
url: 'https://map.qq.com',
CN: '腾讯地图',
EN: 'qqmap',
w_uri: 'https://apis.map.qq.com',
a_intent: 'qqmap://',
a_pname: 'com.tencent.map', //package name
i_scheme: 'qqmap://', // action
i_wlname: 'qqmap' // white list name
},
pingguo: { // 白名单需要增加maps,才可检测到系统地图
url: 'http://maps.apple.com',
CN: '系统地图',
EN: 'maps',
w_uri: 'http://maps.apple.com',
a_intent: 'androidamap://', // 安卓高德地图,理论上用不到
a_pname: 'com.autonavi.minimap', // 安卓高德地图,理论上用不到
i_scheme: 'maps://', // action
i_wlname: 'maps' // white list name
}
}
const DefaultMap = MapsInfo.tengxun // 据自身业务采用【腾讯地图】作为默认地图,腾讯地图scheme需要开发者Key
const DefaultPoint = { // 默认标注,【找不到所在位置】意为lat、lng缺失;【未知位置】意为lbl缺失;
lat: 22.517007, // gcj02
lng: 113.392532, // gcj02
lbl: '找不到所在位置', // label
dtl: '未知地址' // detail
}
const DefaultService = { // 来源信息(可设为包名),百度地图、高德地图必填,不可空
web: 'web.xxx.app',
ios: 'ios.xxx.app',
android: 'android.xxx.app'
}
// GCJ-02(腾讯、高德等火星坐标系) To BD-09(百度坐标系)
const gcj2bd = function(lat, lng) {
var point = new Object();
var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
var x = new Number(lng);
var y = new Number(lat);
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
var bd_lng = z * Math.cos(theta) + 0.0065;
var bd_lat = z * Math.sin(theta) + 0.006;
point.lng = bd_lng;
point.lat = bd_lat;
return point;
}
// BD-09(百度坐标系) To GCJ-02(腾讯、高德等火星坐标系)
const bd2gcj = function(lat, lng) {
var point = new Object();
var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
var x = new Number(lng - 0.0065);
var y = new Number(lat - 0.006);
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
var gcj_lng = z * Math.cos(theta);
var gcj_lat = z * Math.sin(theta);
point.lng = gcj_lng;
point.lat = gcj_lat;
return point;
}
// URL参数 To JSON对象
const query2json = function(query) {
let param = {}; // 存储最终JSON结果对象
query.replace(/([^?&]+)=([^?&]+)/g, function(s, v, k) {
param[v] = decodeURIComponent(k); //解析字符为中文(据业务处理)
return k + '=' + v;
});
return param;
}
// JSON对象 To URL参数
const json2query = function(json) {
let param = []
Object.keys(json).forEach(e => {
param.push(e + '=' + encodeURIComponent(json[e]))
})
return param.join('&')
}
// 深拷贝
function _isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function _deepClone(obj) {
// 对常见的“非”值,直接返回原来值
if([null, undefined, NaN, false].includes(obj)) return obj;
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = _isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? _deepClone(obj[i]) : obj[i];
}
}
return o;
}
// 判断是否存在这四个应用(百度地图、高德地图、腾讯地图、苹果地图),返回对象数组
const mapsExist = function() {
let res = []
if (plus.runtime.isApplicationExist({
pname: MapsInfo.baidu.a_pname,
action: MapsInfo.baidu.i_scheme
})) {
console.log("[已安装百度地图]");
res.push(MapsInfo.baidu)
}
if (plus.runtime.isApplicationExist({
pname: MapsInfo.gaode.a_pname,
action: MapsInfo.gaode.i_scheme
})) {
console.log("[已安装高德地图]");
res.push(MapsInfo.gaode)
}
if (plus.runtime.isApplicationExist({
pname: MapsInfo.tengxun.a_pname,
action: MapsInfo.tengxun.i_scheme
})) {
console.log("[已安装腾讯地图]");
res.push(MapsInfo.tengxun)
}
if (Platform == 'iOS' && plus.runtime.isApplicationExist({
pname: MapsInfo.pingguo.a_pname,
action: MapsInfo.pingguo.i_scheme
})) {
console.log("[已安装系统地图]");
res.push(MapsInfo.pingguo)
}
return res
}
// 仅仅是打开地图应用(不荐)
const openApp = function(map) {
if (Platform == "Android") {
plus.runtime.launchApplication({
pname: map.a_pname,
}, function(e) {
alert("Open system default browser failed: " + e.message);
});
} else if (Platform == "iOS") {
plus.runtime.launchApplication({
action: map.i_scheme
}, function(e) {
alert("Open system default browser failed: " + e.message);
});
} else {
alert("Operating system is not supported");
}
}
// 坐标参数处理
const argHandle = function(point) {
if (!point) {
return DefaultPoint
}
let pot = _deepClone(point)
if (!pot.lat || !pot.lng) {
pot = DefaultPoint
}
if (!pot.lbl) {
pot['lbl'] = '未知位置'
}
if (!pot.dtl) {
pot['dtl'] = '未知地址'
}
return pot
}
// 应用内唤起地图应用,并标注位置,失败则使用浏览器唤起 C2C 对应导航path请查阅api文档
const openByApp = function(map, point) {
return new Promise((resolve) => {
let pot = argHandle(point)
let url = ''
if (map.EN == 'bdmap') {
// 腾讯坐标系转换为百度坐标
let p = gcj2bd(pot.lat, pot.lng)
pot = {
...pot,
...p
}
if (Platform == 'Android') {
url =
`bdapp://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.android}`
} else if (Platform == 'iOS') {
url =
`baidumap://map/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.ios}`
}
} else if (map.EN == 'amap') {
if (Platform == 'Android') {
url =
`androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`
} else if (Platform == 'iOS') {
url =
`iosamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.ios}&dev=0`
}
} else if (map.EN == 'qqmap') {
if (Platform == 'Android') {
url =
`qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`
} else if (Platform == 'iOS') {
url =
`qqmap://map/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${QQMapKey}`
}
} else if (map.EN == 'maps') {
if (Platform == 'Android') {
url =
`androidamap://viewMap?lat=${pot.lat}&lon=${pot.lng}&poiname=${pot.lbl}&sourceApplication=${DefaultService.android}&dev=0`
} else if (Platform == 'iOS') {
// Unlike some schemes, map URLs do not start with a “maps” scheme identifier. Instead, map links are specified as regular http links and are opened either in Safari or the Maps app on the target platform.
url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`
}
}
if (url != '') {
plus.runtime.openURL(encodeURI(url), async function(e) {
console.error(e)
let result = await openByBrowser(map, point)
resolve(result)
});
resolve(true)
} else {
console.error('应用内唤起失败')
resolve(false)
}
})
}
// 浏览器唤起地图应用,并标注位置 C2B2C 对应导航path请查阅api文档
const openByBrowser = function(map, point) {
return new Promise((resolve) => {
let pot = argHandle(point)
let url = ''
if (map.EN == 'bdmap') {
// 腾讯坐标系转换为百度坐标
let p = gcj2bd(pot.lat, pot.lng)
pot = {
...pot,
...p
}
url =
`http://api.map.baidu.com/marker?location=${pot.lat},${pot.lng}&title=${pot.lbl}&content=${pot.dtl}&src=${DefaultService.web}&output=html`
} else if (map.EN == 'amap') {
url =
`https://uri.amap.com/marker?position=${pot.lng},${pot.lat}&name=${pot.lbl}&src=${DefaultService.web}&callnative=1`
} else if (map.EN == 'qqmap') {
url =
`https://apis.map.qq.com/uri/v1/marker?marker=coord:${pot.lat},${pot.lng};title:${pot.lbl};addr:${pot.dtl}&referer=${DefaultService.web}`
} else if (map.EN == 'maps') {
url = `http://maps.apple.com?ll=${pot.lat},${pot.lng}&q=${pot.lbl}`
}
if (url != '') {
plus.runtime.openURL(encodeURI(url), function(e) {
console.error(e)
resolve(false)
});
resolve(true)
} else {
console.error('浏览器唤起失败')
resolve(false)
}
})
}
// 地图标注,map指定唤起地图(string,['qqmap','amap','bdmap','maps']之一),point坐标对象(object,DefaultPoint)
const navTo = async function(point, map) {
// test
// let result = openByBrowser(MapsInfo.pingguo, point)
// let result = await openByApp(MapsInfo.pingguo, point)
// console.log('打开结果', result)
// return false
let result = false
let res = mapsExist()
if (res.length > 0) { // 无地图应用
let index = res.findIndex(e => { // 参数有指定地图应用
return e.EN == map
})
if (index != -1) {
result = await openByApp(res[index], point)
} else if (res.findIndex(e => { // 如果有安装默认地图应用,优先使用默认地图Api
return e.EN == DefaultMap.EN
}) != -1) {
result = await openByApp(DefaultMap, point)
} else { // 默认用第一个地图导航
result = await openByApp(res[0], point)
}
} else { // 无地图应用,浏览器打开
let arr = Object.values(MapsInfo)
let index = Object.values(MapsInfo).findIndex(e => { // 参数有指定地图应用
return e.EN == map
})
if (index != -1) {
result = await openByBrowser(arr[index], point)
} else {
result = await openByBrowser(DefaultMap, point)
}
}
return result
}
module.exports = {
navTo
}
收起阅读 »

企业账号 iOS证书(.p12)和描述文件(.mobileprovision)申请
云打包、自定义基座时,因没有个人开发者账号,使用的是企业版账号,按照https://ask.dcloud.net.cn/article/152的教程生成证书没问题,但是添加设备、Identifiers、profiles的时候没有自动添加选项,给我造成了一定的困扰,现分享下经验。
Identifiers
在xcode——target——Signing&Capabilities中志杰创建后,会自动创建到企业账号的Identifiers中
设备
插入真机调试时,会自动创建到企业账号的Devices中
Profiles
这个耗费了半天时间,百度、Google也没找到指导教程,直到在官方文档中看到一句话
原来已经由Xcode自动接管了。百度下:provisioning profile 本地路径,找到了~/Library/MobileDevice/Provisioning Profiles,在这个目录下找到项目的.mobileprovision文件即为描述文件。
另一个问题
需要企业账号的管理员权限去创建profiles(未验证,因为没有管理员权限);
云打包、自定义基座时,因没有个人开发者账号,使用的是企业版账号,按照https://ask.dcloud.net.cn/article/152的教程生成证书没问题,但是添加设备、Identifiers、profiles的时候没有自动添加选项,给我造成了一定的困扰,现分享下经验。
Identifiers
在xcode——target——Signing&Capabilities中志杰创建后,会自动创建到企业账号的Identifiers中
设备
插入真机调试时,会自动创建到企业账号的Devices中
Profiles
这个耗费了半天时间,百度、Google也没找到指导教程,直到在官方文档中看到一句话
原来已经由Xcode自动接管了。百度下:provisioning profile 本地路径,找到了~/Library/MobileDevice/Provisioning Profiles,在这个目录下找到项目的.mobileprovision文件即为描述文件。
另一个问题
需要企业账号的管理员权限去创建profiles(未验证,因为没有管理员权限);
收起阅读 »
直播预告 | 个推分享从治数到用数的企业数据智能实践
如今,数智化转型成为企业的必选战略。作为一家数据智能上市企业,每日互动(个推)面向行业客户以及政府单位提供专业的数据智能产品和解决方案,用“数据让产业更智能”。结合自身数据智能实践,每日互动(个推)重磅打造“行业数智化实战”系列直播,旨在与行业伙伴们交流企业数智化升级过程中的创新方案和宝贵经验,助力产业进入创新增长的快车道。
12月16日(周四)19:30-20:30,“行业数智化实战”系列直播第一期火热来袭!我们邀请到了个推数据中台“每日治数平台”产品负责人袁凯,为大家带来个推在品牌营销、智慧高速等垂直行业的生动实践。
直播亮点:
解读每日互动治理和挖掘万亿级数据的“高能公式”
分享每日互动在不同垂直行业的数智化“成果样板”
总结企业落地数字化升级战略过程中的“避坑经验”
如今,数智化转型成为企业的必选战略。作为一家数据智能上市企业,每日互动(个推)面向行业客户以及政府单位提供专业的数据智能产品和解决方案,用“数据让产业更智能”。结合自身数据智能实践,每日互动(个推)重磅打造“行业数智化实战”系列直播,旨在与行业伙伴们交流企业数智化升级过程中的创新方案和宝贵经验,助力产业进入创新增长的快车道。
12月16日(周四)19:30-20:30,“行业数智化实战”系列直播第一期火热来袭!我们邀请到了个推数据中台“每日治数平台”产品负责人袁凯,为大家带来个推在品牌营销、智慧高速等垂直行业的生动实践。
直播亮点:
解读每日互动治理和挖掘万亿级数据的“高能公式”
分享每日互动在不同垂直行业的数智化“成果样板”
总结企业落地数字化升级战略过程中的“避坑经验”

系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios)
系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios):https://ext.dcloud.net.cn/plugin?id=6901
系统分享显示本App、获取分享的文件、选择文件以其它应用打开列表(ios):https://ext.dcloud.net.cn/plugin?id=6901

热更新 WGT安装包中manifest.json文件的version版本不匹配
app 热更新后 小米手机清除全部数据,然后重启app ,app 重新执行热更新 这是后就会提示 WGT安装包中manifest.json文件的version版本不匹配,查看plus.runtime.version 果然变成了 基座包版本号,然后就会一直包错 Cannot read property 'screen' of undefined
解决:热更新参数WidgetOptions改为force: true(Boolean 类型 )是否强制安装 就好了。原因暂时未知
true表示强制安装,不进行版本号的校验;false则需要版本号校验,如果将要安装应用的版本号不高于现有应用的版本号则终止安装,并返回安装失败。 仅安装wgt和wgtu时生效,默认值 false。
plus.runtime.install(res.tempFilePath, {
force: true
}
app 热更新后 小米手机清除全部数据,然后重启app ,app 重新执行热更新 这是后就会提示 WGT安装包中manifest.json文件的version版本不匹配,查看plus.runtime.version 果然变成了 基座包版本号,然后就会一直包错 Cannot read property 'screen' of undefined
解决:热更新参数WidgetOptions改为force: true(Boolean 类型 )是否强制安装 就好了。原因暂时未知
true表示强制安装,不进行版本号的校验;false则需要版本号校验,如果将要安装应用的版本号不高于现有应用的版本号则终止安装,并返回安装失败。 仅安装wgt和wgtu时生效,默认值 false。
plus.runtime.install(res.tempFilePath, {
force: true
}

安卓原生插件Component中申请权限
安卓中如果需要申请权限可以调用ActivityCompat.requestPermissions方法来进行申请,第一个参数需要一个Activity。
在Component中没有直接对Activity的引用,但在initComponentHostView时,有传递Context。
所以可以通过Context来获取Activity
参考链接:https://stackoverflow.com/questions/9891360/getting-activity-from-context-in-android
fun getActivity(ctx: Context?): Activity? {
if (ctx == null) return null
ctx.let {
return when (ctx) {
is Activity -> ctx
else -> getActivity((ctx as? ContextWrapper)?.baseContext)
}
}
}
安卓中如果需要申请权限可以调用ActivityCompat.requestPermissions方法来进行申请,第一个参数需要一个Activity。
在Component中没有直接对Activity的引用,但在initComponentHostView时,有传递Context。
所以可以通过Context来获取Activity
参考链接:https://stackoverflow.com/questions/9891360/getting-activity-from-context-in-android
fun getActivity(ctx: Context?): Activity? {
if (ctx == null) return null
ctx.let {
return when (ctx) {
is Activity -> ctx
else -> getActivity((ctx as? ContextWrapper)?.baseContext)
}
}
}
收起阅读 »

uniapp闭坑指南
uniapp闭坑指南,之前用的react+umi+ant 开发I电脑端,所有遇到问题文档都能找到答案。手机端用了umi各种问题在文档中找不到答案。总结了很多问题,闭坑指南“别听忽悠,看着生态大,实则……”
问题1:打包之后 加载首页没有加载uni官方样式
问题2:scroll-view 不支持相对定位
问题3:不支持打包配置 Hbuilder配置极简
问题4:hbuilder 浏览器运行配置代理不生效
问题5:页面写rpx 不能被编译
问题6:后退监听不支持H5原生返回
问题7:生态强大,插件600+ 实际精华插件寥寥无几
问题8:Hbuilder js里面写变量不能自动提示
问题9:代码格式化不如vscode
问题10:框架文档不详细,提问还需要付费
问题11:提供的附件上传组件,直接上传到云上
问题12:路由不如vue路由好用
uniapp闭坑指南,之前用的react+umi+ant 开发I电脑端,所有遇到问题文档都能找到答案。手机端用了umi各种问题在文档中找不到答案。总结了很多问题,闭坑指南“别听忽悠,看着生态大,实则……”
问题1:打包之后 加载首页没有加载uni官方样式
问题2:scroll-view 不支持相对定位
问题3:不支持打包配置 Hbuilder配置极简
问题4:hbuilder 浏览器运行配置代理不生效
问题5:页面写rpx 不能被编译
问题6:后退监听不支持H5原生返回
问题7:生态强大,插件600+ 实际精华插件寥寥无几
问题8:Hbuilder js里面写变量不能自动提示
问题9:代码格式化不如vscode
问题10:框架文档不详细,提问还需要付费
问题11:提供的附件上传组件,直接上传到云上
问题12:路由不如vue路由好用

uniapp实现非连接蓝牙的广播功能
目前官方提供的蓝牙是不支持非连接蓝牙的广播功能的,需要用native.js调用原生模块或者使用原生插件来实现
目前官方提供的蓝牙是不支持非连接蓝牙的广播功能的,需要用native.js调用原生模块或者使用原生插件来实现

uniapp 安卓设置虚拟按钮(底部虚拟按钮)(底部导航栏)为透明、半透明、背景色
var Color = plus.android.importClass("android.graphics.Color");
plus.android.importClass("android.view.Window");
var mainActivity = plus.android.runtimeMainActivity();
var window_android = mainActivity.getWindow();
var WindowManager=plus.android.importClass("android.view.WindowManager");
var View=plus.android.importClass("android.view.View");
//设置为全透明
window_android.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window_android.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window_android.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window_android.setNavigationBarColor(Color.TRANSPARENT);
//设置为全透明结束
//window_android.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); //设置为半透明
// plus.android.invoke(window_android,"setNavigationBarColor",Color.parseColor("#000")); //为虚拟按钮添加背景色
var Color = plus.android.importClass("android.graphics.Color");
plus.android.importClass("android.view.Window");
var mainActivity = plus.android.runtimeMainActivity();
var window_android = mainActivity.getWindow();
var WindowManager=plus.android.importClass("android.view.WindowManager");
var View=plus.android.importClass("android.view.View");
//设置为全透明
window_android.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window_android.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window_android.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window_android.setNavigationBarColor(Color.TRANSPARENT);
//设置为全透明结束
//window_android.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); //设置为半透明
// plus.android.invoke(window_android,"setNavigationBarColor",Color.parseColor("#000")); //为虚拟按钮添加背景色
收起阅读 »