miue-ui
Version:
ali miniProgram UI components for vehicle.
559 lines (543 loc) • 16 kB
JavaScript
/**
* author:dansion@163.com
* data:2022-07-01
* version:V1.0.0
*/
import _MENU from './Menu';
const EVENT={};
EVENT.TYPE={
LOWER:'lower',
UPPER:'upper',
SCROLL:'scroll',
MENU_PRESS:'MENU_PRESS',
LIST_PRESS:'LIST_PRESS',
SCROLL_LOAD:'SCROLL_LOAD',
ON_SHOW:'ON_SHOW',
}
EVENT.build=(target,obj)=>{
/*
* type:事件类型
* target:事件目标
* timeStamp:时间戳
* present:当前 索引
* index:
* data:
* presentMenu
* presentItem
* menu:[]
* list:[]
*
* }
*/
let _timeStamp=Date.now();
let {type='NULL',present=null,item=null,show=null,order=null}=obj;
//console.log(obj);
let _event={
type:type,
target:target,
timeStamp:_timeStamp,
present:present,
index:present,
menu:target.data.menus,
list:target.data.list,
total:target.data.total,
data:item,
style:target.data.style,
show:show,
order:target.data.order
}
if(type==EVENT.TYPE.MENU_PRESS){
}
if(type==EVENT.TYPE.LIST_PRESS){
_event.currentMenu=target.data.menus[target.data.pointType];
}
return _event;
}
const NOTICE_TEXT=new Map([
['loading','数据加载中'],
['end','已经到底了'],
['empty','暂时没有数据'],
['error','发生错误了'],
]);
const LOADING_STYLE=[
'ball-clip-rotate-pulse',
'ball-pulse',
'ball-beat',
'ball-pulse-sync',
'line-scale',
'line-scale-pulse-out',
'line-scale-pulse-out-rapid',
'ball-pulse-rise',
'ball-scale-ripple-multiple',
'ball-scale-ripple-multiple-dot',
];
const animation = my.createAnimation({
transformOrigin: "opacity",
duration: 3000,
timeFunction: "ease-in-out",
delay: 0,
});
Component({
mixins: [],
data: {
list:[],
total:0,
present:-1,
order:'',
scrollIntoView:0,
scrollNum:0,
menus:[],
isMenu:false,
menuIntoView:0,
visible:false,
type:'normal',
listNotice:"",
pointType:-1,
notice:{},
systemInfo:{},
orientation:'landscape',
relation:null,// 关联播放器
style:'list',// list= 文本列表, tile= 图片列表
playState:false,
playiconAnim:false, // false=一般图标, true=动画图标
class:'',
listAnimation:'',
EVENT_TYPE:EVENT.TYPE,
voice_menu:false,
voice_select:false,
voice_next_prev:false,
},
props: {
onRenderList:''
},
didMount() {
//初始化组件
this.animation=animation;
//获取窗口大小,根据窗口的情况,设备默认 orientation的值。
my.getSystemInfo({
success: (res) => {
this.setData({
systemInfo: res
})
//当窗口宽 小于 高 为portrait:竖屏,否则为 横屏。
if(res.windowWidth<res.windowHeight){
this.setData({orientation:"portrait"});
}
}
})
this._init();
},
didUpdate() {},
didUnmount() {},
methods: {
_init(){
// 初始化时候设当前列表为空。
// this._noticeText('empty'); 部份设备以及车机响应慢,默认为empty,再调整共它状态会有闪的。
//向所在 Page 注册自己。
let _id=this.props.id?this.props.id:'lister_'+this.$id;
this.data.id=_id;
//console.log(_id);
if(this.$page.MIUE==undefined){
this.$page.MIUE=new Map();
}
this.$page.MIUE.set(this.props.id,this);
//根据入参 menu 渲染菜单数据。
if(this.props.menu!=undefined){
let _currentType;
let _menu=this.props.menu.split('|');
let _menuObj=[];
_menu.forEach((item,idx)=>{
let _items=item.split(':');
let _disabled=false;
let _type=_items[1];
if(_type==undefined){
_disabled=true;
}
let _selected=_items[2]?true:false;
_menuObj.push({text:_items[0],type:_items[1],selected:_selected,disabled:_disabled});
if(_selected){
_currentType=_items[1];
}
});
this.setMenu({menu:_menuObj,current:_currentType});
}
// 定义列表类型/ 浮动 和 正常类弄
let _type=this.props.type || '';
if(_type=='float'){
this.setData({type:_type});
this.setData({visible:false});
}
// 定义列表是不是可以入参动画
let _playiconAnim=this.props.playiconAnim || false;
if(_playiconAnim=='true'){
this.setData({playiconAnim:true});
}
let {loadingstyle=0}=getApp().config || my.MIUE_CONFIG || {};
this.setData({loadingstyle:loadingstyle});
},
setMenu(obj){
/**
* menu={
* type:
* text:
* disabled:
* visible: 是否可见
* }
*/
let {menu,current}=obj;
// 对菜单进行初始化
this.setData({isMenu:true});// 标记有菜单模式
this.setData({menus:_MENU.init(menu)});
if(current){
this._selectMenu(current);
}
// 菜单无指向时,默认第一个
if(this.data.pointType<0){
current=menu[0].type;
this._selectMenu(current);
}
},
setMenuItem(obj){
let {type,visible=true,disabled=false}=obj
let menus=this.data.menus;
menus.forEach((element,index) => {
//console.log(element);
if(type==element.type){
element.visible=visible;
element.disabled=disabled;
}
});
this.setData({menus:menus});
},
setPresent(idx,toview){
this.setData({present:idx});
if(toview){
this.setScrollIntoView(idx);
}
},
scrollToEnd(){
this.setData({scrollIntoView:'end'});
},
// 设备当前滚动并显示到第N个
setScrollIntoView(idx){
let canViewNun=5;//最小可视条目
let scrollNum=this.data.scrollNum;//当前滚动的位置,默认0
let maxView=scrollNum+canViewNun;
let scrollIntoView=this.data.scrollIntoView;
if(idx < scrollNum){
scrollNum=idx;
}
if(idx > maxView){
scrollNum=idx-canViewNun;
}
this.setData({scrollIntoView:scrollNum});
},
//设置当前列表数据
setDatas(obj){
this.setData({
listAnimation: 'listerViewShow'
});
let {total,list=[],order,style,menus,notice,cssClass}=obj;
total=total || list.length;
//let {total=obj.list.length,style}=obj;
this.setListData(list);
this.setTotal(total,notice);
this.setData({style:style});
this.setData({order:order});
if(menus!=undefined){
this.setData({menus:_MENU.init(menus)});
}
if(cssClass!=undefined){
this.setData({cssClass:cssClass});
}
},
// 清除列表数据。
clearListData(){
this._noticeText('loading');
this.setData({list:[]});
},
claerMenuData(){
this.setData({menus:[{text:''}]});
},
// 设置列表数据。
setListData(ls){
this._noticeText('loading');
try {
//let _list=ls;
//let _listLength = _list.length;
//this.setData({list:_list});
let new_list=[...ls]; //
let new_length = new_list.length;
let original_length=this.data.list.length;
if(new_length>original_length){
// 对新数据进行减除操作;
new_list.splice(0,original_length);
// 定义 spliceData 的入参数数组对象;
let sp_arr=[original_length,0];
sp_arr=sp_arr.concat(new_list);
this.$spliceData({'list':sp_arr})
}
let total=this.data.total;
// 当列表数据长度和数所都是 0 的情况
if(new_length==0 && total==0){
this._noticeText('empty');
}else if(new_length>=total){ // 当实际长度大于总长时:
this._noticeText('end');
}
return new_list;
} catch (error){
//console.warn(error);
this._noticeText('error');
}
},
setListDataPlayItem(playid){
//console.log(`setListDataPlayItem,playid:${playid}`)
if(playid){
let list=this.data.list;
list.forEach((ele,idx) => {
//console.log(`ele.id:${ele.id},playid:${playid}`);
if(ele.id==playid){
ele.isplay=true;
}else{
ele.isplay=false;
}
});
this.setData({list})
}
},
// 设置列表总长度。
setTotal(n,notice={}){
let total=this.data.total;
total=n;
this.setData({total});
let _listLength=this.data.list.length;
if(_listLength>=total){
this._noticeText('end');
}
// 当列表长度为 0时
if(_listLength==0){
let {text,detail}=notice;
this._noticeText({type:'empty',text:text,detail:detail});
}
},
setPlayState(b){
this.setData({playState:b});
},
_renderNotice(){
// todo
},
_noticeText(obj){
let {type='error',text='',detail=''}=obj;
if(typeof(obj)=='string'){
type=obj;
}
if(text==''){
text=NOTICE_TEXT.get(type);
}
//加载的样式
if(type=='loading'){
let loadingstyle=this.data.loadingstyle % LOADING_STYLE.length;
type= LOADING_STYLE[loadingstyle];
}
let notice=this.data.notice;
notice.text = text;
notice.detail= detail;
notice.type=type;
this.setData({notice});
},
setMessage(obj){
//let obj=s;
let {text='',detail='',message=''}=obj;
if(typeof(obj)=='string'){
text=obj;
}
//兼容入参不是 text ,而是 message
if(text=='' && message!=''){
text=message;
}
this._noticeText({type:'error',text:text,detail:detail});
},
// 当菜单被点击的事件
onMenuTap(e){
this.setData({
listAnimation: 'listerViewHide'
});
this._selectMenu(e.target.dataset.type);
},
// 菜单被点击的事件的处理
_selectMenu(type){
let _index;
let _current=type;
//console.log(_current);
let _isfirstItem=null; // 当前是第一个可见菜单
let menus=this.data.menus;
menus.forEach((element,index) => {
//console.log(element);
// 当前是第一个可见菜单
if(element.visible && _isfirstItem==null){
_isfirstItem=index;
}
if(_current==element.type){
element.selected=true;
_index=index;
}else{
element.selected=false;
}
});
// console.log(_obj);
this.setData({pointType:_index});
// 当前是第一个可见菜时间,对页面进行样式设置;
if(_index==_isfirstItem){
this.setData({firstMenuSelected:true});
}else{
this.setData({firstMenuSelected:false});
}
this.setData({menus:menus});
// 处理菜单被点击之事的滚动逻辑
let _currentMenuintoview=this.data.menuIntoView;
let _mentintoview=this.data.menuIntoView;
if(_index > 2 ){
_mentintoview=_index - 2 + 1;
this.setData({menuscrolltop:1}); // 滚动列表会有兼容问题,需要添加一个scrolltop属性兼容
if(_mentintoview==1){
this.setData({menuscrolltop:0});
}
this.setData({menuIntoView:_mentintoview});
}else{
this.setData({menuscrolltop:0}); // 滚动列表会有兼容问题,需要添加一个scrolltop属性兼容
}
// 触发 onPress 事件
if(this.props.onPress){
this.props.onPress(
EVENT.build(this,{type:EVENT.TYPE.MENU_PRESS,present:_index,item:menus[_index]})
);
}
},
// 当列表被点击
onListTap(e){
let idx=e.target.targetDataset.index;
if(idx!=undefined && this.props.onPress){
this.props.onPress(
EVENT.build(this,{type:EVENT.TYPE.LIST_PRESS,present:idx,item:this.data.list[idx]})
);
}
//存在关联player 直接调用播放
if(this.data.relation!=null){
this.data.relation.play(_event);
}
},
// 向上滚动
upper(e){
if(this.props.onScroll){
this.props.onScroll(EVENT.build(this,{type:EVENT.TYPE.UPPER}));
}
},
// 向下滚动
lower(e){
if(this.props.onScroll){
this.props.onScroll(EVENT.build(this,{type:EVENT.TYPE.LOWER}));
}
// 滚动到最下面~~
let {list,total='unknow'}=this.data;
//console.log(list);
//console.log(total);
//console.log(list.length < total);
//console.log((list.length < total || total=='unknow'));
if(list.length>1 && (list.length < total || total=='unknow')){
//console.log('onScrollLoad---');
if(this.props.onScrollLoad){
this.props.onScrollLoad(EVENT.build(this,{type:EVENT.TYPE.SCROLL_LOAD}));
}
this._noticeText('loading');
}
},
// 滚动操作
scroll(e){
if(this.props.onScroll){
this.props.onScroll(EVENT.build(this,{type:EVENT.TYPE.SCROLL}));
}
//当前滚动的位置,默认0
let listLength=this.data.list.length + 1;
this.data.scrollNum= e.detail.scrollTop / e.detail.scrollHeight * listLength;
},
// 当整体表被展示(浮动模式)
show(b){
this.setData({visible:b});
if(this.props.onShow){
this.props.onShow(EVENT.build(this,{type:EVENT.ON_SHOW,show:b}));
}
},
// 当整体被关闭(浮动模式)
onClose(e){
//console.log(e);
this.show(false);
},
// 语音VGUI指令 select:“第N个"
onVoiceSelectCommand(e){
// 获取语音VGUI的热词
let {result=0,command={}}=e || {};
let {action}=command || {}; // action: next,previous,select
let tapEvent={
type:'voice',
timeStamp:Date.now(),
target:{targetDataset:{index:-1,}}
}
if(action=="select"){
result=result-1;// 列表第一项是0开始,所有需要 -1;
tapEvent.target.targetDataset.index=result;
this.onListTap(tapEvent);
}
},
onMenuVoice(e){
//my.showToast({content:e.target.dataset.type});
//console.log(e);
let menu=this.data.menus;
let {querywords=''}=e; //获取Voice 的关健词
//console.log(menu);
menu.forEach((ele,idx)=>{
if(querywords==ele.text){
let type=ele.type;
this._selectMenu(type);
}
});
//this._selectMenu(e.target.dataset.type);
},
// 语音被命中上一页/ 下一页操作
onVoiceNextPrevCommand(e){
let {result=0,command={}}=e || {};
let {action}=command || {}; // action: next,previous
let {present=-1}=this.data;
let tapEvent={
type:'voice',
timeStamp:Date.now(),
target:{targetDataset:{index:-1,}}
}
if(present==-1){
let action_str=action=='next'?'向后一个':'向前一个';
if(this.props.onError){
this.props.onError(
{
refer:`voice_${action}`,
type:'voice',
action:action,
timeStamp:Date.now(),
index:present,
code:'20000',
message:`当前列表为空或不可选,[${action_str}]操作无效!`
}
);
}
}else{
if(action=='next'){
tapEvent.target.targetDataset.index=present+1;
this.onListTap(tapEvent);
}
if(action=='previous'){
console.log(`present:${present},index:${tapEvent.target.targetDataset.index}`)
tapEvent.target.targetDataset.index=present-1;
this.onListTap(tapEvent);
}
}
}
},
});